MediaWiki master
SpecialExpandTemplates.php
Go to the documentation of this file.
1<?php
7namespace MediaWiki\Specials;
8
23
32
34 private const MAX_INCLUDE_SIZE = 50_000_000;
35
36 public function __construct(
37 private readonly ParserFactory $parserFactory,
38 private readonly UserOptionsLookup $userOptionsLookup,
39 private readonly TidyDriverBase $tidy,
40 ) {
41 parent::__construct( 'ExpandTemplates' );
42 }
43
48 public function execute( $subpage ) {
49 $this->setHeaders();
50 $this->addHelpLink( 'Help:ExpandTemplates' );
51
52 $request = $this->getRequest();
53 $input = $request->getText( 'wpInput' );
54
55 if ( $input !== '' ) {
56 $removeComments = $request->getBool( 'wpRemoveComments', false );
57 $removeNowiki = $request->getBool( 'wpRemoveNowiki', false );
58 $generateXML = $request->getBool( 'wpGenerateXml' );
59 $generateRawHtml = $request->getBool( 'wpGenerateRawHtml' );
60
61 $options = ParserOptions::newFromContext( $this->getContext() );
62 $options->setRemoveComments( $removeComments );
63 $options->setMaxIncludeSize( self::MAX_INCLUDE_SIZE );
64 $options->setSuppressSectionEditLinks();
65
66 $titleStr = $request->getText( 'wpContextTitle' );
67 $title = Title::newFromText( $titleStr );
68 if ( !$title ) {
69 $title = $this->getPageTitle();
70 $options->setTargetLanguage( $this->getContentLanguage() );
71 }
72
73 $parser = $this->parserFactory->getInstance();
74 if ( $generateXML ) {
75 $parser->startExternalParse( $title, $options, Parser::OT_PREPROCESS );
76 $dom = $parser->preprocessToDom( $input );
77
78 if ( method_exists( $dom, 'saveXML' ) ) {
79 // @phan-suppress-next-line PhanUndeclaredMethod
80 $xml = $dom->saveXML();
81 } else {
82 // @phan-suppress-next-line PhanUndeclaredMethod
83 $xml = $dom->__toString();
84 }
85 }
86
87 $output = $parser->preprocess( $input, $title, $options );
88 $this->makeForm();
89
90 $out = $this->getOutput();
91 if ( $generateXML ) {
92 // @phan-suppress-next-line PhanPossiblyUndeclaredVariable xml is set when used
93 $out->addHTML( $this->makeOutput( $xml, 'expand_templates_xml_output' ) );
94 }
95
96 $tmp = $this->makeOutput( $output );
97
98 if ( $removeNowiki ) {
99 $tmp = preg_replace(
100 [ '_&lt;nowiki&gt;_', '_&lt;/nowiki&gt;_', '_&lt;nowiki */&gt;_' ],
101 '',
102 $tmp
103 );
104 }
105
106 $tmp = $this->tidy->tidy( $tmp );
107
108 $out->addHTML( $tmp );
109
110 $pout = $parser->parse( $output, $title, $options );
111 // TODO T371008 consider if using the Content framework makes sense instead of creating the pipeline
112 $rawhtml = MediaWikiServices::getInstance()->getDefaultOutputPipeline()
113 ->run( $pout, $options, [] )->getContentHolderText();
114 if ( $generateRawHtml && $rawhtml !== '' ) {
115 $out->addHTML( $this->makeOutput( $rawhtml, 'expand_templates_html_output' ) );
116 }
117
118 $this->showHtmlPreview( $pout, $options, $out );
119 } else {
120 $this->makeForm();
121 }
122 }
123
132 private function onSubmitInput( array $values ) {
133 $status = Status::newGood();
134 if ( $values['Input'] === '' ) {
135 $status = Status::newFatal( 'expand_templates_input_missing' );
136 }
137 return $status;
138 }
139
143 private function makeForm() {
144 $fields = [
145 'Input' => [
146 'type' => 'textarea',
147 'label' => $this->msg( 'expand_templates_input' )->text(),
148 'rows' => 10,
149 'id' => 'input',
150 'useeditfont' => true,
151 'required' => true,
152 'autofocus' => true,
153 ],
154 'ContextTitle' => [
155 'type' => 'text',
156 'label' => $this->msg( 'expand_templates_title' )->plain(),
157 'id' => 'contexttitle',
158 'size' => 60,
159 ],
160 'RemoveComments' => [
161 'type' => 'check',
162 'label' => $this->msg( 'expand_templates_remove_comments' )->text(),
163 'id' => 'removecomments',
164 'default' => true,
165 ],
166 'RemoveNowiki' => [
167 'type' => 'check',
168 'label' => $this->msg( 'expand_templates_remove_nowiki' )->text(),
169 'id' => 'removenowiki',
170 ],
171 'GenerateXml' => [
172 'type' => 'check',
173 'label' => $this->msg( 'expand_templates_generate_xml' )->text(),
174 'id' => 'generate_xml',
175 ],
176 'GenerateRawHtml' => [
177 'type' => 'check',
178 'label' => $this->msg( 'expand_templates_generate_rawhtml' )->text(),
179 'id' => 'generate_rawhtml',
180 ],
181 ];
182
183 $form = HTMLForm::factory( 'ooui', $fields, $this->getContext() );
184 $form
185 ->setSubmitTextMsg( 'expand_templates_ok' )
186 ->setWrapperLegendMsg( 'expandtemplates' )
187 ->setHeaderHtml( $this->msg( 'expand_templates_intro' )->parse() )
188 ->setSubmitCallback( $this->onSubmitInput( ... ) )
189 ->showAlways();
190 }
191
199 private function makeOutput( $output, $heading = 'expand_templates_output' ) {
200 $out = "<h2>" . $this->msg( $heading )->escaped() . "</h2>\n";
201 $out .= Html::textarea(
202 'output',
203 $output,
204 [
205 // #output is used by CodeMirror (T384148)
206 'id' => $heading === 'expand_templates_output' ? 'output' : $heading,
207 'cols' => 10,
208 'rows' => 10,
209 'readonly' => 'readonly',
210 'class' => 'mw-editfont-' . $this->userOptionsLookup->getOption( $this->getUser(), 'editfont' )
211 ]
212 );
213
214 return $out;
215 }
216
224 private function showHtmlPreview( ParserOutput $pout, ParserOptions $popts, OutputPage $out ) {
225 $out->addHTML( "<h2>" . $this->msg( 'expand_templates_preview' )->escaped() . "</h2>\n" );
226
227 if ( $this->getConfig()->get( MainConfigNames::RawHtml ) ) {
228 $request = $this->getRequest();
229 $user = $this->getUser();
230
231 // To prevent cross-site scripting attacks, don't show the preview if raw HTML is
232 // allowed and a valid edit token is not provided (T73111). However, MediaWiki
233 // does not currently provide logged-out users with CSRF protection; in that case,
234 // do not show the preview unless anonymous editing is allowed.
235 if ( $user->isAnon() && !$this->getAuthority()->isAllowed( 'edit' ) ) {
236 $error = [ 'expand_templates_preview_fail_html_anon' ];
237 } elseif ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ), '', $request ) ) {
238 $error = [ 'expand_templates_preview_fail_html' ];
239 } else {
240 $error = false;
241 }
242
243 if ( $error ) {
244 $out->addHTML(
245 Html::errorBox(
246 $out->msg( $error )->parse(),
247 '',
248 'previewnote'
249 )
250 );
251 return;
252 }
253 }
254
255 $out->addParserOutputContent( $pout, $popts );
256 $out->addCategoryLinks( $pout->getCategoryMap() );
257 }
258
260 protected function getGroupName() {
261 return 'wiki';
262 }
263}
264
266class_alias( SpecialExpandTemplates::class, 'SpecialExpandTemplates' );
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:208
This class is a collection of static functions that serve two purposes:
Definition Html.php:44
A class containing constants representing the names of configuration variables.
const RawHtml
Name constant for the RawHtml setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
This is one of the Core classes and should be read at least once by any new developers.
Set options of the Parser.
ParserOutput is a rendering of a Content object or a message.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:138
Parent class for all special pages.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getUser()
Shortcut to get the User executing this instance.
getPageTitle( $subpage=false)
Get a self-referential title object.
getConfig()
Shortcut to get main config object.
getContext()
Gets the context this SpecialPage is executed in.
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
getContentLanguage()
Shortcut to get content language.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
A special page to enter wikitext and expands its templates, parser functions, and variables,...
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
__construct(private readonly ParserFactory $parserFactory, private readonly UserOptionsLookup $userOptionsLookup, private readonly TidyDriverBase $tidy,)
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
Base class for HTML cleanup utilities.
Represents a title within MediaWiki.
Definition Title.php:69
Provides access to user options.