Send disabled event when user disables Page Preview

Changes:
 - introduced new UserPreferencesChangeHandler class that listens to
 PreferencesFormPreSave hook
 - introduced wrapper for EventLogging extension plus NullLogger when
 EventLogging extension is not availalbe
 - when user changes PagePreview to disabled system will trigger
 disabled event

Bug: T167365
Change-Id: I63faecb0495eb30a9fc2763e7ddf3944baf7f55a
This commit is contained in:
Piotr Miazga 2017-07-06 20:16:05 +02:00
parent caec0a85b5
commit 8bba8c1417
12 changed files with 533 additions and 6 deletions

View file

@ -11,7 +11,12 @@
"AutoloadClasses": {
"Popups\\PopupsHooks": "includes/PopupsHooks.php",
"Popups\\PopupsContext": "includes/PopupsContext.php",
"Popups\\PopupsGadgetsIntegration": "includes/PopupsGadgetsIntegration.php"
"Popups\\PopupsGadgetsIntegration": "includes/PopupsGadgetsIntegration.php",
"Popups\\UserPreferencesChangeHandler": "includes/UserPreferencesChangeHandler.php",
"Popups\\EventLogging\\EventLogger": "includes/EventLogging/EventLogger.php",
"Popups\\EventLogging\\NullLogger": "includes/EventLogging/NullLogger.php",
"Popups\\EventLogging\\MWEventLogger": "includes/EventLogging/MWEventLogger.php",
"Popups\\EventLogging\\EventLoggerFactory": "includes/EventLogging/EventLoggerFactory.php"
},
"ConfigRegistry": {
"popups": "GlobalVarConfig::newInstance"
@ -29,6 +34,9 @@
"GetPreferences": [
"Popups\\PopupsHooks::onGetPreferences"
],
"PreferencesFormPreSave" : [
"Popups\\UserPreferencesChangeHandler::onPreferencesFormPreSave"
],
"UserGetDefaultOptions": [
"Popups\\PopupsHooks::onUserGetDefaultOptions"
],

View file

@ -0,0 +1,32 @@
<?php
/*
* This file is part of the MediaWiki extension Popups.
*
* Popups is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Popups is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Popups. If not, see <http://www.gnu.org/licenses/>.
*
* @file
* @ingroup extensions
*/
namespace Popups\EventLogging;
interface EventLogger {
/**
* Page Previews Event logging schema name
* @var string
*/
const PREVIEWS_SCHEMA_NAME = 'Popups';
public function log( array $event );
}

View file

@ -0,0 +1,53 @@
<?php
/*
* This file is part of the MediaWiki extension Popups.
*
* Popups is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Popups is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Popups. If not, see <http://www.gnu.org/licenses/>.
*
* @file
* @ingroup extensions
*/
namespace Popups\EventLogging;
use Config;
use ExtensionRegistry;
class EventLoggerFactory {
/**
* @var ExtensionRegistry
*/
private $registry;
/**
* @var \Config
*/
private $config;
public function __construct( Config $config, ExtensionRegistry $registry ) {
$this->registry = $registry;
$this->config = $config;
}
/**
* @return EventLogger
*/
public function get() {
if ( $this->registry->isLoaded( 'EventLogging' ) ) {
return new MWEventLogger( $this->config, $this->registry );
}
return new NullLogger();
}
}

View file

@ -0,0 +1,72 @@
<?php
/*
* This file is part of the MediaWiki extension Popups.
*
* Popups is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Popups is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Popups. If not, see <http://www.gnu.org/licenses/>.
*
* @file
* @ingroup extensions
*/
namespace Popups\EventLogging;
use Config;
use ExtensionRegistry;
class MWEventLogger implements EventLogger {
/**
* @var Config
*/
private $config;
/**
* @var ExtensionRegistry
*/
private $registry;
/**
* Module constructor.
* @param Config $config
*/
public function __construct( Config $config, ExtensionRegistry $registry ) {
$this->config = $config;
$this->registry = $registry;
}
/**
* @return bool
*/
public function shouldLog() {
// 1 fully enabled, 0 disabled
$samplingRate = $this->config->get( 'PopupsSchemaSamplingRate' );
if ( $samplingRate == 0 ) {
return false;
}
return (float)wfRandom() <= (float)$samplingRate;
}
public function log( array $event ) {
if ( !$this->shouldLog() ) {
return false;
}
$eventLoggingSchemas = $this->registry->getAttribute( 'EventLoggingSchemas' );
\EventLogging::logEvent(
self::PREVIEWS_SCHEMA_NAME,
$eventLoggingSchemas[ self::PREVIEWS_SCHEMA_NAME ],
$event
);
}
}

View file

@ -0,0 +1,29 @@
<?php
/*
* This file is part of the MediaWiki extension Popups.
*
* Popups is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Popups is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Popups. If not, see <http://www.gnu.org/licenses/>.
*
* @file
* @ingroup extensions
*/
namespace Popups\EventLogging;
class NullLogger implements EventLogger {
public function log( array $event ) {
// just do nothing
}
}

View file

@ -24,6 +24,10 @@ use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
use ExtensionRegistry;
use Config;
use Popups\EventLogging\EventLogger;
use Popups\EventLogging\EventLoggerFactory;
use Popups\EventLogging\MWEventLogger;
use Popups\EventLogging\NullLogger;
/**
* Popups Module
@ -68,7 +72,6 @@ class PopupsContext {
* @var string
*/
const PREVIEWS_BETA_PREFERENCE_NAME = 'popups';
/**
* @var \Config
*/
@ -83,12 +86,14 @@ class PopupsContext {
* @param Config $config
* @param ExtensionRegistry $extensionRegistry
* @param PopupsGadgetsIntegration $gadgetsIntegration
* @param EventLogger $eventLogger
*/
protected function __construct( Config $config, ExtensionRegistry $extensionRegistry,
PopupsGadgetsIntegration $gadgetsIntegration ) {
PopupsGadgetsIntegration $gadgetsIntegration, EventLogger $eventLogger ) {
/** @todo Use MediaWikiServices Service Locator when it's ready */
$this->extensionRegistry = $extensionRegistry;
$this->gadgetsIntegration = $gadgetsIntegration;
$this->eventLogger = $eventLogger;
$this->config = $config;
}
@ -106,7 +111,10 @@ class PopupsContext {
$config = MediaWikiServices::getInstance()->getConfigFactory()
->makeConfig( PopupsContext::EXTENSION_NAME );
$gadgetsIntegration = new PopupsGadgetsIntegration( $config, $registry );
self::$instance = new PopupsContext( $config, $registry, $gadgetsIntegration );
$eventLoggerFactory = new EventLoggerFactory( $config, $registry );
self::$instance = new PopupsContext( $config, $registry,
$gadgetsIntegration, $eventLoggerFactory->get() );
}
return self::$instance;
}
@ -191,4 +199,23 @@ class PopupsContext {
return $this->config;
}
/**
* Log disabled event
*/
public function logUserDisabledPagePreviewsEvent() {
// @see https://phabricator.wikimedia.org/T167365
$this->eventLogger->log( [
'pageTitleSource' => 'Special:Preferences',
'namespaceIdSource' => NS_SPECIAL,
'pageIdSource' => -1,
'hovercardsSuppressedByGadget' => false,
'pageToken' => wfRandomString(),
'sessionToken' => wfRandomString(), // we don't have access to mw.user.sessionId()
'action' => 'disabled',
'isAnon' => false,
'popupEnabled' => false,
'previewCountBucket' => 'unknown'
] );
}
}

View file

@ -0,0 +1,94 @@
<?php
/*
* This file is part of the MediaWiki extension Popups.
*
* Popups is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Popups is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Popups. If not, see <http://www.gnu.org/licenses/>.
*
* @file
* @ingroup extensions
*/
namespace Popups;
use User;
use PreferencesForm;
/**
* User Preferences save change listener
*
* @package Popups
*/
class UserPreferencesChangeHandler {
/**
* @var UserPreferencesChangeHandler
*/
private static $instance;
/**
* @var PopupsContext
*/
private $popupsContext;
/**
* UserPreferencesChangeHandler constructor.
* @param PopupsContext $context
*/
public function __construct( PopupsContext $context ) {
$this->popupsContext = $context;
}
/**
* Hook executed on Preferences Form Save, when user disables Page Previews call PopupsContext
* to log `disabled` event.
*
* @param User $user
* @param array $oldUserOptions
*/
public function doPreferencesFormPreSave( User $user, array $oldUserOptions ) {
if ( !array_key_exists( PopupsContext::PREVIEWS_OPTIN_PREFERENCE_NAME, $oldUserOptions ) ) {
return;
}
$oldSetting = $oldUserOptions[ PopupsContext::PREVIEWS_OPTIN_PREFERENCE_NAME ];
$newSetting = $user->getOption( PopupsContext::PREVIEWS_OPTIN_PREFERENCE_NAME );
if ( $oldSetting == PopupsContext::PREVIEWS_ENABLED
&& $newSetting == PopupsContext::PREVIEWS_DISABLED ) {
$this->popupsContext->logUserDisabledPagePreviewsEvent();
}
}
/**
* @return UserPreferencesChangeHandler
*/
private static function newFromGlobalState() {
if ( self::$instance === null ) {
self::$instance = new UserPreferencesChangeHandler( PopupsContext::getInstance() );
}
return self::$instance;
}
/**
* @param array $formData
* @param PreferencesForm $form
* @param User $user
* @param boolean $result
* @param array $oldUserOptions
*/
public static function onPreferencesFormPreSave(
array $formData,
PreferencesForm $form,
User $user,
&$result,
$oldUserOptions ) {
self::newFromGlobalState()->doPreferencesFormPreSave( $user, $oldUserOptions );
}
}

View file

@ -0,0 +1,63 @@
<?php
/*
* This file is part of the MediaWiki extension Popups.
*
* Popups is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Popups is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Popups. If not, see <http://www.gnu.org/licenses/>.
*
* @file
* @ingroup extensions
*/
use Popups\EventLogging\MWEventLogger;
use Popups\EventLogging\NullLogger;
use Popups\EventLogging\EventLoggerFactory;
/**
* Integration tests for Page Preview hooks
*
* @group Popups
* @coversDefaultClass EventLoggerFactory
*/
class EventLoggerFactoryTest extends MediaWikiTestCase {
/**
* @covers ::get
*/
public function testReturnsMWEventWhenEventLoggingIsAvailable() {
$mock = $this->getMock( ExtensionRegistry::class, [ 'isLoaded' ] );
$mock->expects( $this->once() )
->method( 'isLoaded' )
->with( 'EventLogging' )
->willReturn( true );
$config = new HashConfig();
$factory = new EventLoggerFactory( $config, $mock );
$this->assertInstanceOf( MWEventLogger::class, $factory->get() );
}
/**
* @covers ::get
*/
public function testReturnsMWEventWhenEventLoggingIsNotAvailable() {
$mock = $this->getMock( ExtensionRegistry::class, [ 'isLoaded' ] );
$mock->expects( $this->once() )
->method( 'isLoaded' )
->with( 'EventLogging' )
->willReturn( false );
$config = new HashConfig();
$factory = new EventLoggerFactory( $config, $mock );
$this->assertInstanceOf( NullLogger::class, $factory->get() );
}
}

View file

@ -0,0 +1,56 @@
<?php
/*
* This file is part of the MediaWiki extension Popups.
*
* Popups is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Popups is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Popups. If not, see <http://www.gnu.org/licenses/>.
*
* @file
* @ingroup extensions
*/
/**
* Integration tests for Page Preview hooks
*
* @group Popups
* @coversDefaultClass MWEventLogger
*/
class MWEventLoggerTest extends MediaWikiTestCase {
/**
* @covers ::logUserDisabledPagePreviewsEvent
* @dataProvider provideTestDataForLogUserDisabledPagePreviewsEventW
*/
public function testShouldLog( $samplingRate, $expected ) {
$config = $this->getMockBuilder( 'Config' )->setMethods( [ 'get', 'has' ] )->getMock();
$config->expects( $this->once() )
->method( 'get' )
->with( 'PopupsSchemaSamplingRate' )
->willReturn( $samplingRate );
$logger = new \Popups\EventLogging\MWEventLogger( $config, ExtensionRegistry::getInstance() );
$this->assertEquals( $expected, $logger->shouldLog() );
}
/**
* Mock edge cases
* @return array
*/
public function provideTestDataForLogUserDisabledPagePreviewsEventW() {
return [
[ 0, false ],
[ 1, true ]
];
}
}

View file

@ -311,6 +311,23 @@ class PopupsContextTest extends MediaWikiTestCase {
$this->assertEquals( true, $context->conflictsWithNavPopupsGadget( $user ) );
}
/**
* @covers ::logUserDisabledPagePreviewsEvent
*/
public function testLogsEvent() {
$loggerMock = $this->getMock( \Popups\EventLogging\EventLogger::class );
$loggerMock->expects( $this->once() )
->method( 'log' );
$integrationMock = $this->getMockBuilder( \Popups\PopupsGadgetsIntegration::class )
->disableOriginalConstructor()
->setMethods( [ 'conflictsWithNavPopupsGadget' ] )
->getMock();
$context = new PopupsContextTestWrapper( $this->getConfigMock(),
ExtensionRegistry::getInstance(), $integrationMock, $loggerMock );
$context->logUserDisabledPagePreviewsEvent();
}
/**
* @return PHPUnit_Framework_MockObject_MockObject|Config
*/

View file

@ -18,8 +18,11 @@
* @file
* @ingroup extensions
*/
use Popups\PopupsContext;
use Popups\PopupsGadgetsIntegration;
use Popups\EventLogging\EventLogger;
use Popups\EventLogging\NullLogger;
/**
* Create an initializable Popups context.
@ -40,13 +43,16 @@ class PopupsContextTestWrapper extends PopupsContext {
* @param Config $config
* @param ExtensionRegistry $extensionRegistry
* @param PopupsGadgetsIntegration|null $gadgetsIntegration
* @param EventLogger|null $eventLogger
*/
public function __construct( Config $config, ExtensionRegistry $extensionRegistry,
PopupsGadgetsIntegration $gadgetsIntegration = null ) {
PopupsGadgetsIntegration $gadgetsIntegration = null,
EventLogger $eventLogger = null ) {
$gadgetsIntegration = $gadgetsIntegration ? $gadgetsIntegration :
new PopupsGadgetsIntegration( $config, $extensionRegistry );
$eventLogger = $eventLogger ? $eventLogger : new NullLogger();
parent::__construct( $config, $extensionRegistry, $gadgetsIntegration );
parent::__construct( $config, $extensionRegistry, $gadgetsIntegration, $eventLogger );
}
/**

View file

@ -0,0 +1,70 @@
<?php
/*
* This file is part of the MediaWiki extension Popups.
*
* Popups is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Popups is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Popups. If not, see <http://www.gnu.org/licenses/>.
*
* @file
* @ingroup extensions
*/
require_once 'PopupsContextTestWrapper.php';
use Popups\PopupsContext;
use Popups\UserPreferencesChangeHandler;
/**
* Integration tests for Page Preview hooks
*
* @group Popups
* @coversDefaultClass UserPreferencesChangeHandlerTest
*/
class UserPreferencesChangeHandlerTest extends MediaWikiTestCase {
protected function tearDown() {
PopupsContextTestWrapper::resetTestInstance();
parent::tearDown();
}
/**
* @covers ::handle
* @covers ::__construct
* @dataProvider provideDataForEventHandling
*/
public function testEventHandling( $oldOption, $newOption, $expectedMethodCallsCount ) {
$contextMock = $this->getMockBuilder( PopupsContextTestWrapper::class )
->disableOriginalConstructor()
->setMethods( [ 'logUserDisabledPagePreviewsEvent' ] )
->getMock();
$contextMock->expects( $expectedMethodCallsCount )
->method( 'logUserDisabledPagePreviewsEvent' );
$user = $this->getMutableTestUser()->getUser();
$user->setOption( PopupsContext::PREVIEWS_OPTIN_PREFERENCE_NAME, $newOption );
$oldOptions = [
PopupsContext::PREVIEWS_OPTIN_PREFERENCE_NAME => $oldOption
];
$listener = new UserPreferencesChangeHandler( $contextMock );
$listener->doPreferencesFormPreSave( $user, $oldOptions );
}
public function provideDataForEventHandling() {
return [
[ PopupsContext::PREVIEWS_DISABLED, PopupsContext::PREVIEWS_DISABLED, $this->never() ],
[ PopupsContext::PREVIEWS_ENABLED, PopupsContext::PREVIEWS_ENABLED, $this->never() ],
[ PopupsContext::PREVIEWS_DISABLED, PopupsContext::PREVIEWS_ENABLED, $this->never() ],
[ PopupsContext::PREVIEWS_ENABLED, PopupsContext::PREVIEWS_DISABLED, $this->once() ]
];
}
}