MediaWiki master
ImageHistoryList.php
Go to the documentation of this file.
1<?php
22use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
29
36 use ProtectedHookAccessorTrait;
37
41 protected $title;
42
46 protected $img;
47
51 protected $imagePage;
52
56 protected $current;
57
58 protected $repo, $showThumb;
59 protected $preventClickjacking = false;
60
64 public function __construct( $imagePage ) {
65 $context = $imagePage->getContext();
66 $this->current = $imagePage->getPage()->getFile();
67 $this->img = $imagePage->getDisplayedFile();
68 $this->title = $imagePage->getTitle();
69 $this->imagePage = $imagePage;
70 $this->showThumb = $context->getConfig()->get( MainConfigNames::ShowArchiveThumbnails ) &&
71 $this->img->canRender();
72 $this->setContext( $context );
73 }
74
78 public function getImagePage() {
79 return $this->imagePage;
80 }
81
85 public function getFile() {
86 return $this->img;
87 }
88
92 public function beginImageHistoryList() {
93 // Styles for class=history-deleted
94 $this->getOutput()->addModuleStyles( 'mediawiki.interface.helpers.styles' );
95
96 $html = '';
97 $canDelete = $this->current->isLocal() &&
98 $this->getAuthority()->isAllowedAny( 'delete', 'deletedhistory' );
99
100 foreach ( [
101 '',
102 $canDelete ? '' : null,
103 'filehist-datetime',
104 $this->showThumb ? 'filehist-thumb' : null,
105 'filehist-dimensions',
106 'filehist-user',
107 'filehist-comment',
108 ] as $key ) {
109 if ( $key !== null ) {
110 $html .= Html::element( 'th', [], $key ? $this->msg( $key )->text() : '' );
111 }
112 }
113
114 return Html::openElement( 'table', [ 'class' => 'wikitable filehistory' ] ) . "\n"
115 . Html::rawElement( 'tr', [], $html ) . "\n";
116 }
117
121 public function endImageHistoryList() {
122 return Html::closeElement( 'table' ) . "\n";
123 }
124
132 public function imageHistoryLine( $iscur, $file, $formattedComment ) {
133 $user = $this->getUser();
134 $lang = $this->getLanguage();
135 $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
136 $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
137 // @phan-suppress-next-line PhanUndeclaredMethod
138 $img = $iscur ? $file->getName() : $file->getArchiveName();
139 $uploader = $file->getUploader( File::FOR_THIS_USER, $user );
140
141 $local = $this->current->isLocal();
142 $row = '';
143
144 // Deletion link
145 if ( $local && ( $this->getAuthority()->isAllowedAny( 'delete', 'deletedhistory' ) ) ) {
146 $row .= Html::openElement( 'td' );
147 # Link to hide content. Don't show useless link to people who cannot hide revisions.
148 if ( !$iscur && $this->getAuthority()->isAllowed( 'deleterevision' ) ) {
149 // If file is top revision, is missing or locked from this user, don't link
150 if ( !$file->userCan( File::DELETED_RESTRICTED, $user ) || !$file->exists() ) {
151 $row .= Html::check( 'deleterevisions', false, [ 'disabled' => 'disabled' ] );
152 } else {
153 $row .= Html::check( 'ids[' . explode( '!', $img, 2 )[0] . ']', false );
154 }
155 if ( $this->getAuthority()->isAllowed( 'delete' ) ) {
156 $row .= ' ';
157 }
158 }
159 # Link to remove from history
160 if ( $this->getAuthority()->isAllowed( 'delete' ) ) {
161 if ( $file->exists() ) {
162 $row .= $linkRenderer->makeKnownLink(
163 $this->title,
164 $this->msg( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' )->text(),
165 [],
166 [ 'action' => 'delete', 'oldimage' => $iscur ? null : $img ]
167 );
168 } else {
169 // T244567: Non-existing file can not be deleted.
170 $row .= $this->msg( 'filehist-missing' )->escaped();
171 }
172
173 }
174 $row .= Html::closeElement( 'td' );
175 }
176
177 // Reversion link/current indicator
178 $row .= Html::openElement( 'td' );
179 if ( $iscur ) {
180 $row .= $this->msg( 'filehist-current' )->escaped();
181 } elseif ( $local && $this->getAuthority()->probablyCan( 'edit', $this->title )
182 && $this->getAuthority()->probablyCan( 'upload', $this->title )
183 ) {
184 if ( $file->isDeleted( File::DELETED_FILE ) ) {
185 $row .= $this->msg( 'filehist-revert' )->escaped();
186 } elseif ( !$file->exists() ) {
187 // T328112: Lost file, in this case there's no version to revert back to.
188 $row .= $this->msg( 'filehist-missing' )->escaped();
189 } else {
190 $row .= $linkRenderer->makeKnownLink(
191 $this->title,
192 $this->msg( 'filehist-revert' )->text(),
193 [],
194 [
195 'action' => 'revert',
196 'oldimage' => $img,
197 ]
198 );
199 }
200 }
201 $row .= Html::closeElement( 'td' );
202
203 // Date/time and image link
204 $selected = $file->getTimestamp() === $this->img->getTimestamp();
205 $row .= Html::openElement( 'td', [
206 'class' => $selected ? 'filehistory-selected' : null,
207 'style' => 'white-space: nowrap;'
208 ] );
209 if ( !$file->userCan( File::DELETED_FILE, $user ) ) {
210 # Don't link to unviewable files
211 $row .= Html::element( 'span', [ 'class' => 'history-deleted' ],
212 $lang->userTimeAndDate( $timestamp, $user )
213 );
214 } elseif ( $file->isDeleted( File::DELETED_FILE ) ) {
215 $timeAndDate = $lang->userTimeAndDate( $timestamp, $user );
216 if ( $local ) {
217 $this->setPreventClickjacking( true );
218 # Make a link to review the image
219 $url = $linkRenderer->makeKnownLink(
220 SpecialPage::getTitleFor( 'Revisiondelete' ),
221 $timeAndDate,
222 [],
223 [
224 'target' => $this->title->getPrefixedText(),
225 'file' => $img,
226 'token' => $user->getEditToken( $img )
227 ]
228 );
229 } else {
230 $url = htmlspecialchars( $timeAndDate );
231 }
232 $row .= Html::rawElement( 'span', [ 'class' => 'history-deleted' ], $url );
233 } elseif ( !$file->exists() ) {
234 $row .= Html::element( 'span', [ 'class' => 'mw-file-missing' ],
235 $lang->userTimeAndDate( $timestamp, $user )
236 );
237 } else {
238 $url = $iscur ? $this->current->getUrl() : $this->current->getArchiveUrl( $img );
239 $row .= Html::element( 'a', [ 'href' => $url ],
240 $lang->userTimeAndDate( $timestamp, $user )
241 );
242 }
243 $row .= Html::closeElement( 'td' );
244
245 // Thumbnail
246 if ( $this->showThumb ) {
247 $row .= Html::rawElement( 'td', [],
248 $this->getThumbForLine( $file, $iscur ) ?? $this->msg( 'filehist-nothumb' )->escaped()
249 );
250 }
251
252 // Image dimensions + size
253 $row .= Html::openElement( 'td' );
254 $row .= htmlspecialchars( $file->getDimensionsString() );
255 $row .= $this->msg( 'word-separator' )->escaped();
256 $row .= Html::element( 'span', [ 'style' => 'white-space: nowrap;' ],
257 $this->msg( 'parentheses' )->sizeParams( $file->getSize() )->text()
258 );
259 $row .= Html::closeElement( 'td' );
260
261 // Uploading user
262 $row .= Html::openElement( 'td' );
263 // Hide deleted usernames
264 if ( $uploader && $local ) {
265 $row .= Linker::userLink( $uploader->getId(), $uploader->getName() );
266 $row .= Html::rawElement( 'span', [ 'style' => 'white-space: nowrap;' ],
267 Linker::userToolLinks( $uploader->getId(), $uploader->getName() )
268 );
269 } elseif ( $uploader ) {
270 $row .= htmlspecialchars( $uploader->getName() );
271 } else {
272 $row .= Html::element( 'span', [ 'class' => 'history-deleted' ],
273 $this->msg( 'rev-deleted-user' )->text()
274 );
275 }
276 $row .= Html::closeElement( 'td' );
277
278 // Don't show deleted descriptions
279 if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
280 $row .= Html::rawElement( 'td', [],
281 Html::element( 'span', [ 'class' => 'history-deleted' ],
282 $this->msg( 'rev-deleted-comment' )->text()
283 )
284 );
285 } else {
286 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
287 $row .= Html::rawElement( 'td', [ 'dir' => $contLang->getDir() ], $formattedComment );
288 }
289
290 $rowClass = null;
291 $this->getHookRunner()->onImagePageFileHistoryLine( $this, $file, $row, $rowClass );
292
293 return Html::rawElement( 'tr', [ 'class' => $rowClass ], $row ) . "\n";
294 }
295
301 protected function getThumbForLine( $file, $iscur ) {
302 $user = $this->getUser();
303 if ( !$file->allowInlineDisplay() ||
304 $file->isDeleted( File::DELETED_FILE ) ||
305 !$file->userCan( File::DELETED_FILE, $user )
306 ) {
307 return null;
308 }
309
310 $thumbnail = $file->transform(
311 [
312 'width' => '120',
313 'height' => '120',
314 'isFilePageThumb' => $iscur // old revisions are already versioned
315 ]
316 );
317 if ( !$thumbnail ) {
318 return null;
319 }
320
321 $lang = $this->getLanguage();
322 $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() );
323 $alt = $this->msg(
324 'filehist-thumbtext',
325 $lang->userTimeAndDate( $timestamp, $user ),
326 $lang->userDate( $timestamp, $user ),
327 $lang->userTime( $timestamp, $user )
328 )->text();
329 return $thumbnail->toHtml( [ 'alt' => $alt, 'file-link' => true, 'loading' => 'lazy' ] );
330 }
331
336 protected function preventClickjacking( $enable = true ) {
337 $this->preventClickjacking = $enable;
338 }
339
344 protected function setPreventClickjacking( bool $enable ) {
345 $this->preventClickjacking = $enable;
346 }
347
351 public function getPreventClickjacking() {
352 return $this->preventClickjacking;
353 }
354}
getUser()
getAuthority()
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
getContext()
Gets the context this Article is executed in.
Definition Article.php:1987
getTitle()
Get the title object of the article.
Definition Article.php:267
getPage()
Get the WikiPage object of this instance.
Definition Article.php:277
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:73
getName()
Return the name of this file.
Definition File.php:341
Builds the image revision log shown on image pages.
preventClickjacking( $enable=true)
getThumbForLine( $file, $iscur)
setPreventClickjacking(bool $enable)
imageHistoryLine( $iscur, $file, $formattedComment)
__construct( $imagePage)
Rendering of file description pages.
Definition ImagePage.php:37
getDisplayedFile()
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
setContext(IContextSource $context)
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
This class is a collection of static functions that serve two purposes:
Definition Html.php:56
Some internal bits split of from Skin.php.
Definition Linker.php:65
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
Parent class for all special pages.
Represents a title within MediaWiki.
Definition Title.php:78