Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 147 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
SpecialTemplateSandbox | |
0.00% |
0 / 147 |
|
0.00% |
0 / 7 |
1722 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 82 |
|
0.00% |
0 / 1 |
240 | |||
validatePageParam | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
30 | |||
validateRevidParam | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
30 | |||
validatePrefixParam | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
56 | |||
onSubmit | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
56 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\TemplateSandbox; |
4 | |
5 | use MediaWiki\Content\Content; |
6 | use MediaWiki\Content\IContentHandlerFactory; |
7 | use MediaWiki\Content\Renderer\ContentRenderer; |
8 | use MediaWiki\EditPage\EditPage; |
9 | use MediaWiki\Html\Html; |
10 | use MediaWiki\HTMLForm\HTMLForm; |
11 | use MediaWiki\Page\WikiPageFactory; |
12 | use MediaWiki\Parser\ParserOutput; |
13 | use MediaWiki\Revision\RevisionLookup; |
14 | use MediaWiki\Revision\RevisionRecord; |
15 | use MediaWiki\Revision\SlotRecord; |
16 | use MediaWiki\SpecialPage\SpecialPage; |
17 | use MediaWiki\Status\Status; |
18 | use MediaWiki\Title\Title; |
19 | use MediaWiki\User\TempUser\TempUserConfig; |
20 | |
21 | class SpecialTemplateSandbox extends SpecialPage { |
22 | /** @var string[] */ |
23 | private $prefixes = []; |
24 | |
25 | /** |
26 | * @var null|ParserOutput |
27 | */ |
28 | private $output = null; |
29 | |
30 | /** @var RevisionLookup */ |
31 | private $revisionLookup; |
32 | |
33 | /** @var IContentHandlerFactory */ |
34 | private $contentHandlerFactory; |
35 | |
36 | /** @var WikiPageFactory */ |
37 | private $wikiPageFactory; |
38 | |
39 | /** @var ContentRenderer */ |
40 | private $contentRenderer; |
41 | |
42 | /** @var TempUserConfig */ |
43 | private $tempUserConfig; |
44 | |
45 | /** |
46 | * @param RevisionLookup $revisionLookup |
47 | * @param IContentHandlerFactory $contentHandlerFactory |
48 | * @param WikiPageFactory $wikiPageFactory |
49 | * @param ContentRenderer $contentRenderer |
50 | */ |
51 | public function __construct( |
52 | RevisionLookup $revisionLookup, |
53 | IContentHandlerFactory $contentHandlerFactory, |
54 | WikiPageFactory $wikiPageFactory, |
55 | ContentRenderer $contentRenderer, |
56 | TempUserConfig $tempUserConfig |
57 | ) { |
58 | parent::__construct( 'TemplateSandbox' ); |
59 | $this->revisionLookup = $revisionLookup; |
60 | $this->contentHandlerFactory = $contentHandlerFactory; |
61 | $this->wikiPageFactory = $wikiPageFactory; |
62 | $this->contentRenderer = $contentRenderer; |
63 | $this->tempUserConfig = $tempUserConfig; |
64 | } |
65 | |
66 | /** @inheritDoc */ |
67 | protected function getGroupName() { |
68 | return 'wiki'; |
69 | } |
70 | |
71 | /** |
72 | * @param string|null $par |
73 | */ |
74 | public function execute( $par ) { |
75 | $this->setHeaders(); |
76 | $this->addHelpLink( 'Help:Extension:TemplateSandbox' ); |
77 | $this->checkPermissions(); |
78 | |
79 | $request = $this->getRequest(); |
80 | |
81 | if ( $par !== null && !$request->getCheck( 'page' ) ) { |
82 | $request->setVal( 'page', $par ); |
83 | } |
84 | |
85 | if ( $this->tempUserConfig->isEnabled() && $this->getUser()->isAnon() ) { |
86 | // Don't expose IP address for anonymous users if using temp accounts. |
87 | $default_prefix = null; |
88 | } else { |
89 | $default_prefix = Title::makeTitle( NS_USER, |
90 | $this->getUser()->getName() . '/' . $this->msg( 'templatesandbox-suffix' )->plain() |
91 | )->getPrefixedText(); |
92 | } |
93 | |
94 | $form = HTMLForm::factory( 'ooui', [ |
95 | 'prefix' => [ |
96 | 'type' => 'text', |
97 | 'name' => 'prefix', |
98 | 'default' => $default_prefix, |
99 | 'label-message' => 'templatesandbox-prefix-label', |
100 | 'validation-callback' => [ $this, 'validatePrefixParam' ], |
101 | ], |
102 | |
103 | 'page' => [ |
104 | 'type' => 'text', |
105 | 'name' => 'page', |
106 | 'label-message' => 'templatesandbox-page-label', |
107 | 'validation-callback' => [ $this, 'validatePageParam' ], |
108 | ], |
109 | |
110 | 'revid' => [ |
111 | 'type' => 'int', |
112 | 'name' => 'revid', |
113 | 'label-message' => 'templatesandbox-revid-label', |
114 | 'validation-callback' => [ $this, 'validateRevidParam' ], |
115 | ], |
116 | |
117 | 'text' => [ |
118 | 'type' => 'textarea', |
119 | 'name' => 'text', |
120 | 'label-message' => 'templatesandbox-text-label', |
121 | 'useeditfont' => true, |
122 | 'rows' => 5, |
123 | ], |
124 | ], $this->getContext() ); |
125 | $form->setSubmitCallback( [ $this, 'onSubmit' ] ); |
126 | $form->setWrapperLegendMsg( 'templatesandbox-legend' ); |
127 | $form->addHeaderHtml( $this->msg( 'templatesandbox-text' )->parseAsBlock() ); |
128 | $form->setSubmitTextMsg( 'templatesandbox-submit' ); |
129 | |
130 | $form->prepareForm(); |
131 | if ( $request->getCheck( 'page' ) || $request->getCheck( 'revid' ) ) { |
132 | $form->displayForm( $form->tryAuthorizedSubmit() ); |
133 | } else { |
134 | $form->displayForm( false ); |
135 | } |
136 | |
137 | $user = $this->getUser(); |
138 | $output = $this->getOutput(); |
139 | $error = false; |
140 | if ( $this->getRequest()->wasPosted() ) { |
141 | if ( $user->isAnon() && !$user->isAllowed( 'edit' ) ) { |
142 | $error = 'templatesandbox-fail-post-anon'; |
143 | } elseif ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ), '', $request ) ) { |
144 | $error = 'templatesandbox-fail-post'; |
145 | } |
146 | } |
147 | if ( $error !== false ) { |
148 | $output->addHTML( |
149 | Html::errorBox( |
150 | $output->msg( $error )->parse(), |
151 | '', |
152 | 'previewnote' |
153 | ) |
154 | ); |
155 | } elseif ( $this->output !== null ) { |
156 | // Anons have predictable edit tokens, only do the JS/CSS preview for logged-in users. |
157 | if ( $user->isAnon() ) { |
158 | $output->addHTML( |
159 | Html::warningBox( |
160 | $output->msg( 'templatesandbox-anon-limited-preview' )->parse(), |
161 | 'previewnote' |
162 | ) |
163 | ); |
164 | } else { |
165 | Logic::addSubpageHandlerToOutput( $this->prefixes, $output ); |
166 | } |
167 | $output->addParserOutput( $this->output ); |
168 | |
169 | $output->addHTML( Html::rawElement( |
170 | 'div', |
171 | [ 'class' => 'limitreport', 'style' => 'clear:both' ], |
172 | EditPage::getPreviewLimitReport( $this->output ) |
173 | ) ); |
174 | $output->addModules( 'mediawiki.collapseFooterLists' ); |
175 | |
176 | $titleText = $this->output->getTitleText(); |
177 | if ( strval( $titleText ) !== '' ) { |
178 | $output->setPageTitleMsg( $this->msg( 'templatesandbox-title-output', $titleText ) ); |
179 | } |
180 | } |
181 | } |
182 | |
183 | /** |
184 | * @param string|null $value |
185 | * @param array $allData |
186 | * @return bool|string |
187 | */ |
188 | public function validatePageParam( $value, $allData ) { |
189 | if ( $value === '' || $value === null ) { |
190 | return true; |
191 | } |
192 | $title = Title::newFromText( $value ); |
193 | if ( !$title instanceof Title ) { |
194 | return $this->msg( 'templatesandbox-invalid-title' )->parseAsBlock(); |
195 | } |
196 | if ( !$title->exists() ) { |
197 | return $this->msg( 'templatesandbox-title-not-exists' )->parseAsBlock(); |
198 | } |
199 | return true; |
200 | } |
201 | |
202 | /** |
203 | * @param string|null $value |
204 | * @param array $allData |
205 | * @return bool|string |
206 | */ |
207 | public function validateRevidParam( $value, $allData ) { |
208 | if ( $value === '' || $value === null ) { |
209 | return true; |
210 | } |
211 | |
212 | $revisionRecord = $this->revisionLookup->getRevisionById( (int)$value ); |
213 | if ( $revisionRecord === null ) { |
214 | return $this->msg( 'templatesandbox-revision-not-exists' )->parseAsBlock(); |
215 | } |
216 | |
217 | $content = $revisionRecord->getContent( |
218 | SlotRecord::MAIN, |
219 | RevisionRecord::FOR_THIS_USER, |
220 | $this->getUser() |
221 | ); |
222 | |
223 | if ( $content === null ) { |
224 | return $this->msg( 'templatesandbox-revision-no-content' )->parseAsBlock(); |
225 | } |
226 | return true; |
227 | } |
228 | |
229 | /** |
230 | * @param string|null $value |
231 | * @param array $allData |
232 | * @return bool|string |
233 | */ |
234 | public function validatePrefixParam( $value, $allData ) { |
235 | if ( $value === '' || $value === null ) { |
236 | return true; |
237 | } |
238 | $prefixes = array_map( 'trim', explode( '|', $value ) ); |
239 | foreach ( $prefixes as $prefix ) { |
240 | $title = Title::newFromText( rtrim( $prefix, '/' ) ); |
241 | if ( !$title instanceof Title || $title->getFragment() !== '' ) { |
242 | return $this->msg( 'templatesandbox-invalid-prefix' )->parseAsBlock(); |
243 | } |
244 | if ( $title->isExternal() ) { |
245 | return $this->msg( 'templatesandbox-prefix-not-local' )->parseAsBlock(); |
246 | } |
247 | $this->prefixes[] = $title->getPrefixedText(); |
248 | } |
249 | return true; |
250 | } |
251 | |
252 | /** |
253 | * @param array $data |
254 | * @param HTMLForm $form |
255 | * @return Status |
256 | */ |
257 | public function onSubmit( $data, $form ) { |
258 | if ( $data['revid'] !== '' && $data['revid'] !== null ) { |
259 | $rev = $this->revisionLookup->getRevisionById( $data['revid'] ); |
260 | $title = Title::newFromLinkTarget( $rev->getPageAsLinkTarget() ); |
261 | } elseif ( $data['page'] !== '' && $data['page'] !== null ) { |
262 | $title = Title::newFromText( $data['page'] ); |
263 | $rev = $this->revisionLookup->getRevisionByTitle( $title ); |
264 | } else { |
265 | return Status::newFatal( 'templatesandbox-page-or-revid' ); |
266 | } |
267 | |
268 | if ( $data['text'] !== '' && $data['text'] !== null ) { |
269 | $content = $this->contentHandlerFactory |
270 | ->getContentHandler( $rev->getSlot( SlotRecord::MAIN )->getModel() ) |
271 | ->unserializeContent( $data['text'] ); |
272 | } else { |
273 | $content = $rev->getContent( |
274 | SlotRecord::MAIN, |
275 | RevisionRecord::FOR_THIS_USER, |
276 | $this->getUser() |
277 | ); |
278 | } |
279 | |
280 | // Title and Content are validated by validatePrefixParam and validatePageParam |
281 | '@phan-var Title $title'; |
282 | '@phan-var Content $content'; |
283 | |
284 | $page = $this->wikiPageFactory->newFromTitle( $title ); |
285 | $popts = $page->makeParserOptions( $this->getContext() ); |
286 | $popts->setIsPreview( true ); |
287 | $popts->setIsSectionPreview( false ); |
288 | $logic = new Logic( $this->prefixes, null, null ); |
289 | $reset = $logic->setupForParse( $popts ); |
290 | $this->output = $this->contentRenderer->getParserOutput( $content, $title, $rev, $popts ); |
291 | |
292 | return Status::newGood(); |
293 | } |
294 | } |