MediaWiki  REL1_22
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                 WatchAction::doWatchOrUnwatch( $wgRequest->getCheck( 'wpWatch' ), $this->title, $wgUser );
00124             }
00125             return;
00126         }
00127 
00128         $this->showForm();
00129         $this->showLogEntries();
00130     }
00131 
00144     public static function doDelete( &$title, &$file, &$oldimage, $reason, $suppress, User $user = null ) {
00145         if ( $user === null ) {
00146             global $wgUser;
00147             $user = $wgUser;
00148         }
00149 
00150         if ( $oldimage ) {
00151             $page = null;
00152             $status = $file->deleteOld( $oldimage, $reason, $suppress );
00153             if ( $status->ok ) {
00154                 // Need to do a log item
00155                 $logComment = wfMessage( 'deletedrevision', $oldimage )->inContentLanguage()->text();
00156                 if ( trim( $reason ) != '' ) {
00157                     $logComment .= wfMessage( 'colon-separator' )
00158                         ->inContentLanguage()->text() . $reason;
00159                 }
00160 
00161                 $logtype = $suppress ? 'suppress' : 'delete';
00162 
00163                 $logEntry = new ManualLogEntry( $logtype, 'delete' );
00164                 $logEntry->setPerformer( $user );
00165                 $logEntry->setTarget( $title );
00166                 $logEntry->setComment( $logComment );
00167                 $logid = $logEntry->insert();
00168                 $logEntry->publish( $logid );
00169             }
00170         } else {
00171             $status = Status::newFatal( 'cannotdelete',
00172                 wfEscapeWikiText( $title->getPrefixedText() )
00173             );
00174             $page = WikiPage::factory( $title );
00175             $dbw = wfGetDB( DB_MASTER );
00176             try {
00177                 // delete the associated article first
00178                 $error = '';
00179                 $deleteStatus = $page->doDeleteArticleReal( $reason, $suppress, 0, false, $error, $user );
00180                 // doDeleteArticleReal() returns a non-fatal error status if the page
00181                 // or revision is missing, so check for isOK() rather than isGood()
00182                 if ( $deleteStatus->isOK() ) {
00183                     $status = $file->delete( $reason, $suppress );
00184                     if ( $status->isOK() ) {
00185                         $dbw->commit( __METHOD__ );
00186                     } else {
00187                         $dbw->rollback( __METHOD__ );
00188                     }
00189                 }
00190             } catch ( MWException $e ) {
00191                 // rollback before returning to prevent UI from displaying incorrect "View or restore N deleted edits?"
00192                 $dbw->rollback( __METHOD__ );
00193                 throw $e;
00194             }
00195         }
00196 
00197         if ( $status->isOK() ) {
00198             wfRunHooks( 'FileDeleteComplete', array( &$file, &$oldimage, &$page, &$user, &$reason ) );
00199         }
00200 
00201         return $status;
00202     }
00203 
00207     private function showForm() {
00208         global $wgOut, $wgUser, $wgRequest;
00209 
00210         if ( $wgUser->isAllowed( 'suppressrevision' ) ) {
00211             $suppress = "<tr id=\"wpDeleteSuppressRow\">
00212                     <td></td>
00213                     <td class='mw-input'><strong>" .
00214                         Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
00215                             'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '3' ) ) .
00216                     "</strong></td>
00217                 </tr>";
00218         } else {
00219             $suppress = '';
00220         }
00221 
00222         $checkWatch = $wgUser->getBoolOption( 'watchdeletion' ) || $wgUser->isWatched( $this->title );
00223         $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction(),
00224             'id' => 'mw-img-deleteconfirm' ) ) .
00225             Xml::openElement( 'fieldset' ) .
00226             Xml::element( 'legend', null, wfMessage( 'filedelete-legend' )->text() ) .
00227             Html::hidden( 'wpEditToken', $wgUser->getEditToken( $this->oldimage ) ) .
00228             $this->prepareMessage( 'filedelete-intro' ) .
00229             Xml::openElement( 'table', array( 'id' => 'mw-img-deleteconfirm-table' ) ) .
00230             "<tr>
00231                 <td class='mw-label'>" .
00232                     Xml::label( wfMessage( 'filedelete-comment' )->text(), 'wpDeleteReasonList' ) .
00233                 "</td>
00234                 <td class='mw-input'>" .
00235                     Xml::listDropDown(
00236                         'wpDeleteReasonList',
00237                         wfMessage( 'filedelete-reason-dropdown' )->inContentLanguage()->text(),
00238                         wfMessage( 'filedelete-reason-otherlist' )->inContentLanguage()->text(),
00239                         '',
00240                         'wpReasonDropDown',
00241                         1
00242                     ) .
00243                 "</td>
00244             </tr>
00245             <tr>
00246                 <td class='mw-label'>" .
00247                     Xml::label( wfMessage( 'filedelete-otherreason' )->text(), 'wpReason' ) .
00248                 "</td>
00249                 <td class='mw-input'>" .
00250                     Xml::input( 'wpReason', 60, $wgRequest->getText( 'wpReason' ),
00251                         array( 'type' => 'text', 'maxlength' => '255', 'tabindex' => '2', 'id' => 'wpReason' ) ) .
00252                 "</td>
00253             </tr>
00254             {$suppress}";
00255         if ( $wgUser->isLoggedIn() ) {
00256             $form .= "
00257             <tr>
00258                 <td></td>
00259                 <td class='mw-input'>" .
00260                     Xml::checkLabel( wfMessage( 'watchthis' )->text(),
00261                         'wpWatch', 'wpWatch', $checkWatch, array( 'tabindex' => '3' ) ) .
00262                 "</td>
00263             </tr>";
00264         }
00265         $form .= "
00266             <tr>
00267                 <td></td>
00268                 <td class='mw-submit'>" .
00269                     Xml::submitButton( wfMessage( 'filedelete-submit' )->text(),
00270                         array( 'name' => 'mw-filedelete-submit', 'id' => 'mw-filedelete-submit', 'tabindex' => '4' ) ) .
00271                 "</td>
00272             </tr>" .
00273             Xml::closeElement( 'table' ) .
00274             Xml::closeElement( 'fieldset' ) .
00275             Xml::closeElement( 'form' );
00276 
00277             if ( $wgUser->isAllowed( 'editinterface' ) ) {
00278                 $title = Title::makeTitle( NS_MEDIAWIKI, 'Filedelete-reason-dropdown' );
00279                 $link = Linker::link(
00280                     $title,
00281                     wfMessage( 'filedelete-edit-reasonlist' )->escaped(),
00282                     array(),
00283                     array( 'action' => 'edit' )
00284                 );
00285                 $form .= '<p class="mw-filedelete-editreasons">' . $link . '</p>';
00286             }
00287 
00288         $wgOut->addHTML( $form );
00289     }
00290 
00294     private function showLogEntries() {
00295         global $wgOut;
00296         $deleteLogPage = new LogPage( 'delete' );
00297         $wgOut->addHTML( '<h2>' . $deleteLogPage->getName()->escaped() . "</h2>\n" );
00298         LogEventsList::showLogExtract( $wgOut, 'delete', $this->title );
00299     }
00300 
00309     private function prepareMessage( $message ) {
00310         global $wgLang;
00311         if ( $this->oldimage ) {
00312             return wfMessage(
00313                 "{$message}-old", # To ensure grep will find them: 'filedelete-intro-old', 'filedelete-nofile-old', 'filedelete-success-old'
00314                 wfEscapeWikiText( $this->title->getText() ),
00315                 $wgLang->date( $this->getTimestamp(), true ),
00316                 $wgLang->time( $this->getTimestamp(), true ),
00317                 wfExpandUrl( $this->file->getArchiveUrl( $this->oldimage ), PROTO_CURRENT ) )->parseAsBlock();
00318         } else {
00319             return wfMessage(
00320                 $message,
00321                 wfEscapeWikiText( $this->title->getText() )
00322             )->parseAsBlock();
00323         }
00324     }
00325 
00329     private function setHeaders() {
00330         global $wgOut;
00331         $wgOut->setPageTitle( wfMessage( 'filedelete', $this->title->getText() ) );
00332         $wgOut->setRobotPolicy( 'noindex,nofollow' );
00333         $wgOut->addBacklinkSubtitle( $this->title );
00334     }
00335 
00341     public static function isValidOldSpec( $oldimage ) {
00342         return strlen( $oldimage ) >= 16
00343             && strpos( $oldimage, '/' ) === false
00344             && strpos( $oldimage, '\\' ) === false;
00345     }
00346 
00357     public static function haveDeletableFile( &$file, &$oldfile, $oldimage ) {
00358         return $oldimage
00359             ? $oldfile && $oldfile->exists() && $oldfile->isLocal()
00360             : $file && $file->exists() && $file->isLocal();
00361     }
00362 
00368     private function getAction() {
00369         $q = array();
00370         $q['action'] = 'delete';
00371 
00372         if ( $this->oldimage ) {
00373             $q['oldimage'] = $this->oldimage;
00374         }
00375 
00376         return $this->title->getLocalURL( $q );
00377     }
00378 
00384     private function getTimestamp() {
00385         return $this->oldfile->getTimestamp();
00386     }
00387 }