-
-
Notifications
You must be signed in to change notification settings - Fork 836
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Model Visibility Scoping Extender and Tests (#2460)
- Loading branch information
1 parent
e0437d2
commit 8901073
Showing
19 changed files
with
527 additions
and
145 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<?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\Discussion\Access; | ||
|
||
use Flarum\User\User; | ||
use Illuminate\Database\Eloquent\Builder; | ||
|
||
class ScopeDiscussionVisibility | ||
{ | ||
/** | ||
* @param User $actor | ||
* @param Builder $query | ||
*/ | ||
public function __invoke(User $actor, $query) | ||
{ | ||
if ($actor->cannot('viewDiscussions')) { | ||
$query->whereRaw('FALSE'); | ||
|
||
return; | ||
} | ||
|
||
// Hide private discussions by default. | ||
$query->where(function ($query) use ($actor) { | ||
$query->where('discussions.is_private', false) | ||
->orWhere(function ($query) use ($actor) { | ||
$query->whereVisibleTo($actor, 'viewPrivate'); | ||
}); | ||
}); | ||
|
||
// Hide hidden discussions, unless they are authored by the current | ||
// user, or the current user has permission to view hidden discussions. | ||
if (! $actor->hasPermission('discussion.hide')) { | ||
$query->where(function ($query) use ($actor) { | ||
$query->whereNull('discussions.hidden_at') | ||
->orWhere('discussions.user_id', $actor->id) | ||
->orWhere(function ($query) use ($actor) { | ||
$query->whereVisibleTo($actor, 'hide'); | ||
}); | ||
}); | ||
} | ||
|
||
// Hide discussions with no comments, unless they are authored by the | ||
// current user, or the user is allowed to edit the discussion's posts. | ||
if (! $actor->hasPermission('discussion.editPosts')) { | ||
$query->where(function ($query) use ($actor) { | ||
$query->where('discussions.comment_count', '>', 0) | ||
->orWhere('discussions.user_id', $actor->id) | ||
->orWhere(function ($query) use ($actor) { | ||
$query->whereVisibleTo($actor, 'editPosts'); | ||
}); | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
<?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 (! method_exists($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 | ||
* | ||
* @return self | ||
*/ | ||
public function scope($callback, $ability = 'view') | ||
{ | ||
$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 | ||
* | ||
* @return self | ||
*/ | ||
public function scopeAll($callback) | ||
{ | ||
$this->allScopers[] = $callback; | ||
|
||
return $this; | ||
} | ||
|
||
public function extend(Container $container, Extension $extension = null) | ||
{ | ||
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)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?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\Group\Access; | ||
|
||
use Flarum\User\User; | ||
use Illuminate\Database\Eloquent\Builder; | ||
|
||
class ScopeGroupVisibility | ||
{ | ||
/** | ||
* @param User $actor | ||
* @param Builder $query | ||
*/ | ||
public function __invoke(User $actor, $query) | ||
{ | ||
if ($actor->cannot('viewHiddenGroups')) { | ||
$query->where('is_hidden', false); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.