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