1<?php
2/**
3 * Implements Special:Activeusers
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 * Implements Special:Activeusers
26 *
27 * @ingroup SpecialPage
28 */
29class SpecialActiveUsers extends SpecialPage {
30
31	public function __construct() {
32		parent::__construct( 'Activeusers' );
33	}
34
35	/**
36	 * @param string|null $par Parameter passed to the page or null
37	 */
38	public function execute( $par ) {
39		$out = $this->getOutput();
40
41		$this->setHeaders();
42		$this->outputHeader();
43
44		$opts = new FormOptions();
45
46		$opts->add( 'username', '' );
47		$opts->add( 'groups', [] );
48		$opts->add( 'excludegroups', [] );
49		// Backwards-compatibility with old URLs
50		$opts->add( 'hidebots', false, FormOptions::BOOL );
51		$opts->add( 'hidesysops', false, FormOptions::BOOL );
52
53		$opts->fetchValuesFromRequest( $this->getRequest() );
54
55		if ( $par !== null ) {
56			$opts->setValue( 'username', $par );
57		}
58
59		$pager = new ActiveUsersPager( $this->getContext(), $opts );
60		$usersBody = $pager->getBody();
61
62		$this->buildForm();
63
64		if ( $usersBody ) {
65			$out->addHTML(
66				$pager->getNavigationBar() .
67				Html::rawElement( 'ul', [], $usersBody ) .
68				$pager->getNavigationBar()
69			);
70			$out->addModuleStyles( 'mediawiki.interface.helpers.styles' );
71		} else {
72			$out->addWikiMsg( 'activeusers-noresult' );
73		}
74	}
75
76	/**
77	 * Generate and output the form
78	 */
79	protected function buildForm() {
80		$groups = User::getAllGroups();
81
82		$options = [];
83		foreach ( $groups as $group ) {
84			$msg = htmlspecialchars( UserGroupMembership::getGroupName( $group ) );
85			$options[$msg] = $group;
86		}
87		ksort( $options );
88
89		// Backwards-compatibility with old URLs
90		$req = $this->getRequest();
91		$excludeDefault = [];
92		if ( $req->getCheck( 'hidebots' ) ) {
93			$excludeDefault[] = 'bot';
94		}
95		if ( $req->getCheck( 'hidesysops' ) ) {
96			$excludeDefault[] = 'sysop';
97		}
98
99		$formDescriptor = [
100			'username' => [
101				'type' => 'user',
102				'name' => 'username',
103				'label-message' => 'activeusers-from',
104			],
105			'groups' => [
106				'type' => 'multiselect',
107				'dropdown' => true,
108				'flatlist' => true,
109				'name' => 'groups',
110				'label-message' => 'activeusers-groups',
111				'options' => $options,
112			],
113			'excludegroups' => [
114				'type' => 'multiselect',
115				'dropdown' => true,
116				'flatlist' => true,
117				'name' => 'excludegroups',
118				'label-message' => 'activeusers-excludegroups',
119				'options' => $options,
120				'default' => $excludeDefault,
121			],
122		];
123
124		HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() )
125			// For the 'multiselect' field values to be preserved on submit
126			->setFormIdentifier( 'specialactiveusers' )
127			->setIntro( $this->getIntroText() )
128			->setWrapperLegendMsg( 'activeusers' )
129			->setSubmitTextMsg( 'activeusers-submit' )
130			// prevent setting subpage and 'username' parameter at the same time
131			->setAction( $this->getPageTitle()->getLocalURL() )
132			->setMethod( 'get' )
133			->prepareForm()
134			->displayForm( false );
135	}
136
137	/**
138	 * Return introductory message.
139	 * @return string
140	 */
141	protected function getIntroText() {
142		$days = $this->getConfig()->get( 'ActiveUserDays' );
143
144		$intro = $this->msg( 'activeusers-intro' )->numParams( $days )->parse();
145
146		// Mention the level of cache staleness...
147		$dbr = wfGetDB( DB_REPLICA, 'recentchanges' );
148		$rcMax = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', '', __METHOD__ );
149		if ( $rcMax ) {
150			$cTime = $dbr->selectField( 'querycache_info',
151				'qci_timestamp',
152				[ 'qci_type' => 'activeusers' ],
153				__METHOD__
154			);
155			if ( $cTime ) {
156				$secondsOld = wfTimestamp( TS_UNIX, $rcMax ) - wfTimestamp( TS_UNIX, $cTime );
157			} else {
158				$rcMin = $dbr->selectField( 'recentchanges', 'MIN(rc_timestamp)', '', __METHOD__ );
159				$secondsOld = time() - wfTimestamp( TS_UNIX, $rcMin );
160			}
161			if ( $secondsOld > 0 ) {
162				$intro .= $this->msg( 'cachedspecial-viewing-cached-ttl' )
163					->durationParams( $secondsOld )->parseAsBlock();
164			}
165		}
166
167		return $intro;
168	}
169
170	protected function getGroupName() {
171		return 'users';
172	}
173}
174