4 * Special page allowing users to view their own contributions
8 * @subpackage Special pages
10 class ContributionsPage
extends QueryPage
{
12 var $namespace = null;
18 * @param $username username to list contribs for (or "newbies" for extra magic)
20 function __construct( $username ) {
21 // This is an ugly hack. I don't know who came up with it.
22 if ( $username == 'newbies' && $this->newbiesModeEnabled() )
23 $this->newbies
= true;
25 $this->user
= User
::newFromName( $username, false );
29 * @return string Name of this special page.
32 return 'Contributions';
36 * Not expensive, won't work with the query cache anyway.
38 function isExpensive() { return false; }
43 function isSyndicated() { return false; }
46 * Special handling of "newbies" username. May be disabled in subclasses.
48 function newbiesModeEnabled() { return true; }
51 * @return array Extra URL params for self-links.
53 function linkParameters() {
54 $params['target'] = ( $this->newbies ?
"newbies" : $this->user
->getName() );
56 if ( isset($this->namespace) )
57 $params['namespace'] = $this->namespace;
66 * Build the list of links to be shown in the subtitle.
67 * @return string Link list for "contribsub" UI message.
69 function getTargetUserLinks() {
70 global $wgSysopUserBans, $wgLang, $wgUser;
72 $skin = $wgUser->getSkin();
74 $username = $this->user
->getName();
75 $userpage = $this->user
->getUserPage();
76 $userlink = $skin->makeLinkObj( $userpage, $username );
79 $tools[] = $skin->makeLinkObj( $userpage->getTalkPage(), $wgLang->getNsText( NS_TALK
) );
81 // block or block log link
82 $id = $this->user
->getId();
83 if ( ( $id != 0 && $wgSysopUserBans ) ||
( $id == 0 && User
::isIP( $username ) ) ) {
84 if( $wgUser->isAllowed( 'block' ) )
85 $tools[] = $skin->makeKnownLinkObj( SpecialPage
::getTitleFor( 'Blockip', $username ),
86 wfMsgHtml( 'blocklink' ) );
88 $tools[] = $skin->makeKnownLinkObj( SpecialPage
::getTitleFor( 'Log' ),
89 htmlspecialchars( LogPage
::logName( 'block' ) ),
90 'type=block&page=' . $userpage->getPrefixedUrl() );
94 $tools[] = $skin->makeKnownLinkObj( SpecialPage
::getTitleFor( 'Log' ),
96 'user=' . $userpage->getPartialUrl() );
98 return $userlink . ' (' . implode( ' | ', $tools ) . ')';
102 * Generate "For User (...)" message in subtitle. Calls
103 * getTargetUserLinks() for most of the work.
106 function getSubtitleForTarget() {
107 if ( $this->newbies
)
108 return wfMsgHtml( 'sp-contributions-newbies-sub' );
110 return wfMsgHtml( 'contribsub', $this->getTargetUserLinks() );
114 * If the user has deleted contributions and we are allowed to
115 * view them, generate a link to Special:DeletedContributions.
118 function getDeletedContributionsLink() {
121 if( $this->newbies ||
!$wgUser->isAllowed( 'deletedhistory' ) )
124 $dbr = wfGetDB( DB_SLAVE
);
125 $n = $dbr->selectField( 'archive', 'count(*)', array( 'ar_user_text' => $this->user
->getName() ), __METHOD__
);
130 $msg = wfMsg( ( $wgUser->isAllowed( 'delete' ) ?
'thisisdeleted' : 'viewdeleted' ),
131 $wgUser->getSkin()->makeKnownLinkObj(
132 SpecialPage
::getTitleFor( 'DeletedContributions', $this->user
->getName() ),
133 wfMsgExt( 'restorelink', array( 'parsemag', 'escape' ), $n ) ) );
135 return "<p>$msg</p>";
139 * Construct and output the page subtitle.
141 function outputSubtitle() {
143 $subtitle = $this->getSubtitleForTarget();
144 // $subtitle .= $this->getDeletedContributionsLink(); NOT YET...
145 $wgOut->setSubtitle( $subtitle );
149 * Construct the namespace selector form.
152 function getNamespaceForm() {
153 $title = $this->getTitle();
155 $ns = $this->namespace;
159 $form = Xml
::openElement( 'form', array( 'method' => 'post', 'action' => $title->getLocalUrl() ) );
160 $form .= wfMsgHtml( 'namespace' ) . ' ';
161 $form .= Xml
::namespaceSelector( $ns, '' ) . ' ';
162 $form .= Xml
::submitButton( wfMsg( 'allpagessubmit' ) );
163 $form .= Xml
::hidden( 'offset', $this->offset
);
164 $form .= Xml
::hidden( 'limit', $this->limit
);
165 $form .= Xml
::hidden( 'target', ( $this->newbies ?
"newbies" : $this->user
->getName() ) );
166 if ( $this->botmode
)
167 $form .= Xml
::hidden( 'bot', 1 );
170 return '<p>' . $form . '</p>';
174 * Build the page header. Also calls outputSubtitle().
177 function getPageHeader() {
178 $this->outputSubtitle();
179 return $this->getNamespaceForm();
183 * Construct the WHERE clause of the SQL SELECT statement for
187 function makeSQLCond( $dbr ) {
188 $cond = ' page_id = rev_page';
189 if ( $this->newbies
) {
190 $max = $dbr->selectField( 'user', 'max(user_id)', false, 'make_sql' );
191 $cond .= ' AND rev_user > ' . (int)($max - $max / 100);
193 $cond .= ' AND rev_user_text = ' . $dbr->addQuotes( $this->user
->getName() );
195 if ( isset($this->namespace) )
196 $cond .= ' AND page_namespace = ' . (int)$this->namespace;
201 * Construct the SQL SELECT statement for this query.
205 $dbr = wfGetDB( DB_SLAVE
);
207 list( $page, $revision ) = $dbr->tableNamesN( 'page', 'revision' );
209 return "SELECT 'Contributions' as type,
210 page_namespace AS namespace,
212 rev_timestamp AS value,
214 rev_user_text AS username,
215 rev_minor_edit AS is_minor,
216 page_is_new AS is_new,
217 page_latest AS cur_id,
219 rev_comment AS comment,
220 rev_deleted AS deleted
221 FROM $page, $revision
222 WHERE " . $this->makeSQLCond( $dbr );
226 * If in newbies mode, do a batch existence check for any user
227 * and user talk pages that will be shown in the list.
229 function preprocessResults( $dbr, $res ) {
230 if ( !$self->newbies
)
233 // Do a batch existence check for user and talk pages
234 $linkBatch = new LinkBatch();
235 while( $row = $dbr->fetchObject( $res ) ) {
236 $linkBatch->add( NS_USER
, $row->username
);
237 $linkBatch->add( NS_USER_TALK
, $row->username
);
239 $linkBatch->execute();
242 if( $dbr->numRows( $res ) > 0 )
243 $dbr->dataSeek( $res, 0 );
247 * Format a row, providing the timestamp, links to the
248 * page/diff/history and a comment
250 * @param $skin Skin to use
251 * @param $row Result row
254 function formatResult( $skin, $row ) {
255 global $wgLang, $wgContLang, $wgUser;
257 $dm = $wgContLang->getDirMark();
260 * Cache UI messages in a static array so we don't
261 * have to regenerate them for each row.
264 if( !isset( $messages ) ) {
265 foreach( explode( ' ', 'uctop diff newarticle rollbacklink diff hist minoreditletter' ) as $msg )
266 $messages[$msg] = wfMsgExt( $msg, array( 'escape') );
269 $page = Title
::makeTitle( $row->namespace, $row->title
);
272 * HACK: We need a revision object, so we make a very
273 * heavily stripped-down one. All we really need are
274 * the comment, the title and the deletion bitmask.
276 $rev = new Revision( array(
277 'comment' => $row->comment
,
278 'deleted' => $row->deleted
,
279 'user_text' => $row->username
,
280 'user' => $row->userid
,
282 $rev->setTitle( $page );
284 $ts = wfTimestamp( TS_MW
, $row->value
);
285 $time = $wgLang->timeAndDate( $ts, true );
286 $hist = $skin->makeKnownLinkObj( $page, $messages['hist'], 'action=history' );
288 if ( $rev->userCan( Revision
::DELETED_TEXT
) )
289 $diff = $skin->makeKnownLinkObj( $page, $messages['diff'], 'diff=prev&oldid=' . $row->rev_id
);
291 $diff = $messages['diff'];
294 $mflag = '<span class="minor">' . $messages['minoreditletter'] . '</span> ';
298 $link = $skin->makeKnownLinkObj( $page );
299 $comment = $skin->revComment( $rev );
301 if ( $this->newbies
) {
302 $user = ' . . ' . $skin->userLink( $row->userid
, $row->username
)
303 . $skin->userToolLinks( $row->userid
, $row->username
);
310 if( $row->rev_id
== $row->cur_id
) {
311 $notes .= ' <strong>' . $messages['uctop'] . '</strong>';
313 if( $wgUser->isAllowed( 'rollback' ) )
314 $notes .= ' ' . $skin->generateRollback( $rev );
317 if( $rev->isDeleted( Revision
::DELETED_TEXT
) ) {
318 $time = '<span class="history-deleted">' . $time . '</span>';
319 $notes .= ' ' . wfMsgHtml( 'deletedrev' );
322 return "{$time} ({$hist}) ({$diff}) {$mflag} {$dm}{$link}{$user} {$comment}{$notes}";
326 * Called to actually output the page. Override to do a basic
327 * input validity check before proceeding.
329 * @param $offset database query offset
330 * @param $limit database query limit
331 * @param $shownavigation show navigation like "next 200"?
333 function doQuery( $limit, $offset, $shownavigation = true ) {
335 // this needs to be checked before doing anything
336 if( !$this->user
&& !$this->newbies
) {
338 $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
342 return parent
::doQuery( $limit, $offset, $shownavigation );
347 * Show the special page.
349 function wfSpecialContributions( $par = null ) {
350 global $wgRequest, $wgUser;
352 $username = ( isset($par) ?
$par : $wgRequest->getVal( 'target' ) );
354 $page = new ContributionsPage( $username );
356 // hook for Contributionseditcount extension
357 if ( $page->user
&& $page->user
->isLoggedIn() )
358 wfRunHooks( 'SpecialContributionsBeforeMainOutput', $page->user
->getId() );
360 $page->namespace = $wgRequest->getIntOrNull( 'namespace' );
361 $page->botmode
= ( $wgUser->isAllowed( 'rollback' ) && $wgRequest->getBool( 'bot' ) );
363 list( $limit, $offset ) = wfCheckLimits();
364 return $page->doQuery( $offset, $limit );