Index: trunk/extensions/FlaggedRevs/business/RevisionReviewForm.php |
— | — | @@ -0,0 +1,638 @@ |
| 2 | +<?php |
| 3 | +# (c) Aaron Schulz 2010 GPL |
| 4 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 5 | + echo "FlaggedRevs extension\n"; |
| 6 | + exit( 1 ); |
| 7 | +} |
| 8 | +/** |
| 9 | + * Class containing revision review form business logic |
| 10 | + * Note: edit tokens are the responsibility of caller |
| 11 | + * Usage: (a) set ALL form params before doing anything else |
| 12 | + * (b) call ready() when all params are set |
| 13 | + * (c) call submit() as needed |
| 14 | + */ |
| 15 | +class RevisionReviewForm |
| 16 | +{ |
| 17 | + /* Form parameters which can be user given */ |
| 18 | + protected $page = null; |
| 19 | + protected $approve = false; |
| 20 | + protected $unapprove = false; |
| 21 | + protected $reject = false; |
| 22 | + protected $oldid = 0; |
| 23 | + protected $refid = 0; |
| 24 | + protected $templateParams = ''; |
| 25 | + protected $imageParams = ''; |
| 26 | + protected $fileVersion = ''; |
| 27 | + protected $validatedParams = ''; |
| 28 | + protected $comment = ''; |
| 29 | + protected $dims = array(); |
| 30 | + protected $lastChangeTime = null; # Conflict handling |
| 31 | + protected $newLastChangeTime = null; # Conflict handling |
| 32 | + |
| 33 | + protected $oflags = array(); |
| 34 | + protected $inputLock = 0; # Disallow bad submissions |
| 35 | + |
| 36 | + protected $user = null; |
| 37 | + |
| 38 | + public function __construct( User $user ) { |
| 39 | + $this->user = $user; |
| 40 | + foreach ( FlaggedRevs::getTags() as $tag ) { |
| 41 | + $this->dims[$tag] = 0; |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | + public function getUser() { |
| 46 | + return $this->user; |
| 47 | + } |
| 48 | + |
| 49 | + public function getPage() { |
| 50 | + return $this->page; |
| 51 | + } |
| 52 | + |
| 53 | + public function setPage( Title $value ) { |
| 54 | + $this->trySet( $this->page, $value ); |
| 55 | + } |
| 56 | + |
| 57 | + public function setApprove( $value ) { |
| 58 | + $this->trySet( $this->approve, $value ); |
| 59 | + } |
| 60 | + |
| 61 | + public function setUnapprove( $value ) { |
| 62 | + $this->trySet( $this->unapprove, $value ); |
| 63 | + } |
| 64 | + |
| 65 | + public function setReject( $value ) { |
| 66 | + $this->trySet( $this->reject, $value ); |
| 67 | + } |
| 68 | + |
| 69 | + public function setLastChangeTime( $value ) { |
| 70 | + $this->trySet( $this->lastChangeTime, $value ); |
| 71 | + } |
| 72 | + |
| 73 | + public function getNewLastChangeTime() { |
| 74 | + return $this->newLastChangeTime; |
| 75 | + } |
| 76 | + |
| 77 | + public function getRefId() { |
| 78 | + return $this->refid; |
| 79 | + } |
| 80 | + |
| 81 | + public function setRefId( $value ) { |
| 82 | + $this->trySet( $this->refid, (int)$value ); |
| 83 | + } |
| 84 | + |
| 85 | + public function getOldId() { |
| 86 | + return $this->oldid; |
| 87 | + } |
| 88 | + |
| 89 | + public function setOldId( $value ) { |
| 90 | + $this->trySet( $this->oldid, (int)$value ); |
| 91 | + } |
| 92 | + |
| 93 | + public function getTemplateParams() { |
| 94 | + return $this->templateParams; |
| 95 | + } |
| 96 | + |
| 97 | + public function setTemplateParams( $value ) { |
| 98 | + $this->trySet( $this->templateParams, $value ); |
| 99 | + } |
| 100 | + |
| 101 | + public function getFileParams() { |
| 102 | + return $this->imageParams; |
| 103 | + } |
| 104 | + |
| 105 | + public function setFileParams( $value ) { |
| 106 | + $this->trySet( $this->imageParams, $value ); |
| 107 | + } |
| 108 | + |
| 109 | + public function getFileVersion() { |
| 110 | + return $this->fileVersion; |
| 111 | + } |
| 112 | + |
| 113 | + public function setFileVersion( $value ) { |
| 114 | + $this->trySet( $this->fileVersion, $value ); |
| 115 | + } |
| 116 | + |
| 117 | + public function getValidatedParams() { |
| 118 | + return $this->validatedParams; |
| 119 | + } |
| 120 | + |
| 121 | + public function setValidatedParams( $value ) { |
| 122 | + $this->trySet( $this->validatedParams, $value ); |
| 123 | + } |
| 124 | + |
| 125 | + public function getComment() { |
| 126 | + return $this->comment; |
| 127 | + } |
| 128 | + |
| 129 | + public function setComment( $value ) { |
| 130 | + $this->trySet( $this->comment, $value ); |
| 131 | + } |
| 132 | + |
| 133 | + public function getDims() { |
| 134 | + return $this->dims; |
| 135 | + } |
| 136 | + |
| 137 | + public function setDim( $tag, $value ) { |
| 138 | + if ( !in_array( $tag, FlaggedRevs::getTags() ) ) { |
| 139 | + throw new MWException( "FlaggedRevs tag $tag does not exist.\n" ); |
| 140 | + } |
| 141 | + $this->trySet( $this->dims[$tag], (int)$value ); |
| 142 | + } |
| 143 | + |
| 144 | + /** |
| 145 | + * Set a member field to a value if the fields are unlocked |
| 146 | + */ |
| 147 | + protected function trySet( &$field, $value ) { |
| 148 | + if ( $this->inputLock ) { |
| 149 | + throw new MWException( __CLASS__ . " fields cannot be set anymore.\n"); |
| 150 | + } else { |
| 151 | + $field = $value; // still allowing input |
| 152 | + } |
| 153 | + } |
| 154 | + |
| 155 | + /** |
| 156 | + * Signal that inputs are starting |
| 157 | + */ |
| 158 | + public function start() { |
| 159 | + $this->inputLock = 0; |
| 160 | + } |
| 161 | + |
| 162 | + /** |
| 163 | + * Signal that inputs are done and load old config |
| 164 | + * @return mixed (true on success, error string on target failure) |
| 165 | + */ |
| 166 | + public function ready() { |
| 167 | + $this->inputLock = 1; |
| 168 | + $status = $this->checkTarget(); |
| 169 | + if ( $status !== true ) { |
| 170 | + return $status; // bad target |
| 171 | + } |
| 172 | + # Get the revision's current flags, if any |
| 173 | + $this->oflags = FlaggedRevs::getRevisionTags( $this->page, $this->oldid, FR_MASTER ); |
| 174 | + # Set initial value for newLastChangeTime (if unchanged on submit) |
| 175 | + $this->newLastChangeTime = $this->lastChangeTime; |
| 176 | + return $status; |
| 177 | + } |
| 178 | + |
| 179 | + /* |
| 180 | + * Check that the target page is valid |
| 181 | + * @return mixed (true on success, error string on failure) |
| 182 | + */ |
| 183 | + protected function checkTarget() { |
| 184 | + if ( is_null( $this->page ) ) { |
| 185 | + return 'review_page_invalid'; |
| 186 | + } elseif ( !$this->page->exists() ) { |
| 187 | + return 'review_page_notexists'; |
| 188 | + } |
| 189 | + $fa = FlaggedArticle::getTitleInstance( $this->page ); |
| 190 | + if ( !$fa->isReviewable() ) { |
| 191 | + return 'review_page_unreviewable'; |
| 192 | + } |
| 193 | + return true; |
| 194 | + } |
| 195 | + |
| 196 | + /* |
| 197 | + * Verify and clean up parameters (e.g. from POST request). |
| 198 | + * @return mixed (true on success, error string on failure) |
| 199 | + */ |
| 200 | + protected function checkSettings() { |
| 201 | + $status = $this->checkTarget(); |
| 202 | + if ( $status !== true ) { |
| 203 | + return $status; // bad page target |
| 204 | + } elseif ( !$this->oldid ) { |
| 205 | + return 'review_no_oldid'; // bad revision target |
| 206 | + } |
| 207 | + # Check that an action is specified (approve, reject, de-approve) |
| 208 | + if ( $this->getAction() === null ) { |
| 209 | + return 'review_param_missing'; // user didn't say |
| 210 | + } |
| 211 | + # Fill in implicit tag data for binary flag case |
| 212 | + $iDims = $this->implicitDims(); |
| 213 | + if ( $iDims ) { |
| 214 | + $this->dims = $iDims; // binary flag case |
| 215 | + } |
| 216 | + if ( $this->getAction() === 'approve' ) { |
| 217 | + # We must at least rate each category as 1, the minimum |
| 218 | + if ( in_array( 0, $this->dims, true ) ) { |
| 219 | + return 'review_too_low'; |
| 220 | + } |
| 221 | + # Special token to discourage fiddling with template/files... |
| 222 | + $k = self::validationKey( |
| 223 | + $this->templateParams, $this->imageParams, $this->fileVersion, $this->oldid ); |
| 224 | + if ( $this->validatedParams !== $k ) { |
| 225 | + return 'review_bad_key'; |
| 226 | + } |
| 227 | + } |
| 228 | + # Check permissions and validate |
| 229 | + # FIXME: invalid vs denied |
| 230 | + if ( !FlaggedRevs::userCanSetFlags( $this->user, $this->dims, $this->oflags ) ) { |
| 231 | + return 'review_denied'; |
| 232 | + } |
| 233 | + return true; |
| 234 | + } |
| 235 | + |
| 236 | + public function isAllowed() { |
| 237 | + // Basic permission check |
| 238 | + return ( $this->page |
| 239 | + && $this->page->userCan( 'review' ) |
| 240 | + && $this->page->userCan( 'edit' ) |
| 241 | + ); |
| 242 | + } |
| 243 | + |
| 244 | + // implicit dims for binary flag case |
| 245 | + private function implicitDims() { |
| 246 | + $tag = FlaggedRevs::binaryTagName(); |
| 247 | + if ( $tag ) { |
| 248 | + if ( $this->approve ) { |
| 249 | + return array( $tag => 1 ); |
| 250 | + } else if ( $this->unapprove ) { |
| 251 | + return array( $tag => 0 ); |
| 252 | + } |
| 253 | + } |
| 254 | + return null; |
| 255 | + } |
| 256 | + |
| 257 | + /* |
| 258 | + * What are we doing? |
| 259 | + * @return string (approve,unapprove,reject) |
| 260 | + */ |
| 261 | + public function getAction() { |
| 262 | + if ( !$this->reject && !$this->unapprove && $this->approve ) { |
| 263 | + return 'approve'; |
| 264 | + } elseif ( !$this->reject && $this->unapprove && !$this->approve ) { |
| 265 | + return 'unapprove'; |
| 266 | + } elseif ( $this->reject && !$this->unapprove && !$this->approve ) { |
| 267 | + return 'reject'; |
| 268 | + } |
| 269 | + return null; // nothing valid asserted |
| 270 | + } |
| 271 | + |
| 272 | + /** |
| 273 | + * Submit the form parameters for the page config to the DB. |
| 274 | + * |
| 275 | + * @return mixed (true on success, error string on failure) |
| 276 | + */ |
| 277 | + public function submit() { |
| 278 | + if ( !$this->inputLock ) { |
| 279 | + throw new MWException( __CLASS__ . " input fields not set yet.\n"); |
| 280 | + } |
| 281 | + $status = $this->checkSettings(); |
| 282 | + if ( $status !== true ) { |
| 283 | + return $status; // cannot submit - broken params |
| 284 | + } |
| 285 | + # Double-check permissions |
| 286 | + if ( !$this->isAllowed() ) { |
| 287 | + return 'review_denied'; |
| 288 | + } |
| 289 | + # We can only approve actual revisions... |
| 290 | + if ( $this->getAction() === 'approve' ) { |
| 291 | + $rev = Revision::newFromTitle( $this->page, $this->oldid ); |
| 292 | + # Check for archived/deleted revisions... |
| 293 | + if ( !$rev || $rev->mDeleted ) { |
| 294 | + return 'review_bad_oldid'; |
| 295 | + } |
| 296 | + $oldFrev = FlaggedRevision::newFromTitle( $this->page, $this->oldid, FR_MASTER ); |
| 297 | + # Check for review conflicts... |
| 298 | + if ( $this->lastChangeTime !== null ) { // API uses null |
| 299 | + $lastChange = $oldFrev ? $oldFrev->getTimestamp() : ''; |
| 300 | + if ( $lastChange !== $this->lastChangeTime ) { |
| 301 | + return 'review_conflict_oldid'; |
| 302 | + } |
| 303 | + } |
| 304 | + $status = $this->approveRevision( $rev, $oldFrev ); |
| 305 | + # We can only unapprove approved revisions... |
| 306 | + } elseif ( $this->getAction() === 'unapprove' ) { |
| 307 | + $oldFrev = FlaggedRevision::newFromTitle( $this->page, $this->oldid, FR_MASTER ); |
| 308 | + # Check for review conflicts... |
| 309 | + if ( $this->lastChangeTime !== null ) { // API uses null |
| 310 | + $lastChange = $oldFrev ? $oldFrev->getTimestamp() : ''; |
| 311 | + if ( $lastChange !== $this->lastChangeTime ) { |
| 312 | + return 'review_conflict_oldid'; |
| 313 | + } |
| 314 | + } |
| 315 | + # Check if we can find this flagged rev... |
| 316 | + if ( !$oldFrev ) { |
| 317 | + return 'review_not_flagged'; |
| 318 | + } |
| 319 | + $status = $this->unapproveRevision( $oldFrev ); |
| 320 | + } elseif ( $this->getAction() === 'reject' ) { |
| 321 | + $newRev = Revision::newFromTitle( $this->page, $this->oldid ); |
| 322 | + $oldRev = Revision::newFromTitle( $this->page, $this->refid ); |
| 323 | + # Do not mess with archived/deleted revisions |
| 324 | + if ( !$oldRev || $oldRev->isDeleted( Revision::DELETED_TEXT ) ) { |
| 325 | + return 'review_bad_oldid'; |
| 326 | + } elseif ( !$newRev || $newRev->isDeleted( Revision::DELETED_TEXT ) ) { |
| 327 | + return 'review_bad_oldid'; |
| 328 | + } |
| 329 | + $srev = FlaggedRevision::newFromStable( $this->page, FR_MASTER ); |
| 330 | + if ( $srev && $srev->getRevId() > $oldRev->getId() ) { |
| 331 | + return 'review_cannot_reject'; // not really a use case |
| 332 | + } |
| 333 | + $article = new Article( $this->page ); |
| 334 | + $new_text = $article->getUndoText( $newRev, $oldRev ); |
| 335 | + if ( $new_text === false ) { |
| 336 | + return 'review_cannot_undo'; |
| 337 | + } |
| 338 | + $baseRevId = $newRev->isCurrent() ? $oldRev->getId() : 0; |
| 339 | + $article->doEdit( $new_text, $this->getComment(), 0, $baseRevId, $this->user ); |
| 340 | + } |
| 341 | + # Watch page if set to do so |
| 342 | + if ( $status === true ) { |
| 343 | + if ( $this->user->getOption( 'flaggedrevswatch' ) |
| 344 | + && !$this->page->userIsWatching() ) |
| 345 | + { |
| 346 | + $this->user->addWatch( $this->page ); |
| 347 | + } |
| 348 | + } |
| 349 | + return $status; |
| 350 | + } |
| 351 | + |
| 352 | + /** |
| 353 | + * Adds or updates the flagged revision table for this page/id set |
| 354 | + * @param Revision $rev The revision to be accepted |
| 355 | + * @param FlaggedRevision $oldFrev Currently accepted version of $rev or null |
| 356 | + * @returns true on success, array of errors on failure |
| 357 | + */ |
| 358 | + private function approveRevision( Revision $rev, FlaggedRevision $oldFrev = null ) { |
| 359 | + wfProfileIn( __METHOD__ ); |
| 360 | + # Revision rating flags |
| 361 | + $flags = $this->dims; |
| 362 | + $quality = 0; // quality tier from flags |
| 363 | + if ( FlaggedRevs::isQuality( $flags ) ) { |
| 364 | + $quality = FlaggedRevs::isPristine( $flags ) ? 2 : 1; |
| 365 | + } |
| 366 | + # Our template/file version pointers |
| 367 | + list( $tmpVersions, $fileVersions ) = self::getIncludeVersions( |
| 368 | + $this->templateParams, $this->imageParams |
| 369 | + ); |
| 370 | + # If this is an image page, store corresponding file info |
| 371 | + $fileData = array( 'name' => null, 'timestamp' => null, 'sha1' => null ); |
| 372 | + if ( $this->page->getNamespace() == NS_FILE && $this->fileVersion ) { |
| 373 | + # Stable upload version for file pages... |
| 374 | + $data = explode( '#', $this->fileVersion, 2 ); |
| 375 | + if ( count( $data ) == 2 ) { |
| 376 | + $fileData['name'] = $this->page->getDBkey(); |
| 377 | + $fileData['timestamp'] = $data[0]; |
| 378 | + $fileData['sha1'] = $data[1]; |
| 379 | + } |
| 380 | + } |
| 381 | + |
| 382 | + # Get current stable version ID (for logging) |
| 383 | + $oldSv = FlaggedRevision::newFromStable( $this->page, FR_MASTER ); |
| 384 | + |
| 385 | + # Is this a duplicate review? |
| 386 | + if ( $oldFrev && |
| 387 | + $oldFrev->getTags() == $flags && // tags => quality |
| 388 | + $oldFrev->getFileSha1() == $fileData['sha1'] && |
| 389 | + $oldFrev->getFileTimestamp() == $fileData['timestamp'] && |
| 390 | + $oldFrev->getTemplateVersions( FR_MASTER ) == $tmpVersions && |
| 391 | + $oldFrev->getFileVersions( FR_MASTER ) == $fileVersions ) |
| 392 | + { |
| 393 | + wfProfileOut( __METHOD__ ); |
| 394 | + return true; // don't record if the same |
| 395 | + } |
| 396 | + |
| 397 | + # Insert the review entry... |
| 398 | + $flaggedRevision = new FlaggedRevision( array( |
| 399 | + 'rev_id' => $rev->getId(), |
| 400 | + 'page_id' => $rev->getPage(), |
| 401 | + 'user' => $this->user->getId(), |
| 402 | + 'timestamp' => wfTimestampNow(), |
| 403 | + 'quality' => $quality, |
| 404 | + 'tags' => FlaggedRevision::flattenRevisionTags( $flags ), |
| 405 | + 'img_name' => $fileData['name'], |
| 406 | + 'img_timestamp' => $fileData['timestamp'], |
| 407 | + 'img_sha1' => $fileData['sha1'], |
| 408 | + 'templateVersions' => $tmpVersions, |
| 409 | + 'fileVersions' => $fileVersions, |
| 410 | + 'flags' => '' |
| 411 | + ) ); |
| 412 | + $flaggedRevision->insertOn(); |
| 413 | + # Update recent changes... |
| 414 | + $rcId = $rev->isUnpatrolled(); // int |
| 415 | + self::updateRecentChanges( $this->page, $rev->getId(), $rcId, true ); |
| 416 | + |
| 417 | + # Update the article review log... |
| 418 | + $oldSvId = $oldSv ? $oldSv->getRevId() : 0; |
| 419 | + FlaggedRevsLogs::updateReviewLog( $this->page, $this->dims, $this->oflags, |
| 420 | + $this->comment, $this->oldid, $oldSvId, true ); |
| 421 | + |
| 422 | + # Get the new stable version as of now |
| 423 | + $sv = FlaggedRevision::determineStable( $this->page, FR_MASTER/*consistent*/ ); |
| 424 | + # Update page and tracking tables and clear cache |
| 425 | + $changed = FlaggedRevs::stableVersionUpdates( $this->page, $sv, $oldSv ); |
| 426 | + if ( $changed ) { |
| 427 | + FlaggedRevs::HTMLCacheUpdates( $this->page ); // purge pages that use this page |
| 428 | + } |
| 429 | + |
| 430 | + # Caller may want to get the change time |
| 431 | + $this->newLastChangeTime = $flaggedRevision->getTimestamp(); |
| 432 | + |
| 433 | + wfProfileOut( __METHOD__ ); |
| 434 | + return true; |
| 435 | + } |
| 436 | + |
| 437 | + /** |
| 438 | + * @param FlaggedRevision $frev |
| 439 | + * Removes flagged revision data for this page/id set |
| 440 | + */ |
| 441 | + private function unapproveRevision( FlaggedRevision $frev ) { |
| 442 | + wfProfileIn( __METHOD__ ); |
| 443 | + |
| 444 | + # Get current stable version ID (for logging) |
| 445 | + $oldSv = FlaggedRevision::newFromStable( $this->page, FR_MASTER ); |
| 446 | + |
| 447 | + $dbw = wfGetDB( DB_MASTER ); |
| 448 | + # Delete from flaggedrevs table |
| 449 | + $dbw->delete( 'flaggedrevs', |
| 450 | + array( 'fr_page_id' => $frev->getPage(), 'fr_rev_id' => $frev->getRevId() ) ); |
| 451 | + # Wipe versioning params |
| 452 | + $dbw->delete( 'flaggedtemplates', array( 'ft_rev_id' => $frev->getRevId() ) ); |
| 453 | + $dbw->delete( 'flaggedimages', array( 'fi_rev_id' => $frev->getRevId() ) ); |
| 454 | + # Update recent changes |
| 455 | + self::updateRecentChanges( $this->page, $frev->getRevId(), false, false ); |
| 456 | + |
| 457 | + # Update the article review log |
| 458 | + $oldSvId = $oldSv ? $oldSv->getRevId() : 0; |
| 459 | + FlaggedRevsLogs::updateReviewLog( $this->page, $this->dims, $this->oflags, |
| 460 | + $this->comment, $this->oldid, $oldSvId, false ); |
| 461 | + |
| 462 | + # Get the new stable version as of now |
| 463 | + $sv = FlaggedRevision::determineStable( $this->page, FR_MASTER/*consistent*/ ); |
| 464 | + # Update page and tracking tables and clear cache |
| 465 | + $changed = FlaggedRevs::stableVersionUpdates( $this->page, $sv, $oldSv ); |
| 466 | + if ( $changed ) { |
| 467 | + FlaggedRevs::HTMLCacheUpdates( $this->page ); // purge pages that use this page |
| 468 | + } |
| 469 | + |
| 470 | + # Caller may want to get the change time |
| 471 | + $this->newLastChangeTime = ''; |
| 472 | + |
| 473 | + wfProfileOut( __METHOD__ ); |
| 474 | + return true; |
| 475 | + } |
| 476 | + |
| 477 | + /** |
| 478 | + * Get a validation key from versioning metadata |
| 479 | + * @param string $tmpP |
| 480 | + * @param string $imgP |
| 481 | + * @param string $imgV |
| 482 | + * @param integer $rid rev ID |
| 483 | + * @return string |
| 484 | + */ |
| 485 | + public static function validationKey( $tmpP, $imgP, $imgV, $rid ) { |
| 486 | + global $wgSecretKey, $wgProxyKey; |
| 487 | + $key = $wgSecretKey ? $wgSecretKey : $wgProxyKey; // fall back to $wgProxyKey |
| 488 | + $p = md5( $key . $imgP . $tmpP . $rid . $imgV ); |
| 489 | + return $p; |
| 490 | + } |
| 491 | + |
| 492 | + public static function updateRecentChanges( |
| 493 | + Title $title, $revId, $rcId = false, $patrol = true |
| 494 | + ) { |
| 495 | + wfProfileIn( __METHOD__ ); |
| 496 | + $revId = intval( $revId ); |
| 497 | + $dbw = wfGetDB( DB_MASTER ); |
| 498 | + # Olders edits be marked as patrolled now... |
| 499 | + $dbw->update( 'recentchanges', |
| 500 | + array( 'rc_patrolled' => $patrol ? 1 : 0 ), |
| 501 | + array( 'rc_cur_id' => $title->getArticleId(), |
| 502 | + $patrol ? "rc_this_oldid <= $revId" : "rc_this_oldid = $revId" ), |
| 503 | + __METHOD__, |
| 504 | + // Performance |
| 505 | + array( 'USE INDEX' => 'rc_cur_id', 'LIMIT' => 50 ) |
| 506 | + ); |
| 507 | + # New page patrol may be enabled. If so, the rc_id may be the first |
| 508 | + # edit and not this one. If it is different, mark it too. |
| 509 | + if ( $rcId && $rcId != $revId ) { |
| 510 | + $dbw->update( 'recentchanges', |
| 511 | + array( 'rc_patrolled' => 1 ), |
| 512 | + array( 'rc_id' => $rcId, |
| 513 | + 'rc_type' => RC_NEW ), |
| 514 | + __METHOD__ |
| 515 | + ); |
| 516 | + } |
| 517 | + wfProfileOut( __METHOD__ ); |
| 518 | + } |
| 519 | + |
| 520 | + /** |
| 521 | + * Get template and image parameters from parser output to use on forms. |
| 522 | + * @param FlaggedArticle $article |
| 523 | + * @param array $templateIDs (from ParserOutput/OutputPage->mTemplateIds) |
| 524 | + * @param array $imageSHA1Keys (from ParserOutput/OutputPage->mImageTimeKeys) |
| 525 | + * @returns array( templateParams, imageParams, fileVersion ) |
| 526 | + */ |
| 527 | + public static function getIncludeParams( |
| 528 | + FlaggedArticle $article, array $templateIDs, array $imageSHA1Keys |
| 529 | + ) { |
| 530 | + $templateParams = $imageParams = $fileVersion = ''; |
| 531 | + # NS -> title -> rev ID mapping |
| 532 | + foreach ( $templateIDs as $namespace => $t ) { |
| 533 | + foreach ( $t as $dbKey => $revId ) { |
| 534 | + $temptitle = Title::makeTitle( $namespace, $dbKey ); |
| 535 | + $templateParams .= $temptitle->getPrefixedDBKey() . "|" . $revId . "#"; |
| 536 | + } |
| 537 | + } |
| 538 | + # Image -> timestamp -> sha1 mapping |
| 539 | + foreach ( $imageSHA1Keys as $dbKey => $timeAndSHA1 ) { |
| 540 | + $imageParams .= $dbKey . "|" . $timeAndSHA1['time'] . "|" . $timeAndSHA1['sha1'] . "#"; |
| 541 | + } |
| 542 | + # For image pages, note the displayed image version |
| 543 | + if ( $article->getTitle()->getNamespace() == NS_FILE ) { |
| 544 | + $file = $article->getDisplayedFile(); // File obj |
| 545 | + if ( $file ) { |
| 546 | + $fileVersion = $file->getTimestamp() . "#" . $file->getSha1(); |
| 547 | + } |
| 548 | + } |
| 549 | + return array( $templateParams, $imageParams, $fileVersion ); |
| 550 | + } |
| 551 | + |
| 552 | + /** |
| 553 | + * Get template and image versions from form value for parser output. |
| 554 | + * @param string $templateParams |
| 555 | + * @param string $imageParams |
| 556 | + * @returns array( templateIds, fileSHA1Keys ) |
| 557 | + * templateIds like ParserOutput->mTemplateIds |
| 558 | + * fileSHA1Keys like ParserOutput->mImageTimeKeys |
| 559 | + */ |
| 560 | + public static function getIncludeVersions( $templateParams, $imageParams ) { |
| 561 | + $templateIds = array(); |
| 562 | + $templateMap = explode( '#', trim( $templateParams ) ); |
| 563 | + foreach ( $templateMap as $template ) { |
| 564 | + if ( !$template ) { |
| 565 | + continue; |
| 566 | + } |
| 567 | + $m = explode( '|', $template, 2 ); |
| 568 | + if ( !isset( $m[0] ) || !isset( $m[1] ) || !$m[0] ) { |
| 569 | + continue; |
| 570 | + } |
| 571 | + list( $prefixed_text, $rev_id ) = $m; |
| 572 | + # Get the template title |
| 573 | + $tmp_title = Title::newFromText( $prefixed_text ); // Normalize this to be sure... |
| 574 | + if ( is_null( $tmp_title ) ) { |
| 575 | + continue; // Page must be valid! |
| 576 | + } |
| 577 | + if ( !isset( $templateIds[$tmp_title->getNamespace()] ) ) { |
| 578 | + $templateIds[$tmp_title->getNamespace()] = array(); |
| 579 | + } |
| 580 | + $templateIds[$tmp_title->getNamespace()][$tmp_title->getDBkey()] = $rev_id; |
| 581 | + } |
| 582 | + # Our image version pointers |
| 583 | + $fileSHA1Keys = array(); |
| 584 | + $imageMap = explode( '#', trim( $imageParams ) ); |
| 585 | + foreach ( $imageMap as $image ) { |
| 586 | + if ( !$image ) { |
| 587 | + continue; |
| 588 | + } |
| 589 | + $m = explode( '|', $image, 3 ); |
| 590 | + # Expand our parameters ... <name>#<timestamp>#<key> |
| 591 | + if ( !isset( $m[0] ) || !isset( $m[1] ) || !isset( $m[2] ) || !$m[0] ) { |
| 592 | + continue; |
| 593 | + } |
| 594 | + list( $dbkey, $time, $key ) = $m; |
| 595 | + # Get the file title |
| 596 | + $img_title = Title::makeTitle( NS_FILE, $dbkey ); // Normalize |
| 597 | + if ( is_null( $img_title ) ) { |
| 598 | + continue; // Page must be valid! |
| 599 | + } |
| 600 | + $fileSHA1Keys[$img_title->getDBkey()] = array(); |
| 601 | + $fileSHA1Keys[$img_title->getDBkey()]['time'] = $time ? $time : false; |
| 602 | + $fileSHA1Keys[$img_title->getDBkey()]['sha1'] = strlen( $key ) ? $key : false; |
| 603 | + } |
| 604 | + return array( $templateIds, $fileSHA1Keys ); |
| 605 | + } |
| 606 | + |
| 607 | + /** |
| 608 | + * Get template and image versions from parsing a revision. |
| 609 | + * @param Article $article |
| 610 | + * @param Revision $rev |
| 611 | + * @returns array( templateIds, fileSHA1Keys ) |
| 612 | + * templateIds like ParserOutput->mTemplateIds |
| 613 | + * fileSHA1Keys like ParserOutput->mImageTimeKeys |
| 614 | + */ |
| 615 | + public static function currentIncludeVersions( Article $article, Revision $rev ) { |
| 616 | + global $wgParser, $wgOut, $wgEnableParserCache; |
| 617 | + wfProfileIn( __METHOD__ ); |
| 618 | + $pOutput = false; |
| 619 | + # Current version: try parser cache |
| 620 | + if ( $rev->isCurrent() ) { |
| 621 | + $parserCache = ParserCache::singleton(); |
| 622 | + $pOutput = $parserCache->get( $article, $wgOut->parserOptions() ); |
| 623 | + } |
| 624 | + # Otherwise (or on cache miss), parse the rev text... |
| 625 | + if ( !$pOutput ) { |
| 626 | + $text = $rev->getText(); |
| 627 | + $title = $article->getTitle(); |
| 628 | + $options = FlaggedRevs::makeParserOptions(); |
| 629 | + $pOutput = $wgParser->parse( |
| 630 | + $text, $title, $options, true, true, $rev->getId() ); |
| 631 | + # Might as well save the cache while we're at it |
| 632 | + if ( $rev->isCurrent() && $wgEnableParserCache ) { |
| 633 | + $parserCache->save( $pOutput, $article, $options ); |
| 634 | + } |
| 635 | + } |
| 636 | + wfProfileOut( __METHOD__ ); |
| 637 | + return array( $pOutput->getTemplateIds(), $pOutput->getImageTimeKeys() ); |
| 638 | + } |
| 639 | +} |
Property changes on: trunk/extensions/FlaggedRevs/business/RevisionReviewForm.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 640 | + native |
Index: trunk/extensions/FlaggedRevs/business/PageStabilityForm.php |
— | — | @@ -0,0 +1,639 @@ |
| 2 | +<?php |
| 3 | +# (c) Aaron Schulz 2010 GPL |
| 4 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 5 | + echo "FlaggedRevs extension\n"; |
| 6 | + exit( 1 ); |
| 7 | +} |
| 8 | +/** |
| 9 | + * Class containing stability settings form business logic |
| 10 | + * Note: edit tokens are the responsibility of caller |
| 11 | + * Usage: (a) set ALL form params before doing anything else |
| 12 | + * (b) call ready() when all params are set |
| 13 | + * (c) call preloadSettings() or submit() as needed |
| 14 | + */ |
| 15 | +abstract class PageStabilityForm |
| 16 | +{ |
| 17 | + /* Form parameters which can be user given */ |
| 18 | + protected $page = false; # Target page obj |
| 19 | + protected $watchThis = null; # Watch checkbox |
| 20 | + protected $reviewThis = null; # Auto-review option... |
| 21 | + protected $reasonExtra = ''; # Custom/extra reason |
| 22 | + protected $reasonSelection = ''; # Reason dropdown key |
| 23 | + protected $expiryCustom = ''; # Custom expiry |
| 24 | + protected $expirySelection = ''; # Expiry dropdown key |
| 25 | + protected $override = -1; # Default version |
| 26 | + protected $autoreview = ''; # Autoreview restrictions... |
| 27 | + |
| 28 | + protected $oldConfig = array(); # Old page config |
| 29 | + protected $inputLock = 0; # Disallow bad submissions |
| 30 | + |
| 31 | + protected $user = null; |
| 32 | + |
| 33 | + public function __construct( User $user ) { |
| 34 | + $this->user = $user; |
| 35 | + } |
| 36 | + |
| 37 | + public function getPage() { |
| 38 | + return $this->page; |
| 39 | + } |
| 40 | + |
| 41 | + public function setPage( Title $value ) { |
| 42 | + $this->trySet( $this->page, $value ); |
| 43 | + } |
| 44 | + |
| 45 | + public function getWatchThis() { |
| 46 | + return $this->watchThis; |
| 47 | + } |
| 48 | + |
| 49 | + public function setWatchThis( $value ) { |
| 50 | + $this->trySet( $this->watchThis, $value ); |
| 51 | + } |
| 52 | + |
| 53 | + public function getReasonExtra() { |
| 54 | + return $this->reasonExtra; |
| 55 | + } |
| 56 | + |
| 57 | + public function setReasonExtra( $value ) { |
| 58 | + $this->trySet( $this->reasonExtra, $value ); |
| 59 | + } |
| 60 | + |
| 61 | + public function getReasonSelection() { |
| 62 | + return $this->reasonSelection; |
| 63 | + } |
| 64 | + |
| 65 | + public function setReasonSelection( $value ) { |
| 66 | + $this->trySet( $this->reasonSelection, $value ); |
| 67 | + } |
| 68 | + |
| 69 | + public function getExpiryCustom() { |
| 70 | + return $this->expiryCustom; |
| 71 | + } |
| 72 | + |
| 73 | + public function setExpiryCustom( $value ) { |
| 74 | + $this->trySet( $this->expiryCustom, $value ); |
| 75 | + } |
| 76 | + |
| 77 | + public function getExpirySelection() { |
| 78 | + return $this->expirySelection; |
| 79 | + } |
| 80 | + |
| 81 | + public function setExpirySelection( $value ) { |
| 82 | + $this->trySet( $this->expirySelection, $value ); |
| 83 | + } |
| 84 | + |
| 85 | + public function getAutoreview() { |
| 86 | + return $this->autoreview; |
| 87 | + } |
| 88 | + |
| 89 | + public function setAutoreview( $value ) { |
| 90 | + $this->trySet( $this->autoreview, $value ); |
| 91 | + } |
| 92 | + |
| 93 | + /* |
| 94 | + * Get the final expiry, all inputs considered |
| 95 | + * Note: does not check if the expiration is less than wfTimestampNow() |
| 96 | + * @return 14-char timestamp or "infinity", or false if the input was invalid |
| 97 | + */ |
| 98 | + public function getExpiry() { |
| 99 | + if ( $this->expirySelection == 'existing' ) { |
| 100 | + return $this->oldConfig['expiry']; |
| 101 | + } elseif ( $this->expirySelection == 'othertime' ) { |
| 102 | + $value = $this->expiryCustom; |
| 103 | + } else { |
| 104 | + $value = $this->expirySelection; |
| 105 | + } |
| 106 | + if ( $value == 'infinite' || $value == 'indefinite' || $value == 'infinity' ) { |
| 107 | + $time = Block::infinity(); |
| 108 | + } else { |
| 109 | + $unix = strtotime( $value ); |
| 110 | + # On error returns -1 for PHP <5.1 and false for PHP >=5.1 |
| 111 | + if ( !$unix || $unix === -1 ) { |
| 112 | + return false; |
| 113 | + } |
| 114 | + // FIXME: non-qualified absolute times are not in users |
| 115 | + // specified timezone and there isn't notice about it in the ui |
| 116 | + $time = wfTimestamp( TS_MW, $unix ); |
| 117 | + } |
| 118 | + return $time; |
| 119 | + } |
| 120 | + |
| 121 | + /* |
| 122 | + * Get the final reason, all inputs considered |
| 123 | + * @return string |
| 124 | + */ |
| 125 | + public function getReason() { |
| 126 | + # Custom reason replaces dropdown |
| 127 | + if ( $this->reasonSelection != 'other' ) { |
| 128 | + $comment = $this->reasonSelection; // start with dropdown reason |
| 129 | + if ( $this->reasonExtra != '' ) { |
| 130 | + # Append custom reason |
| 131 | + $comment .= wfMsgForContent( 'colon-separator' ) . $this->reasonExtra; |
| 132 | + } |
| 133 | + } else { |
| 134 | + $comment = $this->reasonExtra; // just use custom reason |
| 135 | + } |
| 136 | + return $comment; |
| 137 | + } |
| 138 | + |
| 139 | + /** |
| 140 | + * Set a member field to a value if the fields are unlocked |
| 141 | + */ |
| 142 | + protected function trySet( &$field, $value ) { |
| 143 | + if ( $this->inputLock ) { |
| 144 | + throw new MWException( __CLASS__ . " fields cannot be set anymore.\n"); |
| 145 | + } else { |
| 146 | + $field = $value; // still allowing input |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + /** |
| 151 | + * Signal that inputs are starting |
| 152 | + */ |
| 153 | + public function start() { |
| 154 | + $this->inputLock = 0; |
| 155 | + } |
| 156 | + |
| 157 | + /** |
| 158 | + * Signal that inputs are done and load old config |
| 159 | + * @return mixed (true on success, error string on target failure) |
| 160 | + */ |
| 161 | + public function ready() { |
| 162 | + $this->inputLock = 1; |
| 163 | + $status = $this->checkTarget(); |
| 164 | + if ( $status !== true ) { |
| 165 | + return $status; // bad target |
| 166 | + } |
| 167 | + $this->loadOldConfig(); // current settings from DB |
| 168 | + return $status; |
| 169 | + } |
| 170 | + |
| 171 | + /* |
| 172 | + * Preload existing page settings (e.g. from GET request). |
| 173 | + * @return mixed (true on success, error string on failure) |
| 174 | + */ |
| 175 | + public function preloadSettings() { |
| 176 | + if ( !$this->inputLock ) { |
| 177 | + throw new MWException( __CLASS__ . " input fields not set yet.\n"); |
| 178 | + } |
| 179 | + $status = $this->checkTarget(); |
| 180 | + if ( $status !== true ) { |
| 181 | + return $status; // bad target |
| 182 | + } |
| 183 | + if ( $this->oldConfig['expiry'] == Block::infinity() ) { |
| 184 | + $this->expirySelection = 'infinite'; // no settings set OR indefinite |
| 185 | + } else { |
| 186 | + $this->expirySelection = 'existing'; // settings set and NOT indefinite |
| 187 | + } |
| 188 | + return $this->reallyPreloadSettings(); // load the params... |
| 189 | + } |
| 190 | + |
| 191 | + /* |
| 192 | + * @return mixed (true on success, error string on failure) |
| 193 | + */ |
| 194 | + protected function reallyPreloadSettings() { |
| 195 | + return true; |
| 196 | + } |
| 197 | + |
| 198 | + /* |
| 199 | + * Verify and clean up parameters (e.g. from POST request). |
| 200 | + * @return mixed (true on success, error string on failure) |
| 201 | + */ |
| 202 | + protected function checkSettings() { |
| 203 | + $status = $this->checkTarget(); |
| 204 | + if ( $status !== true ) { |
| 205 | + return $status; // bad target |
| 206 | + } |
| 207 | + if ( $this->expiryCustom != '' ) { |
| 208 | + // Custom expiry takes precedence |
| 209 | + $this->expirySelection = 'othertime'; |
| 210 | + } |
| 211 | + $status = $this->reallyCheckSettings(); // check other params... |
| 212 | + return $status; |
| 213 | + } |
| 214 | + |
| 215 | + /* |
| 216 | + * @return mixed (true on success, error string on failure) |
| 217 | + */ |
| 218 | + protected function reallyCheckSettings() { |
| 219 | + return true; |
| 220 | + } |
| 221 | + |
| 222 | + /* |
| 223 | + * Check that the target page is valid |
| 224 | + * @return mixed (true on success, error string on failure) |
| 225 | + */ |
| 226 | + protected function checkTarget() { |
| 227 | + if ( is_null( $this->page ) ) { |
| 228 | + return 'stabilize_page_invalid'; |
| 229 | + } elseif ( !$this->page->exists() ) { |
| 230 | + return 'stabilize_page_notexists'; |
| 231 | + } elseif ( !FlaggedRevs::inReviewNamespace( $this->page ) ) { |
| 232 | + return 'stabilize_page_unreviewable'; |
| 233 | + } |
| 234 | + return true; |
| 235 | + } |
| 236 | + |
| 237 | + protected function loadOldConfig() { |
| 238 | + # Get the current page config |
| 239 | + $this->oldConfig = FlaggedPageConfig::getPageStabilitySettings( $this->page, FR_MASTER ); |
| 240 | + } |
| 241 | + |
| 242 | + /* |
| 243 | + * Can the user change the settings for this page? |
| 244 | + * Note: if the current autoreview restriction is too high for this user |
| 245 | + * then this will return false. Useful for form selectors. |
| 246 | + * @return bool |
| 247 | + */ |
| 248 | + public function isAllowed() { |
| 249 | + # Users who cannot edit or review the page cannot set this |
| 250 | + return ( $this->page |
| 251 | + && $this->page->userCan( 'stablesettings' ) |
| 252 | + && $this->page->userCan( 'edit' ) |
| 253 | + && $this->page->userCan( 'review' ) |
| 254 | + ); |
| 255 | + } |
| 256 | + |
| 257 | + /** |
| 258 | + * Submit the form parameters for the page config to the DB. |
| 259 | + * |
| 260 | + * @return mixed (true on success, error string on failure) |
| 261 | + */ |
| 262 | + public function submit() { |
| 263 | + if ( !$this->inputLock ) { |
| 264 | + throw new MWException( __CLASS__ . " input fields not set yet.\n"); |
| 265 | + } |
| 266 | + $status = $this->checkSettings(); |
| 267 | + if ( $status !== true ) { |
| 268 | + return $status; // cannot submit - broken params |
| 269 | + } |
| 270 | + # Double-check permissions |
| 271 | + if ( !$this->isAllowed() ) { |
| 272 | + return 'stablize_denied'; |
| 273 | + } |
| 274 | + # Are we are going back to site defaults? |
| 275 | + $reset = $this->newConfigIsReset(); |
| 276 | + # Parse and cleanup the expiry time given... |
| 277 | + $expiry = $this->getExpiry(); |
| 278 | + if ( $expiry === false ) { |
| 279 | + return 'stabilize_expiry_invalid'; |
| 280 | + } elseif ( $expiry !== Block::infinity() && $expiry < wfTimestampNow() ) { |
| 281 | + return 'stabilize_expiry_old'; |
| 282 | + } |
| 283 | + # Update the DB row with the new config... |
| 284 | + $changed = $this->updateConfigRow( $reset ); |
| 285 | + # Log if this actually changed anything... |
| 286 | + if ( $changed ) { |
| 287 | + # Update logs and make a null edit |
| 288 | + $nullRev = $this->updateLogsAndHistory( $reset ); |
| 289 | + if ( $this->reviewThis ) { |
| 290 | + # Null edit may have been auto-reviewed already |
| 291 | + $frev = FlaggedRevision::newFromTitle( |
| 292 | + $this->page, $nullRev->getId(), FR_MASTER ); |
| 293 | + # Check if this null edit is to be reviewed... |
| 294 | + if ( !$frev ) { |
| 295 | + $flags = null; |
| 296 | + $article = new Article( $this->page ); |
| 297 | + # Review this revision of the page... |
| 298 | + $ok = FlaggedRevs::autoReviewEdit( |
| 299 | + $article, $this->user, $nullRev, $flags, true ); |
| 300 | + if ( $ok ) { |
| 301 | + FlaggedRevs::markRevisionPatrolled( $nullRev ); // reviewed -> patrolled |
| 302 | + } |
| 303 | + } |
| 304 | + } |
| 305 | + # Update page and tracking tables and clear cache |
| 306 | + FlaggedRevs::stableVersionUpdates( $this->page ); |
| 307 | + } |
| 308 | + # Apply watchlist checkbox value (may be NULL) |
| 309 | + $this->updateWatchlist(); |
| 310 | + # Take this opportunity to purge out expired configurations |
| 311 | + FlaggedPageConfig::purgeExpiredConfigurations(); |
| 312 | + return true; |
| 313 | + } |
| 314 | + |
| 315 | + /* |
| 316 | + * Do history & log updates: |
| 317 | + * (a) Add a new stability log entry |
| 318 | + * (b) Add a null edit like the log entry |
| 319 | + * @return Revision |
| 320 | + */ |
| 321 | + protected function updateLogsAndHistory( $reset ) { |
| 322 | + global $wgContLang; |
| 323 | + $article = new Article( $this->page ); |
| 324 | + $latest = $this->page->getLatestRevID( Title::GAID_FOR_UPDATE ); |
| 325 | + # Config may have changed to allow stable versions. |
| 326 | + # Refresh tracking to account for any hidden reviewed versions... |
| 327 | + $frev = FlaggedRevision::newFromStable( $this->page, FR_MASTER ); |
| 328 | + if ( $frev ) { |
| 329 | + FlaggedRevs::updateStableVersion( $article, $frev, $latest ); |
| 330 | + } else { |
| 331 | + FlaggedRevs::clearTrackingRows( $article->getId() ); |
| 332 | + } |
| 333 | + $reason = $this->getReason(); |
| 334 | + # Insert stability log entry... |
| 335 | + $log = new LogPage( 'stable' ); |
| 336 | + if ( $reset ) { |
| 337 | + $log->addEntry( 'reset', $this->page, $reason ); |
| 338 | + $type = "stable-logentry-reset"; |
| 339 | + $settings = ''; // no level, expiry info |
| 340 | + } else { |
| 341 | + $params = $this->getLogParams(); |
| 342 | + $action = ( $this->oldConfig === FlaggedPageConfig::getDefaultVisibilitySettings() ) |
| 343 | + ? 'config' // set a custom configuration |
| 344 | + : 'modify'; // modified an existing custom configuration |
| 345 | + $log->addEntry( $action, $this->page, $reason, |
| 346 | + FlaggedRevsLogs::collapseParams( $params ) ); |
| 347 | + $type = "stable-logentry-config"; |
| 348 | + // Settings message in text form (e.g. [x=a,y=b,z]) |
| 349 | + $settings = FlaggedRevsLogs::stabilitySettings( $params, true /*content*/ ); |
| 350 | + } |
| 351 | + # Build null-edit comment...<action: reason [settings] (expiry)> |
| 352 | + $comment = $wgContLang->ucfirst( |
| 353 | + wfMsgForContent( $type, $this->page->getPrefixedText() ) ); // action |
| 354 | + if ( $reason != '' ) { |
| 355 | + $comment .= wfMsgForContent( 'colon-separator' ) . $reason; // add reason |
| 356 | + } |
| 357 | + if ( $settings != '' ) { |
| 358 | + $comment .= " {$settings}"; // add settings |
| 359 | + } |
| 360 | + # Insert a null revision... |
| 361 | + $dbw = wfGetDB( DB_MASTER ); |
| 362 | + $nullRev = Revision::newNullRevision( $dbw, $article->getId(), $comment, true ); |
| 363 | + $nullRev->insertOn( $dbw ); |
| 364 | + # Update page record and touch page |
| 365 | + $article->updateRevisionOn( $dbw, $nullRev, $latest ); |
| 366 | + wfRunHooks( 'NewRevisionFromEditComplete', array( $article, $nullRev, $latest ) ); |
| 367 | + # Return null Revision object for autoreview check |
| 368 | + return $nullRev; |
| 369 | + } |
| 370 | + |
| 371 | + /* |
| 372 | + * Checks if new config is the same as the site default |
| 373 | + * @return bool |
| 374 | + */ |
| 375 | + protected function newConfigIsReset() { |
| 376 | + return false; |
| 377 | + } |
| 378 | + |
| 379 | + /* |
| 380 | + * Get assoc. array of log params |
| 381 | + * @return array |
| 382 | + */ |
| 383 | + protected function getLogParams() { |
| 384 | + return array(); |
| 385 | + } |
| 386 | + |
| 387 | + /* |
| 388 | + * (a) Watch page if $watchThis is true |
| 389 | + * (b) Unwatch if $watchThis is false |
| 390 | + */ |
| 391 | + protected function updateWatchlist() { |
| 392 | + # Apply watchlist checkbox value (may be NULL) |
| 393 | + if ( $this->watchThis === true ) { |
| 394 | + $this->user->addWatch( $this->page ); |
| 395 | + } elseif ( $this->watchThis === false ) { |
| 396 | + $this->user->removeWatch( $this->page ); |
| 397 | + } |
| 398 | + } |
| 399 | + |
| 400 | + // Same JS used for expiry for either $wgFlaggedRevsProtection case |
| 401 | + public static function addProtectionJS() { |
| 402 | + global $wgOut; |
| 403 | + $wgOut->addScript( |
| 404 | + "<script type=\"text/javascript\"> |
| 405 | + function onFRChangeExpiryDropdown() { |
| 406 | + document.getElementById('mwStabilizeExpiryOther').value = ''; |
| 407 | + } |
| 408 | + function onFRChangeExpiryField() { |
| 409 | + document.getElementById('mwStabilizeExpirySelection').value = 'othertime'; |
| 410 | + } |
| 411 | + </script>" |
| 412 | + ); |
| 413 | + } |
| 414 | +} |
| 415 | + |
| 416 | +// Assumes $wgFlaggedRevsProtection is off |
| 417 | +class PageStabilityGeneralForm extends PageStabilityForm { |
| 418 | + public function getReviewThis() { |
| 419 | + return $this->reviewThis; |
| 420 | + } |
| 421 | + |
| 422 | + public function setReviewThis( $value ) { |
| 423 | + $this->trySet( $this->reviewThis, $value ); |
| 424 | + } |
| 425 | + |
| 426 | + public function getOverride() { |
| 427 | + return $this->override; |
| 428 | + } |
| 429 | + |
| 430 | + public function setOverride( $value ) { |
| 431 | + $this->trySet( $this->override, $value ); |
| 432 | + } |
| 433 | + |
| 434 | + protected function reallyPreloadSettings() { |
| 435 | + $this->override = $this->oldConfig['override']; |
| 436 | + $this->autoreview = $this->oldConfig['autoreview']; |
| 437 | + $this->watchThis = $this->page->userIsWatching(); |
| 438 | + return true; |
| 439 | + } |
| 440 | + |
| 441 | + protected function reallyCheckSettings() { |
| 442 | + $this->override = $this->override ? 1 : 0; // default version settings is 0 or 1 |
| 443 | + // Check autoreview restriction setting |
| 444 | + if ( $this->autoreview != '' // restriction other than 'none' |
| 445 | + && !in_array( $this->autoreview, FlaggedRevs::getRestrictionLevels() ) ) |
| 446 | + { |
| 447 | + return 'stabilize_invalid_autoreview'; // invalid value |
| 448 | + } |
| 449 | + if ( !FlaggedRevs::userCanSetAutoreviewLevel( $this->user, $this->autoreview ) ) { |
| 450 | + return 'stabilize_denied'; // invalid value |
| 451 | + } |
| 452 | + return true; |
| 453 | + } |
| 454 | + |
| 455 | + protected function getLogParams() { |
| 456 | + return array( |
| 457 | + 'override' => $this->override, |
| 458 | + 'autoreview' => $this->autoreview, |
| 459 | + 'expiry' => $this->getExpiry(), // TS_MW/infinity |
| 460 | + 'precedence' => 1 // here for log hook b/c |
| 461 | + ); |
| 462 | + } |
| 463 | + |
| 464 | + // Return current config array |
| 465 | + public function getOldConfig() { |
| 466 | + if ( !$this->inputLock ) { |
| 467 | + throw new MWException( __CLASS__ . " input fields not set yet.\n"); |
| 468 | + } |
| 469 | + return $this->oldConfig; |
| 470 | + } |
| 471 | + |
| 472 | + // returns whether row changed |
| 473 | + protected function updateConfigRow( $reset ) { |
| 474 | + $changed = false; |
| 475 | + $dbw = wfGetDB( DB_MASTER ); |
| 476 | + # If setting to site default values and there is a row then erase it |
| 477 | + if ( $reset ) { |
| 478 | + $dbw->delete( 'flaggedpage_config', |
| 479 | + array( 'fpc_page_id' => $this->page->getArticleID() ), |
| 480 | + __METHOD__ |
| 481 | + ); |
| 482 | + $changed = ( $dbw->affectedRows() != 0 ); // did this do anything? |
| 483 | + # Otherwise, add/replace row if we are not just setting it to the site default |
| 484 | + } elseif ( !$reset ) { |
| 485 | + $dbExpiry = Block::encodeExpiry( $this->getExpiry(), $dbw ); |
| 486 | + # Get current config... |
| 487 | + $oldRow = $dbw->selectRow( 'flaggedpage_config', |
| 488 | + array( 'fpc_select', 'fpc_override', 'fpc_level', 'fpc_expiry' ), |
| 489 | + array( 'fpc_page_id' => $this->page->getArticleID() ), |
| 490 | + __METHOD__, |
| 491 | + 'FOR UPDATE' |
| 492 | + ); |
| 493 | + # Check if this is not the same config as the existing row (if any) |
| 494 | + $changed = $this->configIsDifferent( $oldRow, |
| 495 | + $this->select, $this->override, $this->autoreview, $dbExpiry ); |
| 496 | + # If the new config is different, replace the old row... |
| 497 | + if ( $changed ) { |
| 498 | + $dbw->replace( 'flaggedpage_config', |
| 499 | + array( 'PRIMARY' ), |
| 500 | + array( |
| 501 | + 'fpc_page_id' => $this->page->getArticleID(), |
| 502 | + 'fpc_select' => 1, // unused |
| 503 | + 'fpc_override' => (int)$this->override, |
| 504 | + 'fpc_level' => $this->autoreview, |
| 505 | + 'fpc_expiry' => $dbExpiry |
| 506 | + ), |
| 507 | + __METHOD__ |
| 508 | + ); |
| 509 | + } |
| 510 | + } |
| 511 | + return $changed; |
| 512 | + } |
| 513 | + |
| 514 | + protected function newConfigIsReset() { |
| 515 | + return ( $this->override == FlaggedRevs::isStableShownByDefault() |
| 516 | + && $this->autoreview == '' ); |
| 517 | + } |
| 518 | + |
| 519 | + // Checks if new config is different than the existing row |
| 520 | + protected function configIsDifferent( $oldRow, $override, $autoreview, $dbExpiry ) { |
| 521 | + if( !$oldRow ) { |
| 522 | + return true; // no previous config |
| 523 | + } |
| 524 | + return ( $oldRow->fpc_override != $override // ...override changed, or... |
| 525 | + || $oldRow->fpc_level != $autoreview // ...autoreview level changed, or... |
| 526 | + || $oldRow->fpc_expiry != $dbExpiry // ...expiry changed |
| 527 | + ); |
| 528 | + } |
| 529 | +} |
| 530 | + |
| 531 | +// Assumes $wgFlaggedRevsProtection is on |
| 532 | +class PageStabilityProtectForm extends PageStabilityForm { |
| 533 | + protected function reallyPreloadSettings() { |
| 534 | + $this->autoreview = $this->oldConfig['autoreview']; // protect level |
| 535 | + $this->watchThis = $this->page->userIsWatching(); |
| 536 | + return true; |
| 537 | + } |
| 538 | + |
| 539 | + protected function reallyCheckSettings() { |
| 540 | + # WMF temp hack...protection limit quota |
| 541 | + global $wgFlaggedRevsProtectQuota; |
| 542 | + if ( isset( $wgFlaggedRevsProtectQuota ) // quota exists |
| 543 | + && $this->autoreview != '' // and we are protecting |
| 544 | + && FlaggedPageConfig::getProtectionLevel( $this->oldConfig ) == 'none' ) // page unprotected |
| 545 | + { |
| 546 | + $dbw = wfGetDB( DB_MASTER ); |
| 547 | + $count = $dbw->selectField( 'flaggedpage_config', 'COUNT(*)', '', __METHOD__ ); |
| 548 | + if ( $count >= $wgFlaggedRevsProtectQuota ) { |
| 549 | + return 'stabilize_protect_quota'; |
| 550 | + } |
| 551 | + } |
| 552 | + # Autoreview only when protecting currently unprotected pages |
| 553 | + $this->reviewThis = ( FlaggedPageConfig::getProtectionLevel( $this->oldConfig ) == 'none' ); |
| 554 | + # Autoreview restriction => use stable |
| 555 | + # No autoreview restriction => site default |
| 556 | + $this->override = ( $this->autoreview != '' ) |
| 557 | + ? 1 // edits require review before being published |
| 558 | + : (int)FlaggedRevs::isStableShownByDefault(); // site default |
| 559 | + # Check that settings are a valid protection level... |
| 560 | + $newConfig = array( |
| 561 | + 'override' => $this->override, |
| 562 | + 'autoreview' => $this->autoreview |
| 563 | + ); |
| 564 | + if ( FlaggedPageConfig::getProtectionLevel( $newConfig ) == 'invalid' ) { |
| 565 | + return 'stabilize_invalid_level'; // double-check configuration |
| 566 | + } |
| 567 | + # Check autoreview restriction setting |
| 568 | + if ( !FlaggedRevs::userCanSetAutoreviewLevel( $this->user, $this->autoreview ) ) { |
| 569 | + return 'stabilize_denied'; // invalid value |
| 570 | + } |
| 571 | + return true; |
| 572 | + } |
| 573 | + |
| 574 | + // Doesn't and shouldn't include 'precedence'; checked in FlaggedRevsLogs |
| 575 | + protected function getLogParams() { |
| 576 | + return array( |
| 577 | + 'override' => $this->override, // in case of site changes |
| 578 | + 'autoreview' => $this->autoreview, |
| 579 | + 'expiry' => $this->getExpiry() // TS_MW/infinity |
| 580 | + ); |
| 581 | + } |
| 582 | + |
| 583 | + protected function updateConfigRow( $reset ) { |
| 584 | + $changed = false; |
| 585 | + $dbw = wfGetDB( DB_MASTER ); |
| 586 | + # If setting to site default values and there is a row then erase it |
| 587 | + if ( $reset ) { |
| 588 | + $dbw->delete( 'flaggedpage_config', |
| 589 | + array( 'fpc_page_id' => $this->page->getArticleID() ), |
| 590 | + __METHOD__ |
| 591 | + ); |
| 592 | + $changed = ( $dbw->affectedRows() != 0 ); // did this do anything? |
| 593 | + # Otherwise, add/replace row if we are not just setting it to the site default |
| 594 | + } elseif ( !$reset ) { |
| 595 | + $dbExpiry = Block::encodeExpiry( $this->getExpiry(), $dbw ); |
| 596 | + # Get current config... |
| 597 | + $oldRow = $dbw->selectRow( 'flaggedpage_config', |
| 598 | + array( 'fpc_override', 'fpc_level', 'fpc_expiry' ), |
| 599 | + array( 'fpc_page_id' => $this->page->getArticleID() ), |
| 600 | + __METHOD__, |
| 601 | + 'FOR UPDATE' |
| 602 | + ); |
| 603 | + # Check if this is not the same config as the existing row (if any) |
| 604 | + $changed = $this->configIsDifferent( $oldRow, |
| 605 | + $this->override, $this->autoreview, $dbExpiry ); |
| 606 | + # If the new config is different, replace the old row... |
| 607 | + if ( $changed ) { |
| 608 | + $dbw->replace( 'flaggedpage_config', |
| 609 | + array( 'PRIMARY' ), |
| 610 | + array( |
| 611 | + 'fpc_page_id' => $this->page->getArticleID(), |
| 612 | + 'fpc_select' => -1, // ignored |
| 613 | + 'fpc_override' => (int)$this->override, |
| 614 | + 'fpc_level' => $this->autoreview, |
| 615 | + 'fpc_expiry' => $dbExpiry |
| 616 | + ), |
| 617 | + __METHOD__ |
| 618 | + ); |
| 619 | + } |
| 620 | + } |
| 621 | + return $changed; |
| 622 | + } |
| 623 | + |
| 624 | + protected function newConfigIsReset() { |
| 625 | + # For protection config, just ignore the fpc_select column |
| 626 | + return ( $this->autoreview == '' ); |
| 627 | + } |
| 628 | + |
| 629 | + // Checks if new config is different than the existing row |
| 630 | + protected function configIsDifferent( $oldRow, $override, $autoreview, $dbExpiry ) { |
| 631 | + if ( !$oldRow ) { |
| 632 | + return true; // no previous config |
| 633 | + } |
| 634 | + # For protection config, just ignore the fpc_select column |
| 635 | + return ( $oldRow->fpc_override != $override // ...override changed, or... |
| 636 | + || $oldRow->fpc_level != $autoreview // ...autoreview level changed, or... |
| 637 | + || $oldRow->fpc_expiry != $dbExpiry // ...expiry changed |
| 638 | + ); |
| 639 | + } |
| 640 | +} |
Property changes on: trunk/extensions/FlaggedRevs/business/PageStabilityForm.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 641 | + native |
Index: trunk/extensions/FlaggedRevs/specialpages/RevisionReview_body.php |
— | — | @@ -10,7 +10,7 @@ |
11 | 11 | { |
12 | 12 | protected $form; |
13 | 13 | protected $page; |
14 | | - protected $skin; |
| 14 | + var $skin; // FIXME: with RevDel_RevisionList stuff |
15 | 15 | |
16 | 16 | public function __construct() { |
17 | 17 | parent::__construct( 'RevisionReview', 'review' ); |
— | — | @@ -49,7 +49,6 @@ |
50 | 50 | $form->setApprove( $wgRequest->getCheck( 'wpApprove' ) ); |
51 | 51 | $form->setUnapprove( $wgRequest->getCheck( 'wpUnapprove' ) ); |
52 | 52 | $form->setReject( $wgRequest->getCheck( 'wpReject' ) ); |
53 | | - $form->setRejectConfirm( $wgRequest->getBool( 'wpRejectConfirm' ) ); |
54 | 53 | # Rev ID |
55 | 54 | $form->setOldId( $wgRequest->getInt( 'oldid' ) ); |
56 | 55 | $form->setRefId( $wgRequest->getInt( 'refid' ) ); |
— | — | @@ -97,35 +96,52 @@ |
98 | 97 | $wgOut->returnToMain( false, $this->page ); |
99 | 98 | return; |
100 | 99 | } |
101 | | - $status = $form->submit(); |
102 | | - // Success for either flagging or unflagging |
103 | | - if ( $status === true ) { |
104 | | - $wgOut->setPageTitle( wfMsgHtml( 'actioncomplete' ) ); |
105 | | - if ( $form->getAction() === 'approve' ) { |
106 | | - $wgOut->addHTML( $this->approvalSuccessHTML( true ) ); |
107 | | - } elseif ( $form->getAction() === 'unapprove' ) { |
108 | | - $wgOut->addHTML( $this->deapprovalSuccessHTML( true ) ); |
109 | | - } elseif ( $form->getAction() === 'reject' ) { |
110 | | - $wgOut->redirect( $this->page->getFullUrl() ); |
| 100 | + // Use confirmation screen for reject... |
| 101 | + if ( $form->getAction() == 'reject' && !$wgRequest->getBool( 'wpRejectConfirm' ) ) { |
| 102 | + $rejectForm = new RejectConfirmationFormGUI( $form ); |
| 103 | + list( $html, $status ) = $rejectForm->getHtml(); |
| 104 | + // Success... |
| 105 | + if ( $status === true ) { |
| 106 | + $wgOut->addHtml( $html ); |
| 107 | + // Failure... |
| 108 | + } else { |
| 109 | + if ( $status === 'review_bad_oldid' ) { |
| 110 | + $wgOut->showErrorPage( 'internalerror', 'revreview-revnotfound' ); |
| 111 | + } else { |
| 112 | + $wgOut->showErrorPage( 'internalerror', $status ); |
| 113 | + } |
| 114 | + $wgOut->returnToMain( false, $this->page ); |
111 | 115 | } |
112 | | - } elseif ( $status === false ) { |
113 | | - // Reject confirmation screen. HACKY :( |
114 | | - return; |
| 116 | + // Otherwise submit... |
115 | 117 | } else { |
116 | | - if ( $status === 'review_denied' ) { |
117 | | - $wgOut->permissionRequired( 'badaccess-group0' ); // protected? |
118 | | - } elseif ( $status === 'review_bad_key' ) { |
119 | | - $wgOut->permissionRequired( 'badaccess-group0' ); // fiddling |
120 | | - } elseif ( $status === 'review_bad_oldid' ) { |
121 | | - $wgOut->showErrorPage( 'internalerror', 'revreview-revnotfound' ); |
122 | | - } elseif ( $status === 'review_not_flagged' ) { |
123 | | - $wgOut->redirect( $this->page->getFullUrl() ); // already unflagged |
124 | | - } elseif ( $status === 'review_too_low' ) { |
125 | | - $wgOut->addWikiText( wfMsg( 'revreview-toolow' ) ); |
| 118 | + $status = $form->submit(); |
| 119 | + // Success... |
| 120 | + if ( $status === true ) { |
| 121 | + $wgOut->setPageTitle( wfMsgHtml( 'actioncomplete' ) ); |
| 122 | + if ( $form->getAction() === 'approve' ) { |
| 123 | + $wgOut->addHTML( $this->approvalSuccessHTML( true ) ); |
| 124 | + } elseif ( $form->getAction() === 'unapprove' ) { |
| 125 | + $wgOut->addHTML( $this->deapprovalSuccessHTML( true ) ); |
| 126 | + } elseif ( $form->getAction() === 'reject' ) { |
| 127 | + $wgOut->redirect( $this->page->getFullUrl() ); |
| 128 | + } |
| 129 | + // Failure... |
126 | 130 | } else { |
127 | | - $wgOut->showErrorPage( 'internalerror', $status ); |
| 131 | + if ( $status === 'review_denied' ) { |
| 132 | + $wgOut->permissionRequired( 'badaccess-group0' ); // protected? |
| 133 | + } elseif ( $status === 'review_bad_key' ) { |
| 134 | + $wgOut->permissionRequired( 'badaccess-group0' ); // fiddling |
| 135 | + } elseif ( $status === 'review_bad_oldid' ) { |
| 136 | + $wgOut->showErrorPage( 'internalerror', 'revreview-revnotfound' ); |
| 137 | + } elseif ( $status === 'review_not_flagged' ) { |
| 138 | + $wgOut->redirect( $this->page->getFullUrl() ); // already unflagged |
| 139 | + } elseif ( $status === 'review_too_low' ) { |
| 140 | + $wgOut->addWikiText( wfMsg( 'revreview-toolow' ) ); |
| 141 | + } else { |
| 142 | + $wgOut->showErrorPage( 'internalerror', $status ); |
| 143 | + } |
| 144 | + $wgOut->returnToMain( false, $this->page ); |
128 | 145 | } |
129 | | - $wgOut->returnToMain( false, $this->page ); |
130 | 146 | } |
131 | 147 | // No form to view (GET) |
132 | 148 | } else { |
Index: trunk/extensions/FlaggedRevs/api/ApiReview.php |
— | — | @@ -54,33 +54,23 @@ |
55 | 55 | $form->setOldId( $revid ); |
56 | 56 | $form->setApprove( empty( $params['unapprove'] ) ); |
57 | 57 | $form->setUnapprove( !empty( $params['unapprove'] ) ); |
58 | | - if ( isset( $params['comment'] ) ) |
| 58 | + if ( isset( $params['comment'] ) ) { |
59 | 59 | $form->setComment( $params['comment'] ); |
| 60 | + } |
60 | 61 | // The flagging parameters have the form 'flag_$name'. |
61 | 62 | // Extract them and put the values into $form->dims |
62 | 63 | foreach ( FlaggedRevs::getTags() as $tag ) { |
63 | 64 | $form->setDim( $tag, (int)$params['flag_' . $tag] ); |
64 | 65 | } |
65 | 66 | if ( $form->getAction() === 'approve' ) { |
66 | | - $parserOutput = null; |
| 67 | + $article = new FlaggedArticle( $title ); |
67 | 68 | // Now get the template and image parameters needed |
68 | | - // If it is the current revision, try the parser cache first |
69 | | - $article = new FlaggedArticle( $title, $revid ); |
70 | | - if ( $rev->isCurrent() ) { |
71 | | - $parserCache = ParserCache::singleton(); |
72 | | - $parserOutput = $parserCache->get( $article, $wgOut->parserOptions() ); |
73 | | - } |
74 | | - if ( !$parserOutput ) { |
75 | | - // Miss, we have to reparse the page |
76 | | - $text = $article->getContent(); |
77 | | - $options = FlaggedRevs::makeParserOptions(); |
78 | | - $parserOutput = $wgParser->parse( |
79 | | - $text, $title, $options, true, true, $article->getLatest() ); |
80 | | - } |
81 | | - // Set version parameters for review submission |
| 69 | + list( $templateIds, $fileTimeKeys ) = |
| 70 | + RevisionReviewForm::currentIncludeVersions( $article, $rev ); |
| 71 | + // Get version parameters for review submission (flat strings) |
82 | 72 | list( $templateParams, $imageParams, $fileVersion ) = |
83 | | - RevisionReviewForm::getIncludeParams( $article, |
84 | | - $parserOutput->getTemplateIds(), $parserOutput->getImageTimeKeys() ); |
| 73 | + RevisionReviewForm::getIncludeParams( $article, $templateIds, $fileTimeKeys ); |
| 74 | + // Set the version parameters... |
85 | 75 | $form->setTemplateParams( $templateParams ); |
86 | 76 | $form->setFileParams( $imageParams ); |
87 | 77 | $form->setFileVersion( $fileVersion ); |
Index: trunk/extensions/FlaggedRevs/presentation/RevisionReviewFormGUI.php |
— | — | @@ -0,0 +1,382 @@ |
| 2 | +<?php |
| 3 | +# (c) Aaron Schulz 2011 GPL |
| 4 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 5 | + echo "FlaggedRevs extension\n"; |
| 6 | + exit( 1 ); |
| 7 | +} |
| 8 | +/** |
| 9 | + * Main review form UI |
| 10 | + * |
| 11 | + * NOTE: use ONLY for diff-to-stable views and page version views |
| 12 | + */ |
| 13 | +class RevisionReviewFormGUI { |
| 14 | + protected $user, $article, $rev; |
| 15 | + protected $refId = 0, $topNotice = ''; |
| 16 | + protected $templateIDs = null, $imageSHA1Keys = null; |
| 17 | + |
| 18 | + /** |
| 19 | + * Generates a brief review form for a page |
| 20 | + * @param User $user |
| 21 | + * @param FlaggedArticle $article |
| 22 | + * @param Revision $rev |
| 23 | + */ |
| 24 | + public function __construct( User $user, FlaggedArticle $article, Revision $rev ) { |
| 25 | + $this->user = $user; |
| 26 | + $this->article = $article; |
| 27 | + $this->rev = $rev; |
| 28 | + } |
| 29 | + |
| 30 | + /* |
| 31 | + * If $refId > 0: |
| 32 | + * (a) Show "reject" button |
| 33 | + * (b) default the rating tags to those of $rev if it was flagged |
| 34 | + * @param int $refId Left side version ID for diffs, $rev is the right rev |
| 35 | + */ |
| 36 | + public function setDiffLeftId( $refId ) { |
| 37 | + $this->refId = $refId; |
| 38 | + } |
| 39 | + |
| 40 | + /* |
| 41 | + * Add on a notice inside the review box at the top |
| 42 | + * @param string $topNotice Text to |
| 43 | + */ |
| 44 | + public function setTopNotice( $topNotice ) { |
| 45 | + $this->topNotice = (string)$topNotice; |
| 46 | + } |
| 47 | + |
| 48 | + /* |
| 49 | + * Set the template/file version parameters corresponding to what the user is viewing |
| 50 | + * @param string $topNotice Text to |
| 51 | + */ |
| 52 | + public function setIncludeVersions( array $templateIDs, array $imageSHA1Keys ) { |
| 53 | + $this->templateIDs = $templateIDs; |
| 54 | + $this->imageSHA1Keys = $imageSHA1Keys; |
| 55 | + } |
| 56 | + |
| 57 | + /** |
| 58 | + * Generates a brief review form for a page |
| 59 | + * @return Array (html string, error string or true) |
| 60 | + */ |
| 61 | + public function getHtml() { |
| 62 | + global $wgOut, $wgLang, $wgParser, $wgEnableParserCache; |
| 63 | + $id = $this->rev->getId(); |
| 64 | + if ( $this->rev->isDeleted( Revision::DELETED_TEXT ) ) { |
| 65 | + return array( '', 'review_bad_oldid' ); # The revision must be valid and public |
| 66 | + } |
| 67 | + $article = $this->article; // convenience |
| 68 | + |
| 69 | + $srev = $article->getStableRev(); |
| 70 | + # See if the version being displayed is flagged... |
| 71 | + if ( $id == $article->getStable() ) { |
| 72 | + $frev = $srev; // avoid query |
| 73 | + } else { |
| 74 | + $frev = FlaggedRevision::newFromTitle( $article->getTitle(), $id ); |
| 75 | + } |
| 76 | + $oldFlags = $frev |
| 77 | + ? $frev->getTags() // existing tags |
| 78 | + : FlaggedRevs::quickTags( FR_CHECKED ); // basic tags |
| 79 | + $reviewTime = $frev ? $frev->getTimestamp() : ''; // last review of rev |
| 80 | + |
| 81 | + # If we are reviewing updates to a page, start off with the stable revision's |
| 82 | + # flags. Otherwise, we just fill them in with the selected revision's flags. |
| 83 | + # @TODO: do we want to carry over info for other diffs? |
| 84 | + if ( $srev && $srev->getRevId() == $this->refId ) { // diff-to-stable |
| 85 | + $flags = $srev->getTags(); |
| 86 | + # Check if user is allowed to renew the stable version. |
| 87 | + # If not, then get the flags for the new revision itself. |
| 88 | + if ( !FlaggedRevs::userCanSetFlags( $this->user, $oldFlags ) ) { |
| 89 | + $flags = $oldFlags; |
| 90 | + } |
| 91 | + # Re-review button is need for template/file only review case |
| 92 | + $reviewIncludes = ( $srev->getRevId() == $id && !$article->stableVersionIsSynced() ); |
| 93 | + } else { // views |
| 94 | + $flags = $oldFlags; |
| 95 | + $reviewIncludes = false; // re-review button not needed |
| 96 | + } |
| 97 | + |
| 98 | + # Disable form for unprivileged users |
| 99 | + $disabled = array(); |
| 100 | + if ( !$article->getTitle()->quickUserCan( 'review' ) || |
| 101 | + !$article->getTitle()->quickUserCan( 'edit' ) || |
| 102 | + !FlaggedRevs::userCanSetFlags( $this->user, $flags ) ) |
| 103 | + { |
| 104 | + $disabled = array( 'disabled' => 'disabled' ); |
| 105 | + } |
| 106 | + |
| 107 | + # Begin form... |
| 108 | + $reviewTitle = SpecialPage::getTitleFor( 'RevisionReview' ); |
| 109 | + $action = $reviewTitle->getLocalUrl( 'action=submit' ); |
| 110 | + $params = array( 'method' => 'post', 'action' => $action, 'id' => 'mw-fr-reviewform' ); |
| 111 | + $form = Xml::openElement( 'form', $params ); |
| 112 | + $form .= Xml::openElement( 'fieldset', |
| 113 | + array( 'class' => 'flaggedrevs_reviewform noprint' ) ); |
| 114 | + # Add appropriate legend text |
| 115 | + $legendMsg = $frev ? 'revreview-reflag' : 'revreview-flag'; |
| 116 | + $form .= Xml::openElement( 'legend', array( 'id' => 'mw-fr-reviewformlegend' ) ); |
| 117 | + $form .= "<strong>" . wfMsgHtml( $legendMsg ) . "</strong>"; |
| 118 | + $form .= Xml::closeElement( 'legend' ) . "\n"; |
| 119 | + # Show explanatory text |
| 120 | + $form .= $this->topNotice; |
| 121 | + # Show possible conflict warning msg |
| 122 | + if ( $this->refId ) { |
| 123 | + list( $u, $ts ) = |
| 124 | + FRUserActivity::getUserReviewingDiff( $this->refId, $this->rev->getId() ); |
| 125 | + } else { |
| 126 | + list( $u, $ts ) = FRUserActivity::getUserReviewingPage( $this->rev->getPage() ); |
| 127 | + } |
| 128 | + if ( $u !== null && $u != $this->user->getName() ) { |
| 129 | + $msg = $this->refId ? 'revreview-poss-conflict-c' : 'revreview-poss-conflict-p'; |
| 130 | + $form .= '<p><span class="fr-under-review">' . |
| 131 | + wfMsgExt( $msg, 'parseinline', $u, |
| 132 | + $wgLang->date( $ts, true ), $wgLang->time( $ts, true ) ) . |
| 133 | + '</span></p>'; |
| 134 | + } |
| 135 | + |
| 136 | + if ( $disabled ) { |
| 137 | + $form .= Xml::openElement( 'div', array( 'class' => 'fr-rating-controls-disabled', |
| 138 | + 'id' => 'fr-rating-controls-disabled' ) ); |
| 139 | + } else { |
| 140 | + $form .= Xml::openElement( 'div', array( 'class' => 'fr-rating-controls', |
| 141 | + 'id' => 'fr-rating-controls' ) ); |
| 142 | + } |
| 143 | + |
| 144 | + # Add main checkboxes/selects |
| 145 | + $form .= Xml::openElement( 'span', |
| 146 | + array( 'id' => 'mw-fr-ratingselects', 'class' => 'fr-rating-options' ) ); |
| 147 | + $form .= self::ratingInputs( $this->user, $flags, (bool)$disabled, (bool)$frev ); |
| 148 | + $form .= Xml::closeElement( 'span' ); |
| 149 | + |
| 150 | + # Get template/file version info as needed |
| 151 | + list( $templateIDs, $imageSHA1Keys ) = $this->getIncludeVersions(); |
| 152 | + # Convert these into flat string params |
| 153 | + list( $templateParams, $imageParams, $fileVersion ) = |
| 154 | + RevisionReviewForm::getIncludeParams( $article, $templateIDs, $imageSHA1Keys ); |
| 155 | + |
| 156 | + $form .= Xml::openElement( 'span', array( 'style' => 'white-space: nowrap;' ) ); |
| 157 | + # Hide comment input if needed |
| 158 | + if ( !$disabled ) { |
| 159 | + if ( count( FlaggedRevs::getTags() ) > 1 ) { |
| 160 | + $form .= "<br />"; // Don't put too much on one line |
| 161 | + } |
| 162 | + $form .= "<span id='mw-fr-commentbox' style='clear:both'>" . |
| 163 | + Xml::inputLabel( wfMsg( 'revreview-log' ), 'wpReason', 'wpReason', 40, '', |
| 164 | + array( 'class' => 'fr-comment-box' ) ) . "   </span>"; |
| 165 | + } |
| 166 | + # Determine if there will be reject button |
| 167 | + $rejectId = 0; |
| 168 | + if ( $this->refId == $article->getStable() && $id != $this->refId ) { |
| 169 | + $rejectId = $this->refId; // left rev must be stable and right one newer |
| 170 | + } |
| 171 | + # Add the submit buttons |
| 172 | + $form .= self::submitButtons( $rejectId, $frev, (bool)$disabled, $reviewIncludes ); |
| 173 | + # Show stability log if there is anything interesting... |
| 174 | + if ( $article->isPageLocked() ) { |
| 175 | + $form .= ' ' . FlaggedRevsXML::logToggle( 'revreview-log-toggle-show' ); |
| 176 | + } |
| 177 | + $form .= Xml::closeElement( 'span' ); |
| 178 | + # ..add the actual stability log body here |
| 179 | + if ( $article->isPageLocked() ) { |
| 180 | + $form .= FlaggedRevsXML::stabilityLogExcerpt( $article ); |
| 181 | + } |
| 182 | + $form .= Xml::closeElement( 'div' ) . "\n"; |
| 183 | + |
| 184 | + # Hidden params |
| 185 | + $form .= Html::hidden( 'title', $reviewTitle->getPrefixedText() ) . "\n"; |
| 186 | + $form .= Html::hidden( 'target', $article->getTitle()->getPrefixedDBKey() ) . "\n"; |
| 187 | + $form .= Html::hidden( 'refid', $this->refId ) . "\n"; |
| 188 | + $form .= Html::hidden( 'oldid', $id ) . "\n"; |
| 189 | + $form .= Html::hidden( 'action', 'submit' ) . "\n"; |
| 190 | + $form .= Html::hidden( 'wpEditToken', $this->user->editToken() ) . "\n"; |
| 191 | + $form .= Html::hidden( 'changetime', $reviewTime, |
| 192 | + array( 'id' => 'mw-fr-input-changetime' ) ); // id for JS |
| 193 | + # Add review parameters |
| 194 | + $form .= Html::hidden( 'templateParams', $templateParams ) . "\n"; |
| 195 | + $form .= Html::hidden( 'imageParams', $imageParams ) . "\n"; |
| 196 | + $form .= Html::hidden( 'fileVersion', $fileVersion ) . "\n"; |
| 197 | + # Special token to discourage fiddling... |
| 198 | + $checkCode = RevisionReviewForm::validationKey( |
| 199 | + $templateParams, $imageParams, $fileVersion, $id |
| 200 | + ); |
| 201 | + $form .= Html::hidden( 'validatedParams', $checkCode ) . "\n"; |
| 202 | + |
| 203 | + $form .= Xml::closeElement( 'fieldset' ); |
| 204 | + $form .= Xml::closeElement( 'form' ); |
| 205 | + |
| 206 | + return array( $form, true /* ok */ ); |
| 207 | + } |
| 208 | + |
| 209 | + /** |
| 210 | + * @param User $user |
| 211 | + * @param array $flags, selected flags |
| 212 | + * @param bool $disabled, form disabled |
| 213 | + * @param bool $reviewed, rev already reviewed |
| 214 | + * @returns string |
| 215 | + * Generates a main tag inputs (checkboxes/radios/selects) for review form |
| 216 | + */ |
| 217 | + protected static function ratingInputs( $user, $flags, $disabled, $reviewed ) { |
| 218 | + # Get all available tags for this page/user |
| 219 | + list( $labels, $minLevels ) = self::ratingFormTags( $user, $flags ); |
| 220 | + if ( $labels === false ) { |
| 221 | + $disabled = true; // a tag is unsettable |
| 222 | + } |
| 223 | + # If there are no tags, make one checkbox to approve/unapprove |
| 224 | + if ( FlaggedRevs::binaryFlagging() ) { |
| 225 | + return ''; |
| 226 | + } |
| 227 | + $items = array(); |
| 228 | + # Build rating form... |
| 229 | + if ( $disabled ) { |
| 230 | + // Display the value for each tag as text |
| 231 | + foreach ( FlaggedRevs::getTags() as $quality ) { |
| 232 | + $selected = isset( $flags[$quality] ) ? $flags[$quality] : 0; |
| 233 | + $items[] = FlaggedRevs::getTagMsg( $quality ) . ": " . |
| 234 | + FlaggedRevs::getTagValueMsg( $quality, $selected ); |
| 235 | + } |
| 236 | + } else { |
| 237 | + $size = count( $labels, 1 ) - count( $labels ); |
| 238 | + foreach ( $labels as $quality => $levels ) { |
| 239 | + $item = ''; |
| 240 | + $numLevels = count( $levels ); |
| 241 | + $minLevel = $minLevels[$quality]; |
| 242 | + # Determine the level selected by default |
| 243 | + if ( !empty( $flags[$quality] ) && isset( $levels[$flags[$quality]] ) ) { |
| 244 | + $selected = $flags[$quality]; // valid non-zero value |
| 245 | + } else { |
| 246 | + $selected = $minLevel; |
| 247 | + } |
| 248 | + # Show label as needed |
| 249 | + if ( !FlaggedRevs::binaryFlagging() ) { |
| 250 | + $item .= Xml::tags( 'label', array( 'for' => "wp$quality" ), |
| 251 | + FlaggedRevs::getTagMsg( $quality ) ) . ":\n"; |
| 252 | + } |
| 253 | + # If the sum of qualities of all flags is above 6, use drop down boxes. |
| 254 | + # 6 is an arbitrary value choosen according to screen space and usability. |
| 255 | + if ( $size > 6 ) { |
| 256 | + $attribs = array( 'name' => "wp$quality", 'id' => "wp$quality", |
| 257 | + 'onchange' => "FlaggedRevsReview.updateRatingForm()" ); |
| 258 | + $item .= Xml::openElement( 'select', $attribs ) . "\n"; |
| 259 | + foreach ( $levels as $i => $name ) { |
| 260 | + $optionClass = array( 'class' => "fr-rating-option-$i" ); |
| 261 | + $item .= Xml::option( FlaggedRevs::getTagMsg( $name ), $i, |
| 262 | + ( $i == $selected ), $optionClass ) . "\n"; |
| 263 | + } |
| 264 | + $item .= Xml::closeElement( 'select' ) . "\n"; |
| 265 | + # If there are more than two levels, current user gets radio buttons |
| 266 | + } elseif ( $numLevels > 2 ) { |
| 267 | + foreach ( $levels as $i => $name ) { |
| 268 | + $attribs = array( 'class' => "fr-rating-option-$i", |
| 269 | + 'onchange' => "FlaggedRevsReview.updateRatingForm()" ); |
| 270 | + $item .= Xml::radioLabel( FlaggedRevs::getTagMsg( $name ), "wp$quality", |
| 271 | + $i, "wp$quality" . $i, ( $i == $selected ), $attribs ) . "\n"; |
| 272 | + } |
| 273 | + # Otherwise make checkboxes (two levels available for current user) |
| 274 | + } else if ( $numLevels == 2 ) { |
| 275 | + $i = $minLevel; |
| 276 | + $attribs = array( 'class' => "fr-rating-option-$i", |
| 277 | + 'onchange' => "FlaggedRevsReview.updateRatingForm()" ); |
| 278 | + $attribs = $attribs + array( 'value' => $i ); |
| 279 | + $item .= Xml::checkLabel( wfMsg( 'revreview-' . $levels[$i] ), |
| 280 | + "wp$quality", "wp$quality", ( $selected == $i ), $attribs ) . "\n"; |
| 281 | + } |
| 282 | + $items[] = $item; |
| 283 | + } |
| 284 | + } |
| 285 | + return implode( '   ', $items ); |
| 286 | + } |
| 287 | + |
| 288 | + protected static function ratingFormTags( $user, $selected ) { |
| 289 | + $labels = array(); |
| 290 | + $minLevels = array(); |
| 291 | + # Build up all levels available to user |
| 292 | + foreach ( FlaggedRevs::getDimensions() as $tag => $levels ) { |
| 293 | + if ( isset( $selected[$tag] ) && |
| 294 | + !FlaggedRevs::userCanSetTag( $user, $tag, $selected[$tag] ) ) |
| 295 | + { |
| 296 | + return array( false, false ); // form will have to be disabled |
| 297 | + } |
| 298 | + $labels[$tag] = array(); // applicable tag levels |
| 299 | + $minLevels[$tag] = false; // first non-zero level number |
| 300 | + foreach ( $levels as $i => $msg ) { |
| 301 | + # Some levels may be restricted or not applicable... |
| 302 | + if ( !FlaggedRevs::userCanSetTag( $user, $tag, $i ) ) { |
| 303 | + continue; // skip this level |
| 304 | + } else if ( $i > 0 && !$minLevels[$tag] ) { |
| 305 | + $minLevels[$tag] = $i; // first non-zero level number |
| 306 | + } |
| 307 | + $labels[$tag][$i] = $msg; // set label |
| 308 | + } |
| 309 | + if ( !$minLevels[$tag] ) { |
| 310 | + return array( false, false ); // form will have to be disabled |
| 311 | + } |
| 312 | + } |
| 313 | + return array( $labels, $minLevels ); |
| 314 | + } |
| 315 | + |
| 316 | + /** |
| 317 | + * Generates review form submit buttons |
| 318 | + * @param int $rejectId left rev ID for "reject" on diffs |
| 319 | + * @param FlaggedRevision $frev, the flagged revision, if any |
| 320 | + * @param bool $disabled, is the form disabled? |
| 321 | + * @param bool $reviewIncludes, force the review button to be usable? |
| 322 | + * @returns string |
| 323 | + */ |
| 324 | + protected static function submitButtons( |
| 325 | + $rejectId, $frev, $disabled, $reviewIncludes = false |
| 326 | + ) { |
| 327 | + $disAttrib = array( 'disabled' => 'disabled' ); |
| 328 | + # ACCEPT BUTTON: accept a revision |
| 329 | + # We may want to re-review to change: |
| 330 | + # (a) notes (b) tags (c) pending template/file changes |
| 331 | + if ( FlaggedRevs::binaryFlagging() ) { // just the buttons |
| 332 | + $applicable = ( !$frev || $reviewIncludes ); // no tags/notes |
| 333 | + $needsChange = false; // no state change possible |
| 334 | + } else { // buttons + ratings |
| 335 | + $applicable = true; // tags might change |
| 336 | + $needsChange = ( $frev && !$reviewIncludes ); |
| 337 | + } |
| 338 | + $s = Xml::submitButton( wfMsgHtml( 'revreview-submit-review' ), |
| 339 | + array( |
| 340 | + 'name' => 'wpApprove', |
| 341 | + 'id' => 'mw-fr-submit-accept', |
| 342 | + 'accesskey' => wfMsg( 'revreview-ak-review' ), |
| 343 | + 'title' => wfMsg( 'revreview-tt-flag' ) . ' [' . |
| 344 | + wfMsg( 'revreview-ak-review' ) . ']' |
| 345 | + ) + ( ( $disabled || !$applicable ) ? $disAttrib : array() ) |
| 346 | + ); |
| 347 | + # REJECT BUTTON: revert from a pending revision to the stable |
| 348 | + if ( $rejectId ) { |
| 349 | + $s .= ' '; |
| 350 | + $s .= Xml::submitButton( wfMsgHtml( 'revreview-submit-reject' ), |
| 351 | + array( |
| 352 | + 'name' => 'wpReject', |
| 353 | + 'id' => 'mw-fr-submit-reject', |
| 354 | + 'title' => wfMsg( 'revreview-tt-reject' ), |
| 355 | + ) + ( $disabled ? $disAttrib : array() ) |
| 356 | + ); |
| 357 | + } |
| 358 | + # UNACCEPT BUTTON: revoke a revisions acceptance |
| 359 | + # Hide if revision is not flagged |
| 360 | + $s .= ' '; |
| 361 | + $s .= Xml::submitButton( wfMsgHtml( 'revreview-submit-unreview' ), |
| 362 | + array( |
| 363 | + 'name' => 'wpUnapprove', |
| 364 | + 'id' => 'mw-fr-submit-unaccept', |
| 365 | + 'title' => wfMsg( 'revreview-tt-unflag' ), |
| 366 | + 'style' => $frev ? '' : 'display:none' |
| 367 | + ) + ( $disabled ? $disAttrib : array() ) |
| 368 | + ); |
| 369 | + // Disable buttons unless state changes in some cases (non-JS compatible) |
| 370 | + $s .= "<script type=\"text/javascript\"> |
| 371 | + var jsReviewNeedsChange = " . (int)$needsChange . "</script>"; |
| 372 | + return $s; |
| 373 | + } |
| 374 | + |
| 375 | + protected function getIncludeVersions() { |
| 376 | + # Do we need to get inclusion IDs from parser output? |
| 377 | + if ( $this->templateIDs === null || $this->imageSHA1Keys === null ) { |
| 378 | + list( $this->templateIDs, $this->imageSHA1Keys ) = |
| 379 | + RevisionReviewForm::currentIncludeVersions( $this->article, $this->rev ); |
| 380 | + } |
| 381 | + return array( $this->templateIDs, $this->imageSHA1Keys ); |
| 382 | + } |
| 383 | +} |
Property changes on: trunk/extensions/FlaggedRevs/presentation/RevisionReviewFormGUI.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 384 | + native |
Index: trunk/extensions/FlaggedRevs/presentation/RejectConfirmationFormGUI.php |
— | — | @@ -0,0 +1,150 @@ |
| 2 | +<?php |
| 3 | +# (c) Aaron Schulz 2011 GPL |
| 4 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 5 | + echo "FlaggedRevs extension\n"; |
| 6 | + exit( 1 ); |
| 7 | +} |
| 8 | +/** |
| 9 | + * Reject confirmation review form UI |
| 10 | + */ |
| 11 | +class RejectConfirmationFormGUI { |
| 12 | + protected $form, $oldRev, $newRev; |
| 13 | + |
| 14 | + public function __construct( RevisionReviewForm $form ) { |
| 15 | + $this->form = $form; |
| 16 | + $this->newRev = Revision::newFromTitle( $form->getPage(), $form->getOldId() ); |
| 17 | + $this->oldRev = Revision::newFromTitle( $form->getPage(), $form->getRefId() ); |
| 18 | + } |
| 19 | + |
| 20 | + /** |
| 21 | + * Output the "are you sure you want to reject this" form |
| 22 | + * |
| 23 | + * A bit hacky, but we don't have a way to pass more complicated |
| 24 | + * UI things back up, since RevisionReview expects either true |
| 25 | + * or a string message key |
| 26 | + * @return Array (html string, error string or true) |
| 27 | + */ |
| 28 | + public function getHtml() { |
| 29 | + global $wgLang, $wgContLang; |
| 30 | + $oldRev = $this->oldRev; // convenience |
| 31 | + $newRev = $this->newRev; // convenience |
| 32 | + # Do not mess with archived/deleted revisions |
| 33 | + if ( !$oldRev || $newRev->isDeleted( Revision::DELETED_TEXT ) ) { |
| 34 | + return array( '', 'review_bad_oldid' ); |
| 35 | + } elseif ( !$newRev || $newRev->isDeleted( Revision::DELETED_TEXT ) ) { |
| 36 | + return array( '', 'review_bad_oldid' ); |
| 37 | + } |
| 38 | + |
| 39 | + $form = '<div class="plainlinks">'; |
| 40 | + |
| 41 | + $dbr = wfGetDB( DB_SLAVE ); |
| 42 | + $res = $dbr->select( 'revision', |
| 43 | + Revision::selectFields(), |
| 44 | + array( |
| 45 | + 'rev_page' => $oldRev->getPage(), |
| 46 | + 'rev_id > ' . $dbr->addQuotes( $oldRev->getId() ), |
| 47 | + 'rev_id <= ' . $dbr->addQuotes( $newRev->getId() ) |
| 48 | + ), |
| 49 | + __METHOD__, |
| 50 | + array( 'LIMIT' => 251 ) // sanity check |
| 51 | + ); |
| 52 | + if ( !$dbr->numRows( $res ) ) { |
| 53 | + return array( '', 'review_bad_oldid' ); |
| 54 | + } elseif ( $dbr->numRows( $res ) > 250 ) { |
| 55 | + return array( '', 'review_reject_excessive' ); |
| 56 | + } |
| 57 | + |
| 58 | + $rejectIds = $rejectAuthors = array(); |
| 59 | + $contribs = SpecialPage::getTitleFor( 'Contributions' )->getPrefixedText(); |
| 60 | + foreach ( $res as $row ) { |
| 61 | + $rev = new Revision( $row ); |
| 62 | + $rejectIds[] = $rev->getId(); |
| 63 | + $rejectAuthors[] = $rev->isDeleted( Revision::DELETED_USER ) |
| 64 | + ? wfMsg( 'rev-deleted-user' ) |
| 65 | + : "[[{$contribs}/{$rev->getUserText()}|{$rev->getUserText()}]]"; |
| 66 | + } |
| 67 | + $rejectAuthors = array_values( array_unique( $rejectAuthors ) ); |
| 68 | + |
| 69 | + // List of revisions being undone... |
| 70 | + $form .= wfMsgExt( 'revreview-reject-text-list', 'parseinline', |
| 71 | + $wgLang->formatNum( count( $rejectIds ) ) ); |
| 72 | + $form .= '<ul>'; |
| 73 | + // FIXME: we need a generic revision list class...this is bullshit |
| 74 | + $spRevDelete = SpecialPage::getPage( 'RevisionReview' ); |
| 75 | + $spRevDelete->skin = $this->form->getUser()->getSkin(); // XXX |
| 76 | + $list = new RevDel_RevisionList( $spRevDelete, $oldRev->getTitle(), $rejectIds ); |
| 77 | + for ( $list->reset(); $list->current(); $list->next() ) { |
| 78 | + $item = $list->current(); |
| 79 | + if ( $item->canView() ) { |
| 80 | + $form .= $item->getHTML(); |
| 81 | + } |
| 82 | + } |
| 83 | + $form .= '</ul>'; |
| 84 | + if ( $newRev->isCurrent() ) { |
| 85 | + // Revision this will revert to (when reverting the top X revs)... |
| 86 | + $form .= wfMsgExt( 'revreview-reject-text-revto', 'parseinline', |
| 87 | + $oldRev->getTitle()->getPrefixedDBKey(), $oldRev->getId(), |
| 88 | + $wgLang->timeanddate( $oldRev->getTimestamp(), true ) |
| 89 | + ); |
| 90 | + } |
| 91 | + |
| 92 | + $comment = $this->form->getComment(); // convenience |
| 93 | + // Determine the default edit summary... |
| 94 | + $oldRevAuthor = $oldRev->isDeleted( Revision::DELETED_USER ) |
| 95 | + ? wfMsg( 'rev-deleted-user' ) |
| 96 | + : $oldRev->getUserText(); |
| 97 | + // NOTE: *-cur msg wording not safe for (unlikely) edit auto-merge |
| 98 | + $msg = $newRev->isCurrent() |
| 99 | + ? 'revreview-reject-summary-cur' |
| 100 | + : 'revreview-reject-summary-old'; |
| 101 | + $defaultSummary = wfMsgExt( $msg, array( 'parsemag', 'content' ), |
| 102 | + $wgContLang->formatNum( count( $rejectIds ) ), |
| 103 | + $wgContLang->listToText( $rejectAuthors ), |
| 104 | + $oldRev->getId(), |
| 105 | + $oldRevAuthor ); |
| 106 | + // If the message is too big, then fallback to the shorter one |
| 107 | + $colonSeparator = wfMsgForContent( 'colon-separator' ); |
| 108 | + $maxLen = 255 - count( $colonSeparator ) - count( $comment ); |
| 109 | + if ( strlen( $defaultSummary ) > $maxLen ) { |
| 110 | + $msg = $newRev->isCurrent() |
| 111 | + ? 'revreview-reject-summary-cur-short' |
| 112 | + : 'revreview-reject-summary-old-short'; |
| 113 | + $defaultSummary = wfMsgExt( $msg, array( 'parsemag', 'content' ), |
| 114 | + $wgContLang->formatNum( count( $rejectIds ) ), |
| 115 | + $oldRev->getId(), |
| 116 | + $oldRevAuthor ); |
| 117 | + } |
| 118 | + // Append any review comment... |
| 119 | + if ( $comment != '' ) { |
| 120 | + if ( $defaultSummary != '' ) { |
| 121 | + $defaultSummary .= $colonSeparator; |
| 122 | + } |
| 123 | + $defaultSummary .= $comment; |
| 124 | + } |
| 125 | + |
| 126 | + $form .= '</div>'; |
| 127 | + |
| 128 | + $skin = $this->form->getUser()->getSkin(); |
| 129 | + $reviewTitle = SpecialPage::getTitleFor( 'RevisionReview' ); |
| 130 | + $form .= Xml::openElement( 'form', |
| 131 | + array( 'method' => 'POST', 'action' => $reviewTitle->getFullUrl() ) ); |
| 132 | + $form .= Html::hidden( 'action', 'reject' ); |
| 133 | + $form .= Html::hidden( 'wpReject', 1 ); |
| 134 | + $form .= Html::hidden( 'wpRejectConfirm', 1 ); |
| 135 | + $form .= Html::hidden( 'oldid', $this->form->getOldId() ); |
| 136 | + $form .= Html::hidden( 'refid', $this->form->getRefId() ); |
| 137 | + $form .= Html::hidden( 'target', $oldRev->getTitle()->getPrefixedDBKey() ); |
| 138 | + $form .= Html::hidden( 'wpEditToken', $this->form->getUser()->editToken() ); |
| 139 | + $form .= Html::hidden( 'changetime', $newRev->getTimestamp() ); |
| 140 | + $form .= Xml::inputLabel( wfMsg( 'revreview-reject-summary' ), 'wpReason', |
| 141 | + 'wpReason', 120, $defaultSummary ) . "<br />"; |
| 142 | + $form .= Html::input( 'wpSubmit', wfMsg( 'revreview-reject-confirm' ), 'submit' ); |
| 143 | + $form .= ' '; |
| 144 | + $form .= $skin->link( $this->form->getPage(), wfMsg( 'revreview-reject-cancel' ), |
| 145 | + array( 'onClick' => 'history.back()' ), |
| 146 | + array( 'oldid' => $this->form->getRefId(), 'diff' => $this->form->getOldId() ) ); |
| 147 | + $form .= Xml::closeElement( 'form' ); |
| 148 | + |
| 149 | + return array( $form, true ); |
| 150 | + } |
| 151 | +} |
Property changes on: trunk/extensions/FlaggedRevs/presentation/RejectConfirmationFormGUI.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 152 | + native |
Index: trunk/extensions/FlaggedRevs/FlaggedArticleView.php |
— | — | @@ -1070,24 +1070,23 @@ |
1071 | 1071 | } |
1072 | 1072 | # Build the review form as needed |
1073 | 1073 | if ( $rev && ( !$this->diffRevs || $this->isReviewableDiff ) ) { |
| 1074 | + $form = new RevisionReviewFormGUI( $wgUser, $this->article, $rev ); |
| 1075 | + # Tag default and "reject" button depend on context |
| 1076 | + $form->setDiffLeftId( $this->diffRevs['old'] ); |
| 1077 | + # Review notice box goes in top of form |
| 1078 | + $form->setTopNotice( $this->diffNoticeBox ); |
1074 | 1079 | # $wgOut may not already have the inclusion IDs, such as for diffonly=1. |
1075 | 1080 | # RevisionReviewForm will fetch them as needed however. |
1076 | | - $templateIDs = $fileSHA1Keys = null; |
1077 | 1081 | if ( $wgOut->getRevisionId() == $rev->getId() ) { |
1078 | | - $templateIDs = $wgOut->getTemplateIds(); |
1079 | | - $fileSHA1Keys = $wgOut->getImageTimeKeys(); |
| 1082 | + $form->setIncludeVersions( $wgOut->getTemplateIds(), $wgOut->getImageTimeKeys() ); |
1080 | 1083 | } |
1081 | | - # Review notice box goes in top of form |
1082 | | - $form = RevisionReviewForm::buildQuickReview( |
1083 | | - $wgUser, $this->article, $rev, $this->diffRevs['old'], |
1084 | | - $this->diffNoticeBox, $templateIDs, $fileSHA1Keys |
1085 | | - ); |
| 1084 | + list( $html, $status ) = $form->getHtml(); |
1086 | 1085 | # Diff action: place the form at the top of the page |
1087 | 1086 | if ( $this->diffRevs ) { |
1088 | | - $wgOut->prependHTML( $form ); |
| 1087 | + $wgOut->prependHTML( $html ); |
1089 | 1088 | # View action: place the form at the bottom of the page |
1090 | 1089 | } else { |
1091 | | - $data .= $form; |
| 1090 | + $data .= $html; |
1092 | 1091 | } |
1093 | 1092 | } |
1094 | 1093 | return true; |
Index: trunk/extensions/FlaggedRevs/FlaggedRevs.php |
— | — | @@ -249,7 +249,7 @@ |
250 | 250 | $wgAutoloadClasses['FlaggedRevsApiHooks'] = $dir . 'api/FlaggedRevsApi.hooks.php'; |
251 | 251 | $wgAutoloadClasses['FlaggedRevsUpdaterHooks'] = $dir . 'updater/FlaggedRevsUpdater.hooks.php'; |
252 | 252 | |
253 | | -# Object classes... |
| 253 | +# Data object classes... |
254 | 254 | $wgAutoloadClasses['FRExtraCacheUpdate'] = $dir . 'FRExtraCacheUpdate.php'; |
255 | 255 | $wgAutoloadClasses['FRExtraCacheUpdateJob'] = $dir . 'FRExtraCacheUpdate.php'; |
256 | 256 | $wgAutoloadClasses['FRSquidUpdate'] = $dir . 'FRExtraCacheUpdate.php'; |
— | — | @@ -258,11 +258,17 @@ |
259 | 259 | $wgAutoloadClasses['FlaggedArticleView'] = $dir . 'FlaggedArticleView.php'; |
260 | 260 | $wgAutoloadClasses['FlaggedArticle'] = $dir . 'FlaggedArticle.php'; |
261 | 261 | $wgAutoloadClasses['FlaggedRevision'] = $dir . 'FlaggedRevision.php'; |
262 | | -$wgAutoloadClasses['RevisionReviewForm'] = $dir . 'forms/RevisionReviewForm.php'; |
263 | | -$wgAutoloadClasses['PageStabilityForm'] = $dir . 'forms/PageStabilityForm.php'; |
264 | | -$wgAutoloadClasses['PageStabilityGeneralForm'] = $dir . 'forms/PageStabilityForm.php'; |
265 | | -$wgAutoloadClasses['PageStabilityProtectForm'] = $dir . 'forms/PageStabilityForm.php'; |
266 | 262 | |
| 263 | +# Business object classes |
| 264 | +$wgAutoloadClasses['RevisionReviewForm'] = $dir . 'business/RevisionReviewForm.php'; |
| 265 | +$wgAutoloadClasses['PageStabilityForm'] = $dir . 'business/PageStabilityForm.php'; |
| 266 | +$wgAutoloadClasses['PageStabilityGeneralForm'] = $dir . 'business/PageStabilityForm.php'; |
| 267 | +$wgAutoloadClasses['PageStabilityProtectForm'] = $dir . 'business/PageStabilityForm.php'; |
| 268 | + |
| 269 | +# Presentation classes... |
| 270 | +$wgAutoloadClasses['RevisionReviewFormGUI'] = $dir . 'presentation/RevisionReviewFormGUI.php'; |
| 271 | +$wgAutoloadClasses['RejectConfirmationFormGUI'] = $dir . 'presentation/RejectConfirmationFormGUI.php'; |
| 272 | + |
267 | 273 | # Load main i18n file and special page alias file |
268 | 274 | $wgExtensionMessagesFiles['FlaggedRevs'] = $langDir . 'FlaggedRevs.i18n.php'; |
269 | 275 | $wgExtensionAliasesFiles['FlaggedRevs'] = $langDir . 'FlaggedRevs.alias.php'; |