Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
15.19% covered (danger)
15.19%
12 / 79
50.00% covered (danger)
50.00%
6 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
UserGroupMembership
15.38% covered (danger)
15.38%
12 / 78
50.00% covered (danger)
50.00%
6 / 12
435.54
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getUserId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getGroup
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getExpiry
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isExpired
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLink
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 getLinkHTML
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
12
 getLinkWiki
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 getLinkInfo
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 getLinkExpiryParams
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getGroupPage
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 equals
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\User;
8
9use InvalidArgumentException;
10use MediaWiki\Context\IContextSource;
11use MediaWiki\MediaWikiServices;
12use MediaWiki\Message\Message;
13use MediaWiki\Title\Title;
14
15/**
16 * Represents the membership of one user in one user group.
17 *
18 * For example, if user "Mary" belongs to "sysop" and "bureaucrat" groups,
19 * those memberships can be represented by two UserGroupMembership objects.
20 *
21 * The class is a value object. Use UserGroupManager to modify user group memberships.
22 *
23 * @since 1.29
24 * @ingroup User
25 */
26class UserGroupMembership {
27
28    /** @var int The ID of the user who belongs to the group */
29    private $userId;
30
31    /** @var string */
32    private $group;
33
34    /** @var string|null Timestamp of expiry in TS::MW format, or null if no expiry */
35    private $expiry;
36
37    /** @var bool Expiration flag */
38    private $expired;
39
40    /**
41     * @param int $userId The ID of the user who belongs to the group
42     * @param string|null $group The internal group name
43     * @param string|null $expiry Timestamp of expiry in TS::MW format, or null if no expiry
44     */
45    public function __construct( int $userId = 0, ?string $group = null, ?string $expiry = null ) {
46        $this->userId = $userId;
47        $this->group = $group;
48        $this->expiry = $expiry ?: null;
49        $this->expired = $expiry && wfTimestampNow() > $expiry;
50    }
51
52    /**
53     * @return int
54     */
55    public function getUserId() {
56        return $this->userId;
57    }
58
59    /**
60     * @return string
61     */
62    public function getGroup() {
63        return $this->group;
64    }
65
66    /**
67     * @return string|null Timestamp of expiry in TS::MW format, or null if no expiry
68     */
69    public function getExpiry() {
70        return $this->expiry;
71    }
72
73    /**
74     * Has the membership expired?
75     *
76     * @return bool
77     */
78    public function isExpired() {
79        return $this->expired;
80    }
81
82    /**
83     * Gets a link for a user group, possibly including the expiry date if relevant.
84     *
85     * @deprecated since 1.41 use getLinkWiki or getLinkHTML directly
86     *
87     * @param string|UserGroupMembership $ugm Either a group name as a string, or
88     *   a UserGroupMembership object
89     * @param IContextSource $context
90     * @param string $format Either 'wiki' or 'html'
91     * @param string|null $userName If you want to use the group member message
92     *   ("administrator"), pass the name of the user who belongs to the group; it
93     *   is used for GENDER of the group member message. If you instead want the
94     *   group name message ("Administrators"), omit this parameter.
95     * @return string
96     */
97    public static function getLink( $ugm, IContextSource $context, string $format, $userName = null ) {
98        switch ( $format ) {
99            case 'wiki':
100                return self::getLinkWiki( $ugm, $context, $userName );
101            case 'html':
102                return self::getLinkHTML( $ugm, $context, $userName );
103            default:
104                throw new InvalidArgumentException( 'UserGroupMembership::getLink() $format parameter should be ' .
105                    "'wiki' or 'html'" );
106        }
107    }
108
109    /**
110     * Gets a link for a user group, possibly including the expiry date if relevant.
111     * @since 1.41
112     *
113     * @param string|UserGroupMembership $ugm Either a group name as a string, or
114     *   a UserGroupMembership object
115     * @param IContextSource $context
116     * @param string|null $userName If you want to use the group member message
117     *   ("administrator"), pass the name of the user who belongs to the group; it
118     *   is used for GENDER of the group member message. If you instead want the
119     *   group name message ("Administrators"), omit this parameter.
120     * @return string
121     */
122    public static function getLinkHTML( $ugm, IContextSource $context, $userName = null ): string {
123        [
124            'expiry' => $expiry,
125            'linkTitle' => $linkTitle,
126            'groupName' => $groupName
127        ] = self::getLinkInfo( $ugm, $context, $userName );
128
129        // link to the group description page, if it exists
130        $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
131        if ( $linkTitle ) {
132            $groupLink = $linkRenderer->makeLink( $linkTitle, $groupName );
133        } else {
134            $groupLink = htmlspecialchars( $groupName );
135        }
136
137        if ( $expiry ) {
138            [
139                'expiryDT' => $expiryDT,
140                'expiryD' => $expiryD,
141                'expiryT' => $expiryT
142            ] = self::getLinkExpiryParams( $context, $expiry );
143            $groupLink = Message::rawParam( $groupLink );
144            return $context->msg( 'group-membership-link-with-expiry' )
145                ->params( $groupLink, $expiryDT, $expiryD, $expiryT )->escaped();
146        }
147        return $groupLink;
148    }
149
150    /**
151     * Gets a link for a user group, possibly including the expiry date if relevant.
152     * @since 1.41
153     *
154     * @param string|UserGroupMembership $ugm Either a group name as a string, or
155     *   a UserGroupMembership object
156     * @param IContextSource $context
157     * @param string|null $userName If you want to use the group member message
158     *   ("administrator"), pass the name of the user who belongs to the group; it
159     *   is used for GENDER of the group member message. If you instead want the
160     *   group name message ("Administrators"), omit this parameter.
161     * @return string
162     */
163    public static function getLinkWiki( $ugm, IContextSource $context, $userName = null ): string {
164        [
165            'expiry' => $expiry,
166            'linkTitle' => $linkTitle,
167            'groupName' => $groupName
168        ] = self::getLinkInfo( $ugm, $context, $userName );
169
170        // link to the group description page, if it exists
171        if ( $linkTitle ) {
172            $linkPage = $linkTitle->getFullText();
173            $groupLink = "[[$linkPage|$groupName]]";
174        } else {
175            $groupLink = $groupName;
176        }
177
178        if ( $expiry ) {
179            [
180                'expiryDT' => $expiryDT,
181                'expiryD' => $expiryD,
182                'expiryT' => $expiryT
183            ] = self::getLinkExpiryParams( $context, $expiry );
184            return $context->msg( 'group-membership-link-with-expiry' )
185                ->params( $groupLink, $expiryDT, $expiryD, $expiryT )->text();
186        }
187        return $groupLink;
188    }
189
190    /**
191     * @param self|string $ugm
192     * @param IContextSource $context
193     * @param string|null $userName
194     * @return array
195     */
196    private static function getLinkInfo( $ugm, $context, $userName = null ): array {
197        if ( $ugm instanceof UserGroupMembership ) {
198            $expiry = $ugm->getExpiry();
199            $group = $ugm->getGroup();
200        } else {
201            $expiry = null;
202            $group = $ugm;
203        }
204
205        $uiLanguage = $context->getLanguage();
206        if ( $userName !== null ) {
207            $groupName = $uiLanguage->getGroupMemberName( $group, $userName );
208        } else {
209            $groupName = $uiLanguage->getGroupName( $group );
210        }
211        $linkTitle = self::getGroupPage( $group );
212        return [ 'expiry' => $expiry, 'linkTitle' => $linkTitle, 'groupName' => $groupName ];
213    }
214
215    /**
216     * @param IContextSource $context
217     * @param string $expiry
218     * @return array
219     */
220    private static function getLinkExpiryParams( IContextSource $context, string $expiry ): array {
221        // format the expiry to a nice string
222        $uiLanguage = $context->getLanguage();
223        $uiUser = $context->getUser();
224        $expiryDT = $uiLanguage->userTimeAndDate( $expiry, $uiUser );
225        $expiryD = $uiLanguage->userDate( $expiry, $uiUser );
226        $expiryT = $uiLanguage->userTime( $expiry, $uiUser );
227        return [ 'expiryDT' => $expiryDT, 'expiryD' => $expiryD, 'expiryT' => $expiryT ];
228    }
229
230    /**
231     * Gets the title of a page describing a particular user group. When the name
232     * of the group appears in the UI, it can link to this page.
233     *
234     * @param string $group Internal group name
235     * @return Title|false Title of the page if it exists, false otherwise
236     */
237    public static function getGroupPage( $group ) {
238        $msg = wfMessage( "grouppage-$group" )->inContentLanguage();
239        if ( $msg->exists() ) {
240            $title = Title::newFromText( $msg->text() );
241            if ( $title ) {
242                return $title;
243            }
244        }
245        return false;
246    }
247
248    /**
249     * Compares two pure value objects
250     *
251     * @param UserGroupMembership $ugm
252     * @return bool
253     *
254     * @since 1.35
255     */
256    public function equals( UserGroupMembership $ugm ) {
257        return (
258            $ugm->getUserId() === $this->userId
259            && $ugm->getGroup() === $this->group
260        );
261    }
262
263}
264
265/** @deprecated class alias since 1.41 */
266class_alias( UserGroupMembership::class, 'UserGroupMembership' );