]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/specials/SpecialStatistics.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / includes / specials / SpecialStatistics.php
1 <?php
2 /**
3  * Implements Special:Statistics
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  * @file
21  * @ingroup SpecialPage
22  */
23
24 /**
25  * Special page lists various statistics, including the contents of
26  * `site_stats`, plus page view details if enabled
27  *
28  * @ingroup SpecialPage
29  */
30 class SpecialStatistics extends SpecialPage {
31         private $edits, $good, $images, $total, $users,
32                 $activeUsers = 0;
33
34         public function __construct() {
35                 parent::__construct( 'Statistics' );
36         }
37
38         public function execute( $par ) {
39                 $this->setHeaders();
40                 $this->getOutput()->addModuleStyles( 'mediawiki.special' );
41
42                 $this->edits = SiteStats::edits();
43                 $this->good = SiteStats::articles();
44                 $this->images = SiteStats::images();
45                 $this->total = SiteStats::pages();
46                 $this->users = SiteStats::users();
47                 $this->activeUsers = SiteStats::activeUsers();
48                 $this->hook = '';
49
50                 $text = Xml::openElement( 'table', [ 'class' => 'wikitable mw-statistics-table' ] );
51
52                 # Statistic - pages
53                 $text .= $this->getPageStats();
54
55                 # Statistic - edits
56                 $text .= $this->getEditStats();
57
58                 # Statistic - users
59                 $text .= $this->getUserStats();
60
61                 # Statistic - usergroups
62                 $text .= $this->getGroupStats();
63
64                 # Statistic - other
65                 $extraStats = [];
66                 if ( Hooks::run( 'SpecialStatsAddExtra', [ &$extraStats, $this->getContext() ] ) ) {
67                         $text .= $this->getOtherStats( $extraStats );
68                 }
69
70                 $text .= Xml::closeElement( 'table' );
71
72                 # Customizable footer
73                 $footer = $this->msg( 'statistics-footer' );
74                 if ( !$footer->isBlank() ) {
75                         $text .= "\n" . $footer->parse();
76                 }
77
78                 $this->getOutput()->addHTML( $text );
79         }
80
81         /**
82          * Format a row
83          * @param string $text Description of the row
84          * @param float $number A statistical number
85          * @param array $trExtraParams Params to table row, see Html::elememt
86          * @param string $descMsg Message key
87          * @param array|string $descMsgParam Message parameters
88          * @return string Table row in HTML format
89          */
90         private function formatRow( $text, $number, $trExtraParams = [],
91                 $descMsg = '', $descMsgParam = ''
92         ) {
93                 if ( $descMsg ) {
94                         $msg = $this->msg( $descMsg, $descMsgParam );
95                         if ( !$msg->isDisabled() ) {
96                                 $descriptionHtml = $this->msg( 'parentheses' )->rawParams( $msg->parse() )
97                                         ->escaped();
98                                 $text .= "<br />" . Html::rawElement(
99                                         'small',
100                                         [ 'class' => 'mw-statistic-desc' ],
101                                         " $descriptionHtml"
102                                 );
103                         }
104                 }
105
106                 return Html::rawElement( 'tr', $trExtraParams,
107                         Html::rawElement( 'td', [], $text ) .
108                         Html::rawElement( 'td', [ 'class' => 'mw-statistics-numbers' ], $number )
109                 );
110         }
111
112         /**
113          * Each of these methods is pretty self-explanatory, get a particular
114          * row for the table of statistics
115          * @return string
116          */
117         private function getPageStats() {
118                 $linkRenderer = $this->getLinkRenderer();
119
120                 $specialAllPagesTitle = SpecialPage::getTitleFor( 'Allpages' );
121                 $pageStatsHtml = Xml::openElement( 'tr' ) .
122                         Xml::tags( 'th', [ 'colspan' => '2' ], $this->msg( 'statistics-header-pages' )
123                                 ->parse() ) .
124                         Xml::closeElement( 'tr' ) .
125                                 $this->formatRow( $linkRenderer->makeKnownLink(
126                                         $specialAllPagesTitle,
127                                         $this->msg( 'statistics-articles' )->text(),
128                                         [], [ 'hideredirects' => 1 ] ),
129                                         $this->getLanguage()->formatNum( $this->good ),
130                                         [ 'class' => 'mw-statistics-articles' ],
131                                         'statistics-articles-desc' ) .
132                                 $this->formatRow( $linkRenderer->makeKnownLink( $specialAllPagesTitle,
133                                         $this->msg( 'statistics-pages' )->text() ),
134                                         $this->getLanguage()->formatNum( $this->total ),
135                                         [ 'class' => 'mw-statistics-pages' ],
136                                         'statistics-pages-desc' );
137
138                 // Show the image row only, when there are files or upload is possible
139                 if ( $this->images !== 0 || $this->getConfig()->get( 'EnableUploads' ) ) {
140                         $pageStatsHtml .= $this->formatRow(
141                                 $linkRenderer->makeKnownLink( SpecialPage::getTitleFor( 'MediaStatistics' ),
142                                 $this->msg( 'statistics-files' )->text() ),
143                                 $this->getLanguage()->formatNum( $this->images ),
144                                 [ 'class' => 'mw-statistics-files' ] );
145                 }
146
147                 return $pageStatsHtml;
148         }
149
150         private function getEditStats() {
151                 return Xml::openElement( 'tr' ) .
152                         Xml::tags( 'th', [ 'colspan' => '2' ],
153                                 $this->msg( 'statistics-header-edits' )->parse() ) .
154                         Xml::closeElement( 'tr' ) .
155                         $this->formatRow( $this->msg( 'statistics-edits' )->parse(),
156                                 $this->getLanguage()->formatNum( $this->edits ),
157                                 [ 'class' => 'mw-statistics-edits' ]
158                         ) .
159                         $this->formatRow( $this->msg( 'statistics-edits-average' )->parse(),
160                                 $this->getLanguage()->formatNum(
161                                         sprintf( '%.2f', $this->total ? $this->edits / $this->total : 0 )
162                                 ), [ 'class' => 'mw-statistics-edits-average' ]
163                         );
164         }
165
166         private function getUserStats() {
167                 return Xml::openElement( 'tr' ) .
168                         Xml::tags( 'th', [ 'colspan' => '2' ],
169                                 $this->msg( 'statistics-header-users' )->parse() ) .
170                         Xml::closeElement( 'tr' ) .
171                         $this->formatRow( $this->msg( 'statistics-users' )->parse(),
172                                 $this->getLanguage()->formatNum( $this->users ),
173                                 [ 'class' => 'mw-statistics-users' ]
174                         ) .
175                         $this->formatRow( $this->msg( 'statistics-users-active' )->parse() . ' ' .
176                                 $this->getLinkRenderer()->makeKnownLink(
177                                         SpecialPage::getTitleFor( 'Activeusers' ),
178                                         $this->msg( 'listgrouprights-members' )->text()
179                                 ),
180                                 $this->getLanguage()->formatNum( $this->activeUsers ),
181                                 [ 'class' => 'mw-statistics-users-active' ],
182                                 'statistics-users-active-desc',
183                                 $this->getLanguage()->formatNum(
184                                         $this->getConfig()->get( 'ActiveUserDays' ) )
185                         );
186         }
187
188         private function getGroupStats() {
189                 $linkRenderer = $this->getLinkRenderer();
190                 $text = '';
191                 foreach ( $this->getConfig()->get( 'GroupPermissions' ) as $group => $permissions ) {
192                         # Skip generic * and implicit groups
193                         if ( in_array( $group, $this->getConfig()->get( 'ImplicitGroups' ) )
194                                 || $group == '*' ) {
195                                 continue;
196                         }
197                         $groupname = htmlspecialchars( $group );
198                         $msg = $this->msg( 'group-' . $groupname );
199                         if ( $msg->isBlank() ) {
200                                 $groupnameLocalized = $groupname;
201                         } else {
202                                 $groupnameLocalized = $msg->text();
203                         }
204                         $msg = $this->msg( 'grouppage-' . $groupname )->inContentLanguage();
205                         if ( $msg->isBlank() ) {
206                                 $grouppageLocalized = MWNamespace::getCanonicalName( NS_PROJECT ) .
207                                         ':' . $groupname;
208                         } else {
209                                 $grouppageLocalized = $msg->text();
210                         }
211                         $linkTarget = Title::newFromText( $grouppageLocalized );
212
213                         if ( $linkTarget ) {
214                                 $grouppage = $linkRenderer->makeLink(
215                                         $linkTarget,
216                                         $groupnameLocalized
217                                 );
218                         } else {
219                                 $grouppage = htmlspecialchars( $groupnameLocalized );
220                         }
221
222                         $grouplink = $linkRenderer->makeKnownLink(
223                                 SpecialPage::getTitleFor( 'Listusers' ),
224                                 $this->msg( 'listgrouprights-members' )->text(),
225                                 [],
226                                 [ 'group' => $group ]
227                         );
228                         # Add a class when a usergroup contains no members to allow hiding these rows
229                         $classZero = '';
230                         $countUsers = SiteStats::numberingroup( $groupname );
231                         if ( $countUsers == 0 ) {
232                                 $classZero = ' statistics-group-zero';
233                         }
234                         $text .= $this->formatRow( $grouppage . ' ' . $grouplink,
235                                 $this->getLanguage()->formatNum( $countUsers ),
236                                 [ 'class' => 'statistics-group-' . Sanitizer::escapeClass( $group ) .
237                                         $classZero ] );
238                 }
239
240                 return $text;
241         }
242
243         /**
244          * Conversion of external statistics into an internal representation
245          * Following a ([<header-message>][<item-message>] = number) pattern
246          *
247          * @param array $stats
248          * @return string
249          */
250         private function getOtherStats( array $stats ) {
251                 $return = '';
252
253                 foreach ( $stats as $header => $items ) {
254                         // Identify the structure used
255                         if ( is_array( $items ) ) {
256                                 // Ignore headers that are recursively set as legacy header
257                                 if ( $header !== 'statistics-header-hooks' ) {
258                                         $return .= $this->formatRowHeader( $header );
259                                 }
260
261                                 // Collect all items that belong to the same header
262                                 foreach ( $items as $key => $value ) {
263                                         if ( is_array( $value ) ) {
264                                                 $name = $value['name'];
265                                                 $number = $value['number'];
266                                         } else {
267                                                 $name = $this->msg( $key )->parse();
268                                                 $number = $value;
269                                         }
270
271                                         $return .= $this->formatRow(
272                                                 $name,
273                                                 $this->getLanguage()->formatNum( htmlspecialchars( $number ) ),
274                                                 [ 'class' => 'mw-statistics-hook', 'id' => 'mw-' . $key ]
275                                         );
276                                 }
277                         } else {
278                                 // Create the legacy header only once
279                                 if ( $return === '' ) {
280                                         $return .= $this->formatRowHeader( 'statistics-header-hooks' );
281                                 }
282
283                                 // Recursively remap the legacy structure
284                                 $return .= $this->getOtherStats( [ 'statistics-header-hooks' =>
285                                         [ $header => $items ] ] );
286                         }
287                 }
288
289                 return $return;
290         }
291
292         /**
293          * Format row header
294          *
295          * @param string $header
296          * @return string
297          */
298         private function formatRowHeader( $header ) {
299                 return Xml::openElement( 'tr' ) .
300                         Xml::tags( 'th', [ 'colspan' => '2' ], $this->msg( $header )->parse() ) .
301                         Xml::closeElement( 'tr' );
302         }
303
304         protected function getGroupName() {
305                 return 'wiki';
306         }
307 }