Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
79.31% covered (warning)
79.31%
46 / 58
14.29% covered (danger)
14.29%
1 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
HTMLUserTextField
80.70% covered (warning)
80.70%
46 / 57
14.29% covered (danger)
14.29%
1 / 7
45.31
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 validate
91.67% covered (success)
91.67%
22 / 24
0.00% covered (danger)
0.00%
0 / 1
17.17
 isValidIPRange
80.00% covered (warning)
80.00%
12 / 15
0.00% covered (danger)
0.00%
0 / 1
15.57
 getInputWidget
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 shouldInfuseOOUI
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getOOUIModules
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getInputHtml
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\HTMLForm\Field;
4
5use MediaWiki\MediaWikiServices;
6use MediaWiki\User\ExternalUserNames;
7use MediaWiki\Widget\UserInputWidget;
8use Wikimedia\IPUtils;
9
10/**
11 * Implements a text input field for user names.
12 * Automatically auto-completes if using the OOUI display format.
13 *
14 * Optional parameters:
15 * 'exists' - Whether to validate that the user already exists
16 * 'external' - Whether an external user (imported actor) is interpreted as "valid"
17 * 'ipallowed' - Whether an IP address is interpreted as "valid"
18 * 'iprange' - Whether an IP address range is interpreted as "valid"
19 * 'iprangelimits' - Specifies the valid IP ranges for IPv4 and IPv6 in an array.
20 *
21 * @stable to extend
22 * @since 1.26
23 */
24class HTMLUserTextField extends HTMLTextField {
25    /**
26     * @stable to call
27     * @inheritDoc
28     */
29    public function __construct( $params ) {
30        $params = wfArrayPlus2d( $params, [
31                'exists' => false,
32                'external' => false,
33                'ipallowed' => false,
34                'iprange' => false,
35                'iprangelimits' => [
36                    'IPv4' => 0,
37                    'IPv6' => 0,
38                ],
39            ]
40        );
41
42        parent::__construct( $params );
43    }
44
45    public function validate( $value, $alldata ) {
46        // If the value is null, reset it to an empty string which is what is expected by the parent.
47        if ( $value === null ) {
48            $value = '';
49        }
50
51        // If the value is empty, there are no additional checks that can be performed.
52        if ( $value === '' ) {
53            return parent::validate( $value, $alldata );
54        }
55
56        // check if the input is a valid username
57        $user = MediaWikiServices::getInstance()->getUserFactory()->newFromName( $value );
58        if ( $user ) {
59            // check if the user exists, if requested
60            if ( $this->mParams['exists'] && !(
61                $user->isRegistered() &&
62                // Treat hidden users as unregistered if current user can't view them (T309894)
63                !( $user->isHidden() && !( $this->mParent && $this->mParent->getUser()->isAllowed( 'hideuser' ) ) )
64            ) ) {
65                return $this->msg( 'htmlform-user-not-exists', $user->getName() );
66            }
67        } else {
68            // not a valid username
69            $valid = false;
70            // check if the input is a valid external user
71            if ( $this->mParams['external'] && ExternalUserNames::isExternal( $value ) ) {
72                $valid = true;
73            }
74            // check if the input is a valid IP address
75            if ( $this->mParams['ipallowed'] && IPUtils::isValid( $value ) ) {
76                $valid = true;
77            }
78            // check if the input is a valid IP range
79            if ( $this->mParams['iprange'] ) {
80                $rangeError = $this->isValidIPRange( $value );
81                if ( $rangeError === true ) {
82                    $valid = true;
83                } elseif ( $rangeError !== false ) {
84                    return $rangeError;
85                }
86            }
87            if ( !$valid ) {
88                return $this->msg( 'htmlform-user-not-valid', $value );
89            }
90        }
91
92        return parent::validate( $value, $alldata );
93    }
94
95    protected function isValidIPRange( $value ) {
96        $cidrIPRanges = $this->mParams['iprangelimits'];
97
98        if ( !IPUtils::isValidRange( $value ) ) {
99            return false;
100        }
101
102        [ $ip, $range ] = explode( '/', $value, 2 );
103
104        if (
105            ( IPUtils::isIPv4( $ip ) && $cidrIPRanges['IPv4'] == 32 ) ||
106            ( IPUtils::isIPv6( $ip ) && $cidrIPRanges['IPv6'] == 128 )
107        ) {
108            // Range block effectively disabled
109            return $this->msg( 'ip_range_toolow' );
110        }
111
112        if (
113            ( IPUtils::isIPv4( $ip ) && $range > 32 ) ||
114            ( IPUtils::isIPv6( $ip ) && $range > 128 )
115        ) {
116            // Dodgy range
117            return $this->msg( 'ip_range_invalid' );
118        }
119
120        if ( IPUtils::isIPv4( $ip ) && $range < $cidrIPRanges['IPv4'] ) {
121            return $this->msg( 'ip_range_exceeded', $cidrIPRanges['IPv4'] );
122        }
123
124        if ( IPUtils::isIPv6( $ip ) && $range < $cidrIPRanges['IPv6'] ) {
125            return $this->msg( 'ip_range_exceeded', $cidrIPRanges['IPv6'] );
126        }
127
128        return true;
129    }
130
131    protected function getInputWidget( $params ) {
132        return new UserInputWidget( $params );
133    }
134
135    protected function shouldInfuseOOUI() {
136        return true;
137    }
138
139    protected function getOOUIModules() {
140        return [ 'mediawiki.widgets.UserInputWidget' ];
141    }
142
143    public function getInputHtml( $value ) {
144        // add the required module and css class for user suggestions in non-OOUI mode
145        $this->mParent->getOutput()->addModules( 'mediawiki.userSuggest' );
146        $this->mClass .= ' mw-autocomplete-user';
147
148        // return parent html
149        return parent::getInputHTML( $value );
150    }
151}
152
153/** @deprecated class alias since 1.42 */
154class_alias( HTMLUserTextField::class, 'HTMLUserTextField' );