Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 54
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialHideBanners
0.00% covered (danger)
0.00%
0 / 54
0.00% covered (danger)
0.00%
0 / 4
132
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
42
 setHideCookie
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
6
 setP3P
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3use MediaWiki\Extension\CentralAuth\User\CentralAuthUser;
4
5/**
6 * Unlisted Special Page which sets a cookie for hiding banners across all languages of a project.
7 * This is typically used on donation thank-you pages so that users who have donated will no longer
8 * see fundrasing banners.
9 */
10class SpecialHideBanners extends UnlistedSpecialPage {
11    // Cache this blank response for a day or so (60 * 60 * 24 s.)
12    private const CACHE_EXPIRY = 86400;
13    private const P3P_SUBPAGE = 'P3P';
14
15    public function __construct() {
16        parent::__construct( 'HideBanners' );
17    }
18
19    public function execute( $par ) {
20        global $wgNoticeCookieDurations, $wgCentralNoticeHideBannersP3P,
21            $wgCentralNoticeFallbackHideCookieDuration;
22
23        // Handle /P3P subpage with explanation of invalid P3P header
24        if ( ( strval( $par ) === self::P3P_SUBPAGE ) &&
25            !$wgCentralNoticeHideBannersP3P
26        ) {
27            $this->setHeaders();
28            $this->getOutput()->addWikiMsg( 'centralnotice-specialhidebanners-p3p' );
29            return;
30        }
31
32        $reason = $this->getRequest()->getText( 'reason', 'donate' );
33
34        // No duration parameter for a custom reason is not expected; we have a
35        // fallback value, but we log that this happened.
36        $duration = $this->getRequest()->getInt( 'duration', 0 );
37        if ( !$duration ) {
38            if ( isset( $wgNoticeCookieDurations[$reason] ) ) {
39                $duration = $wgNoticeCookieDurations[$reason];
40            } else {
41                $duration = $wgCentralNoticeFallbackHideCookieDuration;
42                wfLogWarning( 'Missing or 0 duration for hide cookie reason '
43                    . $reason . '.' );
44            }
45        }
46
47        $category = $this->getRequest()->getText( 'category', 'fundraising' );
48        $category = Banner::sanitizeRenderedCategory( $category );
49        $this->setHideCookie( $category, $duration, $reason );
50        $this->setP3P();
51
52        $this->getOutput()->disable();
53        wfResetOutputBuffers();
54
55        header( 'Content-Type: image/png' );
56        if ( !$this->getUser()->isRegistered() ) {
57            $expiry = self::CACHE_EXPIRY;
58            header( "Cache-Control: public, s-maxage={$expiry}, max-age=0" );
59        }
60    }
61
62    /**
63     * Set the cookie for hiding fundraising banners.
64     * @param string $category
65     * @param int $duration
66     * @param string $reason
67     */
68    private function setHideCookie( $category, $duration, $reason ) {
69        global $wgNoticeCookieDomain;
70
71        $created = time();
72        $exp = $created + $duration;
73        $value = [
74            'v' => 1,
75            'created' => $created,
76            'reason' => $reason
77        ];
78
79        if ( ExtensionRegistry::getInstance()->isLoaded( 'CentralAuth' ) ) {
80            $cookieDomain = CentralAuthUser::getCookieDomain();
81        } else {
82            $cookieDomain = $wgNoticeCookieDomain;
83        }
84        setcookie(
85            "centralnotice_hide_{$category}",
86            json_encode( $value ),
87            $exp,
88            '/',
89            $cookieDomain,
90            false,
91            false
92        );
93    }
94
95    /**
96     * Set an invalid P3P policy header to make IE accept third-party hide cookies.
97     */
98    private function setP3P() {
99        global $wgCentralNoticeHideBannersP3P;
100
101        if ( !$wgCentralNoticeHideBannersP3P ) {
102            $url = SpecialPage::getTitleFor(
103                'HideBanners', self::P3P_SUBPAGE )
104                ->getCanonicalURL();
105
106            $p3p = "CP=\"This is not a P3P policy! See $url for more info.\"";
107
108        } else {
109            $p3p = $wgCentralNoticeHideBannersP3P;
110        }
111
112        header( "P3P: $p3p", true );
113    }
114}