$confirm = $wgRequest->wasPosted() &&
$wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
$reason = $wgRequest->getText( 'wpReason' );
+ # Flag to hide all contents of the archived revisions
+ $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed('deleterevision');
# This code desperately needs to be totally rewritten
}
if( $confirm ) {
- $this->doDelete( $reason );
+ $this->doDelete( $reason, $suppress );
if( $wgRequest->getCheck( 'wpWatch' ) ) {
$this->doWatch();
} elseif( $this->mTitle->userIsWatching() ) {
$delcom = htmlspecialchars( wfMsg( 'deletecomment' ) );
$token = htmlspecialchars( $wgUser->editToken() );
$watch = Xml::checkLabel( wfMsg( 'watchthis' ), 'wpWatch', 'wpWatch', $wgUser->getBoolOption( 'watchdeletion' ) || $this->mTitle->userIsWatching(), array( 'tabindex' => '2' ) );
-
+ if ( $wgUser->isAllowed( 'deleterevision' ) ) {
+ $supress = "<tr><td> </td><td>";
+ $supress .= Xml::checkLabel( wfMsg( 'revdelete-suppress' ), 'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '2' ) );
+ $supress .= "</td></tr>";
+ } else {
+ $supress = '';
+ }
+
$wgOut->addHTML( "
<form id='deleteconfirm' method='post' action=\"{$formaction}\">
<table border='0'>
<input type='text' size='60' name='wpReason' id='wpReason' value=\"" . htmlspecialchars( $reason ) . "\" tabindex=\"1\" />
</td>
</tr>
+ $supress
<tr>
<td> </td>
<td>$watch</td>
/**
* Perform a deletion and output success or failure messages
*/
- function doDelete( $reason ) {
+ function doDelete( $reason, $suppress = false ) {
global $wgOut, $wgUser;
wfDebug( __METHOD__."\n" );
if (wfRunHooks('ArticleDelete', array(&$this, &$wgUser, &$reason))) {
- if ( $this->doDeleteArticle( $reason ) ) {
+ if ( $this->doDeleteArticle( $reason, $suppress ) ) {
$deleted = wfEscapeWikiText( $this->mTitle->getPrefixedText() );
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
* Deletes the article with database consistency, writes logs, purges caches
* Returns success
*/
- function doDeleteArticle( $reason ) {
+ function doDeleteArticle( $reason, $suppress = false ) {
global $wgUseSquid, $wgDeferredUpdateList;
global $wgUseTrackbacks;
$u = new SiteStatsUpdate( 0, 1, -(int)$this->isCountable( $this->getContent() ), -1 );
array_push( $wgDeferredUpdateList, $u );
+ // Bitfields to further supress the content
+ if ( $suppress ) {
+ $bitfield = 0;
+ $bitfield |= Revision::DELETED_TEXT;
+ $bitfield |= Revision::DELETED_COMMENT;
+ $bitfield |= Revision::DELETED_USER;
+ $bitfield |= Revision::DELETED_RESTRICTED;
+ } else {
+ $bitfield = 'rev_deleted';
+ }
+
// For now, shunt the revision data into the archive table.
// Text is *not* removed from the text table; bulk storage
// is left intact to avoid breaking block-compression or
'ar_text_id' => 'rev_text_id',
'ar_text' => '\'\'', // Be explicit to appease
'ar_flags' => '\'\'', // MySQL's "strict mode"...
- 'ar_len' => 'rev_len'
+ 'ar_deleted' => $bitfield
), array(
'page_id' => $id,
'page_id = rev_page'
# Clear caches
Article::onArticleDelete( $this->mTitle );
- # Log the deletion
- $log = new LogPage( 'delete' );
+ # Log the deletion, if the page was suppressed, log it at Oversight instead
+ $logtype = ($suppress) ? 'oversight' : 'delete';
+ $log = new LogPage( $logtype );
$log->addEntry( 'delete', $this->mTitle, $reason );
# Clear the cached article id so the interface doesn't act like we exist
);
}
- # Get the edit summary
$target = Revision::newFromId( $s->rev_id );
+ # Revision *must* be public and we don't well handle deleted edits on top
+ if ( $target->isDeleted(REVISION::DELETED_TEXT) ) {
+ $wgOut->setPageTitle( wfMsg('rollbackfailed') );
+ $wgOut->addHTML( wfMsg( 'missingarticle' ) );
+ }
+ # Get the edit summary
$newComment = wfMsgForContent( 'revertpage', $target->getUserText(), $from );
$newComment = $wgRequest->getText( 'summary', $newComment );
? wfMsg( 'diff' )
: $sk->makeKnownLinkObj( $this->mTitle, wfMsg( 'diff' ), 'diff=next&oldid='.$oldid );
- $userlinks = $sk->userLink( $revision->getUser(), $revision->getUserText() )
- . $sk->userToolLinks( $revision->getUser(), $revision->getUserText() );
+ $cdel='';
+ if( $wgUser->isAllowed( 'deleterevision' ) ) {
+ $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+ if( $revision->isCurrent() ) {
+ // We don't handle top deleted edits too well
+ $cdel = wfMsgHtml('rev-delundel');
+ } else if( !$revision->userCan( Revision::DELETED_RESTRICTED ) ) {
+ // If revision was hidden from sysops
+ $cdel = wfMsgHtml('rev-delundel');
+ } else {
+ $cdel = $sk->makeKnownLinkObj( $revdel,
+ wfMsgHtml('rev-delundel'),
+ 'target=' . urlencode( $this->mTitle->getPrefixedDbkey() ) .
+ '&oldid=' . urlencode( $oldid ) );
+ // Bolden oversighted content
+ if( $revision->isDeleted( Revision::DELETED_RESTRICTED ) )
+ $cdel = "<strong>$cdel</strong>";
+ }
+ $cdel = "(<small>$cdel</small>)";
+ }
+
+ $userlinks = $sk->revUserTools( $revision, true );
- $r = "\n\t\t\t\t<div id=\"mw-revision-info\">" . wfMsg( 'revision-info', $td, $userlinks ) . "</div>\n" .
+ $r = "\n\t\t\t\t<div id=\"mw-revision-info\">" . "<tt>$cdel</tt>" . wfMsg( 'revision-info', $td, $userlinks ) . "</div>\n" .
"\n\t\t\t\t<div id=\"mw-revision-nav\">" . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>\n\t\t\t";
$wgOut->setSubtitle( $r );
}
<?php
/**
+ * @package MediaWiki
* Contain class to show various lists of change:
* - what's link here
* - related changes
/**
* @todo document
+ * @package MediaWiki
*/
class RCCacheEntry extends RecentChange
{
var $secureName, $link;
- var $curlink , $difflink, $lastlink , $usertalklink , $versionlink ;
+ var $curlinks, $difflink, $lastlink , $usertalklink , $versionlink ;
var $userlink, $timestamp, $watched;
- function newFromParent( $rc ) {
+ function newFromParent( $rc )
+ {
$rc2 = new RCCacheEntry;
$rc2->mAttribs = $rc->mAttribs;
$rc2->mExtra = $rc->mExtra;
} ;
/**
+ * @package MediaWiki
*/
class ChangesList {
# Called by history lists and recent changes
#
/** @todo document */
- function __construct( &$skin ) {
+ function ChangesList( &$skin ) {
$this->skin =& $skin;
$this->preCacheMessages();
}
* @return ChangesList derivative
*/
public static function newFromUser( &$user ) {
- $sk = $user->getSkin();
+ $sk =& $user->getSkin();
$list = NULL;
if( wfRunHooks( 'FetchChangesList', array( &$user, &$sk, &$list ) ) ) {
return $user->getOption( 'usenewrc' ) ? new EnhancedChangesList( $sk ) : new OldChangesList( $sk );
: $nothing;
$f .= $bot ? '<span class="bot">' . $this->message['boteditletter'] . '</span>' : $nothing;
$f .= $patrolled ? '<span class="unpatrolled">!</span>' : $nothing;
- return $f;
+ return "<tt>$f</tt>";
}
/**
}
}
+ /**
+ * int $field one of DELETED_* bitfield constants
+ * @return bool
+ */
+ function isDeleted( $rc, $field ) {
+ return ($rc->mAttribs['rc_deleted'] & $field) == $field;
+ }
+
+ /**
+ * Determine if the current user is allowed to view a particular
+ * field of this revision, if it's marked as deleted.
+ * @param int $field
+ * @return bool
+ */
+ function userCan( $rc, $field ) {
+ if( ( $rc->mAttribs['rc_deleted'] & $field ) == $field ) {
+ global $wgUser;
+ $permission = ( $rc->mAttribs['rc_deleted'] & Revision::DELETED_RESTRICTED ) == Revision::DELETED_RESTRICTED
+ ? 'hiderevision'
+ : 'deleterevision';
+ wfDebug( "Checking for $permission due to $field match on $rc->mAttribs['rc_deleted']\n" );
+ return $wgUser->isAllowed( $permission );
+ } else {
+ return true;
+ }
+ }
function insertMove( &$s, $rc ) {
# Diff
$s .= '(' . $this->skin->makeKnownLinkObj($title, $logname ) . ')';
}
-
function insertDiffHist(&$s, &$rc, $unpatrolled) {
# Diff link
- if( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) {
+ if( !$this->userCan($rc,Revision::DELETED_TEXT) ) {
+ $diffLink = $this->message['diff'];
+ } else if( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG) {
$diffLink = $this->message['diff'];
} else {
$rcidparam = $unpatrolled
$params = ( $unpatrolled && $rc->mAttribs['rc_type'] == RC_NEW )
? 'rcid='.$rc->mAttribs['rc_id']
: '';
- $articlelink = ' '. $this->skin->makeKnownLinkObj( $rc->getTitle(), '', $params );
+ if( $this->isDeleted($rc,Revision::DELETED_TEXT) ) {
+ $articlelink = $this->skin->makeKnownLinkObj( $rc->getTitle(), '', $params );
+ $articlelink = '<span class="history-deleted">'.$articlelink.'</span>';
+ } else {
+ $articlelink = ' '. $this->skin->makeKnownLinkObj( $rc->getTitle(), '', $params );
+ }
if($watched) $articlelink = '<strong>'.$articlelink.'</strong>';
global $wgContLang;
$articlelink .= $wgContLang->getDirMark();
/** Insert links to user page, user talk page and eventually a blocking link */
function insertUserRelatedLinks(&$s, &$rc) {
- $s .= $this->skin->userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
- $s .= $this->skin->userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ if ( $this->isDeleted($rc,Revision::DELETED_USER) ) {
+ $s .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-user') . '</span>';
+ } else {
+ $s .= $this->skin->userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ $s .= $this->skin->userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ }
+ }
+
+ /** insert a formatted action */
+ function insertAction(&$s, &$rc) {
+ # Add comment
+ if( $rc->mAttribs['rc_type'] == RC_LOG ) {
+ // log action
+ if ( $this->isDeleted($rc,LogViewer::DELETED_ACTION) ) {
+ $s .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
+ } else {
+ $s .= ' ' . LogPage::actionText( $rc->mAttribs['rc_log_type'], $rc->mAttribs['rc_log_action'], $rc->getTitle(), $this->skin, LogPage::extractParams($rc->mAttribs['rc_params']), true, true );
+ }
+ }
}
/** insert a formatted comment */
function insertComment(&$s, &$rc) {
# Add comment
if( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) {
- $s .= $this->skin->commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
+ // log comment
+ if ( $this->isDeleted($rc,Revision::DELETED_COMMENT) ) {
+ $s .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-comment') . '</span>';
+ } else {
+ $s .= $this->skin->commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
+ }
}
}
wfProfileIn( $fname );
# Extract DB fields into local scope
- // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rc->mAttribs );
# Should patrol-related stuff be shown?
$s .= '<li>';
- // moved pages
+ // Moved pages
if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
$this->insertMove( $s, $rc );
- // log entries
- } elseif ( $rc_namespace == NS_SPECIAL ) {
+ // Log entries (old format) or log targets, and special pages
+ } elseif( $rc_namespace == NS_SPECIAL ) {
list( $specialName, $specialSubpage ) = SpecialPage::resolveAliasWithSubpage( $rc_title );
if ( $specialName == 'Log' ) {
$this->insertLog( $s, $rc->getTitle(), $specialSubpage );
} else {
wfDebug( "Unexpected special page in recentchanges\n" );
}
- // all other stuff
- } else {
+ // Log entries
+ } elseif( $rc_log_type !='' ) {
+ $logtitle = Title::newFromText( "Log/$rc_log_type", NS_SPECIAL );
+ $this->insertLog( $s, $logtitle, $rc_log_type );
+ // All other stuff
+ } else {
wfProfileIn($fname.'-page');
$this->insertDiffHist($s, $rc, $unpatrolled);
}
$this->insertUserRelatedLinks($s,$rc);
+ $this->insertAction($s, $rc);
$this->insertComment($s, $rc);
-
- $s .= rtrim(' ' . $this->numberofWatchingusers($rc->numberofWatchingusers));
+
+ # Mark revision as deleted
+ if ( $this->isDeleted($rc,Revision::DELETED_TEXT) )
+ $s .= ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
+ if($rc->numberofWatchingusers > 0) {
+ $s .= ' ' . wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($rc->numberofWatchingusers));
+ }
$s .= "</li>\n";
$rc = RCCacheEntry::newFromParent( $baseRC );
# Extract fields from DB into the function scope (rc_xxxx variables)
- // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rc->mAttribs );
$curIdEq = 'curid=' . $rc_cur_id;
$rc->unpatrolled = false;
}
+ $showrev=true;
# Make article link
if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
$msg = ( $rc_type == RC_MOVE ) ? "1movedto2" : "1movedto2_redir";
$clink = wfMsg( $msg, $this->skin->makeKnownLinkObj( $rc->getTitle(), '', 'redirect=no' ),
$this->skin->makeKnownLinkObj( $rc->getMovedToTitle(), '' ) );
- } elseif( $rc_namespace == NS_SPECIAL ) {
+ } else if( $rc_namespace == NS_SPECIAL ) {
+ // Log entries (old format) and special pages
list( $specialName, $logtype ) = SpecialPage::resolveAliasWithSubpage( $rc_title );
if ( $specialName == 'Log' ) {
# Log updates, etc
wfDebug( "Unexpected special page in recentchanges\n" );
$clink = '';
}
- } elseif( $rc->unpatrolled && $rc_type == RC_NEW ) {
+ } elseif ( $rc_log_type !='' ) {
+ // Log entries
+ $logtitle = Title::newFromText( "Log/$rc_log_type", NS_SPECIAL );
+ $logname = LogPage::logName( $rc_log_type );
+ $clink = '(' . $this->skin->makeKnownLinkObj($logtitle, $logname ) . ')';
+ } if ( $this->isDeleted($rc,Revision::DELETED_TEXT) ) {
+ $clink = '<span class="history-deleted">' . $this->skin->makeKnownLinkObj( $rc->getTitle(), '' ) . '</span>';
+ if ( !ChangesList::userCan($rc,Revision::DELETED_TEXT) )
+ $showrev=false;
+ } else if( $rc->unpatrolled && $rc_type == RC_NEW ) {
# Unpatrolled new page, give rc_id in query
$clink = $this->skin->makeKnownLinkObj( $rc->getTitle(), '', "rcid={$rc_id}" );
} else {
$querydiff = $curIdEq."&diff=$rc_this_oldid&oldid=$rc_last_oldid$rcIdQuery";
$aprops = ' tabindex="'.$baseRC->counter.'"';
$curLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['cur'], $querycur, '' ,'', $aprops );
- if( $rc_type == RC_NEW || $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
+ if ( !$showrev ) {
+ $curLink = $this->message['cur'];
+ $diffLink = $this->message['diff'];
+ } else if( $rc_type == RC_NEW || $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
if( $rc_type != RC_NEW ) {
$curLink = $this->message['cur'];
}
}
# Make "last" link
- if( $rc_last_oldid == 0 || $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
+ if ( !$showrev ) {
+ $lastLink = $this->message['last'];
+ } else if( $rc_last_oldid == 0 || $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
$lastLink = $this->message['last'];
} else {
$lastLink = $this->skin->makeKnownLinkObj( $rc->getTitle(), $this->message['last'],
- $curIdEq.'&diff='.$rc_this_oldid.'&oldid='.$rc_last_oldid . $rcIdQuery );
+ $curIdEq.'&diff='.$rc_this_oldid.'&oldid='.$rc_last_oldid . $rcIdQuery );
+ }
+
+ # Make user links
+ if ( $this->isDeleted($rc,Revision::DELETED_USER) ) {
+ $rc->userlink = ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-user') . '</span>';
+ } else {
+ $rc->userlink = $this->skin->userLink( $rc_user, $rc_user_text );
+ $rc->usertalklink = $this->skin->userToolLinks( $rc_user, $rc_user_text );
}
-
- $rc->userlink = $this->skin->userLink( $rc_user, $rc_user_text );
$rc->lastlink = $lastLink;
$rc->curlink = $curLink;
$rc->difflink = $diffLink;
- $rc->usertalklink = $this->skin->userToolLinks( $rc_user, $rc_user_text );
-
# Put accumulated information into the cache, for later display
# Page moves go on their own line
$title = $rc->getTitle();
*/
function recentChangesBlockGroup( $block ) {
global $wgLang, $wgContLang, $wgRCShowChangedSize;
- $r = '';
+ $r = '<table cellpadding="0" cellspacing="0"><tr>';
# Collate list of users
$isnew = false;
+ $namehidden = true;
$unpatrolled = false;
$userlinks = array();
foreach( $block as $rcObj ) {
if( $rcObj->mAttribs['rc_new'] ) {
$isnew = true;
}
+ // if all log actions to this page were hidden, then don't
+ // give the name of the affected page for this block
+ if( !($rcObj->mAttribs['rc_deleted'] & LogViewer::DELETED_ACTION) ) {
+ $namehidden = false;
+ }
$u = $rcObj->userlink;
if( !isset( $userlinks[$u] ) ) {
$userlinks[$u] = 0;
$toggleLink = "javascript:toggleVisibility('$rci','$rcm','$rcl')";
$tl = '<span id="'.$rcm.'"><a href="'.$toggleLink.'">' . $this->sideArrow() . '</a></span>';
$tl .= '<span id="'.$rcl.'" style="display:none"><a href="'.$toggleLink.'">' . $this->downArrow() . '</a></span>';
- $r .= $tl;
+ $r .= '<td valign="top">'.$tl;
# Main line
- $r .= '<tt>';
- $r .= $this->recentChangesFlags( $isnew, false, $unpatrolled, ' ', $bot );
+ $r .= ' '.$this->recentChangesFlags( $isnew, false, $unpatrolled, ' ', $bot );
# Timestamp
- $r .= ' '.$block[0]->timestamp.' </tt>';
+ $r .= ' '.$block[0]->timestamp.' </td><td>';
# Article link
- $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
+ if ( $namehidden )
+ $r .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
+ else
+ $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
$r .= $wgContLang->getDirMark();
$curIdEq = 'curid=' . $block[0]->mAttribs['rc_cur_id'];
$currentRevision = $block[0]->mAttribs['rc_this_oldid'];
if( $block[0]->mAttribs['rc_type'] != RC_LOG ) {
# Changes
-
$n = count($block);
static $nchanges = array();
if ( !isset( $nchanges[$n] ) ) {
$r .= ' (';
- if( $isnew ) {
+ if( !ChangesList::userCan($rcObj,Revision::DELETED_TEXT) ) {
+ $r .= $nchanges[$n];
+ } else if( $isnew ) {
$r .= $nchanges[$n];
} else {
$r .= $this->skin->makeKnownLinkObj( $block[0]->getTitle(),
$nchanges[$n], $curIdEq."&diff=$currentRevision&oldid=$oldid" );
}
- $r .= ') . . ';
-
# Character difference
$chardiff = $rcObj->getCharacterDifference( $block[ count( $block ) - 1 ]->mAttribs['rc_old_len'],
$block[0]->mAttribs['rc_new_len'] );
if( $chardiff == '' ) {
- $r .= ' (';
+ $r .= '; ';
} else {
- $r .= ' ' . $chardiff. ' . . (';
+ $r .= '; ' . $chardiff . ' ';
}
-
# History
- $r .= $this->skin->makeKnownLinkObj( $block[0]->getTitle(),
- $this->message['history'], $curIdEq.'&action=history' );
+ $r .= $this->skin->makeKnownLinkObj( $block[0]->getTitle(), $this->message['history'], $curIdEq.'&action=history' );
+
$r .= ')';
}
$r .= $users;
- $r .= $this->numberofWatchingusers($block[0]->numberofWatchingusers);
- $r .= "<br />\n";
+ if($block[0]->numberofWatchingusers > 0) {
+ global $wgContLang;
+ $r .= wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($block[0]->numberofWatchingusers));
+ }
+ $r .= "</td></tr></table>\n";
# Sub-entries
- $r .= '<div id="'.$rci.'" style="display:none">';
+ $r .= '<div id="'.$rci.'" style="display:none; font-size:95%;"><table cellpadding="0" cellspacing="0">';
foreach( $block as $rcObj ) {
# Get rc_xxxx variables
- // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rcObj->mAttribs );
- $r .= $this->spacerArrow();
- $r .= '<tt> ';
+ #$r .= '<tr><td valign="top">'.$this->spacerArrow();
+ $r .= '<tr><td valign="top">'.$this->spacerIndent();
+ $r .= ' ';
$r .= $this->recentChangesFlags( $rc_new, $rc_minor, $rcObj->unpatrolled, ' ', $rc_bot );
- $r .= ' </tt>';
+ $r .= ' </td><td valign="top">';
$o = '';
if( $rc_this_oldid != 0 ) {
$o = 'oldid='.$rc_this_oldid;
}
+ # Revision link
if( $rc_type == RC_LOG ) {
- $link = $rcObj->timestamp;
+ $link = $rcObj->timestamp.' ';
+ } else if( !ChangesList::userCan($rcObj,Revision::DELETED_TEXT) ) {
+ $link = '<span class="history-deleted">'.$rcObj->timestamp.'</span> ';
} else {
$link = $this->skin->makeKnownLinkObj( $rcObj->getTitle(), $rcObj->timestamp, $curIdEq.'&'.$o );
+ if( $this->isDeleted($rcObj,Revision::DELETED_TEXT) )
+ $link = '<span class="history-deleted">'.$link.'</span> ';
}
- $link = '<tt>'.$link.'</tt>';
-
$r .= $link;
- $r .= ' (';
- $r .= $rcObj->curlink;
- $r .= '; ';
- $r .= $rcObj->lastlink;
- $r .= ') . . ';
+
+ if ( !$rc_log_type ) {
+ $r .= ' (';
+ $r .= $rcObj->curlink;
+ $r .= '; ';
+ $r .= $rcObj->lastlink;
+ $r .= ')';
+ } else {
+ $logname = LogPage::logName( $rc_log_type );
+ $logtitle = Title::newFromText( "Log/$rc_log_type", NS_SPECIAL );
+ $r .= '(' . $this->skin->makeKnownLinkObj($logtitle, $logname ) . ')';
+ }
+ $r .= ' . . ';
# Character diff
if( $wgRCShowChangedSize ) {
$r .= ( $rcObj->getCharacterDifference() == '' ? '' : $rcObj->getCharacterDifference() . ' . . ' ) ;
}
-
+ # User links
$r .= $rcObj->userlink;
$r .= $rcObj->usertalklink;
- $r .= $this->skin->commentBlock( $rc_comment, $rcObj->getTitle() );
- $r .= "<br />\n";
+ // log action
+ parent::insertAction($r, $rcObj);
+ // log comment
+ parent::insertComment($r, $rcObj);
+
+ $r .= "</td></tr>\n";
}
- $r .= "</div>\n";
+ $r .= "</table></div>\n";
$this->rcCacheIndex++;
return $r;
* @access private
*/
function spacerArrow() {
+ //FIXME: problems with FF 1.5x
return $this->arrow( '', ' ' );
}
+
+ /**
+ * Generate HTML for the equivilant of a spacer image for tables
+ * @return string HTML <td> tag
+ * @access private
+ */
+ function spacerColumn() {
+ return '<td width="12"></td>';
+ }
+
+ // Adds a few spaces
+ function spacerIndent() {
+ return ' ';
+ }
/**
* Enhanced RC ungrouped line.
global $wgContLang, $wgRCShowChangedSize;
# Get rc_xxxx variables
- // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( $rcObj->mAttribs );
$curIdEq = 'curid='.$rc_cur_id;
- $r = '';
-
- # Spacer image
- $r .= $this->spacerArrow();
+ $r = '<table cellspacing="0" cellpadding="0"><tr><td>';
+ # spacerArrow() causes issues in FF
+ $r .= $this->spacerColumn();
+ $r .= '<td valign="top">';
+
# Flag and Timestamp
- $r .= '<tt>';
-
if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
- $r .= ' ';
+ $r .= ' ';
} else {
- $r .= $this->recentChangesFlags( $rc_type == RC_NEW, $rc_minor, $rcObj->unpatrolled, ' ', $rc_bot );
+ $r .= ' '.$this->recentChangesFlags( $rc_type == RC_NEW, $rc_minor, $rcObj->unpatrolled, ' ', $rc_bot );
}
- $r .= ' '.$rcObj->timestamp.' </tt>';
-
+ $r .= ' '.$rcObj->timestamp.' </td><td>';
+
# Article link
- $r .= $this->maybeWatchedLink( $rcObj->link, $rcObj->watched );
-
- # Diff
- $r .= ' ('. $rcObj->difflink .'; ';
-
- # Hist
- $r .= $this->skin->makeKnownLinkObj( $rcObj->getTitle(), wfMsg( 'hist' ), $curIdEq.'&action=history' ) . ') . . ';
-
+ if ( $rc_log_type !='' ) {
+ $logtitle = Title::newFromText( "Log/$rc_log_type", NS_SPECIAL );
+ $logname = LogPage::logName( $rc_log_type );
+ $r .= '(' . $this->skin->makeKnownLinkObj($logtitle, $logname ) . ')';
+ // All other stuff
+ } else {
+ $r .= $this->maybeWatchedLink( $rcObj->link, $rcObj->watched );
+ }
+ if ( $rc_type != RC_LOG ) {
+ # Diff
+ $r .= ' ('. $rcObj->difflink .'; ';
+ # Hist
+ $r .= $this->skin->makeKnownLinkObj( $rcObj->getTitle(), wfMsg( 'hist' ), $curIdEq.'&action=history' ) . ')';
+ }
+ $r .= ' . . ';
+
# Character diff
if( $wgRCShowChangedSize ) {
$r .= ( $rcObj->getCharacterDifference() == '' ? '' : ' ' . $rcObj->getCharacterDifference() . ' . . ' ) ;
}
# User/talk
- $r .= $rcObj->userlink . $rcObj->usertalklink;
+ $r .= ' '.$rcObj->userlink . $rcObj->usertalklink;
# Comment
if( $rc_type != RC_MOVE && $rc_type != RC_MOVE_OVER_REDIRECT ) {
- $r .= $this->skin->commentBlock( $rc_comment, $rcObj->getTitle() );
+ // log action
+ if ( $this->isDeleted($rcObj,LogViewer::DELETED_ACTION) ) {
+ $r .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
+ } else {
+ $r .= ' ' . LogPage::actionText( $rc_log_type, $rc_log_action, $rcObj->getTitle(), $this->skin, LogPage::extractParams($rc_params), true, true );
+ }
+ // log comment
+ if ( $this->isDeleted($rcObj,LogViewer::DELETED_COMMENT) ) {
+ $r .= ' <span class="history-deleted">' . wfMsg('rev-deleted-comment') . '</span>';
+ } else {
+ $r .= $this->skin->commentBlock( $rc_comment, $rcObj->getTitle() );
+ }
}
- $r .= $this->numberofWatchingusers($rcObj->numberofWatchingusers);
+ if( $rcObj->numberofWatchingusers > 0 ) {
+ $r .= wfMsg('number_of_watching_users_RCview', $wgContLang->formatNum($rcObj->numberofWatchingusers));
+ }
- $r .= "<br />\n";
+ $r .= "</td></tr></table>\n";
return $r;
}
* is writable to the web server but is not exposed to the internet.
*
* Set $wgSaveDeletedFiles to true and set up the save path in
- * $wgFileStore['deleted']['directory'].
+ * $wgFileStore['deleted']['directory']
*/
$wgSaveDeletedFiles = false;
$wgGroupPermissions['sysop']['block'] = true;
$wgGroupPermissions['sysop']['createaccount'] = true;
$wgGroupPermissions['sysop']['delete'] = true;
+$wgGroupPermissions['sysop']['browsearchive'] = true; // can see the deleted page list
$wgGroupPermissions['sysop']['deletedhistory'] = true; // can view deleted history entries, but not see or restore the text
$wgGroupPermissions['sysop']['editinterface'] = true;
$wgGroupPermissions['sysop']['import'] = true;
// Permission to change users' group assignments
$wgGroupPermissions['bureaucrat']['userrights'] = true;
-// Experimental permissions, not ready for production use
+// Experimental permissions to enable revisiondelete:
+
//$wgGroupPermissions['sysop']['deleterevision'] = true;
+//$wgGroupPermissions['sysop']['hideuser'] = true;
+// To see hidden revs
//$wgGroupPermissions['bureaucrat']['hiderevision'] = true;
+// For private log access
+//$wgGroupPermissions['bureaucrat']['oversight'] = true;
+// Also, we may want titles to be effectively hidden
+//$wgGroupPermissions['sysop']['browsearchive'] = false;
+//$wgGroupPermissions['bureaucrat']['browsearchive'] = true;
/**
* The developer group is deprecated, but can be activated if need be
'move',
'import',
'patrol',
+ 'oversight',
+);
+
+/**
+ * This restricts log access to those who have a certain right
+ * Users without this will not see it in the option menu and can not view it
+ * Restricted logs are not added to recent changes
+ * Logs should remain non-transcludable
+ */
+$wgLogRestrictions = array(
+'oversight' => 'oversight'
);
/**
'move' => 'movelogpage',
'import' => 'importlogpage',
'patrol' => 'patrol-log-page',
+ 'oversight' => 'oversightlog',
);
/**
'move' => 'movelogpagetext',
'import' => 'importlogpagetext',
'patrol' => 'patrol-log-header',
+ 'oversight' => 'overlogpagetext',
);
/**
'delete/delete' => 'deletedarticle',
'delete/restore' => 'undeletedarticle',
'delete/revision' => 'revdelete-logentry',
+ 'delete/event' => 'logdelete-logentry',
'upload/upload' => 'uploadedimage',
'upload/revert' => 'uploadedimage',
'move/move' => '1movedto2',
'move/move_redir' => '1movedto2_redir',
'import/upload' => 'import-logentry-upload',
'import/interwiki' => 'import-logentry-interwiki',
+ 'oversight/revision' => 'revdelete-logentry',
+ 'oversight/file' => 'revdelete-logentry',
+ 'oversight/event' => 'logdelete-logentry',
+ 'oversight/delete' => 'deletedarticle',
+ 'oversight/block' => 'blocklogentry',
);
/**
$newminor = wfElement( 'span', array( 'class' => 'minor' ),
wfMsg( 'minoreditletter') ) . ' ';
}
+
+ $rdel = ''; $ldel = '';
+ if( $wgUser->isAllowed( 'deleterevision' ) ) {
+ $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+ if( !$this->mOldRev->userCan( Revision::DELETED_RESTRICTED ) ) {
+ // If revision was hidden from sysops
+ $ldel = wfMsgHtml('rev-delundel');
+ } else {
+ $ldel = $sk->makeKnownLinkObj( $revdel,
+ wfMsgHtml('rev-delundel'),
+ 'target=' . urlencode( $this->mOldRev->mTitle->getPrefixedDbkey() ) .
+ '&oldid=' . urlencode( $this->mOldRev->getId() ) );
+ // Bolden oversighted content
+ if( $this->mOldRev->isDeleted( Revision::DELETED_RESTRICTED ) )
+ $ldel = "<strong>$ldel</strong>";
+ }
+ $ldel = " <tt>(<small>$ldel</small>)</tt> ";
+ // We don't currently handle well changing the top revision's settings
+ if( $this->mNewRev->isCurrent() ) {
+ // If revision was hidden from sysops
+ $rdel = wfMsgHtml('rev-delundel');
+ } else if( !$this->mNewRev->userCan( Revision::DELETED_RESTRICTED ) ) {
+ // If revision was hidden from sysops
+ $rdel = wfMsgHtml('rev-delundel');
+ } else {
+ $rdel = $sk->makeKnownLinkObj( $revdel,
+ wfMsgHtml('rev-delundel'),
+ 'target=' . urlencode( $this->mNewRev->mTitle->getPrefixedDbkey() ) .
+ '&oldid=' . urlencode( $this->mNewRev->getId() ) );
+ // Bolden oversighted content
+ if( $this->mNewRev->isDeleted( Revision::DELETED_RESTRICTED ) )
+ $rdel = "<strong>$rdel</strong>";
+ }
+ $rdel = " <tt>(<small>$rdel</small>)</tt> ";
+ }
$oldHeader = "<strong>{$this->mOldtitle}</strong><br />" .
- $sk->revUserTools( $this->mOldRev ) . "<br />" .
- $oldminor . $sk->revComment( $this->mOldRev, !$diffOnly ) . "<br />" .
+ $sk->revUserTools( $this->mOldRev, true ) . "<br />" .
+ $oldminor . $sk->revComment( $this->mOldRev, !$diffOnly, true ) . "$ldel<br />" .
$prevlink;
$newHeader = "<strong>{$this->mNewtitle}</strong><br />" .
- $sk->revUserTools( $this->mNewRev ) . " $rollback<br />" .
- $newminor . $sk->revComment( $this->mNewRev, !$diffOnly ) . "<br />" .
+ $sk->revUserTools( $this->mNewRev, true ) . " $rollback<br />" .
+ $newminor . $sk->revComment( $this->mNewRev, !$diffOnly, true ) . "$rdel<br />" .
$nextlink . $patrol;
$this->showDiff( $oldHeader, $newHeader );
$wgOut->addHTML( "<hr /><h2>{$this->mPagetitle}</h2>\n" );
#add deleted rev tag if needed
- if ( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
+ if( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
$wgOut->addWikiText( wfMsg( 'rev-deleted-text-permission' ) );
+ } else if( $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
+ $wgOut->addWikiText( wfMsg( 'rev-deleted-text-view' ) );
}
if( !$this->mNewRev->isCurrent() ) {
}
}
- #loadtext is permission safe, this just clears out the diff
+ // Loadtext is permission safe, this just clears out the diff
if ( !$this->loadText() ) {
wfProfileOut( $fname );
return false;
} else if ( $this->mOldRev && !$this->mOldRev->userCan(Revision::DELETED_TEXT) ) {
- return '';
+ return '';
} else if ( $this->mNewRev && !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
- return '';
+ return '';
}
$difftext = $this->generateDiffBody( $this->mOldtext, $this->mNewtext );
// Save to cache for 7 days
- if ( $key !== false && $difftext !== false ) {
+ // Only do this for public revs, otherwise an admin can view the diff and a non-admin can nab it!
+ if ( $this->mOldRev && $this->mOldRev->isDeleted(Revision::DELETED_TEXT) ) {
+ wfIncrStats( 'diff_uncacheable' );
+ } else if ( $this->mNewRev && $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
+ wfIncrStats( 'diff_uncacheable' );
+ } else if ( $key !== false && $difftext !== false ) {
wfIncrStats( 'diff_cache_miss' );
$wgMemc->set( $key, $difftext, 7*86400 );
} else {
*/
function addHeader( $diff, $otitle, $ntitle, $multi = '' ) {
global $wgOut;
-
- if ( $this->mOldRev && $this->mOldRev->isDeleted(Revision::DELETED_TEXT) ) {
- $otitle = '<span class="history-deleted">'.$otitle.'</span>';
- }
- if ( $this->mNewRev && $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
- $ntitle = '<span class="history-deleted">'.$ntitle.'</span>';
- }
+
$header = "
<table border='0' width='98%' cellpadding='0' cellspacing='4' class='diff'>
<tr>
$this->mNewtitle = "<a href='$newLink'>{$this->mPagetitle}</a>"
. " (<a href='$newEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
}
+ if ( !$this->mNewRev->userCan(Revision::DELETED_TEXT) ) {
+ $this->mNewtitle = "<span class='history-deleted'>{$this->mPagetitle}</span>";
+ } else if ( $this->mNewRev->isDeleted(Revision::DELETED_TEXT) ) {
+ $this->mNewtitle = '<span class="history-deleted">'.$this->mNewtitle.'</span>';
+ }
// Load the old revision object
$this->mOldRev = false;
$t = $wgLang->timeanddate( $this->mOldRev->getTimestamp(), true );
$oldLink = $this->mOldPage->escapeLocalUrl( 'oldid=' . $this->mOldid );
$oldEdit = $this->mOldPage->escapeLocalUrl( 'action=edit&oldid=' . $this->mOldid );
- $this->mOldtitle = "<a href='$oldLink'>" . htmlspecialchars( wfMsg( 'revisionasof', $t ) )
- . "</a> (<a href='$oldEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
- //now that we considered old rev, we can make undo link (bug 8133, multi-edit undo)
+ $this->mOldPagetitle = htmlspecialchars( wfMsg( 'revisionasof', $t ) );
+
+ $this->mOldtitle = "<a href='$oldLink'>{$this->mOldPagetitle}</a>"
+ . "(<a href='$oldEdit'>" . htmlspecialchars( wfMsg( 'editold' ) ) . "</a>)";
+ // Now that we considered old rev, we can make undo link (bug 8133, multi-edit undo)
$newUndo = $this->mNewPage->escapeLocalUrl( 'action=edit&undoafter=' . $this->mOldid . '&undoto=' . $this->mNewid);
- $this->mNewtitle .= " (<a href='$newUndo'>" . htmlspecialchars( wfMsg( 'editundo' ) ) . "</a>)";
+ if ( $this->mNewRev->userCan(Revision::DELETED_TEXT) )
+ $this->mNewtitle .= " (<a href='$newUndo'>" . htmlspecialchars( wfMsg( 'editundo' ) ) . "</a>)";
+
+ if ( !$this->mOldRev->userCan(Revision::DELETED_TEXT) ) {
+ $this->mOldtitle = "<span class='history-deleted'>{$this->mOldPagetitle}</span>";
+ } else if ( $this->mOldRev->isDeleted(Revision::DELETED_TEXT) ) {
+ $this->mOldtitle = '<span class="history-deleted">'.$this->mOldtitle.'</span>';
+ }
}
return true;
return false;
}
if ( $this->mOldRev ) {
- // FIXME: permission tests
$this->mOldtext = $this->mOldRev->revText();
if ( $this->mOldtext === false ) {
return false;
$fname = "do_list_authors" ;
wfProfileIn( $fname );
$this->author_list = "<contributors>";
- $sql = "SELECT DISTINCT rev_user_text,rev_user FROM {$page},{$revision} WHERE page_id=rev_page AND " . $cond ;
+ //rev_deleted
+ $deleted = '(rev_deleted & '.Revision::DELETED_USER.') !=1 ';
+
+ $sql = "SELECT DISTINCT rev_user_text,rev_user FROM {$page},{$revision} WHERE page_id=rev_page AND $deleted AND " . $cond ;
$result = $this->db->query( $sql, $fname );
$resultset = $this->db->resultObject( $result );
while( $row = $resultset->fetchObject() ) {
$reason = $wgRequest->getVal( 'wpReason' );
$image = $wgRequest->getVal( 'image' );
$oldimage = $wgRequest->getVal( 'oldimage' );
-
+ # Flag to hide all contents of the archived revisions
+ $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed('deleterevision');
+
# Only sysops can delete images. Previously ordinary users could delete
# old revisions, but this is no longer the case.
if ( !$wgUser->isAllowed('delete') ) {
# Deleting old images doesn't require confirmation
if ( !is_null( $oldimage ) || $confirm ) {
if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) {
- $this->doDelete( $reason );
+ $this->doDelete( $reason, $suppress );
} else {
$wgOut->showFatalError( wfMsg( 'sessionfailure' ) );
}
* Delete an image.
* @param $reason User provided reason for deletion.
*/
- function doDelete( $reason ) {
+ function doDelete( $reason, $suppress=false ) {
global $wgOut, $wgRequest;
$oldimage = $wgRequest->getVal( 'oldimage' );
$wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
return;
}
- if ( !$this->doDeleteOldImage( $oldimage ) ) {
+ if ( !$this->doDeleteOldImage( $oldimage, $suppress ) ) {
return;
}
$deleted = $oldimage;
} else {
- $ok = $this->img->delete( $reason );
+ $ok = $this->img->delete( $reason, $suppress );
if( !$ok ) {
# If the deletion operation actually failed, bug out:
$wgOut->showFileDeleteError( $this->img->getName() );
# Now we remove the image description page.
$article = new Article( $this->mTitle );
- $article->doDeleteArticle( $reason ); # ignore errors
+ $article->doDeleteArticle( $reason, $suppress ); # ignore errors
$deleted = $this->img->getName();
}
/**
* @return success
*/
- function doDeleteOldImage( $oldimage )
+ function doDeleteOldImage( $oldimage, $suppress=false )
{
global $wgOut;
- $ok = $this->img->deleteOld( $oldimage, '' );
+ $ok = $this->img->deleteOld( $oldimage, '', $suppress );
if( !$ok ) {
# If we actually have a file and can't delete it, throw an error.
# Something went awry...
/**
* Generate a user link if the current user is allowed to view it
* @param $rev Revision object.
+ * @param $isPublic, bool, show only if all users can see it
* @return string HTML
*/
- function revUserLink( $rev ) {
- if( $rev->userCan( Revision::DELETED_USER ) ) {
+ function revUserLink( $rev, $isPublic = false ) {
+ if( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
+ $link = wfMsgHtml( 'rev-deleted-user' );
+ } else if( $rev->userCan( Revision::DELETED_USER ) ) {
$link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() );
} else {
$link = wfMsgHtml( 'rev-deleted-user' );
}
return $link;
}
+
+ /**
+ * Generate a user link if the current user is allowed to view it
+ * @param $event, log item.
+ * @param $isPublic, bool, show only if all users can see it
+ * @return string HTML
+ */
+ function logUserLink( $event, $isPublic = false ) {
+ if( LogViewer::isDeleted( $event, LogViewer::DELETED_USER ) && $isPublic ) {
+ $link = wfMsgHtml( 'rev-deleted-user' );
+ } else if( LogViewer::userCan( $event, LogViewer::DELETED_USER ) ) {
+ if ( isset($event->user_name) ) {
+ $link = $this->userLink( $event->log_user, $event->user_name );
+ } else {
+ $user = $event->log_user;
+ $link = $this->userLink( $event->log_user, User::whoIs( $user ) );
+ }
+ } else {
+ $link = wfMsgHtml( 'rev-deleted-user' );
+ }
+ if( LogViewer::isDeleted( $event, LogViewer::DELETED_USER ) ) {
+ return '<span class="history-deleted">' . $link . '</span>';
+ }
+ return $link;
+ }
+
+ /**
+ * Generate a user link if the current user is allowed to view it
+ * @param $file, filestore file
+ * @param $isPublic, bool, show only if all users can see it
+ * @return string HTML
+ */
+ function fileUserLink( $file, $isPublic = false ) {
+ if( $file->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
+ $link = wfMsgHtml( 'rev-deleted-user' );
+ } else if( $file->userCan( Revision::DELETED_USER ) ) {
+ $link = $this->userLink( $file->mUser, $file->mUserText );
+ } else {
+ $link = wfMsgHtml( 'rev-deleted-user' );
+ }
+ if( $file->isDeleted( Revision::DELETED_USER ) ) {
+ return '<span class="history-deleted">' . $link . '</span>';
+ }
+ return $link;
+ }
/**
* Generate a user tool link cluster if the current user is allowed to view it
* @param $rev Revision object.
+ * @param $isPublic, bool, show only if all users can see it
* @return string HTML
*/
- function revUserTools( $rev ) {
- if( $rev->userCan( Revision::DELETED_USER ) ) {
+ function revUserTools( $rev, $isPublic = false ) {
+ if( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
+ $link = wfMsgHtml( 'rev-deleted-user' );
+ } else if( $rev->userCan( Revision::DELETED_USER ) ) {
$link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() ) .
- ' ' .
- $this->userToolLinks( $rev->getRawUser(), $rev->getRawUserText() );
+ ' ' . $this->userToolLinks( $rev->getRawUser(), $rev->getRawUserText() );
} else {
$link = wfMsgHtml( 'rev-deleted-user' );
}
if( $rev->isDeleted( Revision::DELETED_USER ) ) {
+ return ' <span class="history-deleted">' . $link . '</span>';
+ }
+ return " $link";
+ }
+
+ /**
+ * Generate a user tool link cluster if the current user is allowed to view it
+ * @param $event, log item.
+ * @param $isPublic, bool, show only if all users can see it
+ * @return string HTML
+ */
+ function logUserTools( $event, $isPublic = false ) {
+ if( LogViewer::isDeleted( $event, LogViewer::DELETED_USER ) && $isPublic ) {
+ $link = wfMsgHtml( 'rev-deleted-user' );
+ } else if( LogViewer::userCan( $event, LogViewer::DELETED_USER ) ) {
+ if ( isset($event->user_name) ) {
+ $link = $this->userLink( $event->log_user, $event->user_name ) .
+ ' ' . $this->userToolLinks( $event->log_user, $event->user_name );
+ } else {
+ $usertext = User::whoIs( $event->log_user );
+ $link = $this->userLink( $event->log_user, $usertext ) .
+ ' ' . $this->userToolLinks( $event->log_user, $usertext );
+ }
+ } else {
+ $link = wfMsgHtml( 'rev-deleted-user' );
+ }
+ if( LogViewer::isDeleted( $event, LogViewer::DELETED_USER ) ) {
return '<span class="history-deleted">' . $link . '</span>';
}
return $link;
}
-
+
+ /**
+ * Generate a user tool link cluster if the current user is allowed to view it
+ * @param $file, filestore file
+ * @param $isPublic, bool, show only if all users can see it
+ * @return string HTML
+ */
+ function fileUserTools( $file, $isPublic = false ) {
+ if( $file->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
+ $link = wfMsgHtml( 'rev-deleted-user' );
+ } else if( $file->userCan( Revision::DELETED_USER ) ) {
+ $link = $this->userLink( $file->mUser, $file->mUserText ) .
+ $this->userToolLinks( $file->mUser, $file->mUserText );
+ } else {
+ $link = wfMsgHtml( 'rev-deleted-user' );
+ }
+ if( $file->isDeleted( Revision::DELETED_USER ) ) {
+ return '<span class="history-deleted">' . $link . '</span>';
+ }
+ return $link;
+ }
+
/**
* This function is called by all recent changes variants, by the page history,
* and by the user contributions list. It is responsible for formatting edit
*
* @param Revision $rev
* @param bool $local Whether section links should refer to local page
+ * @param $isPublic, show only if all users can see it
* @return string HTML
*/
- function revComment( Revision $rev, $local = false ) {
- if( $rev->userCan( Revision::DELETED_COMMENT ) ) {
+ function revComment( Revision $rev, $local = false, $isPublic = false ) {
+ if( $rev->isDeleted( Revision::DELETED_COMMENT ) && $isPublic ) {
+ $block = " <span class=\"comment\">" . wfMsgHtml( 'rev-deleted-comment' ) . "</span>";
+ } else if( $rev->userCan( Revision::DELETED_COMMENT ) ) {
$block = $this->commentBlock( $rev->getRawComment(), $rev->getTitle(), $local );
} else {
- $block = " <span class=\"comment\">" .
- wfMsgHtml( 'rev-deleted-comment' ) . "</span>";
+ $block = " <span class=\"comment\">" . wfMsgHtml( 'rev-deleted-comment' ) . "</span>";
}
if( $rev->isDeleted( Revision::DELETED_COMMENT ) ) {
return " <span class=\"history-deleted\">$block</span>";
}
return $block;
}
+
+ /**
+ * Wrap and format the given event's comment block, if the current
+ * user is allowed to view it.
+ *
+ * @param Revision $rev
+ * @return string HTML
+ */
+ function logComment( $event, $isPublic = false ) {
+ if( LogViewer::isDeleted( $event, LogViewer::DELETED_COMMENT ) && $isPublic ) {
+ $block = ' ' . wfMsgHtml( 'rev-deleted-comment' );
+ } else if( LogViewer::userCan( $event, LogViewer::DELETED_COMMENT ) ) {
+ $block = $this->commentBlock( LogViewer::getRawComment( $event ) );
+ } else {
+ $block = ' ' . wfMsgHtml( 'rev-deleted-comment' );
+ }
+ if( LogViewer::isDeleted( $event, LogViewer::DELETED_COMMENT ) ) {
+ return "<span class=\"history-deleted\">$block</span>";
+ }
+ return $block;
+ }
+
+ /**
+ * Wrap and format the given file's comment block, if the current
+ * user is allowed to view it.
+ *
+ * @param FileStore file object $file
+ * @return string HTML
+ */
+ function fileComment( $file, $isPublic = false ) {
+ if( $file->isDeleted( Revision::DELETED_COMMENT ) && $isPublic ) {
+ $block = ' ' . wfMsgHtml( 'rev-deleted-comment' );
+ } else if( $file->userCan( Revision::DELETED_COMMENT ) ) {
+ $block = $this->commentBlock( $file->mDescription );
+ } else {
+ $block = ' ' . wfMsgHtml( 'rev-deleted-comment' );
+ }
+ if( $file->isDeleted( Revision::DELETED_COMMENT ) ) {
+ return "<span class=\"history-deleted\">$block</span>";
+ }
+ return $block;
+ }
/** @todo document */
function tocIndent() {
function saveContent() {
if( wfReadOnly() ) return false;
- global $wgUser;
+ global $wgUser, $wgLogRestrictions;
$fname = 'LogPage::saveContent';
$dbw = wfGetDB( DB_MASTER );
$uid = $wgUser->getID();
$this->timestamp = $now = wfTimestampNow();
+
+ $log_id = $dbw->nextSequenceValue( 'log_log_id_seq' );
$dbw->insert( 'logging',
array(
'log_type' => $this->type,
'log_params' => $this->params
), $fname
);
+ $newId = $dbw->insertId();
# And update recentchanges
if ( $this->updateRecentChanges ) {
- $titleObj = SpecialPage::getTitleFor( 'Log', $this->type );
- $rcComment = $this->actionText;
- if( '' != $this->comment ) {
- if ($rcComment == '')
- $rcComment = $this->comment;
- else
- $rcComment .= ': ' . $this->comment;
+ # Don't add private logs to RC!!!
+ if ( !isset($wgLogRestrictions[$this->type]) || $wgLogRestrictions[$this->type]=='*' ) {
+ RecentChange::notifyLog( $now, $this->target, $wgUser, $this->actionText, '',
+ $this->type, $this->action, $this->target, $this->comment, $this->params, $newId );
}
-
- RecentChange::notifyLog( $now, $titleObj, $wgUser, $rcComment, '',
- $this->type, $this->action, $this->target, $this->comment, $this->params );
}
return true;
}
*/
function logHeader( $type ) {
global $wgLogHeaders;
- return wfMsg( $wgLogHeaders[$type] );
+ return wfMsgHtml( $wgLogHeaders[$type] );
}
/**
* @static
*/
- function actionText( $type, $action, $title = NULL, $skin = NULL, $params = array(), $filterWikilinks=false, $translate=false ) {
+ function actionText( $type, $action, $title=NULL, $skin=NULL, $params = array(), $filterWikilinks=false, $translate=false, $forRC=false ) {
global $wgLang, $wgContLang, $wgLogActions;
$key = "$type/$action";
}
} else {
array_unshift( $params, $titleLink );
- if ( $translate && $key == 'block/block' ) {
+ // Oversighted blocks show as normal
+ if ( $translate && ($key == 'block/block' || $key == 'oversight/block') ) {
$params[1] = $wgLang->translateBlockExpiry( $params[1] );
$params[2] = isset( $params[2] )
? self::formatBlockFlags( $params[2] )
: '';
}
+ if ( $forRC ) {
+ $params[1] = $wgLang->translateBlockExpiry( $params[1], true );
+ $params[2] = isset( $params[2] ) ? str_replace( ",", ", ", self::formatBlockFlags( $params[2] ) ) : '';
+ }
$rv = wfMsgReal( $wgLogActions[$key], $params, true, !$skin );
}
}
$this->comment = $comment;
$this->params = LogPage::makeParamBlob( $params );
- $this->actionText = LogPage::actionText( $this->type, $action, $target, NULL, $params );
+ $this->actionText = LogPage::actionText( $this->type, $action, $target, NULL, $params, false, false, true );
return $this->saveContent();
}
$this->mTitle =& $article->mTitle;
$this->mNotificationTimestamp = NULL;
$this->mSkin = $wgUser->getSkin();
+ $this->preCacheMessages();
+ }
+
+ /**
+ * As we use the same small set of messages in various methods and that
+ * they are called often, we call them once and save them in $this->message
+ */
+ function preCacheMessages() {
+ // Precache various messages
+ if( !isset( $this->message ) ) {
+ foreach( explode(' ', 'cur last rev-delundel' ) as $msg ) {
+ $this->message[$msg] = wfMsgExt( $msg, array( 'escape') );
+ }
+ }
}
/**
$arbitrary = $this->diffButtons( $rev, $firstInList, $counter );
$link = $this->revLink( $rev );
- $user = $this->mSkin->userLink( $rev->getUser(), $rev->getUserText() )
- . $this->mSkin->userToolLinks( $rev->getUser(), $rev->getUserText() );
-
$s .= "($curlink) ($lastlink) $arbitrary";
if( $wgUser->isAllowed( 'deleterevision' ) ) {
$revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
if( $firstInList ) {
- // We don't currently handle well changing the top revision's settings
- $del = wfMsgHtml( 'rev-delundel' );
+ // We don't currently handle well changing the top revision's settings
+ $del = $this->message['rev-delundel'];
} else if( !$rev->userCan( Revision::DELETED_RESTRICTED ) ) {
// If revision was hidden from sysops
- $del = wfMsgHtml( 'rev-delundel' );
+ $del = $this->message['rev-delundel'];
} else {
$del = $this->mSkin->makeKnownLinkObj( $revdel,
- wfMsg( 'rev-delundel' ),
+ $this->message['rev-delundel'],
'target=' . urlencode( $this->mTitle->getPrefixedDbkey() ) .
'&oldid=' . urlencode( $rev->getId() ) );
+ // Bolden oversighted content
+ if( $rev->isDeleted( Revision::DELETED_RESTRICTED ) )
+ $del = "<strong>$del</strong>";
}
- $s .= " (<small>$del</small>) ";
+ $s .= " <tt>(<small>$del</small>)</tt> ";
}
$s .= " $link";
- #getUser is safe, but this avoids making the invalid untargeted contribs links
- if( $row->rev_deleted & Revision::DELETED_USER ) {
- $user = '<span class="history-deleted">' . wfMsg('rev-deleted-user') . '</span>';
- }
- $s .= " <span class='history-user'>$user</span>";
-
+ $s .= $this->mSkin->revUserTools( $rev, true);
+
if( $row->rev_minor_edit ) {
$s .= ' ' . wfElement( 'span', array( 'class' => 'minor' ), wfMsg( 'minoreditletter') );
}
}
#add blurb about text having been deleted
if( $row->rev_deleted & Revision::DELETED_TEXT ) {
- $s .= ' ' . wfMsgHtml( 'deletedrev' );
+ $s .= ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
}
if( $wgUser->isAllowed( 'rollback' ) && $latest ) {
$s .= ' '.$this->mSkin->generateRollback( $rev );
/** @todo document */
function curLink( $rev, $latest ) {
- $cur = wfMsgExt( 'cur', array( 'escape') );
+ $cur = $this->message['cur'];
if( $latest || !$rev->userCan( Revision::DELETED_TEXT ) ) {
return $cur;
} else {
/** @todo document */
function lastLink( $rev, $next, $counter ) {
- $last = wfMsgExt( 'last', array( 'escape' ) );
+ $last = $this->message['last'];
if ( is_null( $next ) ) {
# Probably no next row
return $last;
* rc_patrolled boolean whether or not someone has marked this edit as patrolled
* rc_old_len integer byte length of the text before the edit
* rc_new_len the same after the edit
+ * rc_deleted partial deletion
+ * rc_logid the log_id value for this log entry (or zero)
+ * rc_log_type the log type (or null)
+ * rc_log_action the log action (or null)
+ * rc_params log params
*
* mExtra:
* prefixedDBkey prefixed db key, used by external app via msg queue
# Makes an entry in the database corresponding to an edit
/*static*/ function notifyEdit( $timestamp, &$title, $minor, &$user, $comment,
- $oldId, $lastTimestamp, $bot = "default", $ip = '', $oldSize = 0, $newSize = 0,
- $newId = 0)
+ $oldId, $lastTimestamp, $bot="default", $ip='', $oldSize=0, $newSize=0, $newId=0)
{
if ( $bot === 'default' ) {
'rc_patrolled' => 0,
'rc_new' => 0, # obsolete
'rc_old_len' => $oldSize,
- 'rc_new_len' => $newSize
+ 'rc_new_len' => $newSize,
+ 'rc_deleted' => 0,
+ 'rc_logid' => 0,
+ 'rc_log_type' => null,
+ 'rc_log_action' => '',
+ 'rc_params' => ''
);
$rc->mExtra = array(
* @static
*/
public static function notifyNew( $timestamp, &$title, $minor, &$user, $comment, $bot = "default",
- $ip='', $size = 0, $newId = 0 )
+ $ip='', $size=0, $newId=0 )
{
if ( !$ip ) {
$ip = wfGetIP();
$ip = '';
}
}
+
if ( $bot == 'default' ) {
$bot = $user->isAllowed( 'bot' );
}
'rc_moved_to_title' => '',
'rc_ip' => $ip,
'rc_patrolled' => 0,
- 'rc_new' => 1, # obsolete
+ 'rc_new' => 1, # obsolete
'rc_old_len' => 0,
- 'rc_new_len' => $size
+ 'rc_new_len' => $size,
+ 'rc_deleted' => 0,
+ 'rc_logid' => 0,
+ 'rc_log_type' => null,
+ 'rc_log_action' => '',
+ 'rc_params' => ''
);
$rc->mExtra = array(
$ip = '';
}
}
-
+
$rc = new RecentChange;
$rc->mAttribs = array(
'rc_timestamp' => $timestamp,
'rc_patrolled' => 1,
'rc_old_len' => NULL,
'rc_new_len' => NULL,
+ 'rc_deleted' => 0,
+ 'rc_logid' => 0, # notifyMove not used anymore
+ 'rc_log_type' => null,
+ 'rc_log_action' => '',
+ 'rc_params' => ''
);
$rc->mExtra = array(
RecentChange::notifyMove( $timestamp, $oldTitle, $newTitle, $user, $comment, $ip, true );
}
- # A log entry is different to an edit in that previous revisions are
- # not kept
- /*static*/ function notifyLog( $timestamp, &$title, &$user, $comment, $ip='',
- $type, $action, $target, $logComment, $params )
+ # A log entry is different to an edit in that previous revisions are not kept
+ /*static*/ function notifyLog( $timestamp, &$title, &$user, $actionText = null, $ip='',
+ $type, $action, $target, $logComment, $params, $newId=0 )
{
if ( !$ip ) {
$ip = wfGetIP();
- if ( !$ip ) {
- $ip = '';
- }
+ if ( !$ip ) $ip = '';
}
-
$rc = new RecentChange;
$rc->mAttribs = array(
'rc_timestamp' => $timestamp,
'rc_cur_id' => $title->getArticleID(),
'rc_user' => $user->getID(),
'rc_user_text' => $user->getName(),
- 'rc_comment' => $comment,
+ 'rc_comment' => $logComment,
'rc_this_oldid' => 0,
'rc_last_oldid' => 0,
'rc_bot' => $user->isAllowed( 'bot' ) ? 1 : 0,
'rc_new' => 0, # obsolete
'rc_old_len' => NULL,
'rc_new_len' => NULL,
+ 'rc_deleted' => 0,
+ 'rc_logid' => $newId,
+ 'rc_log_type' => $type,
+ 'rc_log_action' => $action,
+ 'rc_params' => $params
);
$rc->mExtra = array(
'prefixedDBkey' => $title->getPrefixedDBkey(),
'rc_new' => $row->page_is_new, # obsolete
'rc_old_len' => $row->rc_old_len,
'rc_new_len' => $row->rc_new_len,
+ 'rc_deleted' => $row->rc_deleted,
+ 'rc_logid' => $row->rc_logid,
+ 'rc_log_type' => $row->rc_log_type,
+ 'rc_log_action' => $row->rc_log_action,
+ 'rc_params' => $row->rc_params
);
$this->mExtra = array();
var $BlockAddress, $BlockExpiry, $BlockReason;
function IPBlockForm( $par ) {
- global $wgRequest;
+ global $wgRequest, $wgUser;
$this->BlockAddress = $wgRequest->getVal( 'wpBlockAddress', $wgRequest->getVal( 'ip', $par ) );
$this->BlockAddress = strtr( $this->BlockAddress, '_', ' ' );
$this->BlockAnonOnly = $wgRequest->getBool( 'wpAnonOnly', $byDefault );
$this->BlockCreateAccount = $wgRequest->getBool( 'wpCreateAccount', $byDefault );
$this->BlockEnableAutoblock = $wgRequest->getBool( 'wpEnableAutoblock', $byDefault );
+ # Re-check user's rights to hide names, very serious, defaults to 0
+ $this->BlockHideName = $wgRequest->getBool( 'wpHideName', 0 ) && $wgUser->isAllowed( 'hideuser' );
}
function showForm( $err ) {
</td>
");
}
+
$wgOut->addHTML("
</tr>
<tr id='wpBlockOther'>
<tr id='wpAnonOnlyRow'>
<td> </td>
<td align=\"left\">
- " . wfCheckLabel( wfMsg( 'ipbanononly' ),
+ " . wfCheckLabel( wfMsgHtml( 'ipbanononly' ),
'wpAnonOnly', 'wpAnonOnly', $this->BlockAnonOnly,
- array( 'tabindex' => 4 ) ) . "
+ array( 'tabindex' => '4' ) ) . "
</td>
</tr>
<tr id='wpCreateAccountRow'>
<td> </td>
<td align=\"left\">
- " . wfCheckLabel( wfMsg( 'ipbcreateaccount' ),
+ " . wfCheckLabel( wfMsgHtml( 'ipbcreateaccount' ),
'wpCreateAccount', 'wpCreateAccount', $this->BlockCreateAccount,
- array( 'tabindex' => 5 ) ) . "
+ array( 'tabindex' => '5' ) ) . "
</td>
</tr>
<tr id='wpEnableAutoblockRow'>
<td> </td>
<td align=\"left\">
- " . wfCheckLabel( wfMsg( 'ipbenableautoblock' ),
+ " . wfCheckLabel( wfMsgHtml( 'ipbenableautoblock' ),
'wpEnableAutoblock', 'wpEnableAutoblock', $this->BlockEnableAutoblock,
- array( 'tabindex' => 6 ) ) . "
+ array( 'tabindex' => '6' ) ) . "
</td>
</tr>
+ ");
+ // Allow some users to hide name from block log, blocklist and listusers
+ if ( $wgUser->isAllowed( 'hideuser' ) ) {
+ $wgOut->addHTML("
+ <tr>
+ <td> </td>
+ <td align=\"left\">
+ " . wfCheckLabel( wfMsgHtml( 'ipbhidename' ),
+ 'wpHideName', 'wpHideName', $this->BlockHideName,
+ array( 'tabindex' => '6' ) ) . "
+ </td>
+ </tr>
+ ");
+ }
+ $wgOut->addHTML("
<tr>
<td style='padding-top: 1em'> </td>
<td style='padding-top: 1em' align=\"left\">
- " . Xml::submitButton( wfMsg( 'ipbsubmit' ),
+ " . Xml::submitButton( wfMsgHtml( 'ipbsubmit' ),
array( 'name' => 'wpBlock', 'tabindex' => '7' ) ) . "
</td>
</tr>
$this->showLogFragment( $wgOut, $user->getUserPage() );
} elseif( preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $this->BlockAddress ) ) {
$this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) );
- } elseif( preg_match( '/^\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}/', $this->BlockAddress ) ) {
- $this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) );
}
}
global $wgOut, $wgUser, $wgSysopUserBans, $wgSysopRangeBans;
$userId = 0;
- # Expand valid IPv6 addresses, usernames are left as is
- $this->BlockAddress = IP::sanitizeIP( $this->BlockAddress );
- # isIPv4() and IPv6() are used for final validation
+ $this->BlockAddress = trim( $this->BlockAddress );
+ # Expand valid IPv6 addresses
+ if ( IP::isIPv6( $this->BlockAddress ) ) {
+ $this->BlockAddress = IP::expandIP( $this->BlockAddress );
+ }
+ # The above validation is good enough that those below will suffice from here
$rxIP4 = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
$rxIP6 = '\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}';
$rxIP = "($rxIP4|$rxIP6)";
$block = new Block( $this->BlockAddress, $userId, $wgUser->getID(),
$this->BlockReason, wfTimestampNow(), 0, $expiry, $this->BlockAnonOnly,
- $this->BlockCreateAccount, $this->BlockEnableAutoblock );
+ $this->BlockCreateAccount, $this->BlockEnableAutoblock, $this->BlockHideName);
if (wfRunHooks('BlockIp', array(&$block, &$wgUser))) {
$logParams[] = $expirestr;
$logParams[] = $this->blockLogFlags();
- # Make log entry
- $log = new LogPage( 'block' );
+ # Make log entry, if the name is hidden, put it in the oversight log
+ $log_type = ($this->BlockHideName) ? 'oversight' : 'block';
+ $log = new LogPage( $log_type );
$log->addEntry( 'block', Title::makeTitle( NS_USER, $this->BlockAddress ),
$this->BlockReason, $logParams );
list( $this->limit, $this->offset ) = $request->getLimitOffset();
}
+
+ function newFromTitle( $title, $logid=0 ) {
+ $fname = 'LogReader::newFromTitle';
+
+ $matchId = intval( $logid );
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'logging', array('*'),
+ array('log_id' => $matchId, 'log_namespace' => $title->getNamespace(), 'log_title' => $title->getDBkey() ),
+ $fname );
+
+ if ( $res ) {
+ $ret = $dbr->fetchObject( $res );
+ if ( $ret ) {
+ return $ret;
+ }
+ }
+ return null;
+ }
+
+ function newFromId( $logid ) {
+ $fname = 'LogReader::newFromId';
+
+ $matchId = intval( $logid );
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'logging', array('*'), array('log_id' => $matchId ), $fname );
+
+ if ( $res ) {
+ $ret = $dbr->fetchObject( $res );
+ if ( $ret ) {
+ return $ret;
+ }
+ }
+ return null;
+ }
/**
* Set the log reader to return only entries of the given type.
+ * Type restrictions enforced here
* @param string $type A log type ('upload', 'delete', etc)
* @private
*/
function limitType( $type ) {
+ global $wgLogRestrictions, $wgUser;
+
+ // Restriction system
+ if ( isset($wgLogRestrictions) ) {
+ foreach ( $wgLogRestrictions as $logtype => $right ) {
+ if ( !$wgUser->isAllowed( $right ) ) {
+ $safetype = $this->db->strencode( $logtype );
+ $this->whereClauses[] = "log_type <> '$safetype'";
+ }
+ }
+ }
+
if( empty( $type ) ) {
return false;
}
+ // Can user see this log?
+ if ( isset($wgLogRestrictions[$type]) ) {
+ if ( !$wgUser->isAllowed( $wgLogRestrictions[$type] ) ) {
+ return false;
+ }
+ }
+
$this->type = $type;
$safetype = $this->db->strencode( $type );
$this->whereClauses[] = "log_type='$safetype'";
* @private
*/
function limitTitle( $page , $pattern ) {
- $title = Title::newFromText( $page );
+ $title = Title::newFromURL( $page, false );
if( empty( $page ) || is_null( $title ) ) {
return false;
}
$logging = $this->db->tableName( "logging" );
$sql = "SELECT /*! STRAIGHT_JOIN */ log_type, log_action, log_timestamp,
log_user, user_name,
+ log_id, log_deleted,
log_namespace, log_title, page_id,
log_comment, log_params FROM $logging ";
if( !empty( $this->joinClauses ) ) {
* @addtogroup SpecialPage
*/
class LogViewer {
+ const DELETED_ACTION = 1;
+ const DELETED_COMMENT = 2;
+ const DELETED_USER = 4;
+ const DELETED_RESTRICTED = 8;
+
/**
* @var LogReader $reader
*/
global $wgUser;
$this->skin = $wgUser->getSkin();
$this->reader =& $reader;
+ $this->preCacheMessages();
+ }
+
+ /**
+ * As we use the same small set of messages in various methods and that
+ * they are called often, we call them once and save them in $this->message
+ */
+ function preCacheMessages() {
+ // Precache various messages
+ if( !isset( $this->message ) ) {
+ foreach( explode(' ', 'viewpagelogs revhistory imghistory rev-delundel' ) as $msg ) {
+ $this->message[$msg] = wfMsgExt( $msg, array( 'escape') );
+ }
+ }
}
/**
$this->showError( $wgOut );
}
}
+
+ /**
+ * Determine if the current user is allowed to view a particular
+ * field of this event, if it's marked as deleted.
+ * @param int $field
+ * @return bool
+ */
+ function userCan( $event, $field ) {
+ if( ( $event->log_deleted & $field ) == $field ) {
+ global $wgUser;
+ $permission = ( $event->log_deleted & Revision::DELETED_RESTRICTED ) == Revision::DELETED_RESTRICTED
+ ? 'hiderevision'
+ : 'deleterevision';
+ wfDebug( "Checking for $permission due to $field match on $event->log_deleted\n" );
+ return $wgUser->isAllowed( $permission );
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * int $field one of DELETED_* bitfield constants
+ * @return bool
+ */
+ function isDeleted( $event, $field ) {
+ return ($event->log_deleted & $field) == $field;
+ }
+
+ /**
+ * Fetch event's user id if it's available to all users
+ * @return int
+ */
+ function getUser( $event ) {
+ if( $this->isDeleted( $event, Revision::DELETED_USER ) ) {
+ return 0;
+ } else {
+ return $event->log_user;
+ }
+ }
+
+ /**
+ * Fetch event's user id without regard for the current user's permissions
+ * @return string
+ */
+ function getRawUser( $event ) {
+ return $event->log_user;
+ }
+
+ /**
+ * Fetch event's username if it's available to all users
+ * @return string
+ */
+ function getUserText( $event ) {
+ if( $this->isDeleted( $event, Revision::DELETED_USER ) ) {
+ return "";
+ } else {
+ if ( isset($event->user_name) ) {
+ return $event->user_name;
+ } else {
+ return User::whoIs( $event->log_user );
+ }
+ }
+ }
+
+ /**
+ * Fetch event's username without regard for view restrictions
+ * @return string
+ */
+ function getRawUserText( $event ) {
+ if ( isset($event->user_name) ) {
+ return $event->user_name;
+ } else {
+ return User::whoIs( $event->log_user );
+ }
+ }
+
+ /**
+ * Fetch event comment if it's available to all users
+ * @return string
+ */
+ function getComment( $event ) {
+ if( $this->isDeleted( $event, Revision::DELETED_COMMENT ) ) {
+ return "";
+ } else {
+ return $event->log_comment;
+ }
+ }
+
+ /**
+ * Fetch event comment without regard for the current user's permissions
+ * @return string
+ */
+ function getRawComment( $event ) {
+ return $event->log_comment;
+ }
+
+ /**
+ * Returns the title of the page associated with this entry.
+ * @return Title
+ */
+ function getTitle( $event ) {
+ return Title::makeTitle( $event->log_namespace, $event->log_title );
+ }
+
+ /**
+ * Return the log action if it's available to all users
+ * default is deleted if not specified for security
+ * @return Title
+ */
+ function logActionText( $log_type, $log_action, $title, $skin, $paramArray, $log_deleted = LogViewer::DELETED_ACTION ) {
+ if( $log_deleted & LogViewer::DELETED_ACTION ) {
+ return '<span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
+ } else {
+ $action = LogPage::actionText( $log_type, $log_action, $title, $this->skin, $paramArray, true, true );
+ return $action;
+ }
+ }
/**
* Load the data from the linked LogReader
* @private
*/
function logLine( $s ) {
- global $wgLang, $wgUser;;
+ global $wgLang, $wgUser;
+
$skin = $wgUser->getSkin();
$title = Title::makeTitle( $s->log_namespace, $s->log_title );
$time = $wgLang->timeanddate( wfTimestamp(TS_MW, $s->log_timestamp), true );
} else {
$linkCache->addBadLinkObj( $title );
}
+ // User links
+ $userLink = $this->skin->logUserTools( $s, true );
+ // Comment
+ $comment = $this->skin->logComment( $s, true );
- $userLink = $this->skin->userLink( $s->log_user, $s->user_name ) . $this->skin->userToolLinksRedContribs( $s->log_user, $s->user_name );
- $comment = $this->skin->commentBlock( $s->log_comment );
$paramArray = LogPage::extractParams( $s->log_params );
- $revert = '';
- // show revertmove link
+ $revert = ''; $del = '';
+
+ // Some user can hide log items and have review links
+ if( $wgUser->isAllowed( 'deleterevision' ) ) {
+ $del = $this->showhideLinks( $s, $title );
+ }
+
+ // Show restore/unprotect/unblock
+ $revert = $this->showReviewLinks( $s, $title, $paramArray );
+
+ // Event description
+ $action = $this->logActionText( $s->log_type, $s->log_action, $title, $this->skin, $paramArray, $s->log_deleted );
+
+ $out = "<li><tt>$del</tt> $time $userLink $action $comment <small>$revert</small></li>\n";
+ return $out;
+ }
+
+ /**
+ * @param $s, row object
+ * @param $s, title object
+ * @private
+ */
+ function showhideLinks( $s, $title ) {
+ $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+ if( !LogViewer::userCan( $s, Revision::DELETED_RESTRICTED ) ) {
+ // If event was hidden from sysops
+ $del = $this->message['rev-delundel'];
+ } else if( $s->log_type == 'oversight' ) {
+ // No one should be hiding from the oversight log
+ $del = $this->message['rev-delundel'];
+ } else {
+ $del = $this->skin->makeKnownLinkObj( $revdel,
+ $this->message['rev-delundel'],
+ 'target=' . urlencode( $title->getPrefixedDbkey() ) . '&logid=' . $s->log_id );
+ // Bolden oversighted content
+ if( LogViewer::isDeleted( $s, Revision::DELETED_RESTRICTED ) )
+ $del = "<strong>$del</strong>";
+ }
+ return "(<small>$del</small>)";
+ }
+
+ /**
+ * @param $s, row object
+ * @param $title, title object
+ * @param $s, param array
+ * @private
+ */
+ function showReviewLinks( $s, $title, $paramArray ) {
+ global $wgUser;
+
+ $reviewlink='';
+ // Show revertmove link
if ( $s->log_type == 'move' && isset( $paramArray[0] ) ) {
$destTitle = Title::newFromText( $paramArray[0] );
if ( $destTitle ) {
'&wpMovetalk=0' ) . ')';
}
// show undelete link
- } elseif ( $s->log_action == 'delete' && $wgUser->isAllowed( 'delete' ) ) {
- $revert = '(' . $this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Undelete' ),
+ } else if ( $s->log_action == 'delete' && $wgUser->isAllowed( 'delete' ) ) {
+ $reviewlink = $this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Undelete' ),
wfMsg( 'undeletebtn' ) ,
- 'target='. urlencode( $title->getPrefixedDBkey() ) ) . ')';
-
+ 'target='. urlencode( $title->getPrefixedDBkey() ) );
// show unblock link
} elseif ( $s->log_action == 'block' && $wgUser->isAllowed( 'block' ) ) {
- $revert = '(' . $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Ipblocklist' ),
+ $reviewlink = $this->skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Ipblocklist' ),
wfMsg( 'unblocklink' ),
- 'action=unblock&ip=' . urlencode( $s->log_title ) ) . ')';
+ 'action=unblock&ip=' . urlencode( $s->log_title ) );
// show change protection link
} elseif ( $s->log_action == 'protect' && $wgUser->isAllowed( 'protect' ) ) {
- $revert = '(' . $skin->makeKnownLink( $title->getPrefixedDBkey() ,
+ $reviewlink = $this->skin->makeKnownLink( $title->getPrefixedDBkey() ,
wfMsg( 'protect_change' ),
- 'action=unprotect' ) . ')';
+ 'action=unprotect' );
}
-
- $action = LogPage::actionText( $s->log_type, $s->log_action, $title, $this->skin, $paramArray, true, true );
- $out = "<li>$time $userLink $action $comment $revert</li>\n";
- return $out;
+ // If an edit was hidden from a page give a review link to the history
+ if ( $wgUser->isAllowed( 'deleterevision' ) && isset($paramArray[2]) ) {
+ $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+ if ( $s->log_action == 'revision' ) {
+ $reviewlink = $this->skin->makeKnownLinkObj( $title, $this->message['revhistory'],
+ wfArrayToCGI( array('action' => 'history' ) ) ) . ':';
+ } else if ( $s->log_action == 'file' ) {
+ // Currently, only deleted file versions can be hidden
+ $undelete = SpecialPage::getTitleFor( 'Undelete' );
+ $reviewlink = $this->skin->makeKnownLinkObj( $undelete, $this->message['imghistory'],
+ wfArrayToCGI( array('target' => $title->getPrefixedText() ) ) ) . ':';
+ } else if ( $s->log_action == 'event' && isset($paramArray[0]) ) {
+ // If this event was to a log, give a review link to logs for that page only
+ $reviewlink = $this->skin->makeKnownLinkObj( $title, $this->message['viewpagelogs'],
+ wfArrayToCGI( array('page' => $paramArray[0] ) ) ) . ':';
+ }
+ // Link to each hidden object ID
+ $IdType = $paramArray[1].'id';
+ $Ids = explode( ',', $paramArray[2] );
+ foreach ( $Ids as $id ) {
+ $reviewlink .= ' '.$this->skin->makeKnownLinkObj( $revdel, "#$id",
+ wfArrayToCGI( array('target' => $paramArray[0], $IdType => $id ) ) );
+ }
+ }
+ $reviewlink = ( $reviewlink=='' ) ? "" : " ($reviewlink) ";
+ return $reviewlink;
}
/**
* @private
*/
function getTypeMenu() {
+ global $wgLogRestrictions, $wgUser;
+
$out = "<select name='type'>\n";
$validTypes = LogPage::validTypes();
// Third pass generates sorted XHTML content
foreach( $m as $text => $type ) {
$selected = ($type == $this->reader->queryType());
+ // Restricted types
+ if ( isset($wgLogRestrictions[$type]) ) {
+ if ( $wgUser->isAllowed( $wgLogRestrictions[$type] ) ) {
+ $out .= Xml::option( $text, $type, $selected ) . "\n";
+ }
+ } else {
$out .= Xml::option( $text, $type, $selected ) . "\n";
+ }
}
$out .= '</select>';
}
}
+/**
+ * Aliases for backwards compatibility with 1.6
+ */
+define( 'MW_LOG_DELETED_ACTION', LogViewer::DELETED_ACTION );
+define( 'MW_LOG_DELETED_USER', LogViewer::DELETED_USER );
+define( 'MW_LOG_DELETED_COMMENT', LogViewer::DELETED_COMMENT );
+define( 'MW_LOG_DELETED_RESTRICTED', LogViewer::DELETED_RESTRICTED );
?>
rcFormatDiff( $obj ),
$title->getFullURL(),
$obj->rc_timestamp,
- $obj->rc_user_text,
+ ($obj->rc_deleted & Revision::DELETED_USER) ? wfMsgHtml('rev-deleted-user') : $obj->rc_user_text,
$talkpage->getFullURL()
);
$feed->outItem( $item );
return rcFormatDiffRow( $titleObj,
$row->rc_last_oldid, $row->rc_this_oldid,
$timestamp,
- $row->rc_comment );
+ ($row->rc_deleted & Revision::DELETED_COMMENT) ? wfMsgHtml('rev-deleted-comment') : $row->rc_comment,
+ ($row->rc_deleted & Revision::DELETED_NAME) ? wfMsgHtml('rev-deleted-event') : $row->rc_actiontext );
}
-function rcFormatDiffRow( $title, $oldid, $newid, $timestamp, $comment ) {
+function rcFormatDiffRow( $title, $oldid, $newid, $timestamp, $comment, $actiontext='' ) {
global $wgFeedDiffCutoff, $wgContLang, $wgUser;
$fname = 'rcFormatDiff';
wfProfileIn( $fname );
$skin = $wgUser->getSkin();
+ # log enties
+ if( $actiontext ) $comment = "$actiontext $comment";
$completeText = '<p>' . $skin->formatComment( $comment ) . "</p>\n";
if( $title->getNamespace() >= 0 && $title->userCan( 'read' ) ) {
/**
* Not quite ready for production use yet; need to fix up the restricted mode,
- * and provide for preservation across delete/undelete of the page.
- *
- * To try this out, set up extra permissions something like:
- * $wgGroupPermissions['sysop']['deleterevision'] = true;
- * $wgGroupPermissions['bureaucrat']['hiderevision'] = true;
+ * and provide for preservation across delete/undelete of images.
*/
function wfSpecialRevisiondelete( $par = null ) {
global $wgOut, $wgRequest;
- $target = $wgRequest->getVal( 'target' );
+ $target = $wgRequest->getText( 'target' );
+ // handle our many different possible input types
$oldid = $wgRequest->getIntArray( 'oldid' );
-
- $page = Title::newFromUrl( $target );
-
+ $logid = $wgRequest->getIntArray( 'logid' );
+ $arid = $wgRequest->getIntArray( 'arid' );
+ $fileid = $wgRequest->getIntArray( 'fileid' );
+
+ $page = Title::newFromUrl( $target, false );
if( is_null( $page ) ) {
$wgOut->showErrorPage( 'notargettitle', 'notargettext' );
return;
}
-
- if( is_null( $oldid ) ) {
+
+ $input_types = !is_null( $oldid ) + !is_null( $logid ) + !is_null( $arid ) + !is_null( $fileid );
+ if( $input_types > 1 || $input_types==0 ) {
+ //one target set at a time please!
$wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
return;
}
- $form = new RevisionDeleteForm( $wgRequest );
+ $form = new RevisionDeleteForm( $wgRequest, $oldid, $logid, $arid, $fileid );
if( $wgRequest->wasPosted() ) {
$form->submit( $wgRequest );
- } else {
- $form->show( $wgRequest );
+ } else if( $oldid || $arid ) {
+ $form->showRevs( $wgRequest );
+ } else if( $logid ) {
+ $form->showEvents( $wgRequest );
+ } else if( $fileid ) {
+ $form->showImages( $wgRequest );
}
}
* @param Title $page
* @param int $oldid
*/
- function __construct( $request ) {
+ function __construct( $request, $oldid, $logid, $arid, $fileid ) {
global $wgUser;
- $target = $request->getVal( 'target' );
- $this->page = Title::newFromUrl( $target );
+ $target = $request->getText( 'target' );
+ $this->page = Title::newFromUrl( $target, false );
$this->revisions = $request->getIntArray( 'oldid', array() );
+ $this->events = $request->getIntArray( 'logid', array() );
+ $this->archrevs = $request->getIntArray( 'arid', array() );
+ $this->files = $request->getIntArray( 'fileid', array() );
$this->skin = $wgUser->getSkin();
+
+ // log events don't have text to hide, but hiding the page name is useful
+ if ( $fileid ) {
+ $hide_text_name = array( 'revdelete-hide-image', 'wpHideImage', Image::DELETED_FILE );
+ $this->deletetype='file';
+ } else if ( $logid ) {
+ $hide_text_name = array( 'revdelete-hide-name', 'wpHideName', LogViewer::DELETED_ACTION );
+ $this->deletetype='log';
+ } else {
+ $hide_text_name = array( 'revdelete-hide-text', 'wpHideText', Revision::DELETED_TEXT );
+ if ( $arid ) $this->deletetype='ar';
+ else $this->deletetype='old';
+ }
$this->checks = array(
- array( 'revdelete-hide-text', 'wpHideText', Revision::DELETED_TEXT ),
+ $hide_text_name,
array( 'revdelete-hide-comment', 'wpHideComment', Revision::DELETED_COMMENT ),
array( 'revdelete-hide-user', 'wpHideUser', Revision::DELETED_USER ),
array( 'revdelete-hide-restricted', 'wpHideRestricted', Revision::DELETED_RESTRICTED ) );
}
/**
+ * This sets any fields that are true to a bitfield to true on a given bitfield
+ * @param $bitfield, running bitfield
+ * @param $nbitfield, new bitfiled
+ */
+ function setBitfield( $bitfield, $nbitfield ) {
+ if ( $nbitfield & Revision::DELETED_TEXT) $bitfield |= Revision::DELETED_TEXT;
+ if ( $nbitfield & LogViewer::DELETED_ACTION) $bitfield |= LogViewer::DELETED_ACTION;
+ if ( $nbitfield & Image::DELETED_FILE) $bitfield |= Image::DELETED_FILE;
+ if ( $nbitfield & Revision::DELETED_COMMENT) $bitfield |= Revision::DELETED_COMMENT;
+ if ( $nbitfield & Revision::DELETED_USER) $bitfield |= Revision::DELETED_USER;
+ if ( $nbitfield & Revision::DELETED_RESTRICTED) $bitfield |= Revision::DELETED_RESTRICTED;
+ return $bitfield;
+ }
+
+ /**
+ * This lets a user set restrictions for live and archived revisions
* @param WebRequest $request
*/
- function show( $request ) {
- global $wgOut, $wgUser;
+ function showRevs( $request ) {
+ global $wgOut, $wgUser, $action;
- $wgOut->addWikiText( wfMsg( 'revdelete-selected', $this->page->getPrefixedText() ) );
+ $UserAllowed = true;
+ $wgOut->addWikiText( wfMsgHtml( 'revdelete-selected', $this->page->getPrefixedText() ) );
+ $bitfields = 0;
$wgOut->addHtml( "<ul>" );
- foreach( $this->revisions as $revid ) {
- $rev = Revision::newFromTitle( $this->page, $revid );
- if( !isset( $rev ) ) {
+ if ( $this->deletetype=='old') {
+ foreach( $this->revisions as $revid ) {
+ $rev = Revision::newFromTitle( $this->page, $revid );
+ // Hiding top revisison is bad
+ if( !isset( $rev ) || $rev->isCurrent() ) {
+ $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
+ return;
+ } else if( !$rev->userCan(Revision::DELETED_RESTRICTED) ) {
+ // If a rev is hidden from sysops
+ if ( $action != 'submit') {
+ $wgOut->permissionRequired( 'hiderevision' ); return;
+ }
+ $UserAllowed=false;
+ }
+ $wgOut->addHtml( $this->historyLine( $rev ) );
+ $bitfields = $this->setBitfield( $bitfields, $rev->mDeleted );
+ }
+ } else if ( $this->deletetype=='ar') {
+ $archive = new PageArchive( $this->page );
+ foreach( $this->archrevs as $revid ) {
+ $rev = $archive->getRevision('', $revid );
+ if( !isset( $rev ) ) {
+ $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
+ return;
+ } else if( !$rev->userCan(Revision::DELETED_RESTRICTED) ) {
+ //if a rev is hidden from sysops
+ if ( $action != 'submit') {
+ $wgOut->permissionRequired( 'hiderevision' ); return;
+ }
+ $UserAllowed=false;
+ }
+ $wgOut->addHtml( $this->historyLine( $rev ) );
+ $bitfields = $this->setBitfield( $bitfields, $rev->mDeleted );
+ }
+ }
+ $wgOut->addHtml( "</ul>" );
+
+ $wgOut->addWikiText( wfMsgHtml( 'revdelete-text' ) );
+ //Normal sysops can always see what they did, but can't always change it
+ if ( !$UserAllowed ) return;
+
+ $items = array(
+ wfInputLabel( wfMsgHtml( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ),
+ wfSubmitButton( wfMsgHtml( 'revdelete-submit' ) ) );
+ $hidden = array(
+ wfHidden( 'wpEditToken', $wgUser->editToken() ),
+ wfHidden( 'target', $this->page->getPrefixedText() ),
+ wfHidden( 'type', $this->deletetype ) );
+ if( $this->deletetype=='old' ) {
+ foreach( $this->revisions as $revid ) {
+ $hidden[] = wfHidden( 'oldid[]', $revid );
+ }
+ } else if( $this->deletetype=='ar' ) {
+ foreach( $this->archrevs as $revid ) {
+ $hidden[] = wfHidden( 'arid[]', $revid );
+ }
+ }
+ $special = SpecialPage::getTitleFor( 'Revisiondelete' );
+ $wgOut->addHtml( wfElement( 'form', array(
+ 'method' => 'post',
+ 'action' => $special->getLocalUrl( 'action=submit' ) ),
+ null ) );
+
+ $wgOut->addHtml( '<fieldset><legend>' . wfMsgHtml( 'revdelete-legend' ) . '</legend>' );
+ // FIXME: all items checked for just one rev are checked, even if not set for the others
+ foreach( $this->checks as $item ) {
+ list( $message, $name, $field ) = $item;
+ $wgOut->addHtml( '<div>' .
+ wfCheckLabel( wfMsgHtml( $message), $name, $name, $bitfields & $field ) .
+ '</div>' );
+ }
+ $wgOut->addHtml( '</fieldset>' );
+ foreach( $items as $item ) {
+ $wgOut->addHtml( '<p>' . $item . '</p>' );
+ }
+ foreach( $hidden as $item ) {
+ $wgOut->addHtml( $item );
+ }
+
+ $wgOut->addHtml( '</form>' );
+ }
+
+ /**
+ * This lets a user set restrictions for archived images
+ * @param WebRequest $request
+ */
+ function showImages( $request ) {
+ global $wgOut, $wgUser, $action;
+
+ $UserAllowed = true;
+ $wgOut->addWikiText( wfMsgHtml( 'revdelete-selected', $this->page->getPrefixedText() ) );
+
+ $bitfields = 0;
+ $wgOut->addHtml( "<ul>" );
+ foreach( $this->files as $fileid ) {
+ $file = new FSarchivedFile( $this->page, $fileid );
+ if( !isset( $file->mId ) ) {
+ $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
+ return;
+ } else if( !$file->userCan(Revision::DELETED_RESTRICTED) ) {
+ // If a rev is hidden from sysops
+ if ( $action != 'submit') {
+ $wgOut->permissionRequired( 'hiderevision' ); return;
+ }
+ $UserAllowed=false;
+ }
+ $wgOut->addHtml( $this->uploadLine( $file ) );
+ $bitfields = $this->setBitfield( $bitfields, $file->mDeleted );
+ }
+ $wgOut->addHtml( "</ul>" );
+
+ $wgOut->addWikiText( wfMsgHtml( 'revdelete-text' ) );
+ //Normal sysops can always see what they did, but can't always change it
+ if ( !$UserAllowed ) return;
+
+ $items = array(
+ wfInputLabel( wfMsgHtml( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ),
+ wfSubmitButton( wfMsgHtml( 'revdelete-submit' ) ) );
+ $hidden = array(
+ wfHidden( 'wpEditToken', $wgUser->editToken() ),
+ wfHidden( 'target', $this->page->getPrefixedText() ),
+ wfHidden( 'type', $this->deletetype ) );
+ foreach( $this->files as $fileid ) {
+ $hidden[] = wfHidden( 'fileid[]', $fileid );
+ }
+ $special = SpecialPage::getTitleFor( 'Revisiondelete' );
+ $wgOut->addHtml( wfElement( 'form', array(
+ 'method' => 'post',
+ 'action' => $special->getLocalUrl( 'action=submit' ) ),
+ null ) );
+
+ $wgOut->addHtml( '<fieldset><legend>' . wfMsgHtml( 'revdelete-legend' ) . '</legend>' );
+ // FIXME: all items checked for just one file are checked, even if not set for the others
+ foreach( $this->checks as $item ) {
+ list( $message, $name, $field ) = $item;
+ $wgOut->addHtml( '<div>' .
+ wfCheckLabel( wfMsgHtml( $message), $name, $name, $bitfields & $field ) .
+ '</div>' );
+ }
+ $wgOut->addHtml( '</fieldset>' );
+ foreach( $items as $item ) {
+ $wgOut->addHtml( '<p>' . $item . '</p>' );
+ }
+ foreach( $hidden as $item ) {
+ $wgOut->addHtml( $item );
+ }
+
+ $wgOut->addHtml( '</form>' );
+ }
+
+ /**
+ * This lets a user set restrictions for log items
+ * @param WebRequest $request
+ */
+ function showEvents( $request ) {
+ global $wgOut, $wgUser, $action;
+
+ $UserAllowed = true;
+ $wgOut->addWikiText( wfMsgHtml( 'logdelete-selected', $this->page->getPrefixedText() ) );
+
+ $bitfields = 0;
+ $wgOut->addHtml( "<ul>" );
+ foreach( $this->events as $logid ) {
+ $log = new LogViewer( $wgRequest );
+ $event = LogReader::newFromTitle( $this->page, $logid );
+ // Don't hide from oversight log!!!
+ if( !isset( $event ) || $event->log_type == 'oversight' ) {
$wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
return;
+ } else if( !$log->userCan($event, Revision::DELETED_RESTRICTED) ) {
+ // If an event is hidden from sysops
+ if ( $action != 'submit') {
+ $wgOut->permissionRequired( 'hiderevision' ); return;
+ }
+ $UserAllowed=false;
}
- $wgOut->addHtml( $this->historyLine( $rev ) );
- $bitfields[] = $rev->mDeleted; // FIXME
+ $wgOut->addHtml( $this->logLine( $log, $event ) );
+ $bitfields = $this->setBitfield( $bitfields, $event->log_deleted );
}
$wgOut->addHtml( "</ul>" );
-
- $wgOut->addWikiText( wfMsg( 'revdelete-text' ) );
+
+ $wgOut->addWikiText( wfMsgHtml( 'revdelete-text' ) );
+ //Normal sysops can always see what they did, but can't always change it
+ if ( !$UserAllowed ) return;
$items = array(
- wfInputLabel( wfMsg( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ),
- wfSubmitButton( wfMsg( 'revdelete-submit' ) ) );
+ wfInputLabel( wfMsgHtml( 'revdelete-log' ), 'wpReason', 'wpReason', 60 ),
+ wfSubmitButton( wfMsgHtml( 'revdelete-submit' ) ) );
$hidden = array(
wfHidden( 'wpEditToken', $wgUser->editToken() ),
- wfHidden( 'target', $this->page->getPrefixedText() ) );
- foreach( $this->revisions as $revid ) {
- $hidden[] = wfHidden( 'oldid[]', $revid );
+ wfHidden( 'target', $this->page->getPrefixedText() ),
+ wfHidden( 'type', $this->deletetype ) );
+ foreach( $this->events as $logid ) {
+ $hidden[] = wfHidden( 'logid[]', $logid );
}
$special = SpecialPage::getTitleFor( 'Revisiondelete' );
null ) );
$wgOut->addHtml( '<fieldset><legend>' . wfMsgHtml( 'revdelete-legend' ) . '</legend>' );
+ // FIXME: all items checked for just on event are checked, even if not set for the others
foreach( $this->checks as $item ) {
list( $message, $name, $field ) = $item;
$wgOut->addHtml( '<div>' .
- wfCheckLabel( wfMsg( $message), $name, $name, $rev->isDeleted( $field ) ) .
+ wfCheckLabel( wfMsgHtml( $message), $name, $name, $bitfields & $field ) .
'</div>' );
}
$wgOut->addHtml( '</fieldset>' );
function historyLine( $rev ) {
global $wgContLang;
$date = $wgContLang->timeanddate( $rev->getTimestamp() );
+
+ $difflink=''; $del = '';
+ if( $this->deletetype=='old' ) {
+ $difflink = '(' . $this->skin->makeKnownLinkObj( $this->page, wfMsgHtml('diff'),
+ '&diff=' . $rev->getId() . '&oldid=prev' ) . ')';
+ $revlink = $this->skin->makeLinkObj( $this->page, $date, 'oldid=' . $rev->getId() );
+ } else if( $this->deletetype=='ar' ) {
+ $undelete = SpecialPage::getTitleFor( 'Undelete' );
+ $target = $this->page->getPrefixedText();
+ $revlink = $this->skin->makeLinkObj( $undelete, $date, "target=$target×tamp=" . $rev->getTimestamp() );
+ }
+
+ if ( $rev->isDeleted(Revision::DELETED_TEXT) ) {
+ $revlink = '<span class="history-deleted">'.$revlink.'</span>';
+ $del = ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
+ if ( !$rev->userCan(Revision::DELETED_TEXT) ) {
+ $revlink = '<span class="history-deleted">'.$date.'</span>';
+ }
+ }
+
+ return
+ "<li> $difflink $revlink " . $this->skin->revUserLink( $rev ) . " " . $this->skin->revComment( $rev ) . "$del</li>";
+ }
+
+ /**
+ * @param Image $file
+ * @returns string
+ */
+ function uploadLine( $file ) {
+ global $wgContLang;
+
+ $target = $this->page->getPrefixedText();
+ $date = $wgContLang->timeanddate( $file->mTimestamp, true );
+
+ $del = '';
+ if ( $file->mGroup == 'deleted' ) {
+ $undelete = SpecialPage::getTitleFor( 'Undelete' );
+ $pageLink = $this->skin->makeKnownLinkObj( $undelete, $date, "target=$target&file=$file->mKey" );
+ } else {
+ $pageLink = $this->skin->makeKnownLinkObj( $this->page, $date, "file=$file->mKey" );
+ }
+ if ( $file->isDeleted(Image::DELETED_FILE) ) {
+ $pageLink = '<span class="history-deleted">' . $pageLink . '</span>';
+ $del = ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
+ if ( !$file->userCan(Image::DELETED_FILE) ) {
+ $pageLink = '<span class="history-deleted">'.$date.'</span>';
+ }
+ }
+
+ $data = wfMsgHtml( 'widthheight',
+ $wgContLang->formatNum( $file->mWidth ),
+ $wgContLang->formatNum( $file->mHeight ) ) .
+ ' (' . wfMsgHtml( 'nbytes', $wgContLang->formatNum( $file->mSize ) ) . ')';
+
+ return
+ "<li> $pageLink " . $this->skin->fileUserLink( $file ) . " $data " . $this->skin->fileComment( $file ) . "$del</li>";
+ }
+
+ /**
+ * @param Revision $rev
+ * @returns string
+ */
+ function logLine( $log, $event ) {
+ global $wgContLang;
+
+ $date = $wgContLang->timeanddate( $event->log_timestamp );
+ $paramArray = LogPage::extractParams( $event->log_params );
+
+ if ( !LogViewer::userCan($event,LogViewer::DELETED_ACTION) ) {
+ $action = '<span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
+ } else {
+ $action = LogPage::actionText( $event->log_type, $event->log_action, $this->page, $this->skin, $paramArray, true, true );
+ if( $event->log_deleted & LogViewer::DELETED_ACTION )
+ $action = '<span class="history-deleted">' . $action . '</span>';
+ }
return
- "<li>" .
- $this->skin->makeLinkObj( $this->page, $date, 'oldid=' . $rev->getId() ) .
- " " .
- $this->skin->revUserLink( $rev ) .
- " " .
- $this->skin->revComment( $rev ) .
- "</li>";
+ "<li>$date" . " " . $this->skin->logUserLink( $event ) . " $action " . $this->skin->logComment( $event ) . "</li>";
}
/**
function submit( $request ) {
$bitfield = $this->extractBitfield( $request );
$comment = $request->getText( 'wpReason' );
- if( $this->save( $bitfield, $comment ) ) {
- return $this->success( $request );
- } else {
- return $this->show( $request );
- }
+
+ $target = $request->getText( 'target' );
+ $title = Title::newFromURL( $target, false );
+
+ if( $this->save( $bitfield, $comment, $title ) ) {
+ $this->success( $request );
+ } else if( $request->getCheck( 'oldid' ) || $request->getCheck( 'arid' ) ) {
+ return $this->showRevs( $request );
+ } else if( $request->getCheck( 'logid' ) ) {
+ return $this->showLogs( $request );
+ } else if( $request->getCheck( 'fileid' ) ) {
+ return $this->showImages( $request );
+ }
}
function success( $request ) {
global $wgOut;
- $wgOut->addWikiText( 'woo' );
+
+ $wgOut->setPagetitle( wfMsgHtml( 'actioncomplete' ) );
+
+ $target = $request->getText( 'target' );
+ $type = $request->getText( 'type' );
+
+ $title = Title::newFromURL( $target, false );
+ $name = $title->makeName( $title->getNamespace(), $title->getText() );
+
+ $logtitle = SpecialPage::getTitleFor( 'Log' );
+ $loglink = $this->skin->makeKnownLinkObj( $logtitle, wfMsgHtml( 'viewpagelogs' ),
+ wfArrayToCGI( array('page' => $name ) ) );
+ $histlink = $this->skin->makeKnownLinkObj( $title, wfMsgHtml( 'revhistory' ),
+ wfArrayToCGI( array('action' => 'history' ) ) );
+
+ if ( $title->getNamespace() > -1)
+ $wgOut->setSubtitle( '<p>'.$histlink.' / '.$loglink.'</p>' );
+
+ if( $type=='log' ) {
+ $wgOut->addWikiText( wfMsgHtml('logdelete-success', $target), false );
+ $this->showEvents( $request );
+ } else if( $type=='old' || $type=='ar' ) {
+ $wgOut->addWikiText( wfMsgHtml('revdelete-success', $target), false );
+ $this->showRevs( $request );
+ } else if ( $type=='file' ) {
+ $wgOut->addWikiText( wfMsgHtml('revdelete-success', $target), false );
+ $this->showImages( $request );
+ }
}
/**
return $bitfield;
}
- function save( $bitfield, $reason ) {
+ function save( $bitfield, $reason, $title ) {
$dbw = wfGetDB( DB_MASTER );
$deleter = new RevisionDeleter( $dbw );
- $deleter->setVisibility( $this->revisions, $bitfield, $reason );
+
+ if( $this->revisions ) {
+ return $deleter->setRevVisibility( $title, $this->revisions, $bitfield, $reason );
+ } else if( $this->events ) {
+ return $deleter->setEventVisibility( $title, $this->events, $bitfield, $reason );
+ } else if( $this->archrevs ) {
+ return $deleter->setArchiveVisibility( $title, $this->archrevs, $bitfield, $reason );
+ } else if( $this->files ) {
+ return $deleter->setFileVisibility( $title, $this->files, $bitfield, $reason );
+ }
}
}
}
/**
+ * @param $title, the page these events apply to
* @param array $items list of revision ID numbers
* @param int $bitfield new rev_deleted value
* @param string $comment Comment for log records
*/
- function setVisibility( $items, $bitfield, $comment ) {
- $pages = array();
+ function setRevVisibility( $title, $items, $bitfield, $comment ) {
+ global $wgOut;
+ $UserAllowedAll = true;
+ $pages_count = array(); $pages_revIds = array();
// To work!
foreach( $items as $revid ) {
- $rev = Revision::newFromId( $revid );
- if( !isset( $rev ) ) {
+ $rev = Revision::newFromTitle( $title, $revid );
+ if( !isset( $rev ) || $rev->isCurrent() ) {
return false;
+ } else if( !$rev->userCan(Revision::DELETED_RESTRICTED) ) {
+ $UserAllowedAll=false;
+ continue;
}
- $this->updateRevision( $rev, $bitfield );
- $this->updateRecentChanges( $rev, $bitfield );
-
- // For logging, maintain a count of revisions per page
$pageid = $rev->getPage();
- if( isset( $pages[$pageid] ) ) {
- $pages[$pageid]++;
- } else {
- $pages[$pageid] = 1;
+ // For logging, maintain a count of revisions per page
+ if ( !isset($pages_count[$pageid]) ) {
+ $pages_count[$pageid]=0;
+ $pages_revIds[$pageid]=array();
+ }
+ // Which pages did we change anything about?
+ if ( $rev->mDeleted != $bitfield ) {
+ $pages_count[$pageid]++;
+ $pages_revIds[$pageid][]=$revid;
+
+ $this->updateRevision( $rev, $bitfield );
+ $this->updateRecentChangesEdits( $rev, $bitfield, false );
}
}
// Clear caches...
- foreach( $pages as $pageid => $count ) {
- $title = Title::newFromId( $pageid );
- $this->updatePage( $title );
- $this->updateLog( $title, $count, $bitfield, $comment );
+ foreach( $pages_count as $pageid => $count ) {
+ //Don't log or touch if nothing changed
+ if ( $count > 0 ) {
+ $title = Title::newFromId( $pageid );
+ $this->updatePage( $title );
+ $this->updateLog( $title, $count, $bitfield, $comment, $title, 'old', $pages_revIds[$pageid] );
+ }
+ }
+ // Where all revs allowed to be set?
+ if ( !$UserAllowedAll ) {
+ //FIXME: still might be confusing???
+ $wgOut->permissionRequired( 'hiderevision' ); return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $title, the page these events apply to
+ * @param array $items list of revision ID numbers
+ * @param int $bitfield new rev_deleted value
+ * @param string $comment Comment for log records
+ */
+ function setArchiveVisibility( $title, $items, $bitfield, $comment ) {
+ global $wgOut;
+
+ $UserAllowedAll = true;
+ $count = 0; $Id_set = array();
+ // To work!
+ $archive = new PageArchive( $title );
+ foreach( $items as $revid ) {
+ $rev = $archive->getRevision( '', $revid );
+ if( !isset( $rev ) ) {
+ return false;
+ } else if( !$rev->userCan(Revision::DELETED_RESTRICTED) ) {
+ $UserAllowedAll=false;
+ continue;
+ }
+ // For logging, maintain a count of revisions
+ if ( $rev->mDeleted != $bitfield ) {
+ $Id_set[]=$revid;
+ $count++;
+ }
+ $this->updateArchive( $rev, $bitfield );
+ }
+
+ // Log if something was changed
+ if ( $count > 0 ) {
+ $this->updateLog( $title, $count, $bitfield, $comment, $title, 'ar', $Id_set );
+ }
+ // Where all revs allowed to be set?
+ if ( !$UserAllowedAll ) {
+ $wgOut->permissionRequired( 'hiderevision' ); return false;
}
return true;
}
+ /**
+ * @param $title, the page these events apply to
+ * @param array $items list of revision ID numbers
+ * @param int $bitfield new rev_deleted value
+ * @param string $comment Comment for log records
+ */
+ function setFileVisibility( $title, $items, $bitfield, $comment ) {
+ global $wgOut;
+
+ $UserAllowedAll = true;
+ $count = 0; $Id_set = array();
+ // To work!
+ foreach( $items as $fileid ) {
+ $file = new FSarchivedFile( $title, $fileid );
+ if( !isset( $file ) ) {
+ return false;
+ } else if( !$file->userCan(Revision::DELETED_RESTRICTED) ) {
+ $UserAllowedAll=false;
+ continue;
+ }
+ // For logging, maintain a count of revisions
+ if ( $file->mDeleted != $bitfield ) {
+ $Id_set[]=$fileid;
+ $count++;
+ }
+ $this->updateFiles( $file, $bitfield );
+ }
+
+ // Log if something was changed
+ if ( $count > 0 ) {
+ $this->updateLog( $title, $count, $bitfield, $comment, $title, 'file', $Id_set );
+ }
+ // Where all revs allowed to be set?
+ if ( !$UserAllowedAll ) {
+ $wgOut->permissionRequired( 'hiderevision' ); return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $title, the page these events apply to
+ * @param array $items list of log ID numbers
+ * @param int $bitfield new log_deleted value
+ * @param string $comment Comment for log records
+ */
+ function setEventVisibility( $title, $items, $bitfield, $comment ) {
+ global $wgOut;
+
+ $UserAllowedAll = true;
+ $logs_count = array(); $logs_Ids = array();
+ // To work!
+ foreach( $items as $logid ) {
+ $event = LogReader::newFromTitle( $title, $logid );
+ if( !isset( $event ) ) {
+ return false;
+ } else if( !LogViewer::userCan($event, Revision::DELETED_RESTRICTED) || $event->log_type == 'oversight' ) {
+ // Don't hide from oversight log!!!
+ $UserAllowedAll=false;
+ continue;
+ }
+ $logtype = $event->log_type;
+ // For logging, maintain a count of events per log type
+ if( !isset( $logs_count[$logtype] ) ) {
+ $logs_count[$logtype]=0;
+ $logs_Ids[$logtype]=array();
+ }
+ // Which logs did we change anything about?
+ if ( $event->log_deleted != $bitfield ) {
+ $logs_Ids[$logtype][]=$logid;
+ $logs_count[$logtype]++;
+
+ $this->updateLogs( $event, $bitfield );
+ $this->updateRecentChangesLog( $event, $bitfield, true );
+ }
+ }
+ foreach( $logs_count as $logtype => $count ) {
+ //Don't log or touch if nothing changed
+ if ( $count > 0 ) {
+ $target = SpecialPage::getTitleFor( 'Log', $logtype );
+ $this->updateLog( $target, $count, $bitfield, $comment, $title, 'log', $logs_Ids[$logtype] );
+ }
+ }
+ // Where all revs allowed to be set?
+ if ( !$UserAllowedAll ) {
+ $wgOut->permissionRequired( 'hiderevision' ); return false;
+ }
+
+ return true;
+ }
+
/**
* Update the revision's rev_deleted field
* @param Revision $rev
'RevisionDeleter::updateRevision' );
}
+ /**
+ * Update the revision's rev_deleted field
+ * @param Revision $rev
+ * @param int $bitfield new rev_deleted bitfield value
+ */
+ function updateArchive( $rev, $bitfield ) {
+ $this->db->update( 'archive',
+ array( 'ar_deleted' => $bitfield ),
+ array( 'ar_rev_id' => $rev->getId() ),
+ 'RevisionDeleter::updateArchive' );
+ }
+
+ /**
+ * Update the images's fa_deleted field
+ * @param Revision $file
+ * @param int $bitfield new rev_deleted bitfield value
+ */
+ function updateFiles( $file, $bitfield ) {
+ $this->db->update( 'filearchive',
+ array( 'fa_deleted' => $bitfield ),
+ array( 'fa_id' => $file->mId ),
+ 'RevisionDeleter::updateFiles' );
+ }
+
+ /**
+ * Update the logging log_deleted field
+ * @param Revision $rev
+ * @param int $bitfield new rev_deleted bitfield value
+ */
+ function updateLogs( $event, $bitfield ) {
+ $this->db->update( 'logging',
+ array( 'log_deleted' => $bitfield ),
+ array( 'log_id' => $event->log_id ),
+ 'RevisionDeleter::updateLogs' );
+ }
+
+ /**
+ * Update the revision's recentchanges record if fields have been hidden
+ * @param Revision $event
+ * @param int $bitfield new rev_deleted bitfield value
+ */
+ function updateRecentChangesLog( $event, $bitfield ) {
+ $this->db->update( 'recentchanges',
+ array( 'rc_deleted' => $bitfield,
+ 'rc_patrolled' => 1),
+ array( 'rc_logid' => $event->log_id ),
+ 'RevisionDeleter::updateRecentChangesLog' );
+ }
+
/**
* Update the revision's recentchanges record if fields have been hidden
* @param Revision $rev
* @param int $bitfield new rev_deleted bitfield value
*/
- function updateRecentChanges( $rev, $bitfield ) {
+ function updateRecentChangesEdits( $rev, $bitfield ) {
$this->db->update( 'recentchanges',
- array(
- 'rc_user' => ($bitfield & Revision::DELETED_USER) ? 0 : $rev->getUser(),
- 'rc_user_text' => ($bitfield & Revision::DELETED_USER) ? wfMsg( 'rev-deleted-user' ) : $rev->getUserText(),
- 'rc_comment' => ($bitfield & Revision::DELETED_COMMENT) ? wfMsg( 'rev-deleted-comment' ) : $rev->getComment() ),
- array(
- 'rc_this_oldid' => $rev->getId() ),
- 'RevisionDeleter::updateRecentChanges' );
+ array( 'rc_deleted' => $bitfield,
+ 'rc_patrolled' => 1),
+ array( 'rc_this_oldid' => $rev->getId() ),
+ 'RevisionDeleter::updateRecentChangesEdits' );
}
/**
* @param int $bitfield the new rev_deleted value
* @param string $comment
*/
- function updateLog( $title, $count, $bitfield, $comment ) {
- $log = new LogPage( 'delete' );
- $reason = "changed $count revisions to $bitfield";
- $reason .= ": $comment";
- $log->addEntry( 'revision', $title, $reason );
+ function updateLog( $title, $count, $bitfield, $comment, $target, $prefix, $items = array() ) {
+ // Put things hidden from sysops in the oversight log
+ $logtype = ( $bitfield & Revision::DELETED_RESTRICTED ) ? 'oversight' : 'delete';
+ // Add params for effected page and ids
+ $params = array( $target->getPrefixedText(), $prefix, implode( ',', $items) );
+ $log = new LogPage( $logtype );
+ if ( $prefix=='log' ) {
+ $reason = wfMsgExt('logdelete-logaction', array('parsemag'), $count, $bitfield, $target->getPrefixedText() );
+ if ($comment) $reason .= ": $comment";
+ $log->addEntry( 'event', $title, $reason, $params );
+ } else if ( $prefix=='old' ) {
+ $reason = wfMsgExt('revdelete-logaction', array('parsemag'), $count, $bitfield );
+ if ($comment) $reason .= ": $comment";
+ $log->addEntry( 'revision', $title, $reason, $params );
+ } else if ( $prefix=='file' ) {
+ $reason = wfMsgExt('revdelete-logaction', array('parsemag'), $count, $bitfield );
+ if ($comment) $reason .= ": $comment";
+ $log->addEntry( 'file', $title, $reason, $params );
+ }
}
}
function listRevisions() {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'archive',
- array( 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', 'ar_comment', 'ar_len' ),
+ array( 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', 'ar_comment', 'ar_rev_id', 'ar_deleted', 'ar_len' ),
array( 'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDBkey() ),
'PageArchive::listRevisions',
* @return ResultWrapper
* @fixme Does this belong in Image for fuller encapsulation?
*/
- function listFiles() {
+ function listFiles() {
if( $this->title->getNamespace() == NS_IMAGE ) {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'filearchive',
'fa_description',
'fa_user',
'fa_user_text',
- 'fa_timestamp' ),
+ 'fa_timestamp',
+ 'fa_deleted' ),
array( 'fa_name' => $this->title->getDbKey() ),
__METHOD__,
array( 'ORDER BY' => 'fa_timestamp DESC' ) );
$rev = $this->getRevision( $timestamp );
return $rev ? $rev->getText() : null;
}
+
+ function getRevisionConds( $timestamp, $id ) {
+ if ( $id ) {
+ $id = intval($id);
+ return "ar_rev_id=$id";
+ } else if ( $timestamp ) {
+ return "ar_timestamp=$timestamp";
+ } else {
+ return 'ar_rev_id=0';
+ }
+ }
/**
* Return a Revision object containing data for the deleted revision.
- * Note that the result *may* or *may not* have a null page ID.
- * @param string $timestamp
+ * Note that the result *may* have a null page ID.
+ * @param string $timestamp or $id
* @return Revision
*/
- function getRevision( $timestamp ) {
+ function getRevision( $timestamp, $id=null ) {
$dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'archive',
array(
'ar_minor_edit',
'ar_flags',
'ar_text_id',
+ 'ar_deleted',
'ar_len' ),
array( 'ar_namespace' => $this->title->getNamespace(),
'ar_title' => $this->title->getDbkey(),
- 'ar_timestamp' => $dbr->timestamp( $timestamp ) ),
+ $this->getRevisionConds( $dbr->timestamp($timestamp), $id ) ),
__METHOD__ );
if( $row ) {
return new Revision( array(
'user_text' => $row->ar_user_text,
'timestamp' => $row->ar_timestamp,
'minor_edit' => $row->ar_minor_edit,
- 'text_id' => $row->ar_text_id ) );
+ 'text_id' => $row->ar_text_id,
+ 'deleted' => $row->ar_deleted,
+ 'len' => $row->ar_len) );
} else {
return null;
}
*
* @return true on success.
*/
- function undelete( $timestamps, $comment = '', $fileVersions = array() ) {
+ function undelete( $timestamps, $comment = '', $fileVersions = array(), $Unsuppress = false) {
// If both the set of text revisions and file revisions are empty,
// restore everything. Otherwise, just restore the requested items.
$restoreAll = empty( $timestamps ) && empty( $fileVersions );
$restoreText = $restoreAll || !empty( $timestamps );
$restoreFiles = $restoreAll || !empty( $fileVersions );
- if( $restoreFiles && $this->title->getNamespace() == NS_IMAGE ) {
- $img = new Image( $this->title );
- $filesRestored = $img->restore( $fileVersions );
+ if( $restoreText ) {
+ $textRestored = $this->undeleteRevisions( $timestamps, $Unsuppress );
} else {
- $filesRestored = 0;
+ $textRestored = 0;
}
- if( $restoreText ) {
- $textRestored = $this->undeleteRevisions( $timestamps );
+ if ( $restoreText && !$textRestored) {
+ // if the image page didn't restore right, don't restore the file either!!!
+ $filesRestored = 0;
+ } else if( $restoreFiles && $this->title->getNamespace() == NS_IMAGE ) {
+ $img = new Image( $this->title );
+ $filesRestored = $img->restore( $fileVersions, $Unsuppress );
} else {
- $textRestored = 0;
+ $filesRestored = 0;
}
// Touch the log!
$log = new LogPage( 'delete' );
if( $textRestored && $filesRestored ) {
- $reason = wfMsgForContent( 'undeletedrevisions-files',
+ $reason = wfMsgExt( 'undeletedrevisions-files', array('parsemag'),
$wgContLang->formatNum( $textRestored ),
$wgContLang->formatNum( $filesRestored ) );
} elseif( $textRestored ) {
- $reason = wfMsgForContent( 'undeletedrevisions',
+ $reason = wfMsgExt( 'undeletedrevisions', array('parsemag'),
$wgContLang->formatNum( $textRestored ) );
} elseif( $filesRestored ) {
- $reason = wfMsgForContent( 'undeletedfiles',
+ $reason = wfMsgExt( 'undeletedfiles', array('parsemag'),
$wgContLang->formatNum( $filesRestored ) );
} else {
wfDebug( "Undelete: nothing undeleted...\n" );
* @param array $timestamps Pass an empty array to restore all revisions, otherwise list the ones to undelete.
* @param string $comment
* @param array $fileVersions
+ * @param bool $Unsuppress, remove all ar_deleted/fa_deleted restrictions of seletected revs
*
* @return int number of revisions restored
*/
- private function undeleteRevisions( $timestamps ) {
+ private function undeleteRevisions( $timestamps, $Unsuppress = false ) {
global $wgDBtype;
$restoreAll = empty( $timestamps );
}
/**
- * Restore each revision...
+ * Select each archived revision...
*/
$result = $dbw->select( 'archive',
/* fields */ array(
'ar_minor_edit',
'ar_flags',
'ar_text_id',
+ 'ar_deleted',
'ar_len' ),
/* WHERE */ array(
'ar_namespace' => $this->title->getNamespace(),
/* options */ array(
'ORDER BY' => 'ar_timestamp' )
);
- if( $dbw->numRows( $result ) < count( $timestamps ) ) {
+ $rev_count = $dbw->numRows( $result );
+ if( $rev_count < count( $timestamps ) ) {
+ # Remove page stub per failure
+ $dbw->delete( 'page', array( 'page_id' => $pageId ), __METHOD__);
wfDebug( __METHOD__.": couldn't find all requested rows\n" );
return false;
}
+ $ret = $dbw->resultObject( $result );
+ // FIXME: We don't currently handle well changing the top revision's settings
+ $ret->seek( $rev_count - 1 );
+ $last_row = $ret->fetchObject();
+ if ( !$Unsuppress && $last_row->ar_deleted && $last_row->ar_rev_id > $previousRevId ) {
+ # Remove page stub per failure
+ $dbw->delete( 'page', array( 'page_id' => $pageId ), __METHOD__);
+ wfDebug( __METHOD__.": couldn't find all requested rows or not all of them could be restored\n" );
+ return false;
+ }
+
$revision = null;
$restored = 0;
-
- while( $row = $dbw->fetchObject( $result ) ) {
+
+ $ret->seek( 0 );
+ while( $row = $ret->fetchObject() ) {
if( $row->ar_text_id ) {
// Revision was deleted in 1.5+; text is in
// the regular text table, use the reference.
'timestamp' => $row->ar_timestamp,
'minor_edit' => $row->ar_minor_edit,
'text_id' => $row->ar_text_id,
- 'len' => $row->ar_len
+ 'deleted' => ($Unsuppress) ? 0 : $row->ar_deleted,
+ 'len' => $row->ar_len
) );
$revision->insertOn( $dbw );
$restored++;
$this->mRestore = $request->getCheck( 'restore' ) && $posted;
$this->mPreview = $request->getCheck( 'preview' ) && $posted;
$this->mComment = $request->getText( 'wpComment' );
+ $this->mUnsuppress = $request->getVal( 'wpUnsuppress' ) && $wgUser->isAllowed( 'oversight' );
if( $par != "" ) {
$this->mTarget = $par;
}
function execute() {
- global $wgOut;
+ global $wgOut, $wgUser;
if ( $this->mAllowed ) {
- $wgOut->setPagetitle( wfMsg( "undeletepage" ) );
+ $wgOut->setPagetitle( wfMsgHtml( "undeletepage" ) );
} else {
- $wgOut->setPagetitle( wfMsg( "viewdeletedpage" ) );
+ $wgOut->setPagetitle( wfMsgHtml( "viewdeletedpage" ) );
}
if( is_null( $this->mTargetObj ) ) {
- $this->showSearchForm();
+ # Not all users can just browse every deleted page from the list
+ if ( $wgUser->isAllowed( 'browsearchive' ) ) {
+ $this->showSearchForm();
- # List undeletable articles
- if( $this->mSearchPrefix ) {
- $result = PageArchive::listPagesByPrefix(
- $this->mSearchPrefix );
- $this->showList( $result );
+ # List undeletable articles
+ if( $this->mSearchPrefix ) {
+ $result = PageArchive::listPagesByPrefix( $this->mSearchPrefix );
+ $this->showList( $result );
+ }
+ } else {
+ $wgOut->addWikiText( wfMsgHtml( 'undelete-header' ) );
}
return;
}
return $this->showRevision( $this->mTimestamp );
}
if( $this->mFile !== null ) {
- return $this->showFile( $this->mFile );
+ $file = new ArchivedFile( $this->mTargetObj, '', $this->mFile );
+ // Check if user is allowed to see this file
+ if ( !$file->userCan( Image::DELETED_FILE ) ) {
+ $wgOut->permissionRequired( 'hiderevision' );
+ return false;
+ } else {
+ return $this->showFile( $this->mFile );
+ }
}
if( $this->mRestore && $this->mAction == "submit" ) {
return $this->undelete();
function showSearchForm() {
global $wgOut, $wgScript;
- $wgOut->addWikiText( wfMsg( 'undelete-header' ) );
+ $wgOut->addWikiText( wfMsgHtml( 'undelete-header' ) );
$wgOut->addHtml(
Xml::openElement( 'form', array(
'</form>' );
}
+ // Generic list of deleted pages
/* private */ function showList( $result ) {
global $wgLang, $wgContLang, $wgUser, $wgOut;
return;
}
+ if( $rev->isDeleted(Revision::DELETED_TEXT) ) {
+ if( !$rev->userCan(Revision::DELETED_TEXT) ) {
+ $wgOut->addWikiText( wfMsg( 'rev-deleted-text-permission' ) );
+ return;
+ } else {
+ $wgOut->addWikiText( wfMsg( 'rev-deleted-text-view' ) );
+ $wgOut->addHTML( '<br/>' );
+ // and we are allowed to see...
+ }
+ }
+
wfRunHooks( 'UndeleteShowRevision', array( $this->mTargetObj, $rev ) );
if( $this->mPreview ) {
$wgOut->addHtml( "<hr />\n" );
- $wgOut->addWikiTextTitle( $rev->getText(), $this->mTargetObj, false );
+ $wgOut->addWikiTextTitle( $rev->revText(), $this->mTargetObj, false );
}
$wgOut->addHtml(
'readonly' => true,
'cols' => intval( $wgUser->getOption( 'cols' ) ),
'rows' => intval( $wgUser->getOption( 'rows' ) ) ),
- $rev->getText() . "\n" ) .
+ $rev->revText() . "\n" ) .
wfOpenElement( 'div' ) .
wfOpenElement( 'form', array(
'method' => 'post',
/* private */ function showHistory() {
global $wgLang, $wgUser, $wgOut;
- $sk = $wgUser->getSkin();
+ $this->sk = $wgUser->getSkin();
if ( $this->mAllowed ) {
$wgOut->setPagetitle( wfMsg( "undeletepage" ) );
} else {
}
*/
if ( $this->mAllowed ) {
- $wgOut->addWikiText( wfMsg( "undeletehistory" ) );
+ $wgOut->addWikiText( '<p>' . wfMsgHtml( "undeletehistory" ) . '</p>' );
+ $wgOut->addHtml( '<p>' . wfMsgHtml( "undeleterevdel" ) . '</p>' );
} else {
- $wgOut->addWikiText( wfMsg( "undeletehistorynoadmin" ) );
+ $wgOut->addWikiText( wfMsgHtml( "undeletehistorynoadmin" ) );
}
# List all stored revisions
array( 'page' => $this->mTargetObj->getPrefixedText(),
'type' => 'delete' ) ) ) );
$logViewer->showList( $wgOut );
-
+ # Show relevant lines from the oversight log if user is allowed to see it:
+ if ( $wgUser->isAllowed( 'oversight' ) ) {
+ $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'oversight' ) ) . "</h2>\n" );
+ $logViewer = new LogViewer(
+ new LogReader(
+ new FauxRequest(
+ array( 'page' => $this->mTargetObj->getPrefixedText(),
+ 'type' => 'oversight' ) ) ) );
+ $logViewer->showList( $wgOut );
+ }
if( $this->mAllowed && ( $haveRevisions || $haveFiles ) ) {
# Format the user-visible controls (comment field, submission button)
# in a nice little table
$table .= '<td colspan="2">' . wfMsgWikiHtml( 'undeleteextrahelp' ) . '</td></tr><tr>';
$table .= '<td align="right"><strong>' . wfMsgHtml( 'undeletecomment' ) . '</strong></td>';
$table .= '<td>' . wfInput( 'wpComment', 50, $this->mComment ) . '</td>';
+ if ( $wgUser->isAllowed( 'oversight' ) ) {
+ $table .= '</tr><tr><td> </td><td>';
+ $table .= Xml::checkLabel( wfMsg( 'revdelete-unsuppress' ), 'wpUnsuppress', 'wpUnsuppress', false, array( 'tabindex' => '2' ) );
+ }
$table .= '</tr><tr><td> </td><td>';
$table .= wfSubmitButton( wfMsg( 'undeletebtn' ), array( 'name' => 'restore' ) );
$table .= wfElement( 'input', array( 'type' => 'reset', 'value' => wfMsg( 'undeletereset' ) ) );
$target = urlencode( $this->mTarget );
while( $row = $revisions->fetchObject() ) {
$ts = wfTimestamp( TS_MW, $row->ar_timestamp );
+ // We don't handle top edits with rev_deleted
if ( $this->mAllowed ) {
- $checkBox = wfCheck( "ts$ts" );
- $pageLink = $sk->makeKnownLinkObj( $titleObj,
- $wgLang->timeanddate( $ts, true ),
- "target=$target×tamp=$ts" );
+ $checkBox = wfCheck( "ts$ts", false );
+ $titleObj = SpecialPage::getTitleFor( "Undelete" );
+ $pageLink = $this->getPageLink( $row, $titleObj, $ts, $target );
} else {
$checkBox = '';
$pageLink = $wgLang->timeanddate( $ts, true );
}
- $userLink = $sk->userLink( $row->ar_user, $row->ar_user_text ) . $sk->userToolLinks( $row->ar_user, $row->ar_user_text );
+ $userLink = $this->getUser( $row );
$stxt = '';
if (!is_null($size = $row->ar_len)) {
if ($size == 0)
$stxt = wfMsgHtml('historyempty');
- else
+ else
$stxt = wfMsgHtml('historysize', $wgLang->formatNum( $size ) );
}
- $comment = $sk->commentBlock( $row->ar_comment );
- $wgOut->addHTML( "<li>$checkBox $pageLink . . $userLink $stxt $comment</li>\n" );
-
+ $comment = $this->getComment( $row );
+ $rd='';
+ if( $wgUser->isAllowed( 'deleterevision' ) ) {
+ $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+ if( !$this->userCan( $row, Revision::DELETED_RESTRICTED ) ) {
+ // If revision was hidden from sysops
+ $del = wfMsgHtml( 'rev-delundel' );
+ } else {
+ $del = $this->sk->makeKnownLinkObj( $revdel,
+ wfMsg( 'rev-delundel' ),
+ 'target=' . urlencode( $this->mTarget ) .
+ '&arid=' . urlencode( $row->ar_rev_id ) );
+ // Bolden oversighted content
+ if( $this->isDeleted( $row, Revision::DELETED_RESTRICTED ) )
+ $del = "<strong>$del</strong>";
+ }
+ $rd = "<tt>(<small>$del</small>)</tt>";
+ }
+
+ $dflag='';
+ if( $this->isDeleted( $row, Revision::DELETED_TEXT ) )
+ $dflag = ' <tt>' . wfMsgHtml( 'deletedrev' ) . '</tt>';
+
+ // Do we still need checkboxes?
+ $wgOut->addHTML( "<li>$checkBox $rd $pageLink . . $userLink $stxt $comment$dflag</li>\n" );
}
$revisions->free();
$wgOut->addHTML("</ul>");
while( $row = $files->fetchObject() ) {
$ts = wfTimestamp( TS_MW, $row->fa_timestamp );
if ( $this->mAllowed && $row->fa_storage_key ) {
- $checkBox = wfCheck( "fileid" . $row->fa_id );
+ if ( !$this->userCan( $row, Revision::DELETED_RESTRICTED ) )
+ // Grey out boxes to files restricted beyond this user.
+ // In the future, all image storage may use FileStore, allowing
+ // for such items to be restored and retain their fa_deleted status
+ $checkBox = wfCheck( "", false, array("disabled" => "disabled") );
+ else
+ $checkBox = wfCheck( "fileid" . $row->fa_id );
$key = urlencode( $row->fa_storage_key );
$target = urlencode( $this->mTarget );
- $pageLink = $sk->makeKnownLinkObj( $titleObj,
- $wgLang->timeanddate( $ts, true ),
- "target=$target&file=$key" );
+ $pageLink = $this->getFileLink( $row, $titleObj, $ts, $target, $key );
} else {
$checkBox = '';
$pageLink = $wgLang->timeanddate( $ts, true );
}
- $userLink = $sk->userLink( $row->fa_user, $row->fa_user_text ) . $sk->userToolLinks( $row->fa_user, $row->fa_user_text );
+ $userLink = $this->getFileUser( $row );
$data =
wfMsgHtml( 'widthheight',
$wgLang->formatNum( $row->fa_width ),
' (' .
wfMsgHtml( 'nbytes', $wgLang->formatNum( $row->fa_size ) ) .
')';
- $comment = $sk->commentBlock( $row->fa_description );
- $wgOut->addHTML( "<li>$checkBox $pageLink . . $userLink $data $comment</li>\n" );
+ $comment = $this->getFileComment( $row );
+ $rd='';
+ if( $wgUser->isAllowed( 'deleterevision' ) ) {
+ $revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
+ if( !$this->userCan( $row, Image::DELETED_RESTRICTED ) ) {
+ // If revision was hidden from sysops
+ $del = wfMsgHtml( 'rev-delundel' );
+ } else {
+ $del = $this->sk->makeKnownLinkObj( $revdel,
+ wfMsg( 'rev-delundel' ),
+ 'target=' . urlencode( $this->mTarget ) .
+ '&fileid=' . urlencode( $row->fa_id ) );
+ // Bolden oversighted content
+ if( $this->isDeleted( $row, Image::DELETED_RESTRICTED ) )
+ $del = "<strong>$del</strong>";
+ }
+ $rd = "<tt>(<small>$del</small>)</tt>";
+ }
+ $wgOut->addHTML( "<li>$checkBox $rd $pageLink . . $userLink $data $comment</li>\n" );
}
$files->free();
$wgOut->addHTML( "</ul>" );
return true;
}
+ /**
+ * Fetch revision text link if it's available to all users
+ * @return string
+ */
+ function getPageLink( $row, $titleObj, $ts, $target ) {
+ global $wgLang;
+
+ if ( !$this->userCan($row, Revision::DELETED_TEXT) ) {
+ return '<span class="history-deleted">' . $wgLang->timeanddate( $ts, true ) . '</span>';
+ } else {
+ $link = $this->sk->makeKnownLinkObj( $titleObj, $wgLang->timeanddate( $ts, true ), "target=$target×tamp=$ts" );
+ if ( $this->isDeleted($row, Revision::DELETED_TEXT) )
+ $link = '<span class="history-deleted">' . $link . '</span>';
+ return $link;
+ }
+ }
+
+ /**
+ * Fetch image view link if it's available to all users
+ * @return string
+ */
+ function getFileLink( $row, $titleObj, $ts, $target, $key ) {
+ global $wgLang;
+
+ if ( !$this->userCan($row, Image::DELETED_FILE) ) {
+ return '<span class="history-deleted">' . $wgLang->timeanddate( $ts, true ) . '</span>';
+ } else {
+ $link = $this->sk->makeKnownLinkObj( $titleObj, $wgLang->timeanddate( $ts, true ), "target=$target&file=$key" );
+ if ( $this->isDeleted($row, Image::DELETED_FILE) )
+ $link = '<span class="history-deleted">' . $link . '</span>';
+ return $link;
+ }
+ }
+
+ /**
+ * Fetch revision's user id if it's available to this user
+ * @return string
+ */
+ function getUser( $row ) {
+ if ( !$this->userCan($row, Revision::DELETED_USER) ) {
+ return '<span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
+ } else {
+ $link = $this->sk->userLink( $row->ar_user, $row->ar_user_text ) . $this->sk->userToolLinks( $row->ar_user, $row->ar_user_text );
+ if ( $this->isDeleted($row, Revision::DELETED_USER) )
+ $link = '<span class="history-deleted">' . $link . '</span>';
+ return $link;
+ }
+ }
+
+ /**
+ * Fetch file's user id if it's available to this user
+ * @return string
+ */
+ function getFileUser( $row ) {
+ if ( !$this->userCan($row, Image::DELETED_USER) ) {
+ return '<span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
+ } else {
+ $link = $this->sk->userLink( $row->fa_user, $row->fa_user_text ) . $this->sk->userToolLinks( $row->fa_user, $row->fa_user_text );
+ if ( $this->isDeleted($row, Image::DELETED_USER) )
+ $link = '<span class="history-deleted">' . $link . '</span>';
+ return $link;
+ }
+ }
+
+ /**
+ * Fetch revision comment if it's available to this user
+ * @return string
+ */
+ function getComment( $row ) {
+ if ( !$this->userCan($row, Revision::DELETED_COMMENT) ) {
+ return '<span class="history-deleted"><span class="comment">' . wfMsgHtml( 'rev-deleted-comment' ) . '</span></span>';
+ } else {
+ $link = $this->sk->commentBlock( $row->ar_comment );
+ if ( $this->isDeleted($row, Revision::DELETED_COMMENT) )
+ $link = '<span class="history-deleted">' . $link . '</span>';
+ return $link;
+ }
+ }
+
+ /**
+ * Fetch file upload comment if it's available to this user
+ * @return string
+ */
+ function getFileComment( $row ) {
+ if ( !$this->userCan($row, Image::DELETED_COMMENT) ) {
+ return '<span class="history-deleted"><span class="comment">' . wfMsgHtml( 'rev-deleted-comment' ) . '</span></span>';
+ } else {
+ $link = $this->sk->commentBlock( $row->fa_description );
+ if ( $this->isDeleted($row, Image::DELETED_COMMENT) )
+ $link = '<span class="history-deleted">' . $link . '</span>';
+ return $link;
+ }
+ }
+
+ /**
+ * int $field one of DELETED_* bitfield constants
+ * for file or revision rows
+ * @return bool
+ */
+ function isDeleted( $row, $field ) {
+ if ( isset($row->ar_deleted) )
+ // page revisions
+ return ($row->ar_deleted & $field) == $field;
+ else if ( isset($row->fa_deleted) )
+ // files
+ return ($row->fa_deleted & $field) == $field;
+ return false;
+ }
+
+ /**
+ * Determine if the current user is allowed to view a particular
+ * field of this revision, if it's marked as deleted.
+ * @param int $field
+ * @return bool
+ */
+ function userCan( $row, $field ) {
+ global $wgUser;
+
+ if( isset($row->ar_deleted) && ($row->ar_deleted & $field) == $field ) {
+ // page revisions
+ $permission = ( $row->ar_deleted & Revision::DELETED_RESTRICTED ) == Revision::DELETED_RESTRICTED
+ ? 'hiderevision'
+ : 'deleterevision';
+ wfDebug( "Checking for $permission due to $field match on $row->ar_deleted\n" );
+ return $wgUser->isAllowed( $permission );
+ } else if( isset($row->fa_deleted) && ($row->fa_deleted & $field) == $field ) {
+ // files
+ $permission = ( $row->fa_deleted & Image::DELETED_RESTRICTED ) == Image::DELETED_RESTRICTED
+ ? 'hiderevision'
+ : 'deleterevision';
+ wfDebug( "Checking for $permission due to $field match on $row->fa_deleted\n" );
+ return $wgUser->isAllowed( $permission );
+ } else {
+ return true;
+ }
+ }
+
function undelete() {
global $wgOut, $wgUser;
if( !is_null( $this->mTargetObj ) ) {
$ok = $archive->undelete(
$this->mTargetTimestamp,
$this->mComment,
- $this->mFileVersions );
-
+ $this->mFileVersions,
+ $this->mUnsuppress );
if( $ok ) {
$skin = $wgUser->getSkin();
$link = $skin->makeKnownLinkObj( $this->mTargetObj );
$wgOut->addHtml( wfMsgWikiHtml( 'undeletedpage', $link ) );
return true;
}
+ // Give user some idea of what is going on ...
+ // This can happen if the top revision would end up being deleted
+ $wgOut->addHtml( '<p>' . wfMsgHtml( "cannotundelete" ) . '</p>' );
+ $wgOut->addHtml( '<p>' . wfMsgHtml( "undeleterevdel" ) . '</p>' );
+ $wgOut->returnToMain( false, $this->mTargetObj );
+ return false;
}
- $wgOut->showFatalError( wfMsg( "cannotundelete" ) );
+ $wgOut->showFatalError( wfMsgHtml( "cannotundelete" ) );
return false;
}
}