Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
15.44% covered (danger)
15.44%
21 / 136
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ClientHooks
15.44% covered (danger)
15.44%
21 / 136
80.00% covered (warning)
80.00%
4 / 5
84.16
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 onPageSaveComplete
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 onMakeGlobalVariablesScript
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 onResourceLoaderRegisterModules
1.71% covered (danger)
1.71%
2 / 117
0.00% covered (danger)
0.00%
0 / 1
11.55
 getClientTargetUrl
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3/**
4 * WikiLambda extension Parser-related ('client-mode') hooks
5 *
6 * @file
7 * @ingroup Extensions
8 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
9 * @license MIT
10 */
11
12namespace MediaWiki\Extension\WikiLambda\HookHandler;
13
14use MediaWiki\Config\Config;
15use MediaWiki\Extension\WikiLambda\WikiLambdaServices;
16use MediaWiki\Logger\LoggerFactory;
17use MediaWiki\Output\OutputPage;
18use MediaWiki\Page\WikiPage;
19use MediaWiki\Registration\ExtensionRegistry;
20use MediaWiki\ResourceLoader\CodexModule;
21use MediaWiki\ResourceLoader\ImageModule;
22use MediaWiki\ResourceLoader\ResourceLoader;
23use MediaWiki\Revision\RevisionRecord;
24use MediaWiki\Storage\EditResult;
25use MediaWiki\User\UserIdentity;
26use Psr\Log\LoggerInterface;
27
28class ClientHooks implements
29    \MediaWiki\Storage\Hook\PageSaveCompleteHook,
30    \MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook,
31    \MediaWiki\Output\Hook\MakeGlobalVariablesScriptHook
32{
33    private LoggerInterface $logger;
34
35    public function __construct(
36        private readonly Config $config
37    ) {
38        // Non-injected items
39        $this->logger = LoggerFactory::getInstance( 'WikiLambdaClient' );
40    }
41
42    /**
43     * @see https://www.mediawiki.org/wiki/Manual:Hooks/PageSaveComplete
44     *
45     * @param WikiPage $wikiPage
46     * @param UserIdentity $user
47     * @param string $summary
48     * @param int $flags
49     * @param RevisionRecord $revisionRecord
50     * @param EditResult $editResult
51     * @return bool|void
52     */
53    public function onPageSaveComplete(
54        $wikiPage,
55        $user,
56        $summary,
57        $flags,
58        $revisionRecord,
59        $editResult
60    ) {
61        if ( !$this->config->get( 'WikiLambdaEnableClientMode' ) ) {
62            // Nothing for us to do.
63            return;
64        }
65
66        // We use this hook to clear out cached tracking of Wikifunctions calls, if any.
67        // Any new entries are added by WikifunctionsClientUsageUpdateJob, which runs later.
68        $wikifunctionsClientStore = WikiLambdaServices::getWikifunctionsClientStore();
69        $this->logger->debug( __METHOD__ . ': Clearing usage tracking for {page}', [
70            'page' => $wikiPage->getTitle()->getFullText(),
71        ] );
72        $wikifunctionsClientStore->deleteWikifunctionsUsage( $wikiPage->getTitle() );
73    }
74
75    /**
76     * @param array &$vars
77     * @param OutputPage $out
78     */
79    public function onMakeGlobalVariablesScript( &$vars, $out ): void {
80        // 1. Add configuration flags
81        $vars['wgWikiLambdaEnableAbstractMode'] = $this->config->get( 'WikiLambdaEnableAbstractMode' );
82        $vars['wgWikiLambdaEnableRepoMode'] = $this->config->get( 'WikiLambdaEnableRepoMode' );
83
84        // 2. Add wgWikifunctionsBaseUrl when the setup is non-repo
85        if ( !$this->config->get( 'WikiLambdaEnableRepoMode' ) ) {
86            $vars['wgWikifunctionsBaseUrl'] = $this->getClientTargetUrl();
87        }
88
89        // 3. Add primary namespace for Abstract content
90        if ( $this->config->get( 'WikiLambdaEnableAbstractMode' ) ) {
91            $namespaces = $this->config->get( 'WikiLambdaAbstractNamespaces' );
92            $vars['wgWikiLambdaAbstractPrimaryNamespace'] = array_values( $namespaces )[0][0];
93        }
94    }
95
96    /**
97     * @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderRegisterModules
98     *
99     * @param ResourceLoader $resourceLoader
100     * @return void
101     */
102    public function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ): void {
103        // TODO (T386013): Once client mode is always enabled, register this statically in extension.json
104        // via the ResourceModules definition.
105
106        if (
107            $this->config->get( 'WikiLambdaEnableClientMode' )
108            && ExtensionRegistry::getInstance()->isLoaded( 'VisualEditor' )
109        ) {
110            $directoryName = __DIR__ . '/../../resources/ext.wikilambda.visualeditor';
111
112            // First, register our custom icons so we can depend on them
113            $resourceLoader->register( 'ext.wikilambda.visualeditor.icons', [
114                'class' => ImageModule::class,
115                // We're writing to the global OOUI icon namespace for now.
116                'selector' => '.oo-ui-icon-{name}',
117                'images' => [
118                    'functionObject' => [ "file" => "icons/functionObject.svg" ]
119                ],
120                'localBasePath' => $directoryName,
121                'remoteExtPath' => 'WikiLambda/resources'
122            ] );
123
124            // Now register our actual bundle
125            $files = [
126                've.init.mw.WikifunctionsCall.js',
127                've.dm.WikifunctionsCallNode.js',
128                've.ce.WikifunctionsCallNode.js',
129                've.ui.WikifunctionsCallContextItem.js',
130                've.ui.WikifunctionsCallDialogTool.js',
131                've.ui.WikifunctionsCallDialog.js',
132            ];
133
134            array_push( $files, [
135                'name' => 'init.js',
136                'main' => true,
137                'content' => array_reduce( $files, static function ( $carry, $file ) {
138                    return "$carry\nrequire('./$file');\n";
139                }, '' ),
140            ] );
141
142            $visualEditorWfConfig = [
143                'dependencies' => [
144                    'ext.visualEditor.mwcore',
145                    'ext.visualEditor.mwtransclusion',
146                    'ext.wikilambda.visualeditor.icons',
147                ],
148                'localBasePath' => $directoryName,
149                'remoteExtPath' => 'WikiLambda/resources',
150                'packageFiles' => $files,
151                'messages' => [
152                    'wikilambda-suggested-functions.json',
153                    'wikilambda-visualeditor-wikifunctionscall-ce-loading',
154                    'wikilambda-visualeditor-wikifunctionscall-ce-abort',
155                    'wikilambda-visualeditor-wikifunctionscall-error',
156                    'wikilambda-visualeditor-wikifunctionscall-title',
157                    'wikilambda-visualeditor-wikifunctionscall-popup-loading',
158                    'wikilambda-visualeditor-wikifunctionscall-dialog-search-no-results',
159                    'wikilambda-visualeditor-wikifunctionscall-dialog-search-placeholder',
160                    'wikilambda-visualeditor-wikifunctionscall-dialog-search-results-title',
161                    'wikilambda-visualeditor-wikifunctionscall-dialog-suggested-functions-title',
162                    'wikilambda-visualeditor-wikifunctionscall-dialog-string-input-placeholder',
163                    'wikilambda-visualeditor-wikifunctionscall-dialog-enum-selector-placeholder',
164                    'wikilambda-visualeditor-wikifunctionscall-dialog-function-link-footer',
165                    'wikilambda-visualeditor-wikifunctionscall-dialog-cta-suggest-title',
166                    'wikilambda-visualeditor-wikifunctionscall-dialog-cta-suggest-description',
167                    'wikilambda-visualeditor-wikifunctionscall-dialog-cta-create-title',
168                    'wikilambda-visualeditor-wikifunctionscall-dialog-cta-create-description',
169                    'wikilambda-visualeditor-wikifunctionscall-dialog-cta-explore-title',
170                    'wikilambda-visualeditor-wikifunctionscall-dialog-cta-explore-description',
171                    'wikilambda-visualeditor-wikifunctionscall-error-bad-function',
172                    'wikilambda-visualeditor-wikifunctionscall-error-enum',
173                    'wikilambda-visualeditor-wikifunctionscall-error-language',
174                    'wikilambda-visualeditor-wikifunctionscall-error-parser',
175                    'wikilambda-visualeditor-wikifunctionscall-error-parser-empty',
176                    'wikilambda-visualeditor-wikifunctionscall-error-wikidata-lexeme',
177                    'wikilambda-visualeditor-wikifunctionscall-error-wikidata-property',
178                    'wikilambda-visualeditor-wikifunctionscall-error-wikidata-item',
179                    'wikilambda-visualeditor-wikifunctionscall-error-wikidata-lexeme-form',
180                    'wikilambda-visualeditor-wikifunctionscall-dialog-read-more-description',
181                    'wikilambda-visualeditor-wikifunctionscall-dialog-read-less-description',
182                    'wikilambda-visualeditor-wikifunctionscall-info-missing-content',
183                    'brackets',
184                    'wikilambda-visualeditor-wikifunctionscall-back',
185                    'wikilambda-visualeditor-wikifunctionscall-changedesc-title',
186                    'wikilambda-visualeditor-wikifunctionscall-no-name',
187                    'wikilambda-visualeditor-wikifunctionscall-no-description',
188                    'wikilambda-visualeditor-wikifunctionscall-no-input-label',
189                    'wikilambda-visualeditor-wikifunctionscall-preview-title',
190                    'wikilambda-visualeditor-wikifunctionscall-preview-no-result',
191                    'wikilambda-visualeditor-wikifunctionscall-preview-retry-button-label',
192                    'wikilambda-visualeditor-wikifunctionscall-preview-cancel-button-label',
193                    'wikilambda-visualeditor-wikifunctionscall-preview-cancelled',
194                    'wikilambda-visualeditor-wikifunctionscall-preview-error',
195                    'wikilambda-visualeditor-wikifunctionscall-preview-html-fragment-toggle',
196                    'wikilambda-visualeditor-wikifunctionscall-default-value-date',
197                    'wikilambda-visualeditor-wikifunctionscall-default-value-wikidata-item',
198                    'wikilambda-visualeditor-wikifunctionscall-default-value-language',
199                    'wikilambda-functioncall-error-message',
200                    "wikilambda-functioncall-error-message-unknown",
201                    "wikilambda-functioncall-error-message-not-supported",
202                    "wikilambda-functioncall-error-message-bad-inputs",
203                    "wikilambda-functioncall-error-message-bad-input-type",
204                    "wikilambda-functioncall-error-message-bad-langs",
205                    "wikilambda-functioncall-error-message-disabled",
206                    "wikilambda-functioncall-error-message-system",
207                    'wikilambda-functioncall-error',
208                    'wikilambda-functioncall-error-evaluation',
209                    "wikilambda-functioncall-error-unclear",
210                    "wikilambda-functioncall-error-unknown-zid",
211                    "wikilambda-functioncall-error-invalid-zobject",
212                    "wikilambda-functioncall-error-nonfunction",
213                    "wikilambda-functioncall-error-nonstringinput",
214                    "wikilambda-functioncall-error-nonstringoutput",
215                    "wikilambda-functioncall-error-bad-langs",
216                    "wikilambda-functioncall-error-bad-inputs",
217                    "wikilambda-functioncall-error-bad-input-type",
218                    "wikilambda-functioncall-error-bad-output",
219                ],
220                'styles' => [
221                    'ext.wikilambda.visualeditor.less',
222                ]
223            ];
224
225            $resourceLoader->register( 'ext.wikilambda.visualeditor', $visualEditorWfConfig );
226
227            // Finally, register the Codex module for the inline errors
228            $resourceLoader->register( 'ext.wikilambda.inlineerrors', [
229                'class' => CodexModule::class,
230                'codexStyleOnly' => true,
231                'codexComponents' => [
232                    'CdxInfoChip',
233                ],
234            ] );
235        }
236    }
237
238    /**
239     * Return the Url of the Wikilambda server instance,
240     * and if not available in the configuration variables,
241     * returns an empty string and logs an error.
242     *
243     * @return string
244     */
245    private function getClientTargetUrl(): string {
246        $targetUrl = $this->config->get( 'WikiLambdaClientTargetAPI' );
247        if ( !$targetUrl ) {
248            $this->logger->error( __METHOD__ . ': missing configuration variable WikiLambdaClientTargetAPI' );
249        }
250        return $targetUrl ?? '';
251    }
252}