fixfix (thx brion!)
[lhc/web/wiklou.git] / includes / SpecialContributions.php
1 <?php
2
3 /**
4 * Special page allowing users to view their own contributions
5 * and those of others.
6 *
7 * @package MediaWiki
8 * @subpackage Special pages
9 */
10 class ContributionsPage extends QueryPage {
11 var $user = null;
12 var $namespace = null;
13 var $newbies = false;
14 var $botmode = false;
15
16 /**
17 * Constructor.
18 * @param $username username to list contribs for (or "newbies" for extra magic)
19 */
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;
24 else
25 $this->user = User::newFromName( $username, false );
26 }
27
28 /**
29 * @return string Name of this special page.
30 */
31 function getName() {
32 return 'Contributions';
33 }
34
35 /**
36 * Not expensive, won't work with the query cache anyway.
37 */
38 function isExpensive() { return false; }
39
40 /**
41 * Should we?
42 */
43 function isSyndicated() { return false; }
44
45 /**
46 * Special handling of "newbies" username. May be disabled in subclasses.
47 */
48 function newbiesModeEnabled() { return true; }
49
50 /**
51 * @return array Extra URL params for self-links.
52 */
53 function linkParameters() {
54 $params['target'] = ( $this->newbies ? "newbies" : $this->user->getName() );
55
56 if ( isset($this->namespace) )
57 $params['namespace'] = $this->namespace;
58
59 if ( $this->botmode )
60 $params['bot'] = 1;
61
62 return $params;
63 }
64
65 /**
66 * Build the list of links to be shown in the subtitle.
67 * @return string Link list for "contribsub" UI message.
68 */
69 function getTargetUserLinks() {
70 global $wgSysopUserBans, $wgLang, $wgUser;
71
72 $skin = $wgUser->getSkin();
73
74 $username = $this->user->getName();
75 $userpage = $this->user->getUserPage();
76 $userlink = $skin->makeLinkObj( $userpage, $username );
77
78 // talk page link
79 $tools[] = $skin->makeLinkObj( $userpage->getTalkPage(), $wgLang->getNsText( NS_TALK ) );
80
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' ) );
87 else
88 $tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ),
89 htmlspecialchars( LogPage::logName( 'block' ) ),
90 'type=block&page=' . $userpage->getPrefixedUrl() );
91 }
92
93 // other logs link
94 $tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ),
95 wfMsgHtml( 'log' ),
96 'user=' . $userpage->getPartialUrl() );
97
98 return $userlink . ' (' . implode( ' | ', $tools ) . ')';
99 }
100
101 /**
102 * Generate "For User (...)" message in subtitle. Calls
103 * getTargetUserLinks() for most of the work.
104 * @return string
105 */
106 function getSubtitleForTarget() {
107 if ( $this->newbies )
108 return wfMsgHtml( 'sp-contributions-newbies-sub' );
109 else
110 return wfMsgHtml( 'contribsub', $this->getTargetUserLinks() );
111 }
112
113 /**
114 * If the user has deleted contributions and we are allowed to
115 * view them, generate a link to Special:DeletedContributions.
116 * @return string
117 */
118 function getDeletedContributionsLink() {
119 global $wgUser;
120
121 if( $this->newbies || !$wgUser->isAllowed( 'deletedhistory' ) )
122 return '';
123
124 $dbr = wfGetDB( DB_SLAVE );
125 $n = $dbr->selectField( 'archive', 'count(*)', array( 'ar_user_text' => $this->user->getName() ), __METHOD__ );
126
127 if ( $n == 0 )
128 return '';
129
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 ) ) );
134
135 return "<p>$msg</p>";
136 }
137
138 /**
139 * Construct and output the page subtitle.
140 */
141 function outputSubtitle() {
142 global $wgOut;
143 $subtitle = $this->getSubtitleForTarget();
144 // $subtitle .= $this->getDeletedContributionsLink(); NOT YET...
145 $wgOut->setSubtitle( $subtitle );
146 }
147
148 /**
149 * Construct the namespace selector form.
150 * @return string
151 */
152 function getNamespaceForm() {
153 $title = $this->getTitle();
154
155 $ns = $this->namespace;
156 if ( !isset($ns) )
157 $ns = '';
158
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 );
168 $form .= '</form>';
169
170 return '<p>' . $form . '</p>';
171 }
172
173 /**
174 * Build the page header. Also calls outputSubtitle().
175 * @return string
176 */
177 function getPageHeader() {
178 $this->outputSubtitle();
179 return $this->getNamespaceForm();
180 }
181
182 /**
183 * Construct the WHERE clause of the SQL SELECT statement for
184 * this query.
185 * @return string
186 */
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);
192 } else {
193 $cond .= ' AND rev_user_text = ' . $dbr->addQuotes( $this->user->getName() );
194 }
195 if ( isset($this->namespace) )
196 $cond .= ' AND page_namespace = ' . (int)$this->namespace;
197 return $cond;
198 }
199
200 /**
201 * Construct the SQL SELECT statement for this query.
202 * @return string
203 */
204 function getSQL() {
205 $dbr = wfGetDB( DB_SLAVE );
206
207 list( $page, $revision ) = $dbr->tableNamesN( 'page', 'revision' );
208
209 return "SELECT 'Contributions' as type,
210 page_namespace AS namespace,
211 page_title AS title,
212 rev_timestamp AS value,
213 rev_user AS userid,
214 rev_user_text AS username,
215 rev_minor_edit AS is_minor,
216 page_latest AS cur_id,
217 rev_id AS rev_id,
218 rev_comment AS comment,
219 rev_deleted AS deleted
220 FROM $page, $revision
221 WHERE " . $this->makeSQLCond( $dbr );
222 }
223
224 /**
225 * If in newbies mode, do a batch existence check for any user
226 * and user talk pages that will be shown in the list.
227 */
228 function preprocessResults( $dbr, $res ) {
229 if ( !$this->newbies )
230 return;
231
232 // Do a batch existence check for user and talk pages
233 $linkBatch = new LinkBatch();
234 while( $row = $dbr->fetchObject( $res ) ) {
235 $linkBatch->add( NS_USER, $row->username );
236 $linkBatch->add( NS_USER_TALK, $row->username );
237 }
238 $linkBatch->execute();
239
240 // Seek to start
241 if( $dbr->numRows( $res ) > 0 )
242 $dbr->dataSeek( $res, 0 );
243 }
244
245 /**
246 * Format a row, providing the timestamp, links to the
247 * page/diff/history and a comment
248 *
249 * @param $skin Skin to use
250 * @param $row Result row
251 * @return string
252 */
253 function formatResult( $skin, $row ) {
254 global $wgLang, $wgContLang, $wgUser;
255
256 $dm = $wgContLang->getDirMark();
257
258 /*
259 * Cache UI messages in a static array so we don't
260 * have to regenerate them for each row.
261 */
262 static $messages;
263 if( !isset( $messages ) ) {
264 foreach( explode( ' ', 'uctop diff newarticle rollbacklink diff hist minoreditletter' ) as $msg )
265 $messages[$msg] = wfMsgExt( $msg, array( 'escape') );
266 }
267
268 $page = Title::makeTitle( $row->namespace, $row->title );
269
270 /*
271 * HACK: We need a revision object, so we make a very
272 * heavily stripped-down one. All we really need are
273 * the comment, the title and the deletion bitmask.
274 */
275 $rev = new Revision( array(
276 'comment' => $row->comment,
277 'deleted' => $row->deleted,
278 'user_text' => $row->username,
279 'user' => $row->userid,
280 ) );
281 $rev->setTitle( $page );
282
283 $ts = wfTimestamp( TS_MW, $row->value );
284 $time = $wgLang->timeAndDate( $ts, true );
285 $hist = $skin->makeKnownLinkObj( $page, $messages['hist'], 'action=history' );
286
287 if ( $rev->userCan( Revision::DELETED_TEXT ) )
288 $diff = $skin->makeKnownLinkObj( $page, $messages['diff'], 'diff=prev&oldid=' . $row->rev_id );
289 else
290 $diff = $messages['diff'];
291
292 if( $row->is_minor )
293 $mflag = '<span class="minor">' . $messages['minoreditletter'] . '</span> ';
294 else
295 $mflag = '';
296
297 $link = $skin->makeKnownLinkObj( $page );
298 $comment = $skin->revComment( $rev );
299
300 if ( $this->newbies ) {
301 $user = ' . . ' . $skin->userLink( $row->userid, $row->username )
302 . $skin->userToolLinks( $row->userid, $row->username );
303 } else {
304 $user = '';
305 }
306
307 $notes = '';
308
309 if( $row->rev_id == $row->cur_id ) {
310 $notes .= ' <strong>' . $messages['uctop'] . '</strong>';
311
312 if( $wgUser->isAllowed( 'rollback' ) )
313 $notes .= ' ' . $skin->generateRollback( $rev );
314 }
315
316 if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
317 $time = '<span class="history-deleted">' . $time . '</span>';
318 $notes .= ' ' . wfMsgHtml( 'deletedrev' );
319 }
320
321 return "{$time} ({$hist}) ({$diff}) {$mflag} {$dm}{$link}{$user} {$comment}{$notes}";
322 }
323
324 /**
325 * Called to actually output the page. Override to do a basic
326 * input validity check before proceeding.
327 *
328 * @param $offset database query offset
329 * @param $limit database query limit
330 * @param $shownavigation show navigation like "next 200"?
331 */
332 function doQuery( $limit, $offset, $shownavigation = true ) {
333
334 // this needs to be checked before doing anything
335 if( !$this->user && !$this->newbies ) {
336 global $wgOut;
337 $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
338 return;
339 }
340
341 return parent::doQuery( $limit, $offset, $shownavigation );
342 }
343 }
344
345 /**
346 * Show the special page.
347 */
348 function wfSpecialContributions( $par = null ) {
349 global $wgRequest, $wgUser;
350
351 $username = ( isset($par) ? $par : $wgRequest->getVal( 'target' ) );
352
353 $page = new ContributionsPage( $username );
354
355 // hook for Contributionseditcount extension
356 if ( $page->user && $page->user->isLoggedIn() )
357 wfRunHooks( 'SpecialContributionsBeforeMainOutput', $page->user->getId() );
358
359 $page->namespace = $wgRequest->getIntOrNull( 'namespace' );
360 $page->botmode = ( $wgUser->isAllowed( 'rollback' ) && $wgRequest->getBool( 'bot' ) );
361
362 list( $limit, $offset ) = wfCheckLimits();
363 return $page->doQuery( $offset, $limit );
364 }
365
366 ?>