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