MediaWiki  master
SpecialMediaStatistics.php
Go to the documentation of this file.
1 <?php
29 
34 
35  public const MAX_LIMIT = 5000;
36 
37  protected $totalCount = 0, $totalBytes = 0;
38 
42  protected $totalPerType = 0;
43 
47  protected $countPerType = 0;
48 
52  protected $totalSize = 0;
53 
55  private $mimeAnalyzer;
56 
62  public function __construct(
66  ) {
67  parent::__construct( 'MediaStatistics' );
68  // Generally speaking there is only a small number of file types,
69  // so just show all of them.
70  $this->limit = self::MAX_LIMIT;
71  $this->shownavigation = false;
72  $this->mimeAnalyzer = $mimeAnalyzer;
73  $this->setDBLoadBalancer( $loadBalancer );
74  $this->setLinkBatchFactory( $linkBatchFactory );
75  }
76 
77  public function isExpensive() {
78  return true;
79  }
80 
95  public function getQueryInfo() {
96  $dbr = $this->getDBLoadBalancer()->getConnectionRef( ILoadBalancer::DB_REPLICA );
97  $fakeTitle = $dbr->buildConcat( [
98  'img_media_type',
99  $dbr->addQuotes( ';' ),
100  'img_major_mime',
101  $dbr->addQuotes( '/' ),
102  'img_minor_mime',
103  $dbr->addQuotes( ';' ),
104  $dbr->buildStringCast( 'COUNT(*)' ),
105  $dbr->addQuotes( ';' ),
106  $dbr->buildStringCast( 'SUM( img_size )' )
107  ] );
108  return [
109  'tables' => [ 'image' ],
110  'fields' => [
111  'title' => $fakeTitle,
112  'namespace' => NS_MEDIA, /* needs to be something */
113  'value' => '1'
114  ],
115  'options' => [
116  'GROUP BY' => [
117  'img_media_type',
118  'img_major_mime',
119  'img_minor_mime',
120  ]
121  ]
122  ];
123  }
124 
132  protected function getOrderFields() {
133  return [ 'img_media_type', 'count(*)', 'img_major_mime', 'img_minor_mime' ];
134  }
135 
146  protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
147  $prevMediaType = null;
148  foreach ( $res as $row ) {
149  $mediaStats = $this->splitFakeTitle( $row->title );
150  if ( count( $mediaStats ) < 4 ) {
151  continue;
152  }
153  list( $mediaType, $mime, $totalCount, $totalBytes ) = $mediaStats;
154  if ( $prevMediaType !== $mediaType ) {
155  if ( $prevMediaType !== null ) {
156  // We're not at beginning, so we have to
157  // close the previous table.
158  $this->outputTableEnd();
159  }
160  $this->outputMediaType( $mediaType );
161  $this->totalPerType = 0;
162  $this->countPerType = 0;
163  $this->outputTableStart( $mediaType );
164  $prevMediaType = $mediaType;
165  }
166  $this->outputTableRow( $mime, intval( $totalCount ), intval( $totalBytes ) );
167  }
168  if ( $prevMediaType !== null ) {
169  $this->outputTableEnd();
170  // add total size of all files
171  $this->outputMediaType( 'total' );
172  $this->getOutput()->addWikiTextAsInterface(
173  $this->msg( 'mediastatistics-allbytes' )
174  ->numParams( $this->totalSize )
175  ->sizeParams( $this->totalSize )
176  ->numParams( $this->totalCount )
177  ->text()
178  );
179  }
180  }
181 
185  protected function outputTableEnd() {
186  $this->getOutput()->addHTML(
187  Html::closeElement( 'tbody' ) .
188  Html::closeElement( 'table' )
189  );
190  $this->getOutput()->addWikiTextAsInterface(
191  $this->msg( 'mediastatistics-bytespertype' )
192  ->numParams( $this->totalPerType )
193  ->sizeParams( $this->totalPerType )
194  ->numParams( $this->makePercentPretty( $this->totalPerType / $this->totalBytes ) )
195  ->numParams( $this->countPerType )
196  ->numParams( $this->makePercentPretty( $this->countPerType / $this->totalCount ) )
197  ->text()
198  );
199  $this->totalSize += $this->totalPerType;
200  }
201 
209  protected function outputTableRow( $mime, $count, $bytes ) {
210  $mimeSearch = SpecialPage::getTitleFor( 'MIMEsearch', $mime );
211  $linkRenderer = $this->getLinkRenderer();
212  $row = Html::rawElement(
213  'td',
214  [],
215  $linkRenderer->makeLink( $mimeSearch, $mime )
216  );
217  $row .= Html::rawElement(
218  'td',
219  [],
220  $this->getExtensionList( $mime )
221  );
222  $row .= Html::rawElement(
223  'td',
224  // Make sure js sorts it in numeric order
225  [ 'data-sort-value' => $count ],
226  $this->msg( 'mediastatistics-nfiles' )
227  ->numParams( $count )
229  ->numParams( $this->makePercentPretty( $count / $this->totalCount ) )
230  ->parse()
231  );
232  $row .= Html::rawElement(
233  'td',
234  // Make sure js sorts it in numeric order
235  [ 'data-sort-value' => $bytes ],
236  $this->msg( 'mediastatistics-nbytes' )
237  ->numParams( $bytes )
238  ->sizeParams( $bytes )
240  ->numParams( $this->makePercentPretty( $bytes / $this->totalBytes ) )
241  ->parse()
242  );
243  $this->totalPerType += $bytes;
244  $this->countPerType += $count;
245  $this->getOutput()->addHTML( Html::rawElement( 'tr', [], $row ) );
246  }
247 
252  protected function makePercentPretty( $decimal ) {
253  $decimal *= 100;
254  // Always show three useful digits
255  if ( $decimal == 0 ) {
256  return '0';
257  }
258  if ( $decimal >= 100 ) {
259  return '100';
260  }
261  $percent = sprintf( "%." . max( 0, 2 - floor( log10( $decimal ) ) ) . "f", $decimal );
262  // Then remove any trailing 0's
263  return preg_replace( '/\.?0*$/', '', $percent );
264  }
265 
272  private function getExtensionList( $mime ) {
273  $exts = $this->mimeAnalyzer->getExtensionsFromMimeType( $mime );
274  if ( !$exts ) {
275  return '';
276  }
277  foreach ( $exts as &$ext ) {
278  $ext = htmlspecialchars( '.' . $ext );
279  }
280 
281  return $this->getLanguage()->commaList( $exts );
282  }
283 
290  protected function outputTableStart( $mediaType ) {
291  $out = $this->getOutput();
292  $out->addModuleStyles( 'jquery.tablesorter.styles' );
293  $out->addModules( 'jquery.tablesorter' );
294  $out->addHTML(
296  'table',
297  [ 'class' => [
298  'mw-mediastats-table',
299  'mw-mediastats-table-' . strtolower( $mediaType ),
300  'sortable',
301  'wikitable'
302  ] ]
303  ) .
304  Html::rawElement( 'thead', [], $this->getTableHeaderRow() ) .
305  Html::openElement( 'tbody' )
306  );
307  }
308 
314  protected function getTableHeaderRow() {
315  $headers = [ 'mimetype', 'extensions', 'count', 'totalbytes' ];
316  $ths = '';
317  foreach ( $headers as $header ) {
318  $ths .= Html::rawElement(
319  'th',
320  [],
321  // for grep:
322  // mediastatistics-table-mimetype, mediastatistics-table-extensions
323  // mediastatistics-table-count, mediastatistics-table-totalbytes
324  $this->msg( 'mediastatistics-table-' . $header )->parse()
325  );
326  }
327  return Html::rawElement( 'tr', [], $ths );
328  }
329 
335  protected function outputMediaType( $mediaType ) {
336  $this->getOutput()->addHTML(
338  'h2',
339  [ 'class' => [
340  'mw-mediastats-mediatype',
341  'mw-mediastats-mediatype-' . strtolower( $mediaType )
342  ] ],
343  // for grep
344  // mediastatistics-header-unknown, mediastatistics-header-bitmap,
345  // mediastatistics-header-drawing, mediastatistics-header-audio,
346  // mediastatistics-header-video, mediastatistics-header-multimedia,
347  // mediastatistics-header-office, mediastatistics-header-text,
348  // mediastatistics-header-executable, mediastatistics-header-archive,
349  // mediastatistics-header-3d,
350  $this->msg( 'mediastatistics-header-' . strtolower( $mediaType ) )->text()
351  )
352  );
356  }
357 
364  private function splitFakeTitle( $fakeTitle ) {
365  return explode( ';', $fakeTitle, 4 );
366  }
367 
372  protected function getGroupName() {
373  return 'media';
374  }
375 
386  public function formatResult( $skin, $result ) {
387  throw new MWException( "unimplemented" );
388  }
389 
396  public function preprocessResults( $dbr, $res ) {
398  $this->totalCount = $this->totalBytes = 0;
399  foreach ( $res as $row ) {
400  $mediaStats = $this->splitFakeTitle( $row->title );
401  $this->totalCount += $mediaStats[2] ?? 0;
402  $this->totalBytes += $mediaStats[3] ?? 0;
403  }
404  $res->seek( 0 );
405  }
406 }
const NS_MEDIA
Definition: Defines.php:52
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:236
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:214
static openElement( $element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
Definition: Html.php:256
static closeElement( $element)
Returns "</$element>".
Definition: Html.php:320
MediaWiki exception.
Definition: MWException.php:29
Implements functions related to MIME types such as detection and mapping to file extension.
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:42
executeLBFromResultWrapper(IResultWrapper $res, $ns=null)
Creates a new LinkBatch object, adds all pages from the passed result wrapper (MUST include title and...
Definition: QueryPage.php:877
setDBLoadBalancer(ILoadBalancer $loadBalancer)
Definition: QueryPage.php:895
LinkBatchFactory null $linkBatchFactory
Definition: QueryPage.php:75
int $offset
The offset and limit in use, as passed to the query() function.
Definition: QueryPage.php:47
setLinkBatchFactory(LinkBatchFactory $linkBatchFactory)
Definition: QueryPage.php:136
getDBLoadBalancer()
Definition: QueryPage.php:903
ILoadBalancer null $loadBalancer
Definition: QueryPage.php:72
formatResult( $skin, $result)
This method isn't used, since we override outputResults, but we need to implement since abstract in p...
splitFakeTitle( $fakeTitle)
parse the fake title format that this special page abuses querycache with.
getExtensionList( $mime)
Given a mime type, return a comma separated list of allowed extensions.
getGroupName()
What group to put the page in.
preprocessResults( $dbr, $res)
Initialize total values so we can figure out percentages later.
int $totalSize
Combined file size of all files.
int $totalPerType
Combined file size of all files in a section.
__construct(MimeAnalyzer $mimeAnalyzer, ILoadBalancer $loadBalancer, LinkBatchFactory $linkBatchFactory)
int $countPerType
Combined file count of all files in a section.
outputResults( $out, $skin, $dbr, $res, $num, $offset)
Output the results of the query.
outputTableStart( $mediaType)
Output the start of the table.
getTableHeaderRow()
Get (not output) the header row for the table.
outputTableRow( $mime, $count, $bytes)
Output a row of the stats table.
isExpensive()
Is this query expensive (for some definition of expensive)? Then we don't let it run in miser mode.
getOrderFields()
How to sort the results.
outputMediaType( $mediaType)
Output a header for a new media type section.
getOutput()
Get the OutputPage being used for this instance.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
LinkRenderer null $linkRenderer
Definition: SpecialPage.php:81
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getLanguage()
Shortcut to get user's language.
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:39
Database cluster connection, tracking, load balancing, and transaction manager interface.
Result wrapper for grabbing data queried from an IDatabase object.
const DB_REPLICA
Definition: defines.php:25
$mime
Definition: router.php:60
if(!is_readable( $file)) $ext
Definition: router.php:48
$header