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 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 onMakeGlobalVariablesScript
0.00% covered (danger)
0.00%
0 / 4
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\Actions\Hook\CustomEditorHook;
4use MediaWiki\Config\Config;
5use MediaWiki\Context\IContextSource;
6use MediaWiki\MediaWikiServices;
7use MediaWiki\Output\Hook\MakeGlobalVariablesScriptHook;
8use MediaWiki\Output\OutputPage;
9use MediaWiki\Page\Article;
10use MediaWiki\Registration\ExtensionRegistry;
11use MediaWiki\ResourceLoader\Context;
12use MediaWiki\User\User;
13
14class MobileFrontendEditorHooks implements
15    CustomEditorHook,
16    MakeGlobalVariablesScriptHook
17{
18
19    /**
20     * Return messages in content language, for use in a ResourceLoader module.
21     *
22     * @param Context $context
23     * @param Config $config
24     * @param array $messagesKeys
25     * @return array
26     */
27    public static function getContentLanguageMessages(
28        Context $context, Config $config, array $messagesKeys = []
29    ): array {
30        return array_combine(
31            $messagesKeys,
32            array_map( static function ( $key ) {
33                return wfMessage( $key )->inContentLanguage()->text();
34            }, $messagesKeys )
35        );
36    }
37
38    /**
39     * Generate config for usage inside MobileFrontend
40     * This should be used for variables which:
41     *  - vary with the html
42     *  - should work cross skin including anonymous users.
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            'wgMFEnableAbandonSurvey' => $config->get( 'MFEnableAbandonSurvey' ),
54        ];
55    }
56
57    /**
58     * Handler for MakeGlobalVariablesScript hook.
59     * For values that depend on the current page, user or request state.
60     *
61     * @see https://www.mediawiki.org/wiki/Manual:Hooks/MakeGlobalVariablesScript
62     * @param array &$vars Variables to be added into the output
63     * @param OutputPage $out OutputPage instance calling the hook
64     */
65    public function onMakeGlobalVariablesScript( &$vars, $out ): void {
66        /** @var MobileContext $mobileContext */
67        $mobileContext = MediaWikiServices::getInstance()->getService( 'MobileFrontend.Context' );
68        $config = MediaWikiServices::getInstance()->getService( 'MobileFrontend.Config' );
69
70        if ( $mobileContext->shouldDisplayMobileView() ) {
71            // mobile.init
72            $vars['wgMFIsSupportedEditRequest'] = self::isSupportedEditRequest( $out->getContext() );
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            $msgParams = [];
101            if ( $title->inNamespace( NS_FILE ) && !$title->exists() ) {
102                // Is a new file page (enable upload image only) T60311
103                $msg = 'mobile-frontend-editor-uploadenable';
104            } else {
105                $msg = 'mobile-frontend-editor-toload';
106                $urlUtils = MediaWikiServices::getInstance()->getUrlUtils();
107                $msgParams[] = $urlUtils->expand( $url, PROTO_CURRENT );
108            }
109            // @phan-suppress-next-line PhanTypeMismatchArgumentNullable Only null for invalid URL, shouldn't happen
110            $out->showPendingTakeover( $url, $msg, ...$msgParams );
111
112            $out->setRevisionId( $req->getInt( 'oldid', $article->getRevIdFetched() ) );
113            return false;
114        }
115        return true;
116    }
117
118    /**
119     * Whether the custom editor override should occur
120     *
121     * @param IContextSource $context
122     * @return bool Whether the frontend JS should try to display an editor
123     */
124    protected static function isSupportedEditRequest( IContextSource $context ) {
125        /** @var MobileContext $mobileContext */
126        $mobileContext = MediaWikiServices::getInstance()->getService( 'MobileFrontend.Context' );
127        if ( !$mobileContext->shouldDisplayMobileView() ) {
128            return false;
129        }
130
131        $extensionRegistry = ExtensionRegistry::getInstance();
132        $editorAvailableSkins = $extensionRegistry->getAttribute( 'MobileFrontendEditorAvailableSkins' );
133        if ( !in_array( $context->getSkin()->getSkinName(), $editorAvailableSkins ) ) {
134            // Mobile editor commonly doesn't work well with other skins than Minerva (it looks horribly
135            // broken without some styles that are only defined by Minerva). So we only enable it for the
136            // skin that wants it.
137            return false;
138        }
139
140        $req = $context->getRequest();
141        $title = $context->getTitle();
142
143        // Various things fall back to WikiEditor
144        if ( $req->getRawVal( 'action' ) === 'submit' ) {
145            // Don't try to take over if the form has already been submitted
146            return false;
147        }
148        if ( $title->inNamespace( NS_SPECIAL ) ) {
149            return false;
150        }
151        if ( !$title->hasContentModel( CONTENT_MODEL_WIKITEXT ) ) {
152            // Only load the wikitext editor on wikitext. Otherwise we'll rely on the fallback behaviour
153            // (You can test this on MediaWiki:Common.css) ?action=edit url (T173800)
154            return false;
155        }
156        if ( $req->getCheck( 'undo' ) || $req->getCheck( 'undoafter' ) ) {
157            // Undo needs to show a diff above the editor
158            return false;
159        }
160        if ( $req->getRawVal( 'section' ) === 'new' ) {
161            // New sections need a title field
162            return false;
163        }
164        return true;
165    }
166
167}