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:
parent
18a1d48d53
commit
6352190684
|
@ -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();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 ) );
|
||||
}
|
||||
|
||||
}
|
|
@ -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' ) );
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue