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