Index: trunk/phase3/maintenance/language/messages.inc |
— | — | @@ -898,6 +898,19 @@ |
899 | 899 | 'filerevert-success', |
900 | 900 | 'filerevert-badversion', |
901 | 901 | ), |
| 902 | + 'filedelete' => array( |
| 903 | + 'filedelete', |
| 904 | + 'filedelete-legend', |
| 905 | + 'filedelete-intro', |
| 906 | + 'filedelete-intro-old', |
| 907 | + 'filedelete-comment', |
| 908 | + 'filedelete-submit', |
| 909 | + 'filedelete-success', |
| 910 | + 'filedelete-success-old', |
| 911 | + 'filedelete-nofile', |
| 912 | + 'filedelete-nofile-old', |
| 913 | + 'filedelete-iscurrent', |
| 914 | + ), |
902 | 915 | 'mimesearch' => array( |
903 | 916 | 'mimesearch', |
904 | 917 | 'mimesearch-summary', |
— | — | @@ -2241,6 +2254,7 @@ |
2242 | 2255 | 'licenses' => '', |
2243 | 2256 | 'imagelist' => 'Image list', |
2244 | 2257 | 'filerevert' => 'File reversion', |
| 2258 | + 'filedelete' => 'File deletion', |
2245 | 2259 | 'mimesearch' => 'MIME search', |
2246 | 2260 | 'unwatchedpages' => 'Unwatched pages', |
2247 | 2261 | 'listredirects' => 'List redirects', |
— | — | @@ -2383,4 +2397,4 @@ |
2384 | 2398 | 'enotif_subject', |
2385 | 2399 | 'enotif_body', |
2386 | 2400 | 'allmessagesnotsupportedDB', |
2387 | | -); |
| 2401 | +); |
\ No newline at end of file |
Index: trunk/phase3/includes/ImagePage.php |
— | — | @@ -480,141 +480,23 @@ |
481 | 481 | $wgOut->addHTML( "</ul>\n" ); |
482 | 482 | } |
483 | 483 | |
484 | | - function delete() |
485 | | - { |
486 | | - global $wgUser, $wgOut, $wgRequest; |
487 | | - |
488 | | - if ( !$this->img->exists() || !$this->img->isLocal() ) { |
489 | | - # Use standard article deletion |
| 484 | + /** |
| 485 | + * Delete the file, or an earlier version of it |
| 486 | + */ |
| 487 | + public function delete() { |
| 488 | + if( !$this->img->exists() || !$this->img->isLocal() ) { |
| 489 | + // Standard article deletion |
490 | 490 | Article::delete(); |
491 | 491 | return; |
492 | 492 | } |
493 | | - |
494 | | - $confirm = $wgRequest->wasPosted(); |
495 | | - $reason = $wgRequest->getVal( 'wpReason' ); |
496 | | - $image = $wgRequest->getVal( 'image' ); |
497 | | - $oldimage = $wgRequest->getVal( 'oldimage' ); |
498 | | - |
499 | | - # Only sysops can delete images. Previously ordinary users could delete |
500 | | - # old revisions, but this is no longer the case. |
501 | | - if ( !$wgUser->isAllowed('delete') ) { |
502 | | - $wgOut->permissionRequired( 'delete' ); |
503 | | - return; |
504 | | - } |
505 | | - if ( $wgUser->isBlocked() ) { |
506 | | - $wgOut->blockedPage(); |
507 | | - return; |
508 | | - } |
509 | | - if ( wfReadOnly() ) { |
510 | | - $wgOut->readOnlyPage(); |
511 | | - return; |
512 | | - } |
513 | | - |
514 | | - # Better double-check that it hasn't been deleted yet! |
515 | | - $wgOut->setPagetitle( wfMsg( 'confirmdelete' ) ); |
516 | | - if ( ( !is_null( $image ) ) |
517 | | - && ( '' == trim( $image ) ) ) { |
518 | | - $wgOut->showFatalError( wfMsg( 'cannotdelete' ) ); |
519 | | - return; |
520 | | - } |
521 | | - |
522 | | - # Deleting old images doesn't require confirmation |
523 | | - if ( !is_null( $oldimage ) || $confirm ) { |
524 | | - if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) { |
525 | | - $this->doDeleteImage( $reason ); |
526 | | - } else { |
527 | | - $wgOut->showFatalError( wfMsg( 'sessionfailure' ) ); |
528 | | - } |
529 | | - return; |
530 | | - } |
531 | | - |
532 | | - if ( !is_null( $image ) ) { |
533 | | - $q = '&image=' . urlencode( $image ); |
534 | | - } else if ( !is_null( $oldimage ) ) { |
535 | | - $q = '&oldimage=' . urlencode( $oldimage ); |
536 | | - } else { |
537 | | - $q = ''; |
538 | | - } |
539 | | - return $this->confirmDelete( $q, $wgRequest->getText( 'wpReason' ) ); |
| 493 | + $deleter = new FileDeleteForm( $this->img ); |
| 494 | + $deleter->execute(); |
540 | 495 | } |
541 | 496 | |
542 | | - /* |
543 | | - * Delete an image. |
544 | | - * Called doDeleteImage() not doDelete() so that Article::delete() doesn't |
545 | | - * call back to here. |
546 | | - * |
547 | | - * @param $reason User provided reason for deletion. |
548 | | - */ |
549 | | - function doDeleteImage( $reason ) { |
550 | | - global $wgOut, $wgRequest; |
551 | | - |
552 | | - $oldimage = $wgRequest->getVal( 'oldimage' ); |
553 | | - |
554 | | - if ( !is_null( $oldimage ) ) { |
555 | | - if ( strlen( $oldimage ) < 16 ) { |
556 | | - $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) ); |
557 | | - return; |
558 | | - } |
559 | | - if( strpos( $oldimage, '/' ) !== false || strpos( $oldimage, '\\' ) !== false ) { |
560 | | - $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars($oldimage) ); |
561 | | - return; |
562 | | - } |
563 | | - $status = $this->doDeleteOldImage( $oldimage ); |
564 | | - $deleted = $oldimage; |
565 | | - } else { |
566 | | - $status = $this->img->delete( $reason ); |
567 | | - if ( !$status->isGood() ) { |
568 | | - // Warning or error |
569 | | - $wgOut->addWikiText( $status->getWikiText( 'filedeleteerror-short', 'filedeleteerror-long' ) ); |
570 | | - } |
571 | | - if ( $status->ok ) { |
572 | | - # Image itself is now gone, and database is cleaned. |
573 | | - # Now we remove the image description page. |
574 | | - $article = new Article( $this->mTitle ); |
575 | | - $article->doDeleteArticle( $reason ); # ignore errors |
576 | | - $deleted = $this->img->getName(); |
577 | | - } |
578 | | - } |
579 | | - |
580 | | - $wgOut->setRobotpolicy( 'noindex,nofollow' ); |
581 | | - |
582 | | - if ( !$status->ok ) { |
583 | | - // Fatal error flagged |
584 | | - $wgOut->setPagetitle( wfMsg( 'errorpagetitle' ) ); |
585 | | - $wgOut->returnToMain( false, $this->mTitle->getPrefixedText() ); |
586 | | - } else { |
587 | | - // Operation completed |
588 | | - $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); |
589 | | - $loglink = '[[Special:Log/delete|' . wfMsg( 'deletionlog' ) . ']]'; |
590 | | - $text = wfMsg( 'deletedtext', $deleted, $loglink ); |
591 | | - $wgOut->addWikiText( $text ); |
592 | | - $wgOut->returnToMain( false, $this->mTitle->getPrefixedText() ); |
593 | | - } |
594 | | - } |
595 | | - |
596 | 497 | /** |
597 | | - * Delete an old revision of an image, |
598 | | - * @return FileRepoStatus |
599 | | - */ |
600 | | - function doDeleteOldImage( $oldimage ) { |
601 | | - global $wgOut; |
602 | | - |
603 | | - $status = $this->img->deleteOld( $oldimage, '' ); |
604 | | - if( !$status->isGood() ) { |
605 | | - $wgOut->addWikiText( $status->getWikiText( 'filedeleteerror-short', 'filedeleteerror-long' ) ); |
606 | | - } |
607 | | - if ( $status->ok ) { |
608 | | - # Log the deletion |
609 | | - $log = new LogPage( 'delete' ); |
610 | | - $log->addEntry( 'delete', $this->mTitle, wfMsg('deletedrevision',$oldimage) ); |
611 | | - } |
612 | | - return $status; |
613 | | - } |
614 | | - |
615 | | - /** |
616 | 498 | * Revert the file to an earlier version |
617 | 499 | */ |
618 | | - function revert() { |
| 500 | + public function revert() { |
619 | 501 | $reverter = new FileRevertForm( $this->img ); |
620 | 502 | $reverter->execute(); |
621 | 503 | } |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -101,6 +101,7 @@ |
102 | 102 | 'ImagePage' => 'includes/ImagePage.php', |
103 | 103 | 'ImageHistoryList' => 'includes/ImagePage.php', |
104 | 104 | 'ImageRemote' => 'includes/ImageRemote.php', |
| 105 | + 'FileDeleteForm' => 'includes/FileDeleteForm.php', |
105 | 106 | 'FileRevertForm' => 'includes/FileRevertForm.php', |
106 | 107 | 'Job' => 'includes/JobQueue.php', |
107 | 108 | 'EmaillingJob' => 'includes/EmaillingJob.php', |
— | — | @@ -383,7 +384,4 @@ |
384 | 385 | require( $file ); |
385 | 386 | } |
386 | 387 | } |
387 | | -} |
388 | | - |
389 | | - |
390 | | - |
| 388 | +} |
\ No newline at end of file |
Index: trunk/phase3/includes/FileDeleteForm.php |
— | — | @@ -0,0 +1,202 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * File deletion user interface |
| 6 | + * |
| 7 | + * @addtogroup Media |
| 8 | + * @author Rob Church <robchur@gmail.com> |
| 9 | + */ |
| 10 | +class FileDeleteForm { |
| 11 | + |
| 12 | + private $title = null; |
| 13 | + private $file = null; |
| 14 | + private $oldimage = ''; |
| 15 | + |
| 16 | + /** |
| 17 | + * Constructor |
| 18 | + * |
| 19 | + * @param File $file File we're deleting |
| 20 | + */ |
| 21 | + public function __construct( $file ) { |
| 22 | + $this->title = $file->getTitle(); |
| 23 | + $this->file = $file; |
| 24 | + } |
| 25 | + |
| 26 | + /** |
| 27 | + * Fulfil the request; shows the form or deletes the file, |
| 28 | + * pending authentication, confirmation, etc. |
| 29 | + */ |
| 30 | + public function execute() { |
| 31 | + global $wgOut, $wgRequest, $wgUser, $wgLang, $wgServer; |
| 32 | + $this->setHeaders(); |
| 33 | + |
| 34 | + if( wfReadOnly() ) { |
| 35 | + $wgOut->readOnlyPage(); |
| 36 | + return; |
| 37 | + } elseif( !$wgUser->isLoggedIn() ) { |
| 38 | + $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' ); |
| 39 | + return; |
| 40 | + } elseif( !$wgUser->isAllowed( 'delete' ) ) { |
| 41 | + $wgOut->permissionError( 'delete' ); |
| 42 | + return; |
| 43 | + } elseif( $wgUser->isBlocked() ) { |
| 44 | + $wgOut->blockedPage(); |
| 45 | + return; |
| 46 | + } |
| 47 | + |
| 48 | + $this->oldimage = $wgRequest->getText( 'oldimage', false ); |
| 49 | + $token = $wgRequest->getText( 'wpEditToken' ); |
| 50 | + if( $this->oldimage && !$this->isValidOldSpec() ) { |
| 51 | + $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars( $this->oldimage ) ); |
| 52 | + return; |
| 53 | + } |
| 54 | + |
| 55 | + if( !$this->haveDeletableFile() ) { |
| 56 | + $wgOut->addHtml( $this->prepareMessage( 'filedelete-nofile' ) ); |
| 57 | + $wgOut->addReturnTo( $this->title ); |
| 58 | + return; |
| 59 | + } |
| 60 | + |
| 61 | + // Don't allow accidental deletion of a single file revision |
| 62 | + // if this is, in fact, the current revision; things might break |
| 63 | + if( $this->oldimage && $this->file->getTimestamp() == $this->getTimestamp() ) { |
| 64 | + $wgOut->addHtml( wfMsgExt( 'filedelete-iscurrent', 'parse' ) ); |
| 65 | + $wgOut->addReturnTo( $this->title ); |
| 66 | + return; |
| 67 | + } |
| 68 | + |
| 69 | + // Perform the deletion if appropriate |
| 70 | + if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $token, $this->oldimage ) ) { |
| 71 | + $comment = $wgRequest->getText( 'wpComment' ); |
| 72 | + if( $this->oldimage ) { |
| 73 | + $status = $this->file->deleteOld( $this->oldimage, $comment ); |
| 74 | + if( $status->ok ) { |
| 75 | + // Need to do a log item |
| 76 | + $log = new LogPage( 'delete' ); |
| 77 | + $log->addEntry( 'delete', $this->title, wfMsg( 'deletedrevision' , $this->oldimage ) ); |
| 78 | + } |
| 79 | + } else { |
| 80 | + $status = $this->file->delete( $comment ); |
| 81 | + if( $status->ok ) { |
| 82 | + // Need to delete the associated article |
| 83 | + $article = new Article( $this->title ); |
| 84 | + $article->doDeleteArticle( $comment ); |
| 85 | + } |
| 86 | + } |
| 87 | + if( !$status->isGood() ) |
| 88 | + $wgOut->addWikiText( $status->getWikiText( 'filedeleteerror-short', 'filedeleteerror-long' ) ); |
| 89 | + if( $status->ok ) { |
| 90 | + $wgOut->addHtml( $this->prepareMessage( 'filedelete-success' ) ); |
| 91 | + // Return to the main page if we just deleted all versions of the |
| 92 | + // file, otherwise go back to the description page |
| 93 | + $wgOut->addReturnTo( $this->oldimage ? $this->title : Title::newMainPage() ); |
| 94 | + } |
| 95 | + return; |
| 96 | + } |
| 97 | + |
| 98 | + // Show the form |
| 99 | + $this->showForm(); |
| 100 | + } |
| 101 | + |
| 102 | + /** |
| 103 | + * Show the confirmation form |
| 104 | + */ |
| 105 | + private function showForm() { |
| 106 | + global $wgOut, $wgUser, $wgRequest, $wgLang, $wgContLang, $wgServer; |
| 107 | + |
| 108 | + $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction() ) ); |
| 109 | + $form .= Xml::hidden( 'wpEditToken', $wgUser->editToken( $this->oldimage ) ); |
| 110 | + $form .= '<fieldset><legend>' . wfMsgHtml( 'filedelete-legend' ) . '</legend>'; |
| 111 | + $form .= $this->prepareMessage( 'filedelete-intro' ); |
| 112 | + |
| 113 | + $form .= '<p>' . Xml::inputLabel( wfMsg( 'filedelete-comment' ), 'wpComment', 'wpComment', 60 ) . '</p>'; |
| 114 | + $form .= '<p>' . Xml::submitButton( wfMsg( 'filedelete-submit' ) ) . '</p>'; |
| 115 | + $form .= '</fieldset>'; |
| 116 | + $form .= '</form>'; |
| 117 | + |
| 118 | + $wgOut->addHtml( $form ); |
| 119 | + } |
| 120 | + |
| 121 | + /** |
| 122 | + * Prepare a message referring to the file being deleted, |
| 123 | + * showing an appropriate message depending upon whether |
| 124 | + * it's a current file or an old version |
| 125 | + * |
| 126 | + * @param string $message Message base |
| 127 | + * @return string |
| 128 | + */ |
| 129 | + private function prepareMessage( $message ) { |
| 130 | + global $wgLang, $wgServer; |
| 131 | + if( $this->oldimage ) { |
| 132 | + return wfMsgExt( |
| 133 | + "{$message}-old", |
| 134 | + 'parse', |
| 135 | + $this->title->getText(), |
| 136 | + $wgLang->date( $this->getTimestamp() ), |
| 137 | + $wgLang->time( $this->getTimestamp() ), |
| 138 | + $wgServer . $this->file->getArchiveUrl( $this->oldimage ) |
| 139 | + ); |
| 140 | + } else { |
| 141 | + return wfMsgExt( |
| 142 | + $message, |
| 143 | + 'parse', |
| 144 | + $this->title->getText() |
| 145 | + ); |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + /** |
| 150 | + * Set headers, titles and other bits |
| 151 | + */ |
| 152 | + private function setHeaders() { |
| 153 | + global $wgOut; |
| 154 | + $wgOut->setPageTitle( wfMsg( 'filedelete', $this->title->getText() ) ); |
| 155 | + $wgOut->setRobotPolicy( 'noindex,nofollow' ); |
| 156 | + } |
| 157 | + |
| 158 | + /** |
| 159 | + * Is the provided `oldimage` value valid? |
| 160 | + * |
| 161 | + * @return bool |
| 162 | + */ |
| 163 | + private function isValidOldSpec() { |
| 164 | + return strlen( $this->oldimage ) >= 16 |
| 165 | + && strpos( $this->oldimage, '/' ) === false |
| 166 | + && strpos( $this->oldimage, '\\' ) === false; |
| 167 | + } |
| 168 | + |
| 169 | + /** |
| 170 | + * Could we delete the file specified? If an `oldimage` |
| 171 | + * value was provided, does it correspond to an |
| 172 | + * existing, local, old version of this file? |
| 173 | + * |
| 174 | + * @return bool |
| 175 | + */ |
| 176 | + private function haveDeletableFile() { |
| 177 | + $file = wfFindFile( $this->title, $this->oldimage ); |
| 178 | + return $file && $file->exists() && $file->isLocal(); |
| 179 | + } |
| 180 | + |
| 181 | + /** |
| 182 | + * Prepare the form action |
| 183 | + * |
| 184 | + * @return string |
| 185 | + */ |
| 186 | + private function getAction() { |
| 187 | + $q = array(); |
| 188 | + $q[] = 'action=delete'; |
| 189 | + if( $this->oldimage ) |
| 190 | + $q[] = 'oldimage=' . urlencode( $this->oldimage ); |
| 191 | + return $this->title->getLocalUrl( implode( '&', $q ) ); |
| 192 | + } |
| 193 | + |
| 194 | + /** |
| 195 | + * Extract the timestamp of the old version |
| 196 | + * |
| 197 | + * @return string |
| 198 | + */ |
| 199 | + private function getTimestamp() { |
| 200 | + return substr( $this->oldimage, 0, 14 ); |
| 201 | + } |
| 202 | + |
| 203 | +} |
\ No newline at end of file |
Property changes on: trunk/phase3/includes/FileDeleteForm.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 204 | + native |
Index: trunk/phase3/languages/messages/MessagesEn.php |
— | — | @@ -1502,6 +1502,19 @@ |
1503 | 1503 | 'filerevert-success' => "<span class=\"plainlinks\">'''[[Media:$1|$1]]''' has been reverted to the [$4 version as of $2, $3].</span>", |
1504 | 1504 | 'filerevert-badversion' => 'There is no previous local version of this file with the provided timestamp.', |
1505 | 1505 | |
| 1506 | +# File deletion |
| 1507 | +'filedelete' => 'Delete $1', |
| 1508 | +'filedelete-legend' => 'Delete file', |
| 1509 | +'filedelete-intro' => "You are deleting '''[[Media:$1|$1]]'''.", |
| 1510 | +'filedelete-intro-old' => "<span class=\"plainlinks\">You are deleting the version of '''[[Media:$1|$1]]''' as of [$4 $2, $3].</span>", |
| 1511 | +'filedelete-comment' => 'Comment:', |
| 1512 | +'filedelete-submit' => 'Delete', |
| 1513 | +'filedelete-success' => "'''$1''' has been deleted.", |
| 1514 | +'filedelete-success-old' => "<span class=\"plainlinks\">The version of '''[[Media:$1|$1]]''' as of $2, $3 has been deleted.</span>", |
| 1515 | +'filedelete-nofile' => "'''$1''' does not exist on this site.", |
| 1516 | +'filedelete-nofile-old' => "There is no version of '''$1''' dated $2, $3.", |
| 1517 | +'filedelete-iscurrent' => 'You are attempting to delete the most recent version of this file. Please revert to an older version first.', |
| 1518 | + |
1506 | 1519 | # MIME search |
1507 | 1520 | 'mimesearch' => 'MIME search', |
1508 | 1521 | 'mimesearch-summary' => 'This page enables the filtering of files for its MIME-type. Input: contenttype/subtype, e.g. <tt>image/jpeg</tt>.', |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -177,6 +177,8 @@ |
178 | 178 | * (bug 10937) Distinguish overwritten files in upload log |
179 | 179 | * Introduce 'ArticleUpdateBeforeRedirect' hook; see docs/hooks.txt for more |
180 | 180 | information |
| 181 | +* Confirmation is now required when deleting old versions of files |
| 182 | +* Users can now enter comments when deleting old versions of files |
181 | 183 | |
182 | 184 | == Bugfixes since 1.10 == |
183 | 185 | |