Correct empty case for r49149
[lhc/web/wiklou.git] / includes / specials / SpecialRevisiondelete.php
index 7751741..eb8e07a 100644 (file)
@@ -11,18 +11,15 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
 
        public function __construct() {
                parent::__construct( 'Revisiondelete', 'deleterevision' );
-               $this->includable( false );
+               $this->includable( false ); // paranoia
        }
 
        public function execute( $par ) {
                global $wgOut, $wgUser, $wgRequest;
-               if( wfReadOnly() ) {
-                       $wgOut->readOnlyPage();
-                       return;
-               }
                if( !$wgUser->isAllowed( 'deleterevision' ) ) {
-                       $wgOut->permissionRequired( 'deleterevision' );
-                       return;
+                       return $wgOut->permissionRequired( 'deleterevision' );
+               } else if( wfReadOnly() ) {
+                       return $wgOut->readOnlyPage();
                }
                $this->skin =& $wgUser->getSkin();
                $this->setHeaders();
@@ -40,19 +37,19 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                # For reviewing deleted files...
                $this->file = $wgRequest->getVal( 'file' );
                # Only one target set at a time please!
-               $i = (bool)$this->file + (bool)$this->oldids + (bool)$this->logids
+               $types = (bool)$this->file + (bool)$this->oldids + (bool)$this->logids
                        + (bool)$this->artimestamps + (bool)$this->fileids + (bool)$this->oldimgs;
                # No targets?
-               if( $i == 0 ) {
-                       $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
+               if( $types == 0 ) {
+                       $wgOut->showErrorPage( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
                        return;
-               }
                # Too many targets?
-               if( $i !== 1 ) {
+               } else if( $types > 1 ) {
                        $wgOut->showErrorPage( 'revdelete-toomanytargets-title', 'revdelete-toomanytargets-text' );
                        return;
                }
                $this->page = Title::newFromUrl( $this->target );
+               $this->contextPage = Title::newFromUrl( $wgRequest->getText( 'page' ) );
                # If we have revisions, get the title from the first one
                # since they should all be from the same page. This allows 
                # for more flexibility with page moves...
@@ -62,30 +59,46 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                }
                # We need a target page!
                if( is_null($this->page) ) {
-                       $wgOut->addWikiMsg( 'undelete-header' );
-                       return;
+                       return $wgOut->addWikiMsg( 'undelete-header' );
+               }
+               # For reviewing deleted files...show it now if allowed
+               if( $this->file ) {
+                       return $this->tryShowFile( $this->file );
                }
                # Logs must have a type given
                if( $this->logids && !strpos($this->page->getDBKey(),'/') ) {
-                       $wgOut->showErrorPage( 'revdelete-nologtype-title', 'revdelete-nologtype-text' );
-                       return;
+                       return $wgOut->showErrorPage( 'revdelete-nologtype-title', 'revdelete-nologtype-text' );
                }
-               # For reviewing deleted files...show it now if allowed
-               if( $this->file ) {
-                       $oimage = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->page, $this->file );
-                       $oimage->load();
-                       // Check if user is allowed to see this file
-                       if( !$oimage->userCan(File::DELETED_FILE) ) {
-                               $wgOut->permissionRequired( 'suppressrevision' );
-                       } else {
-                               $this->showFile( $this->file );
-                       }
-                       return;
+               # Give a link to the logs/hist for this page
+               $this->showConvenienceLinks();
+               # Lock the operation and the form context
+               $this->secureOperation();
+               # Either submit or create our form
+               if( $this->wasPosted ) {
+                       $this->submit( $wgRequest );
+               } else if( $this->deleteKey == 'oldid' || $this->deleteKey == 'artimestamp' ) {
+                       $this->showRevs();
+               } else if( $this->deleteKey == 'fileid' || $this->deleteKey == 'oldimage' ) {
+                       $this->showImages();
+               } else if( $this->deleteKey == 'logid' ) {
+                       $this->showLogItems();
+               }
+               list($qc,$lim) = $this->getLogQueryCond();
+               # Show relevant lines from the deletion log
+               $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
+               LogEventsList::showLogExtract( $wgOut, 'delete', $this->page->getPrefixedText(), '', $lim, $qc );
+               # Show relevant lines from the suppression log
+               if( $wgUser->isAllowed( 'suppressionlog' ) ) {
+                       $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'suppress' ) ) . "</h2>\n" );
+                       LogEventsList::showLogExtract( $wgOut, 'suppress', $this->page->getPrefixedText(), '', $lim, $qc );
                }
+       }
+       
+       private function showConvenienceLinks() {
+               global $wgOut, $wgUser;
                # Give a link to the logs/hist for this page
                if( !is_null($this->page) && $this->page->getNamespace() > -1 ) {
                        $links = array();
-
                        $logtitle = SpecialPage::getTitleFor( 'Log' );
                        $links[] = $this->skin->makeKnownLinkObj( $logtitle, wfMsgHtml( 'viewpagelogs' ),
                                wfArrayToCGI( array( 'page' => $this->page->getPrefixedUrl() ) ) );
@@ -101,26 +114,60 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        # Logs themselves don't have histories or archived revisions
                        $wgOut->setSubtitle( '<p>'.implode($links,' / ').'</p>' );
                }
-               # Lock the operation and the form context
-               $this->secureOperation();
-               # Either submit or create our form
-               if( $this->wasPosted ) {
-                       $this->submit( $wgRequest );
-               } else if( $this->deleteKey == 'oldid' || $this->deleteKey == 'artimestamp' ) {
-                       $this->showRevs();
-               } else if( $this->deleteKey == 'fileid' || $this->deleteKey == 'oldimage' ) {
-                       $this->showImages();
-               } else if( $this->deleteKey == 'logid' ) {
-                       $this->showLogItems();
+       }
+       
+       private function getLogQueryCond() {
+               $ids = $safeIds = array();
+               $action = 'revision';
+               $limit = 25; // default
+               switch( $this->deleteKey ) {
+                       case 'oldid':
+                               $ids = $this->oldids;
+                               break;
+                       case 'artimestamp':
+                               $ids = $this->artimestamps;
+                               break;
+                       case 'oldimage':
+                               $ids = $this->oldimgs;
+                               break;
+                       case 'fileid':
+                               $ids = $this->fileids;
+                               break;
+                       case 'logid':
+                               $ids = $this->logids;
+                               $action = 'event';
+                               break;
+               }
+               // Revision delete logs
+               $conds = array( 'log_action' => $action );
+               // Just get the whole log if there are a lot if items
+               if( count($ids) > $limit )
+                       return array($conds,$limit);
+               // Digit chars only
+               foreach( $ids as $id ) {
+                       if( preg_match( '/^\d+$/', $id, $m ) ) {
+                               $safeIds[] = $m[0];
+                       }
                }
-               # Show relevant lines from the deletion log
-               $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'delete' ) ) . "</h2>\n" );
-               LogEventsList::showLogExtract( $wgOut, 'delete', $this->page->getPrefixedText() );
-               # Show relevant lines from the suppression log
-               if( $wgUser->isAllowed( 'suppressionlog' ) ) {
-                       $wgOut->addHTML( "<h2>" . htmlspecialchars( LogPage::logName( 'suppress' ) ) . "</h2>\n" );
-                       LogEventsList::showLogExtract( $wgOut, 'suppress', $this->page->getPrefixedText() );
+               // Optimization for logs
+               if( $action == 'event' ) {
+                       $dbr = wfGetDB( DB_SLAVE );
+                       # Get the timestamp of the first item
+                       $first = $dbr->selectField( 'logging', 'log_timestamp',
+                               array('log_id' => $safeIds), __METHOD__, array('ORDER BY' => 'log_id') );
+                       # If there are no items, then stop here
+                       if( $first == false ) {
+                               $conds = array('1=0');
+                               return array($conds,$limit);
+                       }
+                       # The event was be hidden after it was made
+                       $conds[] = 'log_timestamp > '.$dbr->addQuotes($first); // type,time index
+               }
+               // Format is <id1,id2,i3...>
+               if( count($safeIds) ) {
+                       $conds[] = "log_params RLIKE '(^|\n|,)(".implode('|',$safeIds).")(,|$)'";
                }
+               return array($conds,$limit);
        }
 
        private function secureOperation() {
@@ -164,20 +211,26 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
        /**
         * Show a deleted file version requested by the visitor.
         */
-       private function showFile( $key ) {
+       private function tryShowFile( $key ) {
                global $wgOut, $wgRequest;
-               $wgOut->disable();
-
-               # We mustn't allow the output to be Squid cached, otherwise
-               # if an admin previews a deleted image, and it's cached, then
-               # a user without appropriate permissions can toddle off and
-               # nab the image, and Squid will serve it
-               $wgRequest->response()->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
-               $wgRequest->response()->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
-               $wgRequest->response()->header( 'Pragma: no-cache' );
-
-               $store = FileStore::get( 'deleted' );
-               $store->stream( $key );
+               $oimage = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->page, $key );
+               $oimage->load();
+               // Check if user is allowed to see this file
+               if( !$oimage->userCan(File::DELETED_FILE) ) {
+                       $wgOut->permissionRequired( 'suppressrevision' );
+               } else {
+                       $wgOut->disable();
+                       # We mustn't allow the output to be Squid cached, otherwise
+                       # if an admin previews a deleted image, and it's cached, then
+                       # a user without appropriate permissions can toddle off and
+                       # nab the image, and Squid will serve it
+                       $wgRequest->response()->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
+                       $wgRequest->response()->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
+                       $wgRequest->response()->header( 'Pragma: no-cache' );
+                       # Stream the file to the client
+                       $store = FileStore::get( 'deleted' );
+                       $store->stream( $key );
+               }
        }
 
        /**
@@ -187,7 +240,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                global $wgOut, $wgUser;
                $UserAllowed = true;
 
-               $count = ($this->deleteKey=='oldid') ?
+               $count = ($this->deleteKey == 'oldid') ?
                        count($this->revisions) : count($this->archrevs);
                $wgOut->addWikiMsg( 'revdelete-selected', $this->page->getPrefixedText(), $count );
 
@@ -199,7 +252,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                
                $revisions = 0;
                // Live revisions...
-               if( $this->deleteKey=='oldid' ) {
+               if( $this->deleteKey == 'oldid' ) {
                        // Run through and pull all our data in one query
                        foreach( $this->revisions as $revid ) {
                                $where[] = intval($revid);
@@ -293,7 +346,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        Xml::hidden( 'target', $this->page->getPrefixedText() ),
                        Xml::hidden( 'type', $this->deleteKey )
                );
-               if( $this->deleteKey=='oldid' ) {
+               if( $this->deleteKey == 'oldid' ) {
                        $hidden[] = Xml::hidden( 'oldid', implode(',',$this->oldids) );
                } else {
                        $hidden[] = Xml::hidden( 'artimestamp', implode(',',$this->artimestamps) );
@@ -327,7 +380,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                global $wgOut, $wgUser, $wgLang;
                $UserAllowed = true;
 
-               $count = ($this->deleteKey=='oldimage') ? count($this->ofiles) : count($this->afiles);
+               $count = ($this->deleteKey == 'oldimage') ? count($this->ofiles) : count($this->afiles);
                $wgOut->addWikiMsg( 'revdelete-selected', $this->page->getPrefixedText(),
                        $wgLang->formatNum($count) );
 
@@ -338,7 +391,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                $dbr = wfGetDB( DB_MASTER );
                // Live old revisions...
                $revisions = 0;
-               if( $this->deleteKey=='oldimage' ) {
+               if( $this->deleteKey == 'oldimage' ) {
                        // Run through and pull all our data in one query
                        foreach( $this->ofiles as $timestamp ) {
                                $where[] = $timestamp.'!'.$this->page->getDBKey();
@@ -423,7 +476,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        Xml::hidden( 'target', $this->page->getPrefixedText() ),
                        Xml::hidden( 'type', $this->deleteKey )
                );
-               if( $this->deleteKey=='oldimage' ) {
+               if( $this->deleteKey == 'oldimage' ) {
                        $hidden[] = Xml::hidden( 'oldimage', implode(',',$this->oldimgs) );
                } else {
                        $hidden[] = Xml::hidden( 'fileid', implode(',',$this->fileids) );
@@ -512,7 +565,8 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        Xml::submitButton( wfMsg( 'revdelete-submit' ) ) );
                $hidden = array(
                        Xml::hidden( 'wpEditToken', $wgUser->editToken() ),
-                       Xml::hidden( 'target', $this->page->getPrefixedText() ),
+                       Xml::hidden( 'target', $this->page->getPrefixedDBKey() ),
+                       Xml::hidden( 'page', $this->contextPage ? $this->contextPage->getPrefixedDBKey() : '' ),
                        Xml::hidden( 'type', $this->deleteKey ),
                        Xml::hidden( 'logid', implode(',',$this->logids) )
                );
@@ -573,7 +627,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                $date = $wgLang->timeanddate( $rev->getTimestamp() );
                $difflink = $del = '';
                // Live revisions
-               if( $this->deleteKey=='oldid' ) {
+               if( $this->deleteKey == 'oldid' ) {
                        $tokenParams = '&unhide=1&token='.urlencode( $wgUser->editToken( $rev->getId() ) );
                        $revlink = $this->skin->makeLinkObj( $this->page, $date, 'oldid='.$rev->getId() . $tokenParams );
                        $difflink = '(' . $this->skin->makeKnownLinkObj( $this->page, wfMsgHtml('diff'),
@@ -635,7 +689,8 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        ' (' . wfMsgExt( 'nbytes', 'parsemag', $wgLang->formatNum( $file->getSize() ) ) . ')';
                $data = htmlspecialchars( $data );
 
-               return "<li>$pageLink ".$this->fileUserTools( $file )." $data ".$this->fileComment( $file )."$del</li>";
+               return "<li>$pageLink ".$this->fileUserTools( $file )." $data ".
+                       $this->fileComment( $file )."$del</li>";
        }
 
        /**
@@ -649,7 +704,8 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                $date = $wgLang->timeanddate( $file->getTimestamp(), true  );
 
                $undelete = SpecialPage::getTitleFor( 'Undelete' );
-               $pageLink = $this->skin->makeKnownLinkObj( $undelete, $date, "target=$target&file={$file->getKey()}" );
+               $pageLink = $this->skin->makeKnownLinkObj( $undelete, $date,
+                       "target=$target&file={$file->getKey()}" );
 
                $del = '';
                if( $file->isDeleted(File::DELETED_FILE) ) {
@@ -662,7 +718,8 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        ' (' . wfMsgExt( 'nbytes', 'parsemag', $wgLang->formatNum( $file->getSize() ) ) . ')';
                $data = htmlspecialchars( $data );
 
-               return "<li> $pageLink ".$this->fileUserTools( $file )." $data ".$this->fileComment( $file )."$del</li>";
+               return "<li>$pageLink ".$this->fileUserTools( $file )." $data ".
+                       $this->fileComment( $file )."$del</li>";
        }
 
        /**
@@ -775,16 +832,16 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
 
                $wrap = '<span class="success">$1</span>';
 
-               if( $this->deleteKey=='logid' ) {
+               if( $this->deleteKey == 'logid' ) {
                        $wgOut->wrapWikiMsg( $wrap, 'logdelete-success' );
                        $this->showLogItems();
-               } else if( $this->deleteKey=='oldid' || $this->deleteKey=='artimestamp' ) {
+               } else if( $this->deleteKey == 'oldid' || $this->deleteKey == 'artimestamp' ) {
                                $wgOut->wrapWikiMsg( $wrap, 'revdelete-success' );
                        $this->showRevs();
-               } else if( $this->deleteKey=='fileid' ) {
+               } else if( $this->deleteKey == 'fileid' ) {
                        $wgOut->wrapWikiMsg( $wrap, 'revdelete-success' );
                        $this->showImages();
-               } else if( $this->deleteKey=='oldimage' ) {
+               } else if( $this->deleteKey == 'oldimage' ) {
                        $wgOut->wrapWikiMsg( $wrap, 'revdelete-success' );
                        $this->showImages();
                }
@@ -1015,7 +1072,7 @@ class RevisionDeleter {
                                if( $filesObjs[$archivename]->deleted & File::DELETED_FILE ) {
                                        if( $bitfield & File::DELETED_FILE ) {
                                                # Leave it alone if we are not changing this...
-                                               $set[]=$archivename;
+                                               $set[]=$timestamp;
                                                $transaction = true;
                                        } else {
                                                # We are moving this out
@@ -1401,9 +1458,9 @@ class RevisionDeleter {
         * @param int $diff The xor of the old and new bitfields.
         * @param array $arr The array to update.
         */
-       function checkItem ( $desc, $field, $diff, $new, &$arr ) {
-               if ( $diff & $field ) {
-                       $arr [ ( $new & $field ) ? 0 : 1 ][] = $desc;
+       function checkItem( $desc, $field, $diff, $new, &$arr ) {
+               if( $diff & $field ) {
+                       $arr[ ( $new & $field ) ? 0 : 1 ][] = $desc;
                }
        }
 
@@ -1419,25 +1476,23 @@ class RevisionDeleter {
         * @param int $o The old bitfield.
         * @return An array as described above.
         */
-       function getChanges ( $n, $o ) {
+       function getChanges( $n, $o ) {
                $diff = $n ^ $o;
-               $ret = array ( 0 => array(), 1 => array(), 2 => array() );
-
-               $this->checkItem ( wfMsgForContent ( 'revdelete-content' ),
-                               Revision::DELETED_TEXT, $diff, $n, $ret );
-               $this->checkItem ( wfMsgForContent ( 'revdelete-summary' ),
-                               Revision::DELETED_COMMENT, $diff, $n, $ret );
-               $this->checkItem ( wfMsgForContent ( 'revdelete-uname' ),
-                               Revision::DELETED_USER, $diff, $n, $ret );
-
+               $ret = array( 0 => array(), 1 => array(), 2 => array() );
+               // Build bitfield changes in language
+               $this->checkItem( wfMsgForContent( 'revdelete-content' ),
+                       Revision::DELETED_TEXT, $diff, $n, $ret );
+               $this->checkItem( wfMsgForContent( 'revdelete-summary' ),
+                       Revision::DELETED_COMMENT, $diff, $n, $ret );
+               $this->checkItem( wfMsgForContent( 'revdelete-uname' ),
+                       Revision::DELETED_USER, $diff, $n, $ret );
                // Restriction application to sysops
-               if ( $diff & Revision::DELETED_RESTRICTED ) {
-                       if ( $n & Revision::DELETED_RESTRICTED )
-                               $ret[2][] = wfMsgForContent ( 'revdelete-restricted' );
+               if( $diff & Revision::DELETED_RESTRICTED ) {
+                       if( $n & Revision::DELETED_RESTRICTED )
+                               $ret[2][] = wfMsgForContent( 'revdelete-restricted' );
                        else
-                               $ret[2][] = wfMsgForContent ( 'revdelete-unrestricted' );
+                               $ret[2][] = wfMsgForContent( 'revdelete-unrestricted' );
                }
-
                return $ret;
        }
 
@@ -1452,35 +1507,28 @@ class RevisionDeleter {
         * @param string $comment The comment associated with the change.
         * @param bool $isForLog
         */
-       function getLogMessage ( $count, $nbitfield, $obitfield, $comment, $isForLog = false ) {
+       function getLogMessage( $count, $nbitfield, $obitfield, $comment, $isForLog = false ) {
                global $wgContLang;
 
                $s = '';
                $changes = $this->getChanges( $nbitfield, $obitfield );
 
-               if ( count ( $changes[0] ) ) {
+               if( count( $changes[0] ) ) {
                        $s .= wfMsgForContent ( 'revdelete-hid', implode ( ', ', $changes[0] ) );
                }
-
-               if ( count ( $changes[1] ) ) {
+               if( count( $changes[1] ) ) {
                        if ($s) $s .= '; ';
-
                        $s .= wfMsgForContent ( 'revdelete-unhid', implode ( ', ', $changes[1] ) );
                }
-
-               if ( count ( $changes[2] )) {
-                       if ($s)
-                               $s .= ' (' . $changes[2][0] . ')';
-                       else
-                               $s = $changes[2][0];
+               if( count( $changes[2] ) ) {
+                       $s .= $s ? ' (' . $changes[2][0] . ')' : $changes[2][0];
                }
 
                $msg = $isForLog ? 'logdelete-log-message' : 'revdelete-log-message';
                $ret = wfMsgExt ( $msg, array( 'parsemag', 'content' ),
                        $s, $wgContLang->formatNum( $count ) );
 
-               if ( $comment )
-                       $ret .= ": $comment";
+               if( $comment ) $ret .= ": $comment";
 
                return $ret;
 
@@ -1497,12 +1545,15 @@ class RevisionDeleter {
         * @param string $param, URL param
         * @param Array $items
         */
-       function updateLog( $title, $count, $nbitfield, $obitfield, $comment, $target, $param, $items = array() ) {
+       function updateLog( $title, $count, $nbitfield, $obitfield, $comment, $target,
+               $param, $items = array() )
+       {
                // Put things hidden from sysops in the oversight log
-               $logtype = ( ($nbitfield | $obitfield) & Revision::DELETED_RESTRICTED ) ? 'suppress' : 'delete';
+               $logtype = ( ($nbitfield | $obitfield) & Revision::DELETED_RESTRICTED ) ?
+                       'suppress' : 'delete';
                $log = new LogPage( $logtype );
 
-               $reason = $this->getLogMessage ( $count, $nbitfield, $obitfield, $comment, $param == 'logid' );
+               $reason = $this->getLogMessage( $count, $nbitfield, $obitfield, $comment, $param == 'logid' );
 
                if( $param == 'logid' ) {
                        $params = array( implode( ',', $items) );