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
This commit is contained in:
Piotr Miazga 2019-05-24 02:07:27 +02:00 committed by Pmiazga
parent 18a1d48d53
commit 6352190684
9 changed files with 572 additions and 338 deletions

View File

@ -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();
}
}
];

View File

@ -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;

View File

@ -0,0 +1,48 @@
<?php
/**
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace MediaWiki\Minerva\Permissions;
/**
* A wrapper for all available Minerva permissions.
*/
interface IMinervaPagePermissions {
const WATCH = 'watch';
const SWITCH_LANGUAGE = 'switch-language';
const EDIT = 'edit';
const TALK = 'talk';
const HISTORY = 'history';
/**
* Gets whether or not the action is allowed.
*
* @param string $action
* @return bool
*/
public function isAllowed( $action );
/**
* Returns true, if the page can have a talk page and user is logged in.
*
* @return bool
*/
public function isTalkAllowed();
}

View File

@ -0,0 +1,44 @@
<?php
/**
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace MediaWiki\Minerva\Permissions;
/**
* In some context we might not have the Title object, we need a
* NullObject permissions to handle such cases
*
* @link https://phabricator.wikimedia.org/T179833
*/
final class MinervaNoPagePermissions implements IMinervaPagePermissions {
/**
* @inheritDoc
*/
public function isAllowed( $action ) {
return false;
}
/**
* @inheritDoc
*/
public function isTalkAllowed() {
return false;
}
}

View File

@ -0,0 +1,169 @@
<?php
/**
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace MediaWiki\Minerva\Permissions;
use Config;
use ConfigException;
use ContentHandler;
use MediaWiki\Minerva\SkinOptions;
use OutputPage;
use Title;
use User;
/**
* A wrapper for all available Minerva permissions.
*/
final class MinervaPagePermissions implements IMinervaPagePermissions {
/**
* @var Title Current page title
*/
private $title;
/**
* @var Config Extension config
*/
private $config;
/**
* @var User user object
*/
private $user;
/**
* @var ContentHandler
*/
private $contentHandler;
/**
* @var OutputPage just to retrieve list of language links
*/
private $output;
/**
* @var SkinOptions Minerva skin options
*/
private $skinOptions;
/**
* Initialize internal Minerva Permissions system
* @param Title $title Current page title
* @param Config $config Minerva config
* @param User $user Currently logged in user
* @param OutputPage $output Output page used to fetch languages
* @param SkinOptions $skinOptions Skin options`
* @param ContentHandler $contentHandler
*/
public function __construct(
Title $title,
Config $config,
User $user,
OutputPage $output,
SkinOptions $skinOptions,
ContentHandler $contentHandler
) {
$this->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:
* <ul>
* <li>
* the action is disabled (by removing it from the <code>MinervaPageActions</code>
* configuration variable; or
* </li>
* <li>the user is on the main page</li>
* </ul>
*
* 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 <code>$wgMinervaAlwaysShowLanguageButton</code> 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();
}
}

View File

@ -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:
* <ul>
* <li>
* the action is disabled (by removing it from the <code>MinervaPageActions</code>
* configuration variable; or
* </li>
* <li>the user is on the main page</li>
* </ul>
*
* 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 <code>$wgMinervaAlwaysShowLanguageButton</code>
* 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';

View File

@ -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",

View File

@ -0,0 +1,237 @@
<?php
namespace Tests\MediaWiki\Minerva;
use ContentHandler;
use MediaWiki\Minerva\Permissions\IMinervaPagePermissions;
use MediaWiki\Minerva\Permissions\MinervaPagePermissions;
use MediaWiki\Minerva\SkinOptions;
use MediaWikiTestCase;
use OutputPage;
use Title;
use RequestContext;
use User;
/**
* @group MinervaNeue
* @coversDefaultClass \MediaWiki\Minerva\Permissions\MinervaPagePermissions
*/
class MinervaPagePermissionsTest extends MediaWikiTestCase {
private function buildPermissionsObject(
Title $title,
array $actions = null,
array $options = [],
ContentHandler $contentHandler = null,
User $user = null,
OutputPage $outputPage = null,
$alwaysShowLanguageButton = true
) {
$outputPage = $outputPage ?? RequestContext::getMain()->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 <code>$wgMinervaAlwaysShowLanguageButton</code>
* 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 ) );
}
}

View File

@ -1,222 +0,0 @@
<?php
namespace Tests\MediaWiki\Minerva;
use SkinMinerva;
use MediaWikiTestCase;
use Title;
use RequestContext;
use MediaWiki\Minerva\SkinUserPageHelper;
// FIXME: That this class exists is an indicator that at least SkinMinerva#isAllowedPageAction
// should be extracted from SkinMinerva.
// phpcs:ignore MediaWiki.Files.ClassMatchesFilename.NotMatch
class TestSkinMinerva extends SkinMinerva {
public function isAllowedPageAction( $action ) {
return parent::isAllowedPageAction( $action );
}
public function setDoesPageHaveLanguages( $doesPageHaveLanguages ) {
$this->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 <code>$wgMinervaAlwaysShowLanguageButton</code>
* 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' ) );
}
}