MediaWiki fundraising/REL1_35
SpecialFileDuplicateSearch.php
Go to the documentation of this file.
1<?php
2
4
35 protected $hash = '', $filename = '';
36
40 protected $file = null;
41
42 public function __construct( $name = 'FileDuplicateSearch' ) {
43 parent::__construct( $name );
44 }
45
46 public function isSyndicated() {
47 return false;
48 }
49
50 public function isCacheable() {
51 return false;
52 }
53
54 public function isCached() {
55 return false;
56 }
57
58 protected function linkParameters() {
59 return [ 'filename' => $this->filename ];
60 }
61
67 private function getDupes() {
68 return MediaWikiServices::getInstance()->getRepoGroup()->findBySha1( $this->hash );
69 }
70
74 private function showList( $dupes ) {
75 $html = [];
76 $html[] = $this->openList( 0 );
77
78 foreach ( $dupes as $dupe ) {
79 $line = $this->formatResult( null, $dupe );
80 $html[] = "<li>" . $line . "</li>";
81 }
82 $html[] = $this->closeList();
83
84 $this->getOutput()->addHTML( implode( "\n", $html ) );
85 }
86
87 public function getQueryInfo() {
88 $imgQuery = LocalFile::getQueryInfo();
89 return [
90 'tables' => $imgQuery['tables'],
91 'fields' => [
92 'title' => 'img_name',
93 'value' => 'img_sha1',
94 'img_user_text' => $imgQuery['fields']['img_user_text'],
95 'img_timestamp'
96 ],
97 'conds' => [ 'img_sha1' => $this->hash ],
98 'join_conds' => $imgQuery['joins'],
99 ];
100 }
101
102 public function execute( $par ) {
103 $this->setHeaders();
104 $this->outputHeader();
105
106 $this->filename = $par ?? $this->getRequest()->getText( 'filename' );
107 $this->file = null;
108 $this->hash = '';
109 $title = Title::newFromText( $this->filename, NS_FILE );
110 if ( $title && $title->getText() != '' ) {
111 $this->file = MediaWikiServices::getInstance()->getRepoGroup()->findFile( $title );
112 }
113
114 $out = $this->getOutput();
115
116 # Create the input form
117 $formFields = [
118 'filename' => [
119 'type' => 'text',
120 'name' => 'filename',
121 'label-message' => 'fileduplicatesearch-filename',
122 'id' => 'filename',
123 'size' => 50,
124 'default' => $this->filename,
125 ],
126 ];
127 $hiddenFields = [
128 'title' => $this->getPageTitle()->getPrefixedDBkey(),
129 ];
130 $htmlForm = HTMLForm::factory( 'ooui', $formFields, $this->getContext() );
131 $htmlForm->addHiddenFields( $hiddenFields );
132 $htmlForm->setAction( wfScript() );
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' )->numParams(
159 $img->getWidth(), $img->getHeight() )->params(
160 $this->getLanguage()->formatSize( $img->getSize() ),
161 $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
188 private function doBatchLookups( $list ) {
189 $batch = new LinkBatch();
191 foreach ( $list as $file ) {
192 $batch->addObj( $file->getTitle() );
193 if ( $file->isLocal() ) {
194 $userName = $file->getUser( 'text' );
195 $batch->add( NS_USER, $userName );
196 $batch->add( NS_USER_TALK, $userName );
197 }
198 }
199
200 $batch->execute();
201 }
202
208 public function formatResult( $skin, $result ) {
210 $nt = $result->getTitle();
211 $text = MediaWikiServices::getInstance()->getContentLanguage()->convert(
212 htmlspecialchars( $nt->getText() )
213 );
214 $plink = $linkRenderer->makeLink(
215 $nt,
216 new HtmlArmor( $text )
217 );
218
219 $userText = $result->getUser( 'text' );
220 if ( $result->isLocal() ) {
221 $userId = $result->getUser( 'id' );
222 $user = Linker::userLink( $userId, $userText );
223 $user .= '<span style="white-space: nowrap;">';
224 $user .= Linker::userToolLinks( $userId, $userText );
225 $user .= '</span>';
226 } else {
227 $user = htmlspecialchars( $userText );
228 }
229
230 $time = htmlspecialchars( $this->getLanguage()->userTimeAndDate(
231 $result->getTimestamp(), $this->getUser() ) );
232
233 return "$plink . . $user . . $time";
234 }
235
244 public function prefixSearchSubpages( $search, $limit, $offset ) {
245 $title = Title::newFromText( $search, NS_FILE );
246 if ( !$title || $title->getNamespace() !== NS_FILE ) {
247 // No prefix suggestion outside of file namespace
248 return [];
249 }
250 $searchEngine = MediaWikiServices::getInstance()->newSearchEngine();
251 $searchEngine->setLimitOffset( $limit, $offset );
252 // Autocomplete subpage the same as a normal search, but just for files
253 $searchEngine->setNamespaces( [ NS_FILE ] );
254 $result = $searchEngine->defaultPrefixSearch( $search );
255
256 return array_map( function ( Title $t ) {
257 // Remove namespace in search suggestion
258 return $t->getText();
259 }, $result );
260 }
261
262 protected function getGroupName() {
263 return 'media';
264 }
265}
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
wfEscapeWikiText( $text)
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:63
isLocal()
Returns true if the file comes from the local file repository.
Definition File.php:1951
getUser( $type='text')
Returns ID or name of user who uploaded the file STUB.
Definition File.php:599
getTitle()
Return the associated title object.
Definition File.php:345
transform( $params, $flags=0)
Transform a media file Stable to override.
Definition File.php:1127
Marks HTML that shouldn't be escaped.
Definition HtmlArmor.php:30
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Definition LinkBatch.php:35
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition Linker.php:896
static userToolLinks( $userId, $userText, $redContribsWhenNoEdits=false, $flags=0, $edits=null, $useParentheses=true)
Generate standard user tool links (talk, contributions, block link, etc.)
Definition Linker.php:941
MediaWikiServices is the service locator for the application scope of MediaWiki.
This is a class for doing query pages; since they're almost all the same, we factor out some of the f...
Definition QueryPage.php:39
int $offset
The offset and limit in use, as passed to the query() function.
Definition QueryPage.php:44
openList( $offset)
int $numRows
The number of rows returned by the query.
Definition QueryPage.php:56
Searches the database for files of the requested hash, comparing this with the 'img_sha1' field in th...
getDupes()
Fetch dupes from all connected file repositories.
__construct( $name='FileDuplicateSearch')
execute( $par)
This is the actual workhorse.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
linkParameters()
If using extra form wheely-dealies, return a set of parameters here as an associative array.
isCacheable()
Is the output of this query cacheable? Non-cacheable expensive pages will be disabled in miser mode a...
File $file
selected reference file, if present
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
getQueryInfo()
Subclasses return an SQL query here, formatted as an array with the following keys: tables => Table(s...
isCached()
Whether or not the output of the page in question is retrieved from the database cache.
isSyndicated()
Sometime we don't want to build rss / atom feeds.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!...
getOutput()
Get the OutputPage being used for this instance.
getContext()
Gets the context this SpecialPage is executed in.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getRequest()
Get the WebRequest being used for this instance.
getPageTitle( $subpage=false)
Get a self-referential title object.
getLanguage()
Shortcut to get user's language.
MediaWiki Linker LinkRenderer null $linkRenderer
Represents a title within MediaWiki.
Definition Title.php:42
const NS_USER
Definition Defines.php:72
const NS_FILE
Definition Defines.php:76
const NS_USER_TALK
Definition Defines.php:73
$line
Definition mcc.php:119