Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / page / WikiFilePage.php
blob1a52020f94a07859ed0006fea488d32ad12a5e53
1 <?php
2 /**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
18 * @file
21 use MediaWiki\Actions\FileDeleteAction;
22 use MediaWiki\MediaWikiServices;
23 use MediaWiki\Title\Title;
24 use MediaWiki\Title\TitleArrayFromResult;
25 use Wikimedia\Rdbms\FakeResultWrapper;
27 /**
28 * Special handling for representing file pages.
30 * @ingroup Media
32 class WikiFilePage extends WikiPage {
33 /** @var File|false */
34 protected $mFile = false;
35 /** @var LocalRepo|null */
36 protected $mRepo = null;
37 /** @var bool */
38 protected $mFileLoaded = false;
39 /** @var array|null */
40 protected $mDupes = null;
42 /**
43 * @param Title $title
45 public function __construct( $title ) {
46 parent::__construct( $title );
47 $this->mDupes = null;
48 $this->mRepo = null;
51 /**
52 * @param File $file
54 public function setFile( File $file ) {
55 $this->mFile = $file;
56 $this->mFileLoaded = true;
59 /**
60 * @return bool
62 protected function loadFile() {
63 $services = MediaWikiServices::getInstance();
64 if ( $this->mFileLoaded ) {
65 return true;
68 $this->mFile = $services->getRepoGroup()->findFile( $this->mTitle );
69 if ( !$this->mFile ) {
70 $this->mFile = $services->getRepoGroup()->getLocalRepo()
71 ->newFile( $this->mTitle );
74 if ( !$this->mFile instanceof File ) {
75 throw new RuntimeException( 'Expected to find file. See T250767' );
78 $this->mRepo = $this->mFile->getRepo();
79 $this->mFileLoaded = true;
80 return true;
83 /**
84 * @return bool|Title|string False, Title of in-wiki target, or string with URL
86 public function followRedirect() {
87 $this->loadFile();
88 if ( $this->mFile->isLocal() ) {
89 return parent::followRedirect();
91 $from = $this->mFile->getRedirected();
92 $to = $this->mFile->getName();
93 if ( $from === null || $from === $to ) {
94 return false;
96 return Title::makeTitle( NS_FILE, $to );
99 /**
100 * @return bool
102 public function isRedirect() {
103 $this->loadFile();
104 if ( $this->mFile->isLocal() ) {
105 return parent::isRedirect();
108 return $this->mFile->getRedirected() !== null;
112 * @return bool
114 public function isLocal() {
115 $this->loadFile();
116 return $this->mFile->isLocal();
120 * @return File
122 public function getFile(): File {
123 $this->loadFile();
124 return $this->mFile;
128 * @return File[]|null
130 public function getDuplicates() {
131 $this->loadFile();
132 if ( $this->mDupes !== null ) {
133 return $this->mDupes;
135 $hash = $this->mFile->getSha1();
136 if ( !( $hash ) ) {
137 $this->mDupes = [];
138 return $this->mDupes;
140 $dupes = MediaWikiServices::getInstance()->getRepoGroup()->findBySha1( $hash );
141 // Remove duplicates with self and non matching file sizes
142 $self = $this->mFile->getRepoName() . ':' . $this->mFile->getName();
143 $size = $this->mFile->getSize();
146 * @var File $file
148 foreach ( $dupes as $index => $file ) {
149 $key = $file->getRepoName() . ':' . $file->getName();
150 if ( $key === $self || $file->getSize() != $size ) {
151 unset( $dupes[$index] );
154 $this->mDupes = $dupes;
155 return $this->mDupes;
159 * Override handling of action=purge
160 * @return bool
162 public function doPurge() {
163 $this->loadFile();
165 if ( $this->mFile->exists() ) {
166 wfDebug( 'ImagePage::doPurge purging ' . $this->mFile->getName() );
167 $job = HTMLCacheUpdateJob::newForBacklinks(
168 $this->mTitle,
169 'imagelinks',
170 [ 'causeAction' => 'file-purge' ]
172 MediaWikiServices::getInstance()->getJobQueueGroup()->lazyPush( $job );
173 } else {
174 wfDebug( 'ImagePage::doPurge no image for '
175 . $this->mFile->getName() . "; limiting purge to cache only" );
178 // even if the file supposedly doesn't exist, force any cached information
179 // to be updated (in case the cached information is wrong)
181 // Purge current version and its thumbnails
182 $this->mFile->purgeCache( [ 'forThumbRefresh' => true ] );
184 // Purge the old versions and their thumbnails
185 foreach ( $this->mFile->getHistory() as $oldFile ) {
186 $oldFile->purgeCache( [ 'forThumbRefresh' => true ] );
189 if ( $this->mRepo ) {
190 // Purge redirect cache
191 $this->mRepo->invalidateImageRedirect( $this->mTitle );
194 return parent::doPurge();
198 * Get the categories this file is a member of on the wiki where it was uploaded.
199 * For local files, this is the same as getCategories().
200 * For foreign API files (InstantCommons), this is not supported currently.
201 * Results will include hidden categories.
203 * @return TitleArrayFromResult
204 * @since 1.23
206 public function getForeignCategories() {
207 $this->loadFile();
208 $title = $this->mTitle;
209 $file = $this->mFile;
210 $titleFactory = MediaWikiServices::getInstance()->getTitleFactory();
212 if ( !$file instanceof LocalFile ) {
213 wfDebug( __METHOD__ . " is not supported for this file" );
214 return $titleFactory->newTitleArrayFromResult( new FakeResultWrapper( [] ) );
217 /** @var LocalRepo $repo */
218 $repo = $file->getRepo();
219 $dbr = $repo->getReplicaDB();
221 $res = $dbr->newSelectQueryBuilder()
222 ->select( [ 'page_title' => 'cl_to', 'page_namespace' => (string)NS_CATEGORY ] )
223 ->from( 'page' )
224 ->join( 'categorylinks', null, 'page_id = cl_from' )
225 ->where( [ 'page_namespace' => $title->getNamespace(), 'page_title' => $title->getDBkey(), ] )
226 ->caller( __METHOD__ )->fetchResultSet();
228 return $titleFactory->newTitleArrayFromResult( $res );
232 * @since 1.28
233 * @return string
235 public function getWikiDisplayName() {
236 return $this->getFile()->getRepo()->getDisplayName();
240 * @since 1.28
241 * @return string
243 public function getSourceURL() {
244 return $this->getFile()->getDescriptionUrl();
248 * @inheritDoc
250 public function getActionOverrides() {
251 $file = $this->getFile();
252 if ( $file->exists() && $file->isLocal() && !$file->getRedirected() ) {
253 // Would be an actual file deletion
254 return [ 'delete' => FileDeleteAction::class ] + parent::getActionOverrides();
256 // It should use the normal article deletion interface
257 return parent::getActionOverrides();