MediaWiki master
SpecialFileDuplicateSearch.php
Go to the documentation of this file.
1<?php
2
26namespace MediaWiki\Specials;
27
28use File;
36use RepoGroup;
38
49 private $hash = '';
50
54 private $filename = '';
55
59 private $file = null;
60
61 private LinkBatchFactory $linkBatchFactory;
62 private RepoGroup $repoGroup;
63 private SearchEngineFactory $searchEngineFactory;
64 private ILanguageConverter $languageConverter;
65
72 public function __construct(
73 LinkBatchFactory $linkBatchFactory,
74 RepoGroup $repoGroup,
75 SearchEngineFactory $searchEngineFactory,
76 LanguageConverterFactory $languageConverterFactory
77 ) {
78 parent::__construct( 'FileDuplicateSearch' );
79 $this->linkBatchFactory = $linkBatchFactory;
80 $this->repoGroup = $repoGroup;
81 $this->searchEngineFactory = $searchEngineFactory;
82 $this->languageConverter = $languageConverterFactory->getLanguageConverter( $this->getContentLanguage() );
83 }
84
90 private function getDupes() {
91 return $this->repoGroup->findBySha1( $this->hash );
92 }
93
97 private function showList( $dupes ) {
98 $html = [];
99 $html[] = "<ol class='special'>";
100
101 foreach ( $dupes as $dupe ) {
102 $line = $this->formatResult( $dupe );
103 $html[] = "<li>" . $line . "</li>";
104 }
105 $html[] = '</ol>';
106
107 $this->getOutput()->addHTML( implode( "\n", $html ) );
108 }
109
110 public function execute( $par ) {
111 $this->setHeaders();
112 $this->outputHeader();
113
114 $this->filename = $par ?? $this->getRequest()->getText( 'filename' );
115 $this->file = null;
116 $this->hash = '';
117 $title = Title::newFromText( $this->filename, NS_FILE );
118 if ( $title && $title->getText() != '' ) {
119 $this->file = $this->repoGroup->findFile( $title );
120 }
121
122 $out = $this->getOutput();
123
124 # Create the input form
125 $formFields = [
126 'filename' => [
127 'type' => 'text',
128 'name' => 'filename',
129 'label-message' => 'fileduplicatesearch-filename',
130 'id' => 'filename',
131 'size' => 50,
132 'default' => $this->filename,
133 ],
134 ];
135 $htmlForm = HTMLForm::factory( 'ooui', $formFields, $this->getContext() );
136 $htmlForm->setTitle( $this->getPageTitle() );
137 $htmlForm->setMethod( 'get' );
138 $htmlForm->setSubmitTextMsg( $this->msg( 'fileduplicatesearch-submit' ) );
139
140 // The form should be visible always, even if it was submitted (e.g. to perform another action).
141 // To bypass the callback validation of HTMLForm, use prepareForm() and displayForm().
142 $htmlForm->prepareForm()->displayForm( false );
143
144 if ( $this->file ) {
145 $this->hash = $this->file->getSha1();
146 } elseif ( $this->filename !== '' ) {
147 $out->wrapWikiMsg(
148 "<p class='mw-fileduplicatesearch-noresults'>\n$1\n</p>",
149 [ 'fileduplicatesearch-noresults', wfEscapeWikiText( $this->filename ) ]
150 );
151 }
152
153 if ( $this->hash != '' ) {
154 # Show a thumbnail of the file
155 $img = $this->file;
156 if ( $img ) {
157 $thumb = $img->transform( [ 'width' => 120, 'height' => 120 ] );
158 if ( $thumb ) {
159 $out->addModuleStyles( 'mediawiki.special' );
160 $out->addHTML( '<div id="mw-fileduplicatesearch-icon">' .
161 $thumb->toHtml( [ 'desc-link' => false ] ) . '<br />' .
162 $this->msg( 'fileduplicatesearch-info' )
163 ->numParams( $img->getWidth(), $img->getHeight() )
164 ->sizeParams( $img->getSize() )
165 ->params( $img->getMimeType() )->parseAsBlock() .
166 '</div>' );
167 }
168 }
169
170 $dupes = $this->getDupes();
171 $numRows = count( $dupes );
172
173 # Show a short summary
174 if ( $numRows == 1 ) {
175 $out->wrapWikiMsg(
176 "<p class='mw-fileduplicatesearch-result-1'>\n$1\n</p>",
177 [ 'fileduplicatesearch-result-1', wfEscapeWikiText( $this->filename ) ]
178 );
179 } elseif ( $numRows ) {
180 $out->wrapWikiMsg(
181 "<p class='mw-fileduplicatesearch-result-n'>\n$1\n</p>",
182 [ 'fileduplicatesearch-result-n', wfEscapeWikiText( $this->filename ),
183 $this->getLanguage()->formatNum( $numRows - 1 ) ]
184 );
185 }
186
187 $this->doBatchLookups( $dupes );
188 $this->showList( $dupes );
189 }
190 }
191
195 private function doBatchLookups( $list ) {
196 $batch = $this->linkBatchFactory->newLinkBatch();
197 foreach ( $list as $file ) {
198 $batch->addObj( $file->getTitle() );
199 if ( $file->isLocal() ) {
200 $uploader = $file->getUploader( File::FOR_THIS_USER, $this->getAuthority() );
201 if ( $uploader ) {
202 $batch->add( NS_USER, $uploader->getName() );
203 $batch->add( NS_USER_TALK, $uploader->getName() );
204 }
205 }
206 }
207
208 $batch->execute();
209 }
210
215 private function formatResult( $result ) {
216 $linkRenderer = $this->getLinkRenderer();
217 $nt = $result->getTitle();
218 $text = $this->languageConverter->convert( $nt->getText() );
219 $plink = $linkRenderer->makeLink(
220 $nt,
221 $text
222 );
223
224 $uploader = $result->getUploader( File::FOR_THIS_USER, $this->getAuthority() );
225 if ( $result->isLocal() && $uploader ) {
226 $user = Linker::userLink( $uploader->getId(), $uploader->getName() );
227 $user .= '<span style="white-space: nowrap;">';
228 $user .= Linker::userToolLinks( $uploader->getId(), $uploader->getName() );
229 $user .= '</span>';
230 } elseif ( $uploader ) {
231 $user = htmlspecialchars( $uploader->getName() );
232 } else {
233 $user = '<span class="history-deleted">'
234 . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
235 }
236
237 $time = htmlspecialchars( $this->getLanguage()->userTimeAndDate(
238 $result->getTimestamp(), $this->getUser() ) );
239
240 return "$plink . . $user . . $time";
241 }
242
251 public function prefixSearchSubpages( $search, $limit, $offset ) {
252 $title = Title::newFromText( $search, NS_FILE );
253 if ( !$title || $title->getNamespace() !== NS_FILE ) {
254 // No prefix suggestion outside of file namespace
255 return [];
256 }
257 $searchEngine = $this->searchEngineFactory->create();
258 $searchEngine->setLimitOffset( $limit, $offset );
259 // Autocomplete subpage the same as a normal search, but just for files
260 $searchEngine->setNamespaces( [ NS_FILE ] );
261 $result = $searchEngine->defaultPrefixSearch( $search );
262
263 return array_map( static function ( Title $t ) {
264 // Remove namespace in search suggestion
265 return $t->getText();
266 }, $result );
267 }
268
269 protected function getGroupName() {
270 return 'media';
271 }
272}
273
275class_alias( SpecialFileDuplicateSearch::class, 'SpecialFileDuplicateSearch' );
const NS_USER
Definition Defines.php:66
const NS_FILE
Definition Defines.php:70
const NS_USER_TALK
Definition Defines.php:67
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:73
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:206
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:65
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 Per default the message key is the canonical name o...
Searches the database for files of the requested hash, comparing this with the 'img_sha1' field in th...
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:30
Factory class for SearchEngine.
The shared interface for all language converters.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Ge...