From 2df501fa0d2723bf2a196390c2507a6043cdcdaf Mon Sep 17 00:00:00 2001 From: Ilmari Karonen Date: Sun, 3 Dec 2006 00:05:45 +0000 Subject: [PATCH] Didn't really mean to delete SpecialContributions.php back there, but now that it's gone, I might just as well roll out my new, completely rewritten version. --- includes/AutoLoader.php | 2 +- includes/QueryPage.php | 1 + includes/SpecialContributions.php | 367 ++++++++++++++++++++++++++++++ 3 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 includes/SpecialContributions.php diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index f826ec1c2c..e72dcbcfb4 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -162,7 +162,7 @@ function __autoload($className) { 'BrokenRedirectsPage' => 'includes/SpecialBrokenRedirects.php', 'CategoriesPage' => 'includes/SpecialCategories.php', 'EmailConfirmation' => 'includes/SpecialConfirmemail.php', - 'ContribsFinder' => 'includes/SpecialContributions.php', + 'ContributionsPage' => 'includes/SpecialContributions.php', 'DeadendPagesPage' => 'includes/SpecialDeadendpages.php', 'DisambiguationsPage' => 'includes/SpecialDisambiguations.php', 'DoubleRedirectsPage' => 'includes/SpecialDoubleRedirects.php', diff --git a/includes/QueryPage.php b/includes/QueryPage.php index d0fa9f674d..b4df3a0c5b 100644 --- a/includes/QueryPage.php +++ b/includes/QueryPage.php @@ -14,6 +14,7 @@ $wgQueryPages = array( array( 'AncientPagesPage', 'Ancientpages' ), array( 'BrokenRedirectsPage', 'BrokenRedirects' ), array( 'CategoriesPage', 'Categories' ), + array( 'ContributionsPage', 'Contributions' ), array( 'DeadendPagesPage', 'Deadendpages' ), array( 'DisambiguationsPage', 'Disambiguations' ), array( 'DoubleRedirectsPage', 'DoubleRedirects' ), diff --git a/includes/SpecialContributions.php b/includes/SpecialContributions.php new file mode 100644 index 0000000000..57fc9bfb78 --- /dev/null +++ b/includes/SpecialContributions.php @@ -0,0 +1,367 @@ +newbiesModeEnabled() ) + $this->newbies = true; + else + $this->user = User::newFromName( $username, false ); + } + + /** + * @return string Name of this special page. + */ + function getName() { + return 'Contributions'; + } + + /** + * Not expensive, won't work with the query cache anyway. + */ + function isExpensive() { return false; } + + /** + * Should we? + */ + function isSyndicated() { return false; } + + /** + * Special handling of "newbies" username. May be disabled in subclasses. + */ + function newbiesModeEnabled() { return true; } + + /** + * @return array Extra URL params for self-links. + */ + function linkParameters() { + $params['target'] = ( $this->newbies ? "newbies" : $this->user->getName() ); + + if ( isset($this->namespace) ) + $params['namespace'] = $this->namespace; + + if ( $this->botmode ) + $params['bot'] = 1; + + return $params; + } + + /** + * Build the list of links to be shown in the subtitle. + * @return string Link list for "contribsub" UI message. + */ + function getTargetUserLinks() { + global $wgSysopUserBans, $wgLang, $wgUser; + + $skin = $wgUser->getSkin(); + + $username = $this->user->getName(); + $userpage = $this->user->getUserPage(); + $userlink = $skin->makeLinkObj( $userpage, $username ); + + // talk page link + $tools[] = $skin->makeLinkObj( $userpage->getTalkPage(), $wgLang->getNsText( NS_TALK ) ); + + // block or block log link + $id = $this->user->getId(); + if ( ( $id != 0 && $wgSysopUserBans ) || ( $id == 0 && User::isIP( $username ) ) ) { + if( $wgUser->isAllowed( 'block' ) ) + $tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Blockip', $username ), + wfMsgHtml( 'blocklink' ) ); + else + $tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), + htmlspecialchars( LogPage::logName( 'block' ) ), + 'type=block&page=' . $userpage->getPrefixedUrl() ); + } + + // other logs link + $tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), + wfMsgHtml( 'log' ), + 'user=' . $userpage->getPartialUrl() ); + + return $userlink . ' (' . implode( ' | ', $tools ) . ')'; + } + + /** + * Generate "For User (...)" message in subtitle. Calls + * getTargetUserLinks() for most of the work. + * @return string + */ + function getSubtitleForTarget() { + if ( $this->newbies ) + return wfMsgHtml( 'sp-contributions-newbies-sub' ); + else + return wfMsgHtml( 'contribsub', $this->getTargetUserLinks() ); + } + + /** + * If the user has deleted contributions and we are allowed to + * view them, generate a link to Special:DeletedContributions. + * @return string + */ + function getDeletedContributionsLink() { + global $wgUser; + + if( $this->newbies || !$wgUser->isAllowed( 'deletedhistory' ) ) + return ''; + + $dbr = wfGetDB( DB_SLAVE ); + $n = $dbr->selectField( 'archive', 'count(*)', array( 'ar_user_text' => $this->user->getName() ), __METHOD__ ); + + if ( $n == 0 ) + return ''; + + $msg = wfMsg( ( $wgUser->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted' ), + $wgUser->getSkin()->makeKnownLinkObj( + SpecialPage::getTitleFor( 'DeletedContributions', $this->user->getName() ), + wfMsgExt( 'restorelink', array( 'parsemag', 'escape' ), $n ) ) ); + + return "

$msg

"; + } + + /** + * Construct and output the page subtitle. + */ + function outputSubtitle() { + global $wgOut; + $subtitle = $this->getSubtitleForTarget(); + // $subtitle .= $this->getDeletedContributionsLink(); NOT YET... + $wgOut->setSubtitle( $subtitle ); + } + + /** + * Construct the namespace selector form. + * @return string + */ + function getNamespaceForm() { + $title = $this->getTitle(); + + $ns = $this->namespace; + if ( !isset($ns) ) + $ns = ''; + + $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $title->getLocalUrl() ) ); + $form .= wfMsgHtml( 'namespace' ) . ' '; + $form .= Xml::namespaceSelector( $ns, '' ) . ' '; + $form .= Xml::submitButton( wfMsg( 'allpagessubmit' ) ); + $form .= Xml::hidden( 'offset', $this->offset ); + $form .= Xml::hidden( 'limit', $this->limit ); + $form .= Xml::hidden( 'target', ( $this->newbies ? "newbies" : $this->user->getName() ) ); + if ( $this->botmode ) + $form .= Xml::hidden( 'bot', 1 ); + $form .= ''; + + return '

' . $form . '

'; + } + + /** + * Build the page header. Also calls outputSubtitle(). + * @return string + */ + function getPageHeader() { + $this->outputSubtitle(); + return $this->getNamespaceForm(); + } + + /** + * Construct the WHERE clause of the SQL SELECT statement for + * this query. + * @return string + */ + function makeSQLCond( $dbr ) { + $cond = ' page_id = rev_page'; + if ( $this->newbies ) { + $max = $dbr->selectField( 'user', 'max(user_id)', false, 'make_sql' ); + $cond .= ' AND rev_user > ' . (int)($max - $max / 100); + } else { + $cond .= ' AND rev_user_text = ' . $dbr->addQuotes( $this->user->getName() ); + } + if ( isset($this->namespace) ) + $cond .= ' AND page_namespace = ' . (int)$this->namespace; + return $cond; + } + + /** + * Construct the SQL SELECT statement for this query. + * @return string + */ + function getSQL() { + $dbr = wfGetDB( DB_SLAVE ); + + list( $page, $revision ) = $dbr->tableNamesN( 'page', 'revision' ); + + return "SELECT 'Contributions' as type, + page_namespace AS namespace, + page_title AS title, + rev_timestamp AS value, + rev_user AS userid, + rev_user_text AS username, + rev_minor_edit AS is_minor, + page_is_new AS is_new, + page_latest AS cur_id, + rev_id AS rev_id, + rev_comment AS comment, + rev_deleted AS deleted + FROM $page, $revision + WHERE " . $this->makeSQLCond( $dbr ); + } + + /** + * If in newbies mode, do a batch existence check for any user + * and user talk pages that will be shown in the list. + */ + function preprocessResults( $dbr, $res ) { + if ( !$self->newbies ) + return; + + // Do a batch existence check for user and talk pages + $linkBatch = new LinkBatch(); + while( $row = $dbr->fetchObject( $res ) ) { + $linkBatch->add( NS_USER, $row->username ); + $linkBatch->add( NS_USER_TALK, $row->username ); + } + $linkBatch->execute(); + + // Seek to start + if( $dbr->numRows( $res ) > 0 ) + $dbr->dataSeek( $res, 0 ); + } + + /** + * Format a row, providing the timestamp, links to the + * page/diff/history and a comment + * + * @param $skin Skin to use + * @param $row Result row + * @return string + */ + function formatResult( $skin, $row ) { + global $wgLang, $wgContLang, $wgUser; + + $dm = $wgContLang->getDirMark(); + + /* + * Cache UI messages in a static array so we don't + * have to regenerate them for each row. + */ + static $messages; + if( !isset( $messages ) ) { + foreach( explode( ' ', 'uctop diff newarticle rollbacklink diff hist minoreditletter' ) as $msg ) + $messages[$msg] = wfMsgExt( $msg, array( 'escape') ); + } + + $page = Title::makeTitle( $row->namespace, $row->title ); + + /* + * HACK: We need a revision object, so we make a very + * heavily stripped-down one. All we really need are + * the comment, the title and the deletion bitmask. + */ + $rev = new Revision( array( + 'comment' => $row->comment, + 'deleted' => $row->deleted, + 'user_text' => $row->username, + 'user' => $row->userid, + ) ); + $rev->setTitle( $page ); + + $ts = wfTimestamp( TS_MW, $row->value ); + $time = $wgLang->timeAndDate( $ts, true ); + $hist = $skin->makeKnownLinkObj( $page, $messages['hist'], 'action=history' ); + + if ( $rev->userCan( Revision::DELETED_TEXT ) ) + $diff = $skin->makeKnownLinkObj( $page, $messages['diff'], 'diff=prev&oldid=' . $row->rev_id ); + else + $diff = $messages['diff']; + + if( $row->minor ) + $mflag = '' . $messages['minoreditletter'] . ' '; + else + $mflag = ''; + + $link = $skin->makeKnownLinkObj( $page ); + $comment = $skin->revComment( $rev ); + + if ( $this->newbies ) { + $user = ' . . ' . $skin->userLink( $row->userid, $row->username ) + . $skin->userToolLinks( $row->userid, $row->username ); + } else { + $user = ''; + } + + $notes = ''; + + if( $row->rev_id == $row->cur_id ) { + $notes .= ' ' . $messages['uctop'] . ''; + + if( $wgUser->isAllowed( 'rollback' ) ) + $notes .= ' ' . $skin->generateRollback( $rev ); + } + + if( $rev->isDeleted( Revision::DELETED_TEXT ) ) { + $time = '' . $time . ''; + $notes .= ' ' . wfMsgHtml( 'deletedrev' ); + } + + return "{$time} ({$hist}) ({$diff}) {$mflag} {$dm}{$link}{$user} {$comment}{$notes}"; + } + + /** + * Called to actually output the page. Override to do a basic + * input validity check before proceeding. + * + * @param $offset database query offset + * @param $limit database query limit + * @param $shownavigation show navigation like "next 200"? + */ + function doQuery( $limit, $offset, $shownavigation = true ) { + + // this needs to be checked before doing anything + if( !$this->user && !$this->newbies ) { + global $wgOut; + $wgOut->showErrorPage( 'notargettitle', 'notargettext' ); + return; + } + + return parent::doQuery( $limit, $offset, $shownavigation ); + } +} + +/** + * Show the special page. + */ +function wfSpecialContributions( $par = null ) { + global $wgRequest, $wgUser; + + $username = ( isset($par) ? $par : $wgRequest->getVal( 'target' ) ); + + $page = new ContributionsPage( $username ); + + // hook for Contributionseditcount extension + if ( $page->user && $page->user->isLoggedIn() ) + wfRunHooks( 'SpecialContributionsBeforeMainOutput', $page->user->getId() ); + + $page->namespace = $wgRequest->getIntOrNull( 'namespace' ); + $page->botmode = ( $wgUser->isAllowed( 'rollback' ) && $wgRequest->getBool( 'bot' ) ); + + list( $limit, $offset ) = wfCheckLimits(); + return $page->doQuery( $offset, $limit ); +} + +?> -- 2.20.1