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(
63  MimeAnalyzer $mimeAnalyzer,
64  ILoadBalancer $loadBalancer,
65  LinkBatchFactory $linkBatchFactory
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  [ $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:898
setDBLoadBalancer(ILoadBalancer $loadBalancer)
Definition: QueryPage.php:916
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:924
formatResult( $skin, $result)
This method isn't used, since we override outputResults, but we need to implement since abstract in p...
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()
Should this query page only be updated offline on large wikis?
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,...
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:40
Create and track the database connections and transactions for a given database cluster.
Result wrapper for grabbing data queried from an IDatabase object.
const DB_REPLICA
Definition: defines.php:26
$mime
Definition: router.php:60
if(!is_readable( $file)) $ext
Definition: router.php:48
$header