From 6352190684e4ee9507b5a1b0942d14323da0ba5e Mon Sep 17 00:00:00 2001 From: Piotr Miazga Date: Fri, 24 May 2019 02:07:27 +0200 Subject: [PATCH] Hygiene: Extract isAllowedPageAction into MinervaPagePermissions The isAllowedPageAction is used in multiple places (SkinMinerva and in PageActions toolbar builder). This logic should be defined in separate service, easy accessible for different parts of the Minerva skin. Changes: - Introduced MinervaPagePermissions as a centralized place to manage user permissions - Introduced MinervaNoTitlePermissions, an NullObject pattern to handle situations when we do not have Title object (like in CLI) - removed Minerva.ContentHandler service as it's not required any more - moved all permission names into constants - moved isTalkAllowed() into MinervaPermissions - renamed isAllowedPageAction() it `isAllowed()` to not mix it with PageActions. Those checks are used in many places, not only on PageActions menu - made isAllowed( watch ) more robust - now it checks that Title is watchable Bug: T221792 Change-Id: I87d44a9c717b5f752b8d1fd2f146d7f5eef3c53f --- includes/ServiceWiring.php | 31 ++- includes/menu/PageActions/ToolbarBuilder.php | 37 +-- .../permissions/IMinervaPagePermissions.php | 48 ++++ .../permissions/MinervaNoPagePermissions.php | 44 ++++ .../permissions/MinervaPagePermissions.php | 169 +++++++++++++ includes/skins/SkinMinerva.php | 119 ++------- skin.json | 3 +- .../MinervaPagePermissionsTest.php | 237 ++++++++++++++++++ .../skins/SkinMinervaPageActionsTest.php | 222 ---------------- 9 files changed, 572 insertions(+), 338 deletions(-) create mode 100644 includes/permissions/IMinervaPagePermissions.php create mode 100644 includes/permissions/MinervaNoPagePermissions.php create mode 100644 includes/permissions/MinervaPagePermissions.php create mode 100644 tests/phpunit/permissions/MinervaPagePermissionsTest.php delete mode 100644 tests/phpunit/skins/SkinMinervaPageActionsTest.php diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php index 73a7465..ef3b2ff 100644 --- a/includes/ServiceWiring.php +++ b/includes/ServiceWiring.php @@ -23,13 +23,13 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Minerva\Menu\Definitions; use MediaWiki\Minerva\Menu\Main as MainMenu; use MediaWiki\Minerva\Menu\PageActions as PageActionsMenu; +use MediaWiki\Minerva\Permissions\IMinervaPagePermissions; +use MediaWiki\Minerva\Permissions\MinervaPagePermissions; +use MediaWiki\Minerva\Permissions\MinervaNoPagePermissions; use MediaWiki\Minerva\SkinOptions; use MediaWiki\Minerva\SkinUserPageHelper; return [ - 'Minerva.ContentHandler' => function () { - return ContentHandler::getForTitle( RequestContext::getMain()->getTitle() ); - }, 'Minerva.Menu.MainDirector' => function ( MediaWikiServices $services ) { $context = RequestContext::getMain(); /** @var SkinOptions $options */ @@ -51,14 +51,13 @@ return [ */ $skinOptions = $services->getService( 'Minerva.SkinOptions' ); $context = RequestContext::getMain(); - $skin = $context->getSkin(); $userPageHelper = $services->getService( 'Minerva.SkinUserPageHelper' ); $toolbarBuilder = new PageActionsMenu\ToolbarBuilder( - $skin, $context->getTitle(), $context->getUser(), $context, - $services->getPermissionManager() + $services->getPermissionManager(), + $services->getService( 'Minerva.Permissions' ) ); if ( $skinOptions->get( SkinOptions::OPTION_OVERFLOW_SUBMENU ) ) { $overflowBuilder = $userPageHelper->isUserPage() ? @@ -85,5 +84,25 @@ return [ }, 'Minerva.SkinOptions' => function () { return new SkinOptions(); + }, + 'Minerva.Permissions' => function ( MediaWikiServices $services ): IMinervaPagePermissions { + $context = RequestContext::getMain(); + $title = $context->getTitle(); + + // Title may be undefined in certain contexts (T179833) + if ( $title ) { + $contentHandler = ContentHandler::getForTitle( $title ); + + return new MinervaPagePermissions( + $context->getTitle(), + $context->getConfig(), + $context->getUser(), + $context->getOutput(), + $services->getService( 'Minerva.SkinOptions' ), + $contentHandler + ); + } else { + return new MinervaNoPagePermissions(); + } } ]; diff --git a/includes/menu/PageActions/ToolbarBuilder.php b/includes/menu/PageActions/ToolbarBuilder.php index b1f22be..2e1bbeb 100644 --- a/includes/menu/PageActions/ToolbarBuilder.php +++ b/includes/menu/PageActions/ToolbarBuilder.php @@ -24,6 +24,7 @@ use ExtensionRegistry; use Hooks; use MediaWiki\Minerva\Menu\Group; use MediaWiki\Minerva\Menu\LanguageSelectorEntry; +use MediaWiki\Minerva\Permissions\IMinervaPagePermissions; use MediaWiki\Permissions\PermissionManager; use MessageLocalizer; use MinervaUI; @@ -44,16 +45,14 @@ class ToolbarBuilder { * @var Title Article title user is currently browsing */ private $title; - /** - * Temporary variable to access isAllowedPageAction() method. - * FIXME: Create MinervaAllowedPageActions service - * @var SkinMinerva user skin - */ - private $skin; /** * @var MessageLocalizer Message localizer to generate localized texts */ private $messageLocalizer; + /** + * @var IMinervaPagePermissions + */ + private $permissions; /** * @var PermissionManager @@ -61,19 +60,23 @@ class ToolbarBuilder { private $permissionsManager; /** * Build Group containing icons for toolbar - * @param SkinMinerva $skin User Skin * @param Title $title Article title user is currently browsing * @param User $user Currently logged in user * @param MessageLocalizer $msgLocalizer Message localizer to generate localized texts * @param PermissionManager $permissionManager Mediawiki Permissions Manager + * @param IMinervaPagePermissions $permissions Minerva permissions system */ - public function __construct( SkinMinerva $skin, Title $title, User $user, - MessageLocalizer $msgLocalizer, PermissionManager $permissionManager ) { - $this->skin = $skin; + public function __construct( + Title $title, + User $user, + MessageLocalizer $msgLocalizer, + PermissionManager $permissionManager, + IMinervaPagePermissions $permissions ) { $this->title = $title; $this->user = $user; $this->messageLocalizer = $msgLocalizer; $this->permissionsManager = $permissionManager; + $this->permissions = $permissions; } /** @@ -84,25 +87,25 @@ class ToolbarBuilder { */ public function getGroup( $doesPageHaveLanguages ) { $group = new Group(); + $permissions = $this->permissions; - if ( $this->skin->isAllowedPageAction( 'switch-language' ) ) { - $group->insertEntry( - new LanguageSelectorEntry( $this->title, $doesPageHaveLanguages, $this->messageLocalizer ) - ); + if ( $permissions->isAllowed( IMinervaPagePermissions::SWITCH_LANGUAGE ) ) { + $group->insertEntry( new LanguageSelectorEntry( $this->title, $doesPageHaveLanguages, + $this->messageLocalizer ) ); } - if ( $this->skin->isAllowedPageAction( 'watch' ) ) { + if ( $permissions->isAllowed( IMinervaPagePermissions::WATCH ) ) { $group->insertEntry( $this->createWatchPageAction() ); } - if ( $this->skin->isAllowedPageAction( 'history' ) ) { + if ( $permissions->isAllowed( IMinervaPagePermissions::HISTORY ) ) { $group->insertEntry( $this->getHistoryPageAction() ); } Hooks::run( 'MobileMenu', [ 'pageactions.toolbar', &$group ] ); // We want the edit icon/action always to be the last element on the toolbar list - if ( $this->skin->isAllowedPageAction( 'edit' ) ) { + if ( $permissions->isAllowed( IMinervaPagePermissions::EDIT ) ) { $group->insertEntry( $this->createEditPageAction() ); } return $group; diff --git a/includes/permissions/IMinervaPagePermissions.php b/includes/permissions/IMinervaPagePermissions.php new file mode 100644 index 0000000..751f116 --- /dev/null +++ b/includes/permissions/IMinervaPagePermissions.php @@ -0,0 +1,48 @@ +title = $title; + $this->config = $config; + $this->user = $user; + $this->output = $output; + $this->skinOptions = $skinOptions; + $this->contentHandler = $contentHandler; + } + + /** + * Gets whether or not the action is allowed. + * + * Actions isn't allowed when: + * + * + * The "edit" action is not allowed if editing is not possible on the page + * see @method: isCurrentPageContentModelEditable + * + * The "switch-language" is allowed if there are interlanguage links on the page, + * or $wgMinervaAlwaysShowLanguageButton is truthy. + * + * @inheritDoc + * @throws ConfigException + */ + public function isAllowed( $action ) { + // T206406: Enable "Talk" or "Discussion" button on Main page, also, not forgetting + // the "switch-language" button. But disable "edit" and "watch" actions. + if ( $this->title->isMainPage() ) { + return ( in_array( $action, $this->config->get( 'MinervaPageActions' ) ) + && ( $action === self::TALK || $action === self::SWITCH_LANGUAGE ) ); + } + + if ( $action === self::HISTORY && $this->title->exists() ) { + return $this->skinOptions->get( SkinOptions::OPTIONS_HISTORY_PAGE_ACTIONS ); + } + + if ( $action === SkinOptions::OPTION_OVERFLOW_SUBMENU ) { + return $this->skinOptions->get( SkinOptions::OPTION_OVERFLOW_SUBMENU ); + } + + if ( !in_array( $action, $this->config->get( 'MinervaPageActions' ) ) ) { + return false; + } + + if ( $action === self::EDIT ) { + return $this->isCurrentPageContentModelEditable(); + } + + if ( $action === self::WATCH ) { + return $this->title->isWatchable() + ? $this->user->isAllowedAll( 'viewmywatchlist', 'editmywatchlist' ) + : false; + } + + if ( $action === self::SWITCH_LANGUAGE ) { + $hasVariants = $this->title->getPageLanguage()->hasVariants(); + $hasLanguages = count( $this->output->getLanguageLinks() ); + + return $hasVariants || $hasLanguages || + $this->config->get( 'MinervaAlwaysShowLanguageButton' ); + } + return true; + } + + /** + * @inheritDoc + */ + public function isTalkAllowed() { + return $this->isAllowed( self::TALK ) && + ( $this->title->isTalkPage() || $this->title->canHaveTalkPage() ) && + $this->user->isLoggedIn(); + } + + /** + * Checks whether the editor can handle the existing content handler type. + * + * @return bool + */ + protected function isCurrentPageContentModelEditable() { + return $this->contentHandler->supportsDirectEditing() + && $this->contentHandler->supportsDirectApiEditing(); + } + +} diff --git a/includes/skins/SkinMinerva.php b/includes/skins/SkinMinerva.php index d52e94e..7f036a5 100644 --- a/includes/skins/SkinMinerva.php +++ b/includes/skins/SkinMinerva.php @@ -20,6 +20,7 @@ use MediaWiki\MediaWikiServices; use MediaWiki\Minerva\Menu\Main\Director as MainMenuDirector; +use MediaWiki\Minerva\Permissions\IMinervaPagePermissions; use MediaWiki\Minerva\SkinOptions; use MediaWiki\Minerva\SkinUserPageHelper; @@ -44,6 +45,13 @@ class SkinMinerva extends SkinTemplate { /** @var SkinOptions */ private $skinOptions; + /** + * This variable is lazy loaded, please use getPermissions() getter + * @see SkinMinerva::getPermissions() + * @var IMinervaPagePermissions + */ + private $permissions; + /** * Initialize Minerva Skin */ @@ -52,6 +60,20 @@ class SkinMinerva extends SkinTemplate { $this->skinOptions = MediaWikiServices::getInstance()->getService( 'Minerva.SkinOptions' ); } + /** + * Lazy load the permissions object. We don't want to initialize it as it requires many + * dependencies, sometimes some of those dependencies cannot be fulfilled (like missing Title + * object) + * @return IMinervaPagePermissions + */ + private function getPermissions() { + if ( $this->permissions === null ) { + $this->permissions = MediaWikiServices::getInstance() + ->getService( 'Minerva.Permissions' ); + } + return $this->permissions; + } + /** * Initalized main menu. Please use getter. * @return MainMenuDirector @@ -189,70 +211,6 @@ class SkinMinerva extends SkinTemplate { } } - /** - * Gets whether or not the page action is allowed. - * - * Page actions isn't allowed when: - * - * - * The "edit" page action is not allowed if editing is not possible on the page - * see @method: isCurrentPageContentModelEditable - * - * The "switch-language" is allowed if there are interlanguage links on the page, - * or $wgMinervaAlwaysShowLanguageButton - * is truthy. - * - * @param string $action - * @return bool - */ - public function isAllowedPageAction( $action ) { - $title = $this->getTitle(); - $config = $this->getConfig(); - // Title may be undefined in certain contexts (T179833) - if ( !$title ) { - return false; - } - - // T206406: Enable "Talk" or "Discussion" button on Main page, also, not forgetting - // the "switch-language" button. But disable "edit" and "watch" actions. - if ( $title->isMainPage() ) { - return ( in_array( $action, $config->get( 'MinervaPageActions' ) ) - && ( $action === 'talk' || $action === 'switch-language' ) ); - } - - if ( $action === 'history' && $title->exists() ) { - return $this->skinOptions->get( SkinOptions::OPTIONS_HISTORY_PAGE_ACTIONS ); - } - - if ( $action === SkinOptions::OPTION_OVERFLOW_SUBMENU ) { - return $this->skinOptions->get( SkinOptions::OPTION_OVERFLOW_SUBMENU ); - } - - if ( !in_array( $action, $config->get( 'MinervaPageActions' ) ) ) { - return false; - } - - if ( $action === 'edit' ) { - return $this->isCurrentPageContentModelEditable(); - } - - if ( $action === 'watch' ) { - return $this->getUser()->isAllowedAll( 'viewmywatchlist', 'editmywatchlist' ); - } - - if ( $action === 'switch-language' ) { - return $this->doesPageHaveLanguages || $config->get( 'MinervaAlwaysShowLanguageButton' ); - } - - return true; - } - /** * Overrides Skin::doEditSectionLink * @param Title $nt @@ -262,7 +220,8 @@ class SkinMinerva extends SkinTemplate { * @return string */ public function doEditSectionLink( Title $nt, $section, $tooltip, Language $lang ) { - if ( $this->isAllowedPageAction( 'edit' ) && !$nt->isMainPage() ) { + if ( $this->getPermissions()->isAllowed( IMinervaPagePermissions::EDIT ) && + !$nt->isMainPage() ) { $message = $this->msg( 'mobile-frontend-editor-edit' )->inLanguage( $lang )->text(); $html = Html::openElement( 'span', [ 'class' => 'mw-editsection' ] ); $html .= Html::element( 'a', [ @@ -790,7 +749,7 @@ class SkinMinerva extends SkinTemplate { $subjectPage->isMainPage(); $namespaces = $tpl->data['content_navigation']['namespaces']; if ( !$this->getUserPageHelper()->isUserPage() - && $this->isTalkAllowed() && $talkAtBottom + && $this->getPermissions()->isTalkAllowed() && $talkAtBottom ) { // FIXME [core]: This seems unnecessary.. $subjectId = $title->getNamespaceKey( '' ); @@ -852,19 +811,6 @@ class SkinMinerva extends SkinTemplate { return []; } - /** - * Checks whether the editor can handle the existing content handler type. - * - * @return bool - */ - protected function isCurrentPageContentModelEditable() { - $contentHandler = MediaWikiServices::getInstance() - ->getService( 'Minerva.ContentHandler' ); - - return $contentHandler->supportsDirectEditing() - && $contentHandler->supportsDirectApiEditing(); - } - /** * Returns array of config variables that should be added only to this skin * for use in JavaScript. @@ -880,17 +826,6 @@ class SkinMinerva extends SkinTemplate { return $vars; } - /** - * Returns true, if the page can have a talk page and user is logged in. - * @return bool - */ - protected function isTalkAllowed() { - $title = $this->getTitle(); - return $this->isAllowedPageAction( 'talk' ) && - ( $title->isTalkPage() || $title->canHaveTalkPage() ) && - $this->getUser()->isLoggedIn(); - } - /** * Returns true, if the talk page of this page is wikitext-based. * @return bool @@ -912,7 +847,7 @@ class SkinMinerva extends SkinTemplate { $user = $this->getUser(); $title = $this->getTitle(); - if ( !$title->isSpecialPage() && $this->isAllowedPageAction( 'watch' ) ) { + if ( $this->getPermissions()->isAllowed( IMinervaPagePermissions::WATCH ) ) { // Explicitly add the mobile watchstar code. $modules[] = 'skins.minerva.watchstar'; } @@ -924,7 +859,7 @@ class SkinMinerva extends SkinTemplate { // TalkOverlay feature if ( $this->getUserPageHelper()->isUserPage() || - ( $this->isTalkAllowed() || $title->isTalkPage() ) && + ( $this->getPermissions()->isTalkAllowed() || $title->isTalkPage() ) && $this->isWikiTextTalkPage() ) { $modules[] = 'skins.minerva.talk'; diff --git a/skin.json b/skin.json index 62a5768..c5317b3 100644 --- a/skin.json +++ b/skin.json @@ -73,7 +73,8 @@ "minerva": "MinervaNeue" }, "AutoloadNamespaces": { - "MediaWiki\\Minerva\\Menu\\": "includes/menu/" + "MediaWiki\\Minerva\\Menu\\": "includes/menu/", + "MediaWiki\\Minerva\\Permissions\\": "includes/permissions/" }, "AutoloadClasses": { "MinervaUI": "includes/MinervaUI.php", diff --git a/tests/phpunit/permissions/MinervaPagePermissionsTest.php b/tests/phpunit/permissions/MinervaPagePermissionsTest.php new file mode 100644 index 0000000..0e7c072 --- /dev/null +++ b/tests/phpunit/permissions/MinervaPagePermissionsTest.php @@ -0,0 +1,237 @@ +getOutput(); + + $user = $user ?? $this->getTestUser()->getUser(); + $actions = $actions ?? [ + IMinervaPagePermissions::EDIT, + IMinervaPagePermissions::WATCH, + IMinervaPagePermissions::TALK, + IMinervaPagePermissions::SWITCH_LANGUAGE, + ]; + + $contentHandler = $contentHandler ?? + $this->getMockForAbstractClass( ContentHandler::class, [], '', false ); + $skinOptions = new SkinOptions(); + if ( $options ) { + $skinOptions->setMultiple( $options ); + } + + return new MinervaPagePermissions( + $title, + new \HashConfig( [ + 'MinervaPageActions' => $actions, + 'MinervaAlwaysShowLanguageButton' => $alwaysShowLanguageButton + ] ), + $user, + $outputPage, + $skinOptions, + $contentHandler + ); + } + + /** + * @covers ::isAllowed + */ + public function testWatchAndEditNotAllowedOnMainPage() { + $perms = $this->buildPermissionsObject( Title::newMainPage() ); + + $this->assertFalse( $perms->isAllowed( IMinervaPagePermissions::WATCH ) ); + $this->assertFalse( $perms->isAllowed( IMinervaPagePermissions::EDIT ) ); + + // Check to make sure 'talk' and 'switch-language' are enabled on the Main page. + $this->assertTrue( $perms->isAllowed( IMinervaPagePermissions::TALK ) ); + $this->assertTrue( $perms->isAllowed( IMinervaPagePermissions::SWITCH_LANGUAGE ) ); + } + + /** + * @covers ::isAllowed + */ + public function testInvalidPageActionsArentAllowed() { + $perms = $this->buildPermissionsObject( Title::newFromText( 'test' ), [] ); + + // By default, the "talk" and "watch" page actions are allowed but are now deemed invalid. + $this->assertFalse( $perms->isAllowed( IMinervaPagePermissions::TALK ) ); + $this->assertFalse( $perms->isAllowed( IMinervaPagePermissions::WATCH ) ); + } + + /** + * @covers ::isAllowed + */ + public function testValidPageActionsAreAllowed() { + $perms = $this->buildPermissionsObject( Title::newFromText( 'test' ) ); + $this->assertTrue( $perms->isAllowed( IMinervaPagePermissions::TALK ) ); + $this->assertTrue( $perms->isAllowed( IMinervaPagePermissions::WATCH ) ); + } + + public static function editPageActionProvider() { + return [ + [ false, false, false ], + [ true, false, false ], + [ true, true, true ] + ]; + } + + /** + * The "edit" page action is allowed when the page doesn't support direct editing via the API. + * + * @dataProvider editPageActionProvider + * @covers ::isAllowed + */ + public function testEditPageAction( + $supportsDirectEditing, + $supportsDirectApiEditing, + $expected + ) { + $contentHandler = $this->getMockBuilder( 'ContentHandler' ) + ->disableOriginalConstructor() + ->getMock(); + + $contentHandler->method( 'supportsDirectEditing' ) + ->will( $this->returnValue( $supportsDirectEditing ) ); + + $contentHandler->method( 'supportsDirectApiEditing' ) + ->will( $this->returnValue( $supportsDirectApiEditing ) ); + + $perms = $this->buildPermissionsObject( Title::newFromText( 'test' ), null, [], + $contentHandler ); + + $this->assertEquals( $expected, $perms->isAllowed( IMinervaPagePermissions::EDIT ) ); + } + + /** + * @covers ::isAllowed + */ + public function testPageActionsWhenOnUserPage() { + $perms = $this->buildPermissionsObject( Title::newFromText( 'User:Admin' ) ); + $this->assertTrue( $perms->isAllowed( IMinervaPagePermissions::TALK ) ); + } + + /** + * @covers ::isAllowed + */ + public function testPageActionsWhenOnAnonUserPage() { + $perms = $this->buildPermissionsObject( Title::newFromText( 'User:1.1.1.1' ) ); + $this->assertTrue( $perms->isAllowed( IMinervaPagePermissions::TALK ) ); + } + + public static function switchLanguagePageActionProvider() { + return [ + [ true, true, false, true ], + [ false, false, true, true ], + [ false, false, false, false ], + [ true, false, false, true ], + ]; + } + + /** + * The "switch-language" page action is allowed when: v2 of the page action bar is enabled and + * if the page has interlanguage links or if the $wgMinervaAlwaysShowLanguageButton + * configuration variable is set to truthy. + * + * @dataProvider switchLanguagePageActionProvider + * @covers ::isAllowed + */ + public function testSwitchLanguagePageAction( + $hasLanguages, + $hasVariants, + $minervaAlwaysShowLanguageButton, + $expected + ) { + $out = RequestContext::getMain()->getOutput(); + $out->setLanguageLinks( $hasLanguages ? [ 'pl:StronaTestowa', 'en:TestPage' ] : [] ); + + $languageMock = $this->getMock( \Language::class, [ 'hasVariants' ], [], '', false ); + $languageMock->expects( $this->once() ) + ->method( 'hasVariants' ) + ->willReturn( $hasVariants ); + $title = $this->getMock( Title::class, [ 'isMainPage', 'getPageLanguage' ] ); + $title->expects( $this->once() ) + ->method( 'isMainPage' ) + ->willReturn( false ); + $title->expects( $this->once() ) + ->method( 'getPageLanguage' ) + ->willReturn( $languageMock ); + + $permissions = $this->buildPermissionsObject( + $title, + null, + [], + null, + null, + $out, + $minervaAlwaysShowLanguageButton + ); + + $actual = $permissions->isAllowed( IMinervaPagePermissions::SWITCH_LANGUAGE ); + $this->assertEquals( $expected, $actual ); + } + + /** + * Watch action requires 'viewmywatchlist' and 'editmywatchlist' permissions + * to be grated. Verify that isAllowedAction('watch') returns false when user + * do not have those permissions granted + * @covers ::isAllowed + */ + public function testWatchIsAllowedOnlyWhenWatchlistPermissionsAreGranted() { + $title = Title::newFromText( 'test_watchstar_permissions' ); + + $userMock = $this->getMockBuilder( 'User' ) + ->disableOriginalConstructor() + ->setMethods( [ 'isAllowedAll' ] ) + ->getMock(); + $userMock->expects( $this->once() ) + ->method( 'isAllowedAll' ) + ->with( 'viewmywatchlist', 'editmywatchlist' ) + ->willReturn( false ); + + $perms = $this->buildPermissionsObject( $title, null, [], null, $userMock ); + $this->assertTrue( $perms->isAllowed( IMinervaPagePermissions::TALK ) ); + $this->assertFalse( $perms->isAllowed( IMinervaPagePermissions::WATCH ) ); + } + + /** + * If Title is not watchable, it cannot be watched + * @covers ::isAllowed + */ + public function testCannotWatchNotWatchableTitle() { + $title = $this->getMock( Title::class, [ 'isMainPage', 'isWatchable' ] ); + $title->expects( $this->once() ) + ->method( 'isMainPage' ) + ->willReturn( false ); + $title->expects( $this->once() ) + ->method( 'isWatchable' ) + ->willReturn( false ); + + $permissions = $this->buildPermissionsObject( $title ); + $this->assertEquals( false, $permissions->isAllowed( IMinervaPagePermissions::WATCH ) ); + } + +} diff --git a/tests/phpunit/skins/SkinMinervaPageActionsTest.php b/tests/phpunit/skins/SkinMinervaPageActionsTest.php deleted file mode 100644 index 3c2db8d..0000000 --- a/tests/phpunit/skins/SkinMinervaPageActionsTest.php +++ /dev/null @@ -1,222 +0,0 @@ -doesPageHaveLanguages = $doesPageHaveLanguages; - } - -} - -/** - * @group MinervaNeue - */ -class SkinMinervaPageActionsTest extends MediaWikiTestCase { - - /** - * @var TestSkinMinerva - */ - private $skin; - - protected function setUp() { - parent::setUp(); - - $this->skin = $this->getSkin( Title::newFromText( 'SkinMinervaPageActionsTest' ) ); - } - - /** - * @param Title $title - * @return TestSkinMinerva - */ - private function getSkin( Title $title ) { - $requestContext = RequestContext::getMain(); - $requestContext->setTitle( $title ); - - $result = new TestSkinMinerva(); - $result->setContext( $requestContext ); - - return $result; - } - - /** - * @covers SkinMinerva::isAllowedPageAction - */ - public function testPageActionsArentAllowedWhenOnTheMainPage() { - $skin = $this->getSkin( Title::newMainPage() ); - - $this->assertFalse( $skin->isAllowedPageAction( 'watch' ) ); - $this->assertFalse( $skin->isAllowedPageAction( 'edit' ) ); - - // Check to make sure 'talk' and 'switch-language' are enabled on the Main page. - $this->assertTrue( $skin->isAllowedPageAction( 'talk' ) ); - $this->assertTrue( $skin->isAllowedPageAction( 'switch-language' ) ); - } - - /** - * @covers SkinMinerva::isAllowedPageAction - */ - public function testInvalidPageActionsArentAllowed() { - $this->setMwGlobals( 'wgMinervaPageActions', [] ); - - // By default, the "talk" and "watch" page actions are allowed but are now deemed invalid. - $this->assertFalse( $this->skin->isAllowedPageAction( 'talk' ) ); - $this->assertFalse( $this->skin->isAllowedPageAction( 'watch' ) ); - } - - /** - * @covers SkinMinerva::isAllowedPageAction - */ - public function testValidPageActionsAreAllowed() { - $this->assertTrue( $this->skin->isAllowedPageAction( 'talk' ) ); - $this->assertTrue( $this->skin->isAllowedPageAction( 'watch' ) ); - } - - public static function editPageActionProvider() { - return [ - [ false, false, false ], - [ true, false, false ], - [ true, true, true ] - ]; - } - - /** - * The "edit" page action is allowed when the page doesn't support direct editing via the API. - * - * @dataProvider editPageActionProvider - * @covers SkinMinerva::isAllowedPageAction - */ - public function testEditPageAction( - $supportsDirectEditing, - $supportsDirectApiEditing, - $expected - ) { - $contentHandler = $this->getMockBuilder( 'ContentHandler' ) - ->disableOriginalConstructor() - ->getMock(); - - $contentHandler->method( 'supportsDirectEditing' ) - ->will( $this->returnValue( $supportsDirectEditing ) ); - - $contentHandler->method( 'supportsDirectApiEditing' ) - ->will( $this->returnValue( $supportsDirectApiEditing ) ); - - $this->setService( 'Minerva.ContentHandler', $contentHandler ); - - $this->assertEquals( $expected, $this->skin->isAllowedPageAction( 'edit' ) ); - } - - /** - * @covers SkinMinerva::isAllowedPageAction - */ - public function testPageActionsWhenOnUserPage() { - $userPageHelper = $this->getMockBuilder( SkinUserPageHelper::class ) - ->disableOriginalConstructor() - ->getMock(); - - $skin = $this->getSkin( Title::newFromText( 'User:Admin' ) ); - - $this->setService( 'Minerva.SkinUserPageHelper', $userPageHelper ); - - $this->assertTrue( $skin->isAllowedPageAction( 'talk' ) ); - } - - /** - * @covers SkinMinerva::isAllowedPageAction - */ - public function testPageActionsWhenNotOnUserPage() { - $userPageHelper = $this->getMockBuilder( SkinUserPageHelper::class ) - ->disableOriginalConstructor() - ->getMock(); - - $skin = $this->getSkin( Title::newFromText( 'A_test_page' ) ); - $this->setService( 'Minerva.SkinUserPageHelper', $userPageHelper ); - - $this->assertTrue( $skin->isAllowedPageAction( 'talk' ) ); - } - - /** - * @covers SkinMinerva::isAllowedPageAction - */ - public function testPageActionsWhenOnAnonUserPage() { - $userPageHelper = $this->getMockBuilder( SkinUserPageHelper::class ) - ->disableOriginalConstructor() - ->getMock(); - - $skin = $this->getSkin( Title::newFromText( 'User:1.1.1.1' ) ); - $this->setService( 'Minerva.SkinUserPageHelper', $userPageHelper ); - - $this->assertTrue( $skin->isAllowedPageAction( 'talk' ) ); - } - - public static function switchLanguagePageActionProvider() { - return [ - [ true, false, true ], - [ false, true, true ], - [ false, false, false ], - [ true, false, true ], - ]; - } - - /** - * The "switch-language" page action is allowed when: v2 of the page action bar is enabled and - * if the page has interlanguage links or if the $wgMinervaAlwaysShowLanguageButton - * configuration variable is set to truthy. - * - * @dataProvider switchLanguagePageActionProvider - * @covers SkinMinerva::isAllowedPageAction - */ - public function testSwitchLanguagePageAction( - $doesPageHaveLanguages, - $minervaAlwaysShowLanguageButton, - $expected - ) { - $this->skin->setDoesPageHaveLanguages( $doesPageHaveLanguages ); - $this->setMwGlobals( [ - 'wgMinervaAlwaysShowLanguageButton' => $minervaAlwaysShowLanguageButton, - ] ); - - $this->assertEquals( $expected, $this->skin->isAllowedPageAction( 'switch-language' ) ); - } - - /** - * Watch action requires 'viewmywatchlist' and 'editmywatchlist' permissions - * to be grated. Verify that isAllowedAction('watch') returns false when user - * do not have those permissions granted - * @covers SkinMinerva::isAllowedPageAction - */ - public function testWatchIsAllowedOnlyWhenWatchlistPermissionsAreGranted() { - $title = Title::newFromText( 'test_watchstar_permissions' ); - $requestContext = RequestContext::getMain(); - $requestContext->setTitle( $title ); - $userMock = $this->getMockBuilder( 'User' ) - ->disableOriginalConstructor() - ->setMethods( [ 'isAllowedAll' ] ) - ->getMock(); - $userMock->expects( $this->once() ) - ->method( 'isAllowedAll' ) - ->with( 'viewmywatchlist', 'editmywatchlist' ) - ->willReturn( false ); - $requestContext->setUser( $userMock ); - - $result = new TestSkinMinerva(); - $result->setContext( $requestContext ); - - $this->assertTrue( $this->skin->isAllowedPageAction( 'talk' ) ); - $this->assertFalse( $this->skin->isAllowedPageAction( 'watch' ) ); - } -}