Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
65.38% covered (warning)
65.38%
51 / 78
50.00% covered (danger)
50.00%
4 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Hooks
65.38% covered (warning)
65.38%
51 / 78
50.00% covered (danger)
50.00%
4 / 8
39.29
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 onBeforeInitialize
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 onSkinAfterContent
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 generateElements
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
1 / 1
2
 getMoreLink
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
6
 onBeforePageDisplay
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 onResourceLoaderGetConfigVars
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 onGetPreferences
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace CookieWarning;
4
5use Config;
6use ConfigException;
7use MediaWiki;
8use MediaWiki\Hook\BeforeInitializeHook;
9use MediaWiki\Hook\BeforePageDisplayHook;
10use MediaWiki\Hook\SkinAfterContentHook;
11use MediaWiki\Html\Html;
12use MediaWiki\Preferences\Hook\GetPreferencesHook;
13use MediaWiki\ResourceLoader\Hook\ResourceLoaderGetConfigVarsHook;
14use MediaWiki\Title\Title;
15use MediaWiki\User\Options\UserOptionsManager;
16use MWException;
17use OOUI\ButtonInputWidget;
18use OOUI\ButtonWidget;
19use OOUI\HorizontalLayout;
20use OutputPage;
21use Skin;
22use User;
23use WebRequest;
24
25class Hooks implements
26    SkinAfterContentHook,
27    GetPreferencesHook,
28    BeforeInitializeHook,
29    BeforePageDisplayHook,
30    ResourceLoaderGetConfigVarsHook
31{
32    private Config $config;
33    private Decisions $decisions;
34    private UserOptionsManager $userOptionsManager;
35
36    public function __construct(
37        Config $config,
38        Decisions $decisions,
39        UserOptionsManager $userOptionsManager
40    ) {
41        $this->config = $config;
42        $this->decisions = $decisions;
43        $this->userOptionsManager = $userOptionsManager;
44    }
45
46    /**
47     * BeforeInitialize hook handler.
48     *
49     * If the disablecookiewarning POST data is send, disables the cookiewarning bar with a
50     * cookie or a user preference, if the user is logged in.
51     *
52     * @param Title $title
53     * @param null $unused
54     * @param OutputPage $output
55     * @param User $user
56     * @param WebRequest $request
57     * @param MediaWiki $mediawiki
58     * @throws MWException
59     */
60    public function onBeforeInitialize( $title, $unused, $output, $user, $request, $mediawiki ) {
61        if ( !$request->wasPosted() || !$request->getVal( 'disablecookiewarning' ) ) {
62            return;
63        }
64
65        if ( $user->isRegistered() ) {
66            $this->userOptionsManager->setOption( $user, 'cookiewarning_dismissed', 1 );
67            $this->userOptionsManager->saveOptions( $user );
68        } else {
69            $request->response()->setCookie( 'cookiewarning_dismissed', true );
70        }
71        $output->redirect( $request->getRequestURL() );
72    }
73
74    /**
75     * SkinAfterContent hook handler.
76     *
77     * Adds the CookieWarning information bar to the output html.
78     *
79     * @param string &$data
80     * @param Skin $skin
81     *
82     * @throws MWException
83     */
84    public function onSkinAfterContent( &$data, $skin ) {
85        if ( !$this->decisions->shouldShowCookieWarning( $skin->getContext() ) ) {
86            return;
87        }
88
89        $data .= $this->generateElements( $skin );
90    }
91
92    /**
93     * Generates the elements for the banner.
94     *
95     * @param Skin $skin
96     * @return string|null The html for cookie notice.
97     */
98    private function generateElements( Skin $skin ): ?string {
99        $moreLink = $this->getMoreLink();
100
101        $buttons = [];
102        if ( $moreLink ) {
103            $buttons[] = new ButtonWidget( [
104                'href' => $moreLink,
105                'label' => $skin->msg( 'cookiewarning-moreinfo-label' )->text(),
106                'flags' => [ 'progressive' ]
107            ] );
108        }
109        $buttons[] = new ButtonInputWidget( [
110            'type' => 'submit',
111            'label' => $skin->msg( 'cookiewarning-ok-label' )->text(),
112            'name' => 'disablecookiewarning',
113            'value' => 'OK',
114            'flags' => [ 'primary', 'progressive' ]
115        ] );
116
117        $form = Html::rawElement(
118            'form',
119            [ 'method' => 'POST' ],
120            new HorizontalLayout( [ 'items' => $buttons ] )
121        );
122
123        return Html::openElement(
124                'div',
125                [ 'class' => 'mw-cookiewarning-container' ]
126            ) .
127            Html::openElement(
128                'div',
129                [ 'class' => 'mw-cookiewarning-text' ]
130            ) .
131            Html::element(
132                'span',
133                [],
134                $skin->msg( 'cookiewarning-info' )->text()
135            ) .
136            Html::closeElement( 'div' ) .
137            $form .
138            Html::closeElement( 'div' );
139    }
140
141    /**
142     * Returns the target for the "More information" link of the cookie warning bar, if one is set.
143     * The link can be set by either (checked in this order):
144     *  - the configuration variable $wgCookieWarningMoreUrl
145     *  - the interface message MediaWiki:Cookiewarning-more-link
146     *  - the interface message MediaWiki:Cookie-policy-link (bc T145781)
147     *
148     * @return string|null The url or null if none set
149     * @throws ConfigException
150     */
151    private function getMoreLink(): ?string {
152        if ( $this->config->get( 'CookieWarningMoreUrl' ) ) {
153            return $this->config->get( 'CookieWarningMoreUrl' );
154        }
155
156        $cookieWarningMessage = wfMessage( 'cookiewarning-more-link' );
157        if ( $cookieWarningMessage->exists() && !$cookieWarningMessage->isDisabled() ) {
158            return $cookieWarningMessage->text();
159        }
160
161        $cookiePolicyMessage = wfMessage( 'cookie-policy-link' );
162        if ( $cookiePolicyMessage->exists() && !$cookiePolicyMessage->isDisabled() ) {
163            return $cookiePolicyMessage->text();
164        }
165
166        return null;
167    }
168
169    /**
170     * BeforePageDisplay hook handler.
171     *
172     * Adds the required style and JS module, if cookiewarning is enabled.
173     *
174     * @param OutputPage $out
175     * @param Skin $skin
176     * @throws ConfigException
177     * @throws MWException
178     */
179    public function onBeforePageDisplay( $out, $skin ): void {
180        if ( !$this->decisions->shouldShowCookieWarning( $out->getContext() ) ) {
181            return;
182        }
183
184        $modules = [ 'ext.CookieWarning' ];
185        $moduleStyles = [ 'ext.CookieWarning.styles' ];
186
187        if ( $this->decisions->shouldAddResourceLoaderComponents() ) {
188            $modules[] = 'ext.CookieWarning.geolocation';
189            $moduleStyles[] = 'ext.CookieWarning.geolocation.styles';
190        }
191        $out->addModules( $modules );
192        $out->addModuleStyles( $moduleStyles );
193        $out->enableOOUI();
194    }
195
196    /**
197     * ResourceLoaderGetConfigVars hook handler.
198     *
199     * @param array &$vars
200     * @param string $skin
201     * @param Config $config
202     *
203     * @throws ConfigException
204     */
205    public function onResourceLoaderGetConfigVars( array &$vars, $skin, Config $config ): void {
206        if ( $this->decisions->shouldAddResourceLoaderComponents() ) {
207            $vars += [
208                'wgCookieWarningGeoIPServiceURL' => $this->config->get( 'CookieWarningGeoIPServiceURL' ),
209                'wgCookieWarningForCountryCodes' => $this->config->get( 'CookieWarningForCountryCodes' ),
210            ];
211        }
212    }
213
214    /**
215     * GetPreferences hook handler
216     *
217     * @see https://www.mediawiki.org/wiki/Manual:Hooks/GetPreferences
218     *
219     * @param User $user
220     * @param array &$defaultPreferences
221     * @return bool
222     */
223    public function onGetPreferences( $user, &$defaultPreferences ): bool {
224        $defaultPreferences['cookiewarning_dismissed'] = [
225            'type' => 'api',
226            'default' => '0',
227        ];
228        return true;
229    }
230}