Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 38 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
WelcomeSurveyLogger | |
0.00% |
0 / 38 |
|
0.00% |
0 / 4 |
132 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
initialize | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
logInteraction | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
72 | |||
getLoggedActions | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace GrowthExperiments\EventLogging; |
4 | |
5 | use GrowthExperiments\Specials\SpecialWelcomeSurvey; |
6 | use MediaWiki\Extension\EventLogging\EventLogging; |
7 | use MediaWiki\Registration\ExtensionRegistry; |
8 | use MediaWiki\Request\WebRequest; |
9 | use MediaWiki\User\User; |
10 | use Psr\Log\LoggerInterface; |
11 | |
12 | class WelcomeSurveyLogger { |
13 | |
14 | public const SCHEMA = '/analytics/mediawiki/welcomesurvey/interaction/1.0.1'; |
15 | private const STREAM_NAME = 'mediawiki.welcomesurvey.interaction'; |
16 | public const INTERACTION_PHASE_COOKIE = 'growth.welcomesurvey.phase'; |
17 | public const WELCOME_SURVEY_TOKEN = 'growth.welcomesurvey.token'; |
18 | public const WELCOME_SURVEY_LOGGED_OUT = 'logged_out'; |
19 | |
20 | /** @var LoggerInterface */ |
21 | private $logger; |
22 | /** @var WebRequest */ |
23 | private $webRequest; |
24 | /** @var bool */ |
25 | private $isMobile; |
26 | /** @var User */ |
27 | private $user; |
28 | /** @var array */ |
29 | private $loggedActions = []; |
30 | |
31 | /** |
32 | * @param LoggerInterface $logger |
33 | */ |
34 | public function __construct( LoggerInterface $logger ) { |
35 | $this->logger = $logger; |
36 | } |
37 | |
38 | /** |
39 | * @param WebRequest $webRequest |
40 | * @param User $user |
41 | * @param bool $isMobile |
42 | */ |
43 | public function initialize( WebRequest $webRequest, User $user, bool $isMobile ): void { |
44 | $this->webRequest = $webRequest; |
45 | $this->user = $user; |
46 | $this->isMobile = $isMobile; |
47 | } |
48 | |
49 | /** |
50 | * Log user interactions with the WelcomeSurvey form. |
51 | * |
52 | * @param string $action |
53 | */ |
54 | public function logInteraction( string $action ): void { |
55 | $event = [ |
56 | '$schema' => self::SCHEMA, |
57 | 'action' => $action, |
58 | 'is_mobile' => $this->isMobile, |
59 | 'was_posted' => $this->webRequest->wasPosted(), |
60 | 'user_id' => $this->user->getId(), |
61 | 'token' => $this->webRequest->getVal( '_welcomesurveytoken' ), |
62 | 'returnto_param_is_present' => ( $this->webRequest->getVal( '_returnto' ) ?? |
63 | $this->webRequest->getVal( 'returnto' ) ) !== null, |
64 | ]; |
65 | // Used for integration testing, see SpecialWelcomeSurveyTest.php. |
66 | $this->loggedActions[] = $event; |
67 | // Used to keep track of whether the user is logged out when submitting the form. See |
68 | // WelcomeSurveyHooks::onSpecialPageBeforeExecute |
69 | $this->webRequest->response()->setCookie( self::INTERACTION_PHASE_COOKIE, $action ); |
70 | // Except that if the user reached handleResponses() and is logged in, we'll assume submission worked, |
71 | // and we can delete the cookie. |
72 | if ( $this->user->isNamed() && $action === SpecialWelcomeSurvey::ACTION_SUBMIT_SUCCESS ) { |
73 | $this->webRequest->response()->clearCookie( self::INTERACTION_PHASE_COOKIE ); |
74 | } |
75 | |
76 | // Suspicious events: |
77 | // * no token in the event |
78 | // * user is logged out |
79 | // * the stored token cookie doesn't match what we got from the web request |
80 | if ( !$event['token'] || |
81 | $event['user_id'] === 0 || |
82 | $this->webRequest->getCookie( self::WELCOME_SURVEY_TOKEN ) !== $event['token'] || |
83 | $action === self::WELCOME_SURVEY_LOGGED_OUT |
84 | ) { |
85 | $this->logger->debug( 'Suspicious {schema} event for action {action}', [ |
86 | 'schema' => self::SCHEMA, |
87 | 'action' => $action, |
88 | 'user_id' => $event['user_id'], |
89 | 'was_posted' => $event['was_posted'], |
90 | 'token' => $event['token'], |
91 | 'token_from_cookie' => $this->webRequest->getCookie( self::WELCOME_SURVEY_TOKEN ), |
92 | 'is_mobile' => $event['is_mobile'], |
93 | 'returnto_param_is_present' => $event['returnto_param_is_present'], |
94 | 'interaction_phase_from_cookie' => $this->webRequest->getCookie( self::INTERACTION_PHASE_COOKIE ), |
95 | ] ); |
96 | } |
97 | if ( !ExtensionRegistry::getInstance()->isLoaded( 'EventLogging' ) ) { |
98 | return; |
99 | } |
100 | |
101 | // Make sure that the token is a string value for event logging validation. |
102 | $event['token'] ??= $this->webRequest->getCookie( self::WELCOME_SURVEY_TOKEN, null, '' ); |
103 | |
104 | EventLogging::submit( self::STREAM_NAME, $event ); |
105 | } |
106 | |
107 | /** |
108 | * @internal Used for integration testing. |
109 | * @return array |
110 | */ |
111 | public function getLoggedActions(): array { |
112 | return $this->loggedActions; |
113 | } |
114 | |
115 | } |