mirror of
https://github.com/flarum/core.git
synced 2025-07-26 11:10:41 +02:00
Massive refactor
- Use contextual namespaces within Flarum\Core - Clean up and docblock everything - Refactor Activity/Notification blueprint stuff - Refactor Formatter stuff - Refactor Search stuff - Upgrade to JSON-API 1.0 - Removed “addedPosts” and “removedPosts” relationships from discussion API. This was used for adding/removing event posts after renaming a discussion etc. Instead we should make an additional request to get all new posts Todo: - Fix Extenders and extensions - Get rid of repository interfaces - Fix other bugs I’ve inevitably introduced
This commit is contained in:
@@ -1,33 +1,31 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
<?php namespace Flarum\Api;
|
||||
|
||||
use Flarum\Core\Model;
|
||||
|
||||
class AccessToken extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $table = 'access_tokens';
|
||||
|
||||
/**
|
||||
* Use a custom primary key for this model.
|
||||
*
|
||||
* @var boolean
|
||||
* @var bool
|
||||
*/
|
||||
public $incrementing = false;
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $dates = ['created_at', 'expires_at'];
|
||||
|
||||
/**
|
||||
* Generate an access token for the specified user.
|
||||
*
|
||||
* @param int $userId
|
||||
* @param int $minutes
|
||||
* @param int $userId
|
||||
* @param int $minutes
|
||||
* @return static
|
||||
*/
|
||||
public static function generate($userId, $minutes = 60)
|
||||
@@ -49,6 +47,6 @@ class AccessToken extends Model
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\User');
|
||||
return $this->belongsTo('Flarum\Core\Users\User');
|
||||
}
|
||||
}
|
@@ -7,7 +7,7 @@ interface ActionInterface
|
||||
/**
|
||||
* Handle a request to the API, returning an HTTP response.
|
||||
*
|
||||
* @param \Flarum\Api\Request $request
|
||||
* @param Request $request
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
public function handle(Request $request);
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php namespace Flarum\Api\Actions\Activity;
|
||||
|
||||
use Flarum\Core\Repositories\UserRepositoryInterface;
|
||||
use Flarum\Core\Repositories\ActivityRepositoryInterface;
|
||||
use Flarum\Core\Users\UserRepositoryInterface;
|
||||
use Flarum\Core\Activity\ActivityRepositoryInterface;
|
||||
use Flarum\Api\Actions\SerializeCollectionAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Tobscure\JsonApi\Document;
|
||||
@@ -9,12 +9,12 @@ use Tobscure\JsonApi\Document;
|
||||
class IndexAction extends SerializeCollectionAction
|
||||
{
|
||||
/**
|
||||
* @var \Flarum\Core\Repositories\UserRepositoryInterface
|
||||
* @var UserRepositoryInterface
|
||||
*/
|
||||
protected $users;
|
||||
|
||||
/**
|
||||
* @var \Flarum\Core\Repositories\ActivityRepositoryInterface
|
||||
* @var ActivityRepositoryInterface
|
||||
*/
|
||||
protected $activity;
|
||||
|
||||
@@ -58,10 +58,8 @@ class IndexAction extends SerializeCollectionAction
|
||||
public static $sort;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Flarum\Core\Repositories\UserRepositoryInterface $users
|
||||
* @param \Flarum\Core\Repositories\ActivityRepositoryInterface $activity
|
||||
* @param UserRepositoryInterface $users
|
||||
* @param ActivityRepositoryInterface $activity
|
||||
*/
|
||||
public function __construct(UserRepositoryInterface $users, ActivityRepositoryInterface $activity)
|
||||
{
|
||||
@@ -73,17 +71,24 @@ class IndexAction extends SerializeCollectionAction
|
||||
* Get the activity results, ready to be serialized and assigned to the
|
||||
* document response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
$actor = $request->actor->getUser();
|
||||
$userId = $request->get('filter.user');
|
||||
$actor = $request->actor;
|
||||
|
||||
$user = $this->users->findOrFail($request->get('users'), $actor);
|
||||
$user = $this->users->findOrFail($userId, $actor);
|
||||
|
||||
return $this->activity->findByUser($user->id, $actor, $request->limit, $request->offset, $request->get('type'))
|
||||
return $this->activity->findByUser(
|
||||
$user->id,
|
||||
$actor,
|
||||
$request->limit,
|
||||
$request->offset,
|
||||
$request->get('filter.type')
|
||||
)
|
||||
->load($request->include);
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,7 @@
|
||||
<?php namespace Flarum\Api\Actions\Discussions;
|
||||
|
||||
use Flarum\Core\Commands\StartDiscussionCommand;
|
||||
use Flarum\Core\Commands\ReadDiscussionCommand;
|
||||
use Flarum\Core\Models\Forum;
|
||||
use Flarum\Core\Discussions\Commands\StartDiscussion;
|
||||
use Flarum\Core\Discussions\Commands\ReadDiscussion;
|
||||
use Flarum\Api\Actions\CreateAction as BaseCreateAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
@@ -12,17 +11,10 @@ class CreateAction extends BaseCreateAction
|
||||
/**
|
||||
* The command bus.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Bus\Dispatcher
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
/**
|
||||
* The default forum instance.
|
||||
*
|
||||
* @var \Flarum\Core\Models\Forum
|
||||
*/
|
||||
protected $forum;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
@@ -67,35 +59,33 @@ class CreateAction extends BaseCreateAction
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
|
||||
* @param \Flarum\Core\Models\Forum $forum
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus, Forum $forum)
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
$this->bus = $bus;
|
||||
$this->forum = $forum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a discussion according to input from the API request.
|
||||
*
|
||||
* @param JsonApiRequest $request
|
||||
* @return \Flarum\Core\Models\Model
|
||||
* @return \Flarum\Core\Discussions\Discussion
|
||||
*/
|
||||
protected function create(JsonApiRequest $request)
|
||||
{
|
||||
$user = $request->actor->getUser();
|
||||
$actor = $request->actor;
|
||||
|
||||
$discussion = $this->bus->dispatch(
|
||||
new StartDiscussionCommand($user, $this->forum, $request->get('data'))
|
||||
new StartDiscussion($actor, $request->get('data'))
|
||||
);
|
||||
|
||||
// After creating the discussion, we assume that the user has seen all
|
||||
// of the posts in the discussion; thus, we will mark the discussion
|
||||
// as read if they are logged in.
|
||||
if ($user->exists) {
|
||||
if ($actor->exists) {
|
||||
$this->bus->dispatch(
|
||||
new ReadDiscussionCommand($discussion->id, $user, 1)
|
||||
new ReadDiscussion($discussion->id, $actor, 1)
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Discussions;
|
||||
|
||||
use Flarum\Core\Commands\DeleteDiscussionCommand;
|
||||
use Flarum\Core\Discussions\Commands\DeleteDiscussion;
|
||||
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
|
||||
use Flarum\Api\Request;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
@@ -8,16 +8,12 @@ use Illuminate\Contracts\Bus\Dispatcher;
|
||||
class DeleteAction extends BaseDeleteAction
|
||||
{
|
||||
/**
|
||||
* The command bus.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Bus\Dispatcher
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
@@ -25,15 +21,16 @@ class DeleteAction extends BaseDeleteAction
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a discussion.
|
||||
*
|
||||
* @param \Flarum\Api\Request $request
|
||||
* @return void
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function delete(Request $request)
|
||||
{
|
||||
$id = $request->get('id');
|
||||
$actor = $request->actor;
|
||||
$input = $request->all();
|
||||
|
||||
$this->bus->dispatch(
|
||||
new DeleteDiscussionCommand($request->get('id'), $request->actor->getUser())
|
||||
new DeleteDiscussion($id, $actor, $input)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php namespace Flarum\Api\Actions\Discussions;
|
||||
|
||||
use Flarum\Core\Search\Discussions\DiscussionSearchCriteria;
|
||||
use Flarum\Core\Search\Discussions\DiscussionSearcher;
|
||||
use Flarum\Core\Search\SearchCriteria;
|
||||
use Flarum\Core\Discussions\Search\DiscussionSearcher;
|
||||
use Flarum\Api\Actions\SerializeCollectionAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Flarum\Http\UrlGeneratorInterface;
|
||||
@@ -10,16 +10,12 @@ use Tobscure\JsonApi\Document;
|
||||
class IndexAction extends SerializeCollectionAction
|
||||
{
|
||||
/**
|
||||
* The discussion searcher.
|
||||
*
|
||||
* @var \Flarum\Core\Search\Discussions\DiscussionSearcher
|
||||
* @var DiscussionSearcher
|
||||
*/
|
||||
protected $searcher;
|
||||
|
||||
/**
|
||||
* The URL generator.
|
||||
*
|
||||
* @var \Flarum\Http\UrlGeneratorInterface
|
||||
* @var UrlGeneratorInterface
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
@@ -67,10 +63,8 @@ class IndexAction extends SerializeCollectionAction
|
||||
public static $sort;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Flarum\Core\Search\Discussions\DiscussionSearcher $searcher
|
||||
* @param \Flarum\Http\UrlGeneratorInterface $url
|
||||
* @param DiscussionSearcher $searcher
|
||||
* @param UrlGeneratorInterface $url
|
||||
*/
|
||||
public function __construct(DiscussionSearcher $searcher, UrlGeneratorInterface $url)
|
||||
{
|
||||
@@ -82,21 +76,23 @@ class IndexAction extends SerializeCollectionAction
|
||||
* Get the discussion results, ready to be serialized and assigned to the
|
||||
* document response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
$criteria = new DiscussionSearchCriteria(
|
||||
$request->actor->getUser(),
|
||||
$request->get('q'),
|
||||
$criteria = new SearchCriteria(
|
||||
$request->actor,
|
||||
$request->get('filter.q'),
|
||||
$request->sort
|
||||
);
|
||||
|
||||
$load = array_merge($request->include, ['state']);
|
||||
|
||||
$results = $this->searcher->search($criteria, $request->limit, $request->offset, $load);
|
||||
|
||||
// TODO: add query params (filter, sort, include) to the pagination URLs
|
||||
static::addPaginationLinks(
|
||||
$document,
|
||||
$request,
|
||||
@@ -104,6 +100,6 @@ class IndexAction extends SerializeCollectionAction
|
||||
$results->areMoreResults()
|
||||
);
|
||||
|
||||
return $results->getDiscussions();
|
||||
return $results->getResults();
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php namespace Flarum\Api\Actions\Discussions;
|
||||
|
||||
use Flarum\Core\Repositories\DiscussionRepositoryInterface;
|
||||
use Flarum\Core\Repositories\PostRepositoryInterface;
|
||||
use Flarum\Core\Discussions\DiscussionRepositoryInterface;
|
||||
use Flarum\Core\Posts\PostRepositoryInterface;
|
||||
use Flarum\Api\Actions\SerializeResourceAction;
|
||||
use Flarum\Api\Actions\Posts\GetsPosts;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
@@ -12,15 +12,10 @@ class ShowAction extends SerializeResourceAction
|
||||
use GetsPosts;
|
||||
|
||||
/**
|
||||
* @var \Flarum\Core\Repositories\DiscussionRepositoryInterface
|
||||
* @var \Flarum\Core\Discussions\DiscussionRepositoryInterface
|
||||
*/
|
||||
protected $discussions;
|
||||
|
||||
/**
|
||||
* @var \Flarum\Core\Repositories\PostRepositoryInterface
|
||||
*/
|
||||
protected $posts;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
@@ -69,8 +64,8 @@ class ShowAction extends SerializeResourceAction
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Flarum\Core\Repositories\DiscussionRepositoryInterface $discussions
|
||||
* @param \Flarum\Core\Repositories\PostRepositoryInterface $posts
|
||||
* @param DiscussionRepositoryInterface $discussions
|
||||
* @param PostRepositoryInterface $posts
|
||||
*/
|
||||
public function __construct(DiscussionRepositoryInterface $discussions, PostRepositoryInterface $posts)
|
||||
{
|
||||
@@ -82,25 +77,29 @@ class ShowAction extends SerializeResourceAction
|
||||
* Get a single discussion, ready to be serialized and assigned to the
|
||||
* JsonApi response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Flarum\Core\Discussions\Discussion
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
$user = $request->actor->getUser();
|
||||
$discussionId = $request->get('id');
|
||||
$actor = $request->actor;
|
||||
|
||||
$discussion = $this->discussions->findOrFail($request->get('id'), $user);
|
||||
$discussion = $this->discussions->findOrFail($discussionId, $actor);
|
||||
|
||||
$discussion->posts_ids = $discussion->visiblePosts($user)->orderBy('time')->lists('id');
|
||||
$discussion->posts_ids = $discussion->postsVisibleTo($actor)->orderBy('time')->lists('id');
|
||||
|
||||
// TODO: Refactor to be simpler, and get posts straight from the
|
||||
// discussion's postsVisibleTo relation method.
|
||||
if (in_array('posts', $request->include)) {
|
||||
$length = strlen($prefix = 'posts.');
|
||||
$relations = array_filter(array_map(function ($relationship) use ($prefix, $length) {
|
||||
return substr($relationship, 0, $length) === $prefix ? substr($relationship, $length) : false;
|
||||
$prefixLength = strlen($prefix = 'posts.');
|
||||
|
||||
$postRelations = array_filter(array_map(function ($relation) use ($prefix, $prefixLength) {
|
||||
return substr($relation, 0, $prefixLength) === $prefix ? substr($relation, $prefixLength) : false;
|
||||
}, $request->include));
|
||||
|
||||
$discussion->posts = $this->getPosts($request, ['discussion_id' => $discussion->id])->load($relations);
|
||||
$discussion->posts = $this->getPosts($request, ['discussion_id' => $discussion->id])->load($postRelations);
|
||||
}
|
||||
|
||||
return $discussion;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php namespace Flarum\Api\Actions\Discussions;
|
||||
|
||||
use Flarum\Core\Commands\EditDiscussionCommand;
|
||||
use Flarum\Core\Commands\ReadDiscussionCommand;
|
||||
use Flarum\Core\Discussions\Commands\EditDiscussion;
|
||||
use Flarum\Core\Discussions\Commands\ReadDiscussion;
|
||||
use Flarum\Api\Actions\SerializeResourceAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
@@ -10,7 +10,7 @@ use Tobscure\JsonApi\Document;
|
||||
class UpdateAction extends SerializeResourceAction
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Bus\Dispatcher
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
@@ -22,11 +22,7 @@ class UpdateAction extends SerializeResourceAction
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $include = [
|
||||
'addedPosts' => true,
|
||||
'addedPosts.user' => true,
|
||||
'addedPosts.discussion' => true
|
||||
];
|
||||
public static $include = [];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
@@ -54,9 +50,7 @@ class UpdateAction extends SerializeResourceAction
|
||||
public static $sort;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
@@ -67,24 +61,25 @@ class UpdateAction extends SerializeResourceAction
|
||||
* Update a discussion according to input from the API request, and return
|
||||
* it ready to be serialized and assigned to the JsonApi response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Flarum\Core\Discussions\Discussion
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
$user = $request->actor->getUser();
|
||||
$actor = $request->actor;
|
||||
$discussionId = $request->get('id');
|
||||
$data = $request->get('data');
|
||||
|
||||
if ($data = array_except($request->get('data'), ['readNumber'])) {
|
||||
$discussion = $this->bus->dispatch(
|
||||
new EditDiscussionCommand($discussionId, $user, $data)
|
||||
);
|
||||
}
|
||||
$discussion = $this->bus->dispatch(
|
||||
new EditDiscussion($discussionId, $actor, $data)
|
||||
);
|
||||
|
||||
if ($readNumber = $request->get('data.readNumber')) {
|
||||
// TODO: Refactor the ReadDiscussion (state) command into EditDiscussion?
|
||||
// That's what extensions will do anyway.
|
||||
if ($readNumber = array_get($data, 'attributes.readNumber')) {
|
||||
$state = $this->bus->dispatch(
|
||||
new ReadDiscussionCommand($discussionId, $user, $readNumber)
|
||||
new ReadDiscussion($discussionId, $actor, $readNumber)
|
||||
);
|
||||
|
||||
$discussion = $state->discussion;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
use Flarum\Api\Request;
|
||||
use Flarum\Core\Repositories\UserRepositoryInterface;
|
||||
use Flarum\Core\Commands\RequestPasswordResetCommand;
|
||||
use Flarum\Core\Commands\RequestPasswordReset;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Zend\Diactoros\Response\EmptyResponse;
|
||||
|
||||
@@ -29,7 +29,7 @@ class ForgotAction extends JsonApiAction
|
||||
$email = $request->get('email');
|
||||
|
||||
$this->bus->dispatch(
|
||||
new RequestPasswordResetCommand($email)
|
||||
new RequestPasswordReset($email)
|
||||
);
|
||||
|
||||
return new EmptyResponse();
|
||||
|
@@ -45,9 +45,9 @@ class ShowAction extends SerializeResourceAction
|
||||
* Get the forum, ready to be serialized and assigned to the JsonApi
|
||||
* response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @return array
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Flarum\Core\Forum
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Groups;
|
||||
|
||||
use Flarum\Core\Models\Group;
|
||||
use Flarum\Core\Users\Group;
|
||||
use Flarum\Api\Actions\SerializeCollectionAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Tobscure\JsonApi\Document;
|
||||
@@ -46,12 +46,12 @@ class IndexAction extends SerializeCollectionAction
|
||||
* Get the groups, ready to be serialized and assigned to the document
|
||||
* response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
return Group::get();
|
||||
return Group::all();
|
||||
}
|
||||
}
|
||||
|
@@ -12,11 +12,12 @@ abstract class JsonApiAction implements ActionInterface
|
||||
* Handle an API request and return an API response, handling any relevant
|
||||
* (API-related) exceptions that are thrown.
|
||||
*
|
||||
* @param \Flarum\Api\Request $request
|
||||
* @param Request $request
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
public function handle(Request $request)
|
||||
{
|
||||
// TODO: Move this error handling code to middleware?
|
||||
try {
|
||||
return $this->respond($request);
|
||||
} catch (ValidationFailureException $e) {
|
||||
@@ -38,7 +39,7 @@ abstract class JsonApiAction implements ActionInterface
|
||||
/**
|
||||
* Handle an API request and return an API response.
|
||||
*
|
||||
* @param \Flarum\Api\Request $request
|
||||
* @param Request $request
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
abstract protected function respond(Request $request);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Notifications;
|
||||
|
||||
use Flarum\Core\Repositories\NotificationRepositoryInterface;
|
||||
use Flarum\Core\Notifications\NotificationRepositoryInterface;
|
||||
use Flarum\Core\Exceptions\PermissionDeniedException;
|
||||
use Flarum\Api\Actions\SerializeCollectionAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
@@ -9,7 +9,7 @@ use Tobscure\JsonApi\Document;
|
||||
class IndexAction extends SerializeCollectionAction
|
||||
{
|
||||
/**
|
||||
* @var \Flarum\Core\Repositories\NotificationRepositoryInterface
|
||||
* @var NotificationRepositoryInterface
|
||||
*/
|
||||
protected $notifications;
|
||||
|
||||
@@ -55,7 +55,7 @@ class IndexAction extends SerializeCollectionAction
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Flarum\Core\Repositories\NotificationRepositoryInterface $notifications
|
||||
* @param NotificationRepositoryInterface $notifications
|
||||
*/
|
||||
public function __construct(NotificationRepositoryInterface $notifications)
|
||||
{
|
||||
@@ -66,22 +66,22 @@ class IndexAction extends SerializeCollectionAction
|
||||
* Get the notification results, ready to be serialized and assigned to the
|
||||
* document response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @throws PermissionDeniedException
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
if (! $request->actor->isAuthenticated()) {
|
||||
$actor = $request->actor;
|
||||
|
||||
if ($actor->isGuest()) {
|
||||
throw new PermissionDeniedException;
|
||||
}
|
||||
|
||||
$user = $request->actor->getUser();
|
||||
$actor->markNotificationsAsRead()->save();
|
||||
|
||||
$user->markNotificationsAsRead()->save();
|
||||
|
||||
return $this->notifications->findByUser($user, $request->limit, $request->offset)
|
||||
return $this->notifications->findByUser($actor, $request->limit, $request->offset)
|
||||
->load($request->include);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Notifications;
|
||||
|
||||
use Flarum\Core\Commands\ReadNotificationCommand;
|
||||
use Flarum\Core\Notifications\Commands\ReadNotification;
|
||||
use Flarum\Api\Actions\SerializeResourceAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
@@ -9,7 +9,7 @@ use Tobscure\JsonApi\Document;
|
||||
class UpdateAction extends SerializeResourceAction
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Bus\Dispatcher
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
@@ -49,9 +49,7 @@ class UpdateAction extends SerializeResourceAction
|
||||
public static $sort;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
@@ -62,14 +60,14 @@ class UpdateAction extends SerializeResourceAction
|
||||
* Mark a notification as read, and return it ready to be serialized and
|
||||
* assigned to the JsonApi response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Flarum\Core\Notifications\Notification
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
return $this->bus->dispatch(
|
||||
new ReadNotificationCommand($request->get('id'), $request->actor->getUser())
|
||||
new ReadNotification($request->get('id'), $request->actor)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php namespace Flarum\Api\Actions\Posts;
|
||||
|
||||
use Flarum\Core\Commands\PostReplyCommand;
|
||||
use Flarum\Core\Commands\ReadDiscussionCommand;
|
||||
use Flarum\Core\Posts\Commands\PostReply;
|
||||
use Flarum\Core\Discussions\Commands\ReadDiscussion;
|
||||
use Flarum\Api\Actions\CreateAction as BaseCreateAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
@@ -9,7 +9,7 @@ use Illuminate\Contracts\Bus\Dispatcher;
|
||||
class CreateAction extends BaseCreateAction
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Bus\Dispatcher
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
@@ -53,7 +53,7 @@ class CreateAction extends BaseCreateAction
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
@@ -64,24 +64,23 @@ class CreateAction extends BaseCreateAction
|
||||
* Reply to a discussion according to input from the API request.
|
||||
*
|
||||
* @param JsonApiRequest $request
|
||||
* @return \Flarum\Core\Models\Model
|
||||
* @return \Flarum\Core\Posts\Post
|
||||
*/
|
||||
protected function create(JsonApiRequest $request)
|
||||
{
|
||||
$user = $request->actor->getUser();
|
||||
|
||||
$discussionId = $request->get('data.links.discussion.linkage.id');
|
||||
$actor = $request->actor;
|
||||
$discussionId = $request->get('data.relationships.discussion.data.id');
|
||||
|
||||
$post = $this->bus->dispatch(
|
||||
new PostReplyCommand($discussionId, $user, $request->get('data'))
|
||||
new PostReply($discussionId, $actor, $request->get('data'))
|
||||
);
|
||||
|
||||
// After replying, we assume that the user has seen all of the posts
|
||||
// in the discussion; thus, we will mark the discussion as read if
|
||||
// they are logged in.
|
||||
if ($user->exists) {
|
||||
if ($actor->exists) {
|
||||
$this->bus->dispatch(
|
||||
new ReadDiscussionCommand($discussionId, $user, $post->number)
|
||||
new ReadDiscussion($discussionId, $actor, $post->number)
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Posts;
|
||||
|
||||
use Flarum\Core\Commands\DeletePostCommand;
|
||||
use Flarum\Core\Posts\Commands\DeletePost;
|
||||
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
|
||||
use Flarum\Api\Request;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
@@ -8,14 +8,12 @@ use Illuminate\Contracts\Bus\Dispatcher;
|
||||
class DeleteAction extends BaseDeleteAction
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Bus\Dispatcher
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
@@ -25,13 +23,13 @@ class DeleteAction extends BaseDeleteAction
|
||||
/**
|
||||
* Delete a post.
|
||||
*
|
||||
* @param \Flarum\Api\Request $request
|
||||
* @param Request $request
|
||||
* @return void
|
||||
*/
|
||||
protected function delete(Request $request)
|
||||
{
|
||||
$this->bus->dispatch(
|
||||
new DeletePostCommand($request->get('id'), $request->actor->getUser())
|
||||
new DeletePost($request->get('id'), $request->actor)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -4,12 +4,22 @@ use Flarum\Api\JsonApiRequest;
|
||||
|
||||
trait GetsPosts
|
||||
{
|
||||
/**
|
||||
* @var \Flarum\Core\Posts\PostRepositoryInterface
|
||||
*/
|
||||
protected $posts;
|
||||
|
||||
/**
|
||||
* @param JsonApiRequest $request
|
||||
* @param array $where
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
protected function getPosts(JsonApiRequest $request, array $where)
|
||||
{
|
||||
$user = $request->actor->getUser();
|
||||
$actor = $request->actor;
|
||||
|
||||
if (isset($where['discussion_id']) && ($near = $request->get('near')) > 1) {
|
||||
$offset = $this->posts->getIndexForNumber($where['discussion_id'], $near, $user);
|
||||
if (isset($where['discussion_id']) && ($near = $request->get('page.near')) > 1) {
|
||||
$offset = $this->posts->getIndexForNumber($where['discussion_id'], $near, $actor);
|
||||
$offset = max(0, $offset - $request->limit / 2);
|
||||
} else {
|
||||
$offset = 0;
|
||||
@@ -17,7 +27,7 @@ trait GetsPosts
|
||||
|
||||
return $this->posts->findWhere(
|
||||
$where,
|
||||
$user,
|
||||
$actor,
|
||||
$request->sort,
|
||||
$request->limit,
|
||||
$offset
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Posts;
|
||||
|
||||
use Flarum\Core\Repositories\PostRepositoryInterface;
|
||||
use Flarum\Core\Posts\PostRepositoryInterface;
|
||||
use Flarum\Api\Actions\SerializeCollectionAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Tobscure\JsonApi\Document;
|
||||
@@ -9,11 +9,6 @@ class IndexAction extends SerializeCollectionAction
|
||||
{
|
||||
use GetsPosts;
|
||||
|
||||
/**
|
||||
* @var \Flarum\Core\Repositories\PostRepositoryInterface
|
||||
*/
|
||||
protected $posts;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
@@ -56,9 +51,7 @@ class IndexAction extends SerializeCollectionAction
|
||||
public static $sort;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Flarum\Core\Repositories\PostRepositoryInterface $posts
|
||||
* @param PostRepositoryInterface $posts
|
||||
*/
|
||||
public function __construct(PostRepositoryInterface $posts)
|
||||
{
|
||||
@@ -69,26 +62,26 @@ class IndexAction extends SerializeCollectionAction
|
||||
* Get the post results, ready to be serialized and assigned to the
|
||||
* document response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
$postIds = (array) $request->get('ids');
|
||||
$user = $request->actor->getUser();
|
||||
$actor = $request->actor;
|
||||
|
||||
if (count($postIds)) {
|
||||
$posts = $this->posts->findByIds($postIds, $user);
|
||||
$posts = $this->posts->findByIds($postIds, $actor);
|
||||
} else {
|
||||
$where = [];
|
||||
if ($discussionId = $request->get('discussions')) {
|
||||
if ($discussionId = $request->get('filter.discussion')) {
|
||||
$where['discussion_id'] = $discussionId;
|
||||
}
|
||||
if ($number = $request->get('number')) {
|
||||
if ($number = $request->get('page.number')) {
|
||||
$where['number'] = $number;
|
||||
}
|
||||
if ($userId = $request->get('users')) {
|
||||
if ($userId = $request->get('filter.user')) {
|
||||
$where['user_id'] = $userId;
|
||||
}
|
||||
$posts = $this->getPosts($request, $where);
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php namespace Flarum\Api\Actions\Posts;
|
||||
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Flarum\Core\Repositories\PostRepositoryInterface;
|
||||
use Flarum\Core\Posts\PostRepositoryInterface;
|
||||
use Flarum\Api\Actions\SerializeResourceAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Tobscure\JsonApi\Document;
|
||||
@@ -9,7 +9,7 @@ use Tobscure\JsonApi\Document;
|
||||
class ShowAction extends SerializeResourceAction
|
||||
{
|
||||
/**
|
||||
* @var \Flarum\Core\Repositories\PostRepositoryInterface
|
||||
* @var PostRepositoryInterface
|
||||
*/
|
||||
protected $posts;
|
||||
|
||||
@@ -55,9 +55,7 @@ class ShowAction extends SerializeResourceAction
|
||||
public static $sort;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Flarum\Core\Repositories\PostRepositoryInterface $posts
|
||||
* @param PostRepositoryInterface $posts
|
||||
*/
|
||||
public function __construct(PostRepositoryInterface $posts)
|
||||
{
|
||||
@@ -68,12 +66,12 @@ class ShowAction extends SerializeResourceAction
|
||||
* Get a single post, ready to be serialized and assigned to the JsonApi
|
||||
* response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Flarum\Core\Posts\Post
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
return $this->posts->findOrFail($request->get('id'), $request->actor->getUser());
|
||||
return $this->posts->findOrFail($request->get('id'), $request->actor);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Posts;
|
||||
|
||||
use Flarum\Core\Commands\EditPostCommand;
|
||||
use Flarum\Core\Posts\Commands\EditPost;
|
||||
use Flarum\Api\Actions\SerializeResourceAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
@@ -9,7 +9,7 @@ use Tobscure\JsonApi\Document;
|
||||
class UpdateAction extends SerializeResourceAction
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Bus\Dispatcher
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
@@ -49,9 +49,7 @@ class UpdateAction extends SerializeResourceAction
|
||||
public static $sort;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
@@ -62,14 +60,14 @@ class UpdateAction extends SerializeResourceAction
|
||||
* Update a post according to input from the API request, and return it
|
||||
* ready to be serialized and assigned to the JsonApi response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
return $this->bus->dispatch(
|
||||
new EditPostCommand($request->get('id'), $request->actor->getUser(), $request->get('data'))
|
||||
new EditPost($request->get('id'), $request->actor, $request->get('data'))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -63,8 +63,8 @@ abstract class SerializeAction extends JsonApiAction
|
||||
/**
|
||||
* Handle an API request and return an API response.
|
||||
*
|
||||
* @param \Flarum\Api\Request $request
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function respond(Request $request)
|
||||
{
|
||||
@@ -78,14 +78,15 @@ abstract class SerializeAction extends JsonApiAction
|
||||
$serializer = new static::$serializer($request->actor, $request->include, $request->link);
|
||||
|
||||
$document->setData($this->serialize($serializer, $data));
|
||||
|
||||
return new JsonResponse($document, 200, ['content-type' => 'application/vnd.api+json']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data to be serialized and assigned to the response document.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function data(JsonApiRequest $request, Document $document);
|
||||
@@ -93,7 +94,7 @@ abstract class SerializeAction extends JsonApiAction
|
||||
/**
|
||||
* Serialize the data as appropriate.
|
||||
*
|
||||
* @param \Tobscure\JsonApi\SerializerInterface $serializer
|
||||
* @param SerializerInterface $serializer
|
||||
* @param array $data
|
||||
* @return \Tobscure\JsonApi\Elements\ElementInterface
|
||||
*/
|
||||
@@ -103,8 +104,8 @@ abstract class SerializeAction extends JsonApiAction
|
||||
* Extract parameters from the request input and assign them to the
|
||||
* request, restricted by the action's specifications.
|
||||
*
|
||||
* @param \Flarum\Api\Request $request
|
||||
* @return void
|
||||
* @param Request $request
|
||||
* @return JsonApiRequest
|
||||
*/
|
||||
protected static function buildJsonApiRequest(Request $request)
|
||||
{
|
||||
@@ -160,8 +161,8 @@ abstract class SerializeAction extends JsonApiAction
|
||||
* Add pagination links to a JSON-API response, based on input parameters
|
||||
* and the default parameters of this action.
|
||||
*
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @param JsonApiRequest $request
|
||||
* @param string $url The base URL to build pagination links with.
|
||||
* @param integer|boolean $total The total number of results (used to build
|
||||
* a 'last' link), or just true if there are more results but how many
|
||||
|
@@ -7,7 +7,7 @@ abstract class SerializeCollectionAction extends SerializeAction
|
||||
/**
|
||||
* Serialize the data as appropriate.
|
||||
*
|
||||
* @param \Tobscure\JsonApi\SerializerInterface $serializer
|
||||
* @param SerializerInterface $serializer
|
||||
* @param array $data
|
||||
* @return \Tobscure\JsonApi\Elements\Collection
|
||||
*/
|
||||
|
@@ -7,7 +7,7 @@ abstract class SerializeResourceAction extends SerializeAction
|
||||
/**
|
||||
* Serialize the data as appropriate.
|
||||
*
|
||||
* @param \Tobscure\JsonApi\SerializerInterface $serializer
|
||||
* @param SerializerInterface $serializer
|
||||
* @param array $data
|
||||
* @return \Tobscure\JsonApi\Elements\Resource
|
||||
*/
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<?php namespace Flarum\Api\Actions;
|
||||
|
||||
use Flarum\Api\Commands\GenerateAccessToken;
|
||||
use Flarum\Api\Request;
|
||||
use Flarum\Core\Commands\GenerateAccessTokenCommand;
|
||||
use Flarum\Core\Repositories\UserRepositoryInterface;
|
||||
use Flarum\Core\Users\UserRepositoryInterface;
|
||||
use Flarum\Core\Exceptions\PermissionDeniedException;
|
||||
use Flarum\Core\Events\UserEmailChangeWasRequested;
|
||||
use Flarum\Core\Users\Events\UserEmailChangeWasRequested;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
|
||||
@@ -23,7 +23,7 @@ class TokenAction extends JsonApiAction
|
||||
/**
|
||||
* Log in and return a token.
|
||||
*
|
||||
* @param \Flarum\Api\Request $request
|
||||
* @param Request $request
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
* @throws PermissionDeniedException
|
||||
*/
|
||||
@@ -40,6 +40,7 @@ class TokenAction extends JsonApiAction
|
||||
|
||||
if (! $user->is_activated) {
|
||||
event(new UserEmailChangeWasRequested($user, $user->email));
|
||||
|
||||
return new JsonResponse([
|
||||
'code' => 'confirm_email',
|
||||
'email' => $user->email
|
||||
@@ -47,7 +48,7 @@ class TokenAction extends JsonApiAction
|
||||
}
|
||||
|
||||
$token = $this->bus->dispatch(
|
||||
new GenerateAccessTokenCommand($user->id)
|
||||
new GenerateAccessToken($user->id)
|
||||
);
|
||||
|
||||
return new JsonResponse([
|
||||
|
@@ -1,7 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Users;
|
||||
|
||||
use Flarum\Core\Models\Forum;
|
||||
use Flarum\Core\Commands\RegisterUserCommand;
|
||||
use Flarum\Core\Users\Commands\RegisterUser;
|
||||
use Flarum\Api\Actions\CreateAction as BaseCreateAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
@@ -9,19 +8,10 @@ use Illuminate\Contracts\Bus\Dispatcher;
|
||||
class CreateAction extends BaseCreateAction
|
||||
{
|
||||
/**
|
||||
* The command bus.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Bus\Dispatcher
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
/**
|
||||
* The default forum instance.
|
||||
*
|
||||
* @var \Flarum\Core\Models\Forum
|
||||
*/
|
||||
protected $forum;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
@@ -58,27 +48,23 @@ class CreateAction extends BaseCreateAction
|
||||
public static $sort;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
|
||||
* @param \Flarum\Core\Models\Forum $forum
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus, Forum $forum)
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
$this->bus = $bus;
|
||||
$this->forum = $forum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a user according to input from the API request.
|
||||
*
|
||||
* @param JsonApiRequest $request
|
||||
* @return \Flarum\Core\Models\Model
|
||||
* @return \Flarum\Core\Users\User
|
||||
*/
|
||||
protected function create(JsonApiRequest $request)
|
||||
{
|
||||
return $this->bus->dispatch(
|
||||
new RegisterUserCommand($request->actor->getUser(), $this->forum, $request->get('data'))
|
||||
new RegisterUser($request->actor, $request->get('data'))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Users;
|
||||
|
||||
use Flarum\Core\Commands\DeleteUserCommand;
|
||||
use Flarum\Core\Users\Commands\DeleteUser;
|
||||
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
|
||||
use Flarum\Api\Request;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
@@ -8,16 +8,12 @@ use Illuminate\Contracts\Bus\Dispatcher;
|
||||
class DeleteAction extends BaseDeleteAction
|
||||
{
|
||||
/**
|
||||
* The command bus.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Bus\Dispatcher
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
@@ -27,13 +23,12 @@ class DeleteAction extends BaseDeleteAction
|
||||
/**
|
||||
* Delete a user.
|
||||
*
|
||||
* @param \Flarum\Api\Request $request
|
||||
* @return void
|
||||
* @param Request $request
|
||||
*/
|
||||
protected function delete(Request $request)
|
||||
{
|
||||
$this->bus->dispatch(
|
||||
new DeleteUserCommand($request->get('id'), $request->actor->getUser())
|
||||
new DeleteUser($request->get('id'), $request->actor)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Users;
|
||||
|
||||
use Flarum\Core\Commands\DeleteAvatarCommand;
|
||||
use Flarum\Core\Users\Commands\DeleteAvatar;
|
||||
use Flarum\Api\Actions\SerializeResourceAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
@@ -9,7 +9,7 @@ use Tobscure\JsonApi\Document;
|
||||
class DeleteAvatarAction extends SerializeResourceAction
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Bus\Dispatcher
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
@@ -19,9 +19,7 @@ class DeleteAvatarAction extends SerializeResourceAction
|
||||
public static $serializer = 'Flarum\Api\Serializers\UserSerializer';
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
@@ -32,14 +30,14 @@ class DeleteAvatarAction extends SerializeResourceAction
|
||||
* Delete a user's avatar, and return the user ready to be serialized and
|
||||
* assigned to the JsonApi response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @return \Flarum\Core\Models\Discussion
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Flarum\Core\Users\User
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
return $this->bus->dispatch(
|
||||
new DeleteAvatarCommand($request->get('id'), $request->actor->getUser())
|
||||
new DeleteAvatar($request->get('id'), $request->actor)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php namespace Flarum\Api\Actions\Users;
|
||||
|
||||
use Flarum\Core\Search\Users\UserSearchCriteria;
|
||||
use Flarum\Core\Search\Users\UserSearcher;
|
||||
use Flarum\Core\Search\SearchCriteria;
|
||||
use Flarum\Core\Users\Search\UserSearcher;
|
||||
use Flarum\Api\Actions\SerializeCollectionAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Flarum\Http\UrlGeneratorInterface;
|
||||
@@ -10,16 +10,12 @@ use Tobscure\JsonApi\Document;
|
||||
class IndexAction extends SerializeCollectionAction
|
||||
{
|
||||
/**
|
||||
* The user searcher.
|
||||
*
|
||||
* @var \Flarum\Core\Search\Users\UserSearcher
|
||||
* @var UserSearcher
|
||||
*/
|
||||
protected $searcher;
|
||||
|
||||
/**
|
||||
* The URL generator.
|
||||
*
|
||||
* @var \Flarum\Http\UrlGeneratorInterface
|
||||
* @var UrlGeneratorInterface
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
@@ -61,10 +57,8 @@ class IndexAction extends SerializeCollectionAction
|
||||
public static $sort;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Flarum\Core\Search\Users\UserSearcher $searcher
|
||||
* @param \Flarum\Http\UrlGeneratorInterface $url
|
||||
* @param UserSearcher $searcher
|
||||
* @param UrlGeneratorInterface $url
|
||||
*/
|
||||
public function __construct(UserSearcher $searcher, UrlGeneratorInterface $url)
|
||||
{
|
||||
@@ -76,15 +70,15 @@ class IndexAction extends SerializeCollectionAction
|
||||
* Get the user results, ready to be serialized and assigned to the
|
||||
* document response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
$criteria = new UserSearchCriteria(
|
||||
$request->actor->getUser(),
|
||||
$request->get('q'),
|
||||
$criteria = new SearchCriteria(
|
||||
$request->actor,
|
||||
$request->get('filter.q'),
|
||||
$request->sort
|
||||
);
|
||||
|
||||
@@ -97,6 +91,6 @@ class IndexAction extends SerializeCollectionAction
|
||||
$results->areMoreResults()
|
||||
);
|
||||
|
||||
return $results->getUsers();
|
||||
return $results->getResults();
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Users;
|
||||
|
||||
use Flarum\Core\Repositories\UserRepositoryInterface;
|
||||
use Flarum\Core\Users\UserRepositoryInterface;
|
||||
use Flarum\Api\Actions\SerializeResourceAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Tobscure\JsonApi\Document;
|
||||
@@ -8,7 +8,7 @@ use Tobscure\JsonApi\Document;
|
||||
class ShowAction extends SerializeResourceAction
|
||||
{
|
||||
/**
|
||||
* @var \Flarum\Core\Repositories\UserRepositoryInterface
|
||||
* @var UserRepositoryInterface
|
||||
*/
|
||||
protected $users;
|
||||
|
||||
@@ -50,9 +50,7 @@ class ShowAction extends SerializeResourceAction
|
||||
public static $sort;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Flarum\Core\Repositories\UserRepositoryInterface $users
|
||||
* @param UserRepositoryInterface $users
|
||||
*/
|
||||
public function __construct(UserRepositoryInterface $users)
|
||||
{
|
||||
@@ -63,9 +61,9 @@ class ShowAction extends SerializeResourceAction
|
||||
* Get a single user, ready to be serialized and assigned to the JsonApi
|
||||
* response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @return \Flarum\Core\Models\Discussion
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Flarum\Core\Users\User
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
@@ -75,6 +73,6 @@ class ShowAction extends SerializeResourceAction
|
||||
$id = $this->users->getIdForUsername($id);
|
||||
}
|
||||
|
||||
return $this->users->findOrFail($id, $request->actor->getUser());
|
||||
return $this->users->findOrFail($id, $request->actor);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Users;
|
||||
|
||||
use Flarum\Core\Commands\EditUserCommand;
|
||||
use Flarum\Core\Users\Commands\EditUser;
|
||||
use Flarum\Api\Actions\SerializeResourceAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
@@ -9,14 +9,14 @@ use Tobscure\JsonApi\Document;
|
||||
class UpdateAction extends SerializeResourceAction
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Bus\Dispatcher
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static $serializer = 'Flarum\Api\Serializers\UserSerializer';
|
||||
public static $serializer = 'Flarum\Api\Serializers\CurrentUserSerializer';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
@@ -49,9 +49,7 @@ class UpdateAction extends SerializeResourceAction
|
||||
public static $sort;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
@@ -62,14 +60,14 @@ class UpdateAction extends SerializeResourceAction
|
||||
* Update a user according to input from the API request, and return it
|
||||
* ready to be serialized and assigned to the JsonApi response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @return \Flarum\Core\Models\Discussion
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Flarum\Core\Users\User
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
return $this->bus->dispatch(
|
||||
new EditUserCommand($request->get('id'), $request->actor->getUser(), $request->get('data'))
|
||||
new EditUser($request->get('id'), $request->actor, $request->get('data'))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Actions\Users;
|
||||
|
||||
use Flarum\Core\Commands\UploadAvatarCommand;
|
||||
use Flarum\Core\Users\Commands\UploadAvatar;
|
||||
use Flarum\Api\Actions\SerializeResourceAction;
|
||||
use Flarum\Api\JsonApiRequest;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
@@ -9,7 +9,7 @@ use Tobscure\JsonApi\Document;
|
||||
class UploadAvatarAction extends SerializeResourceAction
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Bus\Dispatcher
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
@@ -49,9 +49,7 @@ class UploadAvatarAction extends SerializeResourceAction
|
||||
public static $sort;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
@@ -62,17 +60,17 @@ class UploadAvatarAction extends SerializeResourceAction
|
||||
* Upload an avatar for a user, and return the user ready to be serialized
|
||||
* and assigned to the JsonApi response.
|
||||
*
|
||||
* @param \Flarum\Api\JsonApiRequest $request
|
||||
* @param \Tobscure\JsonApi\Document $document
|
||||
* @return \Flarum\Core\Models\Discussion
|
||||
* @param JsonApiRequest $request
|
||||
* @param Document $document
|
||||
* @return \Flarum\Core\Users\User
|
||||
*/
|
||||
protected function data(JsonApiRequest $request, Document $document)
|
||||
{
|
||||
return $this->bus->dispatch(
|
||||
new UploadAvatarCommand(
|
||||
new UploadAvatar(
|
||||
$request->get('id'),
|
||||
$request->http->getUploadedFiles()['avatar'],
|
||||
$request->actor->getUser()
|
||||
$request->actor
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<?php namespace Flarum\Api;
|
||||
|
||||
use Flarum\Core\Users\Guest;
|
||||
use Flarum\Http\RouteCollection;
|
||||
use Flarum\Http\UrlGenerator;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
@@ -14,7 +15,9 @@ class ApiServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton('Flarum\Support\Actor');
|
||||
$this->app->bind('flarum.actor', function () {
|
||||
return new Guest;
|
||||
});
|
||||
|
||||
$this->app->singleton(
|
||||
'Flarum\Http\UrlGeneratorInterface',
|
||||
@@ -33,11 +36,6 @@ class ApiServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->app->singleton(
|
||||
'Illuminate\Contracts\Debug\ExceptionHandler',
|
||||
'Flarum\Api\ExceptionHandler'
|
||||
);
|
||||
|
||||
$this->routes();
|
||||
}
|
||||
|
||||
@@ -94,7 +92,7 @@ class ApiServiceProvider extends ServiceProvider
|
||||
);
|
||||
|
||||
// Edit a user
|
||||
$routes->put(
|
||||
$routes->patch(
|
||||
'/users/{id}',
|
||||
'flarum.api.users.update',
|
||||
$this->action('Flarum\Api\Actions\Users\UpdateAction')
|
||||
@@ -142,7 +140,7 @@ class ApiServiceProvider extends ServiceProvider
|
||||
);
|
||||
|
||||
// Mark a single notification as read
|
||||
$routes->put(
|
||||
$routes->patch(
|
||||
'/notifications/{id}',
|
||||
'flarum.api.notifications.update',
|
||||
$this->action('Flarum\Api\Actions\Notifications\UpdateAction')
|
||||
@@ -175,7 +173,7 @@ class ApiServiceProvider extends ServiceProvider
|
||||
);
|
||||
|
||||
// Edit a discussion
|
||||
$routes->put(
|
||||
$routes->patch(
|
||||
'/discussions/{id}',
|
||||
'flarum.api.discussions.update',
|
||||
$this->action('Flarum\Api\Actions\Discussions\UpdateAction')
|
||||
@@ -202,7 +200,6 @@ class ApiServiceProvider extends ServiceProvider
|
||||
);
|
||||
|
||||
// Create a post
|
||||
// @todo consider 'discussions/{id}/links/posts'?
|
||||
$routes->post(
|
||||
'/posts',
|
||||
'flarum.api.posts.create',
|
||||
@@ -217,7 +214,7 @@ class ApiServiceProvider extends ServiceProvider
|
||||
);
|
||||
|
||||
// Edit a post
|
||||
$routes->put(
|
||||
$routes->patch(
|
||||
'/posts/{id}',
|
||||
'flarum.api.posts.update',
|
||||
$this->action('Flarum\Api\Actions\Posts\UpdateAction')
|
||||
@@ -258,7 +255,7 @@ class ApiServiceProvider extends ServiceProvider
|
||||
);
|
||||
|
||||
// Edit a group
|
||||
$routes->put(
|
||||
$routes->patch(
|
||||
'/groups/{id}',
|
||||
'flarum.api.groups.update',
|
||||
$this->action('Flarum\Api\Actions\Groups\UpdateAction')
|
||||
@@ -276,7 +273,7 @@ class ApiServiceProvider extends ServiceProvider
|
||||
{
|
||||
return function (ServerRequestInterface $httpRequest, $routeParams) use ($class) {
|
||||
$action = app($class);
|
||||
$actor = app('Flarum\Support\Actor');
|
||||
$actor = app('flarum.actor');
|
||||
|
||||
$input = array_merge($httpRequest->getQueryParams(), $httpRequest->getAttributes(), $routeParams);
|
||||
$request = new Request($input, $actor, $httpRequest);
|
||||
|
@@ -1,35 +1,37 @@
|
||||
<?php
|
||||
<?php namespace Flarum\Api;
|
||||
|
||||
namespace Flarum\Api;
|
||||
|
||||
use Flarum\Support\Actor;
|
||||
use Flarum\Core\Users\User;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class Client
|
||||
{
|
||||
/**
|
||||
* @var Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
protected $actor;
|
||||
|
||||
public function __construct(Container $container, Actor $actor)
|
||||
/**
|
||||
* @param Container $container
|
||||
*/
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->actor = $actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given API action class, pass the input and return its response.
|
||||
*
|
||||
* @param User $actor
|
||||
* @param string $actionClass
|
||||
* @param array $input
|
||||
* @return object
|
||||
*/
|
||||
public function send($actionClass, array $input = [])
|
||||
public function send(User $actor, $actionClass, array $input = [])
|
||||
{
|
||||
/** @var \Flarum\Api\Actions\JsonApiAction $action */
|
||||
$action = $this->container->make($actionClass);
|
||||
|
||||
$response = $action->handle(new Request($input, $this->actor));
|
||||
$response = $action->handle(new Request($input, $actor));
|
||||
|
||||
return json_decode($response->getBody());
|
||||
}
|
||||
|
19
framework/core/src/Api/Commands/GenerateAccessToken.php
Normal file
19
framework/core/src/Api/Commands/GenerateAccessToken.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php namespace Flarum\Api\Commands;
|
||||
|
||||
class GenerateAccessToken
|
||||
{
|
||||
/**
|
||||
* The ID of the user to generate an access token for.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* @param int $userId The ID of the user to generate an access token for.
|
||||
*/
|
||||
public function __construct($userId)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
<?php namespace Flarum\Api\Commands;
|
||||
|
||||
use Flarum\Api\AccessToken;
|
||||
|
||||
class GenerateAccessTokenHandler
|
||||
{
|
||||
public function handle(GenerateAccessToken $command)
|
||||
{
|
||||
$token = AccessToken::generate($command->userId);
|
||||
|
||||
$token->save();
|
||||
|
||||
return $token;
|
||||
}
|
||||
}
|
@@ -1,14 +1,36 @@
|
||||
<?php namespace Flarum\Api\Events;
|
||||
|
||||
use Flarum\Api\Serializers\Serializer;
|
||||
|
||||
class SerializeAttributes
|
||||
{
|
||||
/**
|
||||
* The class doing the serializing.
|
||||
*
|
||||
* @var Serializer
|
||||
*/
|
||||
public $serializer;
|
||||
|
||||
/**
|
||||
* The model being serialized.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* The serialized attributes of the resource.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $attributes;
|
||||
|
||||
public function __construct($serializer, $model, &$attributes)
|
||||
/**
|
||||
* @param Serializer $serializer The class doing the serializing.
|
||||
* @param object $model The model being serialized.
|
||||
* @param array $attributes The serialized attributes of the resource.
|
||||
*/
|
||||
public function __construct(Serializer $serializer, $model, array &$attributes)
|
||||
{
|
||||
$this->serializer = $serializer;
|
||||
$this->model = $model;
|
||||
|
@@ -1,54 +0,0 @@
|
||||
<?php namespace Flarum\Api;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Foundation\Exceptions\Handler;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Config;
|
||||
|
||||
class ExceptionHandler extends Handler
|
||||
{
|
||||
/**
|
||||
* A list of the exception types that should not be reported.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dontReport = [
|
||||
'Symfony\Component\HttpKernel\Exception\HttpException'
|
||||
];
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Exception $e
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render($request, Exception $e)
|
||||
{
|
||||
if ($request->is('api/*')) {
|
||||
$error = [];
|
||||
if (Config::get('app.debug')) {
|
||||
$error['code'] = (new \ReflectionClass($e))->getShortName();
|
||||
}
|
||||
if ($detail = $e->getMessage()) {
|
||||
$error['detail'] = $detail;
|
||||
}
|
||||
$statusCode = $e instanceof HttpException ? $e->getStatusCode() : 500;
|
||||
if (count($error)) {
|
||||
return $this->renderErrors([$error], $statusCode);
|
||||
} else {
|
||||
return new Response(null, $statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
|
||||
protected function renderErrors($errors, $httpCode = 500)
|
||||
{
|
||||
return new JsonResponse(['errors' => $errors], $httpCode);
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
<?php namespace Flarum\Api\Middleware;
|
||||
|
||||
use Flarum\Core\Models\AccessToken;
|
||||
use Flarum\Support\Actor;
|
||||
use Flarum\Api\AccessToken;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Zend\Stratigility\MiddlewareInterface;
|
||||
@@ -9,20 +9,21 @@ use Zend\Stratigility\MiddlewareInterface;
|
||||
class LoginWithHeader implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var Actor
|
||||
* @var Container
|
||||
*/
|
||||
protected $actor;
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix = 'Token ';
|
||||
|
||||
// @todo rather than using a singleton, we should have our own HTTP
|
||||
// Request class and store the actor on that? somehow?
|
||||
public function __construct(Actor $actor)
|
||||
/**
|
||||
* @param Container $app
|
||||
*/
|
||||
public function __construct(Container $app)
|
||||
{
|
||||
$this->actor = $actor;
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,7 +36,7 @@ class LoginWithHeader implements MiddlewareInterface
|
||||
($token = substr($header, strlen($this->prefix))) &&
|
||||
($accessToken = AccessToken::where('id', $token)->first())
|
||||
) {
|
||||
$this->actor->setUser($user = $accessToken->user);
|
||||
$this->app->instance('flarum.actor', $user = $accessToken->user);
|
||||
|
||||
$user->updateLastSeen()->save();
|
||||
}
|
||||
|
@@ -1,12 +1,19 @@
|
||||
<?php namespace Flarum\Api;
|
||||
|
||||
use Flarum\Support\Actor;
|
||||
use Flarum\Core\Users\Guest;
|
||||
use Flarum\Core\Users\User;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class Request
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $input;
|
||||
|
||||
/**
|
||||
* @var Guest
|
||||
*/
|
||||
public $actor;
|
||||
|
||||
/**
|
||||
@@ -14,18 +21,31 @@ class Request
|
||||
*/
|
||||
public $http;
|
||||
|
||||
public function __construct(array $input, Actor $actor = null, ServerRequestInterface $http = null)
|
||||
/**
|
||||
* @param array $input
|
||||
* @param User $actor
|
||||
* @param ServerRequestInterface $http
|
||||
*/
|
||||
public function __construct(array $input, User $actor = null, ServerRequestInterface $http = null)
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->actor = $actor;
|
||||
$this->actor = $actor ?: new Guest;
|
||||
$this->http = $http;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
return array_get($this->input, $key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
return $this->input;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
class ActivitySerializer extends BaseSerializer
|
||||
class ActivitySerializer extends Serializer
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
@@ -16,38 +16,46 @@ class ActivitySerializer extends BaseSerializer
|
||||
protected static $subjectSerializers = [];
|
||||
|
||||
/**
|
||||
* Serialize attributes of an Activity model for JSON output.
|
||||
*
|
||||
* @param Activity $activity The Activity model to serialize.
|
||||
* @return array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function attributes($activity)
|
||||
protected function getDefaultAttributes($activity)
|
||||
{
|
||||
$attributes = [
|
||||
return [
|
||||
'contentType' => $activity->type,
|
||||
'time' => $activity->time->toRFC3339String()
|
||||
];
|
||||
|
||||
return $this->extendAttributes($activity, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function sender()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function subject()
|
||||
{
|
||||
return $this->hasOne(function ($activity) {
|
||||
return static::$subjects[$activity->type];
|
||||
return static::$subjectSerializers[$activity->type];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
* @param $serializer
|
||||
*/
|
||||
public static function setSubjectSerializer($type, $serializer)
|
||||
{
|
||||
static::$subjectSerializers[$type] = $serializer;
|
||||
|
@@ -1,112 +0,0 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Tobscure\JsonApi\SerializerAbstract;
|
||||
use Flarum\Api\Events\SerializeAttributes;
|
||||
use Flarum\Api\Events\SerializeRelationship;
|
||||
use Flarum\Support\Actor;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* A base serializer to call Flarum events at common serialization points.
|
||||
*/
|
||||
abstract class BaseSerializer extends SerializerAbstract
|
||||
{
|
||||
public $actor;
|
||||
|
||||
/**
|
||||
* The custom relationships on this serializer.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $relationships = [];
|
||||
|
||||
public function __construct(Actor $actor, $include = null, $link = null)
|
||||
{
|
||||
parent::__construct($include, $link);
|
||||
|
||||
$this->actor = $actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire an event to allow custom serialization of attributes.
|
||||
*
|
||||
* @param mixed $model The model to serialize.
|
||||
* @param array $attributes Attributes that have already been serialized.
|
||||
* @return array
|
||||
*/
|
||||
protected function extendAttributes($model, &$attributes = [])
|
||||
{
|
||||
event(new SerializeAttributes($this, $model, $attributes));
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
protected function relationship($serializer, $relation = null, $many = false)
|
||||
{
|
||||
// Get the relationship name from the stack trace.
|
||||
if (is_null($relation)) {
|
||||
list(, , $caller) = debug_backtrace(false, 3);
|
||||
$relation = $caller['function'];
|
||||
}
|
||||
|
||||
return function ($model, $include, $included, $links) use ($serializer, $many, $relation) {
|
||||
if ($relation instanceof Closure) {
|
||||
$data = $relation($model, $include);
|
||||
} else {
|
||||
if ($include) {
|
||||
$data = $model->$relation;
|
||||
} elseif ($many) {
|
||||
$relationIds = $relation.'_ids';
|
||||
$data = isset($model->$relationIds) ? $model->$relationIds : $model->$relation()->lists('id');
|
||||
} else {
|
||||
$relationId = $relation.'_id';
|
||||
$data = $model->$relationId;
|
||||
}
|
||||
}
|
||||
|
||||
if ($serializer instanceof Closure) {
|
||||
$serializer = $serializer($model, $data);
|
||||
}
|
||||
$serializer = new $serializer($this->actor, $included, $links);
|
||||
return $many ? $serializer->collection($data) : $serializer->resource($data);
|
||||
};
|
||||
}
|
||||
|
||||
public function hasOne($serializer, $relation = null)
|
||||
{
|
||||
return $this->relationship($serializer, $relation);
|
||||
}
|
||||
|
||||
public function hasMany($serializer, $relation = null)
|
||||
{
|
||||
return $this->relationship($serializer, $relation, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom relationship to the serializer.
|
||||
*
|
||||
* @param string $name The name of the relationship.
|
||||
* @param Closure $callback The callback to execute.
|
||||
* @return void
|
||||
*/
|
||||
public static function addRelationship($name, $callback)
|
||||
{
|
||||
static::$relationships[$name] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for and execute custom relationships.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if (isset(static::$relationships[$name])) {
|
||||
array_unshift($arguments, $this);
|
||||
return call_user_func_array(static::$relationships[$name], $arguments);
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,20 +2,21 @@
|
||||
|
||||
class CurrentUserSerializer extends UserSerializer
|
||||
{
|
||||
protected function attributes($user)
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultAttributes($user)
|
||||
{
|
||||
$attributes = parent::attributes($user);
|
||||
$attributes = parent::getDefaultAttributes($user);
|
||||
|
||||
$actingUser = $this->actor->getUser();
|
||||
|
||||
if ($user->id === $actingUser->id) {
|
||||
if ($user->id == $this->actor->id) {
|
||||
$attributes += [
|
||||
'readTime' => $user->read_time ? $user->read_time->toRFC3339String() : null,
|
||||
'readTime' => $user->read_time ? $user->read_time->toRFC3339String() : null,
|
||||
'unreadNotificationsCount' => $user->getUnreadNotificationsCount(),
|
||||
'preferences' => $user->preferences
|
||||
'preferences' => $user->preferences
|
||||
];
|
||||
}
|
||||
|
||||
return $this->extendAttributes($user, $attributes);
|
||||
return $attributes;
|
||||
}
|
||||
}
|
||||
|
@@ -1,67 +1,67 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
class DiscussionBasicSerializer extends BaseSerializer
|
||||
class DiscussionBasicSerializer extends Serializer
|
||||
{
|
||||
protected static $relationships = [];
|
||||
|
||||
/**
|
||||
* The resource type.
|
||||
*
|
||||
* @var string
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $type = 'discussions';
|
||||
|
||||
/**
|
||||
* Serialize attributes of a Discussion model for JSON output.
|
||||
*
|
||||
* @param Discussion $discussion The Discussion model to serialize.
|
||||
* @return array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function attributes($discussion)
|
||||
protected function getDefaultAttributes($discussion)
|
||||
{
|
||||
$attributes = [
|
||||
return [
|
||||
'title' => $discussion->title
|
||||
];
|
||||
|
||||
if (count($discussion->removedPosts)) {
|
||||
$attributes['removedPosts'] = $discussion->removedPosts;
|
||||
}
|
||||
|
||||
return $this->extendAttributes($discussion, $attributes);
|
||||
}
|
||||
|
||||
public function startUser()
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
protected function startUser()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
|
||||
}
|
||||
|
||||
public function startPost()
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
protected function startPost()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\PostBasicSerializer');
|
||||
}
|
||||
|
||||
public function lastUser()
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
protected function lastUser()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
|
||||
}
|
||||
|
||||
public function lastPost()
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
protected function lastPost()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\PostBasicSerializer');
|
||||
}
|
||||
|
||||
public function posts()
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
protected function posts()
|
||||
{
|
||||
return $this->hasMany('Flarum\Api\Serializers\PostSerializer');
|
||||
}
|
||||
|
||||
public function relevantPosts()
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
protected function relevantPosts()
|
||||
{
|
||||
return $this->hasMany('Flarum\Api\Serializers\PostBasicSerializer');
|
||||
}
|
||||
|
||||
public function addedPosts()
|
||||
{
|
||||
return $this->hasMany('Flarum\Api\Serializers\PostSerializer');
|
||||
}
|
||||
}
|
||||
|
@@ -1,34 +1,34 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Flarum\Core\Discussions\Discussion;
|
||||
|
||||
class DiscussionSerializer extends DiscussionBasicSerializer
|
||||
{
|
||||
/**
|
||||
* Serialize attributes of a Discussion model for JSON output.
|
||||
*
|
||||
* @param Discussion $discussion The Discussion model to serialize.
|
||||
* @return array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function attributes($discussion)
|
||||
protected function getDefaultAttributes($discussion)
|
||||
{
|
||||
$attributes = parent::attributes($discussion);
|
||||
|
||||
$user = $this->actor->getUser();
|
||||
$state = $discussion->state;
|
||||
|
||||
$attributes += [
|
||||
$attributes = parent::getDefaultAttributes($discussion) + [
|
||||
'commentsCount' => (int) $discussion->comments_count,
|
||||
'participantsCount' => (int) $discussion->participants_count,
|
||||
'startTime' => $discussion->start_time->toRFC3339String(),
|
||||
'lastTime' => $discussion->last_time ? $discussion->last_time->toRFC3339String() : null,
|
||||
'lastPostNumber' => $discussion->last_post_number,
|
||||
'canReply' => $discussion->can($user, 'reply'),
|
||||
'canRename' => $discussion->can($user, 'rename'),
|
||||
'canDelete' => $discussion->can($user, 'delete'),
|
||||
|
||||
'readTime' => $state && $state->read_time ? $state->read_time->toRFC3339String() : null,
|
||||
'readNumber' => $state ? (int) $state->read_number : 0
|
||||
'canReply' => $discussion->can($this->actor, 'reply'),
|
||||
'canRename' => $discussion->can($this->actor, 'rename'),
|
||||
'canDelete' => $discussion->can($this->actor, 'delete')
|
||||
];
|
||||
|
||||
return $this->extendAttributes($discussion, $attributes);
|
||||
Discussion::setStateUser($this->actor);
|
||||
|
||||
if ($state = $discussion->state) {
|
||||
$attributes += [
|
||||
'readTime' => $state->read_time ? $state->read_time->toRFC3339String() : null,
|
||||
'readNumber' => (int) $state->read_number
|
||||
];
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
}
|
||||
|
@@ -1,31 +1,27 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
class ForumSerializer extends BaseSerializer
|
||||
class ForumSerializer extends Serializer
|
||||
{
|
||||
/**
|
||||
* The resource type.
|
||||
*
|
||||
* @var string
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $type = 'forums';
|
||||
|
||||
protected function id($forum)
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getId($forum)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize attributes of a Forum model for JSON output.
|
||||
*
|
||||
* @param Forum $forum The Forum model to serialize.
|
||||
* @return array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function attributes($forum)
|
||||
protected function getDefaultAttributes($forum)
|
||||
{
|
||||
$attributes = [
|
||||
return [
|
||||
'title' => $forum->title
|
||||
];
|
||||
|
||||
return $this->extendAttributes($forum, $attributes);
|
||||
}
|
||||
}
|
||||
|
@@ -1,30 +1,23 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
class GroupSerializer extends BaseSerializer
|
||||
class GroupSerializer extends Serializer
|
||||
{
|
||||
/**
|
||||
* The resource type.
|
||||
*
|
||||
* @var string
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $type = 'groups';
|
||||
|
||||
/**
|
||||
* Serialize attributes of a Group model for JSON output.
|
||||
*
|
||||
* @param Group $group The Group model to serialize.
|
||||
* @return array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function attributes($group)
|
||||
protected function getDefaultAttributes($group)
|
||||
{
|
||||
$attributes = [
|
||||
return [
|
||||
'id' => (int) $group->id,
|
||||
'nameSingular' => $group->name_singular,
|
||||
'namePlural' => $group->name_plural,
|
||||
'color' => $group->color,
|
||||
'icon' => $group->icon,
|
||||
];
|
||||
|
||||
return $this->extendAttributes($group, $attributes);
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,9 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
class NotificationSerializer extends BaseSerializer
|
||||
class NotificationSerializer extends Serializer
|
||||
{
|
||||
/**
|
||||
* The resource type.
|
||||
*
|
||||
* @var string
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $type = 'notifications';
|
||||
|
||||
@@ -15,44 +13,55 @@ class NotificationSerializer extends BaseSerializer
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $subjects = [
|
||||
'discussionRenamed' => 'Flarum\Api\Serializers\DiscussionBasicSerializer'
|
||||
];
|
||||
protected static $subjectSerializers = [];
|
||||
|
||||
/**
|
||||
* Serialize attributes of an notification model for JSON output.
|
||||
*
|
||||
* @param Notification $notification The notification model to serialize.
|
||||
* @return array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function attributes($notification)
|
||||
protected function getDefaultAttributes($notification)
|
||||
{
|
||||
$attributes = [
|
||||
'id' => (int) $notification->id,
|
||||
return [
|
||||
'id' => (int) $notification->id,
|
||||
'contentType' => $notification->type,
|
||||
'content' => $notification->data,
|
||||
'time' => $notification->time->toRFC3339String(),
|
||||
'isRead' => (bool) $notification->is_read,
|
||||
'content' => $notification->data,
|
||||
'time' => $notification->time->toRFC3339String(),
|
||||
'isRead' => (bool) $notification->is_read,
|
||||
'unreadCount' => $notification->unread_count
|
||||
];
|
||||
|
||||
return $this->extendAttributes($notification, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function sender()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function subject()
|
||||
{
|
||||
return $this->hasOne(function ($notification) {
|
||||
return static::$subjects[$notification->type];
|
||||
return static::$subjectSerializers[$notification->type];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
* @param $serializer
|
||||
*/
|
||||
public static function setSubjectSerializer($type, $serializer)
|
||||
{
|
||||
static::$subjectSerializers[$type] = $serializer;
|
||||
}
|
||||
}
|
||||
|
@@ -1,29 +1,22 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
class PostBasicSerializer extends BaseSerializer
|
||||
class PostBasicSerializer extends Serializer
|
||||
{
|
||||
protected static $relationships = [];
|
||||
|
||||
/**
|
||||
* The resource type.
|
||||
*
|
||||
* @var string
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $type = 'posts';
|
||||
|
||||
/**
|
||||
* Serialize attributes of a Post model for JSON output.
|
||||
*
|
||||
* @param Post $post The Post model to serialize.
|
||||
* @return array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function attributes($post)
|
||||
protected function getDefaultAttributes($post)
|
||||
{
|
||||
$attributes = [
|
||||
'id' => (int) $post->id,
|
||||
'number' => (int) $post->number,
|
||||
'time' => $post->time->toRFC3339String(),
|
||||
'contentType' => $post->type
|
||||
'id' => (int) $post->id,
|
||||
'number' => (int) $post->number,
|
||||
'time' => $post->time->toRFC3339String(),
|
||||
'contentType' => $post->type
|
||||
];
|
||||
|
||||
if ($post->type === 'comment') {
|
||||
@@ -32,14 +25,20 @@ class PostBasicSerializer extends BaseSerializer
|
||||
$attributes['content'] = $post->content;
|
||||
}
|
||||
|
||||
return $this->extendAttributes($post, $attributes);
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function discussion()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\DiscussionBasicSerializer');
|
||||
|
@@ -3,22 +3,19 @@
|
||||
class PostSerializer extends PostBasicSerializer
|
||||
{
|
||||
/**
|
||||
* Serialize attributes of a Post model for JSON output.
|
||||
*
|
||||
* @param Post $post The Post model to serialize.
|
||||
* @return array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function attributes($post)
|
||||
protected function getDefaultAttributes($post)
|
||||
{
|
||||
$attributes = parent::attributes($post);
|
||||
$user = $this->actor->getUser();
|
||||
$attributes = parent::getDefaultAttributes($post);
|
||||
|
||||
unset($attributes['content']);
|
||||
|
||||
$canEdit = $post->can($user, 'edit');
|
||||
$canEdit = $post->can($this->actor, 'edit');
|
||||
|
||||
if ($post->type === 'comment') {
|
||||
$attributes['contentHtml'] = $post->content_html;
|
||||
|
||||
if ($canEdit) {
|
||||
$attributes['content'] = $post->content;
|
||||
}
|
||||
@@ -37,27 +34,39 @@ class PostSerializer extends PostBasicSerializer
|
||||
|
||||
$attributes += [
|
||||
'canEdit' => $canEdit,
|
||||
'canDelete' => $post->can($user, 'delete')
|
||||
'canDelete' => $post->can($this->actor, 'delete')
|
||||
];
|
||||
|
||||
return $this->extendAttributes($post, $attributes);
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\UserSerializer');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function discussion()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\DiscussionSerializer');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function editUser()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\UserSerializer');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
public function hideUser()
|
||||
{
|
||||
return $this->hasOne('Flarum\Api\Serializers\UserSerializer');
|
||||
|
178
framework/core/src/Api/Serializers/Serializer.php
Normal file
178
framework/core/src/Api/Serializers/Serializer.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Closure;
|
||||
use Flarum\Core\Users\User;
|
||||
use Tobscure\JsonApi\SerializerAbstract;
|
||||
use Flarum\Api\Events\SerializeAttributes;
|
||||
|
||||
abstract class Serializer extends SerializerAbstract
|
||||
{
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
public $actor;
|
||||
|
||||
/**
|
||||
* An array of custom relation methods, grouped by subclass.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $relationMethods = [];
|
||||
|
||||
/**
|
||||
* @param User $actor
|
||||
* @param array|null $include
|
||||
* @param array|null $link
|
||||
*/
|
||||
public function __construct(User $actor, $include = null, $link = null)
|
||||
{
|
||||
parent::__construct($include, $link);
|
||||
|
||||
$this->actor = $actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getAttributes($model)
|
||||
{
|
||||
$attributes = $this->getDefaultAttributes($model);
|
||||
|
||||
event(new SerializeAttributes($this, $model, $attributes));
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default set of serialized attributes for a model.
|
||||
*
|
||||
* @param $model
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function getDefaultAttributes($model);
|
||||
|
||||
/**
|
||||
* Get a closure that returns a Collection/Resource representing a relation.
|
||||
*
|
||||
* @param string|Closure $serializer The name of the serializer, or a
|
||||
* Closure returning the name of the serializer, to use for the related
|
||||
* items.
|
||||
* @param string|Closure|null $relation If a string is provided, it will be
|
||||
* used to retrieve the relation data from the model:
|
||||
* - If the relation is being included, the relation will be accessed
|
||||
* as a property on the model.
|
||||
* - If the relation is not being included and is a to-many relation, a
|
||||
* list of IDs will be accessed as a property on the model with the
|
||||
* suffix '_ids', otherwise by querying the relation method.
|
||||
* - If the relation is not being included and is a to-one relation,
|
||||
* the ID will be accessed as a property on the model with the suffix
|
||||
* '_id'.
|
||||
* If a closure is provided, it will be passed the model and
|
||||
* whether or not the relation is being included. It is expected to
|
||||
* return the relation data.
|
||||
* @param bool $many Whether or not this is a to-many relation.
|
||||
* @return callable
|
||||
*/
|
||||
protected function getRelationship($serializer, $relation = null, $many = false)
|
||||
{
|
||||
// If no relationship name was provided, we can guess it from the
|
||||
// stack trace. The assumes that one of the hasOne or hasMany methods
|
||||
// was called from directly inside a serializer method.
|
||||
if (is_null($relation)) {
|
||||
list(, , $caller) = debug_backtrace(false, 3);
|
||||
$relation = $caller['function'];
|
||||
}
|
||||
|
||||
return function ($model, $include, $included, $links) use ($serializer, $many, $relation) {
|
||||
// If the passed relation was a closure, we can let that take care
|
||||
// of retrieving the relation data from the model. Otherwise, we
|
||||
// need to get the data from the model itself, using the relation
|
||||
// name provided.
|
||||
if ($relation instanceof Closure) {
|
||||
$data = $relation($model, $include);
|
||||
} else {
|
||||
if ($include) {
|
||||
$data = $model->$relation;
|
||||
} elseif ($many) {
|
||||
$relationIds = $relation.'_ids';
|
||||
$data = isset($model->$relationIds) ? $model->$relationIds : $model->$relation()->lists('id');
|
||||
} else {
|
||||
$relationId = $relation.'_id';
|
||||
$data = $model->$relationId;
|
||||
}
|
||||
}
|
||||
|
||||
// If the passed serializer was a closure, we'll need to run
|
||||
// that in order to find out which serializer class to instantiate.
|
||||
// This is useful for polymorphic relations.
|
||||
if ($serializer instanceof Closure) {
|
||||
$serializer = $serializer($model, $data);
|
||||
}
|
||||
|
||||
/** @var \Tobscure\JsonApi\SerializerInterface $serializer */
|
||||
$serializer = new $serializer($this->actor, $included, $links);
|
||||
|
||||
return $many ? $serializer->collection($data) : $serializer->resource($data);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a closure that returns a Resource representing a relation.
|
||||
*
|
||||
* @param string $serializer
|
||||
* @param string|Closure|null $relation
|
||||
* @see Serializer::getRelationship()
|
||||
* @return callable
|
||||
*/
|
||||
public function hasOne($serializer, $relation = null)
|
||||
{
|
||||
return $this->getRelationship($serializer, $relation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a closure that returns a Collection representing a relation.
|
||||
*
|
||||
* @param string $serializer
|
||||
* @param string|Closure|null $relation
|
||||
* @see Serializer::getRelationship()
|
||||
* @return callable
|
||||
*/
|
||||
public function hasMany($serializer, $relation = null)
|
||||
{
|
||||
return $this->getRelationship($serializer, $relation, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom relation to the model.
|
||||
*
|
||||
* @param string $name The name of the relation.
|
||||
* @param callable $callback The callback to execute. This should return a
|
||||
* relation closure {@see Serializer::getRelationship()}
|
||||
*/
|
||||
public static function setRelationMethod($name, callable $callback)
|
||||
{
|
||||
static::$relationMethods[get_called_class()][$name] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for and execute custom relationships.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
* @return mixed
|
||||
* @throws BadMethodCallException
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
if (isset(static::$relationMethods[$method])) {
|
||||
array_unshift($parameters, $this);
|
||||
|
||||
return call_user_func_array(static::$relationMethods[$method], $parameters);
|
||||
}
|
||||
|
||||
$className = get_class($this);
|
||||
|
||||
throw new BadMethodCallException("Call to undefined method {$className}::{$method}()");
|
||||
}
|
||||
}
|
@@ -1,30 +1,26 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
class UserBasicSerializer extends BaseSerializer
|
||||
class UserBasicSerializer extends Serializer
|
||||
{
|
||||
/**
|
||||
* The resource type.
|
||||
*
|
||||
* @var string
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $type = 'users';
|
||||
|
||||
/**
|
||||
* Serialize attributes of a User model for JSON output.
|
||||
*
|
||||
* @param User $user The User model to serialize.
|
||||
* @return array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function attributes($user)
|
||||
protected function getDefaultAttributes($user)
|
||||
{
|
||||
$attributes = [
|
||||
return [
|
||||
'username' => $user->username,
|
||||
'avatarUrl' => $user->avatar_url
|
||||
];
|
||||
|
||||
return $this->extendAttributes($user, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable
|
||||
*/
|
||||
protected function groups()
|
||||
{
|
||||
return $this->hasMany('Flarum\Api\Serializers\GroupSerializer');
|
||||
|
@@ -3,17 +3,13 @@
|
||||
class UserSerializer extends UserBasicSerializer
|
||||
{
|
||||
/**
|
||||
* Serialize attributes of a User model for JSON output.
|
||||
*
|
||||
* @param User $user The User model to serialize.
|
||||
* @return array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function attributes($user)
|
||||
protected function getDefaultAttributes($user)
|
||||
{
|
||||
$attributes = parent::attributes($user);
|
||||
$attributes = parent::getDefaultAttributes($user);
|
||||
|
||||
$actingUser = $this->actor->getUser();
|
||||
$canEdit = $user->can($actingUser, 'edit');
|
||||
$canEdit = $user->can($this->actor, 'edit');
|
||||
|
||||
$attributes += [
|
||||
'bioHtml' => $user->bio_html,
|
||||
@@ -21,10 +17,10 @@ class UserSerializer extends UserBasicSerializer
|
||||
'discussionsCount' => (int) $user->discussions_count,
|
||||
'commentsCount' => (int) $user->comments_count,
|
||||
'canEdit' => $canEdit,
|
||||
'canDelete' => $user->can($actingUser, 'delete'),
|
||||
'canDelete' => $user->can($this->actor, 'delete'),
|
||||
];
|
||||
|
||||
if ($user->preference('discloseOnline')) {
|
||||
if ($user->getPreference('discloseOnline')) {
|
||||
$attributes += [
|
||||
'lastSeenTime' => $user->last_seen_time ? $user->last_seen_time->toRFC3339String() : null
|
||||
];
|
||||
@@ -39,6 +35,6 @@ class UserSerializer extends UserBasicSerializer
|
||||
];
|
||||
}
|
||||
|
||||
return $this->extendAttributes($user, $attributes);
|
||||
return $attributes;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Assets;
|
||||
|
||||
use RuntimeException;
|
||||
use DomainException;
|
||||
|
||||
class AssetManager
|
||||
{
|
||||
@@ -29,7 +29,7 @@ class AssetManager
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException('Unsupported asset type: '.$ext);
|
||||
throw new DomainException('Unsupported asset type: '.$ext);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -13,6 +13,7 @@ class ConsoleServiceProvider extends ServiceProvider
|
||||
{
|
||||
$this->commands('Flarum\Console\InstallCommand');
|
||||
$this->commands('Flarum\Console\SeedCommand');
|
||||
$this->commands('Flarum\Console\ImportCommand');
|
||||
$this->commands('Flarum\Console\GenerateExtensionCommand');
|
||||
}
|
||||
|
||||
|
278
framework/core/src/Console/ImportCommand.php
Normal file
278
framework/core/src/Console/ImportCommand.php
Normal file
@@ -0,0 +1,278 @@
|
||||
<?php namespace Flarum\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Flarum\Core\Exceptions\ValidationFailureException;
|
||||
use Flarum\Core\Models\User;
|
||||
use Flarum\Core\Models\Discussion;
|
||||
use Flarum\Core\Models\DiscussionState;
|
||||
use Flarum\Core\Models\CommentPost;
|
||||
use Flarum\Tags\Tag;
|
||||
use Flarum\Core\Events\PostWasPosted;
|
||||
use Flarum\Core\Events\UserWasRegistered;
|
||||
use Flarum\Core\Activity\JoinedActivity;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
|
||||
class ImportCommand extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* The console command name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'flarum:import';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Import from esoTalk.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Application $app)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fire()
|
||||
{
|
||||
if (!$this->confirm('Warning: all Flarum tables will be truncated. Proceed? [y|N]', false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
app('config')->set('database.connections.esotalk', [
|
||||
'driver' => 'mysql',
|
||||
'host' => env('DB_HOST'),
|
||||
'database' => 'esotalk',
|
||||
'username' => env('DB_USERNAME'),
|
||||
'password' => env('DB_PASSWORD'),
|
||||
'charset' => 'utf8',
|
||||
'collation' => 'utf8_unicode_ci',
|
||||
'prefix' => 'et_',
|
||||
'strict' => false,
|
||||
]);
|
||||
|
||||
User::$rules = [];
|
||||
|
||||
$from = app('db')->connection('esotalk');
|
||||
$to = app('db')->connection();
|
||||
|
||||
$this->importTags($from, $to);
|
||||
$this->importUsers($from, $to);
|
||||
$this->importDiscussions($from, $to);
|
||||
|
||||
$to->table('notifications')->update(['is_read' => true]);
|
||||
}
|
||||
|
||||
protected function importTags($from, $to)
|
||||
{
|
||||
$colors = ['#F16655', '#F59B66', '#4E89DA', '#5AC169', '#96A2AF'];
|
||||
|
||||
$this->info('Importing tags...');
|
||||
|
||||
$to->table('tags')->truncate();
|
||||
|
||||
$channels = $from->table('channel')->orderBy('lft')->get();
|
||||
|
||||
$progress = new ProgressBar($this->output, count($channels));
|
||||
|
||||
$i = 0;
|
||||
foreach ($channels as $c) {
|
||||
$tag = new Tag;
|
||||
|
||||
$tag->id = $c->channelId;
|
||||
$tag->name = $c->title;
|
||||
$tag->slug = $c->slug;
|
||||
$tag->description = $c->description;
|
||||
$tag->color = $colors[$i++ % count($colors)];
|
||||
$tag->discussions_count = $c->countConversations;
|
||||
$tag->position = $c->lft;
|
||||
|
||||
$tag->save();
|
||||
|
||||
$progress->advance();
|
||||
}
|
||||
|
||||
$progress->finish();
|
||||
$this->info("\n");
|
||||
}
|
||||
|
||||
protected function importUsers($from, $to)
|
||||
{
|
||||
$this->info('Importing users...');
|
||||
|
||||
$to->table('users')->truncate();
|
||||
$to->table('email_tokens')->truncate();
|
||||
$to->table('password_tokens')->truncate();
|
||||
$to->table('access_tokens')->truncate();
|
||||
$to->table('users_groups')->truncate();
|
||||
|
||||
$members = $from->table('member')->get();
|
||||
|
||||
$progress = new ProgressBar($this->output, count($members));
|
||||
|
||||
foreach ($members as $m) {
|
||||
$preferences = unserialize($m->preferences);
|
||||
$user = new User;
|
||||
|
||||
$user->id = $m->memberId;
|
||||
$user->username = $m->username;
|
||||
$user->email = $m->email;
|
||||
$user->is_activated = true;
|
||||
$user->password = '';
|
||||
$user->join_time = $m->joinTime;
|
||||
$user->last_seen_time = $m->lastActionTime;
|
||||
$user->avatar_path = $m->avatarFormat ? $m->memberId.'.'.$m->avatarFormat : null;
|
||||
$user->username = $m->username;
|
||||
$user->read_time = array_get($preferences, 'markedAllConversationsAsRead');
|
||||
$user->notification_read_time = array_get($preferences, 'notificationCheckTime');
|
||||
$user->preferences = ['discloseOnline' => !array_get($preferences, 'hideOnline')];
|
||||
$user->discussions_count = $m->countConversations;
|
||||
$user->comments_count = $m->countPosts;
|
||||
|
||||
$user->save();
|
||||
|
||||
$this->app->make('Flarum\Core\Activity\ActivitySyncer')
|
||||
->sync(new JoinedActivity($user), [$user]);
|
||||
|
||||
$progress->advance();
|
||||
}
|
||||
|
||||
$progress->finish();
|
||||
$this->info("\n");
|
||||
}
|
||||
|
||||
protected function importDiscussions($from, $to)
|
||||
{
|
||||
$this->info('Importing discussions...');
|
||||
|
||||
$to->table('discussions')->truncate();
|
||||
$to->table('discussions_tags')->truncate();
|
||||
$to->table('posts')->truncate();
|
||||
$to->table('notifications')->truncate();
|
||||
$to->table('users_discussions')->truncate();
|
||||
$to->table('activity')->truncate();
|
||||
$to->table('mentions_posts')->truncate();
|
||||
$to->table('mentions_users')->truncate();
|
||||
|
||||
$conversations = $from->table('conversation')->where('private', 0)->get();
|
||||
|
||||
$progress = new ProgressBar($this->output, count($conversations));
|
||||
|
||||
foreach ($conversations as $c) {
|
||||
$discussion = new Discussion;
|
||||
|
||||
$discussion->id = $c->conversationId;
|
||||
$discussion->title = $c->title;
|
||||
$discussion->is_sticky = $c->sticky;
|
||||
|
||||
$discussion->start_user_id = $c->startMemberId;
|
||||
$discussion->start_time = $c->startTime;
|
||||
|
||||
$discussion->last_user_id = $c->lastPostMemberId;
|
||||
$discussion->last_time = $c->lastPostTime;
|
||||
|
||||
$discussion->save();
|
||||
|
||||
$discussion->tags()->sync([$c->channelId]);
|
||||
|
||||
foreach ($from->table('post')->where('conversationId', $c->conversationId)->get() as $p) {
|
||||
$post = new CommentPost;
|
||||
|
||||
$post->id = $p->postId;
|
||||
$post->discussion_id = $p->conversationId;
|
||||
$post->user_id = $p->memberId;
|
||||
$post->time = $p->time;
|
||||
$post->edit_user_id = $p->editMemberId;
|
||||
$post->edit_time = $p->editTime;
|
||||
$post->hide_user_id = $p->deleteMemberId;
|
||||
$post->hide_time = $p->deleteTime;
|
||||
$post->content = $p->content;
|
||||
|
||||
$this->formatPost($post);
|
||||
|
||||
$post->save();
|
||||
|
||||
if (!$post->hide_time) {
|
||||
event(new PostWasPosted($post));
|
||||
}
|
||||
}
|
||||
|
||||
$discussion->last_post_id = $p->postId;
|
||||
$discussion->last_post_number = $post->number;
|
||||
$discussion->comments_count = $post->number;
|
||||
|
||||
$discussion->save();
|
||||
|
||||
$states = $from->table('member_conversation')
|
||||
->where('conversationId', $c->conversationId)
|
||||
->where('type', 'member')
|
||||
->get();
|
||||
foreach ($states as $s) {
|
||||
$state = new DiscussionState;
|
||||
|
||||
$state->discussion_id = $s->conversationId;
|
||||
$state->user_id = $s->id;
|
||||
$state->read_time = time();
|
||||
$state->read_number = $discussion->posts()->orderBy('time', 'asc')->skip(min($discussion->comments_count, $s->lastRead) - 1)->pluck('number');
|
||||
|
||||
$state->save();
|
||||
}
|
||||
|
||||
$progress->advance();
|
||||
}
|
||||
|
||||
$progress->finish();
|
||||
$this->info("\n");
|
||||
}
|
||||
|
||||
protected function formatPost($post)
|
||||
{
|
||||
// Code blocks
|
||||
$regexp = "/(.*)^\s*\[code\]\n?(.*?)\n?\[\/code]$/ims";
|
||||
while (preg_match($regexp, $post->content)) {
|
||||
$post->content = preg_replace($regexp, "$1```\n$2\n```", $post->content);
|
||||
}
|
||||
|
||||
// Inline tags
|
||||
$replace = [
|
||||
'/\[url=(.*?)\](.*?)\[\/url\]/i' => '[$2]($1)',
|
||||
'/\[b\](.*?)\[\/b\]/i' => '**$1**',
|
||||
'/\[i\](.*?)\[\/i\]/i' => '*$1*',
|
||||
'/\[h\](.*?)\[\/h\]/i' => '# $1',
|
||||
'/\[img\](.*?)\[\/img\]/i' => '',
|
||||
'/\[code\](.*?)\[\/code\]/i' => '`$1`'
|
||||
];
|
||||
$post->content = preg_replace(array_keys($replace), array_values($replace), $post->content);
|
||||
|
||||
// Quotes
|
||||
$regexp = "/(.*?)\n?\[quote(?:=(.*?)(]?))?\]\n?(.*?)\n?\[\/quote\]\n{0,2}/is";
|
||||
while (preg_match($regexp, $post->content)) {
|
||||
$post->content = preg_replace_callback($regexp, function ($matches) use ($post) {
|
||||
if (strpos($matches[2], ':') !== false) {
|
||||
list($postId, $user) = explode(':', $matches[2]);
|
||||
$mentionedPost = CommentPost::find($postId);
|
||||
|
||||
return $matches[1]."\n@".$mentionedPost->user->username.'#'.$mentionedPost->number.' ';
|
||||
} else {
|
||||
return $matches[1].'> '.str_replace("\n", "\n> ", $matches[4])."\n\n";
|
||||
}
|
||||
}, $post->content);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,6 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
use Flarum\Core\Model;
|
||||
|
||||
/**
|
||||
* Models a user activity record in the database.
|
||||
@@ -16,23 +18,19 @@
|
||||
class Activity extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $table = 'activity';
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $dates = ['time'];
|
||||
protected static $dateAttributes = ['time'];
|
||||
|
||||
/**
|
||||
* A map of activity types and the model classes to use for their subjects.
|
||||
* For example, the 'posted' activity type, which represents that a user
|
||||
* made a post, has the subject model class 'Flarum\Core\Models\Post'.
|
||||
* made a post, has the subject model class 'Flarum\Core\Posts\Post'.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
@@ -79,7 +77,7 @@ class Activity extends Model
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\User', 'user_id');
|
||||
return $this->belongsTo('Flarum\Core\Users\User', 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +104,8 @@ class Activity extends Model
|
||||
* Set the subject model for the given activity type.
|
||||
*
|
||||
* @param string $type The activity type.
|
||||
* @param string $class The class name of the subject model for that type.
|
||||
* @param string $subjectModel The class name of the subject model for that
|
||||
* type.
|
||||
* @return void
|
||||
*/
|
||||
public static function setSubjectModel($type, $subjectModel)
|
@@ -1,5 +0,0 @@
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
abstract class ActivityAbstract implements ActivityInterface
|
||||
{
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
interface ActivityRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Find a user's activity.
|
||||
*
|
||||
* @param integer $userId
|
||||
* @param \Flarum\Core\Users\User $actor
|
||||
* @param null|integer $count
|
||||
* @param integer $start
|
||||
* @param null|string $type
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function findByUser($userId, User $actor, $count = null, $start = 0, $type = null);
|
||||
}
|
@@ -13,24 +13,29 @@ class ActivityServiceProvider extends ServiceProvider
|
||||
public function boot()
|
||||
{
|
||||
$this->extend([
|
||||
(new Extend\EventSubscriber('Flarum\Core\Handlers\Events\UserActivitySyncer')),
|
||||
(new Extend\EventSubscriber('Flarum\Core\Activity\Listeners\UserActivitySyncer')),
|
||||
|
||||
(new Extend\ActivityType('Flarum\Core\Activity\PostedActivity'))
|
||||
(new Extend\ActivityType('Flarum\Core\Activity\PostedBlueprint'))
|
||||
->subjectSerializer('Flarum\Api\Serializers\PostBasicSerializer'),
|
||||
|
||||
(new Extend\ActivityType('Flarum\Core\Activity\StartedDiscussionActivity'))
|
||||
(new Extend\ActivityType('Flarum\Core\Activity\StartedDiscussionBlueprint'))
|
||||
->subjectSerializer('Flarum\Api\Serializers\PostBasicSerializer'),
|
||||
|
||||
(new Extend\ActivityType('Flarum\Core\Activity\JoinedActivity'))
|
||||
(new Extend\ActivityType('Flarum\Core\Activity\JoinedBlueprint'))
|
||||
->subjectSerializer('Flarum\Api\Serializers\UserBasicSerializer')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->bind(
|
||||
'Flarum\Core\Repositories\ActivityRepositoryInterface',
|
||||
'Flarum\Core\Repositories\EloquentActivityRepository'
|
||||
'Flarum\Core\Activity\ActivityRepositoryInterface',
|
||||
'Flarum\Core\Activity\EloquentActivityRepository'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,22 @@
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
use Flarum\Core\Repositories\ActivityRepositoryInterface;
|
||||
use Flarum\Core\Models\Activity;
|
||||
|
||||
/**
|
||||
* The Activity Syncer commits activity blueprints to the database. Where a
|
||||
* blueprint represents a single piece of activity, the syncer associates it
|
||||
* with a particular user(s) and makes it available on their activity feed.
|
||||
*/
|
||||
class ActivitySyncer
|
||||
{
|
||||
/**
|
||||
* @var ActivityRepositoryInterface
|
||||
*/
|
||||
protected $activity;
|
||||
|
||||
/**
|
||||
* Create a new instance of the activity syncer.
|
||||
*
|
||||
* @param ActivityRepositoryInterface $activity
|
||||
*/
|
||||
public function __construct(ActivityRepositoryInterface $activity)
|
||||
{
|
||||
$this->activity = $activity;
|
||||
@@ -16,38 +26,92 @@ class ActivitySyncer
|
||||
* Sync a piece of activity so that it is present for the specified users,
|
||||
* and not present for anyone else.
|
||||
*
|
||||
* @param \Flarum\Core\Activity\ActivityInterface $activity
|
||||
* @param Blueprint $blueprint
|
||||
* @param \Flarum\Core\Models\User[] $users
|
||||
* @return void
|
||||
*/
|
||||
public function sync(ActivityInterface $activity, array $users)
|
||||
public function sync(Blueprint $blueprint, array $users)
|
||||
{
|
||||
Activity::unguard();
|
||||
|
||||
$attributes = [
|
||||
'type' => $activity::getType(),
|
||||
'subject_id' => $activity->getSubject()->id,
|
||||
'time' => $activity->getTime()
|
||||
];
|
||||
$attributes = $this->getAttributes($blueprint);
|
||||
|
||||
// Find all existing activity records in the database matching this
|
||||
// blueprint. We will begin by assuming that they all need to be
|
||||
// deleted in order to match the provided list of users.
|
||||
$toDelete = Activity::where($attributes)->get();
|
||||
$toInsert = [];
|
||||
|
||||
// For each of the provided users, check to see if they already have
|
||||
// an activity record in the database. If they do, we can leave it be;
|
||||
// otherwise, we will need to create a new one for them.
|
||||
foreach ($users as $user) {
|
||||
$existing = $toDelete->where('user_id', $user->id)->first();
|
||||
$existing = $toDelete->first(function ($activity) use ($user) {
|
||||
return $activity->user_id === $user->id;
|
||||
});
|
||||
|
||||
if ($k = $toDelete->search($existing)) {
|
||||
$toDelete->pull($k);
|
||||
if ($existing) {
|
||||
$toDelete->forget($toDelete->search($existing));
|
||||
} else {
|
||||
$toInsert[] = $attributes + ['user_id' => $user->id];
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, delete all of the remaining activity records which weren't
|
||||
// removed from this collection by the above loop. Insert the records
|
||||
// we need to insert as well.
|
||||
if (count($toDelete)) {
|
||||
Activity::whereIn('id', $toDelete->lists('id'))->delete();
|
||||
$this->deleteActivity($toDelete->lists('id'));
|
||||
}
|
||||
|
||||
if (count($toInsert)) {
|
||||
Activity::insert($toInsert);
|
||||
$this->createActivity($toInsert);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a piece of activity for all users.
|
||||
*
|
||||
* @param Blueprint $blueprint
|
||||
* @return void
|
||||
*/
|
||||
public function delete(Blueprint $blueprint)
|
||||
{
|
||||
Activity::where($this->getAttributes($blueprint))->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a list of activity records.
|
||||
*
|
||||
* @param int[] $ids
|
||||
*/
|
||||
protected function deleteActivity(array $ids)
|
||||
{
|
||||
Activity::whereIn('id', $ids)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a list of activity record into the database.
|
||||
*
|
||||
* @param array[] $records An array containing arrays of activity record
|
||||
* attributes to insert.
|
||||
*/
|
||||
protected function createActivity(array $records)
|
||||
{
|
||||
Activity::insert($records);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an array of attributes to be stored in an activity record in
|
||||
* the database, given an activity blueprint.
|
||||
*
|
||||
* @param Blueprint $blueprint
|
||||
* @return array
|
||||
*/
|
||||
protected function getAttributes(Blueprint $blueprint)
|
||||
{
|
||||
return [
|
||||
'type' => $blueprint::getType(),
|
||||
'subject_id' => $blueprint->getSubject()->id,
|
||||
'time' => $blueprint->getTime()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,16 @@
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
interface ActivityInterface
|
||||
/**
|
||||
* An activity Blueprint, when instantiated, represents a single piece of
|
||||
* activity. The blueprint is used by the ActivitySyncer to commit the activity
|
||||
* to the database.
|
||||
*/
|
||||
interface Blueprint
|
||||
{
|
||||
/**
|
||||
* Get the model that is the subject of this activity.
|
||||
*
|
||||
* @return \Flarum\Core\Models\Model
|
||||
* @return \Flarum\Core\Model
|
||||
*/
|
||||
public function getSubject();
|
||||
|
@@ -0,0 +1,35 @@
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class EloquentActivityRepository implements ActivityRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function findByUser($userId, User $actor, $limit = null, $offset = 0, $type = null)
|
||||
{
|
||||
$query = Activity::where('user_id', $userId)
|
||||
->whereIn('type', $this->getRegisteredTypes())
|
||||
->latest('time')
|
||||
->skip($offset)
|
||||
->take($limit);
|
||||
|
||||
if ($type !== null) {
|
||||
$query->where('type', $type);
|
||||
}
|
||||
|
||||
return $query->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of activity types that have been registered with the activity
|
||||
* model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getRegisteredTypes()
|
||||
{
|
||||
return array_keys(Activity::getSubjectModels());
|
||||
}
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class JoinedActivity extends ActivityAbstract
|
||||
{
|
||||
protected $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function getSubject()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function getTime()
|
||||
{
|
||||
return $this->user->join_time;
|
||||
}
|
||||
|
||||
public static function getType()
|
||||
{
|
||||
return 'joined';
|
||||
}
|
||||
|
||||
public static function getSubjectModel()
|
||||
{
|
||||
return 'Flarum\Core\Models\User';
|
||||
}
|
||||
}
|
59
framework/core/src/Core/Activity/JoinedBlueprint.php
Normal file
59
framework/core/src/Core/Activity/JoinedBlueprint.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
/**
|
||||
* An activity blueprint for the 'joined' activity type, which represents a user
|
||||
* joining the forum.
|
||||
*/
|
||||
class JoinedBlueprint implements Blueprint
|
||||
{
|
||||
/**
|
||||
* The user who joined the forum.
|
||||
*
|
||||
* @var User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Create a new 'joined' activity blueprint.
|
||||
*
|
||||
* @param User $user The user who joined the forum.
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSubject()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTime()
|
||||
{
|
||||
return $this->user->join_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getType()
|
||||
{
|
||||
return 'joined';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubjectModel()
|
||||
{
|
||||
return 'Flarum\Core\Users\User';
|
||||
}
|
||||
}
|
126
framework/core/src/Core/Activity/Listeners/UserActivitySyncer.php
Executable file
126
framework/core/src/Core/Activity/Listeners/UserActivitySyncer.php
Executable file
@@ -0,0 +1,126 @@
|
||||
<?php namespace Flarum\Core\Activity\Listeners;
|
||||
|
||||
use Flarum\Core\Activity\ActivitySyncer;
|
||||
use Flarum\Core\Activity\PostedBlueprint;
|
||||
use Flarum\Core\Activity\StartedDiscussionBlueprint;
|
||||
use Flarum\Core\Activity\JoinedBlueprint;
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Core\Posts\Events\PostWasPosted;
|
||||
use Flarum\Core\Posts\Events\PostWasDeleted;
|
||||
use Flarum\Core\Posts\Events\PostWasHidden;
|
||||
use Flarum\Core\Posts\Events\PostWasRestored;
|
||||
use Flarum\Core\Users\Events\UserWasRegistered;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class UserActivitySyncer
|
||||
{
|
||||
/**
|
||||
* @var \Flarum\Core\Activity\ActivitySyncer
|
||||
*/
|
||||
protected $activity;
|
||||
|
||||
/**
|
||||
* @param \Flarum\Core\Activity\ActivitySyncer $activity
|
||||
*/
|
||||
public function __construct(ActivitySyncer $activity)
|
||||
{
|
||||
$this->activity = $activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Contracts\Events\Dispatcher $events
|
||||
* @return void
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen('Flarum\Core\Events\PostWasPosted', __CLASS__.'@whenPostWasPosted');
|
||||
$events->listen('Flarum\Core\Events\PostWasHidden', __CLASS__.'@whenPostWasHidden');
|
||||
$events->listen('Flarum\Core\Events\PostWasRestored', __CLASS__.'@whenPostWasRestored');
|
||||
$events->listen('Flarum\Core\Events\PostWasDeleted', __CLASS__.'@whenPostWasDeleted');
|
||||
$events->listen('Flarum\Core\Events\UserWasRegistered', __CLASS__.'@whenUserWasRegistered');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\Core\Posts\Events\PostWasPosted $event
|
||||
* @return void
|
||||
*/
|
||||
public function whenPostWasPosted(PostWasPosted $event)
|
||||
{
|
||||
$this->postBecameVisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\Core\Posts\Events\PostWasHidden $event
|
||||
* @return void
|
||||
*/
|
||||
public function whenPostWasHidden(PostWasHidden $event)
|
||||
{
|
||||
$this->postBecameInvisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\Core\Posts\Events\PostWasRestored $event
|
||||
* @return void
|
||||
*/
|
||||
public function whenPostWasRestored(PostWasRestored $event)
|
||||
{
|
||||
$this->postBecameVisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\Core\Posts\Events\PostWasDeleted $event
|
||||
* @return void
|
||||
*/
|
||||
public function whenPostWasDeleted(PostWasDeleted $event)
|
||||
{
|
||||
$this->postBecameInvisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\Core\Users\Events\UserWasRegistered $event
|
||||
* @return void
|
||||
*/
|
||||
public function whenUserWasRegistered(UserWasRegistered $event)
|
||||
{
|
||||
$blueprint = new JoinedBlueprint($event->user);
|
||||
|
||||
$this->activity->sync($blueprint, [$event->user]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync activity to a post's author when a post becomes visible.
|
||||
*
|
||||
* @param \Flarum\Core\Posts\Post $post
|
||||
* @return void
|
||||
*/
|
||||
protected function postBecameVisible(Post $post)
|
||||
{
|
||||
$blueprint = $this->postedBlueprint($post);
|
||||
|
||||
$this->activity->sync($blueprint, [$post->user]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete activity when a post becomes invisible.
|
||||
*
|
||||
* @param \Flarum\Core\Posts\Post $post
|
||||
* @return void
|
||||
*/
|
||||
protected function postBecameInvisible(Post $post)
|
||||
{
|
||||
$blueprint = $this->postedBlueprint($post);
|
||||
|
||||
$this->activity->delete($blueprint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the appropriate activity blueprint for a post.
|
||||
*
|
||||
* @param \Flarum\Core\Posts\Post $post
|
||||
* @return \Flarum\Core\Activity\Blueprint
|
||||
*/
|
||||
protected function postedBlueprint(Post $post)
|
||||
{
|
||||
return $post->number == 1 ? new StartedDiscussionBlueprint($post) : new PostedBlueprint($post);
|
||||
}
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
use Flarum\Core\Models\Post;
|
||||
|
||||
class PostedActivity extends ActivityAbstract
|
||||
{
|
||||
protected $post;
|
||||
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
|
||||
public function getSubject()
|
||||
{
|
||||
return $this->post;
|
||||
}
|
||||
|
||||
public function getTime()
|
||||
{
|
||||
return $this->post->time;
|
||||
}
|
||||
|
||||
public static function getType()
|
||||
{
|
||||
return 'posted';
|
||||
}
|
||||
|
||||
public static function getSubjectModel()
|
||||
{
|
||||
return 'Flarum\Core\Models\Post';
|
||||
}
|
||||
}
|
59
framework/core/src/Core/Activity/PostedBlueprint.php
Normal file
59
framework/core/src/Core/Activity/PostedBlueprint.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
use Flarum\Core\Posts\Post;
|
||||
|
||||
/**
|
||||
* An activity blueprint for the 'posted' activity type, which represents a user
|
||||
* posting in a discussion.
|
||||
*/
|
||||
class PostedBlueprint implements Blueprint
|
||||
{
|
||||
/**
|
||||
* The user who joined the forum.
|
||||
*
|
||||
* @var Post
|
||||
*/
|
||||
protected $post;
|
||||
|
||||
/**
|
||||
* Create a new 'posted' activity blueprint.
|
||||
*
|
||||
* @param Post $post The post that was made.
|
||||
*/
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSubject()
|
||||
{
|
||||
return $this->post;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTime()
|
||||
{
|
||||
return $this->post->time;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getType()
|
||||
{
|
||||
return 'posted';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubjectModel()
|
||||
{
|
||||
return 'Flarum\Core\Posts\Post';
|
||||
}
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
class StartedDiscussionActivity extends PostedActivity
|
||||
{
|
||||
public static function getType()
|
||||
{
|
||||
return 'startedDiscussion';
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
/**
|
||||
* An activity blueprint for the 'startedDiscussion' activity type, which
|
||||
* represents a user starting a discussion.
|
||||
*/
|
||||
class StartedDiscussionBlueprint extends PostedBlueprint
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getType()
|
||||
{
|
||||
return 'startedDiscussion';
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class ConfirmEmailCommand
|
||||
{
|
||||
public $token;
|
||||
|
||||
public function __construct($token)
|
||||
{
|
||||
$this->token = $token;
|
||||
}
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class DeleteAvatarCommand
|
||||
{
|
||||
public $userId;
|
||||
|
||||
public $actor;
|
||||
|
||||
public function __construct($userId, $actor)
|
||||
{
|
||||
if (empty($userId) || !intval($userId)) {
|
||||
throw new RuntimeException('No valid user ID specified.');
|
||||
}
|
||||
|
||||
$this->userId = $userId;
|
||||
$this->actor = $actor;
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class DeleteDiscussionCommand
|
||||
{
|
||||
public $discussionId;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($discussionId, $user)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class DeletePostCommand
|
||||
{
|
||||
public $postId;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($postId, $user)
|
||||
{
|
||||
$this->postId = $postId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class DeleteUserCommand
|
||||
{
|
||||
public $userId;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($userId, $user)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class EditDiscussionCommand
|
||||
{
|
||||
public $discussionId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $data;
|
||||
|
||||
public function __construct($discussionId, $user, $data)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->user = $user;
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class EditPostCommand
|
||||
{
|
||||
public $postId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $data;
|
||||
|
||||
public function __construct($postId, $user, $data)
|
||||
{
|
||||
$this->postId = $postId;
|
||||
$this->user = $user;
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class EditUserCommand
|
||||
{
|
||||
public $userId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $data;
|
||||
|
||||
public function __construct($userId, $user, $data)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
$this->user = $user;
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class GenerateAccessTokenCommand
|
||||
{
|
||||
public $userId;
|
||||
|
||||
public function __construct($userId)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class PostReplyCommand
|
||||
{
|
||||
public $discussionId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $data;
|
||||
|
||||
public function __construct($discussionId, $user, $data)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->user = $user;
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class ReadDiscussionCommand
|
||||
{
|
||||
public $discussionId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $readNumber;
|
||||
|
||||
public function __construct($discussionId, $user, $readNumber)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->user = $user;
|
||||
$this->readNumber = $readNumber;
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class ReadNotificationCommand
|
||||
{
|
||||
public $notificationId;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($notificationId, $user)
|
||||
{
|
||||
$this->notificationId = $notificationId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class RegisterUserCommand
|
||||
{
|
||||
public $forum;
|
||||
|
||||
public $user;
|
||||
|
||||
public $data;
|
||||
|
||||
public function __construct($user, $forum, $data)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->forum = $forum;
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class RequestPasswordResetCommand
|
||||
{
|
||||
public $email;
|
||||
|
||||
public function __construct($email)
|
||||
{
|
||||
$this->email = $email;
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class StartDiscussionCommand
|
||||
{
|
||||
public $user;
|
||||
|
||||
public $forum;
|
||||
|
||||
public $data;
|
||||
|
||||
public function __construct($user, $forum, $data)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->forum = $forum;
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class UploadAvatarCommand
|
||||
{
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* @var \Psr\Http\Message\UploadedFileInterface
|
||||
*/
|
||||
public $file;
|
||||
|
||||
public $actor;
|
||||
|
||||
public function __construct($userId, UploadedFileInterface $file, $actor)
|
||||
{
|
||||
if (empty($userId) || !intval($userId)) {
|
||||
throw new RuntimeException('No valid user ID specified.');
|
||||
}
|
||||
|
||||
if (is_null($file)) {
|
||||
throw new RuntimeException('No file to upload');
|
||||
}
|
||||
|
||||
$this->userId = $userId;
|
||||
$this->file = $file;
|
||||
$this->actor = $actor;
|
||||
}
|
||||
}
|
@@ -1,15 +1,7 @@
|
||||
<?php namespace Flarum\Core;
|
||||
|
||||
use Illuminate\Bus\Dispatcher as Bus;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Support\ServiceProvider;
|
||||
use Flarum\Core\Models\CommentPost;
|
||||
use Flarum\Core\Models\Post;
|
||||
use Flarum\Core\Models\Model;
|
||||
use Flarum\Core\Models\Forum;
|
||||
use Flarum\Core\Models\User;
|
||||
use Flarum\Core\Models\Discussion;
|
||||
use Flarum\Core\Search\GambitManager;
|
||||
use Flarum\Extend;
|
||||
|
||||
class CoreServiceProvider extends ServiceProvider
|
||||
@@ -23,21 +15,14 @@ class CoreServiceProvider extends ServiceProvider
|
||||
{
|
||||
$this->loadViewsFrom(__DIR__.'/../../views', 'flarum');
|
||||
|
||||
$this->addEventHandlers();
|
||||
$this->bootModels();
|
||||
$this->addPostTypes();
|
||||
$this->grantPermissions();
|
||||
$this->mapCommandHandlers();
|
||||
}
|
||||
$this->app->make('Illuminate\Contracts\Bus\Dispatcher')->mapUsing(function ($command) {
|
||||
return get_class($command).'Handler@handle';
|
||||
});
|
||||
|
||||
public function mapCommandHandlers()
|
||||
{
|
||||
$this->app->make(Bus::class)->mapUsing(function ($command) {
|
||||
return Bus::simpleMapping(
|
||||
$command,
|
||||
'Flarum\Core\Commands',
|
||||
'Flarum\Core\Handlers\Commands'
|
||||
);
|
||||
Model::setValidator($this->app['validator']);
|
||||
|
||||
Forum::allow('*', function (Forum $forum, User $user, $action) {
|
||||
return $user->hasPermission('forum.'.$action) ?: null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -48,205 +33,15 @@ class CoreServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Register a singleton entity that represents this forum. This entity
|
||||
// will be used to check for global forum permissions (like viewing the
|
||||
// forum, registering, and starting discussions).
|
||||
$this->app->singleton('flarum.forum', 'Flarum\Core\Models\Forum');
|
||||
$this->app->singleton('flarum.forum', 'Flarum\Core\Forum');
|
||||
|
||||
// TODO: probably use Illuminate's AggregateServiceProvider
|
||||
// functionality, because it includes the 'provides' stuff.
|
||||
$this->app->register('Flarum\Core\Activity\ActivityServiceProvider');
|
||||
$this->app->register('Flarum\Core\Discussions\DiscussionsServiceProvider');
|
||||
$this->app->register('Flarum\Core\Formatter\FormatterServiceProvider');
|
||||
$this->app->register('Flarum\Core\Notifications\NotificationsServiceProvider');
|
||||
|
||||
// TODO: refactor these into the appropriate service providers, when
|
||||
// (if) we restructure our namespaces per-entity
|
||||
// (Flarum\Core\Discussions\DiscussionsServiceProvider, etc.)
|
||||
$this->app->bind(
|
||||
'Flarum\Core\Repositories\DiscussionRepositoryInterface',
|
||||
'Flarum\Core\Repositories\EloquentDiscussionRepository'
|
||||
);
|
||||
|
||||
$this->app->bind(
|
||||
'Flarum\Core\Repositories\PostRepositoryInterface',
|
||||
'Flarum\Core\Repositories\EloquentPostRepository'
|
||||
);
|
||||
|
||||
$this->app->bind(
|
||||
'Flarum\Core\Repositories\UserRepositoryInterface',
|
||||
'Flarum\Core\Repositories\EloquentUserRepository'
|
||||
);
|
||||
|
||||
$this->app->bind(
|
||||
'Flarum\Core\Search\Discussions\Fulltext\DriverInterface',
|
||||
'Flarum\Core\Search\Discussions\Fulltext\MySqlFulltextDriver'
|
||||
);
|
||||
|
||||
$this->registerDiscussionGambits();
|
||||
$this->registerUserGambits();
|
||||
$this->registerAvatarsFilesystem();
|
||||
}
|
||||
|
||||
public function registerAvatarsFilesystem()
|
||||
{
|
||||
$avatarsFilesystem = function (Container $app) {
|
||||
return $app->make('Illuminate\Contracts\Filesystem\Factory')->disk('flarum-avatars')->getDriver();
|
||||
};
|
||||
|
||||
$this->app->when('Flarum\Core\Handlers\Commands\UploadAvatarCommandHandler')
|
||||
->needs('League\Flysystem\FilesystemInterface')
|
||||
->give($avatarsFilesystem);
|
||||
|
||||
$this->app->when('Flarum\Core\Handlers\Commands\DeleteAvatarCommandHandler')
|
||||
->needs('League\Flysystem\FilesystemInterface')
|
||||
->give($avatarsFilesystem);
|
||||
}
|
||||
|
||||
public function registerDiscussionGambits()
|
||||
{
|
||||
$this->app->instance('flarum.discussionGambits', [
|
||||
'Flarum\Core\Search\Discussions\Gambits\AuthorGambit',
|
||||
'Flarum\Core\Search\Discussions\Gambits\UnreadGambit'
|
||||
]);
|
||||
|
||||
$this->app->when('Flarum\Core\Search\Discussions\DiscussionSearcher')
|
||||
->needs('Flarum\Core\Search\GambitManager')
|
||||
->give(function (Container $app) {
|
||||
$gambits = new GambitManager($app);
|
||||
|
||||
foreach ($app->make('flarum.discussionGambits') as $gambit) {
|
||||
$gambits->add($gambit);
|
||||
}
|
||||
|
||||
$gambits->setFulltextGambit('Flarum\Core\Search\Discussions\Gambits\FulltextGambit');
|
||||
|
||||
return $gambits;
|
||||
});
|
||||
}
|
||||
|
||||
public function registerUserGambits()
|
||||
{
|
||||
$this->app->instance('flarum.userGambits', []);
|
||||
|
||||
$this->app->when('Flarum\Core\Search\Users\UserSearcher')
|
||||
->needs('Flarum\Core\Search\GambitManager')
|
||||
->give(function (Container $app) {
|
||||
$gambits = new GambitManager($app);
|
||||
|
||||
foreach ($app->make('flarum.userGambits') as $gambit) {
|
||||
$gambits->add($gambit);
|
||||
}
|
||||
|
||||
$gambits->setFulltextGambit('Flarum\Core\Search\Users\Gambits\FulltextGambit');
|
||||
|
||||
return $gambits;
|
||||
});
|
||||
}
|
||||
|
||||
public function addPostTypes()
|
||||
{
|
||||
$this->extend([
|
||||
new Extend\PostType('Flarum\Core\Models\CommentPost'),
|
||||
new Extend\PostType('Flarum\Core\Models\DiscussionRenamedPost')
|
||||
]);
|
||||
}
|
||||
|
||||
public function addEventHandlers()
|
||||
{
|
||||
$this->extend([
|
||||
new Extend\EventSubscriber('Flarum\Core\Handlers\Events\DiscussionMetadataUpdater'),
|
||||
new Extend\EventSubscriber('Flarum\Core\Handlers\Events\UserMetadataUpdater'),
|
||||
new Extend\EventSubscriber('Flarum\Core\Handlers\Events\EmailConfirmationMailer')
|
||||
]);
|
||||
}
|
||||
|
||||
public function bootModels()
|
||||
{
|
||||
Model::setValidator($this->app['validator']);
|
||||
|
||||
CommentPost::setFormatter($this->app['flarum.formatter']);
|
||||
|
||||
User::setHasher($this->app['hash']);
|
||||
User::setFormatter($this->app['flarum.formatter']);
|
||||
|
||||
User::registerPreference('discloseOnline', 'boolval', true);
|
||||
User::registerPreference('indexProfile', 'boolval', true);
|
||||
}
|
||||
|
||||
public function grantPermissions()
|
||||
{
|
||||
Forum::allow('*', function ($forum, $user, $action) {
|
||||
if ($user->hasPermission('forum.'.$action)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Post::allow('*', function ($post, $user, $action) {
|
||||
if ($post->discussion->can($user, $action.'Posts')) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// When fetching a discussion's posts: if the user doesn't have permission
|
||||
// to moderate the discussion, then they can't see posts that have been
|
||||
// hidden by someone other than themself.
|
||||
Discussion::addVisiblePostsScope(function ($query, User $user, Discussion $discussion) {
|
||||
if (! $discussion->can($user, 'editPosts')) {
|
||||
$query->where(function ($query) use ($user) {
|
||||
$query->whereNull('hide_user_id')
|
||||
->orWhere('hide_user_id', $user->id);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Post::allow('view', function ($post, $user) {
|
||||
if (! $post->hide_user_id || $post->can($user, 'edit')) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// A post is allowed to be edited if the user has permission to moderate
|
||||
// the discussion which it's in, or if they are the author and the post
|
||||
// hasn't been deleted by someone else.
|
||||
Post::allow('edit', function ($post, $user) {
|
||||
if ($post->discussion->can($user, 'editPosts') ||
|
||||
($post->user_id == $user->id && (! $post->hide_user_id || $post->hide_user_id == $user->id))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
User::allow('*', function ($discussion, $user, $action) {
|
||||
if ($user->hasPermission('user.'.$action)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
User::allow(['edit', 'delete'], function ($user, $actor) {
|
||||
if ($user->id == $actor->id) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Discussion::allow('*', function ($discussion, $user, $action) {
|
||||
if ($user->hasPermission('discussion.'.$action)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Allow a user to rename their own discussion.
|
||||
Discussion::allow('rename', function ($discussion, $user) {
|
||||
if ($discussion->start_user_id == $user->id) {
|
||||
return true;
|
||||
// @todo add limitations to time etc. according to a config setting
|
||||
}
|
||||
});
|
||||
|
||||
Discussion::allow('delete', function ($discussion, $user) {
|
||||
if ($discussion->start_user_id == $user->id && $discussion->participants_count == 1) {
|
||||
return true;
|
||||
// @todo add limitations to time etc. according to a config setting
|
||||
}
|
||||
});
|
||||
$this->app->register('Flarum\Core\Posts\PostsServiceProvider');
|
||||
$this->app->register('Flarum\Core\Users\UsersServiceProvider');
|
||||
}
|
||||
}
|
||||
|
@@ -40,6 +40,8 @@ class DatabaseServiceProvider extends ServiceProvider
|
||||
$this->app->booting(function() {
|
||||
$resolver = $this->app->make('Illuminate\Database\ConnectionResolverInterface');
|
||||
Model::setConnectionResolver($resolver);
|
||||
|
||||
Model::setEventDispatcher($this->app->make('events'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,41 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class DeleteDiscussion
|
||||
{
|
||||
/**
|
||||
* The ID of the discussion to delete.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $discussionId;
|
||||
|
||||
/**
|
||||
* The user performing the action.
|
||||
*
|
||||
* @var User
|
||||
*/
|
||||
public $actor;
|
||||
|
||||
/**
|
||||
* Any other user input associated with the action. This is unused by
|
||||
* default, but may be used by extensions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $data;
|
||||
|
||||
/**
|
||||
* @param int $discussionId The ID of the discussion to delete.
|
||||
* @param User $actor The user performing the action.
|
||||
* @param array $data Any other user input associated with the action. This
|
||||
* is unused by default, but may be used by extensions.
|
||||
*/
|
||||
public function __construct($discussionId, User $actor, array $data = [])
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->actor = $actor;
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Flarum\Core\Discussions\DiscussionRepositoryInterface;
|
||||
use Flarum\Core\Discussions\Events\DiscussionWillBeDeleted;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class DeleteDiscussionHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
/**
|
||||
* @var \Flarum\Core\Discussions\DiscussionRepositoryInterface
|
||||
*/
|
||||
protected $discussions;
|
||||
|
||||
/**
|
||||
* @param \Flarum\Core\Discussions\DiscussionRepositoryInterface $discussions
|
||||
*/
|
||||
public function __construct(DiscussionRepositoryInterface $discussions)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\Core\Discussions\Commands\DeleteDiscussion $command
|
||||
* @return \Flarum\Core\Discussions\Discussion
|
||||
*/
|
||||
public function handle(DeleteDiscussion $command)
|
||||
{
|
||||
$actor = $command->actor;
|
||||
|
||||
$discussion = $this->discussions->findOrFail($command->discussionId, $actor);
|
||||
|
||||
$discussion->assertCan($actor, 'delete');
|
||||
|
||||
event(new DiscussionWillBeDeleted($discussion, $actor, $command->data));
|
||||
|
||||
$discussion->delete();
|
||||
|
||||
$this->dispatchEventsFor($discussion);
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class EditDiscussion
|
||||
{
|
||||
/**
|
||||
* The ID of the discussion to edit.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $discussionId;
|
||||
|
||||
/**
|
||||
* The user performing the action.
|
||||
*
|
||||
* @var \Flarum\Core\Users\User
|
||||
*/
|
||||
public $actor;
|
||||
|
||||
/**
|
||||
* The attributes to update on the discussion.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $data;
|
||||
|
||||
/**
|
||||
* @param integer $discussionId The ID of the discussion to edit.
|
||||
* @param \Flarum\Core\Users\User $actor The user performing the action.
|
||||
* @param array $data The attributes to update on the discussion.
|
||||
*/
|
||||
public function __construct($discussionId, User $actor, array $data)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->actor = $actor;
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Flarum\Core\Discussions\DiscussionRepositoryInterface;
|
||||
use Flarum\Core\Discussions\Events\DiscussionWillBeSaved;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class EditDiscussionHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
/**
|
||||
* @var DiscussionRepositoryInterface
|
||||
*/
|
||||
protected $discussions;
|
||||
|
||||
/**
|
||||
* @param DiscussionRepositoryInterface $discussions
|
||||
*/
|
||||
public function __construct(DiscussionRepositoryInterface $discussions)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EditDiscussion $command
|
||||
* @return \Flarum\Core\Discussions\Discussion
|
||||
* @throws \Flarum\Core\Exceptions\PermissionDeniedException
|
||||
*/
|
||||
public function handle(EditDiscussion $command)
|
||||
{
|
||||
$actor = $command->actor;
|
||||
$data = $command->data;
|
||||
$attributes = array_get($data, 'attributes', []);
|
||||
|
||||
$discussion = $this->discussions->findOrFail($command->discussionId, $actor);
|
||||
|
||||
if (isset($attributes['title'])) {
|
||||
$discussion->assertCan($actor, 'rename');
|
||||
$discussion->rename($attributes['title'], $actor);
|
||||
}
|
||||
|
||||
event(new DiscussionWillBeSaved($discussion, $actor, $data));
|
||||
|
||||
$discussion->save();
|
||||
|
||||
$this->dispatchEventsFor($discussion);
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class ReadDiscussion
|
||||
{
|
||||
/**
|
||||
* The ID of the discussion to mark as read.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $discussionId;
|
||||
|
||||
/**
|
||||
* The user to mark the discussion as read for.
|
||||
*
|
||||
* @var User
|
||||
*/
|
||||
public $actor;
|
||||
|
||||
/**
|
||||
* The number of the post to mark as read.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $readNumber;
|
||||
|
||||
/**
|
||||
* @param integer $discussionId The ID of the discussion to mark as read.
|
||||
* @param User $actor The user to mark the discussion as read for.
|
||||
* @param integer $readNumber The number of the post to mark as read.
|
||||
*/
|
||||
public function __construct($discussionId, User $actor, $readNumber)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->actor = $actor;
|
||||
$this->readNumber = $readNumber;
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Flarum\Core\Discussions\DiscussionRepositoryInterface;
|
||||
use Flarum\Core\Discussions\Events\DiscussionStateWillBeSaved;
|
||||
use Flarum\Core\Exceptions\PermissionDeniedException;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class ReadDiscussionHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
/**
|
||||
* @var DiscussionRepositoryInterface
|
||||
*/
|
||||
protected $discussions;
|
||||
|
||||
/**
|
||||
* @param DiscussionRepositoryInterface $discussions
|
||||
*/
|
||||
public function __construct(DiscussionRepositoryInterface $discussions)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReadDiscussion $command
|
||||
* @return \Flarum\Core\Discussions\DiscussionState
|
||||
* @throws \Flarum\Core\Exceptions\PermissionDeniedException
|
||||
*/
|
||||
public function handle(ReadDiscussion $command)
|
||||
{
|
||||
$actor = $command->actor;
|
||||
|
||||
if (! $actor->exists) {
|
||||
throw new PermissionDeniedException;
|
||||
}
|
||||
|
||||
$discussion = $this->discussions->findOrFail($command->discussionId, $actor);
|
||||
|
||||
$state = $discussion->stateFor($actor);
|
||||
$state->read($command->readNumber);
|
||||
|
||||
event(new DiscussionStateWillBeSaved($state));
|
||||
|
||||
$state->save();
|
||||
|
||||
$this->dispatchEventsFor($state);
|
||||
|
||||
return $state;
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class StartDiscussion
|
||||
{
|
||||
/**
|
||||
* The user authoring the discussion.
|
||||
*
|
||||
* @var User
|
||||
*/
|
||||
public $actor;
|
||||
|
||||
/**
|
||||
* The discussion attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $data;
|
||||
|
||||
/**
|
||||
* @param User $actor The user authoring the discussion.
|
||||
* @param array $data The discussion attributes.
|
||||
*/
|
||||
public function __construct(User $actor, array $data)
|
||||
{
|
||||
$this->actor = $actor;
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
@@ -1,43 +1,63 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Flarum\Core\Discussions\Events\DiscussionWillBeSaved;
|
||||
use Flarum\Core\Forum;
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Flarum\Core\Models\Discussion;
|
||||
use Flarum\Core\Events\DiscussionWillBeSaved;
|
||||
use Flarum\Core\Commands\PostReplyCommand;
|
||||
use Flarum\Core\Discussions\Discussion;
|
||||
use Flarum\Core\Posts\Commands\PostReply;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class StartDiscussionCommandHandler
|
||||
class StartDiscussionHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
/**
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $bus;
|
||||
|
||||
public function __construct(Dispatcher $bus)
|
||||
/**
|
||||
* @var Forum
|
||||
*/
|
||||
protected $forum;
|
||||
|
||||
/**
|
||||
* @param Dispatcher $bus
|
||||
*/
|
||||
public function __construct(Dispatcher $bus, Forum $forum)
|
||||
{
|
||||
$this->bus = $bus;
|
||||
$this->forum = $forum;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
/**
|
||||
* @param StartDiscussion $command
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(StartDiscussion $command)
|
||||
{
|
||||
$command->forum->assertCan($command->user, 'startDiscussion');
|
||||
$actor = $command->actor;
|
||||
$data = $command->data;
|
||||
|
||||
$this->forum->assertCan($actor, 'startDiscussion');
|
||||
|
||||
// Create a new Discussion entity, persist it, and dispatch domain
|
||||
// events. Before persistance, though, fire an event to give plugins
|
||||
// an opportunity to alter the discussion entity based on data in the
|
||||
// command they may have passed through in the controller.
|
||||
$discussion = Discussion::start(
|
||||
array_get($command->data, 'title'),
|
||||
$command->user
|
||||
array_get($data, 'attributes.title'),
|
||||
$actor
|
||||
);
|
||||
|
||||
event(new DiscussionWillBeSaved($discussion, $command));
|
||||
event(new DiscussionWillBeSaved($discussion, $actor, $data));
|
||||
|
||||
$discussion->save();
|
||||
|
||||
// Now that the discussion has been created, we can add the first post.
|
||||
// We will do this by running the PostReply command.
|
||||
$post = $this->bus->dispatch(
|
||||
new PostReplyCommand($discussion->id, $command->user, $command->data)
|
||||
new PostReply($discussion->id, $actor, $data)
|
||||
);
|
||||
|
||||
// Before we dispatch events, refresh our discussion instance's
|
||||
@@ -50,6 +70,6 @@ class StartDiscussionCommandHandler
|
||||
|
||||
$discussion->save();
|
||||
|
||||
return $post->discussion;
|
||||
return $discussion;
|
||||
}
|
||||
}
|
@@ -1,13 +1,17 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
<?php namespace Flarum\Core\Discussions;
|
||||
|
||||
use Flarum\Core\Model;
|
||||
use Flarum\Core\Discussions\Events\DiscussionWasDeleted;
|
||||
use Flarum\Core\Discussions\Events\DiscussionWasStarted;
|
||||
use Flarum\Core\Discussions\Events\DiscussionWasRenamed;
|
||||
use Flarum\Core\Posts\Events\PostWasDeleted;
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Core\Posts\MergeablePost;
|
||||
use Flarum\Core\Users\Guest;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Core\Support\EventGenerator;
|
||||
use Flarum\Core\Support\Locked;
|
||||
use Flarum\Core\Support\VisibleScope;
|
||||
use Flarum\Core\Events\DiscussionWasDeleted;
|
||||
use Flarum\Core\Events\DiscussionWasStarted;
|
||||
use Flarum\Core\Events\DiscussionWasRenamed;
|
||||
use Flarum\Core\Events\PostWasDeleted;
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class Discussion extends Model
|
||||
{
|
||||
@@ -50,16 +54,16 @@ class Discussion extends Model
|
||||
/**
|
||||
* The user for which the state relationship should be loaded.
|
||||
*
|
||||
* @var \Flarum\Core\Models\User
|
||||
* @var User
|
||||
*/
|
||||
protected static $stateUser;
|
||||
|
||||
/**
|
||||
* An array of callables that apply constraints to the visiblePosts query.
|
||||
* An array of callables that apply constraints to the postsVisibleTo query.
|
||||
*
|
||||
* @var callable[]
|
||||
*/
|
||||
protected static $visiblePostsScopes = [];
|
||||
protected static $postVisibilityScopes = [];
|
||||
|
||||
/**
|
||||
* Boot the model.
|
||||
@@ -94,8 +98,8 @@ class Discussion extends Model
|
||||
* Start a new discussion. Raises the DiscussionWasStarted event.
|
||||
*
|
||||
* @param string $title
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Flarum\Core\Models\Discussion
|
||||
* @param User $user
|
||||
* @return static
|
||||
*/
|
||||
public static function start($title, User $user)
|
||||
{
|
||||
@@ -105,6 +109,8 @@ class Discussion extends Model
|
||||
$discussion->start_time = time();
|
||||
$discussion->start_user_id = $user->id;
|
||||
|
||||
$discussion->setRelation('startUser', $user);
|
||||
|
||||
$discussion->raise(new DiscussionWasStarted($discussion));
|
||||
|
||||
return $discussion;
|
||||
@@ -114,7 +120,7 @@ class Discussion extends Model
|
||||
* Rename the discussion. Raises the DiscussionWasRenamed event.
|
||||
*
|
||||
* @param string $title
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @param User $user
|
||||
* @return $this
|
||||
*/
|
||||
public function rename($title, User $user)
|
||||
@@ -132,7 +138,7 @@ class Discussion extends Model
|
||||
/**
|
||||
* Set the discussion's start post details.
|
||||
*
|
||||
* @param \Flarum\Core\Models\Post $post
|
||||
* @param \Flarum\Core\Posts\Post $post
|
||||
* @return $this
|
||||
*/
|
||||
public function setStartPost(Post $post)
|
||||
@@ -147,7 +153,7 @@ class Discussion extends Model
|
||||
/**
|
||||
* Set the discussion's last post details.
|
||||
*
|
||||
* @param \Flarum\Core\Models\Post $post
|
||||
* @param \Flarum\Core\Posts\Post $post
|
||||
* @return $this
|
||||
*/
|
||||
public function setLastPost(Post $post)
|
||||
@@ -211,7 +217,7 @@ class Discussion extends Model
|
||||
* the same post as was originally intended to be saved. It also may not
|
||||
* exist, if the merge logic resulted in deletion.
|
||||
*/
|
||||
public function mergePost(Mergable $post)
|
||||
public function mergePost(MergeablePost $post)
|
||||
{
|
||||
$lastPost = $this->posts()->latest('time')->first();
|
||||
|
||||
@@ -225,21 +231,21 @@ class Discussion extends Model
|
||||
*/
|
||||
public function posts()
|
||||
{
|
||||
return $this->hasMany('Flarum\Core\Models\Post');
|
||||
return $this->hasMany('Flarum\Core\Posts\Post');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's posts, but only ones which
|
||||
* are visible to the given user.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @param User $user
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function visiblePosts(User $user)
|
||||
public function postsVisibleTo(User $user)
|
||||
{
|
||||
$query = $this->posts();
|
||||
|
||||
foreach (static::$visiblePostsScopes as $scope) {
|
||||
foreach (static::$postVisibilityScopes as $scope) {
|
||||
$scope($query, $user, $this);
|
||||
}
|
||||
|
||||
@@ -253,7 +259,7 @@ class Discussion extends Model
|
||||
*/
|
||||
public function comments()
|
||||
{
|
||||
return $this->visiblePosts(new Guest)->where('type', 'comment');
|
||||
return $this->postsVisibleTo(new Guest)->where('type', 'comment');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,7 +283,7 @@ class Discussion extends Model
|
||||
*/
|
||||
public function startPost()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\Post', 'start_post_id');
|
||||
return $this->belongsTo('Flarum\Core\Posts\Post', 'start_post_id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,7 +293,7 @@ class Discussion extends Model
|
||||
*/
|
||||
public function startUser()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\User', 'start_user_id');
|
||||
return $this->belongsTo('Flarum\Core\Users\User', 'start_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,7 +303,7 @@ class Discussion extends Model
|
||||
*/
|
||||
public function lastPost()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\Post', 'last_post_id');
|
||||
return $this->belongsTo('Flarum\Core\Posts\Post', 'last_post_id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,7 +313,7 @@ class Discussion extends Model
|
||||
*/
|
||||
public function lastUser()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\User', 'last_user_id');
|
||||
return $this->belongsTo('Flarum\Core\Users\User', 'last_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -317,7 +323,7 @@ class Discussion extends Model
|
||||
*/
|
||||
public function readers()
|
||||
{
|
||||
return $this->belongsToMany('Flarum\Core\Models\User', 'users_discussions');
|
||||
return $this->belongsToMany('Flarum\Core\Users\User', 'users_discussions');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -327,24 +333,24 @@ class Discussion extends Model
|
||||
* If no user is passed (i.e. in the case of eager loading the 'state'
|
||||
* relation), then the static `$stateUser` property is used.
|
||||
*
|
||||
* @see \Flarum\Core\Models\Discussion::setStateUser()
|
||||
* @see Discussion::setStateUser()
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @param User|null $user
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function state(User $user = null)
|
||||
{
|
||||
$user = $user ?: static::$stateUser;
|
||||
|
||||
return $this->hasOne('Flarum\Core\Models\DiscussionState')->where('user_id', $user ? $user->id : null);
|
||||
return $this->hasOne('Flarum\Core\Discussions\DiscussionState')->where('user_id', $user ? $user->id : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state model for a user, or instantiate a new one if it does not
|
||||
* exist.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Flarum\Core\Models\DiscussionState
|
||||
* @param User $user
|
||||
* @return \Flarum\Core\Discussions\DiscussionState
|
||||
*/
|
||||
public function stateFor(User $user)
|
||||
{
|
||||
@@ -362,7 +368,7 @@ class Discussion extends Model
|
||||
/**
|
||||
* Set the user for which the state relationship should be loaded.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @param User $user
|
||||
*/
|
||||
public static function setStateUser(User $user)
|
||||
{
|
||||
@@ -376,8 +382,8 @@ class Discussion extends Model
|
||||
* query. It is passed three parameters: the query builder object, the
|
||||
* user to constrain posts for, and the discussion instance.
|
||||
*/
|
||||
public static function addVisiblePostsScope(callable $scope)
|
||||
public static function addPostVisibilityScope(callable $scope)
|
||||
{
|
||||
static::$visiblePostsScopes[] = $scope;
|
||||
static::$postVisibilityScopes[] = $scope;
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php namespace Flarum\Core\Repositories;
|
||||
<?php namespace Flarum\Core\Discussions;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
interface DiscussionRepositoryInterface
|
||||
{
|
||||
@@ -15,18 +15,18 @@ interface DiscussionRepositoryInterface
|
||||
* Find a discussion by ID, optionally making sure it is visible to a certain
|
||||
* user, or throw an exception.
|
||||
*
|
||||
* @param integer $id
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Flarum\Core\Models\Discussion
|
||||
* @param integer $id
|
||||
* @param \Flarum\Core\Users\User $actor
|
||||
* @return \Flarum\Core\Discussions\Discussion
|
||||
*
|
||||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public function findOrFail($id, User $user = null);
|
||||
public function findOrFail($id, User $actor = null);
|
||||
|
||||
/**
|
||||
* Get the IDs of discussions which a user has read completely.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @param \Flarum\Core\Users\User $user
|
||||
* @return array
|
||||
*/
|
||||
public function getReadIds(User $user);
|
@@ -1,6 +1,7 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
<?php namespace Flarum\Core\Discussions;
|
||||
|
||||
use Flarum\Core\Events\DiscussionWasRead;
|
||||
use Flarum\Core\Discussions\Events\DiscussionWasRead;
|
||||
use Flarum\Core\Model;
|
||||
use Flarum\Core\Support\EventGenerator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
@@ -16,16 +17,12 @@ class DiscussionState extends Model
|
||||
use EventGenerator;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $table = 'users_discussions';
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $dateAttributes = ['read_time'];
|
||||
|
||||
@@ -55,7 +52,7 @@ class DiscussionState extends Model
|
||||
*/
|
||||
public function discussion()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\Discussion', 'discussion_id');
|
||||
return $this->belongsTo('Flarum\Core\Discussions\Discussion', 'discussion_id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,14 +62,14 @@ class DiscussionState extends Model
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\User', 'user_id');
|
||||
return $this->belongsTo('Flarum\Core\Users\User', 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the keys for a save update query.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
protected function setKeysForSaveQuery(Builder $query)
|
||||
{
|
@@ -0,0 +1,74 @@
|
||||
<?php namespace Flarum\Core\Discussions;
|
||||
|
||||
use Flarum\Core\Search\GambitManager;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Support\ServiceProvider;
|
||||
use Flarum\Extend;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class DiscussionsServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->extend([
|
||||
new Extend\EventSubscriber('Flarum\Core\Discussions\Listeners\DiscussionMetadataUpdater')
|
||||
]);
|
||||
|
||||
Discussion::allow('*', function (Discussion $discussion, User $user, $action) {
|
||||
return $user->hasPermission('discussion.'.$action) ?: null;
|
||||
});
|
||||
|
||||
// Allow a user to rename their own discussion.
|
||||
Discussion::allow('rename', function (Discussion $discussion, User $user) {
|
||||
return $discussion->start_user_id == $user->id ?: null;
|
||||
// TODO: add limitations to time etc. according to a config setting
|
||||
});
|
||||
|
||||
Discussion::allow('delete', function (Discussion $discussion, User $user) {
|
||||
return $discussion->start_user_id == $user->id && $discussion->participants_count == 1 ?: null;
|
||||
// TODO: add limitations to time etc. according to a config setting
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->bind(
|
||||
'Flarum\Core\Discussions\DiscussionRepositoryInterface',
|
||||
'Flarum\Core\Discussions\EloquentDiscussionRepository'
|
||||
);
|
||||
|
||||
$this->app->bind(
|
||||
'Flarum\Core\Discussions\Search\Fulltext\DriverInterface',
|
||||
'Flarum\Core\Discussions\Search\Fulltext\MySqlFulltextDriver'
|
||||
);
|
||||
|
||||
$this->app->instance('flarum.discussionGambits', [
|
||||
'Flarum\Core\Discussions\Search\Gambits\AuthorGambit',
|
||||
'Flarum\Core\Discussions\Search\Gambits\UnreadGambit'
|
||||
]);
|
||||
|
||||
$this->app->when('Flarum\Core\Discussions\Search\DiscussionSearcher')
|
||||
->needs('Flarum\Core\Search\GambitManager')
|
||||
->give(function (Container $app) {
|
||||
$gambits = new GambitManager($app);
|
||||
|
||||
foreach ($app->make('flarum.discussionGambits') as $gambit) {
|
||||
$gambits->add($gambit);
|
||||
}
|
||||
|
||||
$gambits->setFulltextGambit('Flarum\Core\Discussions\Search\Gambits\FulltextGambit');
|
||||
|
||||
return $gambits;
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,15 +1,14 @@
|
||||
<?php namespace Flarum\Core\Repositories;
|
||||
<?php namespace Flarum\Core\Discussions;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Flarum\Core\Models\Discussion;
|
||||
use Flarum\Core\Models\User;
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class EloquentDiscussionRepository implements DiscussionRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Get a new query builder for the discussions table.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
* @return Builder
|
||||
*/
|
||||
public function query()
|
||||
{
|
||||
@@ -17,14 +16,12 @@ class EloquentDiscussionRepository implements DiscussionRepositoryInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a discussion by ID, optionally making sure it is visible to a certain
|
||||
* user, or throw an exception.
|
||||
* Find a discussion by ID, optionally making sure it is visible to a
|
||||
* certain user, or throw an exception.
|
||||
*
|
||||
* @param integer $id
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Flarum\Core\Models\Discussion
|
||||
*
|
||||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
* @param integer $id
|
||||
* @param \Flarum\Core\Users\User $user
|
||||
* @return \Flarum\Core\Discussions\Discussion
|
||||
*/
|
||||
public function findOrFail($id, User $user = null)
|
||||
{
|
||||
@@ -36,7 +33,7 @@ class EloquentDiscussionRepository implements DiscussionRepositoryInterface
|
||||
/**
|
||||
* Get the IDs of discussions which a user has read completely.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @param \Flarum\Core\Users\User $user
|
||||
* @return array
|
||||
*/
|
||||
public function getReadIds(User $user)
|
||||
@@ -50,9 +47,9 @@ class EloquentDiscussionRepository implements DiscussionRepositoryInterface
|
||||
/**
|
||||
* Scope a query to only include records that are visible to a user.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
* @param Builder $query
|
||||
* @param \Flarum\Core\Users\User $user
|
||||
* @return Builder
|
||||
*/
|
||||
protected function scopeVisibleTo(Builder $query, User $user = null)
|
||||
{
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user