Special:Log and the logging table -- unified logging scariness!
authorBrion Vibber <brion@users.mediawiki.org>
Tue, 24 Aug 2004 08:11:46 +0000 (08:11 +0000)
committerBrion Vibber <brion@users.mediawiki.org>
Tue, 24 Aug 2004 08:11:46 +0000 (08:11 +0000)
Replaces the ugly, often breaking, manually archived log pages with
a nice clean table which can be sorted, trimmed, viewed in pieces, etc.
You can see all logged actions by some user, or affecting some page,
and can combine the views of all all logs in one.

There are probably still some broken things in here, but I want this
committed before the patch gets any bigger.

recentchanges table is altered to make rc_namespace signed so Special:
links can be listed in it.

15 files changed:
RELEASE-NOTES
config/index.php
includes/Article.php
includes/Image.php
includes/LogPage.php
includes/SpecialBlockip.php
includes/SpecialIpblocklist.php
includes/SpecialLog.php [new file with mode: 0644]
includes/SpecialMakesysop.php
includes/SpecialPage.php
includes/SpecialUndelete.php
languages/Language.php
maintenance/archives/patch-logging.sql [new file with mode: 0644]
maintenance/tables.sql
maintenance/updaters.inc

index e3a5e45..45a90f1 100644 (file)
@@ -16,6 +16,7 @@ Major changes from 1.3.x:
 * 'Recentchanges Patrol' to mark new edits that haven't yet been viewed
 * Support for faster C++ diff module (WikiDiff extension)
 * More scary link caching modes
+* Old manually maintained log pages replaced with searchable Special:Log
 * ... and more!
 
 === Caveats ===
index 991e0e3..3df6ad8 100644 (file)
@@ -478,6 +478,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
                        do_objectcache_update(); flush();
                        do_categorylinks_update(); flush();
                        do_image_name_unique_update(); flush();
+                       do_logging_update(); flush();
 
                        if ( isTemplateInitialised() ) {
                                print "Template namespace already initialised\n";
index 03b2cf9..09742e0 100644 (file)
@@ -8,7 +8,6 @@
 # moved to separate EditPage and CacheManager classes.
 
 require_once ( 'CacheManager.php' );
-include_once ( 'SpecialValidate.php' ) ;
 
 $wgArticleCurContentFields = false;
 $wgArticleOldContentFields = false;
@@ -1088,6 +1087,7 @@ class Article {
        function validate () {
                global $wgOut, $wgUseValidation;
                if( $wgUseValidation ) {
+                       require_once ( 'SpecialValidate.php' ) ;
                        $wgOut->setPagetitle( wfMsg( 'validate' ) . ': ' . $this->mTitle->getPrefixedText() );
                        $wgOut->setRobotpolicy( 'noindex,follow' );
                        if( $this->mTitle->getNamespace() != 0 ) {
@@ -1210,11 +1210,11 @@ class Article {
                                ), 'Article::protect'
                        );
 
-                       $log = new LogPage( wfMsg( 'protectlogpage' ), wfMsg( 'protectlogtext' ) );
+                       $log = new LogPage( 'protect' );
                        if ( $limit === '' ) {
-                                       $log->addEntry( wfMsg( 'unprotectedarticle', $this->mTitle->getPrefixedText() ), $reason );
+                                       $log->addEntry( 'unprotect', $this->mTitle, $reason );
                        } else {
-                                       $log->addEntry( wfMsg( 'protectedarticle', $this->mTitle->getPrefixedText() ), $reason );
+                                       $log->addEntry( 'protect', $this->mTitle, $reason );
                        }
                        $wgOut->redirect( $this->mTitle->getFullURL() );
                        return;
@@ -1592,9 +1592,8 @@ class Article {
                $dbw->delete( 'categorylinks', array( 'cl_from' => $id ) );
 
                # Log the deletion
-               $log = new LogPage( wfMsg( 'dellogpage' ), wfMsg( 'dellogpagetext' ) );
-               $art = $this->mTitle->getPrefixedText();
-               $log->addEntry( wfMsg( 'deletedarticle', $art ), $reason );
+               $log = new LogPage( 'delete' );
+               $log->addEntry( 'delete', $this->mTitle, $reason );
 
                # Clear the cached article id so the interface doesn't act like we exist
                $this->mTitle->resetArticleID( 0 );
index 3b9ff5d..5a1af68 100644 (file)
@@ -443,11 +443,8 @@ function wfRecordUpload( $name, $oldver, $size, $desc, $copyStatus = "", $source
                $descTitle->invalidateCache();
        }
 
-       $log = new LogPage( wfMsg( 'uploadlogpage' ), wfMsg( 'uploadlogpagetext' ) );
-       $da = wfMsg( 'uploadedimage', '[[:' . $wgLang->getNsText(
-         Namespace::getImage() ) . ":{$name}|{$name}]]" );
-       $ta = wfMsg( 'uploadedimage', $name );
-       $log->addEntry( $da, $desc, $ta );
+       $log = new LogPage( 'upload' );
+       $log->addEntry( 'upload', $descTitle, $desc );
 }
 
 function wfImageArchiveUrl( $name )
index 0e5aea9..5260356 100644 (file)
@@ -1,58 +1,38 @@
 <?php
 #$Id$
 #
-# Class to simplify the use of log pages
+# Copyright (C) 2002, 2004 Brion Vibber <brion@pobox.com>
+# http://www.mediawiki.org/
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or 
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# http://www.gnu.org/copyleft/gpl.html
+
+# Class to simplify the use of log pages.
+# The logs are now kept in a table which is easier to manage and trim
+# than ever-growing wiki pages.
 
 class LogPage {
-       /* private */ var $mTitle, $mContent, $mContentLoaded, $mId, $mComment;
-       var $mUpdateRecentChanges ;
+       /* private */ var $type, $action, $comment;
+       var $updateRecentChanges = true;
 
-       function LogPage( $title, $defaulttext = "<ul>\n</ul>"  )
-       {
-               # For now, assume title is correct dbkey
-               # and log pages always go in Wikipedia namespace
-               $this->mTitle = str_replace( ' ', '_', $title );
-               $this->mId = 0;
-               $this->mUpdateRecentChanges = true ;
-               $this->mContentLoaded = false;
-               $this->getContent( $defaulttext );
+       function LogPage( $type ) {
+               # Type is one of 'block', 'protect', 'rights', 'delete', 'upload'
+               $this->type = $type;
        }
 
-       function getContent( $defaulttext = "<ul>\n</ul>" )
-       {
-               $fname = 'LogPage::getContent';
-
-               $dbw =& wfGetDB( DB_MASTER );
-               $s = $dbw->getArray( 'cur', 
-                       array( 'cur_id','cur_text','cur_timestamp' ),
-                       array( 'cur_namespace' => Namespace::getWikipedia(), 'cur_title' => $this->mTitle ), 
-                       $fname, 'FOR UPDATE' 
-               );
-
-               if( $s !== false ) {
-                       $this->mId = $s->cur_id;
-                       $this->mContent = $s->cur_text;
-                       $this->mTimestamp = wfTimestamp(TS_MW,$s->cur_timestamp);
-               } else {
-                       $this->mId = 0;
-                       $this->mContent = $defaulttext;
-                       $this->mTimestamp = wfTimestamp(TS_MW);
-               }
-               $this->mContentLoaded = true; # Well, sort of
-               
-               return $this->mContent;
-       }
-       
-       function getTimestamp()
-       {
-               if( !$this->mContentLoaded ) {
-                       $this->getContent();
-               }
-               return $this->mTimestamp;
-       }
-
-       function saveContent()
-       {
+       function saveContent() {
                if( wfReadOnly() ) return;
 
                global $wgUser;
@@ -61,127 +41,62 @@ class LogPage {
                $dbw =& wfGetDB( DB_MASTER );
                $uid = $wgUser->getID();
 
-               if( !$this->mContentLoaded ) return false;
-               $this->mTimestamp = $now = wfTimestampNow();
-               $won = wfInvertTimestamp( $now );
-               if($this->mId == 0) {
-                       $seqVal = $dbw->nextSequenceValue( 'cur_cur_id_seq' );
-
-                       # Note: this query will deadlock if another thread has called getContent(), 
-                       # at least in MySQL 4.0.17 InnoDB
-                       $dbw->insertArray( 'cur',
-                               array(
-                                       'cur_id' => $seqVal,
-                                       'cur_timestamp' => $dbw->timestamp($now),
-                                       'cur_user' => $uid,
-                                       'cur_user_text' => $wgUser->getName(),
-                                       'cur_namespace' => NS_WIKIPEDIA,
-                                       'cur_title' => $this->mTitle, 
-                                       'cur_text' => $this->mContent,
-                                       'cur_comment' => $this->mComment,
-                                       'cur_restrictions' => 'sysop',
-                                       'inverse_timestamp' => $won,
-                                       'cur_touched' => $dbw->timestamp($now),
-                               ), $fname
-                       );
-                       $this->mId = $dbw->insertId();
-               } else {
-                       $dbw->updateArray( 'cur',
-                               array( /* SET */ 
-                                       'cur_timestamp' => $dbw->timestamp($now),
-                                       'cur_user' => $uid, 
-                                       'cur_user_text' => $wgUser->getName(),
-                                       'cur_text' => $this->mContent,
-                                       'cur_comment' => $this->mComment,
-                                       'cur_restrictions' => 'sysop', 
-                                       'inverse_timestamp' => $won,
-                                       'cur_touched' => $dbw->timestamp($now),
-                               ), array( /* WHERE */
-                                       'cur_id' => $this->mId
-                               ), $fname
-                       );
-               }
+               $this->timestamp = $now = wfTimestampNow();
+               $dbw->insertArray( 'logging',
+                       array(
+                               'log_type' => $this->type,
+                               'log_action' => $this->action,
+                               'log_timestamp' => $dbw->timestamp( $now ),
+                               'log_user' => $uid,
+                               'log_namespace' => $this->target->getNamespace(),
+                               'log_title' => $this->target->getDBkey(),
+                               'log_comment' => $this->comment
+                       ), $fname
+               );
                
                # And update recentchanges
-               if ( $this->mUpdateRecentChanges ) {
-                       $titleObj = Title::makeTitle( Namespace::getWikipedia(), $this->mTitle );
-                       RecentChange::notifyLog( $now, $titleObj, $wgUser, $this->mComment );
+               if ( $this->updateRecentChanges ) {
+                       $rcComment = $this->actionText;
+                       if( '' != $this->comment ) {
+                               $rcComment .= ': ' . $this->comment;
+                       }
+                       $titleObj = Title::makeTitle( NS_SPECIAL, 'Log/' . $this->type );
+                       RecentChange::notifyLog( $now, $titleObj, $wgUser, $rcComment );
                }
                return true;
        }
 
-       function addEntry( $action, $comment, $textaction = '' )
-       {
-               global $wgLang, $wgUser;
-
-               $comment_esc = wfEscapeWikiText( $comment );
-
-               $this->getContent();
-
-               $ut = $wgUser->getName();
-               $uid = $wgUser->getID();
-               if( $uid ) {
-                       $ul = '[[' .
-                         $wgLang->getNsText( Namespace::getUser() ) .
-                         ":{$ut}|{$ut}]]";
-               } else {
-                       $ul = $ut;
-               }
-               
-               # Use the wiki-wide default date format instead of the user's setting
-               $d = $wgLang->timeanddate( wfTimestampNow(), false, MW_DATE_DEFAULT );
-
-               if( preg_match( "/^(.*?)<ul>(.*)$/sD", $this->mContent, $m ) ) {
-                       $before = $m[1];
-                       $after = $m[2];
-               } else {
-                       $before = '';
-                       $after = '';
-               }
-               
-               if($textaction)
-                       $this->mComment = $textaction;
-               else
-                       $this->mComment = $action;
-               
-               if ( '' == $comment ) {
-                       $inline = '';
+       /* static */ function actionText( $type, $action, $titleLink ) {
+               static $actions = array(
+                       'block/block' => 'blocklogentry',
+                       'block/unblock' => 'blocklogentry',
+                       'protect/protect' => 'protectedarticle',
+                       'protect/unprotect' => 'unprotectedarticle',
+                       'rights/rights' => 'bureaucratlogentry',
+                       'delete/delete' => 'deletedarticle',
+                       'delete/restore' => 'undeletedarticle',
+                       'upload/upload' => 'uploadedimage',
+                       'upload/revert' => 'uploadedimage',
+               );
+               $key = "$type/$action";
+               if( isset( $actions[$key] ) ) {
+                       return wfMsg( $actions[$key], $titleLink );
                } else {
-                       $inline = " <em>({$comment_esc})</em>";
-                       # comment gets escaped again, so we use the unescaped version
-                       $this->mComment .= ': '.$comment;
+                       wfDebug( "LogPage::actionText - unknown action $key\n" );
+                       return "$action $titleLink";
                }
-               $this->mContent = "{$before}<ul><li>{$d} {$ul} {$action}{$inline}</li>\n{$after}";
-               
-               # TODO: automatic log rotation...
-               
-               return $this->saveContent();
        }
        
-       function replaceContent( $text, $comment = '' )
-       {
-               $this->mContent = $text;
-               $this->mComment = $comment;
-               $this->mTimestamp = wfTimestampNow();
-               return $this->saveContent();
-       }
-
-       function showAsDisabledPage( $rawhtml = true )
-       {
-               global $wgLang, $wgOut;
-               if( $wgOut->checkLastModified( $this->getTimestamp() ) ){
-                       # Client cache fresh and headers sent, nothing more to do.
-                       return;
-               }
-               $func = ( $rawhtml ? 'addHTML' : 'addWikiText' );
-               $wgOut->$func(
-                       "<p>" . wfMsg( 'perfdisabled' ) . "</p>\n\n" .
-                       "<p>" . wfMsg( 'perfdisabledsub', $wgLang->timeanddate( $this->getTimestamp() ) ) . "</p>\n\n" .
-                       "<hr />\n\n" .
-                       $this->getContent()
-                       );
-               return;
+       function addEntry( $action, &$target, $comment ) {
+               global $wgLang, $wgUser;
                
+               $this->action = $action;
+               $this->target =& $target;
+               $this->comment = $comment;
+               $this->actionText = LogPage::actionText( $this->type, $action,
+                       $target->getPrefixedText() );
+                               
+               return $this->saveContent();
        }
 }
 
index 77ed430..42f11eb 100644 (file)
@@ -153,9 +153,8 @@ class IPBlockForm {
                $ban->insert();
 
                # Make log entry
-               $log = new LogPage( wfMsg( "blocklogpage" ), wfMsg( "blocklogtext" ) );
-               $action = wfMsg( "blocklogentry", $this->BlockAddress, $this->BlockExpiry );
-               $log->addEntry( $action, $this->BlockReason );
+               $log = new LogPage( 'block' );
+               $log->addEntry( 'block', Title::makeTitle( NS_USER, $this->BlockAddress ), $this->BlockReason );
 
                # Report to the user
                $titleObj = Title::makeTitle( NS_SPECIAL, "Blockip" );
index b2f535b..cf15f4d 100644 (file)
@@ -96,9 +96,8 @@ class IPUnblockForm {
                $block->delete();
 
                # Make log entry
-               $log = new LogPage( wfMsg( "blocklogpage" ), wfMsg( "blocklogtext" ) );
-               $action = wfMsg( "unblocklogentry", $this->ip );
-               $log->addEntry( $action, $this->reason );
+               $log = new LogPage( 'block' );
+               $log->addEntry( 'unblock', Title::makeTitle( NS_USER, $this->ip ), $this->reason );
 
                # Report to the user
                $titleObj = Title::makeTitle( NS_SPECIAL, "Ipblocklist" );
diff --git a/includes/SpecialLog.php b/includes/SpecialLog.php
new file mode 100644 (file)
index 0000000..f2fda40
--- /dev/null
@@ -0,0 +1,267 @@
+<?php
+# Copyright (C) 2004 Brion Vibber <brion@pobox.com>
+# http://www.mediawiki.org/
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or 
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# http://www.gnu.org/copyleft/gpl.html
+
+function wfSpecialLog( $par = '' ) {
+       global $wgRequest;
+       $logReader =& new LogReader( $wgRequest );
+       if( '' == $wgRequest->getVal( 'type' ) && !empty( $par ) ) {
+               $logReader->limitType( $par );
+       }
+       $logViewer =& new LogViewer( $logReader );
+       $logViewer->show();
+       $logReader->cleanUp();
+}
+
+class LogReader {
+       var $db, $joinClauses, $whereClauses;
+       var $type = '', $user = '', $title = null;
+       
+       function LogReader( $request ) {
+               $this->db =& wfGetDB( DB_SLAVE );
+               $this->setupQuery( $request );
+       }
+       
+       function setupQuery( $request ) {
+               $cur = $this->db->tableName( 'cur' );
+               $user = $this->db->tableName( 'user' );
+               $this->joinClauses = array( "LEFT OUTER JOIN $cur ON log_namespace=cur_namespace AND log_title=cur_title" );
+               $this->whereClauses = array( 'user_id=log_user' );
+               
+               $this->limitType( $request->getVal( 'type' ) );
+               $this->limitUser( $request->getText( 'user' ) );
+               $this->limitTitle( $request->getText( 'page' ) );
+               $this->limitTime( $request->getVal( 'from' ), '>=' );
+               $this->limitTime( $request->getVal( 'until' ), '<=' );
+               
+               list( $this->limit, $this->offset ) = $request->getLimitOffset();
+       }
+       
+       function limitType( $type ) {
+               if( empty( $type ) ) {
+                       return false;
+               }
+               $this->type = $type;
+               $safetype = $this->db->strencode( $type );
+               $this->whereClauses[] = "log_type='$safetype'";
+       }
+       
+       function limitUser( $name ) {
+               $title = Title::makeTitleSafe( NS_USER, $name );
+               if( empty( $name ) || is_null( $title ) ) {
+                       return false;
+               }
+               $this->user = str_replace( '_', ' ', $title->getDBkey() );
+               $safename = $this->db->strencode( $this->user );
+               $user = $this->db->tableName( 'user' );
+               $this->whereClauses[] = "user_name='$safename'";
+       }
+       
+       function limitTitle( $page ) {
+               $title = Title::newFromText( $page );
+               if( empty( $page ) || is_null( $title )  ) {
+                       return false;
+               }
+               $this->title =& $title;
+               $safetitle = $this->db->strencode( $title->getDBkey() );
+               $ns = $title->getNamespace();
+               $this->whereClauses[] = "log_namespace=$ns AND log_title='$safetitle'";
+       }
+       
+       function limitTime( $time, $direction ) {
+               # Direction should be a comparison operator
+               if( empty( $time ) ) {
+                       return false;
+               }
+               $safetime = $this->db->strencode( wfTimestamp( TS_MW, $time ) );
+               $this->whereClauses[] = "log_timestamp $direction '$safetime'";
+       }
+       
+       function getQuery() {
+               $logging = $this->db->tableName( "logging" );
+               $user = $this->db->tableName( 'user' );
+               $sql = "SELECT log_type, log_action, log_timestamp,
+                       log_user, user_name,
+                       log_namespace, log_title, cur_id,
+                       log_comment FROM $logging, $user ";
+               if( !empty( $this->joinClauses ) ) {
+                       $sql .= implode( ',', $this->joinClauses );
+               }
+               if( !empty( $this->whereClauses ) ) {
+                       $sql .= " WHERE " . implode( ' AND ', $this->whereClauses );
+               }
+               $sql .= " ORDER BY log_timestamp DESC ";
+               $sql .= $this->db->limitResult( $this->limit, $this->offset );
+               return $sql;
+       }
+       
+       function initQuery() {
+               $this->result = $this->db->query( $this->getQuery() );
+       }
+       
+       function fetchObject() {
+               return $this->db->fetchObject( $this->result );
+       }
+       
+       function cleanUp() {
+               $this->db->freeResult( $this->result );
+       }
+       
+       function queryType() {
+               return $this->type;
+       }
+       
+       function queryUser() {
+               return $this->user;
+       }
+       
+       function queryTitle() {
+               if( is_null( $this->title ) ) {
+                       return '';
+               } else {
+                       return $this->title->getPrefixedText();
+               }
+       }
+}
+
+class LogViewer {
+       var $reader, $skin;
+       var $typeText = array(
+               ''        => array( 'log',            'alllogstext'       ),
+               'block'   => array( 'blocklogpage',   'blocklogtext'      ),
+               'protect' => array( 'protectlogpage', 'protectlogtext'    ),
+               'rights'  => array( 'bureaucratlog',  ''                  ),
+               'delete'  => array( 'dellogpage',     'dellogpagetext'    ),
+               'upload'  => array( 'uploadlogpage',  'uploadlogpagetext' )
+       );
+
+       
+       function LogViewer( &$reader ) {
+               global $wgUser;
+               $this->skin =& $wgUser->getSkin();
+               $this->reader =& $reader;
+       }
+       
+       function show() {
+               global $wgOut;
+               $this->showHeader( $wgOut );
+               $this->showOptions( $wgOut );
+               $this->showPrevNext( $wgOut );
+               $out = "";
+               $this->reader->initQuery();
+               while( $s = $this->reader->fetchObject() ) {
+                       $out .= $this->logLine( $s );
+               }
+               $wgOut->addHTML( $out );
+               $this->showPrevNext( $wgOut );
+       }
+       
+       # wfMsg( unprotectedarticle, $text )
+       # wfMsg( 'protectedarticle', $text )
+       # wfMsg( 'deletedarticle', $text )
+       # wfMsg( 'uploadedimage', $text )
+       # wfMsg( "blocklogentry", $this->BlockAddress, $this->BlockExpiry );
+       # wfMsg( "bureaucratlogentry", $this->mUser, implode( " ", $rightsNotation ) );
+       # wfMsg( "undeletedarticle", $this->mTarget ),
+       function logLine( $s ) {
+               global $wgLang;
+               $title = Title::makeTitle( $s->log_namespace, $s->log_title );
+               $user = Title::makeTitleSafe( NS_USER, $s->user_name );
+               $time = $wgLang->timeanddate( $s->log_timestamp );
+               if( $s->cur_id ) {
+                       $titleLink = $this->skin->makeKnownLinkObj( $title );
+               } else {
+                       $titleLink = $this->skin->makeBrokenLinkObj( $title );
+               }
+               $userLink = $this->skin->makeLinkObj( $user, htmlspecialchars( $s->user_name ) );
+               if( '' === $s->log_comment ) {
+                       $comment = '';
+               } else {
+                       $comment = '(<em>' . $this->skin->formatComment( $s->log_comment ) . '</em>)';
+               }
+               
+               $action = LogPage::actionText( $s->log_type, $s->log_action, $titleLink );
+               $out = "<li>$time $userLink $action $comment</li>\n";
+               return $out;
+       }
+       
+       function showHeader( &$out ) {
+               $type = $this->reader->queryType();
+               if( isset( $this->typeText[$type] ) ) {
+                       list( $title, $headertext ) = $this->typeText[$type];
+                       $out->setPageTitle( str_replace( '_', ' ', wfMsg( $title ) ) );
+                       $out->addWikiText( wfMsg( $headertext ) );
+               }
+       }
+       
+       function showOptions( &$out ) {
+               global $wgScript;
+               $action = htmlspecialchars( $wgScript );
+               $title = Title::makeTitle( NS_SPECIAL, 'Log' );
+               $special = htmlspecialchars( $title->getPrefixedDBkey() );
+               $out->addHTML( "<form action=\"$action\" method=\"get\">\n" .
+                       "<input type='hidden' name='title' value=\"$special\" />\n" .
+                       $this->getTypeMenu() .
+                       $this->getUserInput() .
+                       $this->getTitleInput() .
+                       "<input type='submit' />" .
+                       "</form>" );
+       }
+       
+       function getTypeMenu() {
+               $out = "<select name='type'>\n";
+               foreach( $this->typeText as $type => $msg ) {
+                       $text = htmlspecialchars( str_replace( '_', ' ', wfMsg( $msg[0] ) ) );
+                       $selected = ($type == $this->reader->queryType()) ? ' selected="selected"' : '';
+                       $out .= "<option value=\"$type\"$selected>$text</option>\n";
+               }
+               $out .= "</select>\n";
+               return $out;
+       }
+       
+       function getUserInput() {
+               $user = htmlspecialchars( $this->reader->queryUser() );
+               return "User: <input type='text' name='user' size='12' value=\"$user\" />\n";
+       }
+       
+       function getTitleInput() {
+               $title = htmlspecialchars( $this->reader->queryTitle() );
+               return "Title: <input type='text' name='page' size='20' value=\"$title\" />\n";
+       }
+       
+       function showPrevNext( &$out ) {
+               global $wgLang;
+               $pieces = array();
+               $pieces[] = 'type=' . htmlspecialchars( $this->reader->queryType() );
+               $pieces[] = 'user=' . htmlspecialchars( $this->reader->queryUser() );
+               $pieces[] = 'page=' . htmlspecialchars( $this->reader->queryTitle() );
+               $bits = implode( '&', $pieces );
+               $offset = 0; $limit = 50;
+               
+               # TODO: use timestamps instead of offsets to make it more natural
+               # to go huge distances in time
+               $html = wfViewPrevNext( $offset, $limit,
+                       $wgLang->specialpage( 'Log' ),
+                       $bits,
+                       false);
+               $out->addHTML( '<p>' . $html . '</p>' );
+       }
+}
+
+
+?>
\ No newline at end of file
index 0c1aa55..f6393ad 100644 (file)
@@ -194,11 +194,9 @@ class MakesysopForm {
                        $dbw->query($sql);
                        $wgMemc->delete( "$dbName:user:id:$id" );
                        
-                       $bureaucratLog = wfMsg( "bureaucratlog" );
-                       $action = wfMsg( "bureaucratlogentry", $this->mUser, implode( " ", $rightsNotation ) );
-                       
-                       $log = new LogPage( $bureaucratLog );
-                       $log->addEntry( $action, "" );
+                       $log = new LogPage( 'rights' );
+                       $log->addEntry( 'rights', Title::makeTitle( NS_USER, $this->mUser ),
+                               implode( " ", $rightsNotation ) );
                        
                        $this->showSuccess();
                }
index a497da4..92c18d6 100644 (file)
@@ -62,6 +62,7 @@ $wgSpecialPages = array_merge($wgSpecialPages, array (
        "Version"               => new SpecialPage( "Version" ),
        "Allmessages"   => new SpecialPage( "Allmessages" ),
        "Search"                => new UnlistedSpecialPage( "Search" ),
+       "Log"           => new SpecialPage( "Log" ),
        "Blockip"               => new SpecialPage( "Blockip", "sysop" ),
        "Asksql"                => new SpecialPage( "Asksql", "sysop" ),
        "Undelete"              => new SpecialPage( "Undelete", "sysop" ),
index eed049b..b65d67b 100644 (file)
@@ -247,8 +247,8 @@ class UndeleteForm {
 
                
                # Touch the log?
-               $log = new LogPage( wfMsg( "dellogpage" ), wfMsg( "dellogpagetext" ) );
-               $log->addEntry( wfMsg( "undeletedarticle", $this->mTarget ), "" );
+               $log = new LogPage( 'delete' );
+               $log->addEntry( 'restore', Title::makeTitle( $namespace, $title ), "" );
 
                $wgOut->addWikiText( wfMsg( "undeletedtext", $this->mTarget ) );
                return 0;
index 7b275e2..85e4a84 100644 (file)
@@ -1014,6 +1014,9 @@ this list should not be construed as an endorsement.",
 'rfcurl' =>  "http://www.faqs.org/rfcs/rfc$1.html",
 'alphaindexline' => "$1 to $2",
 'version'              => 'Version',
+'log'          => 'Logs',
+'alllogstext'  => 'Combined display of upload, deletion, protection, blocking, and sysop logs.
+You can narrow down the view by selecting a log type, the user name, or the affected page.',
 
 # Special:Allpages
 'nextpage'          => 'Next page ($1)',
diff --git a/maintenance/archives/patch-logging.sql b/maintenance/archives/patch-logging.sql
new file mode 100644 (file)
index 0000000..7922fa6
--- /dev/null
@@ -0,0 +1,32 @@
+-- Add the logging table and adjust recentchanges to accomodate special pages
+-- 2004-08-24
+
+CREATE TABLE logging (
+  -- Symbolic keys for the general log type and the action type
+  -- within the log. The output format will be controlled by the
+  -- action field, but only the type controls categorization.
+  log_type char(10) NOT NULL default '',
+  log_action char(10) NOT NULL default '',
+  
+  -- Timestamp. Duh.
+  log_timestamp char(14) NOT NULL default '19700101000000',
+  
+  -- The user who performed this action; key to user_id
+  log_user int unsigned NOT NULL default 0,
+  
+  -- Key to the page affected. Where a user is the target,
+  -- this will point to the user page.
+  log_namespace tinyint unsigned NOT NULL default 0,
+  log_title varchar(255) NOT NULL default '',
+  
+  -- Freeform text. Interpreted as edit history comments.
+  log_comment varchar(255) NOT NULL default '',
+  
+  KEY type_time (log_type, log_timestamp),
+  KEY user_time (log_user, log_timestamp),
+  KEY page_time (log_namespace, log_title, log_timestamp)
+);
+
+-- Change from unsigned to signed so we can store special pages
+ALTER TABLE recentchanges
+  MODIFY rc_namespace tinyint(3) NOT NULL default '0';
index b726f75..892c271 100644 (file)
@@ -180,7 +180,7 @@ CREATE TABLE recentchanges (
   rc_cur_time varchar(14) binary NOT NULL default '',
   rc_user int(10) unsigned NOT NULL default '0',
   rc_user_text varchar(255) binary NOT NULL default '',
-  rc_namespace tinyint(3) unsigned NOT NULL default '0',
+  rc_namespace tinyint(3) NOT NULL default '0',
   rc_title varchar(255) binary NOT NULL default '',
   rc_comment varchar(255) binary NOT NULL default '',
   rc_minor tinyint(3) unsigned NOT NULL default '0',
@@ -265,4 +265,31 @@ CREATE TABLE `validate` (
   `val_value` int(11) default '0',
   `val_comment` varchar(255) NOT NULL default '',
   KEY `val_user` (`val_user`,`val_title`,`val_timestamp`)
-) TYPE=MyISAM;
+);
+
+
+CREATE TABLE logging (
+  -- Symbolic keys for the general log type and the action type
+  -- within the log. The output format will be controlled by the
+  -- action field, but only the type controls categorization.
+  log_type char(10) NOT NULL default '',
+  log_action char(10) NOT NULL default '',
+  
+  -- Timestamp. Duh.
+  log_timestamp char(14) NOT NULL default '19700101000000',
+  
+  -- The user who performed this action; key to user_id
+  log_user int unsigned NOT NULL default 0,
+  
+  -- Key to the page affected. Where a user is the target,
+  -- this will point to the user page.
+  log_namespace tinyint unsigned NOT NULL default 0,
+  log_title varchar(255) NOT NULL default '',
+  
+  -- Freeform text. Interpreted as edit history comments.
+  log_comment varchar(255) NOT NULL default '',
+  
+  KEY type_time (log_type, log_timestamp),
+  KEY user_time (log_user, log_timestamp),
+  KEY page_time (log_namespace, log_title, log_timestamp)
+);
index d5075f3..54b3f04 100644 (file)
@@ -205,4 +205,16 @@ function do_image_name_unique_update() {
                echo "ok\n";
        }
 }
+
+function do_logging_update() {
+       global $wgDatabase;
+       if ( $wgDatabase->tableExists( 'logging' ) ) {
+               echo "...logging table already unique.\n";
+       } else {
+               echo "Creating logging table and adjusting recentchanges... ";
+               dbsource( "maintenance/archives/patch-logging.sql", $wgDatabase );
+               echo "ok\n";
+       }
+}
+
 ?>