2018-08-16 04:55:55 +00:00
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* Block restriction interface.
|
|
|
|
|
*
|
|
|
|
|
* 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\Block;
|
|
|
|
|
|
Introduce infrastructure for partial blocks for actions
This adds a new type of block restriction for actions, which extends
AbstractRestriction. Like page and namespace restrictions, action
restrictions are stored in the ipblocks_restrictions table.
Blockable actions are defined in a BlockActionInfo service, with a
method for getting all the blockable actions, getAllBlockActions.
Action blocks are checked for in PermissionManager::checkUserBlock
using DatabaseBlock::appliesToRight. To make this work, this patch
also removes the 'edit' case from AbstractBlock::appliesToRight,
which always returned true. This was incorrect, as blocks do not
always apply to edit, so cases that called appliesToRight('edit')
were fixed before this commit. appliesToRight('edit') now returns
null (i.e. unsure), which is correct because it is not possible to
determine whether a block applies to editing a particular page
without knowing what that page is, and appliesToRight doesn't know
that page.
There are some flags on sitewide blocks that predate partial blocks,
which block particular actions: 'createaccount' and 'sendemail'.
These are still handled in AbstractBlock::appliesToRight, and are
still checked for separately in the peripheral components.
The feature flag $wgEnablePartialActionBlocks must set to true to
enable partial action blocks.
Bug: T279556
Bug: T6995
Change-Id: I17962bb7c4247a12c722e7bc6bcaf8c36efd8600
2021-04-26 23:07:17 +00:00
|
|
|
use MediaWiki\Block\Restriction\ActionRestriction;
|
2018-10-30 18:19:22 +00:00
|
|
|
use MediaWiki\Block\Restriction\NamespaceRestriction;
|
2018-08-16 04:55:55 +00:00
|
|
|
use MediaWiki\Block\Restriction\PageRestriction;
|
|
|
|
|
use MediaWiki\Block\Restriction\Restriction;
|
2021-09-28 17:21:37 +00:00
|
|
|
use MediaWiki\DAO\WikiAwareEntity;
|
2019-03-22 01:42:46 +00:00
|
|
|
use MWException;
|
2019-06-27 20:42:54 +00:00
|
|
|
use stdClass;
|
2018-08-16 04:55:55 +00:00
|
|
|
use Wikimedia\Rdbms\IDatabase;
|
2019-04-11 19:54:10 +00:00
|
|
|
use Wikimedia\Rdbms\ILoadBalancer;
|
2020-01-10 00:00:51 +00:00
|
|
|
use Wikimedia\Rdbms\IResultWrapper;
|
2018-08-16 04:55:55 +00:00
|
|
|
|
2019-04-11 19:54:10 +00:00
|
|
|
class BlockRestrictionStore {
|
2018-08-16 04:55:55 +00:00
|
|
|
|
2018-10-30 18:19:22 +00:00
|
|
|
/**
|
|
|
|
|
* Map of all of the restriction types.
|
|
|
|
|
*/
|
2020-11-20 23:57:04 +00:00
|
|
|
private const TYPES_MAP = [
|
2018-10-30 18:19:22 +00:00
|
|
|
PageRestriction::TYPE_ID => PageRestriction::class,
|
|
|
|
|
NamespaceRestriction::TYPE_ID => NamespaceRestriction::class,
|
Introduce infrastructure for partial blocks for actions
This adds a new type of block restriction for actions, which extends
AbstractRestriction. Like page and namespace restrictions, action
restrictions are stored in the ipblocks_restrictions table.
Blockable actions are defined in a BlockActionInfo service, with a
method for getting all the blockable actions, getAllBlockActions.
Action blocks are checked for in PermissionManager::checkUserBlock
using DatabaseBlock::appliesToRight. To make this work, this patch
also removes the 'edit' case from AbstractBlock::appliesToRight,
which always returned true. This was incorrect, as blocks do not
always apply to edit, so cases that called appliesToRight('edit')
were fixed before this commit. appliesToRight('edit') now returns
null (i.e. unsure), which is correct because it is not possible to
determine whether a block applies to editing a particular page
without knowing what that page is, and appliesToRight doesn't know
that page.
There are some flags on sitewide blocks that predate partial blocks,
which block particular actions: 'createaccount' and 'sendemail'.
These are still handled in AbstractBlock::appliesToRight, and are
still checked for separately in the peripheral components.
The feature flag $wgEnablePartialActionBlocks must set to true to
enable partial action blocks.
Bug: T279556
Bug: T6995
Change-Id: I17962bb7c4247a12c722e7bc6bcaf8c36efd8600
2021-04-26 23:07:17 +00:00
|
|
|
ActionRestriction::TYPE_ID => ActionRestriction::class,
|
2018-10-30 18:19:22 +00:00
|
|
|
];
|
|
|
|
|
|
2019-04-11 19:54:10 +00:00
|
|
|
/**
|
|
|
|
|
* @var ILoadBalancer
|
|
|
|
|
*/
|
|
|
|
|
private $loadBalancer;
|
|
|
|
|
|
2021-09-28 17:21:37 +00:00
|
|
|
/**
|
|
|
|
|
* @var string|false
|
|
|
|
|
*/
|
|
|
|
|
private $wikiId;
|
|
|
|
|
|
2019-06-27 20:42:54 +00:00
|
|
|
/**
|
|
|
|
|
* @param ILoadBalancer $loadBalancer load balancer for acquiring database connections
|
2021-09-28 17:21:37 +00:00
|
|
|
* @param string|false $wikiId
|
2019-04-11 19:54:10 +00:00
|
|
|
*/
|
2021-09-28 17:21:37 +00:00
|
|
|
public function __construct(
|
|
|
|
|
ILoadBalancer $loadBalancer,
|
|
|
|
|
$wikiId = WikiAwareEntity::LOCAL
|
|
|
|
|
) {
|
2019-04-11 19:54:10 +00:00
|
|
|
$this->loadBalancer = $loadBalancer;
|
2021-09-28 17:21:37 +00:00
|
|
|
$this->wikiId = $wikiId;
|
2019-04-11 19:54:10 +00:00
|
|
|
}
|
|
|
|
|
|
2018-08-16 04:55:55 +00:00
|
|
|
/**
|
|
|
|
|
* Retrieves the restrictions from the database by block id.
|
|
|
|
|
*
|
2022-01-17 23:44:47 +00:00
|
|
|
* Passing a $db is @deprecated since 1.38.
|
|
|
|
|
*
|
2019-01-08 12:44:33 +00:00
|
|
|
* @since 1.33
|
2018-08-16 04:55:55 +00:00
|
|
|
* @param int|array $blockId
|
|
|
|
|
* @param IDatabase|null $db
|
|
|
|
|
* @return Restriction[]
|
|
|
|
|
*/
|
2019-04-11 19:54:10 +00:00
|
|
|
public function loadByBlockId( $blockId, IDatabase $db = null ) {
|
2019-01-11 15:49:44 +00:00
|
|
|
if ( $blockId === null || $blockId === [] ) {
|
2018-08-16 04:55:55 +00:00
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-17 23:44:47 +00:00
|
|
|
if ( $db !== null ) {
|
|
|
|
|
wfDeprecatedMsg( 'Old method signature: Passing a $db is no longer supported', '1.38' );
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-28 17:21:37 +00:00
|
|
|
$db = $db ?: $this->loadBalancer->getConnectionRef( DB_REPLICA, [], $this->wikiId );
|
2018-08-16 04:55:55 +00:00
|
|
|
|
|
|
|
|
$result = $db->select(
|
|
|
|
|
[ 'ipblocks_restrictions', 'page' ],
|
|
|
|
|
[ 'ir_ipb_id', 'ir_type', 'ir_value', 'page_namespace', 'page_title' ],
|
|
|
|
|
[ 'ir_ipb_id' => $blockId ],
|
|
|
|
|
__METHOD__,
|
|
|
|
|
[],
|
|
|
|
|
[ 'page' => [ 'LEFT JOIN', [ 'ir_type' => PageRestriction::TYPE_ID, 'ir_value=page_id' ] ] ]
|
|
|
|
|
);
|
|
|
|
|
|
2019-04-11 19:54:10 +00:00
|
|
|
return $this->resultToRestrictions( $result );
|
2018-08-16 04:55:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Inserts the restrictions into the database.
|
|
|
|
|
*
|
2019-01-08 12:44:33 +00:00
|
|
|
* @since 1.33
|
2018-08-16 04:55:55 +00:00
|
|
|
* @param Restriction[] $restrictions
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2019-04-11 19:54:10 +00:00
|
|
|
public function insert( array $restrictions ) {
|
2019-03-22 23:10:41 +00:00
|
|
|
if ( !$restrictions ) {
|
2018-08-16 04:55:55 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$rows = [];
|
|
|
|
|
foreach ( $restrictions as $restriction ) {
|
|
|
|
|
if ( !$restriction instanceof Restriction ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$rows[] = $restriction->toRow();
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-22 23:10:41 +00:00
|
|
|
if ( !$rows ) {
|
2018-08-16 04:55:55 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-28 17:21:37 +00:00
|
|
|
$dbw = $this->loadBalancer->getConnectionRef( DB_PRIMARY, [], $this->wikiId );
|
2018-08-16 04:55:55 +00:00
|
|
|
|
2019-03-22 21:12:09 +00:00
|
|
|
$dbw->insert(
|
2018-08-16 04:55:55 +00:00
|
|
|
'ipblocks_restrictions',
|
|
|
|
|
$rows,
|
|
|
|
|
__METHOD__,
|
|
|
|
|
[ 'IGNORE' ]
|
|
|
|
|
);
|
2019-03-22 21:12:09 +00:00
|
|
|
|
|
|
|
|
return true;
|
2018-08-16 04:55:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Updates the list of restrictions. This method does not allow removing all
|
|
|
|
|
* of the restrictions. To do that, use ::deleteByBlockId().
|
|
|
|
|
*
|
2019-01-08 12:44:33 +00:00
|
|
|
* @since 1.33
|
2018-08-16 04:55:55 +00:00
|
|
|
* @param Restriction[] $restrictions
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2019-04-11 19:54:10 +00:00
|
|
|
public function update( array $restrictions ) {
|
2021-09-28 17:21:37 +00:00
|
|
|
$dbw = $this->loadBalancer->getConnectionRef( DB_PRIMARY, [], $this->wikiId );
|
2018-08-16 04:55:55 +00:00
|
|
|
|
|
|
|
|
$dbw->startAtomic( __METHOD__ );
|
|
|
|
|
|
|
|
|
|
// Organize the restrictions by blockid.
|
2019-04-11 19:54:10 +00:00
|
|
|
$restrictionList = $this->restrictionsByBlockId( $restrictions );
|
2018-08-16 04:55:55 +00:00
|
|
|
|
|
|
|
|
// Load the existing restrictions and organize by block id. Any block ids
|
|
|
|
|
// that were passed into this function will be used to load all of the
|
|
|
|
|
// existing restrictions. This list might be the same, or may be completely
|
|
|
|
|
// different.
|
|
|
|
|
$existingList = [];
|
|
|
|
|
$blockIds = array_keys( $restrictionList );
|
|
|
|
|
if ( !empty( $blockIds ) ) {
|
|
|
|
|
$result = $dbw->select(
|
2018-11-01 19:37:46 +00:00
|
|
|
[ 'ipblocks_restrictions' ],
|
2018-08-16 04:55:55 +00:00
|
|
|
[ 'ir_ipb_id', 'ir_type', 'ir_value' ],
|
|
|
|
|
[ 'ir_ipb_id' => $blockIds ],
|
|
|
|
|
__METHOD__,
|
|
|
|
|
[ 'FOR UPDATE' ]
|
|
|
|
|
);
|
|
|
|
|
|
2019-04-11 19:54:10 +00:00
|
|
|
$existingList = $this->restrictionsByBlockId(
|
|
|
|
|
$this->resultToRestrictions( $result )
|
2018-08-16 04:55:55 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$result = true;
|
|
|
|
|
// Perform the actions on a per block-id basis.
|
|
|
|
|
foreach ( $restrictionList as $blockId => $blockRestrictions ) {
|
|
|
|
|
// Insert all of the restrictions first, ignoring ones that already exist.
|
2019-04-11 19:54:10 +00:00
|
|
|
$success = $this->insert( $blockRestrictions );
|
2018-08-16 04:55:55 +00:00
|
|
|
|
|
|
|
|
// Update the result. The first false is the result, otherwise, true.
|
|
|
|
|
$result = $success && $result;
|
|
|
|
|
|
2019-04-11 19:54:10 +00:00
|
|
|
$restrictionsToRemove = $this->restrictionsToRemove(
|
2018-08-16 04:55:55 +00:00
|
|
|
$existingList[$blockId] ?? [],
|
|
|
|
|
$restrictions
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ( empty( $restrictionsToRemove ) ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-11 19:54:10 +00:00
|
|
|
$success = $this->delete( $restrictionsToRemove );
|
2018-08-16 04:55:55 +00:00
|
|
|
|
|
|
|
|
// Update the result. The first false is the result, otherwise, true.
|
|
|
|
|
$result = $success && $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$dbw->endAtomic( __METHOD__ );
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Updates the list of restrictions by parent id.
|
|
|
|
|
*
|
2019-01-08 12:44:33 +00:00
|
|
|
* @since 1.33
|
2018-08-16 04:55:55 +00:00
|
|
|
* @param int $parentBlockId
|
|
|
|
|
* @param Restriction[] $restrictions
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2019-04-11 19:54:10 +00:00
|
|
|
public function updateByParentBlockId( $parentBlockId, array $restrictions ) {
|
2018-08-16 04:55:55 +00:00
|
|
|
// If removing all of the restrictions, then just delete them all.
|
|
|
|
|
if ( empty( $restrictions ) ) {
|
2019-04-11 19:54:10 +00:00
|
|
|
return $this->deleteByParentBlockId( $parentBlockId );
|
2018-08-16 04:55:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$parentBlockId = (int)$parentBlockId;
|
|
|
|
|
|
2021-09-28 17:21:37 +00:00
|
|
|
$db = $this->loadBalancer->getConnectionRef( DB_PRIMARY, [], $this->wikiId );
|
2018-08-16 04:55:55 +00:00
|
|
|
|
|
|
|
|
$db->startAtomic( __METHOD__ );
|
|
|
|
|
|
|
|
|
|
$blockIds = $db->selectFieldValues(
|
|
|
|
|
'ipblocks',
|
|
|
|
|
'ipb_id',
|
|
|
|
|
[ 'ipb_parent_block_id' => $parentBlockId ],
|
|
|
|
|
__METHOD__,
|
|
|
|
|
[ 'FOR UPDATE' ]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$result = true;
|
|
|
|
|
foreach ( $blockIds as $id ) {
|
2019-04-11 19:54:10 +00:00
|
|
|
$success = $this->update( $this->setBlockId( $id, $restrictions ) );
|
2018-08-16 04:55:55 +00:00
|
|
|
// Update the result. The first false is the result, otherwise, true.
|
|
|
|
|
$result = $success && $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$db->endAtomic( __METHOD__ );
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete the restrictions.
|
|
|
|
|
*
|
2019-01-08 12:44:33 +00:00
|
|
|
* @since 1.33
|
2019-06-27 20:42:54 +00:00
|
|
|
* @param Restriction[] $restrictions
|
2018-08-16 04:55:55 +00:00
|
|
|
* @throws MWException
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2019-04-11 19:54:10 +00:00
|
|
|
public function delete( array $restrictions ) {
|
2021-09-28 17:21:37 +00:00
|
|
|
$dbw = $this->loadBalancer->getConnectionRef( DB_PRIMARY, [], $this->wikiId );
|
2018-08-16 04:55:55 +00:00
|
|
|
$result = true;
|
|
|
|
|
foreach ( $restrictions as $restriction ) {
|
|
|
|
|
if ( !$restriction instanceof Restriction ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$success = $dbw->delete(
|
|
|
|
|
'ipblocks_restrictions',
|
|
|
|
|
// The restriction row is made up of a compound primary key. Therefore,
|
|
|
|
|
// the row and the delete conditions are the same.
|
|
|
|
|
$restriction->toRow(),
|
|
|
|
|
__METHOD__
|
|
|
|
|
);
|
|
|
|
|
// Update the result. The first false is the result, otherwise, true.
|
|
|
|
|
$result = $success && $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-05-13 14:18:07 +00:00
|
|
|
* Delete the restrictions by block ID.
|
2018-08-16 04:55:55 +00:00
|
|
|
*
|
2019-01-08 12:44:33 +00:00
|
|
|
* @since 1.33
|
2018-08-16 04:55:55 +00:00
|
|
|
* @param int|array $blockId
|
|
|
|
|
* @throws MWException
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2019-04-11 19:54:10 +00:00
|
|
|
public function deleteByBlockId( $blockId ) {
|
2021-09-28 17:21:37 +00:00
|
|
|
$dbw = $this->loadBalancer->getConnectionRef( DB_PRIMARY, [], $this->wikiId );
|
2018-08-16 04:55:55 +00:00
|
|
|
return $dbw->delete(
|
|
|
|
|
'ipblocks_restrictions',
|
|
|
|
|
[ 'ir_ipb_id' => $blockId ],
|
|
|
|
|
__METHOD__
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2019-05-13 14:18:07 +00:00
|
|
|
* Delete the restrictions by parent block ID.
|
2018-08-16 04:55:55 +00:00
|
|
|
*
|
2019-01-08 12:44:33 +00:00
|
|
|
* @since 1.33
|
2018-08-16 04:55:55 +00:00
|
|
|
* @param int|array $parentBlockId
|
|
|
|
|
* @throws MWException
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2019-04-11 19:54:10 +00:00
|
|
|
public function deleteByParentBlockId( $parentBlockId ) {
|
2021-09-28 17:21:37 +00:00
|
|
|
$dbw = $this->loadBalancer->getConnectionRef( DB_PRIMARY, [], $this->wikiId );
|
2018-08-16 04:55:55 +00:00
|
|
|
return $dbw->deleteJoin(
|
|
|
|
|
'ipblocks_restrictions',
|
|
|
|
|
'ipblocks',
|
|
|
|
|
'ir_ipb_id',
|
|
|
|
|
'ipb_id',
|
|
|
|
|
[ 'ipb_parent_block_id' => $parentBlockId ],
|
|
|
|
|
__METHOD__
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Checks if two arrays of Restrictions are effectively equal. This is a loose
|
|
|
|
|
* equality check as the restrictions do not have to contain the same block
|
|
|
|
|
* ids.
|
|
|
|
|
*
|
2019-01-08 12:44:33 +00:00
|
|
|
* @since 1.33
|
2018-08-16 04:55:55 +00:00
|
|
|
* @param Restriction[] $a
|
|
|
|
|
* @param Restriction[] $b
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2019-04-11 19:54:10 +00:00
|
|
|
public function equals( array $a, array $b ) {
|
2021-02-10 22:31:02 +00:00
|
|
|
$filter = static function ( $restriction ) {
|
2018-08-16 04:55:55 +00:00
|
|
|
return $restriction instanceof Restriction;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Ensure that every item in the array is a Restriction. This prevents a
|
|
|
|
|
// fatal error from calling Restriction::getHash if something in the array
|
|
|
|
|
// is not a restriction.
|
|
|
|
|
$a = array_filter( $a, $filter );
|
|
|
|
|
$b = array_filter( $b, $filter );
|
|
|
|
|
|
|
|
|
|
$aCount = count( $a );
|
|
|
|
|
$bCount = count( $b );
|
|
|
|
|
|
|
|
|
|
// If the count is different, then they are obviously a different set.
|
|
|
|
|
if ( $aCount !== $bCount ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If both sets contain no items, then they are the same set.
|
|
|
|
|
if ( $aCount === 0 && $bCount === 0 ) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-10 22:31:02 +00:00
|
|
|
$hasher = static function ( $r ) {
|
2018-08-16 04:55:55 +00:00
|
|
|
return $r->getHash();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$aHashes = array_map( $hasher, $a );
|
|
|
|
|
$bHashes = array_map( $hasher, $b );
|
|
|
|
|
|
|
|
|
|
sort( $aHashes );
|
|
|
|
|
sort( $bHashes );
|
|
|
|
|
|
|
|
|
|
return $aHashes === $bHashes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the blockId on a set of restrictions and return a new set.
|
|
|
|
|
*
|
2019-01-08 12:44:33 +00:00
|
|
|
* @since 1.33
|
2018-08-16 04:55:55 +00:00
|
|
|
* @param int $blockId
|
|
|
|
|
* @param Restriction[] $restrictions
|
|
|
|
|
* @return Restriction[]
|
|
|
|
|
*/
|
2019-04-11 19:54:10 +00:00
|
|
|
public function setBlockId( $blockId, array $restrictions ) {
|
2018-08-16 04:55:55 +00:00
|
|
|
$blockRestrictions = [];
|
|
|
|
|
|
|
|
|
|
foreach ( $restrictions as $restriction ) {
|
|
|
|
|
if ( !$restriction instanceof Restriction ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clone the restriction so any references to the current restriction are
|
|
|
|
|
// not suddenly changed to a different blockId.
|
|
|
|
|
$restriction = clone $restriction;
|
|
|
|
|
$restriction->setBlockId( $blockId );
|
|
|
|
|
|
|
|
|
|
$blockRestrictions[] = $restriction;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $blockRestrictions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the restrictions that should be removed, which are existing
|
|
|
|
|
* restrictions that are not in the new list of restrictions.
|
|
|
|
|
*
|
|
|
|
|
* @param Restriction[] $existing
|
|
|
|
|
* @param Restriction[] $new
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
2019-04-11 19:54:10 +00:00
|
|
|
private function restrictionsToRemove( array $existing, array $new ) {
|
2021-02-10 22:31:02 +00:00
|
|
|
return array_filter( $existing, static function ( $e ) use ( $new ) {
|
2018-08-16 04:55:55 +00:00
|
|
|
foreach ( $new as $restriction ) {
|
|
|
|
|
if ( !$restriction instanceof Restriction ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $restriction->equals( $e ) ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
} );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Converts an array of restrictions to an associative array of restrictions
|
|
|
|
|
* where the keys are the block ids.
|
|
|
|
|
*
|
|
|
|
|
* @param Restriction[] $restrictions
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
2019-04-11 19:54:10 +00:00
|
|
|
private function restrictionsByBlockId( array $restrictions ) {
|
2018-08-16 04:55:55 +00:00
|
|
|
$blockRestrictions = [];
|
|
|
|
|
|
|
|
|
|
foreach ( $restrictions as $restriction ) {
|
|
|
|
|
// Ensure that all of the items in the array are restrictions.
|
|
|
|
|
if ( !$restriction instanceof Restriction ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( !isset( $blockRestrictions[$restriction->getBlockId()] ) ) {
|
|
|
|
|
$blockRestrictions[$restriction->getBlockId()] = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$blockRestrictions[$restriction->getBlockId()][] = $restriction;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $blockRestrictions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert an Result Wrapper to an array of restrictions.
|
|
|
|
|
*
|
|
|
|
|
* @param IResultWrapper $result
|
|
|
|
|
* @return Restriction[]
|
|
|
|
|
*/
|
2019-04-11 19:54:10 +00:00
|
|
|
private function resultToRestrictions( IResultWrapper $result ) {
|
2018-08-16 04:55:55 +00:00
|
|
|
$restrictions = [];
|
|
|
|
|
foreach ( $result as $row ) {
|
2019-04-11 19:54:10 +00:00
|
|
|
$restriction = $this->rowToRestriction( $row );
|
2018-08-16 04:55:55 +00:00
|
|
|
|
|
|
|
|
if ( !$restriction ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$restrictions[] = $restriction;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $restrictions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert a result row from the database into a restriction object.
|
|
|
|
|
*
|
2019-06-27 20:42:54 +00:00
|
|
|
* @param stdClass $row
|
2018-08-16 04:55:55 +00:00
|
|
|
* @return Restriction|null
|
|
|
|
|
*/
|
2019-06-27 20:42:54 +00:00
|
|
|
private function rowToRestriction( stdClass $row ) {
|
2020-11-20 23:57:04 +00:00
|
|
|
if ( array_key_exists( (int)$row->ir_type, self::TYPES_MAP ) ) {
|
|
|
|
|
$class = self::TYPES_MAP[ (int)$row->ir_type ];
|
2018-10-30 18:19:22 +00:00
|
|
|
return call_user_func( [ $class, 'newFromRow' ], $row );
|
2018-08-16 04:55:55 +00:00
|
|
|
}
|
2018-10-30 18:19:22 +00:00
|
|
|
|
|
|
|
|
return null;
|
2018-08-16 04:55:55 +00:00
|
|
|
}
|
|
|
|
|
}
|