Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 67
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
Hooks
0.00% covered (danger)
0.00%
0 / 67
0.00% covered (danger)
0.00%
0 / 11
812
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 onResourceLoaderRegisterModules
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 onGetPreferences
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 onPreferencesGetLayout
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 onFetchChangesList
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 onSpecialPageBeforeExecute
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
72
 onUserLogoutComplete
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 onResourceLoaderGetConfigVars
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 onOutputPageBodyAttributes
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 onSkinPageReadyConfig
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 onDifferenceEngineViewHeader
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
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;
22
23use ChangesList;
24use ChangesListFilterGroup;
25use DifferenceEngine;
26use ExtensionRegistry;
27use MediaWiki\Config\Config;
28use MediaWiki\Diff\Hook\DifferenceEngineViewHeaderHook;
29use MediaWiki\Hook\FetchChangesListHook;
30use MediaWiki\Hook\OutputPageBodyAttributesHook;
31use MediaWiki\Hook\PreferencesGetLayoutHook;
32use MediaWiki\Hook\UserLogoutCompleteHook;
33use MediaWiki\Html\Html;
34use MediaWiki\Minerva\Skins\SkinMinerva;
35use MediaWiki\Output\OutputPage;
36use MediaWiki\Preferences\Hook\GetPreferencesHook;
37use MediaWiki\ResourceLoader\Context;
38use MediaWiki\ResourceLoader\Hook\ResourceLoaderGetConfigVarsHook;
39use MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook;
40use MediaWiki\ResourceLoader\ResourceLoader;
41use MediaWiki\Skins\Hook\SkinPageReadyConfigHook;
42use MediaWiki\SpecialPage\Hook\SpecialPageBeforeExecuteHook;
43use MediaWiki\SpecialPage\SpecialPage;
44use MediaWiki\User\Options\UserOptionsLookup;
45use MediaWiki\User\User;
46use MobileContext;
47use OldChangesList;
48use Skin;
49use Wikimedia\Rdbms\ConfiguredReadOnlyMode;
50
51/**
52 * Hook handlers for Minerva skin.
53 *
54 * Hook handler method names should be in the form of:
55 *    on<HookName>()
56 */
57class Hooks implements
58    DifferenceEngineViewHeaderHook,
59    FetchChangesListHook,
60    GetPreferencesHook,
61    OutputPageBodyAttributesHook,
62    PreferencesGetLayoutHook,
63    ResourceLoaderGetConfigVarsHook,
64    ResourceLoaderRegisterModulesHook,
65    SkinPageReadyConfigHook,
66    SpecialPageBeforeExecuteHook,
67    UserLogoutCompleteHook
68{
69    public const FEATURE_OVERFLOW_PAGE_ACTIONS = 'MinervaOverflowInPageActions';
70
71    private ConfiguredReadOnlyMode $configuredReadOnlyMode;
72    private SkinOptions $skinOptions;
73    private UserOptionsLookup $userOptionsLookup;
74    private ?MobileContext $mobileContext;
75
76    public function __construct(
77        ConfiguredReadOnlyMode $configuredReadOnlyMode,
78        SkinOptions $skinOptions,
79        UserOptionsLookup $userOptionsLookup,
80        ?MobileContext $mobileContext
81    ) {
82        $this->configuredReadOnlyMode = $configuredReadOnlyMode;
83        $this->skinOptions = $skinOptions;
84        $this->userOptionsLookup = $userOptionsLookup;
85        $this->mobileContext = $mobileContext;
86    }
87
88    /**
89     * ResourceLoaderRegisterModules hook handler.
90     *
91     * Registers:
92     *
93     * * EventLogging schema modules, if the EventLogging extension is loaded;
94     * * Modules for the Visual Editor overlay, if the VisualEditor extension is loaded; and
95     * * Modules for the notifications overlay, if the Echo extension is loaded.
96     *
97     * @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderRegisterModules
98     *
99     * @param ResourceLoader $resourceLoader
100     */
101    public function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ): void {
102        if ( !ExtensionRegistry::getInstance()->isLoaded( 'MobileFrontend' ) ) {
103            $resourceLoader->register( [
104                'mobile.startup' => [
105                    'dependencies' => [ 'mediawiki.searchSuggest' ],
106                    'localBasePath' => dirname( __DIR__ ),
107                    'remoteExtPath' => 'Minerva',
108                    'scripts' => 'resources/mobile.startup.stub.js',
109                ]
110            ] );
111        }
112    }
113
114    /**
115     * Adds Minerva-specific user preferences that can only be accessed via API
116     *
117     * @param User $user user whose preferences are being modified
118     * @param array[] &$prefs preferences description array, to be fed to a HTMLForm object
119     */
120    public function onGetPreferences( $user, &$prefs ): void {
121        $minervaPrefs = [
122            'minerva-theme' => [
123                'type' => 'api'
124            ],
125        ];
126
127        $prefs += $minervaPrefs;
128    }
129
130    /**
131     * PreferencesGetLayout hook handler.
132     *
133     * Use mobile layout in Special:Preferences
134     * @see https://www.mediawiki.org/wiki/Manual:Hooks/PreferencesGetLayout
135     *
136     * @param bool &$useMobileLayout
137     * @param string $skinName
138     * @param array $skinProperties
139     */
140    public function onPreferencesGetLayout( &$useMobileLayout, $skinName, $skinProperties = [] ) {
141        if ( $skinName === 'minerva' ) {
142            $useMobileLayout = true;
143        }
144    }
145
146    /**
147     * Disable recent changes enhanced mode (table mode)
148     * @see https://www.mediawiki.org/wiki/Manual:Hooks/FetchChangesList
149     *
150     * @param User $user
151     * @param Skin $skin
152     * @param ChangesList|null &$list
153     * @param ChangesListFilterGroup[] $groups
154     * @return bool|null
155     */
156    public function onFetchChangesList( $user, $skin, &$list, $groups ) {
157        if ( $skin->getSkinName() === 'minerva' ) {
158            // The new changes list (table-based) does not work with Minerva
159            $list = new OldChangesList( $skin->getContext(), $groups );
160            // returning false makes sure $list is used instead.
161            return false;
162        }
163    }
164
165    /**
166     * Invocation of hook SpecialPageBeforeExecute
167     *
168     * We use this hook to ensure that login/account creation pages
169     * are redirected to HTTPS if they are not accessed via HTTPS and
170     * $wgSecureLogin == true - but only when using the
171     * mobile site.
172     *
173     * @param SpecialPage $special
174     * @param string $subpage
175     */
176    public function onSpecialPageBeforeExecute( $special, $subpage ) {
177        $name = $special->getName();
178        if ( !in_array( $name, [ 'Recentchanges', 'Userlogin', 'CreateAccount' ] ) ) {
179            return;
180        }
181        $skin = $special->getSkin();
182        if ( !$skin instanceof SkinMinerva ) {
183            return;
184        }
185        $request = $special->getRequest();
186        if ( $name === 'Recentchanges' ) {
187            $isEnhancedDefaultForUser = $this->userOptionsLookup
188                ->getBoolOption( $special->getUser(), 'usenewrc' );
189            $enhanced = $request->getBool( 'enhanced', $isEnhancedDefaultForUser );
190            if ( $enhanced ) {
191                $special->getOutput()->addHTML( Html::warningBox(
192                    $special->msg( 'skin-minerva-recentchanges-warning-enhanced-not-supported' )->parse()
193                ) );
194            }
195        } else {
196            // Add default warning message to Special:UserLogin and Special:UserCreate
197            // if no warning message set.
198            if (
199                !$request->getCheck( 'warning' ) &&
200                !$special->getUser()->isRegistered() &&
201                !$request->wasPosted()
202            ) {
203                $request->setVal( 'warning', 'mobile-frontend-generic-login-new' );
204            }
205        }
206    }
207
208    /**
209     * UserLogoutComplete hook handler.
210     * Resets skin options if a user logout occurs - this is necessary as the
211     * RequestContextCreateSkinMobile hook runs before the UserLogout hook.
212     *
213     * @param User $user
214     * @param string &$inject_html
215     * @param string $oldName
216     */
217    public function onUserLogoutComplete( $user, &$inject_html, $oldName ) {
218        if ( $this->mobileContext ) {
219            $this->skinOptions->setMinervaSkinOptions( $this->mobileContext, $this->mobileContext->getSkin() );
220        }
221    }
222
223    /**
224     * ResourceLoaderGetConfigVars hook handler.
225     * Used for setting JS variables which are pulled in dynamically with RL
226     * instead of embedded directly on the page with a script tag.
227     * These vars have a shorter cache-life than those in `getJsConfigVars`.
228     *
229     * @param array &$vars Array of variables to be added into the output of the RL startup module.
230     * @param string $skin
231     * @param Config $config
232     */
233    public function onResourceLoaderGetConfigVars( array &$vars, $skin, Config $config ): void {
234        if ( $skin === 'minerva' ) {
235            // This is to let the UI adjust itself to a wiki that is always read-only.
236            // Ignore temporary read-only on live wikis, requires heavy DB check (T233458).
237            $roConf = $this->configuredReadOnlyMode;
238            $vars += [
239                'wgMinervaABSamplingRate' => $config->get( 'MinervaABSamplingRate' ),
240                'wgMinervaReadOnly' => $roConf->isReadOnly(),
241            ];
242        }
243    }
244
245    /**
246     * Modifies the `<body>` element's attributes.
247     *
248     * By default, the `class` attribute is set to the output's "bodyClassName"
249     * property.
250     *
251     * @param OutputPage $out
252     * @param Skin $skin
253     * @param string[] &$bodyAttrs
254     */
255    public function onOutputPageBodyAttributes( $out, $skin, &$bodyAttrs ): void {
256        $classes = $out->getProperty( 'bodyClassName' );
257        $isMinerva = $skin instanceof SkinMinerva;
258
259        if ( $isMinerva && $this->skinOptions->get( SkinOptions::HISTORY_IN_PAGE_ACTIONS ) ) {
260            // Class is used when page actions is modified to contain more elements
261            $classes .= ' minerva--history-page-action-enabled';
262        }
263
264        if ( $isMinerva ) {
265            $bodyAttrs['class'] .= ' ' . $classes;
266        }
267    }
268
269    /**
270     * SkinPageReadyConfig hook handler
271     *
272     * Disable collapsible on page load
273     *
274     * @param Context $context
275     * @param mixed[] &$config Associative array of configurable options
276     */
277    public function onSkinPageReadyConfig(
278        Context $context,
279        array &$config
280    ): void {
281        if ( $context->getSkin() === 'minerva' ) {
282            $config['search'] = false;
283            $config['collapsible'] = false;
284            $config['selectorLogoutLink'] = 'a.menu__item--logout[data-mw="interface"]';
285        }
286    }
287
288    /**
289     * Force inline diffs on mobile site.
290     *
291     * @param DifferenceEngine $differenceEngine
292     */
293    public function onDifferenceEngineViewHeader( $differenceEngine ) {
294        $skin = $differenceEngine->getSkin();
295        if ( $skin->getSkinName() !== 'minerva' ) {
296            return;
297        }
298        $differenceEngine->setSlotDiffOptions( [
299            'diff-type' => 'inline',
300        ] );
301    }
302}