mirror of
https://github.com/flarum/framework.git
synced 2025-03-09 19:55:26 +08:00

* Extender docblocks cleanup * Excplicit type hinting in extenders * Bring method under constructor * Mark some classes and methods as internal * Remove beta references Co-authored-by: Clark Winkelmann <clark.winkelmann@gmail.com>
111 lines
3.5 KiB
PHP
111 lines
3.5 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of Flarum.
|
|
*
|
|
* For detailed copyright and license information, please view the
|
|
* LICENSE file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Flarum\Extend;
|
|
|
|
use Exception;
|
|
use Flarum\Extension\Extension;
|
|
use Flarum\Foundation\ContainerUtil;
|
|
use Illuminate\Contracts\Container\Container;
|
|
|
|
/**
|
|
* Model visibility scoping allows us to scope queries based on the current user.
|
|
* The main usage of this is only showing model instances that a user is allowed to see.
|
|
*
|
|
* This is done by running a query through a series of "scoper" callbacks, which apply
|
|
* additional `where`s to the query based on the user.
|
|
*
|
|
* Scopers are classified under an ability. Calling `whereVisibleTo` on a query
|
|
* will apply scopers under the `view` ability. Generally, the main `view` scopers
|
|
* can request scoping with other abilities, which provides an entrypoint for extensions
|
|
* to modify some restriction to a query.
|
|
*
|
|
* Scopers registered via `scopeAll` will apply to all queries under a model, regardless
|
|
* of the ability, and will accept the ability name as an additional argument.
|
|
*/
|
|
class ModelVisibility implements ExtenderInterface
|
|
{
|
|
private $modelClass;
|
|
private $scopers = [];
|
|
private $allScopers = [];
|
|
|
|
/**
|
|
* @param string $modelClass: The ::class attribute of the model you are applying scopers to.
|
|
* This model must extend from \Flarum\Database\AbstractModel,
|
|
* and use \Flarum\Database\ScopeVisibilityTrait.
|
|
*/
|
|
public function __construct(string $modelClass)
|
|
{
|
|
$this->modelClass = $modelClass;
|
|
|
|
if (class_exists($this->modelClass) && ! is_callable([$modelClass, 'registerVisibilityScoper'])) {
|
|
throw new Exception("Model $modelClass cannot be visibility scoped as it does not use Flarum\Database\ScopeVisibilityTrait.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a scoper for a given ability.
|
|
*
|
|
* @param callable|string $callback
|
|
* @param string $ability: Defaults to 'view'.
|
|
*
|
|
* The callback can be a closure or invokable class, and should accept:
|
|
* - \Flarum\User\User $actor
|
|
* - \Illuminate\Database\Eloquent\Builder $query
|
|
*
|
|
* The callback should return void.
|
|
*
|
|
* @return self
|
|
*/
|
|
public function scope($callback, string $ability = 'view'): self
|
|
{
|
|
$this->scopers[$ability][] = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add a scoper scoper that will always run for this model, regardless of requested ability.
|
|
*
|
|
* @param callable|string $callback
|
|
*
|
|
* The callback can be a closure or invokable class, and should accept:
|
|
* - \Flarum\User\User $actor
|
|
* - \Illuminate\Database\Eloquent\Builder $query
|
|
* - string $ability
|
|
*
|
|
* The callback should return void.
|
|
*
|
|
* @return self
|
|
*/
|
|
public function scopeAll($callback): self
|
|
{
|
|
$this->allScopers[] = $callback;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function extend(Container $container, Extension $extension = null)
|
|
{
|
|
if (! class_exists($this->modelClass)) {
|
|
return;
|
|
}
|
|
|
|
foreach ($this->scopers as $ability => $scopers) {
|
|
foreach ($scopers as $scoper) {
|
|
$this->modelClass::registerVisibilityScoper(ContainerUtil::wrapCallback($scoper, $container), $ability);
|
|
}
|
|
}
|
|
|
|
foreach ($this->allScopers as $scoper) {
|
|
$this->modelClass::registerVisibilityScoper(ContainerUtil::wrapCallback($scoper, $container));
|
|
}
|
|
}
|
|
}
|