Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 70
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
FileDeleteForm
0.00% covered (danger)
0.00%
0 / 69
0.00% covered (danger)
0.00%
0 / 2
306
0.00% covered (danger)
0.00%
0 / 1
 doDelete
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 1
210
 isValidOldSpec
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * File deletion utilities.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @author Rob Church <robchur@gmail.com>
22 * @ingroup Media
23 */
24
25namespace MediaWiki\Page\File;
26
27use LocalFile;
28use ManualLogEntry;
29use MediaWiki\HookContainer\HookRunner;
30use MediaWiki\MediaWikiServices;
31use MediaWiki\Page\DeletePage;
32use MediaWiki\Status\Status;
33use MediaWiki\Title\Title;
34use MediaWiki\User\UserIdentity;
35
36/**
37 * File deletion user interface
38 *
39 * @ingroup Media
40 */
41class FileDeleteForm {
42    /**
43     * Really delete the file
44     *
45     * @param Title $title
46     * @param LocalFile $file
47     * @param string|null $oldimage Archive name
48     * @param string $reason Reason of the deletion
49     * @param bool $suppress Whether to mark all deleted versions as restricted
50     * @param UserIdentity $user
51     * @param string[] $tags Tags to apply to the deletion action
52     * @param bool $deleteTalk
53     * @return Status The value can be an integer with the log ID of the deletion, or false in case of
54     *   scheduled deletion.
55     */
56    public static function doDelete(
57        Title $title,
58        LocalFile $file,
59        ?string $oldimage,
60        $reason,
61        $suppress,
62        UserIdentity $user,
63        $tags = [],
64        bool $deleteTalk = false
65    ): Status {
66        $services = MediaWikiServices::getInstance();
67        if ( $oldimage ) {
68            $page = null;
69            $status = $file->deleteOldFile( $oldimage, $reason, $user, $suppress );
70            if ( $status->isOK() ) {
71                // Need to do a log item
72                $logComment = wfMessage( 'deletedrevision', $oldimage )->inContentLanguage()->text();
73                if ( trim( $reason ) !== '' ) {
74                    $logComment .= wfMessage( 'colon-separator' )
75                        ->inContentLanguage()->text() . $reason;
76                }
77
78                $logtype = $suppress ? 'suppress' : 'delete';
79
80                $logEntry = new ManualLogEntry( $logtype, 'delete' );
81                $logEntry->setPerformer( $user );
82                $logEntry->setTarget( $title );
83                $logEntry->setComment( $logComment );
84                $logEntry->addTags( $tags );
85                $logid = $logEntry->insert();
86                $logEntry->publish( $logid );
87
88                $status->value = $logid;
89            }
90        } else {
91            $status = Status::newFatal( 'cannotdelete',
92                wfEscapeWikiText( $title->getPrefixedText() )
93            );
94            $page = $services->getWikiPageFactory()->newFromTitle( $title );
95            '@phan-var \WikiFilePage $page';
96            $deleter = $services->getUserFactory()->newFromUserIdentity( $user );
97            $deletePage = $services->getDeletePageFactory()->newDeletePage( $page, $deleter );
98            if ( $deleteTalk ) {
99                $checkStatus = $deletePage->canProbablyDeleteAssociatedTalk();
100                if ( !$checkStatus->isGood() ) {
101                    return Status::wrap( $checkStatus );
102                }
103                $deletePage->setDeleteAssociatedTalk( true );
104            }
105            $dbw = $services->getConnectionProvider()->getPrimaryDatabase();
106            $dbw->startAtomic( __METHOD__, $dbw::ATOMIC_CANCELABLE );
107            // delete the associated article first
108            $deleteStatus = $deletePage
109                ->setSuppress( $suppress )
110                ->setTags( $tags ?: [] )
111                ->deleteIfAllowed( $reason );
112
113            // DeletePage returns a non-fatal error status if the page
114            // or revision is missing, so check for isOK() rather than isGood().
115            if ( $deleteStatus->isOK() ) {
116                $status = $file->deleteFile( $reason, $user, $suppress );
117                if ( $status->isOK() ) {
118                    if ( $deletePage->deletionsWereScheduled()[DeletePage::PAGE_BASE] ) {
119                        $status->value = false;
120                    } else {
121                        $deletedID = $deletePage->getSuccessfulDeletionsIDs()[DeletePage::PAGE_BASE];
122                        if ( $deletedID !== null ) {
123                            $status->value = $deletedID;
124                        } else {
125                            // Means that the page/revision didn't exist, so create a log entry here.
126                            $logtype = $suppress ? 'suppress' : 'delete';
127                            $logEntry = new ManualLogEntry( $logtype, 'delete' );
128                            $logEntry->setPerformer( $user );
129                            $logEntry->setTarget( $title );
130                            $logEntry->setComment( $reason );
131                            $logEntry->addTags( $tags );
132                            $logid = $logEntry->insert();
133                            $dbw->onTransactionPreCommitOrIdle(
134                                static function () use ( $logEntry, $logid ) {
135                                    $logEntry->publish( $logid );
136                                },
137                                __METHOD__
138                            );
139                            $status->value = $logid;
140                        }
141                    }
142                    $dbw->endAtomic( __METHOD__ );
143                } else {
144                    // Page deleted but file still there? rollback page delete
145                    $dbw->cancelAtomic( __METHOD__ );
146                }
147            } else {
148                $dbw->endAtomic( __METHOD__ );
149            }
150        }
151
152        if ( $status->isOK() ) {
153            $legacyUser = $services->getUserFactory()
154                ->newFromUserIdentity( $user );
155            ( new HookRunner( $services->getHookContainer() ) )
156                ->onFileDeleteComplete( $file, $oldimage, $page, $legacyUser, $reason );
157        }
158
159        return $status;
160    }
161
162    /**
163     * Is the provided `oldimage` value valid?
164     *
165     * @param string $oldimage
166     * @return bool
167     */
168    public static function isValidOldSpec( $oldimage ) {
169        return strlen( $oldimage ) >= 16
170            && strpos( $oldimage, '/' ) === false
171            && strpos( $oldimage, '\\' ) === false;
172    }
173}
174
175/** @deprecated class alias since 1.40 */
176class_alias( FileDeleteForm::class, 'FileDeleteForm' );