Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 117 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 1 |
| FileDeleteAction | |
0.00% |
0 / 117 |
|
0.00% |
0 / 10 |
870 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
| getPageTitle | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| tempDelete | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| tempExecute | |
0.00% |
0 / 55 |
|
0.00% |
0 / 1 |
182 | |||
| showFormWarnings | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| showConfirm | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
20 | |||
| prepareMessage | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
6 | |||
| getFormAction | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
| checkCanExecute | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| getFormMessages | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
2 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * @license GPL-2.0-or-later |
| 4 | * @file |
| 5 | * @ingroup Actions |
| 6 | */ |
| 7 | |
| 8 | namespace MediaWiki\Actions; |
| 9 | |
| 10 | use MediaWiki\Context\IContextSource; |
| 11 | use MediaWiki\Exception\ErrorPageError; |
| 12 | use MediaWiki\Exception\PermissionsError; |
| 13 | use MediaWiki\FileRepo\File\File; |
| 14 | use MediaWiki\FileRepo\File\LocalFile; |
| 15 | use MediaWiki\FileRepo\File\OldLocalFile; |
| 16 | use MediaWiki\Html\Html; |
| 17 | use MediaWiki\MainConfigNames; |
| 18 | use MediaWiki\MediaWikiServices; |
| 19 | use MediaWiki\Page\Article; |
| 20 | use MediaWiki\Page\File\FileDeleteForm; |
| 21 | use MediaWiki\Permissions\PermissionStatus; |
| 22 | use MediaWiki\Title\Title; |
| 23 | use MediaWiki\User\User; |
| 24 | |
| 25 | /** |
| 26 | * Handle file deletion |
| 27 | * |
| 28 | * @ingroup Actions |
| 29 | */ |
| 30 | class FileDeleteAction extends DeleteAction { |
| 31 | /** @var File */ |
| 32 | private $file; |
| 33 | /** @var string Descriptor for the old version of the image, if applicable */ |
| 34 | private $oldImage; |
| 35 | /** @var OldLocalFile|null Corresponding to oldImage, if applicable */ |
| 36 | private $oldFile; |
| 37 | |
| 38 | /** |
| 39 | * @inheritDoc |
| 40 | */ |
| 41 | public function __construct( Article $article, IContextSource $context ) { |
| 42 | parent::__construct( $article, $context ); |
| 43 | $services = MediaWikiServices::getInstance(); |
| 44 | $this->file = $this->getArticle()->getFile(); |
| 45 | $this->oldImage = $this->getRequest()->getText( 'oldimage', '' ); |
| 46 | if ( $this->oldImage !== '' ) { |
| 47 | $this->oldFile = $services->getRepoGroup()->getLocalRepo()->newFromArchiveName( |
| 48 | $this->getTitle(), |
| 49 | $this->oldImage |
| 50 | ); |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | /** @inheritDoc */ |
| 55 | protected function getPageTitle() { |
| 56 | $title = $this->getTitle(); |
| 57 | return $this->msg( 'filedelete' )->plaintextParams( $title->getText() ); |
| 58 | } |
| 59 | |
| 60 | protected function tempDelete() { |
| 61 | $file = $this->file; |
| 62 | /** @var LocalFile $file */'@phan-var LocalFile $file'; |
| 63 | $this->tempExecute( $file ); |
| 64 | } |
| 65 | |
| 66 | private function tempExecute( LocalFile $file ): void { |
| 67 | $context = $this->getContext(); |
| 68 | $title = $this->getTitle(); |
| 69 | $article = $this->getArticle(); |
| 70 | $outputPage = $context->getOutput(); |
| 71 | $request = $context->getRequest(); |
| 72 | |
| 73 | $checkFile = $this->oldFile ?: $file; |
| 74 | if ( !$checkFile->exists() || !$checkFile->isLocal() ) { |
| 75 | $outputPage->addHTML( $this->prepareMessage( 'filedelete-nofile' ) ); |
| 76 | $outputPage->addReturnTo( $title ); |
| 77 | return; |
| 78 | } |
| 79 | |
| 80 | // Perform the deletion if appropriate |
| 81 | $token = $request->getVal( 'wpEditToken' ); |
| 82 | if ( |
| 83 | !$request->wasPosted() || |
| 84 | !$context->getUser()->matchEditToken( $token, [ 'delete', $title->getPrefixedText() ] ) |
| 85 | ) { |
| 86 | $this->showConfirm(); |
| 87 | return; |
| 88 | } |
| 89 | |
| 90 | // Check to make sure the page has not been edited while the deletion was being confirmed |
| 91 | if ( $article->getRevIdFetched() !== $request->getIntOrNull( 'wpConfirmationRevId' ) ) { |
| 92 | $this->showEditedWarning(); |
| 93 | $this->showConfirm(); |
| 94 | return; |
| 95 | } |
| 96 | |
| 97 | $permissionStatus = PermissionStatus::newEmpty(); |
| 98 | if ( !$context->getAuthority()->authorizeWrite( |
| 99 | 'delete', $title, $permissionStatus |
| 100 | ) ) { |
| 101 | throw new PermissionsError( 'delete', $permissionStatus ); |
| 102 | } |
| 103 | |
| 104 | $reason = $this->getDeleteReason(); |
| 105 | |
| 106 | # Flag to hide all contents of the archived revisions |
| 107 | $suppress = $request->getCheck( 'wpSuppress' ) && |
| 108 | $context->getAuthority()->isAllowed( 'suppressrevision' ); |
| 109 | |
| 110 | $status = FileDeleteForm::doDelete( |
| 111 | $title, |
| 112 | $file, |
| 113 | $this->oldImage, |
| 114 | $reason, |
| 115 | $suppress, |
| 116 | $context->getUser(), |
| 117 | [], |
| 118 | $request->getCheck( 'wpDeleteTalk' ) |
| 119 | ); |
| 120 | |
| 121 | if ( !$status->isGood() ) { |
| 122 | $outputPage->setPageTitleMsg( |
| 123 | $this->msg( 'cannotdelete-title' )->plaintextParams( $title->getPrefixedText() ) |
| 124 | ); |
| 125 | $outputPage->addModuleStyles( 'mediawiki.codex.messagebox.styles' ); |
| 126 | foreach ( $status->getMessages() as $msg ) { |
| 127 | $outputPage->addHTML( Html::errorBox( |
| 128 | $context->msg( $msg )->parse() |
| 129 | ) ); |
| 130 | } |
| 131 | } |
| 132 | if ( $status->isOK() ) { |
| 133 | $outputPage->setPageTitleMsg( $context->msg( 'actioncomplete' ) ); |
| 134 | $outputPage->addHTML( $this->prepareMessage( 'filedelete-success' ) ); |
| 135 | // Return to the main page if we just deleted all versions of the |
| 136 | // file, otherwise go back to the description page |
| 137 | $outputPage->addReturnTo( $this->oldImage ? $title : Title::newMainPage() ); |
| 138 | |
| 139 | $this->watchlistManager->setWatch( |
| 140 | $request->getCheck( 'wpWatch' ), |
| 141 | $context->getAuthority(), |
| 142 | $title |
| 143 | ); |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | protected function showFormWarnings(): void { |
| 148 | $this->getOutput()->addHTML( $this->prepareMessage( 'filedelete-intro' ) ); |
| 149 | $this->showSubpagesWarnings(); |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Show the confirmation form |
| 154 | */ |
| 155 | private function showConfirm() { |
| 156 | $this->prepareOutputForForm(); |
| 157 | $context = $this->getContext(); |
| 158 | $article = $this->getArticle(); |
| 159 | |
| 160 | // oldid is set to the revision id of the page when the page was displayed. |
| 161 | // Check to make sure the page has not been edited between loading the page |
| 162 | // and clicking the delete link |
| 163 | $oldid = $context->getRequest()->getIntOrNull( 'oldid' ); |
| 164 | if ( $oldid !== null && $oldid !== $article->getRevIdFetched() ) { |
| 165 | $this->showEditedWarning(); |
| 166 | } |
| 167 | |
| 168 | $this->showFormWarnings(); |
| 169 | $form = $this->getForm(); |
| 170 | if ( $form->show() ) { |
| 171 | $this->onSuccess(); |
| 172 | } |
| 173 | $this->showEditReasonsLinks(); |
| 174 | $this->showLogEntries(); |
| 175 | } |
| 176 | |
| 177 | /** |
| 178 | * Prepare a message referring to the file being deleted, |
| 179 | * showing an appropriate message depending upon whether |
| 180 | * it's a current file or an old version |
| 181 | * |
| 182 | * @param string $message Message base |
| 183 | * @return string |
| 184 | */ |
| 185 | private function prepareMessage( string $message ) { |
| 186 | if ( $this->oldFile ) { |
| 187 | $lang = $this->getContext()->getLanguage(); |
| 188 | # Message keys used: |
| 189 | # 'filedelete-intro-old', 'filedelete-nofile-old', 'filedelete-success-old' |
| 190 | return $this->getContext()->msg( |
| 191 | "{$message}-old", |
| 192 | wfEscapeWikiText( $this->getTitle()->getText() ), |
| 193 | $lang->date( $this->oldFile->getTimestamp(), true ), |
| 194 | $lang->time( $this->oldFile->getTimestamp(), true ), |
| 195 | (string)MediaWikiServices::getInstance()->getUrlUtils()->expand( |
| 196 | $this->file->getArchiveUrl( $this->oldImage ), |
| 197 | PROTO_CURRENT |
| 198 | ) |
| 199 | )->parseAsBlock(); |
| 200 | } else { |
| 201 | return $this->getContext()->msg( |
| 202 | $message, |
| 203 | wfEscapeWikiText( $this->getTitle()->getText() ) |
| 204 | )->parseAsBlock(); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | protected function getFormAction(): string { |
| 209 | $q = []; |
| 210 | $q['action'] = 'delete'; |
| 211 | |
| 212 | if ( $this->oldImage ) { |
| 213 | $q['oldimage'] = $this->oldImage; |
| 214 | } |
| 215 | |
| 216 | return $this->getTitle()->getLocalURL( $q ); |
| 217 | } |
| 218 | |
| 219 | protected function checkCanExecute( User $user ) { |
| 220 | parent::checkCanExecute( $user ); |
| 221 | |
| 222 | if ( $this->getContext()->getConfig()->get( MainConfigNames::UploadMaintenance ) ) { |
| 223 | throw new ErrorPageError( 'filedelete-maintenance-title', 'filedelete-maintenance' ); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | * TODO Do we need all these messages to be different? |
| 229 | * @return string[] |
| 230 | */ |
| 231 | protected function getFormMessages(): array { |
| 232 | return [ |
| 233 | self::MSG_REASON_DROPDOWN => 'filedelete-reason-dropdown', |
| 234 | self::MSG_REASON_DROPDOWN_SUPPRESS => 'filedelete-reason-dropdown-suppress', |
| 235 | self::MSG_REASON_DROPDOWN_OTHER => 'filedelete-reason-otherlist', |
| 236 | self::MSG_COMMENT => 'filedelete-comment', |
| 237 | self::MSG_REASON_OTHER => 'filedelete-otherreason', |
| 238 | self::MSG_SUBMIT => 'filedelete-submit', |
| 239 | self::MSG_LEGEND => 'filedelete-legend', |
| 240 | self::MSG_EDIT_REASONS => 'filedelete-edit-reasonlist', |
| 241 | self::MSG_EDIT_REASONS_SUPPRESS => 'filedelete-edit-reasonlist-suppress', |
| 242 | ]; |
| 243 | } |
| 244 | } |