]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/specials/SpecialFileDuplicateSearch.php
MediaWiki 1.30.2 renames
[autoinstallsdev/mediawiki.git] / includes / specials / SpecialFileDuplicateSearch.php
1 <?php
2 use MediaWiki\MediaWikiServices;
3
4 /**
5  * Implements Special:FileDuplicateSearch
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  * http://www.gnu.org/copyleft/gpl.html
21  *
22  * @file
23  * @ingroup SpecialPage
24  * @author Raimond Spekking, based on Special:MIMESearch by Ævar Arnfjörð Bjarmason
25  */
26
27 /**
28  * Searches the database for files of the requested hash, comparing this with the
29  * 'img_sha1' field in the image table.
30  *
31  * @ingroup SpecialPage
32  */
33 class FileDuplicateSearchPage extends QueryPage {
34         protected $hash = '', $filename = '';
35
36         /**
37          * @var File $file selected reference file, if present
38          */
39         protected $file = null;
40
41         function __construct( $name = 'FileDuplicateSearch' ) {
42                 parent::__construct( $name );
43         }
44
45         function isSyndicated() {
46                 return false;
47         }
48
49         function isCacheable() {
50                 return false;
51         }
52
53         public function isCached() {
54                 return false;
55         }
56
57         function linkParameters() {
58                 return [ 'filename' => $this->filename ];
59         }
60
61         /**
62          * Fetch dupes from all connected file repositories.
63          *
64          * @return array Array of File objects
65          */
66         function getDupes() {
67                 return RepoGroup::singleton()->findBySha1( $this->hash );
68         }
69
70         /**
71          *
72          * @param array $dupes Array of File objects
73          */
74         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                 return [
89                         'tables' => [ 'image' ],
90                         'fields' => [
91                                 'title' => 'img_name',
92                                 'value' => 'img_sha1',
93                                 'img_user_text',
94                                 'img_timestamp'
95                         ],
96                         'conds' => [ 'img_sha1' => $this->hash ]
97                 ];
98         }
99
100         public function execute( $par ) {
101                 $this->setHeaders();
102                 $this->outputHeader();
103
104                 $this->filename = $par !== null ? $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 = wfFindFile( $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                                 'value' => $this->filename,
123                         ],
124                 ];
125                 $hiddenFields = [
126                         'title' => $this->getPageTitle()->getPrefixedDBkey(),
127                 ];
128                 $htmlForm = HTMLForm::factory( 'ooui', $formFields, $this->getContext() );
129                 $htmlForm->addHiddenFields( $hiddenFields );
130                 $htmlForm->setAction( wfScript() );
131                 $htmlForm->setMethod( 'get' );
132                 $htmlForm->setSubmitProgressive();
133                 $htmlForm->setSubmitTextMsg( $this->msg( 'fileduplicatesearch-submit' ) );
134
135                 // The form should be visible always, even if it was submitted (e.g. to perform another action).
136                 // To bypass the callback validation of HTMLForm, use prepareForm() and displayForm().
137                 $htmlForm->prepareForm()->displayForm( false );
138
139                 if ( $this->file ) {
140                         $this->hash = $this->file->getSha1();
141                 } elseif ( $this->filename !== '' ) {
142                         $out->wrapWikiMsg(
143                                 "<p class='mw-fileduplicatesearch-noresults'>\n$1\n</p>",
144                                 [ 'fileduplicatesearch-noresults', wfEscapeWikiText( $this->filename ) ]
145                         );
146                 }
147
148                 if ( $this->hash != '' ) {
149                         # Show a thumbnail of the file
150                         $img = $this->file;
151                         if ( $img ) {
152                                 $thumb = $img->transform( [ 'width' => 120, 'height' => 120 ] );
153                                 if ( $thumb ) {
154                                         $out->addModuleStyles( 'mediawiki.special' );
155                                         $out->addHTML( '<div id="mw-fileduplicatesearch-icon">' .
156                                                 $thumb->toHtml( [ 'desc-link' => false ] ) . '<br />' .
157                                                 $this->msg( 'fileduplicatesearch-info' )->numParams(
158                                                         $img->getWidth(), $img->getHeight() )->params(
159                                                                 $this->getLanguage()->formatSize( $img->getSize() ),
160                                                                 $img->getMimeType() )->parseAsBlock() .
161                                                 '</div>' );
162                                 }
163                         }
164
165                         $dupes = $this->getDupes();
166                         $numRows = count( $dupes );
167
168                         # Show a short summary
169                         if ( $numRows == 1 ) {
170                                 $out->wrapWikiMsg(
171                                         "<p class='mw-fileduplicatesearch-result-1'>\n$1\n</p>",
172                                         [ 'fileduplicatesearch-result-1', wfEscapeWikiText( $this->filename ) ]
173                                 );
174                         } elseif ( $numRows ) {
175                                 $out->wrapWikiMsg(
176                                         "<p class='mw-fileduplicatesearch-result-n'>\n$1\n</p>",
177                                         [ 'fileduplicatesearch-result-n', wfEscapeWikiText( $this->filename ),
178                                                 $this->getLanguage()->formatNum( $numRows - 1 ) ]
179                                 );
180                         }
181
182                         $this->doBatchLookups( $dupes );
183                         $this->showList( $dupes );
184                 }
185         }
186
187         function doBatchLookups( $list ) {
188                 $batch = new LinkBatch();
189                 /** @var File $file */
190                 foreach ( $list as $file ) {
191                         $batch->addObj( $file->getTitle() );
192                         if ( $file->isLocal() ) {
193                                 $userName = $file->getUser( 'text' );
194                                 $batch->add( NS_USER, $userName );
195                                 $batch->add( NS_USER_TALK, $userName );
196                         }
197                 }
198
199                 $batch->execute();
200         }
201
202         /**
203          *
204          * @param Skin $skin
205          * @param File $result
206          * @return string HTML
207          */
208         function formatResult( $skin, $result ) {
209                 global $wgContLang;
210
211                 $linkRenderer = $this->getLinkRenderer();
212                 $nt = $result->getTitle();
213                 $text = $wgContLang->convert( $nt->getText() );
214                 $plink = $linkRenderer->makeLink(
215                         $nt,
216                         $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
236         /**
237          * Return an array of subpages beginning with $search that this special page will accept.
238          *
239          * @param string $search Prefix to search for
240          * @param int $limit Maximum number of results to return (usually 10)
241          * @param int $offset Number of results to skip (usually 0)
242          * @return string[] Matching subpages
243          */
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 }