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' ) ); - } -}