Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 312 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
Hooks | |
0.00% |
0 / 312 |
|
0.00% |
0 / 9 |
2862 | |
0.00% |
0 / 1 |
onEditPage__importFormData | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
12 | |||
wrapErrorMsg | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
onAlternateEditPreview | |
0.00% |
0 / 104 |
|
0.00% |
0 / 1 |
342 | |||
onEditPage__showStandardInputs_options | |
0.00% |
0 / 86 |
|
0.00% |
0 / 1 |
72 | |||
isUsableApiModule | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
onAPIGetAllowedParams | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
6 | |||
onApiMakeParserOptions | |
0.00% |
0 / 75 |
|
0.00% |
0 / 1 |
306 | |||
getParsedMessages | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getTemplateNamespaces | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\TemplateSandbox; |
4 | |
5 | // phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName |
6 | |
7 | use ApiBase; |
8 | use ApiExpandTemplates; |
9 | use ApiParse; |
10 | use Content; |
11 | use ExtensionRegistry; |
12 | use IContextSource; |
13 | use MediaWiki\Api\Hook\APIGetAllowedParamsHook; |
14 | use MediaWiki\Api\Hook\ApiMakeParserOptionsHook; |
15 | use MediaWiki\Config\Config; |
16 | use MediaWiki\EditPage\EditPage; |
17 | use MediaWiki\Hook\AlternateEditPreviewHook; |
18 | use MediaWiki\Hook\EditPage__importFormDataHook; |
19 | use MediaWiki\Hook\EditPage__showStandardInputs_optionsHook; |
20 | use MediaWiki\Html\Html; |
21 | use MediaWiki\MediaWikiServices; |
22 | use MediaWiki\Output\OutputPage; |
23 | use MediaWiki\Parser\ParserOutput; |
24 | use MediaWiki\Request\WebRequest; |
25 | use MediaWiki\ResourceLoader as RL; |
26 | use MediaWiki\Revision\RevisionRecord; |
27 | use MediaWiki\Revision\SlotRecord; |
28 | use MediaWiki\Title\Title; |
29 | use MediaWiki\Widget\TitleInputWidget; |
30 | use MWContentSerializationException; |
31 | use OOUI\ActionFieldLayout; |
32 | use OOUI\ButtonInputWidget; |
33 | use OOUI\FieldsetLayout; |
34 | use OOUI\HtmlSnippet; |
35 | use OOUI\Layout; |
36 | use ParserOptions; |
37 | use RequestContext; |
38 | use Wikimedia\ParamValidator\ParamValidator; |
39 | use Wikimedia\ScopedCallback; |
40 | use Xml; |
41 | |
42 | class Hooks implements |
43 | EditPage__importFormDataHook, |
44 | EditPage__showStandardInputs_optionsHook, |
45 | AlternateEditPreviewHook, |
46 | APIGetAllowedParamsHook, |
47 | ApiMakeParserOptionsHook |
48 | { |
49 | private static $counter = 0; |
50 | |
51 | /** |
52 | * Hook for EditPage::importFormData to parse our new form fields, and if |
53 | * necessary put $editpage into "preview" mode. |
54 | * |
55 | * Note we specifically do not check $wgTemplateSandboxEditNamespaces here, |
56 | * since users can manually enable this for other namespaces. |
57 | * |
58 | * @param EditPage $editpage |
59 | * @param WebRequest $request |
60 | */ |
61 | public function onEditPage__importFormData( $editpage, $request ) { |
62 | $editpage->templatesandbox_template = $request->getText( |
63 | 'wpTemplateSandboxTemplate', $editpage->getTitle()->getPrefixedText() |
64 | ); |
65 | $editpage->templatesandbox_page = $request->getText( 'wpTemplateSandboxPage' ); |
66 | |
67 | if ( $request->wasPosted() ) { |
68 | if ( $request->getCheck( 'wpTemplateSandboxPreview' ) ) { |
69 | $editpage->templatesandbox_preview = true; |
70 | $editpage->preview = true; |
71 | $editpage->save = false; |
72 | $editpage->live = false; |
73 | } |
74 | } |
75 | } |
76 | |
77 | /** |
78 | * @param IContextSource $context |
79 | * @param string $msg |
80 | * @return string |
81 | */ |
82 | private static function wrapErrorMsg( IContextSource $context, $msg ) { |
83 | return "<div id='mw-$msg'>\n" |
84 | . $context->msg( $msg )->parseAsBlock() |
85 | . "\n</div>"; |
86 | } |
87 | |
88 | /** |
89 | * Hook for AlternateEditPreview to output an entirely different preview |
90 | * when our button was clicked. |
91 | * |
92 | * @param EditPage $editpage |
93 | * @param Content &$content |
94 | * @param string &$out |
95 | * @param ParserOutput &$parserOutput |
96 | * @return bool |
97 | */ |
98 | public function onAlternateEditPreview( $editpage, &$content, &$out, |
99 | &$parserOutput |
100 | ) { |
101 | if ( !isset( $editpage->templatesandbox_preview ) ) { |
102 | return true; |
103 | } |
104 | |
105 | $context = $editpage->getContext(); |
106 | |
107 | if ( $editpage->templatesandbox_template === '' || |
108 | $editpage->templatesandbox_template === null |
109 | ) { |
110 | $out = self::wrapErrorMsg( $context, 'templatesandbox-editform-need-template' ); |
111 | return false; |
112 | } |
113 | if ( $editpage->templatesandbox_page === '' || $editpage->templatesandbox_page === null ) { |
114 | $out = self::wrapErrorMsg( $context, 'templatesandbox-editform-need-title' ); |
115 | return false; |
116 | } |
117 | |
118 | $templatetitle = Title::newFromText( $editpage->templatesandbox_template ); |
119 | if ( !$templatetitle instanceof Title ) { |
120 | $out = self::wrapErrorMsg( $context, 'templatesandbox-editform-invalid-template' ); |
121 | return false; |
122 | } |
123 | |
124 | $title = Title::newFromText( $editpage->templatesandbox_page ); |
125 | if ( !$title instanceof Title ) { |
126 | $out = self::wrapErrorMsg( $context, 'templatesandbox-editform-invalid-title' ); |
127 | return false; |
128 | } |
129 | |
130 | // If we're previewing the same page we're editing, we don't need to check whether |
131 | // we exist, since we fake that we exist later. This is useful to, for example, |
132 | // preview a page move. |
133 | if ( !$title->equals( $templatetitle ) && !$title->exists() ) { |
134 | $out = self::wrapErrorMsg( $context, 'templatesandbox-editform-title-not-exists' ); |
135 | return false; |
136 | } |
137 | |
138 | $note = ''; |
139 | $dtitle = false; |
140 | $parserOutput = null; |
141 | |
142 | $user = $context->getUser(); |
143 | $output = $context->getOutput(); |
144 | $lang = $context->getLanguage(); |
145 | |
146 | try { |
147 | if ( $editpage->sectiontitle !== '' ) { |
148 | // TODO (T314475): If sectiontitle is null this uses '' rather than summary; is that wanted? |
149 | $sectionTitle = $editpage->sectiontitle ?? ''; |
150 | } else { |
151 | $sectionTitle = $editpage->summary; |
152 | } |
153 | |
154 | if ( $editpage->getArticle()->getPage()->exists() ) { |
155 | $content = $editpage->getArticle()->getPage()->replaceSectionContent( |
156 | $editpage->section, $content, $sectionTitle, $editpage->edittime |
157 | ); |
158 | if ( $content === null ) { |
159 | $out = self::wrapErrorMsg( $context, 'templatesandbox-failed-replace-section' ); |
160 | return false; |
161 | } |
162 | } else { |
163 | if ( $editpage->section === 'new' ) { |
164 | $content = $content->addSectionHeader( $sectionTitle ); |
165 | } |
166 | } |
167 | |
168 | // Apply PST to the to-be-saved text |
169 | $popts = $editpage->getArticle()->getPage()->makeParserOptions( |
170 | $context |
171 | ); |
172 | $services = MediaWikiServices::getInstance(); |
173 | $popts->setIsPreview( true ); |
174 | $popts->setIsSectionPreview( false ); |
175 | $contentTransformer = $services->getContentTransformer(); |
176 | $content = $contentTransformer->preSaveTransform( |
177 | $content, |
178 | $templatetitle, |
179 | $user, |
180 | $popts |
181 | ); |
182 | |
183 | $note = $context->msg( 'templatesandbox-previewnote', $title->getPrefixedText() )->plain() . |
184 | ' [[#' . EditPage::EDITFORM_ID . '|' . $lang->getArrow() . ' ' . |
185 | $context->msg( 'continue-editing' )->text() . ']]'; |
186 | |
187 | $page = $services->getWikiPageFactory()->newFromTitle( $title ); |
188 | $popts = $page->makeParserOptions( $context ); |
189 | $popts->setIsPreview( true ); |
190 | $popts->setIsSectionPreview( false ); |
191 | $logic = new Logic( [], $templatetitle, $content ); |
192 | $reset = $logic->setupForParse( $popts ); |
193 | |
194 | $revRecord = call_user_func_array( |
195 | $popts->getCurrentRevisionRecordCallback(), |
196 | [ $title ] |
197 | ); |
198 | |
199 | $pageContent = $revRecord->getContent( |
200 | SlotRecord::MAIN, |
201 | RevisionRecord::FOR_THIS_USER, |
202 | $user |
203 | ); |
204 | $contentRenderer = $services->getContentRenderer(); |
205 | $parserOutput = $contentRenderer->getParserOutput( $pageContent, $title, $revRecord, $popts ); |
206 | |
207 | $output->addParserOutputMetadata( $parserOutput ); |
208 | if ( $output->userCanPreview() ) { |
209 | $output->addContentOverride( $templatetitle, $content ); |
210 | } |
211 | |
212 | $dtitle = $parserOutput->getDisplayTitle(); |
213 | $parserOutput->setTitleText( '' ); |
214 | $skinOptions = $output->getSkin()->getOptions(); |
215 | $out = $parserOutput->getText( [ |
216 | 'injectTOC' => $skinOptions['toc'], |
217 | 'enableSectionEditLinks' => false, |
218 | 'includeDebugInfo' => true, |
219 | ] ); |
220 | |
221 | if ( count( $parserOutput->getWarnings() ) ) { |
222 | $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() ); |
223 | } |
224 | } catch ( MWContentSerializationException $ex ) { |
225 | $m = $context->msg( 'content-failed-to-parse', |
226 | $editpage->contentModel, $editpage->contentFormat, $ex->getMessage() |
227 | ); |
228 | $note .= "\n\n" . $m->parse(); |
229 | $out = ''; |
230 | } |
231 | |
232 | $dtitle = $dtitle === false ? $title->getPrefixedText() : $dtitle; |
233 | $previewhead = Html::rawElement( |
234 | 'div', [ 'class' => 'previewnote' ], |
235 | Html::rawElement( |
236 | 'h2', [ 'id' => 'mw-previewheader' ], |
237 | $context->msg( 'templatesandbox-preview', $title->getPrefixedText(), $dtitle )->parse() |
238 | ) . |
239 | Html::warningBox( |
240 | $output->parseAsInterface( $note ) |
241 | ) |
242 | ); |
243 | |
244 | $out = $previewhead . $out . $editpage->previewTextAfterContent; |
245 | |
246 | return false; |
247 | } |
248 | |
249 | /** |
250 | * Hook for EditPage::showStandardInputs:options to add our form fields to |
251 | * the "editOptions" area of the page. |
252 | * |
253 | * @param EditPage $editpage |
254 | * @param OutputPage $output |
255 | * @param int &$tabindex |
256 | */ |
257 | public function onEditPage__showStandardInputs_options( $editpage, $output, &$tabindex ) { |
258 | global $wgTemplateSandboxEditNamespaces; |
259 | |
260 | $namespaces = array_merge( |
261 | $wgTemplateSandboxEditNamespaces, |
262 | ExtensionRegistry::getInstance()->getAttribute( 'TemplateSandboxEditNamespaces' ) |
263 | ); |
264 | |
265 | $contentModels = ExtensionRegistry::getInstance()->getAttribute( |
266 | 'TemplateSandboxEditContentModels' ); |
267 | |
268 | // Show the form if the title is in an allowed namespace, has an allowed content model |
269 | // or if the user requested it with &wpTemplateSandboxShow |
270 | $showForm = $editpage->getTitle()->inNamespaces( $namespaces ) |
271 | || in_array( $editpage->getTitle()->getContentModel(), $contentModels, true ) |
272 | || $output->getRequest()->getCheck( 'wpTemplateSandboxShow' ); |
273 | |
274 | if ( !$showForm ) { |
275 | // output the values in hidden fields so that a user |
276 | // using a gadget doesn't have to re-enter them every time |
277 | |
278 | $html = Xml::openElement( 'span', [ 'id' => 'templatesandbox-editform' ] ); |
279 | |
280 | $html .= Html::hidden( 'wpTemplateSandboxTemplate', |
281 | $editpage->templatesandbox_template, [ 'id' => 'wpTemplateSandboxTemplate' ] |
282 | ); |
283 | |
284 | $html .= Html::hidden( 'wpTemplateSandboxPage', |
285 | $editpage->templatesandbox_page, [ 'id' => 'wpTemplateSandboxPage' ] |
286 | ); |
287 | |
288 | $html .= Xml::closeElement( 'span' ); |
289 | |
290 | $output->addHTML( $html . "\n" ); |
291 | |
292 | return; |
293 | } |
294 | |
295 | $output->addModuleStyles( 'ext.TemplateSandbox.top' ); |
296 | $output->addModules( 'ext.TemplateSandbox' ); |
297 | |
298 | $context = $editpage->getContext(); |
299 | |
300 | $textHtml = ''; |
301 | $text = $context->msg( 'templatesandbox-editform-text' ); |
302 | if ( !$text->isDisabled() ) { |
303 | $textAttrs = [ |
304 | 'class' => 'mw-templatesandbox-editform-text', |
305 | ]; |
306 | $textHtml = Xml::tags( 'div', $textAttrs, $text->parse() ); |
307 | } |
308 | |
309 | $helptextHtml = ''; |
310 | $helptext = $context->msg( 'templatesandbox-editform-helptext' ); |
311 | if ( !$helptext->isDisabled() ) { |
312 | $helptextAttrs = [ |
313 | 'class' => 'mw-templatesandbox-editform-helptext', |
314 | ]; |
315 | $helptextHtml = Xml::tags( 'span', $helptextAttrs, $helptext->parse() ); |
316 | } |
317 | |
318 | $hiddenInputsHtml = |
319 | Html::hidden( 'wpTemplateSandboxTemplate', |
320 | $editpage->templatesandbox_template, [ 'id' => 'wpTemplateSandboxTemplate' ] |
321 | ) . |
322 | // If they submit our form, pass the parameter along for not allowed namespaces |
323 | Html::hidden( 'wpTemplateSandboxShow', '' ); |
324 | |
325 | $output->enableOOUI(); |
326 | $output->addModules( 'oojs-ui-core' ); |
327 | $output->addModules( 'mediawiki.widgets' ); |
328 | |
329 | $fieldsetLayout = |
330 | new FieldsetLayout( [ |
331 | 'label' => new HtmlSnippet( $context->msg( 'templatesandbox-editform-legend' )->parse() ), |
332 | 'id' => 'templatesandbox-editform', |
333 | 'classes' => [ 'mw-templatesandbox-fieldset' ], |
334 | 'items' => [ |
335 | // TODO: OOUI should provide a plain content layout, as this is |
336 | // technically an abstract class |
337 | new Layout( [ |
338 | 'content' => new HtmlSnippet( $textHtml . "\n" . $hiddenInputsHtml ) |
339 | ] ), |
340 | new ActionFieldLayout( |
341 | new TitleInputWidget( [ |
342 | 'id' => 'wpTemplateSandboxPage', |
343 | 'name' => 'wpTemplateSandboxPage', |
344 | 'value' => $editpage->templatesandbox_page, |
345 | 'tabIndex' => ++$tabindex, |
346 | 'placeholder' => $context->msg( 'templatesandbox-editform-page-label' )->text(), |
347 | 'infusable' => true, |
348 | ] ), |
349 | new ButtonInputWidget( [ |
350 | 'id' => 'wpTemplateSandboxPreview', |
351 | 'name' => 'wpTemplateSandboxPreview', |
352 | 'label' => $context->msg( 'templatesandbox-editform-view-label' )->text(), |
353 | 'tabIndex' => ++$tabindex, |
354 | 'type' => 'submit', |
355 | 'useInputTag' => true, |
356 | ] ), |
357 | [ 'align' => 'top' ] |
358 | ) |
359 | ] |
360 | ] ); |
361 | |
362 | if ( $helptextHtml ) { |
363 | $fieldsetLayout->addItems( [ |
364 | // TODO: OOUI should provide a plain content layout, as this is |
365 | // technically an abstract class |
366 | new Layout( [ |
367 | 'content' => new HtmlSnippet( $helptextHtml ) |
368 | ] ) |
369 | ] ); |
370 | } |
371 | $output->addHTML( $fieldsetLayout ); |
372 | |
373 | $optionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup(); |
374 | if ( $optionsLookup->getOption( $context->getUser(), 'uselivepreview' ) ) { |
375 | $output->addModules( 'ext.TemplateSandbox.preview' ); |
376 | } |
377 | } |
378 | |
379 | /** |
380 | * Determine if this API module is appropriate for us to mess with. |
381 | * @param ApiBase $module |
382 | * @return bool |
383 | */ |
384 | private static function isUsableApiModule( $module ) { |
385 | return $module instanceof ApiParse || $module instanceof ApiExpandTemplates; |
386 | } |
387 | |
388 | /** |
389 | * Hook for APIGetAllowedParams to add our API parameters to the relevant |
390 | * modules. |
391 | * |
392 | * @param ApiBase $module |
393 | * @param array &$params |
394 | * @param int $flags |
395 | */ |
396 | public function onAPIGetAllowedParams( $module, &$params, $flags ) { |
397 | if ( !self::isUsableApiModule( $module ) ) { |
398 | return; |
399 | } |
400 | |
401 | $contentHandlerFactory = MediaWikiServices::getInstance()->getContentHandlerFactory(); |
402 | $params += [ |
403 | 'templatesandboxprefix' => [ |
404 | ParamValidator::PARAM_TYPE => 'string', |
405 | ParamValidator::PARAM_ISMULTI => true, |
406 | ApiBase::PARAM_HELP_MSG => 'templatesandbox-apihelp-prefix', |
407 | ], |
408 | 'templatesandboxtitle' => [ |
409 | ParamValidator::PARAM_TYPE => 'string', |
410 | ApiBase::PARAM_HELP_MSG => 'templatesandbox-apihelp-title', |
411 | ], |
412 | 'templatesandboxtext' => [ |
413 | ParamValidator::PARAM_TYPE => 'text', |
414 | ApiBase::PARAM_HELP_MSG => 'templatesandbox-apihelp-text', |
415 | ], |
416 | 'templatesandboxcontentmodel' => [ |
417 | ParamValidator::PARAM_TYPE => $contentHandlerFactory->getContentModels(), |
418 | ApiBase::PARAM_HELP_MSG => 'templatesandbox-apihelp-contentmodel', |
419 | ], |
420 | 'templatesandboxcontentformat' => [ |
421 | ParamValidator::PARAM_TYPE => $contentHandlerFactory->getAllContentFormats(), |
422 | ApiBase::PARAM_HELP_MSG => 'templatesandbox-apihelp-contentformat', |
423 | ], |
424 | ]; |
425 | } |
426 | |
427 | /** |
428 | * Hook for ApiMakeParserOptions to set things up for TemplateSandbox |
429 | * parsing when necessary. |
430 | * |
431 | * @param ParserOptions $options |
432 | * @param Title $title |
433 | * @param array $params |
434 | * @param ApiBase $module |
435 | * @param null &$reset Set to a ScopedCallback used to reset any hooks set. |
436 | * @param bool &$suppressCache |
437 | */ |
438 | public function onApiMakeParserOptions( |
439 | $options, $title, $params, $module, &$reset, &$suppressCache |
440 | ) { |
441 | // Shouldn't happen, but... |
442 | if ( !self::isUsableApiModule( $module ) ) { |
443 | return; |
444 | } |
445 | |
446 | $params += [ |
447 | 'templatesandboxprefix' => [], |
448 | 'templatesandboxtitle' => null, |
449 | 'templatesandboxtext' => null, |
450 | 'templatesandboxcontentmodel' => null, |
451 | 'templatesandboxcontentformat' => null, |
452 | ]; |
453 | $params = [ |
454 | // @phan-suppress-next-line PhanImpossibleCondition |
455 | 'prefix' => $params['templatesandboxprefix'] ?: [], |
456 | 'title' => $params['templatesandboxtitle'], |
457 | 'text' => $params['templatesandboxtext'], |
458 | 'contentmodel' => $params['templatesandboxcontentmodel'], |
459 | 'contentformat' => $params['templatesandboxcontentformat'], |
460 | ]; |
461 | |
462 | if ( ( $params['title'] === null ) !== ( $params['text'] === null ) ) { |
463 | $p = $module->getModulePrefix(); |
464 | $module->dieWithError( [ 'templatesandbox-apierror-titleandtext', $p ], 'invalidparammix' ); |
465 | } |
466 | |
467 | $prefixes = []; |
468 | foreach ( $params['prefix'] as $prefix ) { |
469 | $prefixTitle = Title::newFromText( rtrim( $prefix, '/' ) ); |
470 | if ( !$prefixTitle instanceof Title || $prefixTitle->getFragment() !== '' || |
471 | $prefixTitle->isExternal() |
472 | ) { |
473 | $p = $module->getModulePrefix(); |
474 | $module->dieWithError( |
475 | [ 'apierror-badparameter', "{$p}templatesandboxprefix" ], "bad_{$p}templatesandboxprefix" |
476 | ); |
477 | } |
478 | $prefixes[] = $prefixTitle->getPrefixedText(); |
479 | } |
480 | |
481 | if ( $params['title'] !== null ) { |
482 | $page = $module->getTitleOrPageId( $params ); |
483 | if ( $params['contentmodel'] == '' ) { |
484 | $contentHandler = $page->getContentHandler(); |
485 | } else { |
486 | $contentHandler = MediaWikiServices::getInstance()->getContentHandlerFactory() |
487 | // @phan-suppress-next-line PhanTypeMismatchArgumentNullable |
488 | ->getContentHandler( $params['contentmodel'] ); |
489 | } |
490 | |
491 | $escName = wfEscapeWikiText( $page->getTitle()->getPrefixedDBkey() ); |
492 | $model = $contentHandler->getModelID(); |
493 | |
494 | if ( $contentHandler->supportsDirectApiEditing() === false ) { |
495 | $module->dieWithError( [ 'apierror-no-direct-editing', $model, $escName ] ); |
496 | } |
497 | |
498 | // @phan-suppress-next-line PhanImpossibleCondition |
499 | $format = $params['contentformat'] ?: $contentHandler->getDefaultFormat(); |
500 | if ( !$contentHandler->isSupportedFormat( $format ) ) { |
501 | $module->dieWithError( [ 'apierror-badformat', $format, $model, $escName ] ); |
502 | } |
503 | |
504 | $templatetitle = $page->getTitle(); |
505 | $content = $contentHandler->makeContent( $params['text'], $page->getTitle(), $model, $format ); |
506 | |
507 | // Apply PST to templatesandboxtext |
508 | $popts = $page->makeParserOptions( $module ); |
509 | $popts->setIsPreview( true ); |
510 | $popts->setIsSectionPreview( false ); |
511 | $user = RequestContext::getMain()->getUser(); |
512 | $contentTransformer = MediaWikiServices::getInstance()->getContentTransformer(); |
513 | $content = $contentTransformer->preSaveTransform( |
514 | $content, |
515 | $templatetitle, |
516 | $user, |
517 | $popts |
518 | ); |
519 | } else { |
520 | $templatetitle = null; |
521 | $content = null; |
522 | } |
523 | |
524 | if ( $prefixes || $templatetitle ) { |
525 | $logic = new Logic( $prefixes, $templatetitle, $content ); |
526 | $resetLogic = $logic->setupForParse( $options ); |
527 | $suppressCache = true; |
528 | |
529 | $hookContainer = MediaWikiServices::getInstance()->getHookContainer(); |
530 | $resetHook = $hookContainer->scopedRegister( 'ApiParseMakeOutputPage', static function ( $module, $output ) |
531 | use ( $prefixes, $templatetitle, $content ) |
532 | { |
533 | if ( $prefixes ) { |
534 | Logic::addSubpageHandlerToOutput( $prefixes, $output ); |
535 | } |
536 | if ( $templatetitle ) { |
537 | $output->addContentOverride( $templatetitle, $content ); |
538 | } |
539 | } ); |
540 | |
541 | $reset = new ScopedCallback( static function () use ( &$resetLogic, &$resetHook ) { |
542 | ScopedCallback::consume( $resetHook ); |
543 | ScopedCallback::consume( $resetLogic ); |
544 | } ); |
545 | } |
546 | } |
547 | |
548 | /** |
549 | * Function that returns an array of parsed messages used in live preview |
550 | * for the ResourceLoader |
551 | * |
552 | * @param RL\Context $context |
553 | * @return array |
554 | */ |
555 | public static function getParsedMessages( $context ) { |
556 | return [ |
557 | 'templatesandbox-previewnote' => $context->msg( 'templatesandbox-previewnote' )->parse(), |
558 | ]; |
559 | } |
560 | |
561 | /** |
562 | * Function that returns an array of valid namespaces to show the page |
563 | * preview form on for the ResourceLoader |
564 | * |
565 | * @param RL\Context $context |
566 | * @param Config $config |
567 | * @return array |
568 | */ |
569 | public static function getTemplateNamespaces( $context, $config ) { |
570 | return array_merge( |
571 | $config->get( 'TemplateSandboxEditNamespaces' ), |
572 | ExtensionRegistry::getInstance()->getAttribute( 'TemplateSandboxEditNamespaces' ) |
573 | ); |
574 | } |
575 | |
576 | } |