Move the warning outside sitenotice, tidy up code

There's a lot of things going on here, so a quick summary:
* Moved the warning outside sitenotice to avoid reflows and all
  kinds of other issues, including those with dismissable notices.
* Placed the warning on the bottom of the page, on all skins, for
  consistency and to avoid obstructing vital UI elements such as
  site name, user tools, search box.
* Changed the UI to use OOUI for, again, consistency and to avoid
  reinventing the wheel with button styling.
* Removed the cookie emoji as it was only taking up valuable space
  on mobile and making designing a sensible layout much harder.
* Merged the mobile and desktop RL modules into a single, responsive
  one. The warning will work correctly for any skin and screen width.
* The integration tests are truly horrible, I tried to at least
  make them work. They deserve a proper rewrite, but that should be
  done after the extension switches to the new hook system.

I will post screenshots of this in action in the first of the linked
tasks.

Bug: T271047
Bug: T173335
Change-Id: I6e2a3d7aeccc0d4df1b3238e52c67e72099d27d8
This commit is contained in:
Ostrzyciel 2021-02-01 10:54:49 +01:00
parent 1f5075224e
commit edd0a7d949
8 changed files with 127 additions and 218 deletions

View file

@ -1,6 +1,3 @@
{ {
"extends": "stylelint-config-wikimedia", "extends": "stylelint-config-wikimedia"
"rules": {
"rule-empty-line-before": null
}
} }

View file

@ -1,10 +1,11 @@
{ {
"name": "CookieWarning", "name": "CookieWarning",
"version": "0.2.0", "version": "0.3.0",
"author": [ "author": [
"Florian Schmidt", "Florian Schmidt",
"Liz Lee", "Liz Lee",
"Jack Phoenix" "Jack Phoenix",
"Ostrzyciel"
], ],
"url": "https://www.mediawiki.org/wiki/Extension:CookieWarning", "url": "https://www.mediawiki.org/wiki/Extension:CookieWarning",
"descriptionmsg": "cookiewarning-desc", "descriptionmsg": "cookiewarning-desc",
@ -19,10 +20,10 @@
] ]
}, },
"Hooks": { "Hooks": {
"SiteNoticeAfter": "CookieWarning\\Hooks::onSiteNoticeAfter", "SkinAfterContent": "CookieWarning\\Hooks::onSkinAfterContent",
"BeforePageDisplay": "CookieWarning\\Hooks::onBeforePageDisplay",
"GetPreferences": "CookieWarning\\Hooks::onGetPreferences", "GetPreferences": "CookieWarning\\Hooks::onGetPreferences",
"BeforeInitialize": "CookieWarning\\Hooks::onBeforeInitialize", "BeforeInitialize": "CookieWarning\\Hooks::onBeforeInitialize",
"BeforePageDisplay": "CookieWarning\\Hooks::onBeforePageDisplay",
"ResourceLoaderGetConfigVars": "CookieWarning\\Hooks::onResourceLoaderGetConfigVars" "ResourceLoaderGetConfigVars": "CookieWarning\\Hooks::onResourceLoaderGetConfigVars"
}, },
"config": { "config": {
@ -94,15 +95,10 @@
] ]
}, },
"ext.CookieWarning.styles": { "ext.CookieWarning.styles": {
"styles": "resources/ext.CookieWarning/ext.CookieWarning.less" "styles": "resources/ext.CookieWarning/ext.CookieWarning.less",
},
"ext.CookieWarning.mobile.styles": {
"styles": "resources/ext.CookieWarning/ext.CookieWarning.mobile.less",
"skinStyles": {
"minerva": "skinStyles/ext.CookieWarning.minerva.styles.less"
},
"targets": [ "targets": [
"mobile" "mobile",
"desktop"
] ]
}, },
"ext.CookieWarning.geolocation": { "ext.CookieWarning.geolocation": {

View file

@ -4,12 +4,13 @@ namespace CookieWarning;
use Config; use Config;
use ConfigException; use ConfigException;
use ExtensionRegistry;
use Html; use Html;
use MediaWiki; use MediaWiki;
use MediaWiki\MediaWikiServices; use MediaWiki\MediaWikiServices;
use MobileContext;
use MWException; use MWException;
use OOUI\ButtonInputWidget;
use OOUI\ButtonWidget;
use OOUI\HorizontalLayout;
use OutputPage; use OutputPage;
use Skin; use Skin;
use Title; use Title;
@ -38,7 +39,7 @@ class Hooks {
return; return;
} }
if ( $user->isLoggedIn() ) { if ( $user->isRegistered() ) {
$user->setOption( 'cookiewarning_dismissed', 1 ); $user->setOption( 'cookiewarning_dismissed', 1 );
$user->saveSettings(); $user->saveSettings();
} else { } else {
@ -48,20 +49,16 @@ class Hooks {
} }
/** /**
* SiteNoticeAfter hook handler. * SkinAfterContent hook handler.
* *
* Adds the CookieWarning information bar to the output html for mobile. * Adds the CookieWarning information bar to the output html.
* *
* @param string &$siteNotice * @param string &$data
* @param Skin $skin * @param Skin $skin
* @throws ConfigException *
* @throws MWException * @throws MWException
* @return bool|void True or no return value to continue or false to abort
*/ */
public static function onSiteNoticeAfter( public static function onSkinAfterContent( string &$data, Skin $skin ) {
string &$siteNotice,
Skin $skin
) {
/** @var Decisions $cookieWarningDecisions */ /** @var Decisions $cookieWarningDecisions */
$cookieWarningDecisions = MediaWikiServices::getInstance() $cookieWarningDecisions = MediaWikiServices::getInstance()
->getService( 'CookieWarning.Decisions' ); ->getService( 'CookieWarning.Decisions' );
@ -70,64 +67,53 @@ class Hooks {
return; return;
} }
$isMobile = ExtensionRegistry::getInstance()->isLoaded( 'MobileFrontend' ) && $data .= self::generateElements( $skin );
MobileContext::singleton()->shouldDisplayMobileView();
$siteNotice .= self::generateElements( $skin, $isMobile );
} }
/** /**
* Generates the elements for the banner. * Generates the elements for the banner.
* *
* @param Skin $skin * @param Skin $skin
* @param bool $isMobile This will return true if using mobile site.
* @return string|null The html for cookie notice. * @return string|null The html for cookie notice.
*/ */
private static function generateElements( Skin $skin, bool $isMobile ) { private static function generateElements( Skin $skin ) : ?string {
$moreLink = self::getMoreLink(); $moreLink = self::getMoreLink();
$buttons = [];
if ( $moreLink ) { if ( $moreLink ) {
$moreLink = "\u{00A0}" . Html::element( $buttons[] = new ButtonWidget( [
'a', 'href' => $moreLink,
[ 'href' => $moreLink ], 'label' => $skin->msg( 'cookiewarning-moreinfo-label' )->text(),
$skin->msg( 'cookiewarning-moreinfo-label' )->text() 'flags' => [ 'progressive' ]
); ] );
} }
$buttons[] = new ButtonInputWidget( [
$form = Html::openElement( 'form', [ 'method' => 'POST' ] ) . 'type' => 'submit',
Html::submitButton( 'label' => $skin->msg( 'cookiewarning-ok-label' )->text(),
$skin->msg( 'cookiewarning-ok-label' )->text(),
[
'name' => 'disablecookiewarning', 'name' => 'disablecookiewarning',
'class' => 'mw-cookiewarning-dismiss' 'value' => 'OK',
] 'flags' => [ 'primary', 'progressive' ]
) . ] );
Html::closeElement( 'form' );
$cookieImage = Html::element( $form = Html::rawElement(
'div', 'form',
[ 'class' => 'mw-cookiewarning-cimage' ], [ 'method' => 'POST' ],
"\u{1F36A}" new HorizontalLayout( [ 'items' => $buttons ] )
); );
return Html::openElement( return Html::openElement(
'div', 'div',
// banner-container marks this as a banner for Minerva [ 'class' => 'mw-cookiewarning-container' ]
// Note to avoid this class, in future we may want to make use of SiteNotice
// or banner display
[ 'class' => 'mw-cookiewarning-container banner-container' ]
) . ) .
Html::openElement( Html::openElement(
'div', 'div',
[ 'class' => 'mw-cookiewarning-text' ] [ 'class' => 'mw-cookiewarning-text' ]
) . ) .
( $isMobile ? $cookieImage : '' ) .
Html::element( Html::element(
'span', 'span',
[], [],
$skin->msg( 'cookiewarning-info' )->text() $skin->msg( 'cookiewarning-info' )->text()
) . ) .
$moreLink .
Html::closeElement( 'div' ) . Html::closeElement( 'div' ) .
$form . $form .
Html::closeElement( 'div' ); Html::closeElement( 'div' );
@ -143,7 +129,7 @@ class Hooks {
* @return string|null The url or null if none set * @return string|null The url or null if none set
* @throws ConfigException * @throws ConfigException
*/ */
private static function getMoreLink() { private static function getMoreLink() : ?string {
$conf = self::getConfig(); $conf = self::getConfig();
if ( $conf->get( 'CookieWarningMoreUrl' ) ) { if ( $conf->get( 'CookieWarningMoreUrl' ) ) {
return $conf->get( 'CookieWarningMoreUrl' ); return $conf->get( 'CookieWarningMoreUrl' );
@ -180,15 +166,8 @@ class Hooks {
return; return;
} }
if (
ExtensionRegistry::getInstance()->isLoaded( 'MobileFrontend' ) &&
MobileContext::singleton()->shouldDisplayMobileView()
) {
$moduleStyles = [ 'ext.CookieWarning.mobile.styles' ];
} else {
$moduleStyles = [ 'ext.CookieWarning.styles' ];
}
$modules = [ 'ext.CookieWarning' ]; $modules = [ 'ext.CookieWarning' ];
$moduleStyles = [ 'ext.CookieWarning.styles' ];
if ( $cookieWarningDecisions->shouldAddResourceLoaderComponents() ) { if ( $cookieWarningDecisions->shouldAddResourceLoaderComponents() ) {
$modules[] = 'ext.CookieWarning.geolocation'; $modules[] = 'ext.CookieWarning.geolocation';
@ -196,6 +175,7 @@ class Hooks {
} }
$out->addModules( $modules ); $out->addModules( $modules );
$out->addModuleStyles( $moduleStyles ); $out->addModuleStyles( $moduleStyles );
$out->enableOOUI();
} }
/** /**

View file

@ -13,7 +13,7 @@
$( '.mw-cookiewarning-container' ).detach(); $( '.mw-cookiewarning-container' ).detach();
} else { } else {
// Click handler for the "Ok" element in the cookiewarning information bar // Click handler for the "Ok" element in the cookiewarning information bar
$( '.mw-cookiewarning-dismiss' ).on( 'click', function ( ev ) { $( '.mw-cookiewarning-container button' ).on( 'click', function ( ev ) {
// an anonymous user doesn't have preferences, so don't try to save this in // an anonymous user doesn't have preferences, so don't try to save this in
// the user preferences. // the user preferences.
if ( !mw.user.isAnon() ) { if ( !mw.user.isAnon() ) {

View file

@ -1,49 +1,38 @@
@z-index: 1999;
/* stylelint-disable selector-max-id */
#siteNotice {
z-index: @z-index;
}
/* stylelint-enable selector-max-id */
.mw-cookiewarning-container { .mw-cookiewarning-container {
position: fixed; position: fixed;
background-color: rgba( 90, 90, 90, 0.85 ); background-color: rgba( 90, 90, 90, 0.85 );
box-sizing: border-box; box-sizing: border-box;
color: #fff;
font-weight: bold;
font-size: 13px;
padding: 7px 15px; padding: 7px 15px;
top: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
z-index: @z-index; z-index: 1999;
display: flex;
justify-content: center;
a, // Narrow mobile screens
.mw-cookiewarning-dismiss { // 550px is a mostly arbitrary number, though it's used by a few other exts and skins
background-color: #3c3c3c; @media screen and ( max-width: 550px ) {
height: 100%; flex-wrap: wrap;
padding: 3px 10px;
border-radius: 2px; .mw-cookiewarning-text {
border: 0; margin-bottom: 0.7em;
cursor: pointer; }
text-decoration: none !important; /* stylelint-disable-line declaration-no-important */
color: #fff;
margin-right: 0.5em;
white-space: nowrap;
font-weight: bold;
font-size: 13px;
} }
.mw-cookiewarning-text { .mw-cookiewarning-text {
display: inline-block; display: flex;
align-items: center;
span { color: #fff;
margin-right: 0.5em; font-weight: bold;
} font-size: 92%;
} }
form { form {
display: inline-block; flex-shrink: 0;
.oo-ui-widget {
margin: 0 0 0 8px;
}
} }
} }

View file

@ -1,65 +0,0 @@
@import 'mediawiki.ui/variables.less';
@cookieWarningFontSize: 12px;
@contentPaddingTablet: @iconGutterWidth + @iconGutterWidth + @iconSize;
.mw-cookiewarning-container {
background-color: #fff;
padding: 16px;
font-size: @cookieWarningFontSize;
font-weight: 500;
font-style: normal;
font-stretch: normal;
display: flex;
align-items: center;
justify-content: space-between;
.mw-cookiewarning-text {
display: flex;
align-items: center;
.mw-cookiewarning-cimage {
width: 28px;
height: 100%;
font-size: 28px;
margin-right: 15px;
float: left;
text-align: center;
line-height: 100%;
}
}
form {
margin-left: 5px;
/* borrowed from mediawiki/core Login Button, following `ButtonWidget (progressive)` from OOjs UI */
.mw-cookiewarning-dismiss {
height: 32px;
background-color: #f8f9fa;
color: #36c;
}
.mw-cookiewarning-dismiss:hover {
background-color: #fff;
border-color: #859ecc;
box-shadow: none;
}
.mw-cookiewarning-dismiss:active {
background-color: #eff3fa;
color: #2a4b8d;
border-color: #2a4b8d;
}
.mw-cookiewarning-dismiss:focus {
border-color: #36c;
box-shadow: inset 0 0 0 1px #36c;
}
}
}
@media all and ( min-width: @width-breakpoint-tablet ) {
.mw-cookiewarning-container {
line-height: @contentPaddingTablet;
.mw-cookiewarning-cimage {
line-height: inherit;
}
}
}

View file

@ -1,12 +0,0 @@
@import 'mediawiki.ui/variables.less';
.mw-cookiewarning-container {
border-bottom: 1px solid #c8ccd1;
padding: 2.5em;
}
@media all and ( min-width: @width-breakpoint-tablet ) {
.mw-cookiewarning-container {
line-height: normal;
}
}

View file

@ -2,19 +2,14 @@
namespace CookieWarning\Tests; namespace CookieWarning\Tests;
use CommentStoreComment;
use CookieWarning\GeoLocation; use CookieWarning\GeoLocation;
use CookieWarning\Hooks; use CookieWarning\Hooks;
use DerivativeContext; use DerivativeContext;
use FauxRequest; use FauxRequest;
use MediaWiki\MediaWikiServices; use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\SlotRecord;
use MediaWikiLangTestCase; use MediaWikiLangTestCase;
use RequestContext; use RequestContext;
use SkinTemplate; use SkinTemplate;
use Title;
use WikiPage;
use WikitextContent;
/** /**
* @covers Hooks * @covers Hooks
@ -24,49 +19,78 @@ class HooksTest extends MediaWikiLangTestCase {
/** /**
* @dataProvider providerOnSiteNoticeAfter * @dataProvider providerOnSiteNoticeAfter
*
* @param bool $enabled
* @param false|string $morelinkConfig
* @param false|string $morelinkCookieWarningMsg
* @param false|string $morelinkCookiePolicyMsg
* @param false|string $expectedLink
*
* @throws \MWException * @throws \MWException
* @throws \ConfigException
*/ */
public function testOnSiteNoticeAfter( $enabled, $morelinkConfig, public function testOnSiteNoticeAfter(
$morelinkCookieWarningMsg, $morelinkCookiePolicyMsg, $expectedLink bool $enabled,
) { $morelinkConfig,
$morelinkCookieWarningMsg,
$morelinkCookiePolicyMsg,
$expectedLink
) : void {
$this->setMwGlobals( [ $this->setMwGlobals( [
'wgCookieWarningEnabled' => $enabled, 'wgCookieWarningEnabled' => $enabled,
'wgCookieWarningMoreUrl' => $morelinkConfig, 'wgCookieWarningMoreUrl' => $morelinkConfig,
'wgCookieWarningForCountryCodes' => false, 'wgCookieWarningForCountryCodes' => false,
'wgUseMediaWikiUIEverywhere' => true,
] ); ] );
MediaWikiServices::getInstance()->getMessageCache()->enable(); MediaWikiServices::getInstance()->getMessageCache()->enable();
if ( $morelinkCookieWarningMsg ) { if ( $morelinkCookieWarningMsg ) {
$title = Title::newFromText( 'cookiewarning-more-link', NS_MEDIAWIKI ); $this->editPage(
$wikiPage = WikiPage::factory( $title ); 'cookiewarning-more-link',
$pageUpdater = $wikiPage->newPageUpdater( \User::newFromName( 'UTSysop' ) ); strval( $morelinkCookieWarningMsg ),
$pageUpdater->setContent( SlotRecord::MAIN, new WikitextContent( $morelinkCookieWarningMsg ) ); '',
$pageUpdater->saveRevision( CommentStoreComment::newUnsavedComment( 'CookieWarning test' ) ); NS_MEDIAWIKI,
$this->getTestSysop()->getUser()
);
} }
if ( $morelinkCookiePolicyMsg ) { if ( $morelinkCookiePolicyMsg ) {
$title = Title::newFromText( 'cookie-policy-link', NS_MEDIAWIKI ); $this->editPage(
$wikiPage = WikiPage::factory( $title ); 'cookie-policy-link',
$pageUpdater = $wikiPage->newPageUpdater( \User::newFromName( 'UTSysop' ) ); strval( $morelinkCookiePolicyMsg ),
$pageUpdater->setContent( SlotRecord::MAIN, new WikitextContent( $morelinkCookiePolicyMsg ) ); '',
$pageUpdater->saveRevision( CommentStoreComment::newUnsavedComment( 'CookieWarning test' ) ); NS_MEDIAWIKI,
} $this->getTestSysop()->getUser()
$sk = new SkinTemplate(); );
$data = '';
Hooks::onSiteNoticeAfter( $data, $sk );
if ( $expectedLink === false ) {
$expected = '';
} else {
// @codingStandardsIgnoreStart Generic.Files.LineLength
$expected =
str_replace( '$1', $expectedLink,
'<div class="mw-cookiewarning-container banner-container"><div class="mw-cookiewarning-text"><span>Cookies help us deliver our services. By using our services, you agree to our use of cookies.</span>$1</div><form method="POST"><input name="disablecookiewarning" class="mw-cookiewarning-dismiss mw-ui-button" type="submit" value="OK"/></form></div>' );
// @codingStandardsIgnoreEnd
}
$this->assertEquals( $expected, $data );
} }
public function providerOnSiteNoticeAfter() { $sk = new SkinTemplate();
// setup OOUI, that would be normally done in BeforePageDisplay hook
$sk->getOutput()->enableOOUI();
$data = '';
Hooks::onSkinAfterContent( $data, $sk );
if ( $enabled ) {
$this->assertNotEmpty( $data, 'Cookie warning should be present' );
} else {
$this->assertEmpty( $data, 'Cookie warning should not be present' );
return;
}
if ( $expectedLink === false ) {
$this->assertNotRegExp(
'/<a[^>]+href=[\'"]([^\'"]+)[\'"].+?>/',
$data,
'More information link should not be present'
);
} else {
$this->assertRegExp(
'/<a[^>]+href=[\'"]' . preg_quote( $expectedLink, '/' ) . '[\'"].+?>/',
$data,
'More information link should be present and match the expectation'
);
}
}
public function providerOnSiteNoticeAfter() : array {
return [ return [
[ [
// $wgCookieWarningEnabled // $wgCookieWarningEnabled
@ -78,7 +102,7 @@ class HooksTest extends MediaWikiLangTestCase {
// MediaWiki:Cookie-policy-link // MediaWiki:Cookie-policy-link
false, false,
// expected cookie warning link (when string), nothing if false // expected cookie warning link (when string), nothing if false
'', false,
], ],
[ [
false, false,
@ -92,21 +116,21 @@ class HooksTest extends MediaWikiLangTestCase {
'http://google.de', 'http://google.de',
false, false,
false, false,
"\u{00A0}<a href=\"http://google.de\">More information</a>", 'http://google.de',
], ],
[ [
true, true,
'', '',
'http://google.de', 'http://google.de',
false, false,
"\u{00A0}<a href=\"http://google.de\">More information</a>", 'http://google.de',
], ],
[ [
true, true,
'', '',
false, false,
'http://google.de', 'http://google.de',
"\u{00A0}<a href=\"http://google.de\">More information</a>", 'http://google.de',
], ],
// the config should be the used, if set (no matter if the messages are used or not) // the config should be the used, if set (no matter if the messages are used or not)
[ [
@ -114,21 +138,21 @@ class HooksTest extends MediaWikiLangTestCase {
'http://google.de', 'http://google.de',
false, false,
'http://google123.de', 'http://google123.de',
"\u{00A0}<a href=\"http://google.de\">More information</a>", 'http://google.de',
], ],
[ [
true, true,
'http://google.de', 'http://google.de',
'http://google1234.de', 'http://google1234.de',
'http://google123.de', 'http://google123.de',
"\u{00A0}<a href=\"http://google.de\">More information</a>", 'http://google.de',
], ],
[ [
true, true,
'', '',
'http://google.de', 'http://google.de',
'http://google123.de', 'http://google123.de',
"\u{00A0}<a href=\"http://google.de\">More information</a>", 'http://google.de',
], ],
]; ];
} }
@ -155,7 +179,7 @@ class HooksTest extends MediaWikiLangTestCase {
$sk = new SkinTemplate(); $sk = new SkinTemplate();
$sk->setContext( $context ); $sk->setContext( $context );
$data = ''; $data = '';
Hooks::onSiteNoticeAfter( $data, $sk ); Hooks::onSkinAfterContent( $data, $sk );
$this->assertEquals( $this->assertEquals(
$expected, $expected,