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