* (bug 8042) Make miser mode caching limits settable via $wgQueryCacheLimit
[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 $botmode = false;
14
15 /**
16 * Constructor.
17 * @param $username username to list contribs for (or "newbies" for extra magic)
18 */
19 function __construct( $username='' ) {
20 $this->user = User::newFromName( $username, false );
21 }
22
23 /**
24 * @return string Name of this special page.
25 */
26 function getName() {
27 return 'Contributions';
28 }
29
30 /**
31 * Not expensive, won't work with the query cache anyway.
32 */
33 function isExpensive() { return false; }
34
35 /**
36 * Should we?
37 */
38 function isSyndicated() { return false; }
39
40 /**
41 * Get target user name. May be overridden in subclasses.
42 * @return string username
43 */
44 function getUsername() {
45 return $this->user->getName();
46 }
47
48 /**
49 * @return array Extra URL params for self-links.
50 */
51 function linkParameters() {
52 $params['target'] = $this->getUsername();
53
54 if ( isset($this->namespace) )
55 $params['namespace'] = $this->namespace;
56
57 if ( $this->botmode )
58 $params['bot'] = 1;
59
60 return $params;
61 }
62
63 /**
64 * Build the list of links to be shown in the subtitle.
65 * @return string Link list for "contribsub" UI message.
66 */
67 function getTargetUserLinks() {
68 global $wgSysopUserBans, $wgLang, $wgUser;
69
70 $skin = $wgUser->getSkin();
71
72 $username = $this->getUsername();
73 $userpage = $this->user->getUserPage();
74 $userlink = $skin->makeLinkObj( $userpage, $username );
75
76 // talk page link
77 $tools[] = $skin->makeLinkObj( $userpage->getTalkPage(), $wgLang->getNsText( NS_TALK ) );
78
79 // block or block log link
80 $id = $this->user->getId();
81 if ( ( $id != 0 && $wgSysopUserBans ) || ( $id == 0 && User::isIP( $username ) ) ) {
82 if( $wgUser->isAllowed( 'block' ) )
83 $tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Blockip', $username ),
84 wfMsgHtml( 'blocklink' ) );
85 else
86 $tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ),
87 htmlspecialchars( LogPage::logName( 'block' ) ),
88 'type=block&page=' . $userpage->getPrefixedUrl() );
89 }
90
91 // other logs link
92 $tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ),
93 wfMsgHtml( 'log' ),
94 'user=' . $userpage->getPartialUrl() );
95
96 return $userlink . ' (' . implode( ' | ', $tools ) . ')';
97 }
98
99 /**
100 * Generate "For User (...)" message in subtitle. Calls
101 * getTargetUserLinks() for most of the work.
102 * @return string
103 */
104 function getSubtitleForTarget() {
105 return wfMsgHtml( 'contribsub', $this->getTargetUserLinks() );
106 }
107
108 /**
109 * If the user has deleted contributions and we are allowed to
110 * view them, generate a link to Special:DeletedContributions.
111 * @return string
112 */
113 function getDeletedContributionsLink() {
114 global $wgUser;
115
116 if( !$wgUser->isAllowed( 'deletedhistory' ) )
117 return '';
118
119 $dbr = wfGetDB( DB_SLAVE );
120 $n = $dbr->selectField( 'archive', 'count(*)', array( 'ar_user_text' => $this->getUsername() ), __METHOD__ );
121
122 if ( $n == 0 )
123 return '';
124
125 $msg = wfMsg( ( $wgUser->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted' ),
126 $wgUser->getSkin()->makeKnownLinkObj(
127 SpecialPage::getTitleFor( 'DeletedContributions', $this->getUsername() ),
128 wfMsgExt( 'restorelink', array( 'parsemag', 'escape' ), $n ) ) );
129
130 return "<p>$msg</p>";
131 }
132
133 /**
134 * Construct and output the page subtitle.
135 */
136 function outputSubtitle() {
137 global $wgOut;
138 $subtitle = $this->getSubtitleForTarget();
139 // $subtitle .= $this->getDeletedContributionsLink(); NOT YET...
140 $wgOut->setSubtitle( $subtitle );
141 }
142
143 /**
144 * Construct the namespace selector form.
145 * @return string
146 */
147 function getNamespaceForm() {
148 $title = $this->getTitle();
149
150 $ns = $this->namespace;
151 if ( !isset($ns) )
152 $ns = '';
153
154 $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $title->getLocalUrl() ) );
155 $form .= wfMsgHtml( 'namespace' ) . ' ';
156 $form .= Xml::namespaceSelector( $ns, '' ) . ' ';
157 $form .= Xml::submitButton( wfMsg( 'allpagessubmit' ) );
158 $form .= Xml::hidden( 'offset', $this->offset );
159 $form .= Xml::hidden( 'limit', $this->limit );
160 $form .= Xml::hidden( 'target', $this->getUsername() );
161 if ( $this->botmode )
162 $form .= Xml::hidden( 'bot', 1 );
163 $form .= '</form>';
164
165 return '<p>' . $form . '</p>';
166 }
167
168 /**
169 * Build the page header. Also calls outputSubtitle().
170 * @return string
171 */
172 function getPageHeader() {
173 $this->outputSubtitle();
174 return $this->getNamespaceForm();
175 }
176
177 /**
178 * Construct the WHERE clause of the SQL SELECT statement for
179 * this query.
180 * @return string
181 */
182 function makeSQLCond( $dbr ) {
183 $cond = ' page_id = rev_page';
184 $cond .= ' AND rev_user_text = ' . $dbr->addQuotes( $this->getUsername() );
185
186 if ( isset($this->namespace) )
187 $cond .= ' AND page_namespace = ' . (int)$this->namespace;
188
189 return $cond;
190 }
191
192 /**
193 * Construct the SQL SELECT statement for this query.
194 * @return string
195 */
196 function getSQL() {
197 $dbr = wfGetDB( DB_SLAVE );
198
199 list( $page, $revision ) = $dbr->tableNamesN( 'page', 'revision' );
200
201 // XXX: the username and userid fields aren't used for much here,
202 // but some subclasses rely on them more than we do.
203
204 return "SELECT 'Contributions' as type,
205 page_namespace AS namespace,
206 page_title AS title,
207 rev_timestamp AS value,
208 rev_user AS userid,
209 rev_user_text AS username,
210 rev_minor_edit AS is_minor,
211 page_latest AS cur_id,
212 rev_id AS rev_id,
213 rev_comment AS comment,
214 rev_deleted AS deleted
215 FROM $page, $revision
216 WHERE " . $this->makeSQLCond( $dbr );
217 }
218
219 /**
220 * Get user links for output row, for subclasses that may want
221 * such functionality.
222 *
223 * @param $skin Skin to use
224 * @param $row Result row
225 * @return string
226 */
227 function getRowUserLinks( $skin, $row ) { return ''; }
228
229 /**
230 * Format a row, providing the timestamp, links to the
231 * page/diff/history and a comment
232 *
233 * @param $skin Skin to use
234 * @param $row Result row
235 * @return string
236 */
237 function formatResult( $skin, $row ) {
238 global $wgLang, $wgContLang, $wgUser;
239
240 $dm = $wgContLang->getDirMark();
241
242 /*
243 * Cache UI messages in a static array so we don't
244 * have to regenerate them for each row.
245 */
246 static $messages;
247 if( !isset( $messages ) ) {
248 foreach( explode( ' ', 'uctop diff newarticle rollbacklink diff hist minoreditletter' ) as $msg )
249 $messages[$msg] = wfMsgExt( $msg, array( 'escape') );
250 }
251
252 $page = Title::makeTitle( $row->namespace, $row->title );
253
254 /*
255 * HACK: We need a revision object, so we make a very
256 * heavily stripped-down one. All we really need are
257 * the comment, the title and the deletion bitmask.
258 */
259 $rev = new Revision( array(
260 'comment' => $row->comment,
261 'deleted' => $row->deleted,
262 'user_text' => $row->username,
263 'user' => $row->userid,
264 ) );
265 $rev->setTitle( $page );
266
267 $ts = wfTimestamp( TS_MW, $row->value );
268 $time = $wgLang->timeAndDate( $ts, true );
269 $hist = $skin->makeKnownLinkObj( $page, $messages['hist'], 'action=history' );
270
271 if ( $rev->userCan( Revision::DELETED_TEXT ) )
272 $diff = $skin->makeKnownLinkObj( $page, $messages['diff'], 'diff=prev&oldid=' . $row->rev_id );
273 else
274 $diff = $messages['diff'];
275
276 if( $row->is_minor )
277 $mflag = '<span class="minor">' . $messages['minoreditletter'] . '</span> ';
278 else
279 $mflag = '';
280
281 $link = $skin->makeKnownLinkObj( $page );
282 $comment = $skin->revComment( $rev );
283
284 $user = $this->getRowUserLinks( $skin, $row ); // for subclasses
285
286 $notes = '';
287
288 if( $row->rev_id == $row->cur_id ) {
289 $notes .= ' <strong>' . $messages['uctop'] . '</strong>';
290
291 if( $wgUser->isAllowed( 'rollback' ) )
292 $notes .= ' ' . $skin->generateRollback( $rev );
293 }
294
295 if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
296 $time = '<span class="history-deleted">' . $time . '</span>';
297 $notes .= ' ' . wfMsgHtml( 'deletedrev' );
298 }
299
300 return "{$time} ({$hist}) ({$diff}) {$mflag} {$dm}{$link}{$user} {$comment}{$notes}";
301 }
302 }
303
304 /**
305 * Show the special page.
306 */
307 function wfSpecialContributions( $par = null ) {
308 global $wgRequest, $wgUser, $wgOut;
309
310 $username = ( isset($par) ? $par : $wgRequest->getVal( 'target' ) );
311
312 // compatibility hack
313 if ( $username == 'newbies' ) {
314 $wgOut->redirect( SpecialPage::getTitleFor( 'NewbieContributions' )->getFullURL() );
315 return;
316 }
317
318 $page = new ContributionsPage( $username );
319
320 if( !$page->user ) {
321 $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
322 return;
323 }
324
325 // hook for Contributionseditcount extension
326 if ( $page->user && $page->user->isLoggedIn() )
327 wfRunHooks( 'SpecialContributionsBeforeMainOutput', $page->user->getId() );
328
329 $page->namespace = $wgRequest->getIntOrNull( 'namespace' );
330 $page->botmode = ( $wgUser->isAllowed( 'rollback' ) && $wgRequest->getBool( 'bot' ) );
331
332 list( $limit, $offset ) = wfCheckLimits();
333 return $page->doQuery( $offset, $limit );
334 }
335
336 ?>