Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 139
0.00% covered (danger)
0.00%
0 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
Definitions
0.00% covered (danger)
0.00%
0 / 139
0.00% covered (danger)
0.00%
0 / 16
1332
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setContext
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 buildMenuEntry
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 insertAuthMenuItem
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 msg
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 insertNearbyIfSupported
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 insertMobileOptionsItem
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
42
 insertPreferencesItem
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 insertAboutItem
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 insertDisclaimersItem
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 insertRecentChanges
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 insertSpecialPages
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 insertCommunityPortal
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
12
 newLogInOutQuery
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
42
 newReturnToQuery
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 insertDonateItem
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
12
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\Minerva\Menu;
22
23use IContextSource;
24use MediaWiki\Minerva\Menu\Entries\AuthMenuEntry;
25use MediaWiki\Minerva\Menu\Entries\SingleMenuEntry;
26use MediaWiki\SpecialPage\SpecialPage;
27use MediaWiki\SpecialPage\SpecialPageFactory;
28use MediaWiki\Title\Title;
29use MediaWiki\User\UserIdentity;
30use Message;
31
32/**
33 * Set of all known menu items for easier building
34 */
35final class Definitions {
36
37    private SpecialPageFactory $specialPageFactory;
38    private IContextSource $context;
39    private UserIdentity $user;
40
41    /**
42     * Initialize definitions helper class
43     *
44     * @param SpecialPageFactory $specialPageFactory
45     */
46    public function __construct(
47        SpecialPageFactory $specialPageFactory
48    ) {
49        $this->specialPageFactory = $specialPageFactory;
50    }
51
52    public function setContext( IContextSource $context ): self {
53        $this->context = $context;
54        $this->user = $context->getUser();
55        return $this;
56    }
57
58    /**
59     * Builds a meny entry.
60     *
61     * @param string $name
62     * @param string $text Entry label
63     * @param string $url The URL entry points to
64     * @param string $className Optional HTML classes
65     * @param string|null $icon defaults to $name if not specified
66     * @param bool $trackable Whether an entry will track clicks or not. Default is false.
67     * @return SingleMenuEntry
68     */
69    private function buildMenuEntry(
70        $name,
71        $text,
72        $url,
73        $className = '',
74        $icon = null,
75        $trackable = false
76    ): SingleMenuEntry {
77        return SingleMenuEntry::create( $name, $text, $url, $className, $icon, $trackable );
78    }
79
80    /**
81     * Creates a login or logout button with a profile button.
82     *
83     * @param Group $group
84     */
85    public function insertAuthMenuItem( Group $group ): void {
86        $group->insertEntry( new AuthMenuEntry(
87                $this->user,
88                $this->context,
89                $this->newLogInOutQuery( $this->newReturnToQuery() )
90        ) );
91    }
92
93    /**
94     * Perform message localization
95     * @param string $key to localize
96     * @return Message
97     */
98    public function msg( string $key ): Message {
99        return $this->context->msg( $key );
100    }
101
102    /**
103     * If Nearby is supported, build and inject the Nearby link
104     * @param Group $group
105     */
106    public function insertNearbyIfSupported( Group $group ): void {
107        // Nearby link (if supported)
108        if ( $this->specialPageFactory->exists( 'Nearby' ) ) {
109            $entry = $this->buildMenuEntry(
110                'nearby',
111                $this->context->msg( 'mobile-frontend-main-menu-nearby' )->text(),
112                SpecialPage::getTitleFor( 'Nearby' )->getLocalURL(),
113                '',
114                'mapPin',
115                true
116            );
117            // Setting this feature for javascript only
118            $entry->setJSOnly();
119            $group->insertEntry( $entry );
120        }
121    }
122
123    /**
124     * Build and insert the Settings link
125     * @param Group $group
126     */
127    public function insertMobileOptionsItem( Group $group ): void {
128        $title = $this->context->getTitle();
129        $config = $this->context->getConfig();
130        $returnToTitle = $title->getPrefixedText();
131        $user = $this->user;
132        $betaEnabled = $config->get( 'MFEnableBeta' );
133        /*
134         * to avoid linking to an empty settings page we make this jsonly when:
135         * - AMC and beta is disabled (if logged in there is nothing to show)
136         * - user is logged out and beta is disabled (beta is the only thing a non-js user can do)
137         * In future we might want to make this a static function on Special:MobileOptions.
138         */
139        $jsonly = ( !$user->isRegistered() && !$betaEnabled ) ||
140            ( $user->isRegistered() && !$config->get( 'MFAdvancedMobileContributions' ) &&
141                !$betaEnabled
142            );
143
144        $entry = $this->buildMenuEntry(
145            'settings',
146            $this->context->msg( 'mobile-frontend-main-menu-settings' )->text(),
147            SpecialPage::getTitleFor( 'MobileOptions' )
148                ->getLocalURL( [ 'returnto' => $returnToTitle ] ),
149            '',
150            null,
151            true
152        );
153        if ( $jsonly ) {
154            $entry->setJSOnly();
155        }
156        $group->insertEntry( $entry );
157    }
158
159    /**
160     * Build and insert the Preferences link
161     * @param Group $group
162     */
163    public function insertPreferencesItem( Group $group ): void {
164        $entry = $this->buildMenuEntry(
165            'preferences',
166            $this->context->msg( 'preferences' )->text(),
167            SpecialPage::getTitleFor( 'Preferences' )->getLocalURL(),
168            '',
169            'settings',
170            true
171        );
172        $group->insertEntry( $entry );
173    }
174
175    /**
176     * Build and insert About page link
177     * @param Group $group
178     */
179    public function insertAboutItem( Group $group ): void {
180        $msg = $this->context->msg( 'aboutsite' );
181        if ( $msg->isDisabled() ) {
182            return;
183        }
184        $title = Title::newFromText( $this->context->msg( 'aboutpage' )->inContentLanguage()->text() );
185        if ( !$title ) {
186            return;
187        }
188        $entry = $this->buildMenuEntry( 'about', $msg->text(), $title->getLocalURL() );
189        $entry->setIcon( null );
190        $group->insertEntry( $entry );
191    }
192
193    /**
194     * Build and insert Disclaimers link
195     * @param Group $group
196     */
197    public function insertDisclaimersItem( Group $group ): void {
198        $msg = $this->context->msg( 'disclaimers' );
199        if ( $msg->isDisabled() ) {
200            return;
201        }
202        $title = Title::newFromText( $this->context->msg( 'disclaimerpage' )
203            ->inContentLanguage()->text() );
204        if ( !$title ) {
205            return;
206        }
207        $entry = $this->buildMenuEntry( 'disclaimers', $msg->text(), $title->getLocalURL() );
208        $entry->setIcon( null );
209        $group->insertEntry( $entry );
210    }
211
212    /**
213     * Build and insert the RecentChanges link
214     * @param Group $group
215     */
216    public function insertRecentChanges( Group $group ): void {
217        $entry = $this->buildMenuEntry(
218            'recentchanges',
219            $this->context->msg( 'recentchanges' )->escaped(),
220            SpecialPage::getTitleFor( 'Recentchanges' )->getLocalURL(),
221            '',
222            'recentChanges',
223            true
224        );
225        $group->insertEntry( $entry );
226    }
227
228    /**
229     * Build and insert the SpecialPages link
230     * @param Group $group
231     */
232    public function insertSpecialPages( Group $group ): void {
233        $entry = $this->buildMenuEntry(
234            'specialPages',
235            $this->context->msg( 'specialpages' )->text(),
236            SpecialPage::getTitleFor( 'Specialpages' )->getLocalURL(),
237            '',
238            null,
239            true
240        );
241        $group->insertEntry( $entry );
242    }
243
244    /**
245     * Build and insert the CommunityPortal link
246     * @param Group $group
247     */
248    public function insertCommunityPortal( Group $group ): void {
249        $msg = $this->context->msg( 'portal' );
250        if ( $msg->isDisabled() ) {
251            return;
252        }
253        $title = Title::newFromText( $this->context->msg( 'portal-url' )
254            ->inContentLanguage()->text() );
255        if ( !$title ) {
256            return;
257        }
258        $entry = $this->buildMenuEntry(
259            'speechBubbles',
260            $msg->text(),
261            $title->getLocalURL(),
262            '',
263            null,
264            true
265        );
266        $group->insertEntry( $entry );
267    }
268
269    /**
270     * @param array $returnToQuery
271     * @return array
272     */
273    private function newLogInOutQuery( array $returnToQuery ): array {
274        $ret = [];
275        $title = $this->context->getTitle();
276        if ( $title && !$title->isSpecial( 'Userlogin' ) ) {
277            $ret[ 'returnto' ] = $title->getPrefixedText();
278        }
279        if ( $this->user && !$this->user->isRegistered() ) {
280            // unset campaign on login link so as not to interfere with A/B tests
281            unset( $returnToQuery['campaign'] );
282        }
283        if ( $returnToQuery ) {
284            $ret['returntoquery'] = wfArrayToCgi( $returnToQuery );
285        }
286        return $ret;
287    }
288
289    /**
290     * Retrieve current query parameters from Request object so system can pass those
291     * to the Login/logout links
292     * Some parameters are disabled (like title), as the returnto will be replaced with
293     * the current page.
294     * @return array
295     */
296    private function newReturnToQuery(): array {
297        $returnToQuery = [];
298        $request = $this->context->getRequest();
299        if ( !$request->wasPosted() ) {
300            $returnToQuery = $request->getValues();
301            unset( $returnToQuery['title'] );
302            unset( $returnToQuery['returnto'] );
303            unset( $returnToQuery['returntoquery'] );
304        }
305        return $returnToQuery;
306    }
307
308    /**
309     * Insert the Donate Link in the Mobile Menu.
310     *
311     * @param Group $group
312     */
313    public function insertDonateItem( Group $group ): void {
314        $labelMsg = $this->context->msg( 'sitesupport' );
315        $urlMsg = $this->context->msg( 'sitesupport-url' );
316        if ( !$urlMsg->exists() || $labelMsg->isDisabled() ) {
317            return;
318        }
319        // Add term field to allow distinguishing from other sidebars.
320        // https://www.mediawiki.org/wiki/Wikimedia_Product/Analytics_Infrastructure/Schema_fragments#Campaign_Attribution
321        $url = wfAppendQuery(
322            $urlMsg->text(),
323            [ 'utm_key' => 'minerva' ]
324        );
325        $entry = $this->buildMenuEntry(
326            'donate',
327            $labelMsg->text(),
328            $url,
329            '',
330            'heart',
331            true
332        );
333        $group->insertEntry( $entry );
334    }
335}