MediaWiki master
SpecialFileDuplicateSearch.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Specials;
22
23use File;
31use RepoGroup;
33
45 private $hash = '';
46
50 private $filename = '';
51
55 private $file = null;
56
57 private LinkBatchFactory $linkBatchFactory;
58 private RepoGroup $repoGroup;
59 private SearchEngineFactory $searchEngineFactory;
60 private ILanguageConverter $languageConverter;
61
68 public function __construct(
69 LinkBatchFactory $linkBatchFactory,
70 RepoGroup $repoGroup,
71 SearchEngineFactory $searchEngineFactory,
72 LanguageConverterFactory $languageConverterFactory
73 ) {
74 parent::__construct( 'FileDuplicateSearch' );
75 $this->linkBatchFactory = $linkBatchFactory;
76 $this->repoGroup = $repoGroup;
77 $this->searchEngineFactory = $searchEngineFactory;
78 $this->languageConverter = $languageConverterFactory->getLanguageConverter( $this->getContentLanguage() );
79 }
80
86 private function getDupes() {
87 return $this->repoGroup->findBySha1( $this->hash );
88 }
89
93 private function showList( $dupes ) {
94 $html = [];
95 $html[] = "<ol class='special'>";
96
97 foreach ( $dupes as $dupe ) {
98 $line = $this->formatResult( $dupe );
99 $html[] = "<li>" . $line . "</li>";
100 }
101 $html[] = '</ol>';
102
103 $this->getOutput()->addHTML( implode( "\n", $html ) );
104 }
105
106 public function execute( $par ) {
107 $this->setHeaders();
108 $this->outputHeader();
109
110 $this->filename = $par ?? $this->getRequest()->getText( 'filename' );
111 $this->file = null;
112 $this->hash = '';
113 $title = Title::newFromText( $this->filename, NS_FILE );
114 if ( $title && $title->getText() != '' ) {
115 $this->file = $this->repoGroup->findFile( $title );
116 }
117
118 $out = $this->getOutput();
119
120 # Create the input form
121 $formFields = [
122 'filename' => [
123 'type' => 'text',
124 'name' => 'filename',
125 'label-message' => 'fileduplicatesearch-filename',
126 'id' => 'filename',
127 'size' => 50,
128 'default' => $this->filename,
129 ],
130 ];
131 $htmlForm = HTMLForm::factory( 'ooui', $formFields, $this->getContext() );
132 $htmlForm->setTitle( $this->getPageTitle() );
133 $htmlForm->setMethod( 'get' );
134 $htmlForm->setSubmitTextMsg( $this->msg( 'fileduplicatesearch-submit' ) );
135
136 // The form should be visible always, even if it was submitted (e.g. to perform another action).
137 // To bypass the callback validation of HTMLForm, use prepareForm() and displayForm().
138 $htmlForm->prepareForm()->displayForm( false );
139
140 if ( $this->file ) {
141 $this->hash = $this->file->getSha1();
142 } elseif ( $this->filename !== '' ) {
143 $out->wrapWikiMsg(
144 "<p class='mw-fileduplicatesearch-noresults'>\n$1\n</p>",
145 [ 'fileduplicatesearch-noresults', wfEscapeWikiText( $this->filename ) ]
146 );
147 }
148
149 if ( $this->hash != '' ) {
150 # Show a thumbnail of the file
151 $img = $this->file;
152 if ( $img ) {
153 $thumb = $img->transform( [ 'width' => 120, 'height' => 120 ] );
154 if ( $thumb ) {
155 $out->addModuleStyles( 'mediawiki.special' );
156 $out->addHTML( '<div id="mw-fileduplicatesearch-icon">' .
157 $thumb->toHtml( [ 'desc-link' => false ] ) . '<br />' .
158 $this->msg( 'fileduplicatesearch-info' )
159 ->numParams( $img->getWidth(), $img->getHeight() )
160 ->sizeParams( $img->getSize() )
161 ->params( $img->getMimeType() )->parseAsBlock() .
162 '</div>' );
163 }
164 }
165
166 $dupes = $this->getDupes();
167 $numRows = count( $dupes );
168
169 # Show a short summary
170 if ( $numRows == 1 ) {
171 $out->wrapWikiMsg(
172 "<p class='mw-fileduplicatesearch-result-1'>\n$1\n</p>",
173 [ 'fileduplicatesearch-result-1', wfEscapeWikiText( $this->filename ) ]
174 );
175 } elseif ( $numRows ) {
176 $out->wrapWikiMsg(
177 "<p class='mw-fileduplicatesearch-result-n'>\n$1\n</p>",
178 [ 'fileduplicatesearch-result-n', wfEscapeWikiText( $this->filename ),
179 $this->getLanguage()->formatNum( $numRows - 1 ) ]
180 );
181 }
182
183 $this->doBatchLookups( $dupes );
184 $this->showList( $dupes );
185 }
186 }
187
191 private function doBatchLookups( $list ) {
192 $batch = $this->linkBatchFactory->newLinkBatch();
193 foreach ( $list as $file ) {
194 $batch->addObj( $file->getTitle() );
195 if ( $file->isLocal() ) {
196 $uploader = $file->getUploader( File::FOR_THIS_USER, $this->getAuthority() );
197 if ( $uploader ) {
198 $batch->add( NS_USER, $uploader->getName() );
199 $batch->add( NS_USER_TALK, $uploader->getName() );
200 }
201 }
202 }
203
204 $batch->execute();
205 }
206
211 private function formatResult( $result ) {
212 $linkRenderer = $this->getLinkRenderer();
213 $nt = $result->getTitle();
214 $text = $this->languageConverter->convert( $nt->getText() );
215 $plink = $linkRenderer->makeLink(
216 $nt,
217 $text
218 );
219
220 $uploader = $result->getUploader( File::FOR_THIS_USER, $this->getAuthority() );
221 if ( $result->isLocal() && $uploader ) {
222 $user = Linker::userLink( $uploader->getId(), $uploader->getName() );
223 $user .= '<span style="white-space: nowrap;">';
224 $user .= Linker::userToolLinks( $uploader->getId(), $uploader->getName() );
225 $user .= '</span>';
226 } elseif ( $uploader ) {
227 $user = htmlspecialchars( $uploader->getName() );
228 } else {
229 $user = '<span class="history-deleted">'
230 . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
231 }
232
233 $time = htmlspecialchars( $this->getLanguage()->userTimeAndDate(
234 $result->getTimestamp(), $this->getUser() ) );
235
236 return "$plink . . $user . . $time";
237 }
238
247 public function prefixSearchSubpages( $search, $limit, $offset ) {
248 $title = Title::newFromText( $search, NS_FILE );
249 if ( !$title || $title->getNamespace() !== NS_FILE ) {
250 // No prefix suggestion outside of file namespace
251 return [];
252 }
253 $searchEngine = $this->searchEngineFactory->create();
254 $searchEngine->setLimitOffset( $limit, $offset );
255 // Autocomplete subpage the same as a normal search, but just for files
256 $searchEngine->setNamespaces( [ NS_FILE ] );
257 $result = $searchEngine->defaultPrefixSearch( $search );
258
259 return array_map( static function ( Title $t ) {
260 // Remove namespace in search suggestion
261 return $t->getText();
262 }, $result );
263 }
264
265 protected function getGroupName() {
266 return 'media';
267 }
268}
269
271class_alias( SpecialFileDuplicateSearch::class, 'SpecialFileDuplicateSearch' );
const NS_USER
Definition Defines.php:67
const NS_FILE
Definition Defines.php:71
const NS_USER_TALK
Definition Defines.php:68
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:79
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:209
An interface for creating language converters.
getLanguageConverter( $language=null)
Provide a LanguageConverter for given language.
Some internal bits split of from Skin.php.
Definition Linker.php:63
Parent class for all special pages.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getPageTitle( $subpage=false)
Get a self-referential title object.
getContext()
Gets the context this SpecialPage is executed in.
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
getAuthority()
Shortcut to get the Authority executing this instance.
getContentLanguage()
Shortcut to get content language.
getLanguage()
Shortcut to get user's language.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages By default the message key is the canonical name of...
Search the database for files of the requested hash, comparing this with the 'img_sha1' field in the ...
execute( $par)
Default execute method Checks user permissions.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
__construct(LinkBatchFactory $linkBatchFactory, RepoGroup $repoGroup, SearchEngineFactory $searchEngineFactory, LanguageConverterFactory $languageConverterFactory)
Represents a title within MediaWiki.
Definition Title.php:78
Prioritized list of file repositories.
Definition RepoGroup.php:32
Factory class for SearchEngine.
The shared interface for all language converters.