Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 125
0.00% covered (danger)
0.00%
0 / 20
CRAP
0.00% covered (danger)
0.00%
0 / 1
RevDelFileItem
0.00% covered (danger)
0.00%
0 / 124
0.00% covered (danger)
0.00%
0 / 20
1122
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 initFile
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getIdField
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTimestampField
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAuthorIdField
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAuthorNameField
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAuthorActorField
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getId
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 canView
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 canViewContent
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getBits
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setBits
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
20
 isDeleted
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getLink
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 getUserTools
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 getComment
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 getHTML
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 getApiData
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
30
 lock
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 unlock
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 * @ingroup RevisionDelete
6 */
7
8namespace MediaWiki\RevisionDelete;
9
10use MediaWiki\Api\ApiResult;
11use MediaWiki\FileRepo\File\File;
12use MediaWiki\FileRepo\File\OldLocalFile;
13use MediaWiki\Html\Html;
14use MediaWiki\Linker\Linker;
15use MediaWiki\MediaWikiServices;
16use MediaWiki\RevisionList\RevisionListBase;
17use MediaWiki\SpecialPage\SpecialPage;
18use Wikimedia\Rdbms\IConnectionProvider;
19use Wikimedia\Timestamp\TimestampFormat as TS;
20
21/**
22 * Item class for an oldimage table row
23 */
24class RevDelFileItem extends RevDelItem {
25    /** @var RevDelFileList */
26    protected $list;
27    /** @var OldLocalFile */
28    protected $file;
29    protected IConnectionProvider $dbProvider;
30
31    /** @inheritDoc */
32    public function __construct( RevisionListBase $list, $row ) {
33        parent::__construct( $list, $row );
34        $this->file = static::initFile( $list, $row );
35        $this->dbProvider = MediaWikiServices::getInstance()->getConnectionProvider();
36    }
37
38    /**
39     * Create file object from $row sourced from $list
40     *
41     * @param RevisionListBase $list
42     * @param mixed $row
43     * @return mixed
44     */
45    protected static function initFile( $list, $row ) {
46        return MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()
47            ->newFileFromRow( $row );
48    }
49
50    /** @inheritDoc */
51    public function getIdField() {
52        return 'oi_archive_name';
53    }
54
55    /** @inheritDoc */
56    public function getTimestampField() {
57        return 'oi_timestamp';
58    }
59
60    /** @inheritDoc */
61    public function getAuthorIdField() {
62        return 'oi_user';
63    }
64
65    /** @inheritDoc */
66    public function getAuthorNameField() {
67        return 'oi_user_text';
68    }
69
70    /** @inheritDoc */
71    public function getAuthorActorField() {
72        return 'oi_actor';
73    }
74
75    /** @inheritDoc */
76    public function getId() {
77        $parts = explode( '!', $this->row->oi_archive_name );
78
79        return $parts[0];
80    }
81
82    /** @inheritDoc */
83    public function canView() {
84        return $this->file->userCan( File::DELETED_RESTRICTED, $this->list->getAuthority() );
85    }
86
87    /** @inheritDoc */
88    public function canViewContent() {
89        return $this->file->userCan( File::DELETED_FILE, $this->list->getAuthority() );
90    }
91
92    /** @inheritDoc */
93    public function getBits() {
94        return $this->file->getVisibility();
95    }
96
97    /** @inheritDoc */
98    public function setBits( $bits ) {
99        # Queue the file op
100        # @todo FIXME: Move to LocalFile.php
101        if ( $this->isDeleted() ) {
102            if ( $bits & File::DELETED_FILE ) {
103                # Still deleted
104            } else {
105                # Newly undeleted
106                $key = $this->file->getStorageKey();
107                $srcRel = $this->file->repo->getDeletedHashPath( $key ) . $key;
108                $this->list->storeBatch[] = [
109                    $this->file->repo->getVirtualUrl( 'deleted' ) . '/' . $srcRel,
110                    'public',
111                    $this->file->getRel()
112                ];
113                $this->list->cleanupBatch[] = $key;
114            }
115        } elseif ( $bits & File::DELETED_FILE ) {
116            # Newly deleted
117            $key = $this->file->getStorageKey();
118            $dstRel = $this->file->repo->getDeletedHashPath( $key ) . $key;
119            $this->list->deleteBatch[] = [ $this->file->getRel(), $dstRel ];
120        }
121
122        # Do the database operations
123        $dbw = $this->dbProvider->getPrimaryDatabase();
124        $dbw->newUpdateQueryBuilder()
125            ->update( 'oldimage' )
126            ->set( [ 'oi_deleted' => $bits ] )
127            ->where( [
128                'oi_name' => $this->row->oi_name,
129                'oi_timestamp' => $this->row->oi_timestamp,
130                'oi_deleted' => $this->getBits()
131            ] )
132            ->caller( __METHOD__ )->execute();
133
134        return (bool)$dbw->affectedRows();
135    }
136
137    /**
138     * @return bool
139     */
140    public function isDeleted() {
141        return $this->file->isDeleted( File::DELETED_FILE );
142    }
143
144    /**
145     * Get the link to the file.
146     * Overridden by RevDelArchivedFileItem.
147     * @return string
148     */
149    protected function getLink() {
150        $date = $this->list->getLanguage()->userTimeAndDate(
151            $this->file->getTimestamp(), $this->list->getUser() );
152
153        if ( !$this->isDeleted() ) {
154            # Regular files...
155            return Html::element( 'a', [ 'href' => $this->file->getUrl() ], $date );
156        }
157
158        # Hidden files...
159        if ( !$this->canViewContent() ) {
160            $link = htmlspecialchars( $date );
161        } else {
162            $link = $this->getLinkRenderer()->makeLink(
163                SpecialPage::getTitleFor( 'Revisiondelete' ),
164                $date,
165                [],
166                [
167                    'target' => $this->list->getPageName(),
168                    'file' => $this->file->getArchiveName(),
169                    'token' => $this->list->getUser()->getEditToken(
170                        $this->file->getArchiveName() )
171                ]
172            );
173        }
174
175        return '<span class="history-deleted">' . $link . '</span>';
176    }
177
178    /**
179     * Generate a user tool link cluster if the current user is allowed to view it
180     * @return string HTML
181     */
182    protected function getUserTools() {
183        $uploader = $this->file->getUploader( File::FOR_THIS_USER, $this->list->getAuthority() );
184        if ( $uploader ) {
185            $link = Linker::userLink( $uploader->getId(), $uploader->getName() ) .
186                Linker::userToolLinks( $uploader->getId(), $uploader->getName() );
187            return $link;
188        } else {
189            $link = $this->list->msg( 'rev-deleted-user' )->escaped();
190        }
191        if ( $this->file->isDeleted( File::DELETED_USER ) ) {
192            return '<span class="history-deleted">' . $link . '</span>';
193        }
194        return $link;
195    }
196
197    /**
198     * Wrap and format the file's comment block, if the current
199     * user is allowed to view it.
200     *
201     * @return string HTML
202     */
203    protected function getComment() {
204        if ( $this->file->userCan( File::DELETED_COMMENT, $this->list->getAuthority() ) ) {
205            $block = MediaWikiServices::getInstance()->getCommentFormatter()
206                ->formatBlock( $this->file->getDescription() );
207        } else {
208            $block = ' ' . $this->list->msg( 'rev-deleted-comment' )->escaped();
209        }
210        if ( $this->file->isDeleted( File::DELETED_COMMENT ) ) {
211            return "<span class=\"history-deleted\">$block</span>";
212        }
213
214        return $block;
215    }
216
217    /** @inheritDoc */
218    public function getHTML() {
219        $data =
220            $this->list->msg( 'widthheight' )->numParams(
221                $this->file->getWidth(),
222                $this->file->getHeight() )->escaped() .
223            ' (' . $this->list->msg( 'nbytes' )->numParams(
224                $this->file->getSize() )->escaped() . ')';
225
226        return '<li>' . $this->getLink() . ' ' . $this->getUserTools() . ' ' .
227            $data . ' ' . $this->getComment() . '</li>';
228    }
229
230    /** @inheritDoc */
231    public function getApiData( ApiResult $result ) {
232        $file = $this->file;
233        $user = $this->list->getUser();
234        $ret = [
235            'title' => $this->list->getPageName(),
236            'archivename' => $file->getArchiveName(),
237            'timestamp' => wfTimestamp( TS::ISO_8601, $file->getTimestamp() ),
238            'width' => $file->getWidth(),
239            'height' => $file->getHeight(),
240            'size' => $file->getSize(),
241            'userhidden' => (bool)$file->isDeleted( File::DELETED_USER ),
242            'commenthidden' => (bool)$file->isDeleted( File::DELETED_COMMENT ),
243            'contenthidden' => (bool)$this->isDeleted(),
244        ];
245        if ( !$this->isDeleted() ) {
246            $ret += [
247                'url' => $file->getUrl(),
248            ];
249        } elseif ( $this->canViewContent() ) {
250            $ret += [
251                'url' => SpecialPage::getTitleFor( 'Revisiondelete' )->getLinkURL(
252                    [
253                        'target' => $this->list->getPageName(),
254                        'file' => $file->getArchiveName(),
255                        'token' => $user->getEditToken( $file->getArchiveName() )
256                    ]
257                ),
258            ];
259        }
260        $uploader = $file->getUploader( File::FOR_THIS_USER, $user );
261        if ( $uploader ) {
262            $ret += [
263                'userid' => $uploader->getId(),
264                'user' => $uploader->getName(),
265            ];
266        }
267        $comment = $file->getDescription( File::FOR_THIS_USER, $user );
268        if ( ( $comment ?? '' ) !== '' ) {
269            $ret += [
270                'comment' => $comment,
271            ];
272        }
273
274        return $ret;
275    }
276
277    /** @inheritDoc */
278    public function lock() {
279        return $this->file->acquireFileLock();
280    }
281
282    /** @inheritDoc */
283    public function unlock() {
284        return $this->file->releaseFileLock();
285    }
286}
287
288/** @deprecated class alias since 1.46 */
289class_alias( RevDelFileItem::class, 'RevDelFileItem' );