MediaWiki  master
SpecialFileDuplicateSearch.php
Go to the documentation of this file.
1 <?php
2 
28 
39  private $hash = '';
40 
44  private $filename = '';
45 
49  private $file = null;
50 
52  private $linkBatchFactory;
53 
55  private $repoGroup;
56 
58  private $searchEngineFactory;
59 
61  private $languageConverter;
62 
69  public function __construct(
70  LinkBatchFactory $linkBatchFactory,
71  RepoGroup $repoGroup,
72  SearchEngineFactory $searchEngineFactory,
73  LanguageConverterFactory $languageConverterFactory
74  ) {
75  parent::__construct( 'FileDuplicateSearch' );
76  $this->linkBatchFactory = $linkBatchFactory;
77  $this->repoGroup = $repoGroup;
78  $this->searchEngineFactory = $searchEngineFactory;
79  $this->languageConverter = $languageConverterFactory->getLanguageConverter( $this->getContentLanguage() );
80  }
81 
87  private function getDupes() {
88  return $this->repoGroup->findBySha1( $this->hash );
89  }
90 
94  private function showList( $dupes ) {
95  $html = [];
96  $html[] = "<ol class='special'>";
97 
98  foreach ( $dupes as $dupe ) {
99  $line = $this->formatResult( $dupe );
100  $html[] = "<li>" . $line . "</li>";
101  }
102  $html[] = '</ol>';
103 
104  $this->getOutput()->addHTML( implode( "\n", $html ) );
105  }
106 
107  public function execute( $par ) {
108  $this->setHeaders();
109  $this->outputHeader();
110 
111  $this->filename = $par ?? $this->getRequest()->getText( 'filename' );
112  $this->file = null;
113  $this->hash = '';
114  $title = Title::newFromText( $this->filename, NS_FILE );
115  if ( $title && $title->getText() != '' ) {
116  $this->file = $this->repoGroup->findFile( $title );
117  }
118 
119  $out = $this->getOutput();
120 
121  # Create the input form
122  $formFields = [
123  'filename' => [
124  'type' => 'text',
125  'name' => 'filename',
126  'label-message' => 'fileduplicatesearch-filename',
127  'id' => 'filename',
128  'size' => 50,
129  'default' => $this->filename,
130  ],
131  ];
132  $htmlForm = HTMLForm::factory( 'ooui', $formFields, $this->getContext() );
133  $htmlForm->setTitle( $this->getPageTitle() );
134  $htmlForm->setMethod( 'get' );
135  $htmlForm->setSubmitTextMsg( $this->msg( 'fileduplicatesearch-submit' ) );
136 
137  // The form should be visible always, even if it was submitted (e.g. to perform another action).
138  // To bypass the callback validation of HTMLForm, use prepareForm() and displayForm().
139  $htmlForm->prepareForm()->displayForm( false );
140 
141  if ( $this->file ) {
142  $this->hash = $this->file->getSha1();
143  } elseif ( $this->filename !== '' ) {
144  $out->wrapWikiMsg(
145  "<p class='mw-fileduplicatesearch-noresults'>\n$1\n</p>",
146  [ 'fileduplicatesearch-noresults', wfEscapeWikiText( $this->filename ) ]
147  );
148  }
149 
150  if ( $this->hash != '' ) {
151  # Show a thumbnail of the file
152  $img = $this->file;
153  if ( $img ) {
154  $thumb = $img->transform( [ 'width' => 120, 'height' => 120 ] );
155  if ( $thumb ) {
156  $out->addModuleStyles( 'mediawiki.special' );
157  $out->addHTML( '<div id="mw-fileduplicatesearch-icon">' .
158  $thumb->toHtml( [ 'desc-link' => false ] ) . '<br />' .
159  $this->msg( 'fileduplicatesearch-info' )
160  ->numParams( $img->getWidth(), $img->getHeight() )
161  ->sizeParams( $img->getSize() )
162  ->params( $img->getMimeType() )->parseAsBlock() .
163  '</div>' );
164  }
165  }
166 
167  $dupes = $this->getDupes();
168  $numRows = count( $dupes );
169 
170  # Show a short summary
171  if ( $numRows == 1 ) {
172  $out->wrapWikiMsg(
173  "<p class='mw-fileduplicatesearch-result-1'>\n$1\n</p>",
174  [ 'fileduplicatesearch-result-1', wfEscapeWikiText( $this->filename ) ]
175  );
176  } elseif ( $numRows ) {
177  $out->wrapWikiMsg(
178  "<p class='mw-fileduplicatesearch-result-n'>\n$1\n</p>",
179  [ 'fileduplicatesearch-result-n', wfEscapeWikiText( $this->filename ),
180  $this->getLanguage()->formatNum( $numRows - 1 ) ]
181  );
182  }
183 
184  $this->doBatchLookups( $dupes );
185  $this->showList( $dupes );
186  }
187  }
188 
192  private function doBatchLookups( $list ) {
193  $batch = $this->linkBatchFactory->newLinkBatch();
194  foreach ( $list as $file ) {
195  $batch->addObj( $file->getTitle() );
196  if ( $file->isLocal() ) {
197  $uploader = $file->getUploader( File::FOR_THIS_USER, $this->getAuthority() );
198  if ( $uploader ) {
199  $batch->add( NS_USER, $uploader->getName() );
200  $batch->add( NS_USER_TALK, $uploader->getName() );
201  }
202  }
203  }
204 
205  $batch->execute();
206  }
207 
212  private function formatResult( $result ) {
213  $linkRenderer = $this->getLinkRenderer();
214  $nt = $result->getTitle();
215  $text = $this->languageConverter->convert( $nt->getText() );
216  $plink = $linkRenderer->makeLink(
217  $nt,
218  $text
219  );
220 
221  $uploader = $result->getUploader( File::FOR_THIS_USER, $this->getAuthority() );
222  if ( $result->isLocal() && $uploader ) {
223  $user = Linker::userLink( $uploader->getId(), $uploader->getName() );
224  $user .= '<span style="white-space: nowrap;">';
225  $user .= Linker::userToolLinks( $uploader->getId(), $uploader->getName() );
226  $user .= '</span>';
227  } elseif ( $uploader ) {
228  $user = htmlspecialchars( $uploader->getName() );
229  } else {
230  $user = '<span class="history-deleted">'
231  . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
232  }
233 
234  $time = htmlspecialchars( $this->getLanguage()->userTimeAndDate(
235  $result->getTimestamp(), $this->getUser() ) );
236 
237  return "$plink . . $user . . $time";
238  }
239 
248  public function prefixSearchSubpages( $search, $limit, $offset ) {
249  $title = Title::newFromText( $search, NS_FILE );
250  if ( !$title || $title->getNamespace() !== NS_FILE ) {
251  // No prefix suggestion outside of file namespace
252  return [];
253  }
254  $searchEngine = $this->searchEngineFactory->create();
255  $searchEngine->setLimitOffset( $limit, $offset );
256  // Autocomplete subpage the same as a normal search, but just for files
257  $searchEngine->setNamespaces( [ NS_FILE ] );
258  $result = $searchEngine->defaultPrefixSearch( $search );
259 
260  return array_map( static function ( Title $t ) {
261  // Remove namespace in search suggestion
262  return $t->getText();
263  }, $result );
264  }
265 
266  protected function getGroupName() {
267  return 'media';
268  }
269 }
const NS_USER
Definition: Defines.php:66
const NS_FILE
Definition: Defines.php:70
const NS_USER_TALK
Definition: Defines.php:67
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
const FOR_THIS_USER
Definition: File.php:89
static factory( $displayFormat, $descriptor, IContextSource $context, $messagePrefix='')
Construct a HTMLForm object for given display type.
Definition: HTMLForm.php:344
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:1070
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:1115
An interface for creating language converters.
getLanguageConverter( $language=null)
Provide a LanguageConverter for given language.
Prioritized list of file repositories.
Definition: RepoGroup.php:29
Factory class for SearchEngine.
Searches the database for files of the requested hash, comparing this with the 'img_sha1' field in th...
execute( $par)
Default execute method Checks user permissions.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
__construct(LinkBatchFactory $linkBatchFactory, RepoGroup $repoGroup, SearchEngineFactory $searchEngineFactory, LanguageConverterFactory $languageConverterFactory)
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
Parent class for all special pages.
Definition: SpecialPage.php:44
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.
getAuthority()
Shortcut to get the Authority executing this instance.
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.
getContentLanguage()
Shortcut to get content language.
Represents a title within MediaWiki.
Definition: Title.php:49
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:370
$line
Definition: mcc.php:119
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42