MediaWiki  REL1_20
FileDeleteForm.php
Go to the documentation of this file.
00001 <?php
00030 class FileDeleteForm {
00031 
00035         private $title = null;
00036 
00040         private $file = null;
00041 
00045         private $oldfile = null;
00046         private $oldimage = '';
00047 
00053         public function __construct( $file ) {
00054                 $this->title = $file->getTitle();
00055                 $this->file = $file;
00056         }
00057 
00062         public function execute() {
00063                 global $wgOut, $wgRequest, $wgUser, $wgUploadMaintenance;
00064 
00065                 $permissionErrors = $this->title->getUserPermissionsErrors( 'delete', $wgUser );
00066                 if ( count( $permissionErrors ) ) {
00067                         throw new PermissionsError( 'delete', $permissionErrors );
00068                 }
00069 
00070                 if ( wfReadOnly() ) {
00071                         throw new ReadOnlyError;
00072                 }
00073 
00074                 if ( $wgUploadMaintenance ) {
00075                         throw new ErrorPageError( 'filedelete-maintenance-title', 'filedelete-maintenance' );
00076                 }
00077 
00078                 $this->setHeaders();
00079 
00080                 $this->oldimage = $wgRequest->getText( 'oldimage', false );
00081                 $token = $wgRequest->getText( 'wpEditToken' );
00082                 # Flag to hide all contents of the archived revisions
00083                 $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed('suppressrevision');
00084 
00085                 if( $this->oldimage ) {
00086                         $this->oldfile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->title, $this->oldimage );
00087                 }
00088 
00089                 if( !self::haveDeletableFile($this->file, $this->oldfile, $this->oldimage) ) {
00090                         $wgOut->addHTML( $this->prepareMessage( 'filedelete-nofile' ) );
00091                         $wgOut->addReturnTo( $this->title );
00092                         return;
00093                 }
00094 
00095                 // Perform the deletion if appropriate
00096                 if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $token, $this->oldimage ) ) {
00097                         $deleteReasonList = $wgRequest->getText( 'wpDeleteReasonList' );
00098                         $deleteReason = $wgRequest->getText( 'wpReason' );
00099 
00100                         if ( $deleteReasonList == 'other' ) {
00101                                 $reason = $deleteReason;
00102                         } elseif ( $deleteReason != '' ) {
00103                                 // Entry from drop down menu + additional comment
00104                                 $reason = $deleteReasonList . wfMessage( 'colon-separator' )
00105                                         ->inContentLanguage()->text() . $deleteReason;
00106                         } else {
00107                                 $reason = $deleteReasonList;
00108                         }
00109 
00110                         $status = self::doDelete( $this->title, $this->file, $this->oldimage, $reason, $suppress, $wgUser );
00111 
00112                         if( !$status->isGood() ) {
00113                                 $wgOut->addHTML( '<h2>' . $this->prepareMessage( 'filedeleteerror-short' ) . "</h2>\n" );
00114                                 $wgOut->addWikiText( '<div class="error">' . $status->getWikiText( 'filedeleteerror-short', 'filedeleteerror-long' ) . '</div>' );
00115                         }
00116                         if( $status->ok ) {
00117                                 $wgOut->setPageTitle( wfMessage( 'actioncomplete' ) );
00118                                 $wgOut->addHTML( $this->prepareMessage( 'filedelete-success' ) );
00119                                 // Return to the main page if we just deleted all versions of the
00120                                 // file, otherwise go back to the description page
00121                                 $wgOut->addReturnTo( $this->oldimage ? $this->title : Title::newMainPage() );
00122 
00123                                 if ( $wgUser->isLoggedIn() && $wgRequest->getCheck( 'wpWatch' ) != $wgUser->isWatched( $this->title ) ) {
00124                                         if ( $wgRequest->getCheck( 'wpWatch' ) ) {
00125                                                 WatchAction::doWatch( $this->title, $wgUser );
00126                                         } else {
00127                                                 WatchAction::doUnwatch( $this->title, $wgUser );
00128                                         }
00129                                 }
00130                         }
00131                         return;
00132                 }
00133 
00134                 $this->showForm();
00135                 $this->showLogEntries();
00136         }
00137 
00149         public static function doDelete( &$title, &$file, &$oldimage, $reason, $suppress, User $user = null ) {
00150                 if ( $user === null ) {
00151                         global $wgUser;
00152                         $user = $wgUser;
00153                 }
00154 
00155                 if( $oldimage ) {
00156                         $page = null;
00157                         $status = $file->deleteOld( $oldimage, $reason, $suppress );
00158                         if( $status->ok ) {
00159                                 // Need to do a log item
00160                                 $logComment = wfMessage( 'deletedrevision', $oldimage )->inContentLanguage()->text();
00161                                 if( trim( $reason ) != '' ) {
00162                                         $logComment .= wfMessage( 'colon-separator' )
00163                                                 ->inContentLanguage()->text() . $reason;
00164                                 }
00165 
00166                                 $logtype = $suppress ? 'suppress' : 'delete';
00167 
00168                                 $logEntry = new ManualLogEntry( $logtype, 'delete' );
00169                                 $logEntry->setPerformer( $user );
00170                                 $logEntry->setTarget( $title );
00171                                 $logEntry->setComment( $logComment );
00172                                 $logid = $logEntry->insert();
00173                                 $logEntry->publish( $logid );
00174                         }
00175                 } else {
00176                         $status = Status::newFatal( 'cannotdelete',
00177                                 wfEscapeWikiText( $title->getPrefixedText() )
00178                         );
00179                         $page = WikiPage::factory( $title );
00180                         $dbw = wfGetDB( DB_MASTER );
00181                         try {
00182                                 // delete the associated article first
00183                                 $error = '';
00184                                 $deleteStatus = $page->doDeleteArticleReal( $reason, $suppress, 0, false, $error, $user );
00185                                 // doDeleteArticleReal() returns a non-fatal error status if the page
00186                                 // or revision is missing, so check for isOK() rather than isGood()
00187                                 if ( $deleteStatus->isOK() ) {
00188                                         $status = $file->delete( $reason, $suppress );
00189                                         if( $status->isOK() ) {
00190                                                 $dbw->commit( __METHOD__ );
00191                                         } else {
00192                                                 $dbw->rollback( __METHOD__ );
00193                                         }
00194                                 }
00195                         } catch ( MWException $e ) {
00196                                 // rollback before returning to prevent UI from displaying incorrect "View or restore N deleted edits?"
00197                                 $dbw->rollback( __METHOD__ );
00198                                 throw $e;
00199                         }
00200                 }
00201 
00202                 if ( $status->isOK() ) {
00203                         wfRunHooks( 'FileDeleteComplete', array( &$file, &$oldimage, &$page, &$user, &$reason ) );
00204                 }
00205 
00206                 return $status;
00207         }
00208 
00212         private function showForm() {
00213                 global $wgOut, $wgUser, $wgRequest;
00214 
00215                 if( $wgUser->isAllowed( 'suppressrevision' ) ) {
00216                         $suppress = "<tr id=\"wpDeleteSuppressRow\">
00217                                         <td></td>
00218                                         <td class='mw-input'><strong>" .
00219                                                 Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
00220                                                         'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '3' ) ) .
00221                                         "</strong></td>
00222                                 </tr>";
00223                 } else {
00224                         $suppress = '';
00225                 }
00226 
00227                 $checkWatch = $wgUser->getBoolOption( 'watchdeletion' ) || $wgUser->isWatched( $this->title );
00228                 $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction(),
00229                         'id' => 'mw-img-deleteconfirm' ) ) .
00230                         Xml::openElement( 'fieldset' ) .
00231                         Xml::element( 'legend', null, wfMessage( 'filedelete-legend' )->text() ) .
00232                         Html::hidden( 'wpEditToken', $wgUser->getEditToken( $this->oldimage ) ) .
00233                         $this->prepareMessage( 'filedelete-intro' ) .
00234                         Xml::openElement( 'table', array( 'id' => 'mw-img-deleteconfirm-table' ) ) .
00235                         "<tr>
00236                                 <td class='mw-label'>" .
00237                                         Xml::label( wfMessage( 'filedelete-comment' )->text(), 'wpDeleteReasonList' ) .
00238                                 "</td>
00239                                 <td class='mw-input'>" .
00240                                         Xml::listDropDown(
00241                                                 'wpDeleteReasonList',
00242                                                 wfMessage( 'filedelete-reason-dropdown' )->inContentLanguage()->text(),
00243                                                 wfMessage( 'filedelete-reason-otherlist' )->inContentLanguage()->text(),
00244                                                 '',
00245                                                 'wpReasonDropDown',
00246                                                 1
00247                                         ) .
00248                                 "</td>
00249                         </tr>
00250                         <tr>
00251                                 <td class='mw-label'>" .
00252                                         Xml::label( wfMessage( 'filedelete-otherreason' )->text(), 'wpReason' ) .
00253                                 "</td>
00254                                 <td class='mw-input'>" .
00255                                         Xml::input( 'wpReason', 60, $wgRequest->getText( 'wpReason' ),
00256                                                 array( 'type' => 'text', 'maxlength' => '255', 'tabindex' => '2', 'id' => 'wpReason' ) ) .
00257                                 "</td>
00258                         </tr>
00259                         {$suppress}";
00260                 if( $wgUser->isLoggedIn() ) {
00261                         $form .= "
00262                         <tr>
00263                                 <td></td>
00264                                 <td class='mw-input'>" .
00265                                         Xml::checkLabel( wfMessage( 'watchthis' )->text(),
00266                                                 'wpWatch', 'wpWatch', $checkWatch, array( 'tabindex' => '3' ) ) .
00267                                 "</td>
00268                         </tr>";
00269                 }
00270                 $form .= "
00271                         <tr>
00272                                 <td></td>
00273                                 <td class='mw-submit'>" .
00274                                         Xml::submitButton( wfMessage( 'filedelete-submit' )->text(),
00275                                                 array( 'name' => 'mw-filedelete-submit', 'id' => 'mw-filedelete-submit', 'tabindex' => '4' ) ) .
00276                                 "</td>
00277                         </tr>" .
00278                         Xml::closeElement( 'table' ) .
00279                         Xml::closeElement( 'fieldset' ) .
00280                         Xml::closeElement( 'form' );
00281 
00282                         if ( $wgUser->isAllowed( 'editinterface' ) ) {
00283                                 $title = Title::makeTitle( NS_MEDIAWIKI, 'Filedelete-reason-dropdown' );
00284                                 $link = Linker::link(
00285                                         $title,
00286                                         wfMessage( 'filedelete-edit-reasonlist' )->escaped(),
00287                                         array(),
00288                                         array( 'action' => 'edit' )
00289                                 );
00290                                 $form .= '<p class="mw-filedelete-editreasons">' . $link . '</p>';
00291                         }
00292 
00293                 $wgOut->addHTML( $form );
00294         }
00295 
00299         private function showLogEntries() {
00300                 global $wgOut;
00301                 $deleteLogPage = new LogPage( 'delete' );
00302                 $wgOut->addHTML( '<h2>' . $deleteLogPage->getName()->escaped() . "</h2>\n" );
00303                 LogEventsList::showLogExtract( $wgOut, 'delete', $this->title );
00304         }
00305 
00314         private function prepareMessage( $message ) {
00315                 global $wgLang;
00316                 if( $this->oldimage ) {
00317                         return wfMessage(
00318                                 "{$message}-old", # To ensure grep will find them: 'filedelete-intro-old', 'filedelete-nofile-old', 'filedelete-success-old'
00319                                 wfEscapeWikiText( $this->title->getText() ),
00320                                 $wgLang->date( $this->getTimestamp(), true ),
00321                                 $wgLang->time( $this->getTimestamp(), true ),
00322                                 wfExpandUrl( $this->file->getArchiveUrl( $this->oldimage ), PROTO_CURRENT ) )->parseAsBlock();
00323                 } else {
00324                         return wfMessage(
00325                                 $message,
00326                                 wfEscapeWikiText( $this->title->getText() )
00327                         )->parseAsBlock();
00328                 }
00329         }
00330 
00334         private function setHeaders() {
00335                 global $wgOut;
00336                 $wgOut->setPageTitle( wfMessage( 'filedelete', $this->title->getText() ) );
00337                 $wgOut->setRobotPolicy( 'noindex,nofollow' );
00338                 $wgOut->addBacklinkSubtitle( $this->title );
00339         }
00340 
00346         public static function isValidOldSpec($oldimage) {
00347                 return strlen( $oldimage ) >= 16
00348                         && strpos( $oldimage, '/' ) === false
00349                         && strpos( $oldimage, '\\' ) === false;
00350         }
00351 
00362         public static function haveDeletableFile(&$file, &$oldfile, $oldimage) {
00363                 return $oldimage
00364                         ? $oldfile && $oldfile->exists() && $oldfile->isLocal()
00365                         : $file && $file->exists() && $file->isLocal();
00366         }
00367 
00373         private function getAction() {
00374                 $q = array();
00375                 $q['action'] = 'delete';
00376 
00377                 if( $this->oldimage )
00378                         $q['oldimage'] = $this->oldimage;
00379 
00380                 return $this->title->getLocalUrl( $q );
00381         }
00382 
00388         private function getTimestamp() {
00389                 return $this->oldfile->getTimestamp();
00390         }
00391 }