r18166 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r18165‎ | r18166 | r18167 >
Date:01:11, 5 December 2006
Author:brion
Status:old
Tags:
Comment:
Restore Special:Contributions to its state as of 17964
New version doesn't keep all features; keep it off trunk until complete
Modified paths:
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/includes/QueryPage.php (modified) (history)
  • /trunk/phase3/includes/SpecialContributions.php (replaced) (history)
  • /trunk/phase3/includes/SpecialNewbieContributions.php (deleted) (history)
  • /trunk/phase3/includes/SpecialPage.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesFi.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/SpecialNewbieContributions.php
@@ -1,107 +0,0 @@
2 -<?php
3 -
4 -/**
5 - * Special page allowing users to view the contributions of new users.
6 - *
7 - * @package MediaWiki
8 - * @subpackage Special pages
9 - */
10 -class NewbieContributionsPage extends ContributionsPage {
11 -
12 - /**
13 - * Constructor. No need for username.
14 - */
15 - function __construct() { }
16 -
17 - /**
18 - * @return string Name of this special page.
19 - */
20 - function getName() {
21 - return 'NewbieContributions';
22 - }
23 -
24 - /**
25 - * No target user here.
26 - */
27 - function getUsername() {
28 - return '';
29 - }
30 -
31 - /**
32 - * No target user => no subtitle.
33 - */
34 - function getSubtitleForTarget() {
35 - return '';
36 - }
37 -
38 - /**
39 - * No target user => no deleted contribs link.
40 - */
41 - function getDeletedContributionsLink() {
42 - return '';
43 - }
44 -
45 - /**
46 - * Construct the WHERE clause of the SQL SELECT statement for
47 - * this query.
48 - * @return string
49 - */
50 - function makeSQLCond( $dbr ) {
51 - $cond = ' page_id = rev_page';
52 -
53 - $max = $dbr->selectField( 'user', 'max(user_id)', false, 'make_sql' );
54 - $cond .= ' AND rev_user > ' . (int)($max - $max / 100);
55 -
56 - if ( isset($this->namespace) )
57 - $cond .= ' AND page_namespace = ' . (int)$this->namespace;
58 -
59 - return $cond;
60 - }
61 -
62 - /**
63 - * Do a batch existence check for any user and user talk pages
64 - * that will be shown in the list.
65 - */
66 - function preprocessResults( $dbr, $res ) {
67 - $linkBatch = new LinkBatch();
68 - while( $row = $dbr->fetchObject( $res ) ) {
69 - $linkBatch->add( NS_USER, $row->username );
70 - $linkBatch->add( NS_USER_TALK, $row->username );
71 - }
72 - $linkBatch->execute();
73 -
74 - // Seek to start
75 - if( $dbr->numRows( $res ) > 0 )
76 - $dbr->dataSeek( $res, 0 );
77 - }
78 -
79 - /**
80 - * Get user links for output row.
81 - *
82 - * @param $skin Skin to use
83 - * @param $row Result row
84 - * @return string User links
85 - */
86 - function getRowUserLinks( $skin, $row ) {
87 - $user = ' . . ' . $skin->userLink( $row->userid, $row->username )
88 - . $skin->userToolLinks( $row->userid, $row->username );
89 - return $user;
90 - }
91 -}
92 -
93 -/**
94 - * Show the special page.
95 - */
96 -function wfSpecialNewbieContributions( $par = null ) {
97 - global $wgRequest, $wgUser, $wgOut;
98 -
99 - $page = new NewbieContributionsPage();
100 -
101 - $page->namespace = $wgRequest->getIntOrNull( 'namespace' );
102 - $page->botmode = ( $wgUser->isAllowed( 'rollback' ) && $wgRequest->getBool( 'bot' ) );
103 -
104 - list( $limit, $offset ) = wfCheckLimits();
105 - return $page->doQuery( $offset, $limit );
106 -}
107 -
108 -?>
Index: trunk/phase3/includes/SpecialContributions.php
@@ -1,344 +0,0 @@
2 -<?php
3 -
4 -/**
5 - * Special page allowing users to view their own contributions
6 - * and those of others.
7 - *
8 - * @package MediaWiki
9 - * @subpackage Special pages
10 - */
11 -class ContributionsPage extends QueryPage {
12 - var $user = null;
13 - var $namespace = null;
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->user = User::newFromName( $username, false );
22 - }
23 -
24 - function openList( $offset ) {
25 - return "<ul class='special'>";
26 - }
27 -
28 - function closeList() {
29 - return '</ul>';
30 - }
31 -
32 - /**
33 - * @return string Name of this special page.
34 - */
35 - function getName() {
36 - return 'Contributions';
37 - }
38 -
39 - /**
40 - * Not expensive, won't work with the query cache anyway.
41 - */
42 - function isExpensive() { return false; }
43 -
44 - /**
45 - * Should we?
46 - */
47 - function isSyndicated() { return false; }
48 -
49 - /**
50 - * Get target user name. May be overridden in subclasses.
51 - * @return string username
52 - */
53 - function getUsername() {
54 - return $this->user->getName();
55 - }
56 -
57 - /**
58 - * @return array Extra URL params for self-links.
59 - */
60 - function linkParameters() {
61 - $params['target'] = $this->getUsername();
62 -
63 - if ( isset($this->namespace) )
64 - $params['namespace'] = $this->namespace;
65 -
66 - if ( $this->botmode )
67 - $params['bot'] = 1;
68 -
69 - return $params;
70 - }
71 -
72 - /**
73 - * Build the list of links to be shown in the subtitle.
74 - * @return string Link list for "contribsub" UI message.
75 - */
76 - function getTargetUserLinks() {
77 - global $wgSysopUserBans, $wgLang, $wgUser;
78 -
79 - $skin = $wgUser->getSkin();
80 -
81 - $username = $this->getUsername();
82 - $userpage = $this->user->getUserPage();
83 - $userlink = $skin->makeLinkObj( $userpage, $username );
84 -
85 - // talk page link
86 - $tools[] = $skin->makeLinkObj( $userpage->getTalkPage(), $wgLang->getNsText( NS_TALK ) );
87 -
88 - // block or block log link
89 - $id = $this->user->getId();
90 - if ( ( $id != 0 && $wgSysopUserBans ) || ( $id == 0 && User::isIP( $username ) ) ) {
91 - if( $wgUser->isAllowed( 'block' ) )
92 - $tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Blockip', $username ),
93 - wfMsgHtml( 'blocklink' ) );
94 - else
95 - $tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ),
96 - htmlspecialchars( LogPage::logName( 'block' ) ),
97 - 'type=block&page=' . $userpage->getPrefixedUrl() );
98 - }
99 -
100 - // other logs link
101 - $tools[] = $skin->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ),
102 - wfMsgHtml( 'log' ),
103 - 'user=' . $userpage->getPartialUrl() );
104 -
105 - return $userlink . ' (' . implode( ' | ', $tools ) . ')';
106 - }
107 -
108 - /**
109 - * Generate "For User (...)" message in subtitle. Calls
110 - * getTargetUserLinks() for most of the work.
111 - * @return string
112 - */
113 - function getSubtitleForTarget() {
114 - return wfMsgHtml( 'contribsub', $this->getTargetUserLinks() );
115 - }
116 -
117 - /**
118 - * If the user has deleted contributions and we are allowed to
119 - * view them, generate a link to Special:DeletedContributions.
120 - * @return string
121 - */
122 - function getDeletedContributionsLink() {
123 - global $wgUser;
124 -
125 - if( !$wgUser->isAllowed( 'deletedhistory' ) )
126 - return '';
127 -
128 - $dbr = wfGetDB( DB_SLAVE );
129 - $n = $dbr->selectField( 'archive', 'count(*)', array( 'ar_user_text' => $this->getUsername() ), __METHOD__ );
130 -
131 - if ( $n == 0 )
132 - return '';
133 -
134 - $msg = wfMsg( ( $wgUser->isAllowed( 'delete' ) ? 'thisisdeleted' : 'viewdeleted' ),
135 - $wgUser->getSkin()->makeKnownLinkObj(
136 - SpecialPage::getTitleFor( 'DeletedContributions', $this->getUsername() ),
137 - wfMsgExt( 'restorelink', array( 'parsemag', 'escape' ), $n ) ) );
138 -
139 - return "<p>$msg</p>";
140 - }
141 -
142 - /**
143 - * Construct and output the page subtitle.
144 - */
145 - function outputSubtitle() {
146 - global $wgOut;
147 - $subtitle = $this->getSubtitleForTarget();
148 - // $subtitle .= $this->getDeletedContributionsLink(); NOT YET...
149 - $wgOut->setSubtitle( $subtitle );
150 - }
151 -
152 - /**
153 - * Construct the namespace selector form.
154 - * @return string
155 - */
156 - function getNamespaceForm() {
157 - $title = $this->getTitle();
158 -
159 - $ns = $this->namespace;
160 - if ( !isset($ns) )
161 - $ns = '';
162 -
163 - $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $title->getLocalUrl() ) );
164 - $form .= wfMsgHtml( 'namespace' ) . ' ';
165 - $form .= Xml::namespaceSelector( $ns, '' ) . ' ';
166 - $form .= Xml::submitButton( wfMsg( 'allpagessubmit' ) );
167 - $form .= Xml::hidden( 'offset', $this->offset );
168 - $form .= Xml::hidden( 'limit', $this->limit );
169 - $form .= Xml::hidden( 'target', $this->getUsername() );
170 - if ( $this->botmode )
171 - $form .= Xml::hidden( 'bot', 1 );
172 - $form .= '</form>';
173 -
174 - return '<p>' . $form . '</p>';
175 - }
176 -
177 - /**
178 - * Build the page header. Also calls outputSubtitle().
179 - * @return string
180 - */
181 - function getPageHeader() {
182 - $this->outputSubtitle();
183 - return $this->getNamespaceForm();
184 - }
185 -
186 - /**
187 - * Construct the WHERE clause of the SQL SELECT statement for
188 - * this query.
189 - * @return string
190 - */
191 - function makeSQLCond( $dbr ) {
192 - $cond = ' page_id = rev_page';
193 - $cond .= ' AND rev_user_text = ' . $dbr->addQuotes( $this->getUsername() );
194 -
195 - if ( isset($this->namespace) )
196 - $cond .= ' AND page_namespace = ' . (int)$this->namespace;
197 -
198 - return $cond;
199 - }
200 -
201 - /**
202 - * Construct the SQL SELECT statement for this query.
203 - * @return string
204 - */
205 - function getSQL() {
206 - $dbr = wfGetDB( DB_SLAVE );
207 -
208 - list( $page, $revision ) = $dbr->tableNamesN( 'page', 'revision' );
209 -
210 - // XXX: the username and userid fields aren't used for much here,
211 - // but some subclasses rely on them more than we do.
212 -
213 - return "SELECT 'Contributions' as type,
214 - page_namespace AS namespace,
215 - page_title AS title,
216 - rev_timestamp AS value,
217 - rev_user AS userid,
218 - rev_user_text AS username,
219 - rev_minor_edit AS is_minor,
220 - page_latest AS cur_id,
221 - rev_id AS rev_id,
222 - rev_comment AS comment,
223 - rev_deleted AS deleted
224 - FROM $page, $revision
225 - WHERE " . $this->makeSQLCond( $dbr );
226 - }
227 -
228 - /**
229 - * Get user links for output row, for subclasses that may want
230 - * such functionality.
231 - *
232 - * @param $skin Skin to use
233 - * @param $row Result row
234 - * @return string
235 - */
236 - function getRowUserLinks( $skin, $row ) { return ''; }
237 -
238 - /**
239 - * Format a row, providing the timestamp, links to the
240 - * page/diff/history and a comment
241 - *
242 - * @param $skin Skin to use
243 - * @param $row Result row
244 - * @return string
245 - */
246 - function formatResult( $skin, $row ) {
247 - global $wgLang, $wgContLang, $wgUser;
248 -
249 - $dm = $wgContLang->getDirMark();
250 -
251 - /*
252 - * Cache UI messages in a static array so we don't
253 - * have to regenerate them for each row.
254 - */
255 - static $messages;
256 - if( !isset( $messages ) ) {
257 - foreach( explode( ' ', 'uctop diff newarticle rollbacklink diff hist minoreditletter' ) as $msg )
258 - $messages[$msg] = wfMsgExt( $msg, array( 'escape') );
259 - }
260 -
261 - $page = Title::makeTitle( $row->namespace, $row->title );
262 -
263 - /*
264 - * HACK: We need a revision object, so we make a very
265 - * heavily stripped-down one. All we really need are
266 - * the comment, the title and the deletion bitmask.
267 - */
268 - $rev = new Revision( array(
269 - 'comment' => $row->comment,
270 - 'deleted' => $row->deleted,
271 - 'user_text' => $row->username,
272 - 'user' => $row->userid,
273 - ) );
274 - $rev->setTitle( $page );
275 -
276 - $ts = wfTimestamp( TS_MW, $row->value );
277 - $time = $wgLang->timeAndDate( $ts, true );
278 - $hist = $skin->makeKnownLinkObj( $page, $messages['hist'], 'action=history' );
279 -
280 - if ( $rev->userCan( Revision::DELETED_TEXT ) )
281 - $diff = $skin->makeKnownLinkObj( $page, $messages['diff'], 'diff=prev&oldid=' . $row->rev_id );
282 - else
283 - $diff = $messages['diff'];
284 -
285 - if( $row->is_minor )
286 - $mflag = '<span class="minor">' . $messages['minoreditletter'] . '</span> ';
287 - else
288 - $mflag = '';
289 -
290 - $link = $skin->makeKnownLinkObj( $page );
291 - $comment = $skin->revComment( $rev );
292 -
293 - $user = $this->getRowUserLinks( $skin, $row ); // for subclasses
294 -
295 - $notes = '';
296 -
297 - if( $row->rev_id == $row->cur_id ) {
298 - $notes .= ' <strong>' . $messages['uctop'] . '</strong>';
299 -
300 - if( $wgUser->isAllowed( 'rollback' ) )
301 - $notes .= ' ' . $skin->generateRollback( $rev );
302 - }
303 -
304 - if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
305 - $time = '<span class="history-deleted">' . $time . '</span>';
306 - $notes .= ' ' . wfMsgHtml( 'deletedrev' );
307 - }
308 -
309 - return "{$time} ({$hist}) ({$diff}) {$mflag} {$dm}{$link}{$user} {$comment}{$notes}";
310 - }
311 -}
312 -
313 -/**
314 - * Show the special page.
315 - */
316 -function wfSpecialContributions( $par = null ) {
317 - global $wgRequest, $wgUser, $wgOut;
318 -
319 - $username = ( isset($par) ? $par : $wgRequest->getVal( 'target' ) );
320 -
321 - // compatibility hack
322 - if ( $username == 'newbies' ) {
323 - $wgOut->redirect( SpecialPage::getTitleFor( 'NewbieContributions' )->getFullURL() );
324 - return;
325 - }
326 -
327 - $page = new ContributionsPage( $username );
328 -
329 - if( !$page->user ) {
330 - $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
331 - return;
332 - }
333 -
334 - // hook for Contributionseditcount extension
335 - if ( $page->user && $page->user->isLoggedIn() )
336 - wfRunHooks( 'SpecialContributionsBeforeMainOutput', $page->user->getId() );
337 -
338 - $page->namespace = $wgRequest->getIntOrNull( 'namespace' );
339 - $page->botmode = ( $wgUser->isAllowed( 'rollback' ) && $wgRequest->getBool( 'bot' ) );
340 -
341 - list( $limit, $offset ) = wfCheckLimits();
342 - return $page->doQuery( $offset, $limit );
343 -}
344 -
345 -?>
Index: trunk/phase3/includes/SpecialContributions.php
@@ -0,0 +1,435 @@
 2+<?php
 3+/**
 4+ * @package MediaWiki
 5+ * @subpackage SpecialPage
 6+ */
 7+
 8+/** @package MediaWiki */
 9+class ContribsFinder {
 10+ var $username, $offset, $limit, $namespace;
 11+ var $dbr;
 12+
 13+ function ContribsFinder( $username ) {
 14+ $this->username = $username;
 15+ $this->namespace = false;
 16+ $this->dbr =& wfGetDB( DB_SLAVE );
 17+ }
 18+
 19+ function setNamespace( $ns ) {
 20+ $this->namespace = $ns;
 21+ }
 22+
 23+ function setLimit( $limit ) {
 24+ $this->limit = $limit;
 25+ }
 26+
 27+ function setOffset( $offset ) {
 28+ $this->offset = $offset;
 29+ }
 30+
 31+ function getEditLimit( $dir ) {
 32+ list( $index, $usercond ) = $this->getUserCond();
 33+ $nscond = $this->getNamespaceCond();
 34+ $use_index = $this->dbr->useIndexClause( $index );
 35+ list( $revision, $page) = $this->dbr->tableNamesN( 'revision', 'page' );
 36+ $sql = "SELECT rev_timestamp " .
 37+ " FROM $page,$revision $use_index " .
 38+ " WHERE rev_page=page_id AND $usercond $nscond" .
 39+ " ORDER BY rev_timestamp $dir LIMIT 1";
 40+
 41+ $res = $this->dbr->query( $sql, __METHOD__ );
 42+ $row = $this->dbr->fetchObject( $res );
 43+ if ( $row ) {
 44+ return $row->rev_timestamp;
 45+ } else {
 46+ return false;
 47+ }
 48+ }
 49+
 50+ function getEditLimits() {
 51+ return array(
 52+ $this->getEditLimit( "ASC" ),
 53+ $this->getEditLimit( "DESC" )
 54+ );
 55+ }
 56+
 57+ function getUserCond() {
 58+ $condition = '';
 59+
 60+ if ( $this->username == 'newbies' ) {
 61+ $max = $this->dbr->selectField( 'user', 'max(user_id)', false, 'make_sql' );
 62+ $condition = '>' . (int)($max - $max / 100);
 63+ }
 64+
 65+ if ( $condition == '' ) {
 66+ $condition = ' rev_user_text=' . $this->dbr->addQuotes( $this->username );
 67+ $index = 'usertext_timestamp';
 68+ } else {
 69+ $condition = ' rev_user '.$condition ;
 70+ $index = 'user_timestamp';
 71+ }
 72+ return array( $index, $condition );
 73+ }
 74+
 75+ function getNamespaceCond() {
 76+ if ( $this->namespace !== false )
 77+ return ' AND page_namespace = ' . (int)$this->namespace;
 78+ return '';
 79+ }
 80+
 81+ function getPreviousOffsetForPaging() {
 82+ list( $index, $usercond ) = $this->getUserCond();
 83+ $nscond = $this->getNamespaceCond();
 84+
 85+ $use_index = $this->dbr->useIndexClause( $index );
 86+ list( $page, $revision ) = $this->dbr->tableNamesN( 'page', 'revision' );
 87+
 88+ $sql = "SELECT rev_timestamp FROM $page, $revision $use_index " .
 89+ "WHERE page_id = rev_page AND rev_timestamp > '" . $this->offset . "' AND " .
 90+ $usercond . $nscond;
 91+ $sql .= " ORDER BY rev_timestamp ASC";
 92+ $sql = $this->dbr->limitResult( $sql, $this->limit, 0 );
 93+ $res = $this->dbr->query( $sql );
 94+
 95+ $numRows = $this->dbr->numRows( $res );
 96+ if ( $numRows ) {
 97+ $this->dbr->dataSeek( $res, $numRows - 1 );
 98+ $row = $this->dbr->fetchObject( $res );
 99+ $offset = $row->rev_timestamp;
 100+ } else {
 101+ $offset = false;
 102+ }
 103+ $this->dbr->freeResult( $res );
 104+ return $offset;
 105+ }
 106+
 107+ function getFirstOffsetForPaging() {
 108+ list( $index, $usercond ) = $this->getUserCond();
 109+ $use_index = $this->dbr->useIndexClause( $index );
 110+ list( $page, $revision ) = $this->dbr->tableNamesN( 'page', 'revision' );
 111+ $nscond = $this->getNamespaceCond();
 112+ $sql = "SELECT rev_timestamp FROM $page, $revision $use_index " .
 113+ "WHERE page_id = rev_page AND " .
 114+ $usercond . $nscond;
 115+ $sql .= " ORDER BY rev_timestamp ASC";
 116+ $sql = $this->dbr->limitResult( $sql, $this->limit, 0 );
 117+ $res = $this->dbr->query( $sql );
 118+
 119+ $numRows = $this->dbr->numRows( $res );
 120+ if ( $numRows ) {
 121+ $this->dbr->dataSeek( $res, $numRows - 1 );
 122+ $row = $this->dbr->fetchObject( $res );
 123+ $offset = $row->rev_timestamp;
 124+ } else {
 125+ $offset = false;
 126+ }
 127+ $this->dbr->freeResult( $res );
 128+ return $offset;
 129+ }
 130+
 131+ /* private */ function makeSql() {
 132+ $offsetQuery = '';
 133+
 134+ list( $page, $revision ) = $this->dbr->tableNamesN( 'page', 'revision' );
 135+ list( $index, $userCond ) = $this->getUserCond();
 136+
 137+ if ( $this->offset )
 138+ $offsetQuery = "AND rev_timestamp <= '{$this->offset}'";
 139+
 140+ $nscond = $this->getNamespaceCond();
 141+ $use_index = $this->dbr->useIndexClause( $index );
 142+ $sql = "SELECT
 143+ page_namespace,page_title,page_is_new,page_latest,
 144+ rev_id,rev_page,rev_text_id,rev_timestamp,rev_comment,rev_minor_edit,rev_user,rev_user_text,
 145+ rev_deleted
 146+ FROM $page,$revision $use_index
 147+ WHERE page_id=rev_page AND $userCond $nscond $offsetQuery
 148+ ORDER BY rev_timestamp DESC";
 149+ $sql = $this->dbr->limitResult( $sql, $this->limit, 0 );
 150+ return $sql;
 151+ }
 152+
 153+ function find() {
 154+ $contribs = array();
 155+ $res = $this->dbr->query( $this->makeSql(), __METHOD__ );
 156+ while ( $c = $this->dbr->fetchObject( $res ) )
 157+ $contribs[] = $c;
 158+ $this->dbr->freeResult( $res );
 159+ return $contribs;
 160+ }
 161+};
 162+
 163+/**
 164+ * Special page "user contributions".
 165+ * Shows a list of the contributions of a user.
 166+ *
 167+ * @return none
 168+ * @param $par String: (optional) user name of the user for which to show the contributions
 169+ */
 170+function wfSpecialContributions( $par = null ) {
 171+ global $wgUser, $wgOut, $wgLang, $wgRequest;
 172+
 173+ $target = isset( $par ) ? $par : $wgRequest->getVal( 'target' );
 174+ if ( !strlen( $target ) ) {
 175+ $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
 176+ return;
 177+ }
 178+
 179+ $nt = Title::newFromURL( $target );
 180+ if ( !$nt ) {
 181+ $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
 182+ return;
 183+ }
 184+
 185+ $options = array();
 186+
 187+ list( $options['limit'], $options['offset']) = wfCheckLimits();
 188+ $options['offset'] = $wgRequest->getVal( 'offset' );
 189+ /* Offset must be an integral. */
 190+ if ( !strlen( $options['offset'] ) || !preg_match( '/^[0-9]+$/', $options['offset'] ) )
 191+ $options['offset'] = '';
 192+
 193+ $title = SpecialPage::getTitleFor( 'Contributions' );
 194+ $options['target'] = $target;
 195+
 196+ $nt =& Title::makeTitle( NS_USER, $nt->getDBkey() );
 197+ $finder = new ContribsFinder( ( $target == 'newbies' ) ? 'newbies' : $nt->getText() );
 198+ $finder->setLimit( $options['limit'] );
 199+ $finder->setOffset( $options['offset'] );
 200+
 201+ if ( ( $ns = $wgRequest->getVal( 'namespace', null ) ) !== null && $ns !== '' ) {
 202+ $options['namespace'] = intval( $ns );
 203+ $finder->setNamespace( $options['namespace'] );
 204+ } else {
 205+ $options['namespace'] = '';
 206+ }
 207+
 208+ if ( $wgUser->isAllowed( 'rollback' ) && $wgRequest->getBool( 'bot' ) ) {
 209+ $options['bot'] = '1';
 210+ }
 211+
 212+ if ( $wgRequest->getText( 'go' ) == 'prev' ) {
 213+ $offset = $finder->getPreviousOffsetForPaging();
 214+ if ( $offset !== false ) {
 215+ $options['offset'] = $offset;
 216+ $prevurl = $title->getLocalURL( wfArrayToCGI( $options ) );
 217+ $wgOut->redirect( $prevurl );
 218+ return;
 219+ }
 220+ }
 221+
 222+ if ( $wgRequest->getText( 'go' ) == 'first' && $target != 'newbies') {
 223+ $offset = $finder->getFirstOffsetForPaging();
 224+ if ( $offset !== false ) {
 225+ $options['offset'] = $offset;
 226+ $prevurl = $title->getLocalURL( wfArrayToCGI( $options ) );
 227+ $wgOut->redirect( $prevurl );
 228+ return;
 229+ }
 230+ }
 231+
 232+ if ( $target == 'newbies' ) {
 233+ $wgOut->setSubtitle( wfMsgHtml( 'sp-contributions-newbies-sub') );
 234+ } else {
 235+ $wgOut->setSubtitle( wfMsgHtml( 'contribsub', contributionsSub( $nt ) ) );
 236+ }
 237+
 238+ $id = User::idFromName( $nt->getText() );
 239+ wfRunHooks( 'SpecialContributionsBeforeMainOutput', $id );
 240+
 241+ $wgOut->addHTML( contributionsForm( $options) );
 242+
 243+ $contribs = $finder->find();
 244+
 245+ if ( count( $contribs ) == 0) {
 246+ $wgOut->addWikiText( wfMsg( 'nocontribs' ) );
 247+ return;
 248+ }
 249+
 250+ list( $early, $late ) = $finder->getEditLimits();
 251+ $lastts = count( $contribs ) ? $contribs[count( $contribs ) - 1]->rev_timestamp : 0;
 252+ $atstart = ( !count( $contribs ) || $late == $contribs[0]->rev_timestamp );
 253+ $atend = ( !count( $contribs ) || $early == $lastts );
 254+
 255+ // These four are defaults
 256+ $newestlink = wfMsgHtml( 'sp-contributions-newest' );
 257+ $oldestlink = wfMsgHtml( 'sp-contributions-oldest' );
 258+ $newerlink = wfMsgHtml( 'sp-contributions-newer', $options['limit'] );
 259+ $olderlink = wfMsgHtml( 'sp-contributions-older', $options['limit'] );
 260+
 261+ if ( !$atstart ) {
 262+ $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'offset' => '' ), $options ) );
 263+ $newestlink = "<a href=\"$stuff\">$newestlink</a>";
 264+ $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'go' => 'prev' ), $options ) );
 265+ $newerlink = "<a href=\"$stuff\">$newerlink</a>";
 266+ }
 267+
 268+ if ( !$atend ) {
 269+ $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'go' => 'first' ), $options ) );
 270+ $oldestlink = "<a href=\"$stuff\">$oldestlink</a>";
 271+ $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'offset' => $lastts ), $options ) );
 272+ $olderlink = "<a href=\"$stuff\">$olderlink</a>";
 273+ }
 274+
 275+ if ( $target == 'newbies' ) {
 276+ $firstlast ="($newestlink)";
 277+ } else {
 278+ $firstlast = "($newestlink | $oldestlink)";
 279+ }
 280+
 281+ $urls = array();
 282+ foreach ( array( 20, 50, 100, 250, 500 ) as $num ) {
 283+ $stuff = $title->escapeLocalURL( wfArrayToCGI( array( 'limit' => $num ), $options ) );
 284+ $urls[] = "<a href=\"$stuff\">".$wgLang->formatNum( $num )."</a>";
 285+ }
 286+ $bits = implode( $urls, ' | ' );
 287+
 288+ $prevnextbits = $firstlast .' '. wfMsgHtml( 'viewprevnext', $newerlink, $olderlink, $bits );
 289+
 290+ $wgOut->addHTML( "<p>{$prevnextbits}</p>\n" );
 291+
 292+ $wgOut->addHTML( "<ul>\n" );
 293+
 294+ $sk = $wgUser->getSkin();
 295+ foreach ( $contribs as $contrib )
 296+ $wgOut->addHTML( ucListEdit( $sk, $contrib ) );
 297+
 298+ $wgOut->addHTML( "</ul>\n" );
 299+ $wgOut->addHTML( "<p>{$prevnextbits}</p>\n" );
 300+}
 301+
 302+/**
 303+ * Generates the subheading with links
 304+ * @param $nt @see Title object for the target
 305+ */
 306+function contributionsSub( $nt ) {
 307+ global $wgSysopUserBans, $wgLang, $wgUser;
 308+
 309+ $sk = $wgUser->getSkin();
 310+ $id = User::idFromName( $nt->getText() );
 311+
 312+ if ( 0 == $id ) {
 313+ $ul = $nt->getText();
 314+ } else {
 315+ $ul = $sk->makeLinkObj( $nt, htmlspecialchars( $nt->getText() ) );
 316+ }
 317+ $talk = $nt->getTalkPage();
 318+ if( $talk ) {
 319+ # Talk page link
 320+ $tools[] = $sk->makeLinkObj( $talk, $wgLang->getNsText( NS_TALK ) );
 321+ if( ( $id != 0 && $wgSysopUserBans ) || ( $id == 0 && User::isIP( $nt->getText() ) ) ) {
 322+ # Block link
 323+ if( $wgUser->isAllowed( 'block' ) )
 324+ $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Blockip', $nt->getDBkey() ), wfMsgHtml( 'blocklink' ) );
 325+ # Block log link
 326+ $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), htmlspecialchars( LogPage::logName( 'block' ) ), 'type=block&page=' . $nt->getPrefixedUrl() );
 327+ }
 328+ # Other logs link
 329+ $tools[] = $sk->makeKnownLinkObj( SpecialPage::getTitleFor( 'Log' ), wfMsgHtml( 'log' ), 'user=' . $nt->getPartialUrl() );
 330+ $ul .= ' (' . implode( ' | ', $tools ) . ')';
 331+ }
 332+ return $ul;
 333+}
 334+
 335+/**
 336+ * Generates the namespace selector form with hidden attributes.
 337+ * @param $options Array: the options to be included.
 338+ */
 339+function contributionsForm( $options ) {
 340+ global $wgScript, $wgTitle;
 341+
 342+ $options['title'] = $wgTitle->getPrefixedText();
 343+
 344+ $f = "<form method='get' action=\"$wgScript\">\n";
 345+ foreach ( $options as $name => $value ) {
 346+ if( $name === 'namespace') continue;
 347+ $f .= "\t" . wfElement( 'input', array(
 348+ 'name' => $name,
 349+ 'type' => 'hidden',
 350+ 'value' => $value ) ) . "\n";
 351+ }
 352+
 353+ $f .= '<p>' . wfMsgHtml( 'namespace' ) . ' ' .
 354+ HTMLnamespaceselector( $options['namespace'], '' ) .
 355+ wfElement( 'input', array(
 356+ 'type' => 'submit',
 357+ 'value' => wfMsg( 'allpagessubmit' ) )
 358+ ) .
 359+ "</p></form>\n";
 360+
 361+ return $f;
 362+}
 363+
 364+/**
 365+ * Generates each row in the contributions list.
 366+ *
 367+ * Contributions which are marked "top" are currently on top of the history.
 368+ * For these contributions, a [rollback] link is shown for users with sysop
 369+ * privileges. The rollback link restores the most recent version that was not
 370+ * written by the target user.
 371+ *
 372+ * @todo This would probably look a lot nicer in a table.
 373+ */
 374+function ucListEdit( $sk, $row ) {
 375+ $fname = 'ucListEdit';
 376+ wfProfileIn( $fname );
 377+
 378+ global $wgLang, $wgUser, $wgRequest;
 379+ static $messages;
 380+ if( !isset( $messages ) ) {
 381+ foreach( explode( ' ', 'uctop diff newarticle rollbacklink diff hist minoreditletter' ) as $msg ) {
 382+ $messages[$msg] = wfMsgExt( $msg, array( 'escape') );
 383+ }
 384+ }
 385+
 386+ $rev = new Revision( $row );
 387+
 388+ $page = Title::makeTitle( $row->page_namespace, $row->page_title );
 389+ $link = $sk->makeKnownLinkObj( $page );
 390+ $difftext = $topmarktext = '';
 391+ if( $row->rev_id == $row->page_latest ) {
 392+ $topmarktext .= '<strong>' . $messages['uctop'] . '</strong>';
 393+ if( !$row->page_is_new ) {
 394+ $difftext .= '(' . $sk->makeKnownLinkObj( $page, $messages['diff'], 'diff=0' ) . ')';
 395+ } else {
 396+ $difftext .= $messages['newarticle'];
 397+ }
 398+
 399+ if( $wgUser->isAllowed( 'rollback' ) ) {
 400+ $topmarktext .= ' '.$sk->generateRollback( $rev );
 401+ }
 402+
 403+ }
 404+
 405+ if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
 406+ if ( $rev->userCan( Revision::DELETED_TEXT ) ) {
 407+ $difftext .= '(' . $sk->makeKnownLinkObj( $page, $messages['diff'], 'diff=prev&oldid='.$row->rev_id ) . ')';
 408+ } else {
 409+ $difftext .= '(' . $messages['diff'] . ')';
 410+ }
 411+ }
 412+ $histlink='('.$sk->makeKnownLinkObj( $page, $messages['hist'], 'action=history' ) . ')';
 413+
 414+ $comment = $sk->revComment( $rev );
 415+ $d = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->rev_timestamp ), true );
 416+
 417+ if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
 418+ $d = '<span class="history-deleted">' . $d . '</span>';
 419+ }
 420+
 421+ if( $row->rev_minor_edit ) {
 422+ $mflag = '<span class="minor">' . $messages['minoreditletter'] . '</span> ';
 423+ } else {
 424+ $mflag = '';
 425+ }
 426+
 427+ $ret = "{$d} {$histlink} {$difftext} {$mflag} {$link} {$comment} {$topmarktext}";
 428+ if( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
 429+ $ret .= ' ' . wfMsgHtml( 'deletedrev' );
 430+ }
 431+ $ret = "<li>$ret</li>\n";
 432+ wfProfileOut( $fname );
 433+ return $ret;
 434+}
 435+
 436+?>
Index: trunk/phase3/includes/QueryPage.php
@@ -14,7 +14,6 @@
1515 array( 'AncientPagesPage', 'Ancientpages' ),
1616 array( 'BrokenRedirectsPage', 'BrokenRedirects' ),
1717 array( 'CategoriesPage', 'Categories' ),
18 - array( 'ContributionsPage', 'Contributions' ),
1918 array( 'DeadendPagesPage', 'Deadendpages' ),
2019 array( 'DisambiguationsPage', 'Disambiguations' ),
2120 array( 'DoubleRedirectsPage', 'DoubleRedirects' ),
@@ -28,7 +27,6 @@
2928 array( 'MostlinkedPage', 'Mostlinked' ),
3029 array( 'MostrevisionsPage', 'Mostrevisions' ),
3130 array( 'NewPagesPage', 'Newpages' ),
32 - array( 'NewbieContributionsPage', 'NewbieContributions' ),
3331 array( 'ShortPagesPage', 'Shortpages' ),
3432 array( 'UncategorizedCategoriesPage', 'Uncategorizedcategories' ),
3533 array( 'UncategorizedPagesPage', 'Uncategorizedpages' ),
Index: trunk/phase3/includes/SpecialPage.php
@@ -114,7 +114,6 @@
115115 'Ipblocklist' => array( 'SpecialPage', 'Ipblocklist' ),
116116 'Specialpages' => array( 'UnlistedSpecialPage', 'Specialpages' ),
117117 'Contributions' => array( 'UnlistedSpecialPage', 'Contributions' ),
118 - 'NewbieContributions' => array( 'SpecialPage', 'NewbieContributions' ),
119118 'Emailuser' => array( 'UnlistedSpecialPage', 'Emailuser' ),
120119 'Whatlinkshere' => array( 'UnlistedSpecialPage', 'Whatlinkshere' ),
121120 'Recentchangeslinked' => array( 'UnlistedSpecialPage', 'Recentchangeslinked' ),
Index: trunk/phase3/RELEASE-NOTES
@@ -242,9 +242,6 @@
243243 * {{REVISIONTIMESTAMP}} now uses site local timezone instead of user timezone
244244 to ensure consistent behavior
245245 * {{REVISIONTIMESTAMP}} and friends should now work on non-MySQL backends
246 -* Special:Contributions has been rewritten to inherit from QueryPage
247 -* New special page Special:NewbieContributions, with a (deprecated)
248 - redirect from Special:Contributions/newbies for backwards compatibility
249246 * (bug 7671) Observe canonical media namespace prefix in Linker::formatComment
250247 * Added js variable wgCurRevisionId to the output
251248 * (bug 8141) Cleanup of Parser::doTableStuff, patch by AzaTht
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -380,7 +380,6 @@
381381 'Ipblocklist' => array( 'Ipblocklist' ),
382382 'Specialpages' => array( 'Specialpages' ),
383383 'Contributions' => array( 'Contributions' ),
384 - 'NewbieContributions' => array( 'NewbieContributions' ),
385384 'Emailuser' => array( 'Emailuser' ),
386385 'Whatlinkshere' => array( 'Whatlinkshere' ),
387386 'Recentchangeslinked' => array( 'Recentchangeslinked' ),
@@ -1755,13 +1754,21 @@
17561755 'contributions' => 'User contributions',
17571756 'mycontris' => 'My contributions',
17581757 'contribsub' => "For $1",
 1758+'nocontribs' => 'No changes were found matching these criteria.',
 1759+'ucnote' => "Below are this user's last <b>$1</b> changes in the last <b>$2</b> days.",
 1760+'uclinks' => "View the last $1 changes; view the last $2 days.",
17591761 'uctop' => ' (top)' ,
 1762+'newbies' => 'newbies',
17601763
1761 -# Newbie contributions
1762 -#
1763 -'newbiecontributions' => 'Newbie contributions',
 1764+'sp-newimages-showfrom' => 'Show new images starting from $1',
17641765
 1766+'sp-contributions-newest' => 'Newest',
 1767+'sp-contributions-oldest' => 'Oldest',
 1768+'sp-contributions-newer' => 'Newer $1',
 1769+'sp-contributions-older' => 'Older $1',
 1770+'sp-contributions-newbies-sub' => 'For newbies',
17651771
 1772+
17661773 # What links here
17671774 #
17681775 'whatlinkshere' => 'What links here',
@@ -2161,7 +2168,6 @@
21622169 'newimages-summary' => '',
21632170 'showhidebots' => '($1 bots)',
21642171 'noimages' => 'Nothing to see.',
2165 -'sp-newimages-showfrom' => 'Show new images starting from $1',
21662172
21672173 # short names for language variants used for language conversion links.
21682174 # to disable showing a particular link, set it to 'disable', e.g.
Index: trunk/phase3/languages/messages/MessagesFi.php
@@ -1137,11 +1137,17 @@
11381138 'contributions' => 'Käyttäjän muokkaukset',
11391139 'mycontris' => 'Muokkaukset',
11401140 'contribsub' => 'Käyttäjän $1 muokkaukset',
 1141+'nocontribs' => 'Näihin ehtoihin sopivia muokkauksia ei löytynyt.',
 1142+'ucnote' => 'Alla on \'\'\'$1\'\'\' viimeisintä tämän käyttäjän tekemää muokkausta viimeisten \'\'\'$2\'\'\' päivän aikana.',
 1143+'uclinks' => 'Katso $1 viimeisintä muokkausta; katso $2 viimeisintä päivää.',
11411144 'uctop' => ' (uusin)' ,
 1145+'newbies' => 'tulokkaat',
11421146
1143 -# Newbie contributions
1144 -#
1145 -'newbiecontributions' => 'Uusien tulokkaiden muokkaukset',
 1147+'sp-contributions-newest' => 'Uusimmat',
 1148+'sp-contributions-oldest' => 'Vanhimmat',
 1149+'sp-contributions-newer' => '← $1 uudempaa',
 1150+'sp-contributions-older' => '$1 vanhempaa →',
 1151+'sp-contributions-newbies-sub' => 'Uusien tulokkaiden muokkaukset',
11461152
11471153 # What links here
11481154 #

Follow-up revisions

RevisionCommit summaryAuthorDate
r18181Reverting r18153, per the revert of Special:Contributions and Special:NewbieC...rotem14:40, 5 December 2006