Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
14.78% |
34 / 230 |
|
0.00% |
0 / 22 |
CRAP | |
0.00% |
0 / 1 |
SpecialWelcomeSurvey | |
14.78% |
34 / 230 |
|
0.00% |
0 / 22 |
1298.17 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDisplayFormat | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
75.00% |
9 / 12 |
|
0.00% |
0 / 1 |
6.56 | |||
getDescription | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
doesWrites | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
processSkip | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
12 | |||
getFormFields | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
42 | |||
alterForm | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
2 | |||
onSubmit | |
80.65% |
25 / 31 |
|
0.00% |
0 / 1 |
3.07 | |||
showConfirmationPage | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
showHomepageAwareConfirmationPage | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
12 | |||
showDefaultConfirmationPage | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
6 | |||
getCloseButtonHtml | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
getConfirmationButtonsWrapper | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getHomepageAwareActionButtons | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
getHomepageButton | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
2 | |||
redirect | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
buildGettingStartedLinks | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
20 | |||
buildSidebar | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
2 | |||
loadDependencies | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
initializeWelcomeSurveyLogger | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace GrowthExperiments\Specials; |
4 | |
5 | use GrowthExperiments\EventLogging\WelcomeSurveyLogger; |
6 | use GrowthExperiments\HomepageHooks; |
7 | use GrowthExperiments\Util; |
8 | use GrowthExperiments\WelcomeSurveyFactory; |
9 | use MediaWiki\Config\ConfigException; |
10 | use MediaWiki\Html\Html; |
11 | use MediaWiki\HTMLForm\HTMLForm; |
12 | use MediaWiki\SpecialPage\FormSpecialPage; |
13 | use MediaWiki\SpecialPage\SpecialPageFactory; |
14 | use MediaWiki\Status\Status; |
15 | use MediaWiki\Title\Title; |
16 | use MediaWiki\Utils\MWTimestamp; |
17 | |
18 | class SpecialWelcomeSurvey extends FormSpecialPage { |
19 | |
20 | public const ACTION_VIEW = 'view'; |
21 | public const ACTION_SUBMIT_ATTEMPT = 'submit_attempt'; |
22 | public const ACTION_SAVE = 'save'; |
23 | public const ACTION_SKIP = 'skip'; |
24 | public const ACTION_SUBMIT_SUCCESS = 'submit_success'; |
25 | public const ACTION_SHOW_CONFIRMATION_PAGE = 'show_confirmation_page'; |
26 | |
27 | private string $groupName; |
28 | private SpecialPageFactory $specialPageFactory; |
29 | private WelcomeSurveyFactory $welcomeSurveyFactory; |
30 | private WelcomeSurveyLogger $welcomeSurveyLogger; |
31 | |
32 | /** |
33 | * @param SpecialPageFactory $specialPageFactory |
34 | * @param WelcomeSurveyFactory $welcomeSurveyFactory |
35 | * @param WelcomeSurveyLogger $welcomeSurveyLogger |
36 | */ |
37 | public function __construct( |
38 | SpecialPageFactory $specialPageFactory, |
39 | WelcomeSurveyFactory $welcomeSurveyFactory, |
40 | WelcomeSurveyLogger $welcomeSurveyLogger |
41 | ) { |
42 | parent::__construct( 'WelcomeSurvey', '', false ); |
43 | $this->specialPageFactory = $specialPageFactory; |
44 | $this->welcomeSurveyFactory = $welcomeSurveyFactory; |
45 | $this->welcomeSurveyLogger = $welcomeSurveyLogger; |
46 | } |
47 | |
48 | /** @inheritDoc */ |
49 | protected function getGroupName() { |
50 | return 'growth-tools'; |
51 | } |
52 | |
53 | /** |
54 | * @inheritDoc |
55 | */ |
56 | protected function getDisplayFormat() { |
57 | return 'ooui'; |
58 | } |
59 | |
60 | /** |
61 | * @inheritDoc |
62 | */ |
63 | public function execute( $par ) { |
64 | $this->initializeWelcomeSurveyLogger(); |
65 | if ( !$par && !$this->getRequest()->wasPosted() ) { |
66 | $this->welcomeSurveyLogger->logInteraction( self::ACTION_VIEW ); |
67 | } |
68 | if ( !$par && $this->getRequest()->wasPosted() ) { |
69 | $this->welcomeSurveyLogger->logInteraction( self::ACTION_SUBMIT_ATTEMPT ); |
70 | } |
71 | $this->requireNamedUser(); |
72 | if ( $par === 'skip' ) { |
73 | $this->processSkip(); |
74 | return; |
75 | } |
76 | $this->getOutput()->addModuleStyles( 'ext.growthExperiments.Account.styles' ); |
77 | $this->getOutput()->addJsConfigVars( 'welcomesurvey', true ); |
78 | parent::execute( $par ); |
79 | } |
80 | |
81 | /** |
82 | * Overridden in order to inject the current user's name as message parameter |
83 | * |
84 | * @inheritDoc |
85 | */ |
86 | public function getDescription() { |
87 | return $this->msg( strtolower( $this->mName ) ) |
88 | ->params( $this->getUser()->getName() ); |
89 | } |
90 | |
91 | /** |
92 | * @inheritDoc |
93 | */ |
94 | public function doesWrites() { |
95 | return true; |
96 | } |
97 | |
98 | /** |
99 | * Handle the /skip endpoint, used to dismiss reminder notices about the survey |
100 | * as a no-JS fallback to the /growthexperiments/v0/welcomesurvey/skip API. |
101 | */ |
102 | protected function processSkip() { |
103 | $output = $this->getOutput(); |
104 | |
105 | // Don't do writes on GET. There is no legitimate way to get here with a GET query |
106 | // so we don't bother with error processing. |
107 | if ( $this->getRequest()->wasPosted() ) { |
108 | $csrfToken = $this->getRequest()->getRawVal( 'token' ); |
109 | if ( !$this->getContext()->getCsrfTokenSet()->matchToken( $csrfToken, 'welcomesurvey' ) ) { |
110 | $output->showErrorPage( 'sessionfailure-title', 'sessionfailure', [], |
111 | $this->specialPageFactory->getTitleForAlias( 'Homepage' ) ); |
112 | return; |
113 | } |
114 | |
115 | $welcomeSurvey = $this->welcomeSurveyFactory->newWelcomeSurvey( $this->getContext() ); |
116 | $welcomeSurvey->dismiss(); |
117 | } |
118 | |
119 | $output->redirect( |
120 | $this->specialPageFactory->getTitleForAlias( 'Homepage' )->getFullURL() |
121 | ); |
122 | } |
123 | |
124 | /** |
125 | * Get an HTMLForm descriptor array |
126 | * @return array |
127 | */ |
128 | protected function getFormFields() { |
129 | $welcomeSurvey = $this->welcomeSurveyFactory->newWelcomeSurvey( $this->getContext() ); |
130 | $this->groupName = $welcomeSurvey->getGroup( true ); |
131 | $questions = $welcomeSurvey->getQuestions( $this->groupName ); |
132 | |
133 | if ( !$questions ) { |
134 | // redirect away |
135 | $request = $this->getRequest(); |
136 | $this->redirect( |
137 | $request->getVal( 'returnto' ), |
138 | $request->getVal( 'returntoquery' ) |
139 | ); |
140 | return []; |
141 | } |
142 | |
143 | // Transform questions |
144 | foreach ( $questions as &$question ) { |
145 | // Add select options for 'placeholder' and 'other' |
146 | if ( $question[ 'type' ] === 'select' ) { |
147 | if ( isset( $question[ 'placeholder-message' ] ) ) { |
148 | // Add 'placeholder' as the first options |
149 | $question['options-messages'] = [ $question['placeholder-message'] => 'placeholder' ] + |
150 | $question['options-messages']; |
151 | } |
152 | if ( isset( $question[ 'other-message' ] ) ) { |
153 | // Add 'other' as the last options |
154 | $question['options-messages'] += [ $question[ 'other-message' ] => 'other' ]; |
155 | } |
156 | } |
157 | } |
158 | $this->loadDependencies( $questions ); |
159 | return $questions; |
160 | } |
161 | |
162 | /** |
163 | * @inheritDoc |
164 | */ |
165 | protected function alterForm( HTMLForm $form ) { |
166 | $form->setId( 'welcome-survey-form' ); |
167 | |
168 | // subtitle |
169 | $form->addHeaderHtml( |
170 | Html::rawElement( |
171 | 'div', |
172 | [ 'class' => 'welcomesurvey-subtitle' ], |
173 | $this->msg( 'welcomesurvey-subtitle' )->parse() |
174 | ) |
175 | ); |
176 | |
177 | $form->addHiddenField( '_render_date', MWTimestamp::now() ); |
178 | $form->addHiddenField( '_group', $this->groupName ); |
179 | $form->addHiddenField( '_returnto', $this->getRequest()->getVal( 'returnto' ) ); |
180 | $form->addHiddenField( '_welcomesurveytoken', $this->getRequest()->getVal( '_welcomesurveytoken' ) ); |
181 | |
182 | // save button |
183 | $form->setSubmitTextMsg( 'welcomesurvey-save-btn' ); |
184 | $form->setSubmitName( 'save' ); |
185 | |
186 | // skip button |
187 | $form->addButton( [ |
188 | 'name' => 'skip', |
189 | 'value' => 'skip', |
190 | 'framed' => false, |
191 | 'flags' => 'destructive', |
192 | 'attribs' => [ 'class' => 'welcomesurvey-skip-btn' ], |
193 | 'label-message' => [ 'welcomesurvey-skip-btn', $this->getUser()->getName() ], |
194 | ] ); |
195 | |
196 | // sidebar |
197 | $form->setPostHtml( $this->buildSidebar() ); |
198 | } |
199 | |
200 | /** |
201 | * Process the form on POST submission. |
202 | * @param array $data |
203 | * @return bool|string|array|Status As documented for HTMLForm::trySubmit. |
204 | */ |
205 | public function onSubmit( array $data ) { |
206 | $this->initializeWelcomeSurveyLogger(); |
207 | |
208 | $request = $this->getRequest(); |
209 | $save = $request->getBool( 'save' ); |
210 | $group = $request->getVal( '_group' ); |
211 | $token = $request->getVal( '_welcomesurveytoken' ); |
212 | $renderDate = $request->getVal( '_render_date' ); |
213 | $redirectParams = wfCgiToArray( $request->getVal( 'redirectparams', '' ) ); |
214 | $returnTo = $redirectParams[ 'returnto' ] ?? $request->getVal( '_returnto', '' ); |
215 | $returnToQuery = $redirectParams[ 'returntoquery' ] ?? ''; |
216 | |
217 | $welcomeSurvey = $this->welcomeSurveyFactory->newWelcomeSurvey( $this->getContext() ); |
218 | $welcomeSurvey->handleResponses( |
219 | $data, |
220 | $save, |
221 | $group, |
222 | $renderDate |
223 | ); |
224 | |
225 | $this->welcomeSurveyLogger->logInteraction( self::ACTION_SUBMIT_SUCCESS ); |
226 | |
227 | if ( $save ) { |
228 | // show confirmation page |
229 | $returnToQueryArray = wfCgiToArray( $returnToQuery ); |
230 | $returnToQueryArray['_welcomesurveytoken'] = $token; |
231 | $returnToQuery = wfArrayToCgi( $returnToQueryArray ); |
232 | $this->welcomeSurveyLogger->logInteraction( self::ACTION_SAVE ); |
233 | $this->showConfirmationPage( $returnTo, $returnToQuery ); |
234 | } else { |
235 | // redirect to pre-createaccount page with query |
236 | $returnToQueryArray = wfCgiToArray( $returnToQuery ); |
237 | $returnToQueryArray['_welcomesurveytoken'] = $token; |
238 | if ( HomepageHooks::isHomepageEnabled( $this->getUser() ) ) { |
239 | $returnToQueryArray['source'] = 'welcomesurvey-originalcontext'; |
240 | } |
241 | $returnToQuery = wfArrayToCgi( $returnToQueryArray ); |
242 | $this->welcomeSurveyLogger->logInteraction( self::ACTION_SKIP ); |
243 | $this->redirect( $returnTo, $returnToQuery ); |
244 | } |
245 | |
246 | return true; |
247 | } |
248 | |
249 | private function showConfirmationPage( $to, $query ) { |
250 | $this->getOutput()->setPageTitleMsg( $this->msg( 'welcomesurvey-save-confirmation-title' ) ); |
251 | HomepageHooks::isHomepageEnabled( $this->getUser() ) ? |
252 | $this->showHomepageAwareConfirmationPage( $to, $query ) : |
253 | $this->showDefaultConfirmationPage( $to, $query ); |
254 | $this->welcomeSurveyLogger->logInteraction( self::ACTION_SHOW_CONFIRMATION_PAGE ); |
255 | } |
256 | |
257 | private function showHomepageAwareConfirmationPage( $to, $query ) { |
258 | $title = Title::newFromText( $to ) ?: $this->specialPageFactory->getTitleForAlias( 'Homepage' ); |
259 | if ( $title->isMainPage() ) { |
260 | $title = $this->specialPageFactory->getTitleForAlias( 'Homepage' ); |
261 | } |
262 | |
263 | $this->getOutput()->addHTML( |
264 | Html::rawElement( |
265 | 'div', |
266 | [ 'class' => 'welcomesurvey-confirmation' ], |
267 | $this->msg( 'welcomesurvey-save-confirmation-text' ) |
268 | ->parseAsBlock() . |
269 | $this->getHomepageAwareActionButtons( $title, $query ) |
270 | ) |
271 | ); |
272 | } |
273 | |
274 | private function showDefaultConfirmationPage( $to, $query ) { |
275 | $this->getOutput()->addHTML( |
276 | Html::rawElement( |
277 | 'div', |
278 | [ 'class' => 'welcomesurvey-confirmation' ], |
279 | $this->msg( 'welcomesurvey-save-confirmation-text' ) |
280 | ->parseAsBlock() . |
281 | Html::element( |
282 | 'div', |
283 | [ 'class' => 'welcomesurvey-confirmation-editing-title' ], |
284 | $this->msg( 'welcomesurvey-sidebar-editing-title' )->text() |
285 | ) . |
286 | $this->msg( 'welcomesurvey-sidebar-editing-text' ) |
287 | ->params( $this->getUser()->getName() ) |
288 | ->parseAsBlock() . |
289 | $this->buildGettingStartedLinks( 'confirmation' ) . |
290 | $this->getCloseButtonHtml( Title::newFromText( $to ) ?: Title::newMainPage(), $query ) |
291 | ) |
292 | ); |
293 | } |
294 | |
295 | /** |
296 | * @param Title $title |
297 | * @param string $query |
298 | * @return string |
299 | * @throws ConfigException |
300 | */ |
301 | private function getCloseButtonHtml( Title $title, $query ) { |
302 | return $this->getConfirmationButtonsWrapper( |
303 | Html::linkButton( |
304 | $this->msg( 'welcomesurvey-close-btn', $title->getPrefixedText() )->text(), |
305 | [ |
306 | 'href' => $title->getLinkURL( $query ), |
307 | 'class' => 'mw-ui-button mw-ui-progressive' |
308 | ] |
309 | ) |
310 | ); |
311 | } |
312 | |
313 | private function getConfirmationButtonsWrapper( $rawHtml ) { |
314 | return Html::rawElement( |
315 | 'div', |
316 | [ 'class' => 'welcomesurvey-confirmation-buttons' ], |
317 | $rawHtml |
318 | ); |
319 | } |
320 | |
321 | private function getHomepageAwareActionButtons( Title $title, $query ) { |
322 | if ( $title->isSpecial( 'Homepage' ) ) { |
323 | return $this->getConfirmationButtonsWrapper( $this->getHomepageButton() ); |
324 | } |
325 | $queryArray = wfCgiToArray( $query ); |
326 | $queryArray['source'] = 'welcomesurvey-originalcontext'; |
327 | return $this->getConfirmationButtonsWrapper( |
328 | Html::linkButton( $this->msg( 'welcomesurvey-close-btn', $title )->text(), [ |
329 | 'href' => $title->getLinkURL( wfArrayToCgi( $queryArray ) ), |
330 | 'class' => 'mw-ui-button mw-ui-safe' |
331 | ] ) . |
332 | $this->getHomepageButton() |
333 | ); |
334 | } |
335 | |
336 | private function getHomepageButton() { |
337 | return Html::linkButton( |
338 | $this->msg( 'growthexperiments-homepage-welcomesurvey-default-close', |
339 | $this->getUser()->getName() |
340 | )->text(), |
341 | [ |
342 | 'href' => $this->specialPageFactory->getTitleForAlias( 'Homepage' )->getLinkURL( |
343 | [ 'source' => 'specialwelcomesurvey' ] |
344 | ), |
345 | 'class' => 'mw-ui-button mw-ui-progressive mw-ge-welcomesurvey-homepage-button' |
346 | ] |
347 | ); |
348 | } |
349 | |
350 | private function redirect( $to, $query ) { |
351 | $title = Title::newFromText( $to ) ?: Title::newMainPage(); |
352 | $this->getOutput()->redirect( $title->getFullUrlForRedirect( $query ) ); |
353 | } |
354 | |
355 | private function buildGettingStartedLinks( $source ) { |
356 | $html = '<ul class="welcomesurvey-gettingstarted-links">'; |
357 | for ( $i = 1; $i <= 4; $i++ ) { |
358 | $text = $this->msg( "welcomesurvey-sidebar-editing-link$i-text" ); |
359 | $title = $this->msg( "welcomesurvey-sidebar-editing-link$i-title" ); |
360 | if ( $text->isDisabled() || $title->isDisabled() ) { |
361 | continue; |
362 | } |
363 | |
364 | $url = Title::newFromText( $title->text() )->getLinkURL( [ 'source' => $source ] ); |
365 | $html .= Html::rawElement( |
366 | 'li', |
367 | [ 'class' => 'mw-parser-output' ], |
368 | Html::element( |
369 | 'a', |
370 | [ |
371 | 'href' => $url, |
372 | 'target' => '_blank', |
373 | 'class' => 'external', |
374 | ], |
375 | $text->text() |
376 | ) |
377 | ); |
378 | } |
379 | $html .= '</ul>'; |
380 | return $html; |
381 | } |
382 | |
383 | private function buildSidebar() { |
384 | return Html::rawElement( |
385 | 'div', |
386 | [ 'class' => 'welcomesurvey-sidebar' ], |
387 | Html::rawElement( |
388 | 'div', |
389 | [ 'class' => 'welcomesurvey-sidebar-section' ], |
390 | Html::element( |
391 | 'div', |
392 | [ 'class' => 'welcomesurvey-sidebar-section-title' ], |
393 | $this->msg( 'welcomesurvey-sidebar-editing-title' )->text() |
394 | ) . |
395 | Html::rawElement( |
396 | 'div', |
397 | [ 'class' => 'welcomesurvey-sidebar-section-text' ], |
398 | $this->msg( 'welcomesurvey-sidebar-editing-text' ) |
399 | ->params( $this->getUser()->getName() ) |
400 | ->parseAsBlock() |
401 | ) . |
402 | $this->buildGettingStartedLinks( 'survey' ) |
403 | ) |
404 | ); |
405 | } |
406 | |
407 | /** |
408 | * Load ResourceLoader module dependencies defined by questions. |
409 | * @param array $questions |
410 | */ |
411 | private function loadDependencies( array $questions ) { |
412 | array_walk( $questions, function ( $question ) { |
413 | $this->getOutput()->addModules( $question['dependencies']['modules'] ?? '' ); |
414 | } ); |
415 | } |
416 | |
417 | private function initializeWelcomeSurveyLogger(): void { |
418 | $this->welcomeSurveyLogger->initialize( |
419 | $this->getRequest(), |
420 | $this->getUser(), |
421 | Util::isMobile( $this->getSkin() ) |
422 | ); |
423 | } |
424 | |
425 | } |