mirror of
https://github.com/flarum/core.git
synced 2025-08-05 16:07:34 +02:00
feat: refactor flags extension
This commit is contained in:
@@ -7,25 +7,17 @@
|
|||||||
* LICENSE file that was distributed with this source code.
|
* LICENSE file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Flarum\Api\Controller\AbstractSerializeController;
|
use Flarum\Api\Endpoint;
|
||||||
use Flarum\Api\Controller\ListPostsController;
|
use Flarum\Api\Resource;
|
||||||
use Flarum\Api\Controller\ShowDiscussionController;
|
|
||||||
use Flarum\Api\Controller\ShowPostController;
|
|
||||||
use Flarum\Api\Serializer\CurrentUserSerializer;
|
|
||||||
use Flarum\Api\Serializer\ForumSerializer;
|
|
||||||
use Flarum\Api\Serializer\PostSerializer;
|
|
||||||
use Flarum\Extend;
|
use Flarum\Extend;
|
||||||
use Flarum\Flags\Access\ScopeFlagVisibility;
|
use Flarum\Flags\Access\ScopeFlagVisibility;
|
||||||
use Flarum\Flags\AddCanFlagAttribute;
|
|
||||||
use Flarum\Flags\AddFlagsApiAttributes;
|
|
||||||
use Flarum\Flags\AddNewFlagCountAttribute;
|
|
||||||
use Flarum\Flags\Api\Controller\CreateFlagController;
|
|
||||||
use Flarum\Flags\Api\Controller\DeleteFlagsController;
|
use Flarum\Flags\Api\Controller\DeleteFlagsController;
|
||||||
use Flarum\Flags\Api\Controller\ListFlagsController;
|
use Flarum\Flags\Api\ForumResourceFields;
|
||||||
use Flarum\Flags\Api\Serializer\FlagSerializer;
|
use Flarum\Flags\Api\PostResourceFields;
|
||||||
|
use Flarum\Flags\Api\Resource\FlagResource;
|
||||||
|
use Flarum\Flags\Api\UserResourceFields;
|
||||||
use Flarum\Flags\Flag;
|
use Flarum\Flags\Flag;
|
||||||
use Flarum\Flags\Listener;
|
use Flarum\Flags\Listener;
|
||||||
use Flarum\Flags\PrepareFlagsApiData;
|
|
||||||
use Flarum\Forum\Content\AssertRegistered;
|
use Flarum\Forum\Content\AssertRegistered;
|
||||||
use Flarum\Post\Event\Deleted;
|
use Flarum\Post\Event\Deleted;
|
||||||
use Flarum\Post\Post;
|
use Flarum\Post\Post;
|
||||||
@@ -41,8 +33,6 @@ return [
|
|||||||
->js(__DIR__.'/js/dist/admin.js'),
|
->js(__DIR__.'/js/dist/admin.js'),
|
||||||
|
|
||||||
(new Extend\Routes('api'))
|
(new Extend\Routes('api'))
|
||||||
->get('/flags', 'flags.index', ListFlagsController::class)
|
|
||||||
->post('/flags', 'flags.create', CreateFlagController::class)
|
|
||||||
->delete('/posts/{id}/flags', 'flags.delete', DeleteFlagsController::class),
|
->delete('/posts/{id}/flags', 'flags.delete', DeleteFlagsController::class),
|
||||||
|
|
||||||
(new Extend\Model(User::class))
|
(new Extend\Model(User::class))
|
||||||
@@ -51,27 +41,26 @@ return [
|
|||||||
(new Extend\Model(Post::class))
|
(new Extend\Model(Post::class))
|
||||||
->hasMany('flags', Flag::class, 'post_id'),
|
->hasMany('flags', Flag::class, 'post_id'),
|
||||||
|
|
||||||
(new Extend\ApiSerializer(PostSerializer::class))
|
(new Extend\ApiResource(FlagResource::class)),
|
||||||
->hasMany('flags', FlagSerializer::class)
|
|
||||||
->attribute('canFlag', AddCanFlagAttribute::class),
|
|
||||||
|
|
||||||
(new Extend\ApiSerializer(CurrentUserSerializer::class))
|
(new Extend\ApiResource(Resource\PostResource::class))
|
||||||
->attribute('newFlagCount', AddNewFlagCountAttribute::class),
|
->fields(PostResourceFields::class),
|
||||||
|
|
||||||
(new Extend\ApiSerializer(ForumSerializer::class))
|
(new Extend\ApiResource(Resource\UserResource::class))
|
||||||
->attributes(AddFlagsApiAttributes::class),
|
->fields(UserResourceFields::class),
|
||||||
|
|
||||||
(new Extend\ApiController(ShowDiscussionController::class))
|
(new Extend\ApiResource(Resource\ForumResource::class))
|
||||||
->addInclude(['posts.flags', 'posts.flags.user']),
|
->fields(ForumResourceFields::class),
|
||||||
|
|
||||||
(new Extend\ApiController(ListPostsController::class))
|
(new Extend\ApiResource(Resource\DiscussionResource::class))
|
||||||
->addInclude(['flags', 'flags.user']),
|
->endpoint(Endpoint\Show::class, function (Endpoint\Show $endpoint) {
|
||||||
|
return $endpoint->addDefaultInclude(['posts.flags', 'posts.flags.user']);
|
||||||
|
}),
|
||||||
|
|
||||||
(new Extend\ApiController(ShowPostController::class))
|
(new Extend\ApiResource(Resource\PostResource::class))
|
||||||
->addInclude(['flags', 'flags.user']),
|
->endpoint([Endpoint\Index::class, Endpoint\Show::class], function (Endpoint\Index|Endpoint\Show $endpoint) {
|
||||||
|
return $endpoint->addDefaultInclude(['flags', 'flags.user']);
|
||||||
(new Extend\ApiController(AbstractSerializeController::class))
|
}),
|
||||||
->prepareDataForSerialization(PrepareFlagsApiData::class),
|
|
||||||
|
|
||||||
(new Extend\Settings())
|
(new Extend\Settings())
|
||||||
->serializeToForum('guidelinesUrl', 'flarum-flags.guidelines_url'),
|
->serializeToForum('guidelinesUrl', 'flarum-flags.guidelines_url'),
|
||||||
|
2
extensions/flags/js/dist/forum.js
generated
vendored
2
extensions/flags/js/dist/forum.js
generated
vendored
File diff suppressed because one or more lines are too long
2
extensions/flags/js/dist/forum.js.map
generated
vendored
2
extensions/flags/js/dist/forum.js.map
generated
vendored
File diff suppressed because one or more lines are too long
@@ -151,7 +151,6 @@ export default class FlagPostModal extends FormModal {
|
|||||||
reason: this.reason() === 'other' ? null : this.reason(),
|
reason: this.reason() === 'other' ? null : this.reason(),
|
||||||
reasonDetail: this.reasonDetail(),
|
reasonDetail: this.reasonDetail(),
|
||||||
relationships: {
|
relationships: {
|
||||||
user: app.session.user,
|
|
||||||
post: this.attrs.post,
|
post: this.attrs.post,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -23,31 +23,26 @@ class ScopeFlagVisibility
|
|||||||
|
|
||||||
public function __invoke(User $actor, Builder $query): void
|
public function __invoke(User $actor, Builder $query): void
|
||||||
{
|
{
|
||||||
if ($this->extensions->isEnabled('flarum-tags')) {
|
$query
|
||||||
$query
|
->whereHas('post', function (Builder $query) use ($actor) {
|
||||||
->select('flags.*')
|
$query->whereVisibleTo($actor);
|
||||||
->leftJoin('posts', 'posts.id', '=', 'flags.post_id')
|
})
|
||||||
->leftJoin('discussions', 'discussions.id', '=', 'posts.discussion_id')
|
->where(function (Builder $query) use ($actor) {
|
||||||
->whereNotExists(function ($query) use ($actor) {
|
if ($this->extensions->isEnabled('flarum-tags')) {
|
||||||
return $query->selectRaw('1')
|
$query
|
||||||
->from('discussion_tag')
|
->select('flags.*')
|
||||||
->whereNotIn('tag_id', function ($query) use ($actor) {
|
->whereHas('post.discussion.tags', function ($query) use ($actor) {
|
||||||
Tag::query()->setQuery($query->from('tags'))->whereHasPermission($actor, 'discussion.viewFlags')->select('tags.id');
|
$query->whereHasPermission($actor, 'discussion.viewFlags');
|
||||||
})
|
});
|
||||||
->whereColumn('discussions.id', 'discussion_id');
|
|
||||||
});
|
|
||||||
|
|
||||||
if (! $actor->hasPermission('discussion.viewFlags')) {
|
if ($actor->hasPermission('discussion.viewFlags')) {
|
||||||
$query->whereExists(function ($query) {
|
$query->orWhereDoesntHave('post.discussion.tags');
|
||||||
return $query->selectRaw('1')
|
}
|
||||||
->from('discussion_tag')
|
}
|
||||||
->whereColumn('discussions.id', 'discussion_id');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $actor->hasPermission('discussion.viewFlags')) {
|
if (! $actor->hasPermission('discussion.viewFlags')) {
|
||||||
$query->orWhere('flags.user_id', $actor->id);
|
$query->orWhere('flags.user_id', $actor->id);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,39 +0,0 @@
|
|||||||
<?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\Flags;
|
|
||||||
|
|
||||||
use Flarum\Api\Serializer\PostSerializer;
|
|
||||||
use Flarum\Post\Post;
|
|
||||||
use Flarum\Settings\SettingsRepositoryInterface;
|
|
||||||
use Flarum\User\User;
|
|
||||||
|
|
||||||
class AddCanFlagAttribute
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected SettingsRepositoryInterface $settings
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __invoke(PostSerializer $serializer, Post $post): bool
|
|
||||||
{
|
|
||||||
return $serializer->getActor()->can('flag', $post) && $this->checkFlagOwnPostSetting($serializer->getActor(), $post);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function checkFlagOwnPostSetting(User $actor, Post $post): bool
|
|
||||||
{
|
|
||||||
if ($actor->id === $post->user_id) {
|
|
||||||
// If $actor is the post author, check to see if the setting is enabled
|
|
||||||
return (bool) $this->settings->get('flarum-flags.can_flag_own');
|
|
||||||
}
|
|
||||||
|
|
||||||
// $actor is not the post author
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,40 +0,0 @@
|
|||||||
<?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\Flags;
|
|
||||||
|
|
||||||
use Flarum\Api\Serializer\ForumSerializer;
|
|
||||||
use Flarum\Settings\SettingsRepositoryInterface;
|
|
||||||
use Flarum\User\User;
|
|
||||||
|
|
||||||
class AddFlagsApiAttributes
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected SettingsRepositoryInterface $settings
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __invoke(ForumSerializer $serializer): array
|
|
||||||
{
|
|
||||||
$attributes = [
|
|
||||||
'canViewFlags' => $serializer->getActor()->hasPermissionLike('discussion.viewFlags')
|
|
||||||
];
|
|
||||||
|
|
||||||
if ($attributes['canViewFlags']) {
|
|
||||||
$attributes['flagCount'] = (int) $this->getFlagCount($serializer->getActor());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getFlagCount(User $actor): int
|
|
||||||
{
|
|
||||||
return Flag::whereVisibleTo($actor)->distinct()->count('flags.post_id');
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,32 +0,0 @@
|
|||||||
<?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\Flags;
|
|
||||||
|
|
||||||
use Flarum\Api\Serializer\CurrentUserSerializer;
|
|
||||||
use Flarum\User\User;
|
|
||||||
|
|
||||||
class AddNewFlagCountAttribute
|
|
||||||
{
|
|
||||||
public function __invoke(CurrentUserSerializer $serializer, User $user): int
|
|
||||||
{
|
|
||||||
return $this->getNewFlagCount($user);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getNewFlagCount(User $actor): int
|
|
||||||
{
|
|
||||||
$query = Flag::whereVisibleTo($actor);
|
|
||||||
|
|
||||||
if ($time = $actor->read_flags_at) {
|
|
||||||
$query->where('flags.created_at', '>', $time);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $query->distinct()->count('flags.post_id');
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,43 +0,0 @@
|
|||||||
<?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\Flags\Api\Controller;
|
|
||||||
|
|
||||||
use Flarum\Api\Controller\AbstractCreateController;
|
|
||||||
use Flarum\Flags\Api\Serializer\FlagSerializer;
|
|
||||||
use Flarum\Flags\Command\CreateFlag;
|
|
||||||
use Flarum\Flags\Flag;
|
|
||||||
use Flarum\Http\RequestUtil;
|
|
||||||
use Illuminate\Contracts\Bus\Dispatcher;
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
|
||||||
use Tobscure\JsonApi\Document;
|
|
||||||
|
|
||||||
class CreateFlagController extends AbstractCreateController
|
|
||||||
{
|
|
||||||
public ?string $serializer = FlagSerializer::class;
|
|
||||||
|
|
||||||
public array $include = [
|
|
||||||
'post',
|
|
||||||
'post.flags',
|
|
||||||
'user'
|
|
||||||
];
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
protected Dispatcher $bus
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function data(ServerRequestInterface $request, Document $document): Flag
|
|
||||||
{
|
|
||||||
return $this->bus->dispatch(
|
|
||||||
new CreateFlag(RequestUtil::getActor($request), Arr::get($request->getParsedBody(), 'data', []))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,81 +0,0 @@
|
|||||||
<?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\Flags\Api\Controller;
|
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Flarum\Api\Controller\AbstractListController;
|
|
||||||
use Flarum\Flags\Api\Serializer\FlagSerializer;
|
|
||||||
use Flarum\Flags\Flag;
|
|
||||||
use Flarum\Http\RequestUtil;
|
|
||||||
use Flarum\Http\UrlGenerator;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
|
||||||
use Tobscure\JsonApi\Document;
|
|
||||||
|
|
||||||
class ListFlagsController extends AbstractListController
|
|
||||||
{
|
|
||||||
public ?string $serializer = FlagSerializer::class;
|
|
||||||
|
|
||||||
public array $include = [
|
|
||||||
'user',
|
|
||||||
'post',
|
|
||||||
'post.user',
|
|
||||||
'post.discussion'
|
|
||||||
];
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
protected UrlGenerator $url
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function data(ServerRequestInterface $request, Document $document): iterable
|
|
||||||
{
|
|
||||||
$actor = RequestUtil::getActor($request);
|
|
||||||
|
|
||||||
$actor->assertRegistered();
|
|
||||||
|
|
||||||
$actor->read_flags_at = Carbon::now();
|
|
||||||
$actor->save();
|
|
||||||
|
|
||||||
$limit = $this->extractLimit($request);
|
|
||||||
$offset = $this->extractOffset($request);
|
|
||||||
$include = $this->extractInclude($request);
|
|
||||||
|
|
||||||
if (in_array('post.user', $include)) {
|
|
||||||
$include[] = 'post.user.groups';
|
|
||||||
}
|
|
||||||
|
|
||||||
$flags = Flag::whereVisibleTo($actor)
|
|
||||||
->latest('flags.created_at')
|
|
||||||
->groupBy('post_id')
|
|
||||||
->limit($limit + 1)
|
|
||||||
->offset($offset)
|
|
||||||
->get();
|
|
||||||
|
|
||||||
$this->loadRelations($flags, $include, $request);
|
|
||||||
|
|
||||||
$flags = $flags->all();
|
|
||||||
|
|
||||||
$areMoreResults = false;
|
|
||||||
|
|
||||||
if (count($flags) > $limit) {
|
|
||||||
array_pop($flags);
|
|
||||||
$areMoreResults = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addPaginationData(
|
|
||||||
$document,
|
|
||||||
$request,
|
|
||||||
$this->url->to('api')->route('flags.index'),
|
|
||||||
$areMoreResults ? null : 0
|
|
||||||
);
|
|
||||||
|
|
||||||
return $flags;
|
|
||||||
}
|
|
||||||
}
|
|
31
extensions/flags/src/Api/ForumResourceFields.php
Normal file
31
extensions/flags/src/Api/ForumResourceFields.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Api;
|
||||||
|
|
||||||
|
use Flarum\Api\Context;
|
||||||
|
use Flarum\Api\Schema;
|
||||||
|
use Flarum\Flags\Flag;
|
||||||
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
|
|
||||||
|
class ForumResourceFields
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected SettingsRepositoryInterface $settings
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Schema\Boolean::make('canViewFlags')
|
||||||
|
->get(function (object $model, Context $context) {
|
||||||
|
return $context->getActor()->hasPermissionLike('discussion.viewFlags');
|
||||||
|
}),
|
||||||
|
Schema\Integer::make('flagCount')
|
||||||
|
->visible(fn (object $model, Context $context) => $context->getActor()->hasPermissionLike('discussion.viewFlags'))
|
||||||
|
->get(function (object $model, Context $context) {
|
||||||
|
return Flag::whereVisibleTo($context->getActor())->distinct()->count('flags.post_id');
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
35
extensions/flags/src/Api/PostResourceFields.php
Normal file
35
extensions/flags/src/Api/PostResourceFields.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Api;
|
||||||
|
|
||||||
|
use Flarum\Api\Context;
|
||||||
|
use Flarum\Api\Schema;
|
||||||
|
use Flarum\Post\Post;
|
||||||
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
|
|
||||||
|
class PostResourceFields
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected SettingsRepositoryInterface $settings
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Schema\Boolean::make('canFlag')
|
||||||
|
->get(function (Post $post, Context $context) {
|
||||||
|
$actor = $context->getActor();
|
||||||
|
|
||||||
|
return $actor->can('flag', $post) && (
|
||||||
|
// $actor is not the post author
|
||||||
|
$actor->id !== $post->user_id
|
||||||
|
// If $actor is the post author, check to see if the setting is enabled
|
||||||
|
|| ((bool) $this->settings->get('flarum-flags.can_flag_own'))
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
Schema\Relationship\ToMany::make('flags')
|
||||||
|
->includable(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
155
extensions/flags/src/Api/Resource/FlagResource.php
Normal file
155
extensions/flags/src/Api/Resource/FlagResource.php
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Api\Resource;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Flarum\Api\Context as FlarumContext;
|
||||||
|
use Flarum\Api\Endpoint;
|
||||||
|
use Flarum\Api\Resource\AbstractDatabaseResource;
|
||||||
|
use Flarum\Api\Schema;
|
||||||
|
use Flarum\Api\Sort\SortColumn;
|
||||||
|
use Flarum\Flags\Event\Created;
|
||||||
|
use Flarum\Flags\Flag;
|
||||||
|
use Flarum\Http\Exception\InvalidParameterException;
|
||||||
|
use Flarum\Locale\TranslatorInterface;
|
||||||
|
use Flarum\Post\CommentPost;
|
||||||
|
use Flarum\Post\Post;
|
||||||
|
use Flarum\Post\PostRepository;
|
||||||
|
use Flarum\Settings\SettingsRepositoryInterface;
|
||||||
|
use Flarum\User\Exception\PermissionDeniedException;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Tobyz\JsonApiServer\Context;
|
||||||
|
|
||||||
|
class FlagResource extends AbstractDatabaseResource
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected PostRepository $posts,
|
||||||
|
protected TranslatorInterface $translator,
|
||||||
|
protected SettingsRepositoryInterface $settings,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function type(): string
|
||||||
|
{
|
||||||
|
return 'flags';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function model(): string
|
||||||
|
{
|
||||||
|
return Flag::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function query(Context $context): object
|
||||||
|
{
|
||||||
|
if ($context->collection instanceof self && $context->endpoint instanceof Endpoint\Index) {
|
||||||
|
$query = Flag::query()->groupBy('post_id');
|
||||||
|
|
||||||
|
$this->scope($query, $context);
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::query($context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scope(Builder $query, Context $context): void
|
||||||
|
{
|
||||||
|
$query->whereVisibleTo($context->getActor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newModel(Context $context): object
|
||||||
|
{
|
||||||
|
if ($context->collection instanceof self && $context->endpoint instanceof Endpoint\Create) {
|
||||||
|
Flag::unguard();
|
||||||
|
|
||||||
|
return Flag::query()->firstOrNew([
|
||||||
|
'post_id' => (int) Arr::get($context->body(), 'data.relationships.post.data.id'),
|
||||||
|
'user_id' => $context->getActor()->id
|
||||||
|
], [
|
||||||
|
'type' => 'user',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::newModel($context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function endpoints(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Endpoint\Create::make()
|
||||||
|
->authenticated()
|
||||||
|
->defaultInclude(['post', 'post.flags', 'user']),
|
||||||
|
Endpoint\Index::make()
|
||||||
|
->authenticated()
|
||||||
|
->defaultInclude(['user', 'post', 'post.user', 'post.discussion'])
|
||||||
|
->defaultSort('-createdAt')
|
||||||
|
->paginate()
|
||||||
|
->after(function (FlarumContext $context, $data) {
|
||||||
|
$actor = $context->getActor();
|
||||||
|
|
||||||
|
$actor->read_flags_at = Carbon::now();
|
||||||
|
$actor->save();
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fields(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Schema\Str::make('type'),
|
||||||
|
Schema\Str::make('reason')
|
||||||
|
->writableOnCreate()
|
||||||
|
->nullable()
|
||||||
|
->requiredOnCreateWithout(['reasonDetail'])
|
||||||
|
->validationMessages([
|
||||||
|
'reason.required_without' => $this->translator->trans('flarum-flags.forum.flag_post.reason_missing_message'),
|
||||||
|
]),
|
||||||
|
Schema\Str::make('reasonDetail')
|
||||||
|
->writableOnCreate()
|
||||||
|
->nullable()
|
||||||
|
->requiredOnCreateWithout(['reason'])
|
||||||
|
->validationMessages([
|
||||||
|
'reasonDetail.required_without' => $this->translator->trans('flarum-flags.forum.flag_post.reason_missing_message'),
|
||||||
|
]),
|
||||||
|
Schema\DateTime::make('createdAt'),
|
||||||
|
|
||||||
|
Schema\Relationship\ToOne::make('post')
|
||||||
|
->includable()
|
||||||
|
->writable(fn (Flag $flag, FlarumContext $context) => $context->endpoint instanceof Endpoint\Create)
|
||||||
|
->set(function (Flag $flag, Post $post, FlarumContext $context) {
|
||||||
|
if (! ($post instanceof CommentPost)) {
|
||||||
|
throw new InvalidParameterException;
|
||||||
|
}
|
||||||
|
|
||||||
|
$actor = $context->getActor();
|
||||||
|
|
||||||
|
$actor->assertCan('flag', $post);
|
||||||
|
|
||||||
|
if ($actor->id === $post->user_id && ! $this->settings->get('flarum-flags.can_flag_own')) {
|
||||||
|
throw new PermissionDeniedException;
|
||||||
|
}
|
||||||
|
|
||||||
|
$flag->post_id = $post->id;
|
||||||
|
}),
|
||||||
|
Schema\Relationship\ToOne::make('user')
|
||||||
|
->includable(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sorts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
SortColumn::make('createdAt'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function created(object $model, Context $context): ?object
|
||||||
|
{
|
||||||
|
$this->events->dispatch(new Created($model, $context->getActor(), $context->body()));
|
||||||
|
|
||||||
|
return parent::created($model, $context);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,48 +0,0 @@
|
|||||||
<?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\Flags\Api\Serializer;
|
|
||||||
|
|
||||||
use Flarum\Api\Serializer\AbstractSerializer;
|
|
||||||
use Flarum\Api\Serializer\BasicUserSerializer;
|
|
||||||
use Flarum\Api\Serializer\PostSerializer;
|
|
||||||
use Flarum\Flags\Flag;
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use Tobscure\JsonApi\Relationship;
|
|
||||||
|
|
||||||
class FlagSerializer extends AbstractSerializer
|
|
||||||
{
|
|
||||||
protected $type = 'flags';
|
|
||||||
|
|
||||||
protected function getDefaultAttributes(object|array $model): array
|
|
||||||
{
|
|
||||||
if (! ($model instanceof Flag)) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
$this::class.' can only serialize instances of '.Flag::class
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'type' => $model->type,
|
|
||||||
'reason' => $model->reason,
|
|
||||||
'reasonDetail' => $model->reason_detail,
|
|
||||||
'createdAt' => $this->formatDate($model->created_at),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function post(Flag $flag): ?Relationship
|
|
||||||
{
|
|
||||||
return $this->hasOne($flag, PostSerializer::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function user(Flag $flag): ?Relationship
|
|
||||||
{
|
|
||||||
return $this->hasOne($flag, BasicUserSerializer::class);
|
|
||||||
}
|
|
||||||
}
|
|
29
extensions/flags/src/Api/UserResourceFields.php
Normal file
29
extensions/flags/src/Api/UserResourceFields.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Flarum\Flags\Api;
|
||||||
|
|
||||||
|
use Flarum\Api\Context;
|
||||||
|
use Flarum\Api\Schema;
|
||||||
|
use Flarum\Flags\Flag;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class UserResourceFields
|
||||||
|
{
|
||||||
|
public function __invoke(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Schema\Integer::make('newFlagCount')
|
||||||
|
->visible(fn (User $user, Context $context) => $context->getActor()->id === $user->id)
|
||||||
|
->get(function (User $user, Context $context) {
|
||||||
|
$actor = $context->getActor();
|
||||||
|
$query = Flag::whereVisibleTo($actor);
|
||||||
|
|
||||||
|
if ($time = $actor->read_flags_at) {
|
||||||
|
$query->where('flags.created_at', '>', $time);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->distinct()->count('flags.post_id');
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -1,79 +0,0 @@
|
|||||||
<?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\Flags\Command;
|
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Flarum\Flags\Event\Created;
|
|
||||||
use Flarum\Flags\Flag;
|
|
||||||
use Flarum\Foundation\ValidationException;
|
|
||||||
use Flarum\Locale\TranslatorInterface;
|
|
||||||
use Flarum\Post\CommentPost;
|
|
||||||
use Flarum\Post\PostRepository;
|
|
||||||
use Flarum\Settings\SettingsRepositoryInterface;
|
|
||||||
use Flarum\User\Exception\PermissionDeniedException;
|
|
||||||
use Illuminate\Events\Dispatcher;
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Tobscure\JsonApi\Exception\InvalidParameterException;
|
|
||||||
|
|
||||||
class CreateFlagHandler
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
protected PostRepository $posts,
|
|
||||||
protected TranslatorInterface $translator,
|
|
||||||
protected SettingsRepositoryInterface $settings,
|
|
||||||
protected Dispatcher $events
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(CreateFlag $command): Flag
|
|
||||||
{
|
|
||||||
$actor = $command->actor;
|
|
||||||
$data = $command->data;
|
|
||||||
|
|
||||||
$postId = Arr::get($data, 'relationships.post.data.id');
|
|
||||||
$post = $this->posts->findOrFail($postId, $actor);
|
|
||||||
|
|
||||||
if (! ($post instanceof CommentPost)) {
|
|
||||||
throw new InvalidParameterException;
|
|
||||||
}
|
|
||||||
|
|
||||||
$actor->assertCan('flag', $post);
|
|
||||||
|
|
||||||
if ($actor->id === $post->user_id && ! $this->settings->get('flarum-flags.can_flag_own')) {
|
|
||||||
throw new PermissionDeniedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Arr::get($data, 'attributes.reason') === null && Arr::get($data, 'attributes.reasonDetail') === '') {
|
|
||||||
throw new ValidationException([
|
|
||||||
'message' => $this->translator->trans('flarum-flags.forum.flag_post.reason_missing_message')
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Flag::unguard();
|
|
||||||
|
|
||||||
$flag = Flag::firstOrNew([
|
|
||||||
'post_id' => $post->id,
|
|
||||||
'user_id' => $actor->id
|
|
||||||
]);
|
|
||||||
|
|
||||||
$flag->post_id = $post->id;
|
|
||||||
$flag->user_id = $actor->id;
|
|
||||||
$flag->type = 'user';
|
|
||||||
$flag->reason = Arr::get($data, 'attributes.reason');
|
|
||||||
$flag->reason_detail = Arr::get($data, 'attributes.reasonDetail');
|
|
||||||
$flag->created_at = Carbon::now();
|
|
||||||
|
|
||||||
$flag->save();
|
|
||||||
|
|
||||||
$this->events->dispatch(new Created($flag, $actor, $data));
|
|
||||||
|
|
||||||
return $flag;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -31,6 +31,10 @@ class Flag extends AbstractModel
|
|||||||
{
|
{
|
||||||
use ScopeVisibilityTrait;
|
use ScopeVisibilityTrait;
|
||||||
|
|
||||||
|
public $timestamps = true;
|
||||||
|
|
||||||
|
public const UPDATED_AT = null;
|
||||||
|
|
||||||
protected $casts = ['created_at' => 'datetime'];
|
protected $casts = ['created_at' => 'datetime'];
|
||||||
|
|
||||||
public function post(): BelongsTo
|
public function post(): BelongsTo
|
||||||
|
@@ -1,64 +0,0 @@
|
|||||||
<?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\Flags;
|
|
||||||
|
|
||||||
use Flarum\Api\Controller;
|
|
||||||
use Flarum\Flags\Api\Controller\CreateFlagController;
|
|
||||||
use Flarum\Http\RequestUtil;
|
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
|
||||||
|
|
||||||
class PrepareFlagsApiData
|
|
||||||
{
|
|
||||||
public function __invoke(Controller\AbstractSerializeController $controller, mixed $data, ServerRequestInterface $request): void
|
|
||||||
{
|
|
||||||
// For any API action that allows the 'flags' relationship to be
|
|
||||||
// included, we need to preload this relationship onto the data (Post
|
|
||||||
// models) so that we can selectively expose only the flags that the
|
|
||||||
// user has permission to view.
|
|
||||||
if ($controller instanceof Controller\ShowDiscussionController) {
|
|
||||||
if ($data->relationLoaded('posts')) {
|
|
||||||
$posts = $data->getRelation('posts');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($controller instanceof Controller\ListPostsController) {
|
|
||||||
$posts = $data->all();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($controller instanceof Controller\ShowPostController) {
|
|
||||||
$posts = [$data];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($controller instanceof CreateFlagController) {
|
|
||||||
$posts = [$data->post];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($posts)) {
|
|
||||||
$actor = RequestUtil::getActor($request);
|
|
||||||
$postsWithPermission = [];
|
|
||||||
|
|
||||||
foreach ($posts as $post) {
|
|
||||||
if (is_object($post)) {
|
|
||||||
$post->setRelation('flags', null);
|
|
||||||
|
|
||||||
if ($actor->can('viewFlags', $post->discussion)) {
|
|
||||||
$postsWithPermission[] = $post;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($postsWithPermission)) {
|
|
||||||
(new Collection($postsWithPermission))
|
|
||||||
->load('flags', 'flags.user');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -51,6 +51,7 @@ class ListTest extends TestCase
|
|||||||
['id' => 1, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
['id' => 1, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
['id' => 2, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
['id' => 2, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
['id' => 3, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
['id' => 3, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 4, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>', 'is_private' => true],
|
||||||
],
|
],
|
||||||
'flags' => [
|
'flags' => [
|
||||||
['id' => 1, 'post_id' => 1, 'user_id' => 1],
|
['id' => 1, 'post_id' => 1, 'user_id' => 1],
|
||||||
@@ -58,6 +59,7 @@ class ListTest extends TestCase
|
|||||||
['id' => 3, 'post_id' => 1, 'user_id' => 3],
|
['id' => 3, 'post_id' => 1, 'user_id' => 3],
|
||||||
['id' => 4, 'post_id' => 2, 'user_id' => 2],
|
['id' => 4, 'post_id' => 2, 'user_id' => 2],
|
||||||
['id' => 5, 'post_id' => 3, 'user_id' => 1],
|
['id' => 5, 'post_id' => 3, 'user_id' => 1],
|
||||||
|
['id' => 6, 'post_id' => 4, 'user_id' => 1],
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -65,7 +67,7 @@ class ListTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
*/
|
*/
|
||||||
public function admin_can_see_one_flag_per_post()
|
public function admin_can_see_one_flag_per_visible_post()
|
||||||
{
|
{
|
||||||
$response = $this->send(
|
$response = $this->send(
|
||||||
$this->request('GET', '/api/flags', [
|
$this->request('GET', '/api/flags', [
|
||||||
@@ -73,9 +75,9 @@ class ListTest extends TestCase
|
|||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals(200, $response->getStatusCode());
|
$this->assertEquals(200, $response->getStatusCode(), $body = $response->getBody()->getContents());
|
||||||
|
|
||||||
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
$data = json_decode($body, true)['data'];
|
||||||
|
|
||||||
$ids = Arr::pluck($data, 'id');
|
$ids = Arr::pluck($data, 'id');
|
||||||
$this->assertEqualsCanonicalizing(['1', '4', '5'], $ids);
|
$this->assertEqualsCanonicalizing(['1', '4', '5'], $ids);
|
||||||
@@ -84,7 +86,7 @@ class ListTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
*/
|
*/
|
||||||
public function regular_user_sees_own_flags()
|
public function regular_user_sees_own_flags_of_visible_posts()
|
||||||
{
|
{
|
||||||
$response = $this->send(
|
$response = $this->send(
|
||||||
$this->request('GET', '/api/flags', [
|
$this->request('GET', '/api/flags', [
|
||||||
@@ -103,7 +105,7 @@ class ListTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
*/
|
*/
|
||||||
public function mod_can_see_one_flag_per_post()
|
public function mod_can_see_one_flag_per_visible_post()
|
||||||
{
|
{
|
||||||
$response = $this->send(
|
$response = $this->send(
|
||||||
$this->request('GET', '/api/flags', [
|
$this->request('GET', '/api/flags', [
|
||||||
|
@@ -50,9 +50,9 @@ class ListWithTagsTest extends TestCase
|
|||||||
],
|
],
|
||||||
'group_permission' => [
|
'group_permission' => [
|
||||||
['group_id' => Group::MODERATOR_ID, 'permission' => 'discussion.viewFlags'],
|
['group_id' => Group::MODERATOR_ID, 'permission' => 'discussion.viewFlags'],
|
||||||
['group_id' => Group::MODERATOR_ID, 'permission' => 'tag2.viewDiscussions'],
|
['group_id' => Group::MODERATOR_ID, 'permission' => 'tag2.viewForum'],
|
||||||
['group_id' => Group::MODERATOR_ID, 'permission' => 'tag3.discussion.viewFlags'],
|
['group_id' => Group::MODERATOR_ID, 'permission' => 'tag3.discussion.viewFlags'],
|
||||||
['group_id' => Group::MODERATOR_ID, 'permission' => 'tag4.viewDiscussions'],
|
['group_id' => Group::MODERATOR_ID, 'permission' => 'tag4.viewForum'],
|
||||||
['group_id' => Group::MODERATOR_ID, 'permission' => 'tag4.discussion.viewFlags'],
|
['group_id' => Group::MODERATOR_ID, 'permission' => 'tag4.discussion.viewFlags'],
|
||||||
],
|
],
|
||||||
'discussions' => [
|
'discussions' => [
|
||||||
@@ -149,9 +149,7 @@ class ListWithTagsTest extends TestCase
|
|||||||
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
$ids = Arr::pluck($data, 'id');
|
$ids = Arr::pluck($data, 'id');
|
||||||
// 7 is included, even though mods can't view discussions.
|
$this->assertEqualsCanonicalizing(['1', '4', '5', '8', '9'], $ids);
|
||||||
// This is because the UI doesnt allow discussions.viewFlags without viewDiscussions.
|
|
||||||
$this->assertEqualsCanonicalizing(['1', '4', '5', '7', '8', '9'], $ids);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -0,0 +1,143 @@
|
|||||||
|
<?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\Flags\Tests\integration\api\posts;
|
||||||
|
|
||||||
|
use Flarum\Group\Group;
|
||||||
|
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||||
|
use Flarum\Testing\integration\TestCase;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
class IncludeFlagsVisibilityTest extends TestCase
|
||||||
|
{
|
||||||
|
use RetrievesAuthorizedUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function setup(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->extension('flarum-tags', 'flarum-flags');
|
||||||
|
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'users' => [
|
||||||
|
$this->normalUser(),
|
||||||
|
[
|
||||||
|
'id' => 3,
|
||||||
|
'username' => 'mod',
|
||||||
|
'password' => '$2y$10$LO59tiT7uggl6Oe23o/O6.utnF6ipngYjvMvaxo1TciKqBttDNKim', // BCrypt hash for "too-obscure"
|
||||||
|
'email' => 'normal2@machine.local',
|
||||||
|
'is_email_confirmed' => 1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 4,
|
||||||
|
'username' => 'tod',
|
||||||
|
'password' => '$2y$10$LO59tiT7uggl6Oe23o/O6.utnF6ipngYjvMvaxo1TciKqBttDNKim', // BCrypt hash for "too-obscure"
|
||||||
|
'email' => 'tod@machine.local',
|
||||||
|
'is_email_confirmed' => 1,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 5,
|
||||||
|
'username' => 'ted',
|
||||||
|
'password' => '$2y$10$LO59tiT7uggl6Oe23o/O6.utnF6ipngYjvMvaxo1TciKqBttDNKim', // BCrypt hash for "too-obscure"
|
||||||
|
'email' => 'ted@machine.local',
|
||||||
|
'is_email_confirmed' => 1,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'group_user' => [
|
||||||
|
['group_id' => 5, 'user_id' => 2],
|
||||||
|
['group_id' => 6, 'user_id' => 3],
|
||||||
|
],
|
||||||
|
'groups' => [
|
||||||
|
['id' => 5, 'name_singular' => 'group5', 'name_plural' => 'group5', 'color' => null, 'icon' => 'fas fa-crown', 'is_hidden' => false],
|
||||||
|
['id' => 6, 'name_singular' => 'group1', 'name_plural' => 'group1', 'color' => null, 'icon' => 'fas fa-cog', 'is_hidden' => false],
|
||||||
|
],
|
||||||
|
'group_permission' => [
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag1.viewForum'],
|
||||||
|
['group_id' => 5, 'permission' => 'tag1.viewForum'],
|
||||||
|
['group_id' => 5, 'permission' => 'discussion.viewFlags'],
|
||||||
|
['group_id' => 6, 'permission' => 'tag1.discussion.viewFlags'],
|
||||||
|
['group_id' => 6, 'permission' => 'tag1.viewForum'],
|
||||||
|
],
|
||||||
|
'tags' => [
|
||||||
|
['id' => 1, 'name' => 'Tag 1', 'slug' => 'tag-1', 'is_primary' => false, 'position' => null, 'parent_id' => null, 'is_restricted' => true],
|
||||||
|
['id' => 2, 'name' => 'Tag 2', 'slug' => 'tag-2', 'is_primary' => true, 'position' => 2, 'parent_id' => null, 'is_restricted' => false],
|
||||||
|
],
|
||||||
|
'discussions' => [
|
||||||
|
['id' => 1, 'title' => 'Test1', 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
['id' => 2, 'title' => 'Test2', 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
],
|
||||||
|
'discussion_tag' => [
|
||||||
|
['discussion_id' => 1, 'tag_id' => 1],
|
||||||
|
['discussion_id' => 2, 'tag_id' => 2],
|
||||||
|
],
|
||||||
|
'posts' => [
|
||||||
|
['id' => 1, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 2, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 3, 'discussion_id' => 1, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
|
||||||
|
['id' => 4, 'discussion_id' => 2, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 5, 'discussion_id' => 2, 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
],
|
||||||
|
'flags' => [
|
||||||
|
['id' => 1, 'post_id' => 1, 'user_id' => 1],
|
||||||
|
['id' => 2, 'post_id' => 1, 'user_id' => 5],
|
||||||
|
['id' => 3, 'post_id' => 1, 'user_id' => 3],
|
||||||
|
['id' => 4, 'post_id' => 2, 'user_id' => 5],
|
||||||
|
['id' => 5, 'post_id' => 3, 'user_id' => 1],
|
||||||
|
|
||||||
|
['id' => 6, 'post_id' => 4, 'user_id' => 1],
|
||||||
|
['id' => 7, 'post_id' => 5, 'user_id' => 5],
|
||||||
|
['id' => 8, 'post_id' => 5, 'user_id' => 5],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider listFlagsIncludesDataProvider
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function user_sees_where_allowed_with_included_tags(int $actorId, array $expectedIncludes)
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/posts', [
|
||||||
|
'authenticatedAs' => $actorId,
|
||||||
|
])->withQueryParams([
|
||||||
|
'include' => 'flags'
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$responseBody = json_decode($response->getBody()->getContents(), true);
|
||||||
|
|
||||||
|
$data = $responseBody['data'];
|
||||||
|
|
||||||
|
$this->assertEquals(['1', '2', '3', '4', '5'], Arr::pluck($data, 'id'));
|
||||||
|
$this->assertEqualsCanonicalizing($expectedIncludes, collect($responseBody['included'] ?? [])
|
||||||
|
->filter(fn($include) => $include['type'] === 'flags')
|
||||||
|
->pluck('id')
|
||||||
|
->map(strval(...))
|
||||||
|
->all()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listFlagsIncludesDataProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'admin_sees_all' => [1, [1, 2, 3, 4, 5, 6, 7, 8]],
|
||||||
|
'user_with_general_permission_sees_where_unrestricted_tag' => [2, [6, 7, 8]],
|
||||||
|
'user_with_tag1_permission_sees_tag1_flags' => [3, [1, 2, 3, 4, 5]],
|
||||||
|
'normal_user_sees_none' => [4, []],
|
||||||
|
'normal_user_sees_own' => [5, [2, 7, 4, 8]],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -12,10 +12,9 @@ use Flarum\Api\Endpoint;
|
|||||||
use Flarum\Api\Resource;
|
use Flarum\Api\Resource;
|
||||||
use Flarum\Api\Schema;
|
use Flarum\Api\Schema;
|
||||||
use Flarum\Discussion\Discussion;
|
use Flarum\Discussion\Discussion;
|
||||||
use Flarum\Discussion\Event\Saving;
|
|
||||||
use Flarum\Discussion\Search\DiscussionSearcher;
|
use Flarum\Discussion\Search\DiscussionSearcher;
|
||||||
use Flarum\Extend;
|
use Flarum\Extend;
|
||||||
use Flarum\Flags\Api\Controller\ListFlagsController;
|
use Flarum\Flags\Api\Resource\FlagResource;
|
||||||
use Flarum\Http\RequestUtil;
|
use Flarum\Http\RequestUtil;
|
||||||
use Flarum\Post\Filter\PostSearcher;
|
use Flarum\Post\Filter\PostSearcher;
|
||||||
use Flarum\Post\Post;
|
use Flarum\Post\Post;
|
||||||
@@ -104,8 +103,13 @@ return [
|
|||||||
return $endpoint->eagerLoad('discussion.tags');
|
return $endpoint->eagerLoad('discussion.tags');
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// (new Extend\ApiController(ListFlagsController::class))
|
(new Extend\Conditional())
|
||||||
// ->load('post.discussion.tags'),
|
->whenExtensionEnabled('flarum-flags', fn () => [
|
||||||
|
(new Extend\ApiResource(FlagResource::class))
|
||||||
|
->endpoint(Endpoint\Index::class, function (Endpoint\Index $endpoint) {
|
||||||
|
return $endpoint->eagerLoad(['post.discussion.tags']);
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
|
||||||
(new Extend\ApiResource(Resource\DiscussionResource::class))
|
(new Extend\ApiResource(Resource\DiscussionResource::class))
|
||||||
->endpoint(
|
->endpoint(
|
||||||
|
@@ -11,9 +11,9 @@ namespace Flarum\Foundation;
|
|||||||
|
|
||||||
use Flarum\Extension\Exception as ExtensionException;
|
use Flarum\Extension\Exception as ExtensionException;
|
||||||
use Flarum\Foundation\ErrorHandling as Handling;
|
use Flarum\Foundation\ErrorHandling as Handling;
|
||||||
|
use Flarum\Http\Exception\InvalidParameterException;
|
||||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
use Illuminate\Validation\ValidationException as IlluminateValidationException;
|
use Illuminate\Validation\ValidationException as IlluminateValidationException;
|
||||||
use Tobscure\JsonApi\Exception\InvalidParameterException;
|
|
||||||
use Tobyz\JsonApiServer\Exception as TobyzJsonApiServerException;
|
use Tobyz\JsonApiServer\Exception as TobyzJsonApiServerException;
|
||||||
|
|
||||||
class ErrorServiceProvider extends AbstractServiceProvider
|
class ErrorServiceProvider extends AbstractServiceProvider
|
||||||
|
@@ -7,15 +7,11 @@
|
|||||||
* LICENSE file that was distributed with this source code.
|
* LICENSE file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Flarum\Flags\Command;
|
namespace Flarum\Http\Exception;
|
||||||
|
|
||||||
use Flarum\User\User;
|
use Exception;
|
||||||
|
|
||||||
class CreateFlag
|
class InvalidParameterException extends Exception
|
||||||
{
|
{
|
||||||
public function __construct(
|
//
|
||||||
public User $actor,
|
|
||||||
public array $data
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user