mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/CookieWarning
synced 2024-11-27 15:40:16 +00:00
Add option to limit the cookie warning to specific regions
With this change, CookieWarning gets two new ways to limit the warning bar to users of specific (configurable) regions. Based on the geo location service (which can be configured, too, defaults to freegeoip.net), the extension will try to get the region of the user (based on the IP address) and checks it against the configured regions. With the configuration variable $wgCookieWarningGeoIPLookup, the wiki sysadmin can configure, if the lookup should be done with PHP (the timeout for the request is hardcoded to 2 sedonds max), which will save the paylod sent to the user, or with JavaScript, which will be asynchronous. Deactivating this feature is easily done by simply setting the configured regions to false, instead of an object. Bug: T145780 Change-Id: I4d9371b8608ef270c11c42bdb3a773675977ab3a
This commit is contained in:
parent
c0f1942eca
commit
c35b78fae8
|
@ -2,7 +2,9 @@
|
|||
"name": "CookieWarning",
|
||||
"version": "0.1.0",
|
||||
"author": [
|
||||
"Florian Schmidt"
|
||||
"Florian Schmidt",
|
||||
"Liz Lee",
|
||||
"Jack Phoenix"
|
||||
],
|
||||
"url": "https://www.droidwiki.de",
|
||||
"descriptionmsg": "cookiewarning-desc",
|
||||
|
@ -17,11 +19,63 @@
|
|||
"SkinTemplateOutputPageBeforeExec": "CookieWarningHooks::onSkinTemplateOutputPageBeforeExec",
|
||||
"BeforePageDisplay": "CookieWarningHooks::onBeforePageDisplay",
|
||||
"GetPreferences": "CookieWarningHooks::onGetPreferences",
|
||||
"BeforeInitialize": "CookieWarningHooks::onBeforeInitialize"
|
||||
"BeforeInitialize": "CookieWarningHooks::onBeforeInitialize",
|
||||
"ResourceLoaderGetConfigVars": "CookieWarningHooks::onResourceLoaderGetConfigVars"
|
||||
},
|
||||
"config": {
|
||||
"CookieWarningEnabled": false,
|
||||
"CookieWarningMoreUrl": ""
|
||||
"CookieWarningMoreUrl": "",
|
||||
"CookieWarningGeoIPServiceURL": "//freegeoip.net/json/",
|
||||
"CookieWarningGeoIPLookup": "none",
|
||||
"CookieWarningForCountryCodes": {
|
||||
"EU": "Europe",
|
||||
"AD": "Andorra",
|
||||
"AL": "Albania",
|
||||
"AT": "Austria",
|
||||
"BA": "Bosnia and Herzegovina",
|
||||
"BE": "Belgium",
|
||||
"BG": "Bulgaria",
|
||||
"BY": "Belarus",
|
||||
"CH": "Switzerland",
|
||||
"CS": "Serbia and Montenegro",
|
||||
"CZ": "Czech Republic",
|
||||
"DE": "Germany",
|
||||
"DK": "Denmark",
|
||||
"EE": "Estonia",
|
||||
"ES": "Spain",
|
||||
"FI": "Finland",
|
||||
"FO": "Faroe Islands",
|
||||
"FR": "France",
|
||||
"FX": "France, Metropolitan",
|
||||
"GB": "United Kingdom",
|
||||
"GI": "Gibraltar",
|
||||
"GR": "Greece",
|
||||
"HR": "Croatia",
|
||||
"HU": "Hungary",
|
||||
"IE": "Ireland",
|
||||
"IS": "Iceland",
|
||||
"IT": "Italy",
|
||||
"LI": "Liechtenstein",
|
||||
"LT": "Lithuania",
|
||||
"LU": "Luxembourg",
|
||||
"LV": "Latvia",
|
||||
"MC": "Monaco",
|
||||
"MD": "Moldova, Republic of",
|
||||
"MK": "Macedonia",
|
||||
"MT": "Malta",
|
||||
"NL": "Netherlands",
|
||||
"NO": "Norway",
|
||||
"PL": "Poland",
|
||||
"PT": "Portugal",
|
||||
"RO": "Romania",
|
||||
"SE": "Sweden",
|
||||
"SI": "Slovenia",
|
||||
"SJ": "Svalbard and Jan Mayen",
|
||||
"SK": "Slovakia",
|
||||
"SM": "San Marino",
|
||||
"UA": "Ukraine",
|
||||
"VA": "Holy See (Vatican City State)"
|
||||
}
|
||||
},
|
||||
"ResourceModules": {
|
||||
"ext.CookieWarning": {
|
||||
|
@ -42,6 +96,22 @@
|
|||
"mobile",
|
||||
"desktop"
|
||||
]
|
||||
},
|
||||
"ext.CookieWarning.geolocation": {
|
||||
"scripts": "resources/ext.CookieWarning.geolocation/cookiePolicy.js",
|
||||
"dependencies": "ext.CookieWarning",
|
||||
"targets": [
|
||||
"mobile",
|
||||
"desktop"
|
||||
]
|
||||
},
|
||||
"ext.CookieWarning.geolocation.styles": {
|
||||
"position": "top",
|
||||
"styles": "resources/ext.CookieWarning.geolocation/styles.css",
|
||||
"targets": [
|
||||
"mobile",
|
||||
"desktop"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ResourceFileModulePaths": {
|
||||
|
@ -49,7 +119,8 @@
|
|||
"remoteExtPath": "CookieWarning"
|
||||
},
|
||||
"AutoloadClasses": {
|
||||
"CookieWarningHooks": "includes/CookieWarning.hooks.php"
|
||||
"CookieWarningHooks": "includes/CookieWarning.hooks.php",
|
||||
"GeoLocation": "includes/GeoLocation.php"
|
||||
},
|
||||
"ConfigRegistry": {
|
||||
"cookiewarning": "GlobalVarConfig::newInstance"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
class CookieWarningHooks {
|
||||
private static $inConfiguredRegion;
|
||||
|
||||
/**
|
||||
* BeforeInitialize hook handler.
|
||||
|
@ -30,7 +31,6 @@ class CookieWarningHooks {
|
|||
}
|
||||
$output->redirect( $request->getRequestURL() );
|
||||
}
|
||||
|
||||
/**
|
||||
* SkinTemplateOutputPageBeforeExec hook handler.
|
||||
*
|
||||
|
@ -122,9 +122,46 @@ class CookieWarningHooks {
|
|||
*/
|
||||
public static function onBeforePageDisplay( OutputPage $out ) {
|
||||
if ( self::showWarning( $out->getContext() ) ) {
|
||||
$out->addModuleStyles( [ 'ext.CookieWarning.styles' ] );
|
||||
$out->addModules( [ 'ext.CookieWarning' ] );
|
||||
$conf = self::getConfig();
|
||||
$moduleStyles = [ 'ext.CookieWarning.styles' ];
|
||||
$modules = [ 'ext.CookieWarning' ];
|
||||
if (
|
||||
$conf->get( 'CookieWarningGeoIPLookup' ) === 'js' &&
|
||||
is_array( $conf->get( 'CookieWarningForCountryCodes' ) )
|
||||
) {
|
||||
$modules[] = 'ext.CookieWarning.geolocation';
|
||||
$moduleStyles[] = 'ext.CookieWarning.geolocation.styles';
|
||||
}
|
||||
$out->addModules( $modules );
|
||||
$out->addModuleStyles( $moduleStyles );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ResourceLoaderGetConfigVars hook handler.
|
||||
*
|
||||
* @param array $vars
|
||||
*/
|
||||
public static function onResourceLoaderGetConfigVars( array &$vars ) {
|
||||
$conf = self::getConfig();
|
||||
if (
|
||||
$conf->get( 'CookieWarningGeoIPLookup' ) === 'js' &&
|
||||
is_array( $conf->get( 'CookieWarningForCountryCodes' ) )
|
||||
) {
|
||||
$vars += [
|
||||
'wgCookieWarningGeoIPServiceURL' => $conf->get( 'CookieWarningGeoIPServiceURL' ),
|
||||
'wgCookieWarningForCountryCodes' => $conf->get( 'CookieWarningForCountryCodes' ),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retruns the Config object for the CookieWarning extension.
|
||||
*
|
||||
* @return Config
|
||||
*/
|
||||
private static function getConfig() {
|
||||
return ConfigFactory::getDefaultInstance()->makeConfig( 'cookiewarning' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,20 +173,63 @@ class CookieWarningHooks {
|
|||
*/
|
||||
private static function showWarning( IContextSource $context ) {
|
||||
$user = $context->getUser();
|
||||
$conf = ConfigFactory::getDefaultInstance()->makeConfig( 'cookiewarning' );
|
||||
$conf = self::getConfig();
|
||||
if (
|
||||
// if enabled in LocalSettings.php
|
||||
$conf->get( 'CookieWarningEnabled' ) &&
|
||||
// if not already dismissed by this user (and saved in the user prefs)
|
||||
!$user->getBoolOption( 'cookiewarning_dismissed', false ) &&
|
||||
// if not already dismissed by this user (and saved in the browser cookies)
|
||||
!$context->getRequest()->getCookie( 'cookiewarning_dismissed' )
|
||||
!$context->getRequest()->getCookie( 'cookiewarning_dismissed' ) &&
|
||||
(
|
||||
$conf->get( 'CookieWarningGeoIPLookup' ) === 'js' ||
|
||||
self::inConfiguredRegion( $context, $conf )
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks, if the user is in one of the configured regions.
|
||||
*
|
||||
* @TODO: This function or the function users should set the cookie or user option, if this
|
||||
* function returns false to avoid a location lookup on each request.
|
||||
* @param IContextSource $context
|
||||
* @param Config $conf
|
||||
* @return bool
|
||||
*/
|
||||
private static function inConfiguredRegion( IContextSource $context, Config $conf ) {
|
||||
if ( self::$inConfiguredRegion === null ) {
|
||||
if (
|
||||
!$conf->get( 'CookieWarningForCountryCodes' ) ||
|
||||
$conf->get( 'CookieWarningGeoIPLookup' ) === 'none'
|
||||
) {
|
||||
wfDebugLog( 'CookieWarning', 'IP geolocation not configured, skipping.' );
|
||||
self::$inConfiguredRegion = true;
|
||||
} else {
|
||||
wfDebugLog( 'CookieWarning', 'Try to locate the user\'s IP address.' );
|
||||
$geoLocation = new GeoLocation;
|
||||
$located = $geoLocation
|
||||
->setConfig( $conf )
|
||||
->setIP( $context->getRequest()->getIP() )
|
||||
->locate();
|
||||
if ( !$located ) {
|
||||
wfDebugLog( 'CookieWarning', 'Locating the user\'s IP address failed or is' .
|
||||
' configured false.' );
|
||||
self::$inConfiguredRegion = true;
|
||||
} else {
|
||||
wfDebugLog( 'CookieWarning', 'Locating the user was successful, located' .
|
||||
' region: ' . $geoLocation->getCountryCode() );
|
||||
self::$inConfiguredRegion = array_key_exists( $geoLocation->getCountryCode(),
|
||||
$conf->get( 'CookieWarningForCountryCodes' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
return self::$inConfiguredRegion;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetPreferences hook handler
|
||||
*
|
||||
|
|
98
includes/GeoLocation.php
Normal file
98
includes/GeoLocation.php
Normal file
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
/**
|
||||
* GeoLocation implementation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements the GeoLocation class, which allows to locate the user based on the IP address.
|
||||
*/
|
||||
class GeoLocation {
|
||||
private $ip;
|
||||
private $config;
|
||||
private $countryCode;
|
||||
|
||||
/**
|
||||
* Set the IP address you want to locate.
|
||||
*
|
||||
* @param string $ip A valid IP address
|
||||
* @return $this
|
||||
*/
|
||||
public function setIP( $ip ) {
|
||||
if ( !IP::isValid( $ip ) ) {
|
||||
throw new InvalidArgumentException( "$ip is not a valid IP address." );
|
||||
}
|
||||
$this->ip = $ip;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IP address.
|
||||
*
|
||||
* @return null|string NULL if the address is not set so far, string otherwise.
|
||||
*/
|
||||
public function getIP() {
|
||||
return $this->ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Config object used by this class.
|
||||
*
|
||||
* @param Config $config
|
||||
* @return $this
|
||||
*/
|
||||
public function setConfig( Config $config ) {
|
||||
$this->config = $config;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the country code, if the last call to self::locate() returned true. Otherwise, NULL.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getCountryCode() {
|
||||
return $this->countryCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to locate the IP address set with self::setIP() using the geolocation service
|
||||
* configured with the $wgCookieWarningGeoIPServiceURL configuration variable. If the config
|
||||
* isn't set, this function returns NULL. If the config is set, but the URL is invalid or an
|
||||
* other problem occures which resulted in a failed locating process, this function returns
|
||||
* false, otherwise it returns true.
|
||||
*
|
||||
* @return bool|null NULL if no geolocation service configured, false on error, true otherwise.
|
||||
*/
|
||||
public function locate() {
|
||||
$this->countryCode = null;
|
||||
if ( $this->ip === null ) {
|
||||
throw new RuntimeException(
|
||||
'No IP address set, locating now would return the servers location.' );
|
||||
}
|
||||
if ( $this->config === null ) {
|
||||
throw new RuntimeException(
|
||||
'You need to set the Config object first, before you can locate an IP address.' );
|
||||
}
|
||||
if ( !$this->config->get( 'CookieWarningGeoIPServiceURL' ) ) {
|
||||
return null;
|
||||
}
|
||||
$requestUrl = $this->config->get( 'CookieWarningGeoIPServiceURL' );
|
||||
if ( substr( $requestUrl, -1 ) !== '/' ) {
|
||||
$requestUrl .= '/';
|
||||
}
|
||||
$json = Http::get( $requestUrl . $this->getIP(), [
|
||||
'timeout' => '2'
|
||||
] );
|
||||
if ( !$json ) {
|
||||
return false;
|
||||
}
|
||||
$returnObject = json_decode( $json );
|
||||
if ( $returnObject === null || !property_exists( $returnObject, 'country_code' ) ) {
|
||||
return false;
|
||||
}
|
||||
$this->countryCode = $returnObject->country_code;
|
||||
return true;
|
||||
}
|
||||
}
|
53
resources/ext.CookieWarning.geolocation/cookiePolicy.js
Normal file
53
resources/ext.CookieWarning.geolocation/cookiePolicy.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
/* global Geo, mediaWiki */
|
||||
( function ( mw, $ ) {
|
||||
'use strict';
|
||||
var geoLocation;
|
||||
|
||||
geoLocation = {
|
||||
/**
|
||||
* @return string Two-letter country code
|
||||
*/
|
||||
getCountryCode: function () {
|
||||
/**
|
||||
* safe fallback -- if geolocation fails, display the notice anyway
|
||||
*/
|
||||
var countryCode = 'EU';
|
||||
|
||||
if ( !$.cookie( 'euCookieWarningCountryCode' ) ) {
|
||||
// @see http://www.dwuser.com/education/content/web-services-made-practical-where-are-your-visitors-from/
|
||||
$.get( mw.config.get( 'wgCookieWarningGeoIPServiceURL' ), function ( data ) {
|
||||
// Get the country code
|
||||
countryCode = data.country_code;
|
||||
// Store the result in a cookie (ah, the sweet, sweet irony) to
|
||||
// avoid hitting the geolocation service unnecessarily
|
||||
$.cookie( 'euCookieWarningCountryCode', countryCode, {
|
||||
domain: window.mw.config.get( 'wgCookieDomain' ),
|
||||
path: '/',
|
||||
expires: 30
|
||||
} );
|
||||
}, 'jsonp' );
|
||||
} else if ( $.cookie( 'euCookieWarningCountryCode' ) !== null ) {
|
||||
countryCode = $.cookie( 'euCookieWarningCountryCode' );
|
||||
}
|
||||
|
||||
return countryCode;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the supplied country code is that of a configured region.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isInRegion: function () {
|
||||
return mw.config.get( 'wgCookieWarningForCountryCodes' ).hasOwnProperty( this.getCountryCode() );
|
||||
}
|
||||
};
|
||||
|
||||
$( function () {
|
||||
if ( geoLocation.isInRegion() ) {
|
||||
$( '.mw-cookiewarning-container' ).show();
|
||||
} else {
|
||||
$( '.mw-cookiewarning-container' ).detach();
|
||||
}
|
||||
} );
|
||||
}( mediaWiki, jQuery ) );
|
3
resources/ext.CookieWarning.geolocation/styles.css
Normal file
3
resources/ext.CookieWarning.geolocation/styles.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
.mw-cookiewarning-container {
|
||||
display: none;
|
||||
}
|
|
@ -17,6 +17,7 @@ class CookieWarningHooksTest extends MediaWikiLangTestCase {
|
|||
$this->setMwGlobals( [
|
||||
'wgCookieWarningEnabled' => $enabled,
|
||||
'wgCookieWarningMoreUrl' => $morelinkConfig,
|
||||
'wgCookieWarningForCountryCodes' => false,
|
||||
] );
|
||||
if ( $morelinkCookieWarningMsg ) {
|
||||
$title = Title::newFromText( 'cookiewarning-more-link', NS_MEDIAWIKI );
|
||||
|
@ -115,6 +116,64 @@ class CookieWarningHooksTest extends MediaWikiLangTestCase {
|
|||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerOnSkinTemplateOutputPageBeforeExecGeoLocation
|
||||
*/
|
||||
public function testOnSkinTemplateOutputPageBeforeExecGeoLocation( $ipAddress, $countryCodes,
|
||||
$expected
|
||||
) {
|
||||
$this->resetCookieWarningHooks();
|
||||
$this->setMwGlobals( [
|
||||
'wgCookieWarningEnabled' => true,
|
||||
'wgCookieWarningGeoIPLookup' => is_array( $countryCodes ) ? 'php' : 'none',
|
||||
'wgCookieWarningForCountryCodes' => $countryCodes,
|
||||
] );
|
||||
|
||||
$request = new FauxRequest();
|
||||
$request->setIP( $ipAddress );
|
||||
$context = new DerivativeContext( RequestContext::getMain() );
|
||||
$context->setRequest( $request );
|
||||
$sk = new SkinTemplate();
|
||||
$sk->setContext( $context );
|
||||
$tpl = new CookieWarningTestTemplate();
|
||||
CookieWarningHooks::onSkinTemplateOutputPageBeforeExec( $sk, $tpl );
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
isset( $tpl->data['headelement'] ) && (bool)$tpl->data['headelement']
|
||||
);
|
||||
}
|
||||
|
||||
public function providerOnSkinTemplateOutputPageBeforeExecGeoLocation() {
|
||||
return [
|
||||
[
|
||||
'8.8.8.8',
|
||||
[ 'US' => 'United States of America' ],
|
||||
true,
|
||||
],
|
||||
[
|
||||
'8.8.8.8',
|
||||
[ 'EU' => 'European Union' ],
|
||||
false,
|
||||
],
|
||||
[
|
||||
'8.8.8.8',
|
||||
false,
|
||||
true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function resetCookieWarningHooks() {
|
||||
// reset the inConfiguredRegion value to retrigger a location lookup, if called again
|
||||
$singleton = CookieWarningHooks::class;
|
||||
$reflection = new ReflectionClass( $singleton );
|
||||
$instance = $reflection->getProperty( 'inConfiguredRegion' );
|
||||
$instance->setAccessible( true );
|
||||
$instance->setValue( null, null );
|
||||
$instance->setAccessible( false );
|
||||
}
|
||||
}
|
||||
|
||||
class CookieWarningTestTemplate extends BaseTemplate {
|
||||
|
|
Loading…
Reference in a new issue