r23937 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r23936‎ | r23937 | r23938 >
Date:06:59, 10 July 2007
Author:aaron
Status:old
Tags:
Comment:
*Rename
Modified paths:
  • /trunk/extensions/FlaggedRevs/FlaggedRevsPage.body.php (deleted) (history)
  • /trunk/extensions/FlaggedRevs/FlaggedRevsPage_body.php (added) (history)

Diff [purge]

Index: trunk/extensions/FlaggedRevs/FlaggedRevsPage.body.php
@@ -1,813 +0,0 @@
2 -<?php
3 -
4 -#(c) Joerg Baach, Aaron Schulz, 2007 GPL
5 -
6 -global $IP;
7 -require_once( "$IP/includes/LogPage.php" );
8 -require_once( "$IP/includes/SpecialLog.php" );
9 -
10 -class Revisionreview extends SpecialPage
11 -{
12 -
13 - function Revisionreview() {
14 - SpecialPage::SpecialPage('Revisionreview', 'review');
15 - }
16 -
17 - function execute( $par ) {
18 - global $wgRequest, $wgUser, $wgOut, $wgFlaggedRevComments, $wgFlaggedRevTags;
19 -
20 - $confirm = $wgRequest->wasPosted() &&
21 - $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
22 -
23 - if( $wgUser->isAllowed( 'review' ) ) {
24 - if( $wgUser->isBlocked( !$confirm ) ) {
25 - $wgOut->blockedPage();
26 - return;
27 - }
28 - } else {
29 - $wgOut->permissionRequired( 'review' );
30 - return;
31 - }
32 -
33 - $this->setHeaders();
34 - // Our target page
35 - $this->target = $wgRequest->getText( 'target' );
36 - // Revision ID
37 - $this->oldid = $wgRequest->getIntOrNull( 'oldid' );
38 - // Must be a valid content page
39 - $this->page = Title::newFromUrl( $this->target );
40 - if ( !$this->target || !$this->oldid || !$this->page->isContentPage() ) {
41 - $wgOut->addHTML( wfMsgExt('revreview-main',array('parse')) );
42 - return;
43 - }
44 - if( is_null($this->page) || is_null($this->oldid) ) {
45 - $wgOut->showErrorPage('notargettitle', 'notargettext' );
46 - return;
47 - }
48 - // Check if page is protected
49 - if( !$this->page->quickUserCan( 'edit' ) ) {
50 - $wgOut->permissionRequired( 'badaccess-group0' );
51 - return;
52 - }
53 - // Special parameter mapping
54 - $this->templateParams = $wgRequest->getVal( 'templateParams' );
55 - $this->imageParams = $wgRequest->getVal( 'imageParams' );
56 - // Log comment
57 - $this->comment = $wgRequest->getText( 'wpReason' );
58 - // Additional notes
59 - $this->notes = ($wgFlaggedRevComments) ? $wgRequest->getText('wpNotes') : '';
60 - // Get the revision's current flags, if any
61 - $this->oflags = FlaggedRevs::getFlagsForPageRev( $this->oldid );
62 - // Get our accuracy/quality dimensions
63 - $this->dims = array();
64 - $this->upprovedTags = 0;
65 - foreach ( array_keys($wgFlaggedRevTags) as $tag ) {
66 - $this->dims[$tag] = $wgRequest->getIntOrNull( "wp$tag" );
67 - // Must be greater than zero
68 - if ( $this->dims[$tag] < 0 ) {
69 - $wgOut->showErrorPage('notargettitle', 'notargettext' );
70 - return;
71 - }
72 - if ( $this->dims[$tag]==0 )
73 - $this->upprovedTags++;
74 - // Check permissions
75 - if( !$this->userCan( $tag, $this->oflags[$tag] ) ) {
76 - # Users can't take away a status they can't set
77 - $wgOut->permissionRequired( 'badaccess-group0' );
78 - return;
79 - } else if( !$this->userCan( $tag, $this->dims[$tag] ) ) {
80 - // Users cannot review to beyond their rights level
81 - $wgOut->permissionRequired( 'badaccess-group0' );
82 - return;
83 - }
84 - }
85 - // We must at least rate each category as 1, the minimum
86 - // Exception: we can rate ALL as unapproved to depreciate a revision
87 - $this->isValid = true;
88 - if ( $this->upprovedTags && ($this->upprovedTags < count($wgFlaggedRevTags) || !$this->oflags) )
89 - $this->isValid = false;
90 -
91 - if( $this->isValid && $wgRequest->wasPosted() ) {
92 - $this->submit( $wgRequest );
93 - } else {
94 - $this->showRevision( $wgRequest );
95 - }
96 - }
97 -
98 - /**
99 - * @param string $tag
100 - * @param int $val
101 - * Returns true if a user can do something
102 - */
103 - public static function userCan( $tag, $value ) {
104 - global $wgFlagRestrictions, $wgUser;
105 -
106 - if ( !isset($wgFlagRestrictions[$tag]) )
107 - return true;
108 - // Validators always have full access
109 - if ( $wgUser->isAllowed('validate') )
110 - return true;
111 - // Check if this user has any right that lets him/her set
112 - // up to this particular value
113 - foreach ( $wgFlagRestrictions[$tag] as $right => $level ) {
114 - if ( $value <= $level && $wgUser->isAllowed($right) ) {
115 - return true;
116 - }
117 - }
118 - return false;
119 - }
120 -
121 - /**
122 - * @param webrequest $request
123 - */
124 - function showRevision( $request ) {
125 - global $wgOut, $wgUser, $wgTitle, $wgFlaggedRevComments, $wgFlaggedRevsOverride,
126 - $wgFlaggedRevTags, $wgFlaggedRevValues;
127 -
128 - if ( !$this->isValid )
129 - $wgOut->addWikiText( '<strong>' . wfMsg( 'revreview-toolow' ) . '</strong>' );
130 -
131 - $wgOut->addWikiText( wfMsg( 'revreview-selected', $this->page->getPrefixedText() ) );
132 -
133 - $this->skin = $wgUser->getSkin();
134 - $rev = Revision::newFromTitle( $this->page, $this->oldid );
135 - // Check if rev exists
136 - // Do not mess with deleted revisions
137 - if( !isset( $rev ) || $rev->mDeleted ) {
138 - $wgOut->showErrorPage( 'internalerror', 'notargettitle', 'notargettext' );
139 - return;
140 - }
141 -
142 - $wgOut->addHtml( "<ul>" );
143 - $wgOut->addHtml( $this->historyLine( $rev ) );
144 - $wgOut->addHtml( "</ul>" );
145 -
146 - if( $wgFlaggedRevsOverride )
147 - $wgOut->addWikiText( wfMsg('revreview-text') );
148 -
149 - $formradios = array();
150 - // Dynamically contruct our radio options
151 - foreach ( array_keys($wgFlaggedRevTags) as $tag ) {
152 - $formradios[$tag] = array();
153 - for ($i=0; $i <= $wgFlaggedRevValues; $i++) {
154 - $formradios[$tag][] = array( "revreview-$tag-$i", "wp$tag", $i );
155 - }
156 - }
157 - $items = array(
158 - wfInputLabel( wfMsgHtml( 'revreview-log' ), 'wpReason', 'wpReason', 60 ),
159 - wfSubmitButton( wfMsgHtml( 'revreview-submit' ) ) );
160 - $hidden = array(
161 - wfHidden( 'wpEditToken', $wgUser->editToken() ),
162 - wfHidden( 'target', $this->page->getPrefixedText() ),
163 - wfHidden( 'oldid', $this->oldid ) );
164 -
165 - $action = $wgTitle->escapeLocalUrl( 'action=submit' );
166 - $form = "<form name='revisionreview' action='$action' method='post'>";
167 - $form .= '<fieldset><legend>' . wfMsgHtml( 'revreview-legend' ) . '</legend><table><tr>';
168 - // Dynamically contruct our review types
169 - foreach ( array_keys($wgFlaggedRevTags) as $tag ) {
170 - $form .= '<td><strong>' . wfMsgHtml( "revreview-$tag" ) . '</strong></td><td width=\'20\'></td>';
171 - }
172 - $form .= '</tr><tr>';
173 - foreach ( $formradios as $set => $ratioset ) {
174 - $form .= '<td>';
175 - foreach( $ratioset as $item ) {
176 - list( $message, $name, $field ) = $item;
177 - // Don't give options the user can't set unless its the status quo
178 - $attribs = array('id' => $name.$field);
179 - if( !$this->userCan($set,$field) )
180 - $attribs['disabled'] = 'true';
181 - $form .= "<div>";
182 - $form .= Xml::radio( $name, $field, ($field==$this->dims[$set]), $attribs );
183 - $form .= Xml::label( wfMsg($message), $name.$field );
184 - $form .= "</div>\n";
185 - }
186 - $form .= '</td><td width=\'20\'></td>';
187 - }
188 - $form .= '</tr></table></fieldset>';
189 - // Add box to add live notes to a flagged revision
190 - if ( $wgFlaggedRevComments ) {
191 - $form .= "<fieldset><legend>" . wfMsgHtml( 'revreview-notes' ) . "</legend>" .
192 - "<textarea tabindex='1' name='wpNotes' id='wpNotes' rows='3' cols='80' style='width:100%'>$this->notes</textarea>" .
193 - "</fieldset>";
194 - }
195 -
196 - foreach( $items as $item ) {
197 - $form .= '<p>' . $item . '</p>';
198 - }
199 - foreach( $hidden as $item ) {
200 - $form .= $item;
201 - }
202 - // XXX: hack, versioning params
203 - $form .= Xml::hidden( 'templateParams', $this->templateParams );
204 - $form .= Xml::hidden( 'imageParams', $this->imageParams );
205 -
206 - $form .= '</form>';
207 - $wgOut->addHtml( $form );
208 - }
209 -
210 - /**
211 - * @param Revision $rev
212 - * @returns string
213 - */
214 - function historyLine( $rev ) {
215 - global $wgContLang;
216 - $date = $wgContLang->timeanddate( $rev->getTimestamp() );
217 -
218 - $difflink = '(' . $this->skin->makeKnownLinkObj( $this->page, wfMsgHtml('diff'),
219 - '&diff=' . $rev->getId() . '&oldid=prev' ) . ')';
220 -
221 - $revlink = $this->skin->makeLinkObj( $this->page, $date, 'oldid=' . $rev->getId() );
222 -
223 - return
224 - "<li> $difflink $revlink " . $this->skin->revUserLink( $rev ) . " " . $this->skin->revComment( $rev ) . "</li>";
225 - }
226 -
227 - function submit( $request ) {
228 - global $wgOut, $wgUser;
229 -
230 - $approved = false;
231 - # If all values are set to zero, this has been unapproved
232 - foreach( $this->dims as $quality => $value ) {
233 - if( $value ) {
234 - $approved = true;
235 - break;
236 - }
237 - }
238 - // We can only approve actual revisions...
239 - if ( $approved ) {
240 - $rev = Revision::newFromTitle( $this->page, $this->oldid );
241 - // Do not mess with archived/deleted revisions
242 - if ( is_null($rev) || $rev->mDeleted ) {
243 - $wgOut->showErrorPage( 'internalerror', 'revnotfoundtext' );
244 - return;
245 - }
246 - } else {
247 - $frev = FlaggedRevs::getFlaggedRev( $this->oldid );
248 - // If we can't find this flagged rev, return to page???
249 - if ( is_null($frev) ) {
250 - $wgOut->redirect( $this->page->escapeLocalUrl() );
251 - return;
252 - }
253 - }
254 -
255 - $success = $approved ? $this->approveRevision( $rev, $this->notes ) : $this->unapproveRevision( $frev );
256 -
257 - // Return to our page
258 - if ( $success ) {
259 - if( $request->getCheck( 'wpWatchthis' ) ) {
260 - $wgUser->addWatch( $this->page );
261 - } else {
262 - $wgUser->removeWatch( $this->page );
263 - }
264 - $wgOut->redirect( $this->page->escapeLocalUrl() );
265 - } else {
266 - $wgOut->showErrorPage( 'internalerror', 'revreview-changed' );
267 - }
268 - }
269 -
270 - /**
271 - * @param Revision $rev
272 - * Adds or updates the flagged revision table for this page/id set
273 - */
274 - function approveRevision( $rev=NULL, $notes='' ) {
275 - global $wgUser, $wgFlaggedRevsWatch, $wgParser;
276 -
277 - if( is_null($rev) )
278 - return false;
279 - // Get the page this corresponds to
280 - $title = $rev->getTitle();
281 -
282 - $quality = 0;
283 - if ( FlaggedRevs::isQuality($this->dims) ) {
284 - $quality = FlaggedRevs::getLCQuality($this->dims);
285 - $quality = ($quality > 1) ? $quality : 1;
286 - }
287 - // Our flags
288 - $flagset = array();
289 - foreach( $this->dims as $tag => $value ) {
290 - $flagset[] = array(
291 - 'frt_rev_id' => $rev->getId(),
292 - 'frt_dimension' => $tag,
293 - 'frt_value' => $value
294 - );
295 - }
296 -
297 - // XXX: hack, our template version pointers
298 - $tmpset = $templates = array();
299 - $templateMap = explode('#',trim($this->templateParams) );
300 - foreach( $templateMap as $template ) {
301 - if( !$template ) continue;
302 -
303 - $m = explode('|',$template,2);
304 - if( !isset($m[0]) || !isset($m[1]) || !$m[0] ) continue;
305 -
306 - list($prefixed_text,$rev_id) = $m;
307 -
308 - if( in_array($prefixed_text,$templates) ) continue; // No dups!
309 - $templates[] = $prefixed_text;
310 -
311 - $tmp_title = Title::newFromText( $prefixed_text ); // Normalize this to be sure...
312 - if( is_null($title) ) continue; // Page must exist!
313 -
314 - $tmpset[] = array(
315 - 'ft_rev_id' => $rev->getId(),
316 - 'ft_namespace' => $tmp_title->getNamespace(),
317 - 'ft_title' => $tmp_title->getDBKey(),
318 - 'ft_tmp_rev_id' => $rev_id
319 - );
320 - }
321 - // XXX: hack, our image version pointers
322 - $imgset = $images = array();
323 - $imageMap = explode('#',trim($this->imageParams) );
324 - foreach( $imageMap as $image ) {
325 - if( !$image ) continue;
326 - $m = explode('|',$image,2);
327 -
328 - if( !isset($m[0]) || !isset($m[1]) || !$m[0] ) continue;
329 -
330 - list($dbkey,$timestamp) = $m;
331 -
332 - if( in_array($dbkey,$images) ) continue; // No dups!
333 - $images[] = $dbkey;
334 -
335 - $img_title = Title::makeTitle( NS_IMAGE, $dbkey ); // Normalize this to be sure...
336 - if( is_null($img_title) ) continue; // Page must exist!
337 -
338 - $imgset[] = array(
339 - 'fi_rev_id' => $rev->getId(),
340 - 'fi_name' => $img_title->getDBKey(),
341 - 'fi_img_timestamp' => $timestamp
342 - );
343 - }
344 -
345 - $dbw = wfGetDB( DB_MASTER );
346 - $dbw->begin();
347 - // Update our versioning pointers
348 - if( !empty( $tmpset ) ) {
349 - $dbw->replace( 'flaggedtemplates', array( array('ft_rev_id','ft_namespace','ft_title') ), $tmpset,
350 - __METHOD__ );
351 - }
352 - if( !empty( $imgset ) ) {
353 - $dbw->replace( 'flaggedimages', array( array('fi_rev_id','fi_name') ), $imgset,
354 - __METHOD__ );
355 - }
356 - // Get the page text and resolve all templates
357 - list($fulltext,$complete) = FlaggedRevs::expandText( $rev->getText(), $rev->getTitle(), $rev->getId() );
358 - if( !$complete ) {
359 - $dbw->rollback(); // All versions must be specified, 0 for none
360 - return false;
361 - }
362 - // Our review entry
363 - $revset = array(
364 - 'fr_rev_id' => $rev->getId(),
365 - 'fr_namespace' => $title->getNamespace(),
366 - 'fr_title' => $title->getDBkey(),
367 - 'fr_user' => $wgUser->getId(),
368 - 'fr_timestamp' => wfTimestampNow(),
369 - 'fr_comment' => $notes,
370 - 'fr_text' => $fulltext, // Store expanded text for speed
371 - 'fr_quality' => $quality
372 - );
373 - // Update flagged revisions table
374 - $dbw->replace( 'flaggedrevs', array( array('fr_rev_id','fr_namespace','fr_title') ), $revset, __METHOD__ );
375 - // Set all of our flags
376 - $dbw->replace( 'flaggedrevtags', array( array('frt_rev_id','frt_dimension') ), $flagset, __METHOD__ );
377 - // Mark as patrolled
378 - $dbw->update( 'recentchanges',
379 - array( 'rc_patrolled' => 1 ),
380 - array( 'rc_this_oldid' => $rev->getId() ),
381 - __METHOD__
382 - );
383 - $dbw->commit();
384 -
385 - // Update the article review log
386 - $this->updateLog( $this->page, $this->dims, $this->comment, $this->oldid, true );
387 -
388 - $article = new Article( $this->page );
389 - // Update the links tables as the stable version may now be the default page...
390 - $parserCache =& ParserCache::singleton();
391 - $poutput = $parserCache->get( $article, $wgUser );
392 - if( $poutput==false ) {
393 - $text = $article->getContent();
394 - $poutput = $wgParser->parse($text, $article->mTitle, ParserOptions::newFromUser($wgUser));
395 - }
396 - $u = new LinksUpdate( $this->page, $poutput );
397 - $u->doUpdate(); // Will trigger our hook to add stable links too...
398 -
399 - # Clear the cache...
400 - $this->page->invalidateCache();
401 - # Might as well save the cache
402 - $parserCache->save( $poutput, $article, $wgUser );
403 - # Purge squid for this page only
404 - $this->page->purgeSquid();
405 -
406 - return true;
407 - }
408 -
409 - /**
410 - * @param Revision $rev
411 - * Removes flagged revision data for this page/id set
412 - */
413 - function unapproveRevision( $row=NULL ) {
414 - global $wgUser, $wgFlaggedRevsWatch;
415 -
416 - if( is_null($row) ) return false;
417 -
418 - $user = $wgUser->getId();
419 -
420 - wfProfileIn( __METHOD__ );
421 - $dbw = wfGetDB( DB_MASTER );
422 - // Delete from table
423 - $dbw->delete( 'flaggedrevs', array( 'fr_rev_id' => $row->fr_rev_id ) );
424 - // Wipe versioning pointers
425 - $dbw->delete( 'flaggedtemplates', array( 'ft_rev_id' => $row->fr_rev_id ) );
426 - $dbw->delete( 'flaggedimages', array( 'fi_rev_id' => $row->fr_rev_id ) );
427 - // And the flags...
428 - $dbw->delete( 'flaggedrevtags', array( 'frt_rev_id' => $row->fr_rev_id ) );
429 -
430 - // Update the article review log
431 - $this->updateLog( $this->page, $this->dims, $this->comment, $this->oldid, false );
432 -
433 - $article = new Article( $this->page );
434 - // Update the links tables as a new stable version
435 - // may now be the default page.
436 - $parserCache =& ParserCache::singleton();
437 - $poutput = $parserCache->get( $article, $wgUser );
438 - if ( $poutput==false ) {
439 - $text = $article->getContent();
440 - $poutput = $wgParser->parse($text, $article->mTitle, ParserOptions::newFromUser($wgUser));
441 - }
442 - $u = new LinksUpdate( $this->page, $poutput );
443 - $u->doUpdate();
444 -
445 - # Clear the cache...
446 - $this->page->invalidateCache();
447 - # Might as well save the cache
448 - $parserCache->save( $poutput, $article, $wgUser );
449 - # Purge squid for this page only
450 - $this->page->purgeSquid();
451 -
452 - wfProfileOut( __METHOD__ );
453 -
454 - return true;
455 - }
456 -
457 - /**
458 - * Record a log entry on the action
459 - * @param Title $title
460 - * @param array $dimensions
461 - * @param string $comment
462 - * @param int $revid
463 - * @param bool $approve
464 - */
465 - function updateLog( $title, $dimensions, $comment, $oldid, $approve ) {
466 - $log = new LogPage( 'review' );
467 - // ID, accuracy, depth, style
468 - $ratings = array();
469 - foreach( $dimensions as $quality => $level ) {
470 - $ratings[] = wfMsg( "revreview-$quality" ) . ": " . wfMsg("revreview-$quality-$level");
471 - }
472 - $rating = ($approve) ? ' [' . implode(', ',$ratings). ']' : '';
473 - // Append comment with action
474 - // FIXME: do this better
475 - $action = wfMsgExt('review-logaction', array('parsemag'), $oldid );
476 - if( $approve )
477 - $comment = ($comment) ? "$action: $comment$rating" : "$action $rating";
478 - else
479 - $comment = ($comment) ? "$action: $comment" : "$action";
480 -
481 - if( $approve ) {
482 - $log->addEntry( 'approve', $title, $comment );
483 - } else {
484 - $log->addEntry( 'unapprove', $title, $comment );
485 - }
486 - }
487 -}
488 -
489 -class Stableversions extends SpecialPage
490 -{
491 -
492 - function Stableversions() {
493 - SpecialPage::SpecialPage('Stableversions');
494 - }
495 -
496 - function execute( $par ) {
497 - global $wgRequest;
498 -
499 - $this->setHeaders();
500 - // Our target page
501 - $this->page = $wgRequest->getText( 'page' );
502 - // Revision ID
503 - $this->oldid = $wgRequest->getIntOrNull( 'oldid' );
504 -
505 - if( $this->oldid ) {
506 - $this->showStableRevision( $wgRequest );
507 - } else if( $this->page ) {
508 - $this->showStableList( $wgRequest );
509 - } else {
510 - $this->showForm( $wgRequest );
511 - }
512 - }
513 -
514 - function showForm( $wgRequest ) {
515 - global $wgOut, $wgTitle, $wgScript;
516 -
517 - $encPage = $this->page;
518 - $encId = $this->oldid;
519 -
520 - $form = "<form name='stableversions' action='$wgScript' method='get'>";
521 - $form .= "<fieldset><legend>".wfMsg('stableversions-leg1')."</legend>";
522 - $form .= "<table><tr>";
523 - $form .= "<td>".Xml::hidden( 'title', $wgTitle->getPrefixedText() )."</td>";
524 - $form .= "<td>".wfMsgHtml("stableversions-page").":</td>";
525 - $form .= "<td>".Xml::input('page', 50, $encPage, array( 'id' => 'page' ) )."</td>";
526 - $form .= "<td>".wfSubmitButton( wfMsgHtml( 'go' ) )."</td>";
527 - $form .= "</tr></table>";
528 - $form .= "</fieldset></form>\n";
529 -
530 - $form .= "<form name='stableversion' action='$wgScript' method='get'>";
531 - $form .= "<fieldset><legend>".wfMsg('stableversions-leg2')."</legend>";
532 - $form .= "<table><tr>";
533 - $form .= "<td>".Xml::hidden( 'title', $wgTitle->getPrefixedDBkey() )."</td>";
534 - $form .= "<td>".wfMsgHtml("stableversions-rev").":</td>";
535 - $form .= "<td>".Xml::input('oldid', 15, $encId, array( 'id' => 'oldid' ) )."</td>";
536 - $form .= "<td>".wfSubmitButton( wfMsgHtml( 'go' ) )."</td>";
537 - $form .= "</tr></table>";
538 - $form .= "</fieldset></form>";
539 -
540 - $wgOut->addHTML( $form );
541 - }
542 -
543 - function showStableRevision( $frev ) {
544 - global $wgParser, $wgLang, $wgUser, $wgOut, $wgTitle;
545 -
546 - // Get the revision
547 - $frev = FlaggedRevs::getFlaggedRev( $this->oldid );
548 - // Revision must exists
549 - if( is_null($frev) ) {
550 - $wgOut->showErrorPage( 'notargettitle', 'revnotfoundtext' );
551 - return;
552 - }
553 - $page = Title::makeTitle( $frev->fr_namespace, $frev->fr_title );
554 -
555 - $wgOut->setPagetitle( $page->getPrefixedText() );
556 -
557 - // Modifier instance
558 - $RevFlagging = new FlaggedRevs();
559 - // Get flags and date
560 - $flags = FlaggedRevs::getFlagsForPageRev( $frev->fr_rev_id );
561 - $time = $wgLang->timeanddate( wfTimestamp(TS_MW, $frev->fr_timestamp), true );
562 - // We will be looking at the reviewed revision...
563 - $tag = wfMsgExt('revreview-static', array('parseinline'), urlencode($page->getPrefixedText()), $time, $page->getPrefixedText());
564 - $tag .= ' <a id="mwrevisiontoggle" style="display:none;" href="javascript:toggleRevRatings()">' . wfMsg('revreview-toggle') . '</a>';
565 - $tag .= '<span id="mwrevisionratings" style="display:block;">' .
566 - wfMsg('revreview-oldrating') . $RevFlagging->addTagRatings( $flags ) .
567 - '</span>';
568 - // Parse the text...
569 - $text = $RevFlagging->getFlaggedRevText( $this->oldid );
570 - $options = ParserOptions::newFromUser($wgUser);
571 - $parserOutput = $RevFlagging->parseStableText( $page, $text, $this->oldid, $options );
572 - $notes = $RevFlagging->ReviewNotes( $frev );
573 - // Set the new body HTML, place a tag on top
574 - $wgOut->addHTML('<div id="mwrevisiontag" class="flaggedrevs_notice plainlinks">'.$tag.'</div>' . $parserOutput->getText() . $notes);
575 - // Show stable categories and interwiki links only
576 - $wgOut->mCategoryLinks = array();
577 - $wgOut->addCategoryLinks( $parserOutput->getCategories() );
578 - $wgOut->mLanguageLinks = array();
579 - $wgOut->addLanguageLinks( $parserOutput->getLanguageLinks() );
580 - }
581 -
582 - function showStableList() {
583 - global $wgOut, $wgUser, $wgLang;
584 -
585 - $skin = $wgUser->getSkin();
586 - // Must be a valid page/Id
587 - $page = Title::newFromUrl( $this->page );
588 - if( is_null($page) || !$page->isContentPage() ) {
589 - $wgOut->showErrorPage('notargettitle', 'allpagesbadtitle' );
590 - return;
591 - }
592 - $article = new Article( $page );
593 - if( !$article ) {
594 - $wgOut->showErrorPage('notargettitle', 'allpagesbadtitle' );
595 - return;
596 - }
597 - $pager = new StableRevisionsPager( $this, array(), $page->getNamespace(), $page->getDBkey() );
598 - if ( $pager->getNumRows() ) {
599 - $wgOut->addHTML( wfMsgExt('stableversions-list', array('parse'), $page->getPrefixedText() ) );
600 - $wgOut->addHTML( $pager->getNavigationBar() );
601 - $wgOut->addHTML( "<ul>" . $pager->getBody() . "</ul>" );
602 - $wgOut->addHTML( $pager->getNavigationBar() );
603 - } else {
604 - $wgOut->addHTML( wfMsgExt('stableversions-none', array('parse'), $page->getPrefixedText() ) );
605 - }
606 - }
607 -
608 - function formatRow( $row ) {
609 - global $wgLang, $wgUser;
610 -
611 - static $skin=null;
612 - if( is_null( $skin ) )
613 - $skin = $wgUser->getSkin();
614 -
615 - $SV = SpecialPage::getTitleFor( 'Stableversions' );
616 - $time = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->rev_timestamp), true );
617 - $ftime = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->fr_timestamp), true );
618 - $review = wfMsg( 'stableversions-review', $ftime );
619 -
620 - $lev = wfMsg('hist-stable');
621 - if( $row->fr_quality >=1 ) $lev = wfMsg('hist-quality');
622 -
623 - return '<li>'.$skin->makeKnownLinkObj( $SV, $time, 'oldid='.$row->fr_rev_id ).' ('.$review.') <b>'.$lev.'</b></li>';
624 - }
625 -}
626 -
627 -/**
628 - * Query to list out stable versions for a page
629 - */
630 -class StableRevisionsPager extends ReverseChronologicalPager {
631 - public $mForm, $mConds;
632 -
633 - function __construct( $form, $conds = array(), $namespace, $title ) {
634 - $this->mForm = $form;
635 - $this->mConds = $conds;
636 - $this->namespace = $namespace;
637 - $this->title = $title;
638 - parent::__construct();
639 - }
640 -
641 - function formatRow( $row ) {
642 - $block = new Block;
643 - return $this->mForm->formatRow( $row );
644 - }
645 -
646 - function getQueryInfo() {
647 - $conds = $this->mConds;
648 - $conds["fr_namespace"] = $this->namespace;
649 - $conds["fr_title"] = $this->title;
650 - $conds[] = "fr_rev_id = rev_id";
651 - $conds["rev_deleted"] = 0;
652 - return array(
653 - 'tables' => array('flaggedrevs','revision'),
654 - 'fields' => 'fr_rev_id,fr_timestamp,rev_timestamp,fr_quality',
655 - 'conds' => $conds
656 - );
657 - }
658 -
659 - function getIndexField() {
660 - return 'fr_rev_id';
661 - }
662 -}
663 -
664 -/**
665 - * Special page to list unreviewed pages
666 - */
667 -class Unreviewedpages extends SpecialPage
668 -{
669 -
670 - function Unreviewedpages() {
671 - SpecialPage::SpecialPage('Unreviewedpages');
672 - }
673 -
674 - function execute( $par ) {
675 - global $wgRequest;
676 -
677 - $this->setHeaders();
678 -
679 - $this->showList( $wgRequest );
680 - }
681 -
682 - function showList( $wgRequest ) {
683 - global $wgOut, $wgUser, $wgScript, $wgTitle;
684 -
685 - $skin = $wgUser->getSkin();
686 - $namespace = $wgRequest->getIntOrNull( 'namespace' );
687 - $nonquality = $wgRequest->getVal( 'includenonquality' );
688 -
689 - $action = htmlspecialchars( $wgScript );
690 - $wgOut->addHTML( "<form action=\"$action\" method=\"get\">\n" .
691 - '<fieldset><legend>' . wfMsg('viewunreviewed') . '</legend>' .
692 - $this->getNamespaceMenu( $namespace ) . "\n" .
693 - Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
694 - '<p>' . Xml::check( 'includenonquality', $nonquality, array('id' => 'includenonquality') ) .
695 - ' ' . Xml::label( wfMsgHtml("included-nonquality"), 'includenonquality' ) . "</p>\n" .
696 - Xml::hidden( 'title', $wgTitle->getPrefixedText() ) .
697 - "</fieldset></form>");
698 -
699 - list( $limit, $offset ) = wfCheckLimits();
700 -
701 - $sdr = new UnreviewedPagesPage( $namespace, $nonquality );
702 - $sdr->doQuery( $offset, $limit );
703 - }
704 -
705 - function getNamespaceMenu( $selected=NULL, $allnamespaces = null, $includehidden=false ) {
706 - global $wgContLang, $wgContentNamespaces;
707 -
708 - $selector = "<label for='namespace'>" . wfMsgHtml('namespace') . "</label>";
709 - if( $selected !== '' ) {
710 - if( is_null( $selected ) ) {
711 - // No namespace selected; let exact match work without hitting Main
712 - $selected = '';
713 - } else {
714 - // Let input be numeric strings without breaking the empty match.
715 - $selected = intval( $selected );
716 - }
717 - }
718 - $s = "\n<select id='namespace' name='namespace' class='namespaceselector'>\n";
719 - $arr = $wgContLang->getFormattedNamespaces();
720 - if( !is_null($allnamespaces) ) {
721 - $arr = array($allnamespaces => wfMsg('namespacesall')) + $arr;
722 - }
723 -
724 - $s .= "\t" . Xml::element("option", array("value" => "all"), "all") . "\n";
725 -
726 - foreach ($arr as $index => $name) {
727 - # Content only
728 - if ($index < NS_MAIN || !in_array($index, $wgContentNamespaces) ) continue;
729 -
730 - $name = $index !== 0 ? $name : wfMsg('blanknamespace');
731 -
732 - if ($index === $selected) {
733 - $s .= "\t" . Xml::element("option",
734 - array("value" => $index, "selected" => "selected"),
735 - $name) . "\n";
736 - } else {
737 - $s .= "\t" . Xml::element("option", array("value" => $index), $name) . "\n";
738 - }
739 - }
740 - $s .= "</select>\n";
741 - return $s;
742 - }
743 -}
744 -
745 -/**
746 - * Query to list out unreviewed pages
747 - */
748 -class UnreviewedPagesPage extends PageQueryPage {
749 -
750 - function __construct( $namespace=NULL, $nonquality=false ) {
751 - $this->namespace = $namespace;
752 - $this->nonquality = $nonquality;
753 - }
754 -
755 - function getName() {
756 - return 'UnreviewedPages';
757 - }
758 -
759 - function isExpensive( ) { return true; }
760 - function isSyndicated() { return false; }
761 -
762 - function getPageHeader( ) {
763 - return '<p>'.wfMsg("unreviewed-list")."</p>\n";
764 - }
765 -
766 - function getSQLText( &$dbr, $namespace, $includenonquality = false ) {
767 - global $wgContentNamespaces;
768 -
769 - list( $page, $flaggedrevs ) = $dbr->tableNamesN( 'page', 'flaggedrevs' );
770 -
771 - # Must be a content page...
772 - $contentNS = 'page_namespace IN(' . implode(',',$wgContentNamespaces) . ')';
773 -
774 - $ns = ($namespace !== null) ? "page_namespace=$namespace" : '1 = 1';
775 -
776 - $where = $includenonquality ? '1 = 1' : 'fr_rev_id IS NULL';
777 - $having = $includenonquality ? 'MAX(fr_quality) < 1' : '1 = 1';
778 -
779 - $sql =
780 - "SELECT page_namespace,page_title,page_len AS size
781 - FROM $page
782 - LEFT JOIN $flaggedrevs ON (fr_namespace = page_namespace AND fr_title = page_title)
783 - WHERE page_is_redirect=0 AND $ns AND $contentNS AND ($where)
784 - GROUP BY page_id HAVING $having ";
785 - return $sql;
786 - }
787 -
788 - function getSQL() {
789 - $dbr = wfGetDB( DB_SLAVE );
790 - return $this->getSQLText( $dbr, $this->namespace, $this->nonquality );
791 - }
792 -
793 - function getOrder() {
794 - return 'ORDER BY page_id DESC';
795 - }
796 -
797 - function formatResult( $skin, $result ) {
798 - global $wgLang;
799 -
800 - $fname = 'UnreviewedPagesPage::formatResult';
801 - $title = Title::makeTitle( $result->page_namespace, $result->page_title );
802 - $link = $skin->makeKnownLinkObj( $title );
803 - $stxt = '';
804 - if (!is_null($size = $result->size)) {
805 - if ($size == 0)
806 - $stxt = ' <small>' . wfMsgHtml('historyempty') . '</small>';
807 - else
808 - $stxt = ' <small>' . wfMsgHtml('historysize', $wgLang->formatNum( $size ) ) . '</small>';
809 - }
810 -
811 - return( "{$link} {$stxt}" );
812 - }
813 -}
814 -
Index: trunk/extensions/FlaggedRevs/FlaggedRevsPage_body.php
@@ -0,0 +1,813 @@
 2+<?php
 3+
 4+#(c) Joerg Baach, Aaron Schulz, 2007 GPL
 5+
 6+global $IP;
 7+require_once( "$IP/includes/LogPage.php" );
 8+require_once( "$IP/includes/SpecialLog.php" );
 9+
 10+class Revisionreview extends SpecialPage
 11+{
 12+
 13+ function Revisionreview() {
 14+ SpecialPage::SpecialPage('Revisionreview', 'review');
 15+ }
 16+
 17+ function execute( $par ) {
 18+ global $wgRequest, $wgUser, $wgOut, $wgFlaggedRevComments, $wgFlaggedRevTags;
 19+
 20+ $confirm = $wgRequest->wasPosted() &&
 21+ $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
 22+
 23+ if( $wgUser->isAllowed( 'review' ) ) {
 24+ if( $wgUser->isBlocked( !$confirm ) ) {
 25+ $wgOut->blockedPage();
 26+ return;
 27+ }
 28+ } else {
 29+ $wgOut->permissionRequired( 'review' );
 30+ return;
 31+ }
 32+
 33+ $this->setHeaders();
 34+ // Our target page
 35+ $this->target = $wgRequest->getText( 'target' );
 36+ // Revision ID
 37+ $this->oldid = $wgRequest->getIntOrNull( 'oldid' );
 38+ // Must be a valid content page
 39+ $this->page = Title::newFromUrl( $this->target );
 40+ if ( !$this->target || !$this->oldid || !$this->page->isContentPage() ) {
 41+ $wgOut->addHTML( wfMsgExt('revreview-main',array('parse')) );
 42+ return;
 43+ }
 44+ if( is_null($this->page) || is_null($this->oldid) ) {
 45+ $wgOut->showErrorPage('notargettitle', 'notargettext' );
 46+ return;
 47+ }
 48+ // Check if page is protected
 49+ if( !$this->page->quickUserCan( 'edit' ) ) {
 50+ $wgOut->permissionRequired( 'badaccess-group0' );
 51+ return;
 52+ }
 53+ // Special parameter mapping
 54+ $this->templateParams = $wgRequest->getVal( 'templateParams' );
 55+ $this->imageParams = $wgRequest->getVal( 'imageParams' );
 56+ // Log comment
 57+ $this->comment = $wgRequest->getText( 'wpReason' );
 58+ // Additional notes
 59+ $this->notes = ($wgFlaggedRevComments) ? $wgRequest->getText('wpNotes') : '';
 60+ // Get the revision's current flags, if any
 61+ $this->oflags = FlaggedRevs::getFlagsForPageRev( $this->oldid );
 62+ // Get our accuracy/quality dimensions
 63+ $this->dims = array();
 64+ $this->upprovedTags = 0;
 65+ foreach ( array_keys($wgFlaggedRevTags) as $tag ) {
 66+ $this->dims[$tag] = $wgRequest->getIntOrNull( "wp$tag" );
 67+ // Must be greater than zero
 68+ if ( $this->dims[$tag] < 0 ) {
 69+ $wgOut->showErrorPage('notargettitle', 'notargettext' );
 70+ return;
 71+ }
 72+ if ( $this->dims[$tag]==0 )
 73+ $this->upprovedTags++;
 74+ // Check permissions
 75+ if( !$this->userCan( $tag, $this->oflags[$tag] ) ) {
 76+ # Users can't take away a status they can't set
 77+ $wgOut->permissionRequired( 'badaccess-group0' );
 78+ return;
 79+ } else if( !$this->userCan( $tag, $this->dims[$tag] ) ) {
 80+ // Users cannot review to beyond their rights level
 81+ $wgOut->permissionRequired( 'badaccess-group0' );
 82+ return;
 83+ }
 84+ }
 85+ // We must at least rate each category as 1, the minimum
 86+ // Exception: we can rate ALL as unapproved to depreciate a revision
 87+ $this->isValid = true;
 88+ if ( $this->upprovedTags && ($this->upprovedTags < count($wgFlaggedRevTags) || !$this->oflags) )
 89+ $this->isValid = false;
 90+
 91+ if( $this->isValid && $wgRequest->wasPosted() ) {
 92+ $this->submit( $wgRequest );
 93+ } else {
 94+ $this->showRevision( $wgRequest );
 95+ }
 96+ }
 97+
 98+ /**
 99+ * @param string $tag
 100+ * @param int $val
 101+ * Returns true if a user can do something
 102+ */
 103+ public static function userCan( $tag, $value ) {
 104+ global $wgFlagRestrictions, $wgUser;
 105+
 106+ if ( !isset($wgFlagRestrictions[$tag]) )
 107+ return true;
 108+ // Validators always have full access
 109+ if ( $wgUser->isAllowed('validate') )
 110+ return true;
 111+ // Check if this user has any right that lets him/her set
 112+ // up to this particular value
 113+ foreach ( $wgFlagRestrictions[$tag] as $right => $level ) {
 114+ if ( $value <= $level && $wgUser->isAllowed($right) ) {
 115+ return true;
 116+ }
 117+ }
 118+ return false;
 119+ }
 120+
 121+ /**
 122+ * @param webrequest $request
 123+ */
 124+ function showRevision( $request ) {
 125+ global $wgOut, $wgUser, $wgTitle, $wgFlaggedRevComments, $wgFlaggedRevsOverride,
 126+ $wgFlaggedRevTags, $wgFlaggedRevValues;
 127+
 128+ if ( !$this->isValid )
 129+ $wgOut->addWikiText( '<strong>' . wfMsg( 'revreview-toolow' ) . '</strong>' );
 130+
 131+ $wgOut->addWikiText( wfMsg( 'revreview-selected', $this->page->getPrefixedText() ) );
 132+
 133+ $this->skin = $wgUser->getSkin();
 134+ $rev = Revision::newFromTitle( $this->page, $this->oldid );
 135+ // Check if rev exists
 136+ // Do not mess with deleted revisions
 137+ if( !isset( $rev ) || $rev->mDeleted ) {
 138+ $wgOut->showErrorPage( 'internalerror', 'notargettitle', 'notargettext' );
 139+ return;
 140+ }
 141+
 142+ $wgOut->addHtml( "<ul>" );
 143+ $wgOut->addHtml( $this->historyLine( $rev ) );
 144+ $wgOut->addHtml( "</ul>" );
 145+
 146+ if( $wgFlaggedRevsOverride )
 147+ $wgOut->addWikiText( wfMsg('revreview-text') );
 148+
 149+ $formradios = array();
 150+ // Dynamically contruct our radio options
 151+ foreach ( array_keys($wgFlaggedRevTags) as $tag ) {
 152+ $formradios[$tag] = array();
 153+ for ($i=0; $i <= $wgFlaggedRevValues; $i++) {
 154+ $formradios[$tag][] = array( "revreview-$tag-$i", "wp$tag", $i );
 155+ }
 156+ }
 157+ $items = array(
 158+ wfInputLabel( wfMsgHtml( 'revreview-log' ), 'wpReason', 'wpReason', 60 ),
 159+ wfSubmitButton( wfMsgHtml( 'revreview-submit' ) ) );
 160+ $hidden = array(
 161+ wfHidden( 'wpEditToken', $wgUser->editToken() ),
 162+ wfHidden( 'target', $this->page->getPrefixedText() ),
 163+ wfHidden( 'oldid', $this->oldid ) );
 164+
 165+ $action = $wgTitle->escapeLocalUrl( 'action=submit' );
 166+ $form = "<form name='revisionreview' action='$action' method='post'>";
 167+ $form .= '<fieldset><legend>' . wfMsgHtml( 'revreview-legend' ) . '</legend><table><tr>';
 168+ // Dynamically contruct our review types
 169+ foreach ( array_keys($wgFlaggedRevTags) as $tag ) {
 170+ $form .= '<td><strong>' . wfMsgHtml( "revreview-$tag" ) . '</strong></td><td width=\'20\'></td>';
 171+ }
 172+ $form .= '</tr><tr>';
 173+ foreach ( $formradios as $set => $ratioset ) {
 174+ $form .= '<td>';
 175+ foreach( $ratioset as $item ) {
 176+ list( $message, $name, $field ) = $item;
 177+ // Don't give options the user can't set unless its the status quo
 178+ $attribs = array('id' => $name.$field);
 179+ if( !$this->userCan($set,$field) )
 180+ $attribs['disabled'] = 'true';
 181+ $form .= "<div>";
 182+ $form .= Xml::radio( $name, $field, ($field==$this->dims[$set]), $attribs );
 183+ $form .= Xml::label( wfMsg($message), $name.$field );
 184+ $form .= "</div>\n";
 185+ }
 186+ $form .= '</td><td width=\'20\'></td>';
 187+ }
 188+ $form .= '</tr></table></fieldset>';
 189+ // Add box to add live notes to a flagged revision
 190+ if ( $wgFlaggedRevComments ) {
 191+ $form .= "<fieldset><legend>" . wfMsgHtml( 'revreview-notes' ) . "</legend>" .
 192+ "<textarea tabindex='1' name='wpNotes' id='wpNotes' rows='3' cols='80' style='width:100%'>$this->notes</textarea>" .
 193+ "</fieldset>";
 194+ }
 195+
 196+ foreach( $items as $item ) {
 197+ $form .= '<p>' . $item . '</p>';
 198+ }
 199+ foreach( $hidden as $item ) {
 200+ $form .= $item;
 201+ }
 202+ // XXX: hack, versioning params
 203+ $form .= Xml::hidden( 'templateParams', $this->templateParams );
 204+ $form .= Xml::hidden( 'imageParams', $this->imageParams );
 205+
 206+ $form .= '</form>';
 207+ $wgOut->addHtml( $form );
 208+ }
 209+
 210+ /**
 211+ * @param Revision $rev
 212+ * @returns string
 213+ */
 214+ function historyLine( $rev ) {
 215+ global $wgContLang;
 216+ $date = $wgContLang->timeanddate( $rev->getTimestamp() );
 217+
 218+ $difflink = '(' . $this->skin->makeKnownLinkObj( $this->page, wfMsgHtml('diff'),
 219+ '&diff=' . $rev->getId() . '&oldid=prev' ) . ')';
 220+
 221+ $revlink = $this->skin->makeLinkObj( $this->page, $date, 'oldid=' . $rev->getId() );
 222+
 223+ return
 224+ "<li> $difflink $revlink " . $this->skin->revUserLink( $rev ) . " " . $this->skin->revComment( $rev ) . "</li>";
 225+ }
 226+
 227+ function submit( $request ) {
 228+ global $wgOut, $wgUser;
 229+
 230+ $approved = false;
 231+ # If all values are set to zero, this has been unapproved
 232+ foreach( $this->dims as $quality => $value ) {
 233+ if( $value ) {
 234+ $approved = true;
 235+ break;
 236+ }
 237+ }
 238+ // We can only approve actual revisions...
 239+ if ( $approved ) {
 240+ $rev = Revision::newFromTitle( $this->page, $this->oldid );
 241+ // Do not mess with archived/deleted revisions
 242+ if ( is_null($rev) || $rev->mDeleted ) {
 243+ $wgOut->showErrorPage( 'internalerror', 'revnotfoundtext' );
 244+ return;
 245+ }
 246+ } else {
 247+ $frev = FlaggedRevs::getFlaggedRev( $this->oldid );
 248+ // If we can't find this flagged rev, return to page???
 249+ if ( is_null($frev) ) {
 250+ $wgOut->redirect( $this->page->escapeLocalUrl() );
 251+ return;
 252+ }
 253+ }
 254+
 255+ $success = $approved ? $this->approveRevision( $rev, $this->notes ) : $this->unapproveRevision( $frev );
 256+
 257+ // Return to our page
 258+ if ( $success ) {
 259+ if( $request->getCheck( 'wpWatchthis' ) ) {
 260+ $wgUser->addWatch( $this->page );
 261+ } else {
 262+ $wgUser->removeWatch( $this->page );
 263+ }
 264+ $wgOut->redirect( $this->page->escapeLocalUrl() );
 265+ } else {
 266+ $wgOut->showErrorPage( 'internalerror', 'revreview-changed' );
 267+ }
 268+ }
 269+
 270+ /**
 271+ * @param Revision $rev
 272+ * Adds or updates the flagged revision table for this page/id set
 273+ */
 274+ function approveRevision( $rev=NULL, $notes='' ) {
 275+ global $wgUser, $wgFlaggedRevsWatch, $wgParser;
 276+
 277+ if( is_null($rev) )
 278+ return false;
 279+ // Get the page this corresponds to
 280+ $title = $rev->getTitle();
 281+
 282+ $quality = 0;
 283+ if ( FlaggedRevs::isQuality($this->dims) ) {
 284+ $quality = FlaggedRevs::getLCQuality($this->dims);
 285+ $quality = ($quality > 1) ? $quality : 1;
 286+ }
 287+ // Our flags
 288+ $flagset = array();
 289+ foreach( $this->dims as $tag => $value ) {
 290+ $flagset[] = array(
 291+ 'frt_rev_id' => $rev->getId(),
 292+ 'frt_dimension' => $tag,
 293+ 'frt_value' => $value
 294+ );
 295+ }
 296+
 297+ // XXX: hack, our template version pointers
 298+ $tmpset = $templates = array();
 299+ $templateMap = explode('#',trim($this->templateParams) );
 300+ foreach( $templateMap as $template ) {
 301+ if( !$template ) continue;
 302+
 303+ $m = explode('|',$template,2);
 304+ if( !isset($m[0]) || !isset($m[1]) || !$m[0] ) continue;
 305+
 306+ list($prefixed_text,$rev_id) = $m;
 307+
 308+ if( in_array($prefixed_text,$templates) ) continue; // No dups!
 309+ $templates[] = $prefixed_text;
 310+
 311+ $tmp_title = Title::newFromText( $prefixed_text ); // Normalize this to be sure...
 312+ if( is_null($title) ) continue; // Page must exist!
 313+
 314+ $tmpset[] = array(
 315+ 'ft_rev_id' => $rev->getId(),
 316+ 'ft_namespace' => $tmp_title->getNamespace(),
 317+ 'ft_title' => $tmp_title->getDBKey(),
 318+ 'ft_tmp_rev_id' => $rev_id
 319+ );
 320+ }
 321+ // XXX: hack, our image version pointers
 322+ $imgset = $images = array();
 323+ $imageMap = explode('#',trim($this->imageParams) );
 324+ foreach( $imageMap as $image ) {
 325+ if( !$image ) continue;
 326+ $m = explode('|',$image,2);
 327+
 328+ if( !isset($m[0]) || !isset($m[1]) || !$m[0] ) continue;
 329+
 330+ list($dbkey,$timestamp) = $m;
 331+
 332+ if( in_array($dbkey,$images) ) continue; // No dups!
 333+ $images[] = $dbkey;
 334+
 335+ $img_title = Title::makeTitle( NS_IMAGE, $dbkey ); // Normalize this to be sure...
 336+ if( is_null($img_title) ) continue; // Page must exist!
 337+
 338+ $imgset[] = array(
 339+ 'fi_rev_id' => $rev->getId(),
 340+ 'fi_name' => $img_title->getDBKey(),
 341+ 'fi_img_timestamp' => $timestamp
 342+ );
 343+ }
 344+
 345+ $dbw = wfGetDB( DB_MASTER );
 346+ $dbw->begin();
 347+ // Update our versioning pointers
 348+ if( !empty( $tmpset ) ) {
 349+ $dbw->replace( 'flaggedtemplates', array( array('ft_rev_id','ft_namespace','ft_title') ), $tmpset,
 350+ __METHOD__ );
 351+ }
 352+ if( !empty( $imgset ) ) {
 353+ $dbw->replace( 'flaggedimages', array( array('fi_rev_id','fi_name') ), $imgset,
 354+ __METHOD__ );
 355+ }
 356+ // Get the page text and resolve all templates
 357+ list($fulltext,$complete) = FlaggedRevs::expandText( $rev->getText(), $rev->getTitle(), $rev->getId() );
 358+ if( !$complete ) {
 359+ $dbw->rollback(); // All versions must be specified, 0 for none
 360+ return false;
 361+ }
 362+ // Our review entry
 363+ $revset = array(
 364+ 'fr_rev_id' => $rev->getId(),
 365+ 'fr_namespace' => $title->getNamespace(),
 366+ 'fr_title' => $title->getDBkey(),
 367+ 'fr_user' => $wgUser->getId(),
 368+ 'fr_timestamp' => wfTimestampNow(),
 369+ 'fr_comment' => $notes,
 370+ 'fr_text' => $fulltext, // Store expanded text for speed
 371+ 'fr_quality' => $quality
 372+ );
 373+ // Update flagged revisions table
 374+ $dbw->replace( 'flaggedrevs', array( array('fr_rev_id','fr_namespace','fr_title') ), $revset, __METHOD__ );
 375+ // Set all of our flags
 376+ $dbw->replace( 'flaggedrevtags', array( array('frt_rev_id','frt_dimension') ), $flagset, __METHOD__ );
 377+ // Mark as patrolled
 378+ $dbw->update( 'recentchanges',
 379+ array( 'rc_patrolled' => 1 ),
 380+ array( 'rc_this_oldid' => $rev->getId() ),
 381+ __METHOD__
 382+ );
 383+ $dbw->commit();
 384+
 385+ // Update the article review log
 386+ $this->updateLog( $this->page, $this->dims, $this->comment, $this->oldid, true );
 387+
 388+ $article = new Article( $this->page );
 389+ // Update the links tables as the stable version may now be the default page...
 390+ $parserCache =& ParserCache::singleton();
 391+ $poutput = $parserCache->get( $article, $wgUser );
 392+ if( $poutput==false ) {
 393+ $text = $article->getContent();
 394+ $poutput = $wgParser->parse($text, $article->mTitle, ParserOptions::newFromUser($wgUser));
 395+ }
 396+ $u = new LinksUpdate( $this->page, $poutput );
 397+ $u->doUpdate(); // Will trigger our hook to add stable links too...
 398+
 399+ # Clear the cache...
 400+ $this->page->invalidateCache();
 401+ # Might as well save the cache
 402+ $parserCache->save( $poutput, $article, $wgUser );
 403+ # Purge squid for this page only
 404+ $this->page->purgeSquid();
 405+
 406+ return true;
 407+ }
 408+
 409+ /**
 410+ * @param Revision $rev
 411+ * Removes flagged revision data for this page/id set
 412+ */
 413+ function unapproveRevision( $row=NULL ) {
 414+ global $wgUser, $wgFlaggedRevsWatch;
 415+
 416+ if( is_null($row) ) return false;
 417+
 418+ $user = $wgUser->getId();
 419+
 420+ wfProfileIn( __METHOD__ );
 421+ $dbw = wfGetDB( DB_MASTER );
 422+ // Delete from table
 423+ $dbw->delete( 'flaggedrevs', array( 'fr_rev_id' => $row->fr_rev_id ) );
 424+ // Wipe versioning pointers
 425+ $dbw->delete( 'flaggedtemplates', array( 'ft_rev_id' => $row->fr_rev_id ) );
 426+ $dbw->delete( 'flaggedimages', array( 'fi_rev_id' => $row->fr_rev_id ) );
 427+ // And the flags...
 428+ $dbw->delete( 'flaggedrevtags', array( 'frt_rev_id' => $row->fr_rev_id ) );
 429+
 430+ // Update the article review log
 431+ $this->updateLog( $this->page, $this->dims, $this->comment, $this->oldid, false );
 432+
 433+ $article = new Article( $this->page );
 434+ // Update the links tables as a new stable version
 435+ // may now be the default page.
 436+ $parserCache =& ParserCache::singleton();
 437+ $poutput = $parserCache->get( $article, $wgUser );
 438+ if ( $poutput==false ) {
 439+ $text = $article->getContent();
 440+ $poutput = $wgParser->parse($text, $article->mTitle, ParserOptions::newFromUser($wgUser));
 441+ }
 442+ $u = new LinksUpdate( $this->page, $poutput );
 443+ $u->doUpdate();
 444+
 445+ # Clear the cache...
 446+ $this->page->invalidateCache();
 447+ # Might as well save the cache
 448+ $parserCache->save( $poutput, $article, $wgUser );
 449+ # Purge squid for this page only
 450+ $this->page->purgeSquid();
 451+
 452+ wfProfileOut( __METHOD__ );
 453+
 454+ return true;
 455+ }
 456+
 457+ /**
 458+ * Record a log entry on the action
 459+ * @param Title $title
 460+ * @param array $dimensions
 461+ * @param string $comment
 462+ * @param int $revid
 463+ * @param bool $approve
 464+ */
 465+ function updateLog( $title, $dimensions, $comment, $oldid, $approve ) {
 466+ $log = new LogPage( 'review' );
 467+ // ID, accuracy, depth, style
 468+ $ratings = array();
 469+ foreach( $dimensions as $quality => $level ) {
 470+ $ratings[] = wfMsg( "revreview-$quality" ) . ": " . wfMsg("revreview-$quality-$level");
 471+ }
 472+ $rating = ($approve) ? ' [' . implode(', ',$ratings). ']' : '';
 473+ // Append comment with action
 474+ // FIXME: do this better
 475+ $action = wfMsgExt('review-logaction', array('parsemag'), $oldid );
 476+ if( $approve )
 477+ $comment = ($comment) ? "$action: $comment$rating" : "$action $rating";
 478+ else
 479+ $comment = ($comment) ? "$action: $comment" : "$action";
 480+
 481+ if( $approve ) {
 482+ $log->addEntry( 'approve', $title, $comment );
 483+ } else {
 484+ $log->addEntry( 'unapprove', $title, $comment );
 485+ }
 486+ }
 487+}
 488+
 489+class Stableversions extends SpecialPage
 490+{
 491+
 492+ function Stableversions() {
 493+ SpecialPage::SpecialPage('Stableversions');
 494+ }
 495+
 496+ function execute( $par ) {
 497+ global $wgRequest;
 498+
 499+ $this->setHeaders();
 500+ // Our target page
 501+ $this->page = $wgRequest->getText( 'page' );
 502+ // Revision ID
 503+ $this->oldid = $wgRequest->getIntOrNull( 'oldid' );
 504+
 505+ if( $this->oldid ) {
 506+ $this->showStableRevision( $wgRequest );
 507+ } else if( $this->page ) {
 508+ $this->showStableList( $wgRequest );
 509+ } else {
 510+ $this->showForm( $wgRequest );
 511+ }
 512+ }
 513+
 514+ function showForm( $wgRequest ) {
 515+ global $wgOut, $wgTitle, $wgScript;
 516+
 517+ $encPage = $this->page;
 518+ $encId = $this->oldid;
 519+
 520+ $form = "<form name='stableversions' action='$wgScript' method='get'>";
 521+ $form .= "<fieldset><legend>".wfMsg('stableversions-leg1')."</legend>";
 522+ $form .= "<table><tr>";
 523+ $form .= "<td>".Xml::hidden( 'title', $wgTitle->getPrefixedText() )."</td>";
 524+ $form .= "<td>".wfMsgHtml("stableversions-page").":</td>";
 525+ $form .= "<td>".Xml::input('page', 50, $encPage, array( 'id' => 'page' ) )."</td>";
 526+ $form .= "<td>".wfSubmitButton( wfMsgHtml( 'go' ) )."</td>";
 527+ $form .= "</tr></table>";
 528+ $form .= "</fieldset></form>\n";
 529+
 530+ $form .= "<form name='stableversion' action='$wgScript' method='get'>";
 531+ $form .= "<fieldset><legend>".wfMsg('stableversions-leg2')."</legend>";
 532+ $form .= "<table><tr>";
 533+ $form .= "<td>".Xml::hidden( 'title', $wgTitle->getPrefixedDBkey() )."</td>";
 534+ $form .= "<td>".wfMsgHtml("stableversions-rev").":</td>";
 535+ $form .= "<td>".Xml::input('oldid', 15, $encId, array( 'id' => 'oldid' ) )."</td>";
 536+ $form .= "<td>".wfSubmitButton( wfMsgHtml( 'go' ) )."</td>";
 537+ $form .= "</tr></table>";
 538+ $form .= "</fieldset></form>";
 539+
 540+ $wgOut->addHTML( $form );
 541+ }
 542+
 543+ function showStableRevision( $frev ) {
 544+ global $wgParser, $wgLang, $wgUser, $wgOut, $wgTitle;
 545+
 546+ // Get the revision
 547+ $frev = FlaggedRevs::getFlaggedRev( $this->oldid );
 548+ // Revision must exists
 549+ if( is_null($frev) ) {
 550+ $wgOut->showErrorPage( 'notargettitle', 'revnotfoundtext' );
 551+ return;
 552+ }
 553+ $page = Title::makeTitle( $frev->fr_namespace, $frev->fr_title );
 554+
 555+ $wgOut->setPagetitle( $page->getPrefixedText() );
 556+
 557+ // Modifier instance
 558+ $RevFlagging = new FlaggedRevs();
 559+ // Get flags and date
 560+ $flags = FlaggedRevs::getFlagsForPageRev( $frev->fr_rev_id );
 561+ $time = $wgLang->timeanddate( wfTimestamp(TS_MW, $frev->fr_timestamp), true );
 562+ // We will be looking at the reviewed revision...
 563+ $tag = wfMsgExt('revreview-static', array('parseinline'), urlencode($page->getPrefixedText()), $time, $page->getPrefixedText());
 564+ $tag .= ' <a id="mwrevisiontoggle" style="display:none;" href="javascript:toggleRevRatings()">' . wfMsg('revreview-toggle') . '</a>';
 565+ $tag .= '<span id="mwrevisionratings" style="display:block;">' .
 566+ wfMsg('revreview-oldrating') . $RevFlagging->addTagRatings( $flags ) .
 567+ '</span>';
 568+ // Parse the text...
 569+ $text = $RevFlagging->getFlaggedRevText( $this->oldid );
 570+ $options = ParserOptions::newFromUser($wgUser);
 571+ $parserOutput = $RevFlagging->parseStableText( $page, $text, $this->oldid, $options );
 572+ $notes = $RevFlagging->ReviewNotes( $frev );
 573+ // Set the new body HTML, place a tag on top
 574+ $wgOut->addHTML('<div id="mwrevisiontag" class="flaggedrevs_notice plainlinks">'.$tag.'</div>' . $parserOutput->getText() . $notes);
 575+ // Show stable categories and interwiki links only
 576+ $wgOut->mCategoryLinks = array();
 577+ $wgOut->addCategoryLinks( $parserOutput->getCategories() );
 578+ $wgOut->mLanguageLinks = array();
 579+ $wgOut->addLanguageLinks( $parserOutput->getLanguageLinks() );
 580+ }
 581+
 582+ function showStableList() {
 583+ global $wgOut, $wgUser, $wgLang;
 584+
 585+ $skin = $wgUser->getSkin();
 586+ // Must be a valid page/Id
 587+ $page = Title::newFromUrl( $this->page );
 588+ if( is_null($page) || !$page->isContentPage() ) {
 589+ $wgOut->showErrorPage('notargettitle', 'allpagesbadtitle' );
 590+ return;
 591+ }
 592+ $article = new Article( $page );
 593+ if( !$article ) {
 594+ $wgOut->showErrorPage('notargettitle', 'allpagesbadtitle' );
 595+ return;
 596+ }
 597+ $pager = new StableRevisionsPager( $this, array(), $page->getNamespace(), $page->getDBkey() );
 598+ if ( $pager->getNumRows() ) {
 599+ $wgOut->addHTML( wfMsgExt('stableversions-list', array('parse'), $page->getPrefixedText() ) );
 600+ $wgOut->addHTML( $pager->getNavigationBar() );
 601+ $wgOut->addHTML( "<ul>" . $pager->getBody() . "</ul>" );
 602+ $wgOut->addHTML( $pager->getNavigationBar() );
 603+ } else {
 604+ $wgOut->addHTML( wfMsgExt('stableversions-none', array('parse'), $page->getPrefixedText() ) );
 605+ }
 606+ }
 607+
 608+ function formatRow( $row ) {
 609+ global $wgLang, $wgUser;
 610+
 611+ static $skin=null;
 612+ if( is_null( $skin ) )
 613+ $skin = $wgUser->getSkin();
 614+
 615+ $SV = SpecialPage::getTitleFor( 'Stableversions' );
 616+ $time = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->rev_timestamp), true );
 617+ $ftime = $wgLang->timeanddate( wfTimestamp(TS_MW, $row->fr_timestamp), true );
 618+ $review = wfMsg( 'stableversions-review', $ftime );
 619+
 620+ $lev = wfMsg('hist-stable');
 621+ if( $row->fr_quality >=1 ) $lev = wfMsg('hist-quality');
 622+
 623+ return '<li>'.$skin->makeKnownLinkObj( $SV, $time, 'oldid='.$row->fr_rev_id ).' ('.$review.') <b>'.$lev.'</b></li>';
 624+ }
 625+}
 626+
 627+/**
 628+ * Query to list out stable versions for a page
 629+ */
 630+class StableRevisionsPager extends ReverseChronologicalPager {
 631+ public $mForm, $mConds;
 632+
 633+ function __construct( $form, $conds = array(), $namespace, $title ) {
 634+ $this->mForm = $form;
 635+ $this->mConds = $conds;
 636+ $this->namespace = $namespace;
 637+ $this->title = $title;
 638+ parent::__construct();
 639+ }
 640+
 641+ function formatRow( $row ) {
 642+ $block = new Block;
 643+ return $this->mForm->formatRow( $row );
 644+ }
 645+
 646+ function getQueryInfo() {
 647+ $conds = $this->mConds;
 648+ $conds["fr_namespace"] = $this->namespace;
 649+ $conds["fr_title"] = $this->title;
 650+ $conds[] = "fr_rev_id = rev_id";
 651+ $conds["rev_deleted"] = 0;
 652+ return array(
 653+ 'tables' => array('flaggedrevs','revision'),
 654+ 'fields' => 'fr_rev_id,fr_timestamp,rev_timestamp,fr_quality',
 655+ 'conds' => $conds
 656+ );
 657+ }
 658+
 659+ function getIndexField() {
 660+ return 'fr_rev_id';
 661+ }
 662+}
 663+
 664+/**
 665+ * Special page to list unreviewed pages
 666+ */
 667+class Unreviewedpages extends SpecialPage
 668+{
 669+
 670+ function Unreviewedpages() {
 671+ SpecialPage::SpecialPage('Unreviewedpages');
 672+ }
 673+
 674+ function execute( $par ) {
 675+ global $wgRequest;
 676+
 677+ $this->setHeaders();
 678+
 679+ $this->showList( $wgRequest );
 680+ }
 681+
 682+ function showList( $wgRequest ) {
 683+ global $wgOut, $wgUser, $wgScript, $wgTitle;
 684+
 685+ $skin = $wgUser->getSkin();
 686+ $namespace = $wgRequest->getIntOrNull( 'namespace' );
 687+ $nonquality = $wgRequest->getVal( 'includenonquality' );
 688+
 689+ $action = htmlspecialchars( $wgScript );
 690+ $wgOut->addHTML( "<form action=\"$action\" method=\"get\">\n" .
 691+ '<fieldset><legend>' . wfMsg('viewunreviewed') . '</legend>' .
 692+ $this->getNamespaceMenu( $namespace ) . "\n" .
 693+ Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" .
 694+ '<p>' . Xml::check( 'includenonquality', $nonquality, array('id' => 'includenonquality') ) .
 695+ ' ' . Xml::label( wfMsgHtml("included-nonquality"), 'includenonquality' ) . "</p>\n" .
 696+ Xml::hidden( 'title', $wgTitle->getPrefixedText() ) .
 697+ "</fieldset></form>");
 698+
 699+ list( $limit, $offset ) = wfCheckLimits();
 700+
 701+ $sdr = new UnreviewedPagesPage( $namespace, $nonquality );
 702+ $sdr->doQuery( $offset, $limit );
 703+ }
 704+
 705+ function getNamespaceMenu( $selected=NULL, $allnamespaces = null, $includehidden=false ) {
 706+ global $wgContLang, $wgContentNamespaces;
 707+
 708+ $selector = "<label for='namespace'>" . wfMsgHtml('namespace') . "</label>";
 709+ if( $selected !== '' ) {
 710+ if( is_null( $selected ) ) {
 711+ // No namespace selected; let exact match work without hitting Main
 712+ $selected = '';
 713+ } else {
 714+ // Let input be numeric strings without breaking the empty match.
 715+ $selected = intval( $selected );
 716+ }
 717+ }
 718+ $s = "\n<select id='namespace' name='namespace' class='namespaceselector'>\n";
 719+ $arr = $wgContLang->getFormattedNamespaces();
 720+ if( !is_null($allnamespaces) ) {
 721+ $arr = array($allnamespaces => wfMsg('namespacesall')) + $arr;
 722+ }
 723+
 724+ $s .= "\t" . Xml::element("option", array("value" => "all"), "all") . "\n";
 725+
 726+ foreach ($arr as $index => $name) {
 727+ # Content only
 728+ if ($index < NS_MAIN || !in_array($index, $wgContentNamespaces) ) continue;
 729+
 730+ $name = $index !== 0 ? $name : wfMsg('blanknamespace');
 731+
 732+ if ($index === $selected) {
 733+ $s .= "\t" . Xml::element("option",
 734+ array("value" => $index, "selected" => "selected"),
 735+ $name) . "\n";
 736+ } else {
 737+ $s .= "\t" . Xml::element("option", array("value" => $index), $name) . "\n";
 738+ }
 739+ }
 740+ $s .= "</select>\n";
 741+ return $s;
 742+ }
 743+}
 744+
 745+/**
 746+ * Query to list out unreviewed pages
 747+ */
 748+class UnreviewedPagesPage extends PageQueryPage {
 749+
 750+ function __construct( $namespace=NULL, $nonquality=false ) {
 751+ $this->namespace = $namespace;
 752+ $this->nonquality = $nonquality;
 753+ }
 754+
 755+ function getName() {
 756+ return 'UnreviewedPages';
 757+ }
 758+
 759+ function isExpensive( ) { return true; }
 760+ function isSyndicated() { return false; }
 761+
 762+ function getPageHeader( ) {
 763+ return '<p>'.wfMsg("unreviewed-list")."</p>\n";
 764+ }
 765+
 766+ function getSQLText( &$dbr, $namespace, $includenonquality = false ) {
 767+ global $wgContentNamespaces;
 768+
 769+ list( $page, $flaggedrevs ) = $dbr->tableNamesN( 'page', 'flaggedrevs' );
 770+
 771+ # Must be a content page...
 772+ $contentNS = 'page_namespace IN(' . implode(',',$wgContentNamespaces) . ')';
 773+
 774+ $ns = ($namespace !== null) ? "page_namespace=$namespace" : '1 = 1';
 775+
 776+ $where = $includenonquality ? '1 = 1' : 'fr_rev_id IS NULL';
 777+ $having = $includenonquality ? 'MAX(fr_quality) < 1' : '1 = 1';
 778+
 779+ $sql =
 780+ "SELECT page_namespace,page_title,page_len AS size
 781+ FROM $page
 782+ LEFT JOIN $flaggedrevs ON (fr_namespace = page_namespace AND fr_title = page_title)
 783+ WHERE page_is_redirect=0 AND $ns AND $contentNS AND ($where)
 784+ GROUP BY page_id HAVING $having ";
 785+ return $sql;
 786+ }
 787+
 788+ function getSQL() {
 789+ $dbr = wfGetDB( DB_SLAVE );
 790+ return $this->getSQLText( $dbr, $this->namespace, $this->nonquality );
 791+ }
 792+
 793+ function getOrder() {
 794+ return 'ORDER BY page_id DESC';
 795+ }
 796+
 797+ function formatResult( $skin, $result ) {
 798+ global $wgLang;
 799+
 800+ $fname = 'UnreviewedPagesPage::formatResult';
 801+ $title = Title::makeTitle( $result->page_namespace, $result->page_title );
 802+ $link = $skin->makeKnownLinkObj( $title );
 803+ $stxt = '';
 804+ if (!is_null($size = $result->size)) {
 805+ if ($size == 0)
 806+ $stxt = ' <small>' . wfMsgHtml('historyempty') . '</small>';
 807+ else
 808+ $stxt = ' <small>' . wfMsgHtml('historysize', $wgLang->formatNum( $size ) ) . '</small>';
 809+ }
 810+
 811+ return( "{$link} {$stxt}" );
 812+ }
 813+}
 814+

Status & tagging log