Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
MobileFrontendEditorHooks
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 5
380
0.00% covered (danger)
0.00%
0 / 1
 getContentLanguageMessages
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getResourceLoaderMFConfigVars
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 onMakeGlobalVariablesScript
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 onCustomEditor
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
42
 isSupportedEditRequest
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
90
1<?php
2
3use MediaWiki\Config\Config;
4use MediaWiki\Context\IContextSource;
5use MediaWiki\Hook\CustomEditorHook;
6use MediaWiki\MediaWikiServices;
7use MediaWiki\Output\Hook\MakeGlobalVariablesScriptHook;
8use MediaWiki\Output\OutputPage;
9use MediaWiki\ResourceLoader\Context;
10use MediaWiki\User\User;
11
12class MobileFrontendEditorHooks implements
13    CustomEditorHook,
14    MakeGlobalVariablesScriptHook
15{
16
17    /**
18     * Return messages in content language, for use in a ResourceLoader module.
19     *
20     * @param Context $context
21     * @param Config $config
22     * @param array $messagesKeys
23     * @return array
24     */
25    public static function getContentLanguageMessages(
26        Context $context, Config $config, array $messagesKeys = []
27    ): array {
28        return array_combine(
29            $messagesKeys,
30            array_map( static function ( $key ) {
31                return wfMessage( $key )->inContentLanguage()->text();
32            }, $messagesKeys )
33        );
34    }
35
36    /**
37     * Generate config for usage inside MobileFrontend
38     * This should be used for variables which:
39     *  - vary with the html
40     *  - variables that should work cross skin including anonymous users
41     *  - used for both, stable and beta mode (don't use
42     *    MobileContext::isBetaGroupMember in this function - T127860)
43     *
44     * @return array
45     */
46    public static function getResourceLoaderMFConfigVars() {
47        $config = MediaWikiServices::getInstance()->getService( 'MobileFrontend.Config' );
48
49        return [
50            'wgMFDefaultEditor' => $config->get( 'MFDefaultEditor' ),
51            'wgMFFallbackEditor' => $config->get( 'MFFallbackEditor' ),
52            'wgMFEnableVEWikitextEditor' => $config->get( 'MFEnableVEWikitextEditor' ),
53        ];
54    }
55
56    /**
57     * Handler for MakeGlobalVariablesScript hook.
58     * For values that depend on the current page, user or request state.
59     *
60     * @see https://www.mediawiki.org/wiki/Manual:Hooks/MakeGlobalVariablesScript
61     * @param array &$vars Variables to be added into the output
62     * @param OutputPage $out OutputPage instance calling the hook
63     */
64    public function onMakeGlobalVariablesScript( &$vars, $out ): void {
65        /** @var MobileContext $mobileContext */
66        $mobileContext = MediaWikiServices::getInstance()->getService( 'MobileFrontend.Context' );
67        $config = MediaWikiServices::getInstance()->getService( 'MobileFrontend.Config' );
68
69        if ( $mobileContext->shouldDisplayMobileView() ) {
70            // mobile.init
71            $vars['wgMFIsSupportedEditRequest'] = self::isSupportedEditRequest( $out->getContext() );
72            $vars['wgMFScriptPath'] = $config->get( 'MFScriptPath' );
73        }
74    }
75
76    /**
77     * Decide whether to bother showing the wikitext editor at all.
78     * If not, we expect the editor initialisation JS to activate.
79     *
80     * @param Article $article The article being viewed.
81     * @param User $user The user-specific settings.
82     * @return bool Whether to show the wikitext editor or not.
83     */
84    public function onCustomEditor( $article, $user ) {
85        $req = $article->getContext()->getRequest();
86        $title = $article->getTitle();
87        if (
88            !$req->getVal( 'mfnoscript' ) &&
89            self::isSupportedEditRequest( $article->getContext() )
90        ) {
91            $params = $req->getValues();
92            $params['mfnoscript'] = '1';
93            $url = wfScript() . '?' . wfArrayToCgi( $params );
94            $escapedUrl = htmlspecialchars( $url );
95
96            $out = $article->getContext()->getOutput();
97            $titleMsg = $title->exists() ? 'editing' : 'creating';
98            $out->setPageTitleMsg( wfMessage( $titleMsg, $title->getPrefixedText() ) );
99
100            $msg = false;
101            $msgParams = false;
102            if ( $title->inNamespace( NS_FILE ) && !$title->exists() ) {
103                // Is a new file page (enable upload image only) T60311
104                $msg = 'mobile-frontend-editor-uploadenable';
105            } else {
106                $msg = 'mobile-frontend-editor-toload';
107                $msgParams = wfExpandUrl( $url );
108            }
109            $out->showPendingTakeover( $url, $msg, $msgParams );
110
111            $out->setRevisionId( $req->getInt( 'oldid', $article->getRevIdFetched() ) );
112            return false;
113        }
114        return true;
115    }
116
117    /**
118     * Whether the custom editor override should occur
119     *
120     * @param IContextSource $context
121     * @return bool Whether the frontend JS should try to display an editor
122     */
123    protected static function isSupportedEditRequest( IContextSource $context ) {
124        /** @var MobileContext $mobileContext */
125        $mobileContext = MediaWikiServices::getInstance()->getService( 'MobileFrontend.Context' );
126        if ( !$mobileContext->shouldDisplayMobileView() ) {
127            return false;
128        }
129
130        $extensionRegistry = ExtensionRegistry::getInstance();
131        $editorAvailableSkins = $extensionRegistry->getAttribute( 'MobileFrontendEditorAvailableSkins' );
132        if ( !in_array( $context->getSkin()->getSkinName(), $editorAvailableSkins ) ) {
133            // Mobile editor commonly doesn't work well with other skins than Minerva (it looks horribly
134            // broken without some styles that are only defined by Minerva). So we only enable it for the
135            // skin that wants it.
136            return false;
137        }
138
139        $req = $context->getRequest();
140        $title = $context->getTitle();
141
142        // Various things fall back to WikiEditor
143        if ( $req->getVal( 'action' ) == 'submit' ) {
144            // Don't try to take over if the form has already been submitted
145            return false;
146        }
147        if ( $title->inNamespace( NS_SPECIAL ) ) {
148            return false;
149        }
150        if ( $title->getContentModel() !== 'wikitext' ) {
151            // Only load the wikitext editor on wikitext. Otherwise we'll rely on the fallback behaviour
152            // (You can test this on MediaWiki:Common.css) ?action=edit url (T173800)
153            return false;
154        }
155        if ( $req->getVal( 'undo' ) !== null || $req->getVal( 'undoafter' ) !== null ) {
156            // Undo needs to show a diff above the editor
157            return false;
158        }
159        if ( $req->getVal( 'section' ) == 'new' ) {
160            // New sections need a title field
161            return false;
162        }
163        return true;
164    }
165
166}