Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.92% covered (success)
95.92%
47 / 49
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
GrantsLocalization
95.92% covered (success)
95.92%
47 / 49
83.33% covered (warning)
83.33%
5 / 6
17
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
1
 getGrantDescription
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 getGrantDescriptions
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getGrantDescriptionsWithClasses
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
4
 getGrantsLink
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getGrantsWikiText
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
5.09
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\Permissions;
22
23use HtmlArmor;
24use Language;
25use MediaWiki\Html\Html;
26use MediaWiki\Languages\LanguageFactory;
27use MediaWiki\Linker\LinkRenderer;
28use MediaWiki\SpecialPage\SpecialPage;
29
30/**
31 * This separate service is needed because the ::getGrantsLink method requires a LinkRenderer
32 * and if we tried to inject a LinkRenderer into the GrantsInfo service, it would result in
33 * recursive service instantiation for sessions using the BotPasswordSessionProvider, as a
34 * result of injecting the LinkRenderer when trying to use a GrantsInfo method that doesn't
35 * even need it.
36 *
37 * @since 1.38
38 */
39class GrantsLocalization {
40    /** @var GrantsInfo */
41    private $grantsInfo;
42
43    /** @var LinkRenderer */
44    private $linkRenderer;
45
46    /** @var LanguageFactory */
47    private $languageFactory;
48
49    /** @var Language */
50    private $contentLanguage;
51
52    /**
53     * @param GrantsInfo $grantsInfo
54     * @param LinkRenderer $linkRenderer
55     * @param LanguageFactory $languageFactory
56     * @param Language $contentLanguage
57     */
58    public function __construct(
59        GrantsInfo $grantsInfo,
60        LinkRenderer $linkRenderer,
61        LanguageFactory $languageFactory,
62        Language $contentLanguage
63    ) {
64        $this->grantsInfo = $grantsInfo;
65        $this->linkRenderer = $linkRenderer;
66        $this->languageFactory = $languageFactory;
67        $this->contentLanguage = $contentLanguage;
68    }
69
70    /**
71     * Fetch the description of the grant.
72     * @param string $grant
73     * @param Language|string|null $lang
74     * @return string Grant description
75     */
76    public function getGrantDescription( string $grant, $lang = null ): string {
77        // Give grep a chance to find the usages:
78        // grant-blockusers, grant-createeditmovepage, grant-delete,
79        // grant-editinterface, grant-editmycssjs, grant-editmywatchlist,
80        // grant-editsiteconfig, grant-editpage, grant-editprotected,
81        // grant-highvolume, grant-oversight, grant-patrol, grant-protect,
82        // grant-rollback, grant-sendemail, grant-uploadeditmovefile,
83        // grant-uploadfile, grant-basic, grant-viewdeleted,
84        // grant-viewmywatchlist, grant-createaccount, grant-mergehistory,
85        // grant-import
86
87        // TODO: replace wfMessage with something that can be injected like TextFormatter
88        $msg = wfMessage( "grant-$grant" );
89
90        if ( $lang ) {
91            $msg->inLanguage( $lang );
92        }
93
94        if ( !$msg->exists() ) {
95            $msg = $lang
96                ? wfMessage( 'grant-generic', $grant )->inLanguage( $lang )
97                : wfMessage( 'grant-generic', $grant );
98        }
99
100        return $msg->text();
101    }
102
103    /**
104     * Fetch the descriptions for the grants.
105     * @param string[] $grants
106     * @param Language|string|null $lang
107     * @return string[] Corresponding grant descriptions, keyed by grant name
108     */
109    public function getGrantDescriptions( array $grants, $lang = null ): array {
110        $ret = [];
111
112        foreach ( $grants as $grant ) {
113            $ret[$grant] = $this->getGrantDescription( $grant, $lang );
114        }
115        return $ret;
116    }
117
118    /**
119     * Fetch the descriptions for the grants, like getGrantDescriptions, but with HTML classes
120     * for styling. The HTML is wikitext-compatible.
121     * @param string[] $grants
122     * @param Language|string|null $lang
123     * @return string[] Grant description HTML for each grant, in the same order
124     */
125    public function getGrantDescriptionsWithClasses( array $grants, $lang = null ): array {
126        $riskGroupsByGrant = $this->grantsInfo->getRiskGroupsByGrant( 'unknown' );
127        $grantDescriptions = $this->getGrantDescriptions( $grants, $lang );
128        $results = [];
129        foreach ( $grantDescriptions as $grant => $description ) {
130            $riskGroup = $riskGroupsByGrant[$grant] ?? 'unknown';
131            // Messages used here: grantriskgroup-vandalism, grantriskgroup-security,
132            // grantriskgroup-internal
133            $riskGroupMsg = wfMessage( "grantriskgroup-$riskGroup" );
134            if ( $lang ) {
135                $riskGroupMsg->inLanguage( $lang );
136            }
137            if ( $riskGroupMsg->exists() ) {
138                $riskDescription = $riskGroupMsg->text();
139            } else {
140                $riskDescription = '';
141            }
142            $results[] = htmlspecialchars( $description ) . ' ' .
143                Html::element( 'span', [ 'class' => "mw-grant mw-grantriskgroup-$riskGroup" ], $riskDescription );
144        }
145        return $results;
146    }
147
148    /**
149     * Generate a link to Special:ListGrants for a particular grant name.
150     *
151     * This can be used to link end users to a full description of what
152     * rights they are giving when they authorize a grant.
153     *
154     * @param string $grant the grant name
155     * @param Language|string|null $lang
156     * @return string (proto-relative) HTML link
157     */
158    public function getGrantsLink( string $grant, $lang = null ): string {
159        $riskGroupsByGrant = $this->grantsInfo->getRiskGroupsByGrant( 'unknown' );
160        $riskGroup = $riskGroupsByGrant[$grant] ?? 'unknown';
161        return $this->linkRenderer->makeKnownLink(
162            SpecialPage::getTitleFor( 'Listgrants', false, $grant ),
163            new HtmlArmor( $this->getGrantDescriptionsWithClasses( [ $grant ], $lang )[ 0 ] )
164        );
165    }
166
167    /**
168     * Generate wikitext to display a list of grants. It will be in the format
169     *     * <grant-group-$group>
170     *     : <grant-$grant>; <grant-$grant>; ...
171     *     * ...
172     * with some HTML classes for styling.
173     * @param string[]|null $grantsFilter If non-null, only display these grants.
174     * @param Language|string|null $lang
175     * @return string Wikitext
176     */
177    public function getGrantsWikiText( $grantsFilter, $lang = null ): string {
178        if ( is_string( $lang ) ) {
179            $lang = $this->languageFactory->getLanguage( $lang );
180        } elseif ( $lang === null ) {
181            $lang = $this->contentLanguage;
182        }
183
184        $s = '';
185        foreach ( $this->grantsInfo->getGrantGroups( $grantsFilter ) as $group => $grants ) {
186            if ( $group === 'hidden' ) {
187                continue; // implicitly granted
188            }
189            $grantDescriptionsWithClasses = $this->getGrantDescriptionsWithClasses( $grants, $lang );
190            // Give grep a chance to find the usages:
191            // grant-group-page-interaction, grant-group-file-interaction
192            // grant-group-watchlist-interaction, grant-group-email,
193            // grant-group-high-volume, grant-group-customization,
194            // grant-group-administration, grant-group-private-information,
195            // grant-group-other
196            $s .= "*<span class=\"mw-grantgroup\">" .
197                // TODO: replace wfMessage with something that can be injected like TextFormatter
198                wfMessage( "grant-group-$group" )->inLanguage( $lang )->text() . "</span>\n";
199            $s .= ":" . $lang->semicolonList( $grantDescriptionsWithClasses ) . "\n";
200        }
201        return "$s\n";
202    }
203}