Extension:UserSnoop

Description
The UserSnoop MediaWiki extension/special page

This extension creates a new special page through which a user with the appropriate permissions ('usersnoop' and 'sysop') can view and manipulate users.

The main section is display of user information: If the calling user is a member of the 'bureaucrat' group then they also can:
 * internal user id
 * username
 * real name
 * email address
 * whether the user has new talk or not
 * date of registration
 * number of edits
 * whether the user is blocked or not
 * block reason (if blocked)
 * list of *effective* groups
 * last update of user profile
 * user's signature
 * user's last login
 * force the user logout (boot user)
 * block the user
 * spread the block on the user to all of his/her ip addresses
 * unblock the user

The actions on information that can be called on the target user:
 * page views
 * page name
 * number of visits
 * last visit
 * watchlist
 * page name
 * last visit
 * last edit by user
 * last edit by any user
 * new pages
 * page name
 * create date
 * last edit by user
 * last edit by any user

Installation

 * 1) Run UserPageViews.sql on your wiki db
 * 2) Run UserPageHits.sql on your wiki db
 * 3) Copy and paste the source code to UserSnoop.php
 * 4) Edit your LocalSettings.php to append:

UserPageViews.sql
replace "wiki_" with the relevant prefix (if any) used for your database.

UserPageHits.sql
replace "wiki_" (4 instances) with the relevant prefix (if any) used for your database.

UserSnoop.php
\n \n";               }                return $out;          }        function sandboxParse($wikiText) {                        global $wgTitle, $wgUser;                        $myParser = new Parser;                        $myParserOptions = new ParserOptions;                        $myParserOptions->initialiseFromUser($wgUser);                        $result = $myParser->parse($wikiText, $wgTitle, $myParserOptions);                        return $result->getText;        }        function blockUser($user_id = 0) {                global $wgUser;                if($user_id == 0) {                        $user_id = $this->uid;                }                $blk = new Block($this->targetUser, $user_id, $wgUser->getID, wfMsg('usersnoopblockmessage'),                                wfTimestamp, 0, Block::infinity, 0, 1, 0, 0, 1);                if($blk->insert) {                        $log = new LogPage('block'); $log->addEntry('block', Title::makeTitle( NS_USER, $this->targetUser ),                                'Blocked through Special:UserSnoop', array('infinite', 'nocreate')); }       }        function unblockUser($user_id = 0) { global $wgUser; if($user_id == 0) { $user_id = $this->uid; }       $dbr = wfGetDB(DB_SLAVE); $ipb_id = $dbr->selectField('ipblocks', 'ipb_id', array('ipb_user'=>$user_id), __METHOD__); $blk = Block::newFromId($ipb_id); if($blk->delete) { $log = new LogPage('block'); $log->addEntry('unblock', Title::makeTitle(NS_USER, $this->targetUser), wfMsg('usersnoopunblockmessage')); }        }        function loadAllMessages { static $messagesLoaded = false; if(!$messagesLoaded) { global $wgMessageCache; #todo: add more languages $wgMessageCache->addMessages(                               array('usersnoop' => 'User snoop', 'usersnoopusername' => 'Username', 'usersnoopaction' => 'Action', 'usersnoopnone' => '(none)', 'usersnooppageviews' => 'Page views', 'usersnoopwatchlist' => 'Watchlist', 'usersnoopnewpages' => 'New pages', 'usersnoopsubmit' => 'Submit', 'usersnoopid' => 'ID', 'usersnooprealname' => 'Real name', 'usersnoopnewtalk' => 'New talk', 'usersnoopregistered' => 'Registered', 'usersnoopedits' => 'Edits', 'usersnoopyes' => 'Yes', 'usersnoopno' => 'No', 'usersnoopblk' => 'Blk', 'usersnoopblockreason' => 'Block reason', 'usersnoopgroups' => 'Groups', 'usersnooplastupdated' => 'Last updated', 'usersnoopsignature' => 'Signature', 'usersnooplastlogin' => 'Last login', 'usersnoopforcelogout' => 'Force logout', 'usersnoopblock' => 'Block', 'usersnoopspreadblock' => 'Spread block', 'usersnoopunblock' => 'Unblock', 'usersnoopblockmessage' => 'Blocked through Special:UserSnoop', 'usersnoopunblockmessage' => 'Unblocked through Special:UserSnoop', 'usersnooppage' => 'Page', 'usersnoophits' => 'Hits', 'usersnooplast' => 'Last', 'usersnooplastvisit' => 'Last visit', 'usersnooplasteditedbythisuser' => 'Last edited by this user', 'usersnooplasteditedbyanyuser' => 'Last edited by any user', 'usersnoopcreated' => 'Created')                               ,'en'); $messagesLoaded = true; }               return true; }       function loadLocalizedName(&$specialPageArray, $code) { self::loadAllMessages; $text = wfMsg('usersnoop'); $title = Title::newFromText($text); $specialPageArray['UserSnoop'][] = $title->getDBKey; return true; } } require_once("$IP/includes/Pager.php"); class UserSnoopPager extends AlphabeticPager {       protected $targetUser = ""; //our victim protected $uid = 0;       //our victim's uid #constructor - also inilizes "stuff" function __construct($uid=0) { if($uid > 0) { $this->uid = $uid; $dbr = wfGetDB(DB_SLAVE); $this->targetUser = $dbr->selectField('user','user_name',array('user_id'=>$this->uid),__METHOD__); } else { global $wgRequest; $this->targetUser = $wgRequest->getVal( 'username' ); if($this->targetUser != "") { $dbr = wfGetDB(DB_SLAVE); $this->uid = $dbr->selectField('user','user_id',array('user_name'=>$this->targetUser),__METHOD__); }               }                parent::__construct; }       function getBodyHeader { }       function getBodyFooter { }       function getBody { if (!$this->mQueryDone) { $this->doQuery; }               $batch = new LinkBatch; $db = $this->mDb; $this->mResult->rewind; $batch->execute; $this->mResult->rewind; return parent::getBody; }       function formatRow($row) { }       function getQueryInfo { }       function getIndexField { }       function getPageHeader { }       function getDefaultQuery { global $wgRequest; $query = parent::getDefaultQuery; if($this->targetUser != '') { $query['username'] = $this->targetUser; }               $query['action'] = $wgRequest->getVal('action'); return $query; }       function sandboxParse($wikiText) { global $wgTitle, $wgUser; $myParser = new Parser; $myParserOptions = new ParserOptions; $myParserOptions->initialiseFromUser($wgUser); $result = $myParser->parse($wikiText, $wgTitle, $myParserOptions); return $result->getText; } } class UserSnoopPagerPageviews extends UserSnoopPager{
 * 1) include the pager class
 * 1) create a custom pager for our extension
 * 2) all other pagers will inherit this one
 * 1) pager class used for user page views

function UserSnoopPagerPageviews($uid=0) { parent::__construct($uid); }       function getIndexField { return "rownum"; }       function getBodyHeader { $s = ' ";               return $s;        }        function getQueryInfo {		global $wgDBprefix;                list ($userpagehits) = wfGetDB->tableNamesN($wgDBprefix.'user_page_hits');                $conds = array;

$table = "(select @rownum:=@rownum+1 as rownum,";               $table .= "user_name, user_real_name, page_namespace, page_title,hits, last ";                $table .= "from (select @rownum:=0) r, ";                $table .= "(select user_name, user_real_name, page_namespace, page_title,hits,"; $table .= "last from ".$wgDBprefix."user_page_hits) p";               if($this->targetUser) {                        $table .= " where user_name = ";                        $table .= wfGetDB->addQuotes($this->targetUser);                }                $table .= ") results"; return array(                       'tables' => " $table ",                        'fields' => array( 'rownum', 'user_name', 'user_real_name', 'page_namespace', 'page_title', 'hits', "concat(substr(last, 1, 4),'-',substr(last,5,2),'-',substr(last,7,2),' ',substr(last,9,2),':',substr(last,11,2),':',substr(last,13,2)) AS last"),                       'conds' => $conds                ); }       function formatRow( $row ) { $userPage = Title::makeTitle( NS_USER, $row->user_name ); $name = $this->getSkin->makeLinkObj( $userPage, htmlspecialchars( $userPage->getText ) ); $pageTitle = Title::makeTitle( $row->page_namespace, $row->page_title ); if($row->page_namespace > 0) { $pageFullName = $pageTitle->getNsText.":".htmlspecialchars($pageTitle->getText ); } else { $pageFullName = htmlspecialchars( $pageTitle->getText); }               $page = $this->getSkin->makeLinkObj( $pageTitle, $pageFullName ); $res = ' '; $res .= "$row->rownum "; $res .= " $page "; $res .= "$row->hits "; $res .= "$row->last "; $res .= " \n"; return $res; } } class UserSnoopPagerWatchlist extends UserSnoopPager { function getIndexField { return "rownum"; }       function getBodyHeader { $s = ' ";               return $s;        }        function getQueryInfo {		global $wgDBprefix;                $conds = array;                $table = "(select @rownum:=@rownum+1 as rownum, wl_user userid, wl_namespace namespace, wl_title title, `last` lastview, lastedit, lastuseredit";                $table .= " from ".$wgDBprefix."page, ".$wgDBprefix."watchlist, ".$wgDBprefix."user_page_views,";                $table .= " (select @rownum:=0) r,";                $table .= " (select rev_page, max(rev_timestamp) lastedit from ".$wgDBprefix."revision group by rev_page) rev1,";                $table .= " (select rev_user, rev_page, max(rev_timestamp) lastuseredit from ".$wgDBprefix."revision group by rev_user, rev_page) rev2";                $table .= " where ".$wgDBprefix."page.page_namespace = wl_namespace and ".$wgDBprefix."page.page_title = wl_title";                $table .= " and rev1.rev_page = ".$wgDBprefix."page.page_id"; $table .= " and rev2.rev_user = wl_user"; $table .= " and rev2.rev_page = ".$wgDBprefix."page.page_id"; $table .= " and ".$wgDBprefix."user_page_views.user_id = wl_user"; $table .= " and ".$wgDBprefix."user_page_views.page_id = ".$wgDBprefix."page.page_id"; if($this->targetUser) { $table .= " and wl_user = ".$this->uid; }               $table .= ") result";                return array( 'tables' => " $table ", 'fields' => array( 'rownum',                               'namespace',                                'title',                                "concat(substr(lastview, 1, 4),'-',substr(lastview,5,2),'-',substr(lastview,7,2),' ',substr(lastview,9,2),':',substr(lastview,11,2),':',substr(lastview,13,2)) AS lastview",                                "concat(substr(lastedit, 1, 4),'-',substr(lastedit,5,2),'-',substr(lastedit,7,2),' ',substr(lastedit,9,2),':',substr(lastedit,11,2),':',substr(lastedit,13,2)) AS lastedit",                                "concat(substr(lastuseredit, 1, 4),'-',substr(lastuseredit,5,2),'-',substr(lastuseredit,7,2),' ',substr(lastuseredit,9,2),':',substr(lastuseredit,11,2),':',substr(lastuseredit,13,2)) AS lastuseredit"                                ), 'conds' => $conds );       }        function formatRow( $row ) {                $pageTitle = Title::makeTitle( $row->namespace, $row->title );                if($row->namespace > 0) {                        $pageFullName = $pageTitle->getNsText.":".htmlspecialchars($pageTitle->getText );                } else {                        $pageFullName = htmlspecialchars( $pageTitle->getText);                }                $page = $this->getSkin->makeLinkObj( $pageTitle, $pageFullName );                $res = ' ';                $res .= "$row->rownum ";                $res .= " $page ";                $res .= "$row->lastview ";                $res .= "$row->lastuseredit ";                $res .= "$row->lastedit ";                $res .= " \n";                return $res; } } class UserSnoopPagerNewPages extends UserSnoopPager { function getIndexField { return "rownum"; }       function getBodyHeader { $s = ' ";               return $s;        }        function getQueryInfo {		global $wgDBprefix;                list ($userpagehits) = wfGetDB->tableNamesN($wgDBprefix.'user_page_hits');                $conds = array;                $table = "(select @rownum:=@rownum+1 as rownum, ".$wgDBprefix."revision.rev_user user_id, namespace, title, created, lastuseredit, lastedit";                $table .= " from ".$wgDBprefix."revision, (select @rownum:=0) r,";                $table .= " (SELECT page_namespace namespace, page_title title, page_id pageid, min(rev_timestamp) created";                $table .= " FROM ".$wgDBprefix."revision, ".$wgDBprefix."page";                $table .= " where ".$wgDBprefix."page.page_id = ".$wgDBprefix."revision.rev_page";                $table .= " group by namespace, title, pageid) revs,";                $table .= " (select rev_page, max(rev_timestamp) lastedit from ".$wgDBprefix."revision group by rev_page) lastedit,"; $table .= " (select rev_page, rev_user, max(rev_timestamp) lastuseredit from ".$wgDBprefix."revision group by rev_page,";               $table .= " rev_user) lastuseredit"; $table .= " where ".$wgDBprefix."revision.rev_page = revs.pageid"; $table .= " and ".$wgDBprefix."revision.rev_timestamp = revs.created"; $table .= " and lastedit.rev_page = revs.pageid"; $table .= " and lastuseredit.rev_page = revs.pageid"; $table .= " and lastuseredit.rev_user = ".$wgDBprefix."revision.rev_user"; if($this->targetUser) { $table .= " and ".$wgDBprefix."revision.rev_user = ".$this->uid; }               $table .= ") res";                return array( 'tables' => " $table ", 'fields' => array( 'rownum',                                                                       'namespace',                                                                        'title',                                                                        "concat(substr(created, 1, 4),'-',substr(created,5,2),'-',substr(created,7,2),' ',substr(created,9,2),':',substr(created,11,2),':',substr(created,13,2)) AS created",                                                                        "concat(substr(lastuseredit, 1, 4),'-',substr(lastuseredit,5,2),'-',substr(lastuseredit,7,2),' ',substr(lastuseredit,9,2),':',substr(lastuseredit,11,2),':',substr(lastuseredit,13,2)) AS lastuseredit",                                                                        "concat(substr(lastedit, 1, 4),'-',substr(lastedit,5,2),'-',substr(lastedit,7,2),' ',substr(lastedit,9,2),':',substr(lastedit,11,2),':',substr(lastedit,13,2)) AS lastedit", ),                       'conds' => $conds                ); }       function formatRow( $row ) { $pageTitle = Title::makeTitle( $row->namespace, $row->title ); if($row->namespace > 0) { $pageFullName = $pageTitle->getNsText.":".htmlspecialchars($pageTitle->getText ); } else { $pageFullName = htmlspecialchars( $pageTitle->getText); }               $page = $this->getSkin->makeLinkObj( $pageTitle, $pageFullName ); $res = ' '; $res .= "$row->rownum "; $res .= " $page "; $res .= "$row->created "; $res .= "$row->lastuseredit "; $res .= "$row->lastedit "; $res .= " \n"; return $res; } } } //if !defined('USER_SNOOP')
 * 1) used for the watchlist data
 * 1) used for the newpage listing

Note
This extension duplicates some of the functionality of Extension:UserPageViewTracker