MediaWiki  master
SpecialStatistics.php
Go to the documentation of this file.
1 <?php
24 namespace MediaWiki\Specials;
25 
34 use Xml;
35 
43  private $edits, $good, $images, $total, $users,
45 
46  private UserGroupManager $userGroupManager;
47 
51  public function __construct( UserGroupManager $userGroupManager ) {
52  parent::__construct( 'Statistics' );
53  $this->userGroupManager = $userGroupManager;
54  }
55 
56  public function execute( $par ) {
57  $this->setHeaders();
58  $this->outputHeader();
59  $this->getOutput()->addModuleStyles( 'mediawiki.special' );
60 
61  $this->edits = SiteStats::edits();
62  $this->good = SiteStats::articles();
63  $this->images = SiteStats::images();
64  $this->total = SiteStats::pages();
65  $this->users = SiteStats::users();
66  $this->activeUsers = SiteStats::activeUsers();
67 
68  $text = Xml::openElement( 'table', [ 'class' => 'wikitable mw-statistics-table' ] );
69 
70  # Statistic - pages
71  $text .= $this->getPageStats();
72 
73  # Statistic - edits
74  $text .= $this->getEditStats();
75 
76  # Statistic - users
77  $text .= $this->getUserStats();
78 
79  # Statistic - usergroups
80  $text .= $this->getGroupStats();
81 
82  # Statistic - other
83  $extraStats = [];
84  if ( $this->getHookRunner()->onSpecialStatsAddExtra(
85  $extraStats, $this->getContext() )
86  ) {
87  $text .= $this->getOtherStats( $extraStats );
88  }
89 
90  $text .= Xml::closeElement( 'table' );
91 
92  # Customizable footer
93  $footer = $this->msg( 'statistics-footer' );
94  if ( !$footer->isBlank() ) {
95  $text .= "\n" . $footer->parse();
96  }
97 
98  $this->getOutput()->addHTML( $text );
99  }
100 
110  private function formatRow( $text, $number, $trExtraParams = [],
111  $descMsg = '', $descMsgParam = ''
112  ) {
113  if ( $descMsg ) {
114  $msg = $this->msg( $descMsg, $descMsgParam );
115  if ( !$msg->isDisabled() ) {
116  $descriptionHtml = $this->msg( 'parentheses' )->rawParams( $msg->parse() )
117  ->escaped();
118  $text .= "<br />" . Html::rawElement(
119  'small',
120  [ 'class' => 'mw-statistic-desc' ],
121  " $descriptionHtml"
122  );
123  }
124  }
125 
126  return Html::rawElement( 'tr', $trExtraParams,
127  Html::rawElement( 'td', [], $text ) .
128  Html::rawElement( 'td', [ 'class' => 'mw-statistics-numbers' ], $number )
129  );
130  }
131 
137  private function getPageStats() {
138  $linkRenderer = $this->getLinkRenderer();
139 
140  $specialAllPagesTitle = SpecialPage::getTitleFor( 'Allpages' );
141  $pageStatsHtml = Xml::openElement( 'tr' ) .
142  Xml::tags( 'th', [ 'colspan' => '2' ], $this->msg( 'statistics-header-pages' )
143  ->parse() ) .
144  Xml::closeElement( 'tr' ) .
145  $this->formatRow(
146  $this->getConfig()->get( MainConfigNames::MiserMode )
147  ? $this->msg( 'statistics-articles' )->escaped()
148  : $linkRenderer->makeKnownLink(
149  $specialAllPagesTitle,
150  $this->msg( 'statistics-articles' )->text(),
151  [], [ 'hideredirects' => 1 ] ),
152  $this->getLanguage()->formatNum( $this->good ),
153  [ 'class' => 'mw-statistics-articles' ],
154  'statistics-articles-desc' ) .
155  $this->formatRow( $linkRenderer->makeKnownLink( $specialAllPagesTitle,
156  $this->msg( 'statistics-pages' )->text() ),
157  $this->getLanguage()->formatNum( $this->total ),
158  [ 'class' => 'mw-statistics-pages' ],
159  'statistics-pages-desc' );
160 
161  // Show the image row only, when there are files or upload is possible
162  if ( $this->images !== 0 || $this->getConfig()->get( MainConfigNames::EnableUploads ) ) {
163  $pageStatsHtml .= $this->formatRow(
164  $linkRenderer->makeKnownLink( SpecialPage::getTitleFor( 'MediaStatistics' ),
165  $this->msg( 'statistics-files' )->text() ),
166  $this->getLanguage()->formatNum( $this->images ),
167  [ 'class' => 'mw-statistics-files' ], 'statistics-files-desc' );
168  }
169 
170  return $pageStatsHtml;
171  }
172 
173  private function getEditStats() {
174  return Xml::openElement( 'tr' ) .
175  Xml::tags( 'th', [ 'colspan' => '2' ],
176  $this->msg( 'statistics-header-edits' )->parse() ) .
177  Xml::closeElement( 'tr' ) .
178  $this->formatRow( $this->msg( 'statistics-edits' )->parse(),
179  $this->getLanguage()->formatNum( $this->edits ),
180  [ 'class' => 'mw-statistics-edits' ]
181  ) .
182  $this->formatRow( $this->msg( 'statistics-edits-average' )->parse(),
183  $this->getLanguage()->formatNum(
184  sprintf( '%.2f', $this->total ? $this->edits / $this->total : 0 )
185  ), [ 'class' => 'mw-statistics-edits-average' ]
186  );
187  }
188 
189  private function getUserStats() {
190  return Xml::openElement( 'tr' ) .
191  Xml::tags( 'th', [ 'colspan' => '2' ],
192  $this->msg( 'statistics-header-users' )->parse() ) .
193  Xml::closeElement( 'tr' ) .
194  $this->formatRow( $this->msg( 'statistics-users' )->parse() . ' ' .
195  $this->getLinkRenderer()->makeKnownLink(
196  SpecialPage::getTitleFor( 'Listusers' ),
197  $this->msg( 'listgrouprights-members' )->text()
198  ),
199  $this->getLanguage()->formatNum( $this->users ),
200  [ 'class' => 'mw-statistics-users' ]
201  ) .
202  $this->formatRow( $this->msg( 'statistics-users-active' )->parse() . ' ' .
203  $this->getLinkRenderer()->makeKnownLink(
204  SpecialPage::getTitleFor( 'Activeusers' ),
205  $this->msg( 'listgrouprights-members' )->text()
206  ),
207  $this->getLanguage()->formatNum( $this->activeUsers ),
208  [ 'class' => 'mw-statistics-users-active' ],
209  'statistics-users-active-desc',
210  $this->getLanguage()->formatNum(
211  $this->getConfig()->get( MainConfigNames::ActiveUserDays ) )
212  );
213  }
214 
215  private function getGroupStats() {
216  $linkRenderer = $this->getLinkRenderer();
217  $lang = $this->getLanguage();
218  $text = '';
219  foreach ( $this->userGroupManager->listAllGroups() as $group ) {
220  $groupnameLocalized = $lang->getGroupName( $group );
221  $linkTarget = UserGroupMembership::getGroupPage( $group )
222  ?: Title::makeTitleSafe( NS_PROJECT, $group );
223 
224  if ( $linkTarget ) {
225  $grouppage = $linkRenderer->makeLink(
226  $linkTarget,
227  $groupnameLocalized
228  );
229  } else {
230  $grouppage = htmlspecialchars( $groupnameLocalized );
231  }
232 
233  $grouplink = $linkRenderer->makeKnownLink(
234  SpecialPage::getTitleFor( 'Listusers' ),
235  $this->msg( 'listgrouprights-members' )->text(),
236  [],
237  [ 'group' => $group ]
238  );
239  # Add a class when a usergroup contains no members to allow hiding these rows
240  $classZero = '';
241  $countUsers = SiteStats::numberingroup( $group );
242  if ( $countUsers == 0 ) {
243  $classZero = ' statistics-group-zero';
244  }
245  $text .= $this->formatRow( $grouppage . ' ' . $grouplink,
246  $this->getLanguage()->formatNum( $countUsers ),
247  [ 'class' => 'statistics-group-' . Sanitizer::escapeClass( $group ) .
248  $classZero ] );
249  }
250 
251  return $text;
252  }
253 
261  private function getOtherStats( array $stats ) {
262  $return = '';
263 
264  foreach ( $stats as $header => $items ) {
265  // Identify the structure used
266  if ( is_array( $items ) ) {
267  // Ignore headers that are recursively set as legacy header
268  if ( $header !== 'statistics-header-hooks' ) {
269  $return .= $this->formatRowHeader( $header );
270  }
271 
272  // Collect all items that belong to the same header
273  foreach ( $items as $key => $value ) {
274  if ( is_array( $value ) ) {
275  $name = $value['name'];
276  $number = $value['number'];
277  } else {
278  $name = $this->msg( $key )->parse();
279  $number = $value;
280  }
281 
282  $return .= $this->formatRow(
283  $name,
284  $this->getLanguage()->formatNum( htmlspecialchars( $number ) ),
285  [ 'class' => 'mw-statistics-hook', 'id' => 'mw-' . $key ]
286  );
287  }
288  } else {
289  // Create the legacy header only once
290  if ( $return === '' ) {
291  $return .= $this->formatRowHeader( 'statistics-header-hooks' );
292  }
293 
294  // Recursively remap the legacy structure
295  $return .= $this->getOtherStats( [ 'statistics-header-hooks' =>
296  [ $header => $items ] ] );
297  }
298  }
299 
300  return $return;
301  }
302 
309  private function formatRowHeader( $header ) {
310  return Xml::openElement( 'tr' ) .
311  Xml::tags( 'th', [ 'colspan' => '2' ], $this->msg( $header )->parse() ) .
312  Xml::closeElement( 'tr' );
313  }
314 
315  protected function getGroupName() {
316  return 'wiki';
317  }
318 }
319 
324 class_alias( SpecialStatistics::class, 'SpecialStatistics' );
const NS_PROJECT
Definition: Defines.php:68
This class is a collection of static functions that serve two purposes:
Definition: Html.php:57
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:239
A class containing constants representing the names of configuration variables.
const EnableUploads
Name constant for the EnableUploads setting, for use with Config::get()
const MiserMode
Name constant for the MiserMode setting, for use with Config::get()
const ActiveUserDays
Name constant for the ActiveUserDays setting, for use with Config::get()
HTML sanitizer for MediaWiki.
Definition: Sanitizer.php:46
static escapeClass( $class)
Given a value, escape it so that it can be used as a CSS class and return it.
Definition: Sanitizer.php:1101
Static accessor class for site_stats and related things.
Definition: SiteStats.php:36
static numberingroup( $group)
Find the number of users in a given user group.
Definition: SiteStats.php:149
Parent class for all special pages.
Definition: SpecialPage.php:65
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
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,...
getConfig()
Shortcut to get main config object.
getContext()
Gets the context this SpecialPage is executed in.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
getLanguage()
Shortcut to get user's language.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
Special page lists various statistics, including the contents of site_stats, plus page view details i...
execute( $par)
Default execute method Checks user permissions.
__construct(UserGroupManager $userGroupManager)
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Represents a title within MediaWiki.
Definition: Title.php:76
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:650
Represents a "user group membership" – a specific instance of a user belonging to a group.
static getGroupPage( $group)
Gets the title of a page describing a particular user group.
Module of static functions for generating XML.
Definition: Xml.php:33
static closeElement( $element)
Shortcut to close an XML element.
Definition: Xml.php:124
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:115
static tags( $element, $attribs, $contents)
Same as Xml::element(), but does not escape contents.
Definition: Xml.php:141
$header
$footer