1
0
mirror of https://github.com/flarum/core.git synced 2025-10-20 03:06:07 +02:00

Major refactor and improvements

- Reorganised all namespaces and class names for consistency and structure. Following PSR bylaws (Abstract prefix, Interface/Trait suffix).
  - Move models into root of Core, because writing `use Flarum\Core\Discussion` is nice. Namespace the rest by type. (Namespacing by entity was too arbitrary.)
  - Moved some non-domain stuff out of Core: Database, Formatter, Settings.
  - Renamed config table and all references to "settings" for consistency.
  - Remove Core class and add url()/isInstalled()/inDebugMode() as instance methods of Foundation\Application.
  - Cleanup, docblocking, etc.

- Improvements to HTTP architecture
  - API and forum/admin Actions are now actually all the same thing (simple PSR-7 Request handlers), renamed to Controllers.
  - Upgrade to tobscure/json-api 0.2 branch.
  - Where possible, moved generic functionality to tobscure/json-api (e.g. pagination links). I'm quite happy with the backend balance now re: #262

- Improvements to other architecture
  - Use Illuminate's Auth\Access\Gate interface/implementation instead of our old Locked trait. We still use events to actually determine the permissions though. Our Policy classes are actually glorified event subscribers.
  - Extract model validation into Core\Validator classes.
  - Make post visibility permission stuff much more efficient and DRY.

- Renamed Flarum\Event classes for consistency. ref #246
  - `Configure` prefix for events dedicated to configuring an object.
  - `Get` prefix for events whose listeners should return something.
  - `Prepare` prefix when a variable is passed by reference so it can be modified.
  - `Scope` prefix when a query builder is passed.

- Miscellaneous improvements/bug-fixes. I'm easily distracted!
  - Increase default height of post composer.
  - Improve post stream redraw flickering in Safari by keying loading post placeholders with their IDs. ref #451
  - Use a PHP JavaScript minification library for minifying TextFormatter's JavaScript, instead of ClosureCompilerService (can't rely on external service!)
  - Use UrlGenerator properly in various places. closes #123
  - Make Api\Client return Response object. closes #128
  - Allow extensions to specify custom icon images.
  - Allow external API/admin URLs to be optionally specified in config.php. If the value or "url" is an array, we look for the corresponding path inside. Otherwise, we append the path to the base URL, using the corresponding value in "paths" if present. closes #244
This commit is contained in:
Toby Zerner
2015-10-08 14:28:02 +10:30
parent 8c7cdb184f
commit dd67291ce0
434 changed files with 8676 additions and 7997 deletions

View File

@@ -10,14 +10,17 @@
namespace Flarum\Api;
use Flarum\Core\Model;
use Flarum\Database\AbstractModel;
use DateTime;
use Exception;
/**
* @todo document database columns with @property
* @property string $id
* @property int $user_id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $expires_at
* @property \Flarum\Core\User|null $user
*/
class AccessToken extends Model
class AccessToken extends AbstractModel
{
/**
* {@inheritdoc}
@@ -72,6 +75,6 @@ class AccessToken extends Model
*/
public function user()
{
return $this->belongsTo('Flarum\Core\Users\User');
return $this->belongsTo('Flarum\Core\User');
}
}

View File

@@ -1,24 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions;
use Flarum\Api\Request;
interface Action
{
/**
* Handle a request to the API, returning an HTTP response.
*
* @param Request $request
* @return \Psr\Http\Message\ResponseInterface
*/
public function handle(Request $request);
}

View File

@@ -1,68 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions;
use Flarum\Api\Request;
use Flarum\Core\Settings\SettingsRepository;
use Flarum\Core\Groups\Permission;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Events\SerializeConfig;
use Zend\Diactoros\Response\EmptyResponse;
use Exception;
class ConfigAction implements Action
{
/**
* @var SettingsRepository
*/
protected $settings;
/**
* @param SettingsRepository $settings
*/
public function __construct(SettingsRepository $settings)
{
$this->settings = $settings;
}
/**
* {@inheritdoc}
*/
public function handle(Request $request, array $routeParams = [])
{
if (! $request->actor->isAdmin()) {
throw new PermissionDeniedException;
}
$config = $request->get('config', []);
// TODO: throw HTTP status 400 or 422
if (! is_array($config)) {
throw new Exception;
}
foreach ($config as $k => $v) {
event($event = new SerializeConfig($k, $v));
$this->settings->set($k, $v);
if (strpos($k, 'theme_') === 0 || $k === 'custom_less') {
$forum = app('Flarum\Forum\Actions\ClientAction');
$forum->flushAssets();
$admin = app('Flarum\Admin\Actions\ClientAction');
$admin->flushAssets();
}
}
return new EmptyResponse(204);
}
}

View File

@@ -1,49 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\Request;
use Tobscure\JsonApi\Document;
abstract class CreateAction extends SerializeResourceAction
{
/**
* Set a 201 Created status code on the response.
*
* @param Request $request
* @return \Psr\Http\Message\ResponseInterface
*/
public function respond(Request $request)
{
return parent::respond($request)->withStatus(201);
}
/**
* Get the newly created resource to be serialized and assigned to the response document.
*
* @param JsonApiRequest $request
* @param Document $document
* @return array
*/
protected function data(JsonApiRequest $request, Document $document)
{
return $this->create($request);
}
/**
* Create the resource.
*
* @param JsonApiRequest $request
* @return \Flarum\Core\Model
*/
abstract protected function create(JsonApiRequest $request);
}

View File

@@ -1,104 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Discussions;
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;
class CreateAction extends BaseCreateAction
{
/**
* The command bus.
*
* @var Dispatcher
*/
protected $bus;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\DiscussionSerializer';
/**
* @inheritdoc
*/
public $include = [
'posts' => true,
'startUser' => true,
'lastUser' => true,
'startPost' => true,
'lastPost' => true
];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = [];
/**
* @inheritdoc
*/
public $sort;
/**
* Instantiate the action.
*
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* Create a discussion according to input from the API request.
*
* @param JsonApiRequest $request
* @return \Flarum\Core\Discussions\Discussion
*/
protected function create(JsonApiRequest $request)
{
$actor = $request->actor;
$discussion = $this->bus->dispatch(
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 ($actor->exists) {
$this->bus->dispatch(
new ReadDiscussion($discussion->id, $actor, 1)
);
}
return $discussion;
}
}

View File

@@ -1,115 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Discussions;
use Flarum\Core\Search\SearchCriteria;
use Flarum\Core\Discussions\Search\DiscussionSearcher;
use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\UrlGenerator;
use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction
{
/**
* @var DiscussionSearcher
*/
protected $searcher;
/**
* @var UrlGenerator
*/
protected $url;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\DiscussionSerializer';
/**
* @inheritdoc
*/
public $include = [
'startUser' => true,
'lastUser' => true,
'startPost' => false,
'lastPost' => false,
'relevantPosts' => true,
'relevantPosts.discussion' => true,
'relevantPosts.user' => true
];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = ['lastTime', 'commentsCount', 'startTime'];
/**
* @inheritdoc
*/
public $sort;
/**
* @param DiscussionSearcher $searcher
* @param UrlGenerator $url
*/
public function __construct(DiscussionSearcher $searcher, UrlGenerator $url)
{
$this->searcher = $searcher;
$this->url = $url;
}
/**
* Get the discussion results, ready to be serialized and assigned to the
* document response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, Document $document)
{
$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
$this->addPaginationLinks(
$document,
$request,
$request->http ? $this->url->toRoute('discussions.index') : '',
$results->areMoreResults()
);
return $results->getResults();
}
}

View File

@@ -1,117 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Discussions;
use Flarum\Core\Discussions\DiscussionRepository;
use Flarum\Core\Posts\PostRepository;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\Actions\Posts\GetsPosts;
use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Document;
class ShowAction extends SerializeResourceAction
{
use GetsPosts;
/**
* @var \Flarum\Core\Discussions\DiscussionRepository
*/
protected $discussions;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\DiscussionSerializer';
/**
* @inheritdoc
*/
public $include = [
'startUser' => false,
'lastUser' => false,
'startPost' => false,
'lastPost' => false,
'posts' => true,
'posts.user' => true,
'posts.user.groups' => true,
'posts.editUser' => true,
'posts.hideUser' => true
];
/**
* @inheritdoc
*/
public $link = ['posts', 'posts.discussion'];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = ['time'];
/**
* @inheritdoc
*/
public $sort = ['time' => 'asc'];
/**
* Instantiate the action.
*
* @param DiscussionRepository $discussions
* @param PostRepository $posts
*/
public function __construct(DiscussionRepository $discussions, PostRepository $posts)
{
$this->discussions = $discussions;
$this->posts = $posts;
}
/**
* Get a single discussion, ready to be serialized and assigned to the
* JsonApi response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Flarum\Core\Discussions\Discussion
*/
protected function data(JsonApiRequest $request, Document $document)
{
$discussionId = $request->get('id');
$actor = $request->actor;
$discussion = $this->discussions->findOrFail($discussionId, $actor);
$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)) {
$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($postRelations);
}
return $discussion;
}
}

View File

@@ -1,111 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Discussions;
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;
use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction
{
/**
* @var Dispatcher
*/
protected $bus;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\DiscussionSerializer';
/**
* @inheritdoc
*/
public $include = [];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = [];
/**
* @inheritdoc
*/
public $sort;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* Update a discussion according to input from the API request, and return
* it ready to be serialized and assigned to the JsonApi response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Flarum\Core\Discussions\Discussion
*/
protected function data(JsonApiRequest $request, Document $document)
{
$actor = $request->actor;
$discussionId = $request->get('id');
$data = $request->get('data');
$discussion = $this->bus->dispatch(
new EditDiscussion($discussionId, $actor, $data)
);
// 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 ReadDiscussion($discussionId, $actor, $readNumber)
);
$discussion = $state->discussion;
}
if ($posts = $discussion->getModifiedPosts()) {
$discussion->posts_ids = $discussion->postsVisibleTo($actor)->orderBy('time')->lists('id');
$discussion->posts = array_filter($posts, function ($post) {
return $post->exists;
});
$request->include = array_merge($request->include, ['posts']);
$request->link = array_merge($request->include, ['posts', 'posts.discussion', 'posts.user']);
}
return $discussion;
}
}

View File

@@ -1,47 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Extensions;
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
use Flarum\Api\Request;
use Illuminate\Contracts\Bus\Dispatcher;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Support\ExtensionManager;
class DeleteAction extends BaseDeleteAction
{
protected $extensions;
public function __construct(ExtensionManager $extensions)
{
$this->extensions = $extensions;
}
protected function delete(Request $request)
{
if (! $request->actor->isAdmin()) {
throw new PermissionDeniedException;
}
$name = $request->get('name');
$this->extensions->disable($name);
$this->extensions->uninstall($name);
app('flarum.formatter')->flush();
$forum = app('Flarum\Forum\Actions\ClientAction');
$forum->flushAssets();
$admin = app('Flarum\Admin\Actions\ClientAction');
$admin->flushAssets();
}
}

View File

@@ -1,51 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Extensions;
use Flarum\Api\Actions\Action;
use Flarum\Api\Request;
use Illuminate\Contracts\Bus\Dispatcher;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Support\ExtensionManager;
class UpdateAction implements Action
{
protected $extensions;
public function __construct(ExtensionManager $extensions)
{
$this->extensions = $extensions;
}
public function handle(Request $request)
{
if (! $request->actor->isAdmin()) {
throw new PermissionDeniedException;
}
$enabled = $request->get('enabled');
$name = $request->get('name');
if ($enabled === true) {
$this->extensions->enable($name);
} elseif ($enabled === false) {
$this->extensions->disable($name);
}
app('flarum.formatter')->flush();
$forum = app('Flarum\Forum\Actions\ClientAction');
$forum->flushAssets();
$admin = app('Flarum\Admin\Actions\ClientAction');
$admin->flushAssets();
}
}

View File

@@ -1,47 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions;
use Flarum\Api\Request;
use Flarum\Core\Users\UserRepository;
use Flarum\Core\Users\Commands\RequestPasswordReset;
use Illuminate\Contracts\Bus\Dispatcher;
use Zend\Diactoros\Response\EmptyResponse;
class ForgotAction implements Action
{
protected $users;
protected $bus;
public function __construct(UserRepository $users, Dispatcher $bus)
{
$this->users = $users;
$this->bus = $bus;
}
/**
* Log in and return a token.
*
* @param \Flarum\Api\Request $request
* @return \Psr\Http\Message\ResponseInterface
*/
public function handle(Request $request)
{
$email = $request->get('email');
$this->bus->dispatch(
new RequestPasswordReset($email)
);
return new EmptyResponse();
}
}

View File

@@ -1,73 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Forum;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Core\Groups\Group;
use Tobscure\JsonApi\Document;
class ShowAction extends SerializeResourceAction
{
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\ForumSerializer';
/**
* @inheritdoc
*/
public $include = [
'groups' => true
];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = [];
/**
* @inheritdoc
*/
public $sort;
/**
* Get the forum, ready to be serialized and assigned to the JsonApi
* response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Flarum\Core\Forum
*/
protected function data(JsonApiRequest $request, Document $document)
{
$forum = app('flarum.forum');
$forum->groups = Group::whereVisibleTo($request->actor)->get();
return $forum;
}
}

View File

@@ -1,63 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Forum;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Core\Settings\SettingsRepository;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction
{
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\ForumSerializer';
/**
* @var SettingsRepository
*/
protected $settings;
/**
* @param SettingsRepository $settings
*/
public function __construct(SettingsRepository $settings)
{
$this->settings = $settings;
}
/**
* Get the forum, ready to be serialized and assigned to the JsonApi
* response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Flarum\Core\Forum
*/
protected function data(JsonApiRequest $request, Document $document)
{
if (! $request->actor->isAdmin()) {
throw new PermissionDeniedException;
}
$config = $request->get('data.attributes.config');
if (is_array($config)) {
foreach ($config as $k => $v) {
$this->settings->set($k, $v);
}
}
return app('flarum.forum');
}
}

View File

@@ -1,50 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Groups;
use Flarum\Core\Groups\Commands\CreateGroup;
use Flarum\Api\Actions\CreateAction as BaseCreateAction;
use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher;
class CreateAction extends BaseCreateAction
{
/**
* @var Dispatcher
*/
protected $bus;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\GroupSerializer';
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* Create a group according to input from the API request.
*
* @param JsonApiRequest $request
* @return \Flarum\Core\Groups\Group
*/
protected function create(JsonApiRequest $request)
{
return $this->bus->dispatch(
new CreateGroup($request->actor, $request->get('data'))
);
}
}

View File

@@ -1,37 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Groups;
use Flarum\Core\Groups\Group;
use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction
{
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\GroupSerializer';
/**
* Get the groups, ready to be serialized and assigned to the document
* response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, Document $document)
{
return Group::all();
}
}

View File

@@ -1,53 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Groups;
use Flarum\Core\Groups\Commands\EditGroup;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher;
use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction
{
/**
* @var Dispatcher
*/
protected $bus;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\GroupSerializer';
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* Update a group according to input from the API request, and return it
* ready to be serialized and assigned to the JsonApi response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Flarum\Core\Groups\Group
*/
protected function data(JsonApiRequest $request, Document $document)
{
return $this->bus->dispatch(
new EditGroup($request->get('id'), $request->actor, $request->get('data'))
);
}
}

View File

@@ -1,97 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Notifications;
use Flarum\Core\Notifications\NotificationRepository;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction
{
/**
* @var NotificationRepository
*/
protected $notifications;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\NotificationSerializer';
/**
* @inheritdoc
*/
public $include = [
'sender' => true,
'subject' => true,
'subject.discussion' => true
];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 10;
/**
* @inheritdoc
*/
public $sortFields = [];
/**
* @inheritdoc
*/
public $sort;
/**
* Instantiate the action.
*
* @param NotificationRepository $notifications
*/
public function __construct(NotificationRepository $notifications)
{
$this->notifications = $notifications;
}
/**
* Get the notification results, ready to be serialized and assigned to the
* document response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Illuminate\Database\Eloquent\Collection
* @throws PermissionDeniedException
*/
protected function data(JsonApiRequest $request, Document $document)
{
$actor = $request->actor;
if ($actor->isGuest()) {
throw new PermissionDeniedException;
}
$actor->markNotificationsAsRead()->save();
return $this->notifications->findByUser($actor, $request->limit, $request->offset)
->load($request->include);
}
}

View File

@@ -1,83 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Notifications;
use Flarum\Core\Notifications\Commands\ReadNotification;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher;
use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction
{
/**
* @var Dispatcher
*/
protected $bus;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\NotificationSerializer';
/**
* @inheritdoc
*/
public $include = [];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = [];
/**
* @inheritdoc
*/
public $sort;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* Mark a notification as read, and return it ready to be serialized and
* assigned to the JsonApi response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Flarum\Core\Notifications\Notification
*/
protected function data(JsonApiRequest $request, Document $document)
{
return $this->bus->dispatch(
new ReadNotification($request->get('id'), $request->actor)
);
}
}

View File

@@ -1,104 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Posts;
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;
class CreateAction extends BaseCreateAction
{
/**
* @var Dispatcher
*/
protected $bus;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\PostSerializer';
/**
* @inheritdoc
*/
public $include = [
'user' => true,
'discussion' => true,
'discussion.lastUser' => true
];
/**
* @inheritdoc
*/
public $link = ['discussion.posts'];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = [];
/**
* @inheritdoc
*/
public $sort;
/**
* Instantiate the action.
*
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* Reply to a discussion according to input from the API request.
*
* @param JsonApiRequest $request
* @return \Flarum\Core\Posts\Post
*/
protected function create(JsonApiRequest $request)
{
$actor = $request->actor;
$discussionId = $request->get('data.relationships.discussion.data.id');
$post = $this->bus->dispatch(
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 ($actor->exists) {
$this->bus->dispatch(
new ReadDiscussion($discussionId, $actor, $post->number)
);
}
$discussion = $post->discussion;
$discussion->posts_ids = $discussion->postsVisibleTo($actor)->orderBy('time')->lists('id');
return $post;
}
}

View File

@@ -1,46 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Posts;
use Flarum\Api\JsonApiRequest;
trait GetsPosts
{
/**
* @var \Flarum\Core\Posts\PostRepository
*/
protected $posts;
/**
* @param JsonApiRequest $request
* @param array $where
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function getPosts(JsonApiRequest $request, array $where)
{
$actor = $request->actor;
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 = $request->offset;
}
return $this->posts->findWhere(
$where,
$actor,
$request->sort,
$request->limit,
$offset
);
}
}

View File

@@ -1,105 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Posts;
use Flarum\Core\Posts\PostRepository;
use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction
{
use GetsPosts;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\PostSerializer';
/**
* @inheritdoc
*/
public $include = [
'user' => true,
'user.groups' => true,
'editUser' => true,
'hideUser' => true,
'discussion' => true
];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = ['time'];
/**
* @inheritdoc
*/
public $sort;
/**
* @param PostRepository $posts
*/
public function __construct(PostRepository $posts)
{
$this->posts = $posts;
}
/**
* Get the post results, ready to be serialized and assigned to the
* document response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, Document $document)
{
$postIds = (array) $request->get('ids');
$actor = $request->actor;
if (count($postIds)) {
$posts = $this->posts->findByIds($postIds, $actor);
} else {
$where = [];
if ($discussionId = $request->get('filter.discussion')) {
$where['discussion_id'] = $discussionId;
}
if ($number = $request->get('filter.number')) {
$where['number'] = $number;
}
if ($userId = $request->get('filter.user')) {
$where['user_id'] = $userId;
}
if ($type = $request->get('filter.type')) {
$where['type'] = $type;
}
$posts = $this->getPosts($request, $where);
}
return $posts;
}
}

View File

@@ -1,87 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Posts;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Flarum\Core\Posts\PostRepository;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Document;
class ShowAction extends SerializeResourceAction
{
/**
* @var PostRepository
*/
protected $posts;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\PostSerializer';
/**
* @inheritdoc
*/
public $include = [
'user' => true,
'user.groups' => true,
'editUser' => true,
'hideUser' => true,
'discussion' => true
];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = [];
/**
* @inheritdoc
*/
public $sort;
/**
* @param PostRepository $posts
*/
public function __construct(PostRepository $posts)
{
$this->posts = $posts;
}
/**
* Get a single post, ready to be serialized and assigned to the JsonApi
* response.
*
* @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);
}
}

View File

@@ -1,86 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Posts;
use Flarum\Core\Posts\Commands\EditPost;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher;
use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction
{
/**
* @var Dispatcher
*/
protected $bus;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\PostSerializer';
/**
* @inheritdoc
*/
public $include = [
'editUser' => true,
'discussion' => true
];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = [];
/**
* @inheritdoc
*/
public $sort;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* Update a post according to input from the API request, and return it
* ready to be serialized and assigned to the JsonApi response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, Document $document)
{
return $this->bus->dispatch(
new EditPost($request->get('id'), $request->actor, $request->get('data'))
);
}
}

View File

@@ -1,210 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions;
use Flarum\Events\BuildApiAction;
use Flarum\Events\WillSerializeData;
use Flarum\Api\Request;
use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Criteria;
use Tobscure\JsonApi\Document;
use Tobscure\JsonApi\SerializerInterface;
use Zend\Diactoros\Response\JsonResponse;
abstract class SerializeAction implements Action
{
/**
* The name of the serializer class to output results with.
*
* @var string
*/
public $serializer;
/**
* The relationships that are available to be included (keys), and which
* ones are included by default (boolean values).
*
* @var array
*/
public $include = [];
/**
* The relationships that are linked by default.
*
* @var array
*/
public $link = [];
/**
* The maximum number of records that can be requested.
*
* @var integer
*/
public $limitMax = 50;
/**
* The number of records included by default.
*
* @var integer
*/
public $limit = 20;
/**
* The fields that are available to be sorted by.
*
* @var array
*/
public $sortFields = [];
/**
* The default sort field and order to user.
*
* @var string
*/
public $sort;
/**
* Handle an API request and return an API response.
*
* @param Request $request
* @return JsonResponse
*/
public function handle(Request $request)
{
$request = $this->buildJsonApiRequest($request);
$document = new Document();
$data = $this->data($request, $document);
event(new WillSerializeData($this, $data, $request));
$serializer = new $this->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 JsonApiRequest $request
* @param Document $document
* @return array
*/
abstract protected function data(JsonApiRequest $request, Document $document);
/**
* Serialize the data as appropriate.
*
* @param SerializerInterface $serializer
* @param array $data
* @return \Tobscure\JsonApi\Elements\ElementInterface
*/
abstract protected function serialize(SerializerInterface $serializer, $data);
/**
* Extract parameters from the request input and assign them to the
* request, restricted by the action's specifications.
*
* @param Request $request
* @return JsonApiRequest
*/
protected function buildJsonApiRequest(Request $request)
{
$request = new JsonApiRequest($request->input, $request->actor, $request->http);
$criteria = new Criteria($request->input);
event(new BuildApiAction($this));
$request->include = $this->sanitizeInclude($criteria->getInclude());
$request->sort = $this->sanitizeSort($criteria->getSort());
$request->offset = $criteria->getOffset();
$request->limit = $this->sanitizeLimit($criteria->getLimit());
$request->link = $this->link;
return $request;
}
/**
* Sanitize an array of included relationships according to the action's
* configuration.
*
* @param array $include
* @return array
*/
protected function sanitizeInclude(array $include)
{
return array_intersect($include, array_keys($this->include)) ?: array_keys(array_filter($this->include));
}
/**
* Sanitize an array of sort criteria according to the action's
* configuration.
*
* @param array $sort
* @return array
*/
protected function sanitizeSort(array $sort)
{
return array_intersect_key($sort, array_flip($this->sortFields)) ?: $this->sort;
}
/**
* Sanitize a limit according to the action's configuration.
*
* @param int $limit
* @return int
*/
protected function sanitizeLimit($limit)
{
return min($limit, $this->limitMax) ?: $this->limit;
}
/**
* Add pagination links to a JSON-API response, based on input parameters
* and the default parameters of this action.
*
* @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
* is unknown ('last' link is ommitted).
* @return void
*/
protected function addPaginationLinks(Document $document, JsonApiRequest $request, $url, $total = true)
{
$input = [];
if ($request->limit != $this->limit) {
array_set($input, 'page.limit', $request->limit);
}
array_set($input, 'page.offset', 0);
$document->addLink('first', $url.'?'.http_build_query($input));
if ($request->offset > 0) {
array_set($input, 'page.offset', max(0, $request->offset - $request->limit));
$document->addLink('prev', $url.'?'.http_build_query($input));
}
if ($total === true || $request->offset + $request->limit < $total) {
array_set($input, 'page.offset', $request->offset + $request->limit);
$document->addLink('next', $url.'?'.http_build_query($input));
}
if ($total && $total !== true) {
array_set($input, 'page.offset', $total - $request->limit);
$document->addLink('last', $url.'?'.http_build_query($input));
}
}
}

View File

@@ -1,28 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions;
use Tobscure\JsonApi\SerializerInterface;
abstract class SerializeCollectionAction extends SerializeAction
{
/**
* Serialize the data as appropriate.
*
* @param SerializerInterface $serializer
* @param array $data
* @return \Tobscure\JsonApi\Elements\Collection
*/
protected function serialize(SerializerInterface $serializer, $data)
{
return $serializer->collection($data);
}
}

View File

@@ -1,28 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions;
use Tobscure\JsonApi\SerializerInterface;
abstract class SerializeResourceAction extends SerializeAction
{
/**
* Serialize the data as appropriate.
*
* @param SerializerInterface $serializer
* @param array $data
* @return \Tobscure\JsonApi\Elements\Resource
*/
protected function serialize(SerializerInterface $serializer, $data)
{
return $serializer->resource($data);
}
}

View File

@@ -1,69 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions;
use Flarum\Api\Commands\GenerateAccessToken;
use Flarum\Api\Request;
use Flarum\Core\Users\UserRepository;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Events\UserEmailChangeWasRequested;
use Illuminate\Contracts\Bus\Dispatcher;
use Zend\Diactoros\Response\JsonResponse;
class TokenAction implements Action
{
protected $users;
protected $bus;
public function __construct(UserRepository $users, Dispatcher $bus)
{
$this->users = $users;
$this->bus = $bus;
}
/**
* Log in and return a token.
*
* @param Request $request
* @return \Psr\Http\Message\ResponseInterface
* @throws PermissionDeniedException
*/
public function handle(Request $request)
{
$identification = $request->get('identification');
$password = $request->get('password');
$user = $this->users->findByIdentification($identification);
if (! $user || ! $user->checkPassword($password)) {
throw new PermissionDeniedException;
}
if (! $user->is_activated) {
event(new UserEmailChangeWasRequested($user, $user->email));
return new JsonResponse([
'code' => 'confirm_email',
'email' => $user->email
], 401);
}
$token = $this->bus->dispatch(
new GenerateAccessToken($user->id)
);
return new JsonResponse([
'token' => $token->id,
'userId' => $user->id
]);
}
}

View File

@@ -1,84 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Users;
use Flarum\Core\Users\Commands\RegisterUser;
use Flarum\Api\Actions\CreateAction as BaseCreateAction;
use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher;
class CreateAction extends BaseCreateAction
{
/**
* @var Dispatcher
*/
protected $bus;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\UserSerializer';
/**
* @inheritdoc
*/
public $include = [];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = [];
/**
* @inheritdoc
*/
public $sort;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* Register a user according to input from the API request.
*
* @param JsonApiRequest $request
* @return \Flarum\Core\Users\User
*/
protected function create(JsonApiRequest $request)
{
$user = $this->bus->dispatch(
new RegisterUser($request->actor, $request->get('data'))
);
$request->actor = $user;
return $user;
}
}

View File

@@ -1,53 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Users;
use Flarum\Core\Users\Commands\DeleteAvatar;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher;
use Tobscure\JsonApi\Document;
class DeleteAvatarAction extends SerializeResourceAction
{
/**
* @var Dispatcher
*/
protected $bus;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\UserSerializer';
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* Delete a user's avatar, and return the user ready to be serialized and
* assigned to the JsonApi response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Flarum\Core\Users\User
*/
protected function data(JsonApiRequest $request, Document $document)
{
return $this->bus->dispatch(
new DeleteAvatar($request->get('id'), $request->actor)
);
}
}

View File

@@ -1,106 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Users;
use Flarum\Core\Search\SearchCriteria;
use Flarum\Core\Users\Search\UserSearcher;
use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest;
use Flarum\Api\UrlGenerator;
use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction
{
/**
* @var UserSearcher
*/
protected $searcher;
/**
* @var UrlGenerator
*/
protected $url;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\UserSerializer';
/**
* @inheritdoc
*/
public $include = [
'groups' => true
];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = ['username', 'postsCount', 'discussionsCount', 'lastSeenTime', 'joinTime'];
/**
* @inheritdoc
*/
public $sort;
/**
* @param UserSearcher $searcher
* @param UrlGenerator $url
*/
public function __construct(UserSearcher $searcher, UrlGenerator $url)
{
$this->searcher = $searcher;
$this->url = $url;
}
/**
* Get the user results, ready to be serialized and assigned to the
* document response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function data(JsonApiRequest $request, Document $document)
{
$criteria = new SearchCriteria(
$request->actor,
$request->get('filter.q'),
$request->sort
);
$results = $this->searcher->search($criteria, $request->limit, $request->offset, $request->include);
$this->addPaginationLinks(
$document,
$request,
$this->url->toRoute('users.index'),
$results->areMoreResults()
);
return $results->getResults();
}
}

View File

@@ -1,88 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Users;
use Flarum\Core\Users\UserRepository;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Document;
class ShowAction extends SerializeResourceAction
{
/**
* @var UserRepository
*/
protected $users;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\CurrentUserSerializer';
/**
* @inheritdoc
*/
public $include = [
'groups' => true
];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = [];
/**
* @inheritdoc
*/
public $sort;
/**
* @param UserRepository $users
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* Get a single user, ready to be serialized and assigned to the JsonApi
* response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Flarum\Core\Users\User
*/
protected function data(JsonApiRequest $request, Document $document)
{
$id = $request->get('id');
if (! is_numeric($id)) {
$id = $this->users->getIdForUsername($id);
}
return $this->users->findOrFail($id, $request->actor);
}
}

View File

@@ -1,85 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Users;
use Flarum\Core\Users\Commands\EditUser;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher;
use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction
{
/**
* @var Dispatcher
*/
protected $bus;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\CurrentUserSerializer';
/**
* @inheritdoc
*/
public $include = [
'groups' => true
];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = [];
/**
* @inheritdoc
*/
public $sort;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* Update a user according to input from the API request, and return it
* ready to be serialized and assigned to the JsonApi response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Flarum\Core\Users\User
*/
protected function data(JsonApiRequest $request, Document $document)
{
return $this->bus->dispatch(
new EditUser($request->get('id'), $request->actor, $request->get('data'))
);
}
}

View File

@@ -1,87 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Users;
use Flarum\Core\Users\Commands\UploadAvatar;
use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher;
use Tobscure\JsonApi\Document;
class UploadAvatarAction extends SerializeResourceAction
{
/**
* @var Dispatcher
*/
protected $bus;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializers\UserSerializer';
/**
* @inheritdoc
*/
public $include = [];
/**
* @inheritdoc
*/
public $link = [];
/**
* @inheritdoc
*/
public $limitMax = 50;
/**
* @inheritdoc
*/
public $limit = 20;
/**
* @inheritdoc
*/
public $sortFields = [];
/**
* @inheritdoc
*/
public $sort;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* Upload an avatar for a user, and return the user ready to be serialized
* and assigned to the JsonApi response.
*
* @param JsonApiRequest $request
* @param Document $document
* @return \Flarum\Core\Users\User
*/
protected function data(JsonApiRequest $request, Document $document)
{
return $this->bus->dispatch(
new UploadAvatar(
$request->get('id'),
$request->http->getUploadedFiles()['avatar'],
$request->actor
)
);
}
}

View File

@@ -10,13 +10,12 @@
namespace Flarum\Api;
use Flarum\Core\Model;
use DateTime;
use Flarum\Database\AbstractModel;
/**
* @todo document database columns with @property
* @property string $id
*/
class ApiKey extends Model
class ApiKey extends AbstractModel
{
/**
* {@inheritdoc}

View File

@@ -10,48 +10,46 @@
namespace Flarum\Api;
use Flarum\Api\Serializers\ActivitySerializer;
use Flarum\Api\Serializers\NotificationSerializer;
use Flarum\Core\Users\Guest;
use Flarum\Events\RegisterApiRoutes;
use Flarum\Events\RegisterActivityTypes;
use Flarum\Events\RegisterNotificationTypes;
use Flarum\Api\Controller\AbstractSerializeController;
use Flarum\Api\Serializer\AbstractSerializer;
use Flarum\Api\Serializer\NotificationSerializer;
use Flarum\Event\ConfigureApiRoutes;
use Flarum\Event\ConfigureNotificationTypes;
use Flarum\Http\GenerateRouteHandlerTrait;
use Flarum\Http\RouteCollection;
use Flarum\Api\UrlGenerator;
use Illuminate\Support\ServiceProvider;
use Flarum\Foundation\AbstractServiceProvider;
use Psr\Http\Message\ServerRequestInterface;
class ApiServiceProvider extends ServiceProvider
class ApiServiceProvider extends AbstractServiceProvider
{
use GenerateRouteHandlerTrait;
/**
* Register the service provider.
*
* @return void
* {@inheritdoc}
*/
public function register()
{
$this->app->bind('flarum.actor', function () {
return new Guest;
$this->app->singleton(UrlGenerator::class, function () {
return new UrlGenerator($this->app, $this->app->make('flarum.api.routes'));
});
$this->app->singleton(
UrlGenerator::class,
function () {
return new UrlGenerator($this->app->make('flarum.api.routes'));
}
);
$this->app->singleton('flarum.api.routes', function () {
return $this->getRoutes();
});
}
/**
* Bootstrap the application events.
*
* @return void
* {@inheritdoc}
*/
public function boot()
{
$this->routes();
$this->registerNotificationSerializers();
AbstractSerializeController::setContainer($this->app);
AbstractSerializeController::setEventDispatcher($events = $this->app->make('events'));
AbstractSerializer::setContainer($this->app);
AbstractSerializer::setEventDispatcher($events);
}
/**
@@ -61,46 +59,55 @@ class ApiServiceProvider extends ServiceProvider
{
$blueprints = [];
$serializers = [
'discussionRenamed' => 'Flarum\Api\Serializers\DiscussionBasicSerializer'
'discussionRenamed' => 'Flarum\Api\Serializer\DiscussionBasicSerializer'
];
event(new RegisterNotificationTypes($blueprints, $serializers));
$this->app->make('events')->fire(
new ConfigureNotificationTypes($blueprints, $serializers)
);
foreach ($serializers as $type => $serializer) {
NotificationSerializer::setSubjectSerializer($type, $serializer);
}
}
protected function routes()
/**
* Get the API routes.
*
* @return RouteCollection
*/
protected function getRoutes()
{
$this->app->instance('flarum.api.routes', $routes = new RouteCollection);
$routes = new RouteCollection;
$toController = $this->getHandlerGenerator($this->app);
// Get forum information
$routes->get(
'/forum',
'forum.show',
$this->action('Flarum\Api\Actions\Forum\ShowAction')
$toController('Flarum\Api\Controller\ShowForumController')
);
// Save forum information
$routes->patch(
'/forum',
'forum.update',
$this->action('Flarum\Api\Actions\Forum\UpdateAction')
$toController('Flarum\Api\Controller\UpdateForumController')
);
// Retrieve authentication token
$routes->post(
'/token',
'token',
$this->action('Flarum\Api\Actions\TokenAction')
$toController('Flarum\Api\Controller\TokenController')
);
// Send forgot password email
$routes->post(
'/forgot',
'forgot',
$this->action('Flarum\Api\Actions\ForgotAction')
$toController('Flarum\Api\Controller\ForgotPasswordController')
);
/*
@@ -113,83 +120,76 @@ class ApiServiceProvider extends ServiceProvider
$routes->get(
'/users',
'users.index',
$this->action('Flarum\Api\Actions\Users\IndexAction')
$toController('Flarum\Api\Controller\ListUsersController')
);
// Register a user
$routes->post(
'/users',
'users.create',
$this->action('Flarum\Api\Actions\Users\CreateAction')
$toController('Flarum\Api\Controller\CreateUserController')
);
// Get a single user
$routes->get(
'/users/{id}',
'users.show',
$this->action('Flarum\Api\Actions\Users\ShowAction')
$toController('Flarum\Api\Controller\ShowUserController')
);
// Edit a user
$routes->patch(
'/users/{id}',
'users.update',
$this->action('Flarum\Api\Actions\Users\UpdateAction')
$toController('Flarum\Api\Controller\UpdateUserController')
);
// Delete a user
$routes->delete(
'/users/{id}',
'users.delete',
$this->action('Flarum\Api\Actions\Users\DeleteAction')
$toController('Flarum\Api\Controller\DeleteUserController')
);
// Upload avatar
$routes->post(
'/users/{id}/avatar',
'users.avatar.upload',
$this->action('Flarum\Api\Actions\Users\UploadAvatarAction')
$toController('Flarum\Api\Controller\UploadAvatarController')
);
// Remove avatar
$routes->delete(
'/users/{id}/avatar',
'users.avatar.delete',
$this->action('Flarum\Api\Actions\Users\DeleteAvatarAction')
$toController('Flarum\Api\Controller\DeleteAvatarController')
);
/*
|--------------------------------------------------------------------------
| Activity
| Notifications
|--------------------------------------------------------------------------
*/
// List activity
$routes->get(
'/activity',
'activity.index',
$this->action('Flarum\Api\Actions\Activity\IndexAction')
);
// List notifications for the current user
$routes->get(
'/notifications',
'notifications.index',
$this->action('Flarum\Api\Actions\Notifications\IndexAction')
$toController('Flarum\Api\Controller\ListNotificationsController')
);
// Mark all notifications as read
$routes->post(
'/notifications/read',
'notifications.readAll',
$this->action('Flarum\Api\Actions\Notifications\ReadAllAction')
$toController('Flarum\Api\Controller\ReadAllNotificationsController')
);
// Mark a single notification as read
$routes->patch(
'/notifications/{id}',
'notifications.update',
$this->action('Flarum\Api\Actions\Notifications\UpdateAction')
$toController('Flarum\Api\Controller\UpdateNotificationController')
);
/*
@@ -202,35 +202,35 @@ class ApiServiceProvider extends ServiceProvider
$routes->get(
'/discussions',
'discussions.index',
$this->action('Flarum\Api\Actions\Discussions\IndexAction')
$toController('Flarum\Api\Controller\ListDiscussionsController')
);
// Create a discussion
$routes->post(
'/discussions',
'discussions.create',
$this->action('Flarum\Api\Actions\Discussions\CreateAction')
$toController('Flarum\Api\Controller\CreateDiscussionController')
);
// Show a single discussion
$routes->get(
'/discussions/{id}',
'discussions.show',
$this->action('Flarum\Api\Actions\Discussions\ShowAction')
$toController('Flarum\Api\Controller\ShowDiscussionController')
);
// Edit a discussion
$routes->patch(
'/discussions/{id}',
'discussions.update',
$this->action('Flarum\Api\Actions\Discussions\UpdateAction')
$toController('Flarum\Api\Controller\UpdateDiscussionController')
);
// Delete a discussion
$routes->delete(
'/discussions/{id}',
'discussions.delete',
$this->action('Flarum\Api\Actions\Discussions\DeleteAction')
$toController('Flarum\Api\Controller\DeleteDiscussionController')
);
/*
@@ -243,35 +243,35 @@ class ApiServiceProvider extends ServiceProvider
$routes->get(
'/posts',
'posts.index',
$this->action('Flarum\Api\Actions\Posts\IndexAction')
$toController('Flarum\Api\Controller\ListPostsController')
);
// Create a post
$routes->post(
'/posts',
'posts.create',
$this->action('Flarum\Api\Actions\Posts\CreateAction')
$toController('Flarum\Api\Controller\CreatePostController')
);
// Show a single or multiple posts by ID
$routes->get(
'/posts/{id}',
'posts.show',
$this->action('Flarum\Api\Actions\Posts\ShowAction')
$toController('Flarum\Api\Controller\ShowPostController')
);
// Edit a post
$routes->patch(
'/posts/{id}',
'posts.update',
$this->action('Flarum\Api\Actions\Posts\UpdateAction')
$toController('Flarum\Api\Controller\UpdatePostController')
);
// Delete a post
$routes->delete(
'/posts/{id}',
'posts.delete',
$this->action('Flarum\Api\Actions\Posts\DeleteAction')
$toController('Flarum\Api\Controller\DeletePostController')
);
/*
@@ -284,28 +284,28 @@ class ApiServiceProvider extends ServiceProvider
$routes->get(
'/groups',
'groups.index',
$this->action('Flarum\Api\Actions\Groups\IndexAction')
$toController('Flarum\Api\Controller\ListGroupsController')
);
// Create a group
$routes->post(
'/groups',
'groups.create',
$this->action('Flarum\Api\Actions\Groups\CreateAction')
$toController('Flarum\Api\Controller\CreateGroupController')
);
// Edit a group
$routes->patch(
'/groups/{id}',
'groups.update',
$this->action('Flarum\Api\Actions\Groups\UpdateAction')
$toController('Flarum\Api\Controller\UpdateGroupController')
);
// Delete a group
$routes->delete(
'/groups/{id}',
'groups.delete',
$this->action('Flarum\Api\Actions\Groups\DeleteAction')
$toController('Flarum\Api\Controller\DeleteGroupController')
);
/*
@@ -318,49 +318,34 @@ class ApiServiceProvider extends ServiceProvider
$routes->patch(
'/extensions/{name}',
'extensions.update',
$this->action('Flarum\Api\Actions\Extensions\UpdateAction')
$toController('Flarum\Api\Controller\UpdateExtensionController')
);
// Uninstall an extension
$routes->delete(
'/extensions/{name}',
'extensions.delete',
$this->action('Flarum\Api\Actions\Extensions\DeleteAction')
$toController('Flarum\Api\Controller\UninstallExtensionController')
);
// Update config settings
// Update settings
$routes->post(
'/config',
'config',
$this->action('Flarum\Api\Actions\ConfigAction')
'/settings',
'settings',
$toController('Flarum\Api\Controller\SetSettingsController')
);
// Update a permission
$routes->post(
'/permission',
'permission',
$this->action('Flarum\Api\Actions\PermissionAction')
$toController('Flarum\Api\Controller\SetPermissionController')
);
event(new RegisterApiRoutes($routes));
}
$this->app->make('events')->fire(
new ConfigureApiRoutes($routes, $toController)
);
protected function action($class)
{
return function (ServerRequestInterface $httpRequest, $routeParams) use ($class) {
$action = app($class);
$actor = app('flarum.actor');
$input = array_merge(
$httpRequest->getQueryParams(),
$httpRequest->getAttributes(),
$httpRequest->getParsedBody(),
$routeParams
);
$request = new Request($input, $actor, $httpRequest);
return $action->handle($request);
};
return $routes;
}
}

View File

@@ -10,10 +10,12 @@
namespace Flarum\Api;
use Flarum\Core\Users\User;
use Flarum\Http\Controller\ControllerInterface;
use Flarum\Core\User;
use Illuminate\Contracts\Container\Container;
use Exception;
use Flarum\Api\Middleware\JsonApiErrors;
use InvalidArgumentException;
use Zend\Diactoros\ServerRequestFactory;
class Client
{
@@ -33,24 +35,31 @@ class Client
/**
* Execute the given API action class, pass the input and return its response.
*
* @param string|ControllerInterface $controller
* @param User $actor
* @param string $actionClass
* @param array $input
* @return object
* @param array $queryParams
* @param array $body
* @return \Psr\Http\Message\ResponseInterface
*/
public function send(User $actor, $actionClass, array $input = [])
public function send($controller, User $actor, array $queryParams = [], array $body = [])
{
/** @var \Flarum\Api\Actions\Action $action */
$action = $this->container->make($actionClass);
$request = ServerRequestFactory::fromGlobals(null, $queryParams, $body)->withAttribute('actor', $actor);
try {
$response = $action->handle(new Request($input, $actor));
} catch (Exception $e) {
$middleware = new JsonApiErrors();
$response = $middleware->handle($e);
if (is_string($controller)) {
$controller = $this->container->make($controller);
}
return new Response($response);
if (! ($controller instanceof ControllerInterface)) {
throw new InvalidArgumentException('Endpoint must be an instance of '
. ControllerInterface::class);
}
try {
$response = $controller->handle($request);
} catch (Exception $e) {
$response = $this->container->make('Flarum\Api\Middleware\HandleErrors')->handle($e);
}
return $response;
}
}

View File

@@ -8,7 +8,7 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Commands;
namespace Flarum\Api\Command;
class GenerateAccessToken
{

View File

@@ -8,9 +8,10 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Commands;
namespace Flarum\Api\Command;
use Flarum\Api\AccessToken;
use Flarum\Api\Command\GenerateAccessToken;
class GenerateAccessTokenHandler
{

View File

@@ -0,0 +1,25 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Tobscure\JsonApi\Collection;
use Tobscure\JsonApi\SerializerInterface;
abstract class AbstractCollectionController extends AbstractSerializeController
{
/**
* {@inheritdoc}
*/
protected function createElement($data, SerializerInterface $serializer)
{
return new Collection($data, $serializer);
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Psr\Http\Message\ServerRequestInterface;
abstract class AbstractCreateController extends AbstractResourceController
{
/**
* {@inheritdoc}
*/
public function handle(ServerRequestInterface $request)
{
return parent::handle($request)->withStatus(201);
}
}

View File

@@ -8,21 +8,18 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions;
namespace Flarum\Api\Controller;
use Flarum\Api\Request;
use Flarum\Http\Controller\ControllerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\EmptyResponse;
abstract class DeleteAction implements Action
abstract class AbstractDeleteController implements ControllerInterface
{
/**
* Delegate deletion of the resource, and return a 204 No Content
* response.
*
* @param Request $request
* @return EmptyResponse
* {@inheritdoc}
*/
public function handle(Request $request)
public function handle(ServerRequestInterface $request)
{
$this->delete($request);
@@ -32,8 +29,7 @@ abstract class DeleteAction implements Action
/**
* Delete the resource.
*
* @param Request $request
* @return void
* @param ServerRequestInterface $request
*/
abstract protected function delete(Request $request);
abstract protected function delete(ServerRequestInterface $request);
}

View File

@@ -0,0 +1,25 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Tobscure\JsonApi\Resource;
use Tobscure\JsonApi\SerializerInterface;
abstract class AbstractResourceController extends AbstractSerializeController
{
/**
* {@inheritdoc}
*/
protected function createElement($data, SerializerInterface $serializer)
{
return new Resource($data, $serializer);
}
}

View File

@@ -0,0 +1,231 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Api\JsonApiResponse;
use Flarum\Http\Controller\ControllerInterface;
use Illuminate\Contracts\Container\Container;
use Flarum\Event\ConfigureApiController;
use Flarum\Event\PrepareApiData;
use Illuminate\Contracts\Events\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
use Tobscure\JsonApi\Parameters;
use Tobscure\JsonApi\SerializerInterface;
abstract class AbstractSerializeController implements ControllerInterface
{
/**
* The name of the serializer class to output results with.
*
* @var string
*/
public $serializer;
/**
* The relationships that are included by default.
*
* @var array
*/
public $include = [];
/**
* The relationships that are available to be included.
*
* @var array
*/
public $optionalInclude = [];
/**
* The maximum number of records that can be requested.
*
* @var int
*/
public $maxLimit = 50;
/**
* The number of records included by default.
*
* @var int
*/
public $limit = 20;
/**
* The fields that are available to be sorted by.
*
* @var array
*/
public $sortFields = [];
/**
* The default sort field and order to user.
*
* @var array|null
*/
public $sort;
/**
* @var Container
*/
protected static $container;
/**
* @var Dispatcher
*/
protected static $events;
/**
* {@inheritdoc}
*/
public function handle(ServerRequestInterface $request)
{
$document = new Document;
static::$events->fire(
new ConfigureApiController($this)
);
$data = $this->data($request, $document);
static::$events->fire(
new PrepareApiData($this, $data, $request, $document)
);
$serializer = static::$container->make($this->serializer);
$serializer->setActor($request->getAttribute('actor'));
$element = $this->createElement($data, $serializer)
->with($this->extractInclude($request))
->fields($this->extractFields($request));
$document->setData($element);
return new JsonApiResponse($document);
}
/**
* Get the data to be serialized and assigned to the response document.
*
* @param ServerRequestInterface $request
* @param Document $document
* @return mixed
*/
abstract protected function data(ServerRequestInterface $request, Document $document);
/**
* Create a PHP JSON-API Element for output in the document.
*
* @param mixed $data
* @param SerializerInterface $serializer
* @return \Tobscure\JsonApi\ElementInterface
*/
abstract protected function createElement($data, SerializerInterface $serializer);
/**
* @param ServerRequestInterface $request
* @return array
* @throws \Tobscure\JsonApi\Exception\InvalidParameterException
*/
protected function extractInclude(ServerRequestInterface $request)
{
$available = array_merge($this->include, $this->optionalInclude);
return $this->buildParameters($request)->getInclude($available) ?: $this->include;
}
/**
* @param ServerRequestInterface $request
* @return array
*/
protected function extractFields(ServerRequestInterface $request)
{
return $this->buildParameters($request)->getFields();
}
/**
* @param ServerRequestInterface $request
* @return array|null
* @throws \Tobscure\JsonApi\Exception\InvalidParameterException
*/
protected function extractSort(ServerRequestInterface $request)
{
return $this->buildParameters($request)->getSort($this->sortFields) ?: $this->sort;
}
/**
* @param ServerRequestInterface $request
* @return int
* @throws \Tobscure\JsonApi\Exception\InvalidParameterException
*/
protected function extractOffset(ServerRequestInterface $request)
{
return $this->buildParameters($request)->getOffset($this->extractLimit($request)) ?: 0;
}
/**
* @param ServerRequestInterface $request
* @return int
*/
protected function extractLimit(ServerRequestInterface $request)
{
return $this->buildParameters($request)->getLimit($this->maxLimit) ?: $this->limit;
}
/**
* @param ServerRequestInterface $request
* @return array
*/
protected function extractFilter(ServerRequestInterface $request)
{
return $this->buildParameters($request)->getFilter();
}
/**
* @param ServerRequestInterface $request
* @return Parameters
*/
protected function buildParameters(ServerRequestInterface $request)
{
return new Parameters($request->getQueryParams());
}
/**
* @return Dispatcher
*/
public static function getEventDispatcher()
{
return static::$events;
}
/**
* @param Dispatcher $events
*/
public static function setEventDispatcher(Dispatcher $events)
{
static::$events = $events;
}
/**
* @return Container
*/
public static function getContainer()
{
return static::$container;
}
/**
* @param Container $container
*/
public static function setContainer(Container $container)
{
static::$container = $container;
}
}

View File

@@ -0,0 +1,72 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Command\StartDiscussion;
use Flarum\Core\Command\ReadDiscussion;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class CreateDiscussionController extends AbstractCreateController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\DiscussionSerializer';
/**
* {@inheritdoc}
*/
public $include = [
'posts',
'startUser',
'lastUser',
'startPost',
'lastPost'
];
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$discussion = $this->bus->dispatch(
new StartDiscussion($actor, array_get($request->getParsedBody(), '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 ($actor->exists) {
$this->bus->dispatch(
new ReadDiscussion($discussion->id, $actor, 1)
);
}
return $discussion;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Command\CreateGroup;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class CreateGroupController extends AbstractCreateController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\GroupSerializer';
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->bus->dispatch(
new CreateGroup($request->getAttribute('actor'), array_get($request->getParsedBody(), 'data'))
);
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Command\PostReply;
use Flarum\Core\Command\ReadDiscussion;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class CreatePostController extends AbstractCreateController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\PostSerializer';
/**
* {@inheritdoc}
*/
public $include = [
'user',
'discussion',
'discussion.posts',
'discussion.lastUser'
];
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$data = array_get($request->getParsedBody(), 'data');
$discussionId = array_get($data, 'relationships.discussion.data.id');
$post = $this->bus->dispatch(
new PostReply($discussionId, $actor, $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 ($actor->exists) {
$this->bus->dispatch(
new ReadDiscussion($discussionId, $actor, $post->number)
);
}
$discussion = $post->discussion;
$discussion->posts = $discussion->postsVisibleTo($actor)->orderBy('time')->lists('id');
return $post;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Command\RegisterUser;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class CreateUserController extends AbstractCreateController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\CurrentUserSerializer';
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->bus->dispatch(
new RegisterUser($request->getAttribute('actor'), array_get($request->getParsedBody(), 'data'))
);
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Command\DeleteAvatar;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class DeleteAvatarController extends AbstractResourceController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\UserSerializer';
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->bus->dispatch(
new DeleteAvatar(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
);
}
}

View File

@@ -8,14 +8,13 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Discussions;
namespace Flarum\Api\Controller;
use Flarum\Core\Discussions\Commands\DeleteDiscussion;
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
use Flarum\Api\Request;
use Flarum\Core\Command\DeleteDiscussion;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
class DeleteAction extends BaseDeleteAction
class DeleteDiscussionController extends AbstractDeleteController
{
/**
* @var Dispatcher
@@ -33,11 +32,11 @@ class DeleteAction extends BaseDeleteAction
/**
* {@inheritdoc}
*/
protected function delete(Request $request)
protected function delete(ServerRequestInterface $request)
{
$id = $request->get('id');
$actor = $request->actor;
$input = $request->all();
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$input = $request->getParsedBody();
$this->bus->dispatch(
new DeleteDiscussion($id, $actor, $input)

View File

@@ -8,14 +8,13 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Users;
namespace Flarum\Api\Controller;
use Flarum\Core\Users\Commands\DeleteUser;
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
use Flarum\Api\Request;
use Flarum\Core\Command\DeleteGroup;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
class DeleteAction extends BaseDeleteAction
class DeleteGroupController extends AbstractDeleteController
{
/**
* @var Dispatcher
@@ -31,14 +30,12 @@ class DeleteAction extends BaseDeleteAction
}
/**
* Delete a user.
*
* @param Request $request
* {@inheritdoc}
*/
protected function delete(Request $request)
protected function delete(ServerRequestInterface $request)
{
$this->bus->dispatch(
new DeleteUser($request->get('id'), $request->actor)
new DeleteGroup(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
);
}
}

View File

@@ -8,14 +8,13 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Groups;
namespace Flarum\Api\Controller;
use Flarum\Core\Groups\Commands\DeleteGroup;
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
use Flarum\Api\Request;
use Flarum\Core\Command\DeletePost;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
class DeleteAction extends BaseDeleteAction
class DeletePostController extends AbstractDeleteController
{
/**
* @var Dispatcher
@@ -31,14 +30,12 @@ class DeleteAction extends BaseDeleteAction
}
/**
* Delete a group.
*
* @param Request $request
* {@inheritdoc}
*/
protected function delete(Request $request)
protected function delete(ServerRequestInterface $request)
{
$this->bus->dispatch(
new DeleteGroup($request->get('id'), $request->actor)
new DeletePost(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
);
}
}

View File

@@ -8,14 +8,13 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Posts;
namespace Flarum\Api\Controller;
use Flarum\Core\Posts\Commands\DeletePost;
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
use Flarum\Api\Request;
use Flarum\Core\Command\DeleteUser;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
class DeleteAction extends BaseDeleteAction
class DeleteUserController extends AbstractDeleteController
{
/**
* @var Dispatcher
@@ -31,15 +30,12 @@ class DeleteAction extends BaseDeleteAction
}
/**
* Delete a post.
*
* @param Request $request
* @return void
* {@inheritdoc}
*/
protected function delete(Request $request)
protected function delete(ServerRequestInterface $request)
{
$this->bus->dispatch(
new DeletePost($request->get('id'), $request->actor)
new DeleteUser(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'))
);
}
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Repository\UserRepository;
use Flarum\Core\Command\RequestPasswordReset;
use Flarum\Http\Controller\ControllerInterface;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\EmptyResponse;
class ForgotPasswordController implements ControllerInterface
{
/**
* @var \Flarum\Core\Repository\UserRepository
*/
protected $users;
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param \Flarum\Core\Repository\UserRepository $users
* @param Dispatcher $bus
*/
public function __construct(UserRepository $users, Dispatcher $bus)
{
$this->users = $users;
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
public function handle(ServerRequestInterface $request)
{
$email = array_get($request->getParsedBody(), 'email');
$this->bus->dispatch(
new RequestPasswordReset($email)
);
return new EmptyResponse;
}
}

View File

@@ -0,0 +1,97 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Search\SearchCriteria;
use Flarum\Core\Search\Discussion\DiscussionSearcher;
use Flarum\Api\UrlGenerator;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ListDiscussionsController extends AbstractCollectionController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\DiscussionSerializer';
/**
* {@inheritdoc}
*/
public $include = [
'startUser',
'lastUser',
'relevantPosts',
'relevantPosts.discussion',
'relevantPosts.user'
];
/**
* {@inheritdoc}
*/
public $optionalInclude = [
'startPost',
'lastPost'
];
/**
* {@inheritdoc}
*/
public $sortFields = ['lastTime', 'commentsCount', 'startTime'];
/**
* @var DiscussionSearcher
*/
protected $searcher;
/**
* @var UrlGenerator
*/
protected $url;
/**
* @param DiscussionSearcher $searcher
* @param UrlGenerator $url
*/
public function __construct(DiscussionSearcher $searcher, UrlGenerator $url)
{
$this->searcher = $searcher;
$this->url = $url;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$query = array_get($this->extractFilter($request), 'q');
$sort = $this->extractSort($request);
$criteria = new SearchCriteria($actor, $query, $sort);
$limit = $this->extractLimit($request);
$offset = $this->extractOffset($request);
$load = array_merge($this->extractInclude($request), ['state']);
$results = $this->searcher->search($criteria, $limit, $offset, $load);
$document->addPaginationLinks(
$this->url->toRoute('discussions.index'),
$request->getQueryParams(),
$offset,
$limit,
$results->areMoreResults() ? null : 0
);
return $results->getResults();
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Group;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ListGroupsController extends AbstractCollectionController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\GroupSerializer';
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
return Group::all();
}
}

View File

@@ -0,0 +1,71 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Repository\NotificationRepository;
use Flarum\Core\Exception\PermissionDeniedException;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ListNotificationsController extends AbstractCollectionController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\NotificationSerializer';
/**
* {@inheritdoc}
*/
public $include = [
'sender',
'subject',
'subject.discussion'
];
/**
* {@inheritdoc}
*/
public $limit = 10;
/**
* @var \Flarum\Core\Repository\NotificationRepository
*/
protected $notifications;
/**
* @param \Flarum\Core\Repository\NotificationRepository $notifications
*/
public function __construct(NotificationRepository $notifications)
{
$this->notifications = $notifications;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
if ($actor->isGuest()) {
throw new PermissionDeniedException;
}
$actor->markNotificationsAsRead()->save();
$limit = $this->extractLimit($request);
$offset = $this->extractOffset($request);
$include = $this->extractInclude($request);
return $this->notifications->findByUser($actor, $limit, $offset)->load($include);
}
}

View File

@@ -0,0 +1,113 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Repository\PostRepository;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
use Tobscure\JsonApi\Exception\InvalidParameterException;
class ListPostsController extends AbstractCollectionController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\PostSerializer';
/**
* {@inheritdoc}
*/
public $include = [
'user',
'user.groups',
'editUser',
'hideUser',
'discussion'
];
/**
* {@inheritdoc}
*/
public $sortFields = ['time'];
/**
* @var \Flarum\Core\Repository\PostRepository
*/
private $posts;
/**
* @param \Flarum\Core\Repository\PostRepository $posts
*/
public function __construct(PostRepository $posts)
{
$this->posts = $posts;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$filter = $this->extractFilter($request);
$include = $this->extractInclude($request);
$where = [];
if ($postIds = array_get($filter, 'id')) {
$posts = $this->posts->findByIds(explode(',', $postIds), $actor);
} else {
if ($discussionId = array_get($filter, 'discussion')) {
$where['discussion_id'] = $discussionId;
}
if ($number = array_get($filter, 'number')) {
$where['number'] = $number;
}
if ($userId = array_get($filter, 'user')) {
$where['user_id'] = $userId;
}
if ($type = array_get($filter, 'type')) {
$where['type'] = $type;
}
$posts = $this->getPosts($request, $where);
}
return $posts->load($include);
}
/**
* @param ServerRequestInterface $request
* @param array $where
* @return \Illuminate\Database\Eloquent\Collection
* @throws InvalidParameterException
*/
private function getPosts(ServerRequestInterface $request, array $where)
{
$queryParams = $request->getQueryParams();
$actor = $request->getAttribute('actor');
$sort = $this->extractSort($request);
$limit = $this->extractLimit($request);
if (($near = array_get($queryParams, 'page.near')) > 1) {
if (count($where) > 1 || ! isset($where['discussion_id']) || $sort) {
throw new InvalidParameterException('You can only use page[near] with '
. 'filter[discussion] and the default sort order');
}
$offset = $this->posts->getIndexForNumber($where['discussion_id'], $near, $actor);
$offset = max(0, $offset - $limit / 2);
} else {
$offset = $this->extractOffset($request);
}
return $this->posts->findWhere($where, $actor, $sort, $limit, $offset);
}
}

View File

@@ -0,0 +1,89 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Search\SearchCriteria;
use Flarum\Core\Search\User\UserSearcher;
use Flarum\Api\UrlGenerator;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ListUsersController extends AbstractCollectionController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\UserSerializer';
/**
* {@inheritdoc}
*/
public $include = ['groups'];
/**
* {@inheritdoc}
*/
public $sortFields = [
'username',
'postsCount',
'discussionsCount',
'lastSeenTime',
'joinTime'
];
/**
* @var UserSearcher
*/
protected $searcher;
/**
* @var UrlGenerator
*/
protected $url;
/**
* @param UserSearcher $searcher
* @param UrlGenerator $url
*/
public function __construct(UserSearcher $searcher, UrlGenerator $url)
{
$this->searcher = $searcher;
$this->url = $url;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$query = array_get($this->extractFilter($request), 'q');
$sort = $this->extractSort($request);
$criteria = new SearchCriteria($actor, $query, $sort);
$limit = $this->extractLimit($request);
$offset = $this->extractOffset($request);
$load = $this->extractInclude($request);
$results = $this->searcher->search($criteria, $limit, $offset, $load);
$document->addPaginationLinks(
$this->url->toRoute('users.index'),
$request->getQueryParams(),
$offset,
$limit,
$results->areMoreResults() ? null : 0
);
return $results->getResults();
}
}

View File

@@ -8,14 +8,13 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions\Notifications;
namespace Flarum\Api\Controller;
use Flarum\Core\Notifications\Commands\ReadAllNotifications;
use Flarum\Core\Command\ReadAllNotifications;
use Illuminate\Contracts\Bus\Dispatcher;
use Flarum\Api\Request;
use Flarum\Api\Actions\DeleteAction;
use Psr\Http\Message\ServerRequestInterface;
class ReadAllAction extends DeleteAction
class ReadAllNotificationsController extends AbstractDeleteController
{
/**
* @var Dispatcher
@@ -31,15 +30,12 @@ class ReadAllAction extends DeleteAction
}
/**
* Mark all notifications as read.
*
* @param Request $request
* @return void
* {@inheritdoc}
*/
protected function delete(Request $request)
protected function delete(ServerRequestInterface $request)
{
$this->bus->dispatch(
new ReadAllNotifications($request->actor)
new ReadAllNotifications($request->getAttribute('actor'))
);
}
}

View File

@@ -8,26 +8,28 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Actions;
namespace Flarum\Api\Controller;
use Flarum\Api\Request;
use Flarum\Core\Groups\Permission;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Core\Access\AssertPermissionTrait;
use Flarum\Core\Permission;
use Flarum\Http\Controller\ControllerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\EmptyResponse;
class PermissionAction implements Action
class SetPermissionController implements ControllerInterface
{
use AssertPermissionTrait;
/**
* {@inheritdoc}
*/
public function handle(Request $request, array $routeParams = [])
public function handle(ServerRequestInterface $request)
{
if (! $request->actor->isAdmin()) {
throw new PermissionDeniedException;
}
$this->assertAdmin($request->getAttribute('actor'));
$permission = $request->get('permission');
$groupIds = $request->get('groupIds');
$body = $request->getParsedBody();
$permission = array_get($body, 'permission');
$groupIds = array_get($body, 'groupIds');
Permission::where('permission', $permission)->delete();

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Access\AssertPermissionTrait;
use Flarum\Http\Controller\ControllerInterface;
use Flarum\Settings\SettingsRepository;
use Flarum\Event\SettingWasSet;
use Flarum\Event\PrepareSerializedSetting;
use Illuminate\Contracts\Events\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\EmptyResponse;
class SetSettingsController implements ControllerInterface
{
use AssertPermissionTrait;
/**
* @var \Flarum\Settings\SettingsRepository
*/
protected $settings;
/**
* @var Dispatcher
*/
protected $dispatcher;
/**
* @param SettingsRepository $settings
*/
public function __construct(SettingsRepository $settings, Dispatcher $dispatcher)
{
$this->settings = $settings;
$this->dispatcher = $dispatcher;
}
/**
* {@inheritdoc}
*/
public function handle(ServerRequestInterface $request)
{
$this->assertAdmin($request->getAttribute('actor'));
$settings = $request->getParsedBody();
foreach ($settings as $k => $v) {
$this->dispatcher->fire(new PrepareSerializedSetting($k, $v));
$this->settings->set($k, $v);
$this->dispatcher->fire(new SettingWasSet($k, $v));
}
return new EmptyResponse(204);
}
}

View File

@@ -0,0 +1,168 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Discussion;
use Flarum\Core\Repository\DiscussionRepository;
use Flarum\Core\Repository\PostRepository;
use Flarum\Core\User;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ShowDiscussionController extends AbstractResourceController
{
/**
* @var DiscussionRepository
*/
protected $discussions;
/**
* @inheritdoc
*/
public $serializer = 'Flarum\Api\Serializer\DiscussionSerializer';
/**
* @inheritdoc
*/
public $include = [
'posts',
'posts.discussion',
'posts.user',
'posts.user.groups',
'posts.editUser',
'posts.hideUser'
];
/**
* @inheritdoc
*/
public $optionalInclude = [
'startUser',
'lastUser',
'startPost',
'lastPost'
];
/**
* @param \Flarum\Core\Repository\DiscussionRepository $discussions
* @param \Flarum\Core\Repository\PostRepository $posts
*/
public function __construct(DiscussionRepository $discussions, PostRepository $posts)
{
$this->discussions = $discussions;
$this->posts = $posts;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$discussionId = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$include = $this->extractInclude($request);
$discussion = $this->discussions->findOrFail($discussionId, $actor);
if (in_array('posts', $include)) {
$postRelationships = $this->getPostRelationships($include);
$this->includePosts($discussion, $request, $postRelationships);
}
return $discussion;
}
/**
* @param Discussion $discussion
* @param ServerRequestInterface $request
* @param array $include
*/
private function includePosts(Discussion $discussion, ServerRequestInterface $request, array $include)
{
$actor = $request->getAttribute('actor');
$limit = $this->extractLimit($request);
$offset = $this->getPostsOffset($request, $discussion, $limit);
$allPosts = $this->loadPostIds($discussion, $actor);
$loadedPosts = $this->loadPosts($discussion, $actor, $offset, $limit, $include);
array_splice($allPosts, $offset, $limit, $loadedPosts);
$discussion->setRelation('posts', $allPosts);
}
/**
* @param Discussion $discussion
* @param User $actor
* @return array
*/
private function loadPostIds(Discussion $discussion, User $actor)
{
return $discussion->postsVisibleTo($actor)->orderBy('time')->lists('id')->all();
}
/**
* @param array $include
* @return array
*/
private function getPostRelationships(array $include)
{
$prefixLength = strlen($prefix = 'posts.');
$relationships = [];
foreach ($include as $relationship) {
if (substr($relationship, 0, $prefixLength) === $prefix) {
$relationships[] = substr($relationship, $prefixLength);
}
}
return $relationships;
}
/**
* @param ServerRequestInterface $request
* @param Discussion$discussion
* @param int $limit
* @return int
*/
private function getPostsOffset(ServerRequestInterface $request, Discussion $discussion, $limit)
{
$queryParams = $request->getQueryParams();
$actor = $request->getAttribute('actor');
if (($near = array_get($queryParams, 'page.near')) > 1) {
$offset = $this->posts->getIndexForNumber($discussion->id, $near, $actor);
$offset = max(0, $offset - $limit / 2);
} else {
$offset = $this->extractOffset($request);
}
return $offset;
}
/**
* @param Discussion $discussion
* @param User $actor
* @param int $offset
* @param int $limit
* @param array $include
* @return mixed
*/
private function loadPosts($discussion, $actor, $offset, $limit, array $include)
{
$query = $discussion->postsVisibleTo($actor);
$query->orderBy('time')->skip($offset)->take($limit)->with($include);
return $query->get()->all();
}
}

View File

@@ -0,0 +1,38 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Group;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ShowForumController extends AbstractResourceController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\ForumSerializer';
/**
* {@inheritdoc}
*/
public $include = ['groups'];
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
return [
'groups' => Group::whereVisibleTo($request->getAttribute('actor'))->get()
];
}
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Repository\PostRepository;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ShowPostController extends AbstractResourceController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\PostSerializer';
/**
* {@inheritdoc}
*/
public $include = [
'user',
'user.groups',
'editUser',
'hideUser',
'discussion'
];
/**
* @var \Flarum\Core\Repository\PostRepository
*/
protected $posts;
/**
* @param PostRepository $posts
*/
public function __construct(PostRepository $posts)
{
$this->posts = $posts;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
return $this->posts->findOrFail(array_get($request->getQueryParams(), 'id'), $request->getAttribute('actor'));
}
}

View File

@@ -0,0 +1,61 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Repository\UserRepository;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ShowUserController extends AbstractResourceController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\UserSerializer';
/**
* {@inheritdoc}
*/
public $include = ['groups'];
/**
* @var UserRepository
*/
protected $users;
/**
* @param UserRepository $users
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = array_get($request->getQueryParams(), 'id');
if (! is_numeric($id)) {
$id = $this->users->getIdForUsername($id);
}
$actor = $request->getAttribute('actor');
if ($actor->id == $id) {
$this->serializer = 'Flarum\Api\Serializer\CurrentUserSerializer';
}
return $this->users->findOrFail($id, $actor);
}
}

View File

@@ -0,0 +1,86 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Api\Command\GenerateAccessToken;
use Flarum\Core\Repository\UserRepository;
use Flarum\Core\Exception\PermissionDeniedException;
use Flarum\Event\UserEmailChangeWasRequested;
use Flarum\Http\Controller\ControllerInterface;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\JsonResponse;
class TokenController implements ControllerInterface
{
/**
* @var UserRepository
*/
protected $users;
/**
* @var BusDispatcher
*/
protected $bus;
/**
* @var EventDispatcher
*/
protected $events;
/**
* @param UserRepository $users
* @param BusDispatcher $bus
* @param EventDispatcher $events
*/
public function __construct(UserRepository $users, BusDispatcher $bus, EventDispatcher $events)
{
$this->users = $users;
$this->bus = $bus;
$this->events = $events;
}
/**
* {@inheritdoc}
*/
public function handle(ServerRequestInterface $request)
{
$body = $request->getParsedBody();
$identification = array_get($body, 'identification');
$password = array_get($body, 'password');
$user = $this->users->findByIdentification($identification);
if (! $user || ! $user->checkPassword($password)) {
throw new PermissionDeniedException;
}
if (! $user->is_activated) {
$this->events->fire(new UserEmailChangeWasRequested($user, $user->email));
return new JsonResponse([
'code' => 'confirm_email',
'email' => $user->email
], 401);
}
$token = $this->bus->dispatch(
new GenerateAccessToken($user->id)
);
return new JsonResponse([
'token' => $token->id,
'userId' => $user->id
]);
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Access\AssertPermissionTrait;
use Flarum\Extension\ExtensionManager;
use Psr\Http\Message\ServerRequestInterface;
class UninstallExtensionController extends AbstractDeleteController
{
use AssertPermissionTrait;
/**
* @var ExtensionManager
*/
protected $extensions;
/**
* @param \Flarum\Extension\ExtensionManager $extensions
*/
public function __construct(ExtensionManager $extensions)
{
$this->extensions = $extensions;
}
protected function delete(ServerRequestInterface $request)
{
$this->assertAdmin($request->getAttribute('actor'));
$name = array_get($request->getParsedBody(), 'name');
$this->extensions->disable($name);
$this->extensions->uninstall($name);
}
}

View File

@@ -0,0 +1,82 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Command\EditDiscussion;
use Flarum\Core\Command\ReadDiscussion;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class UpdateDiscussionController extends AbstractResourceController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\DiscussionSerializer';
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = $request->getAttribute('actor');
$discussionId = array_get($request->getQueryParams(), 'id');
$data = array_get($request->getParsedBody(), 'data', []);
$discussion = $this->bus->dispatch(
new EditDiscussion($discussionId, $actor, $data)
);
// 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 ReadDiscussion($discussionId, $actor, $readNumber)
);
$discussion = $state->discussion;
}
if ($posts = $discussion->getModifiedPosts()) {
$discussionPosts = $discussion->postsVisibleTo($actor)->orderBy('time')->lists('id')->all();
foreach ($discussionPosts as &$id) {
foreach ($posts as $post) {
if ($id == $post->id) {
$id = $post;
$post->discussion = $post->discussion_id;
$post->user = $post->user_id;
}
}
}
$discussion->setRelation('posts', $discussionPosts);
$this->include = array_merge($this->include, ['posts', 'posts.discussion', 'posts.user']);
}
return $discussion;
}
}

View File

@@ -0,0 +1,52 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Access\AssertPermissionTrait;
use Flarum\Extension\ExtensionManager;
use Flarum\Http\Controller\ControllerInterface;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
class UpdateExtensionController implements ControllerInterface
{
use AssertPermissionTrait;
/**
* @var ExtensionManager
*/
protected $extensions;
/**
* @param ExtensionManager $extensions
*/
public function __construct(ExtensionManager $extensions)
{
$this->extensions = $extensions;
}
/**
* {@inheritdoc}
*/
public function handle(ServerRequestInterface $request)
{
$this->assertAdmin($request->getAttribute('actor'));
$enabled = array_get($request->getParsedBody(), 'enabled');
$name = array_get($request->getQueryParams(), 'name');
if ($enabled === true) {
$this->extensions->enable($name);
} elseif ($enabled === false) {
$this->extensions->disable($name);
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Command\EditGroup;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class UpdateGroupController extends AbstractResourceController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\GroupSerializer';
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$data = array_get($request->getParsedBody(), 'data', []);
return $this->bus->dispatch(
new EditGroup($id, $actor, $data)
);
}
}

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Command\ReadNotification;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class UpdateNotificationController extends AbstractResourceController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\NotificationSerializer';
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
return $this->bus->dispatch(
new ReadNotification($id, $actor)
);
}
}

View File

@@ -0,0 +1,59 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Command\EditPost;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class UpdatePostController extends AbstractResourceController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\PostSerializer';
/**
* {@inheritdoc}
*/
public $include = [
'editUser',
'discussion'
];
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$data = array_get($request->getParsedBody(), 'data', []);
return $this->bus->dispatch(
new EditPost($id, $actor, $data)
);
}
}

View File

@@ -0,0 +1,56 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Command\EditUser;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class UpdateUserController extends AbstractResourceController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\CurrentUserSerializer';
/**
* {@inheritdoc}
*/
public $include = ['groups'];
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$data = array_get($request->getParsedBody(), 'data', []);
return $this->bus->dispatch(
new EditUser($id, $actor, $data)
);
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Controller;
use Flarum\Core\Command\UploadAvatar;
use Illuminate\Contracts\Bus\Dispatcher;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class UploadAvatarController extends AbstractResourceController
{
/**
* {@inheritdoc}
*/
public $serializer = 'Flarum\Api\Serializer\UserSerializer';
/**
* @var Dispatcher
*/
protected $bus;
/**
* @param Dispatcher $bus
*/
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* {@inheritdoc}
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$id = array_get($request->getQueryParams(), 'id');
$actor = $request->getAttribute('actor');
$file = array_get($request->getUploadedFiles(), 'avatar');
return $this->bus->dispatch(
new UploadAvatar($id, $file, $actor)
);
}
}

View File

@@ -1,39 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api;
class JsonApiRequest extends Request
{
/**
* @var array
*/
public $sort;
/**
* @var array
*/
public $include;
/**
* @var array
*/
public $link;
/**
* @var int
*/
public $limit;
/**
* @var int
*/
public $offset;
}

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api;
use Tobscure\JsonApi\Document;
use Zend\Diactoros\Response\JsonResponse;
class JsonApiResponse extends JsonResponse
{
/**
* {@inheritdoc}
*/
public function __construct(Document $document, $status = 200, array $headers = [], $encodingOptions = 15)
{
$headers['content-type'] = 'application/vnd.api+json';
parent::__construct($document, $status, $headers, $encodingOptions);
}
}

View File

@@ -12,13 +12,14 @@ namespace Flarum\Api\Middleware;
use Flarum\Api\AccessToken;
use Flarum\Api\ApiKey;
use Flarum\Core\Users\User;
use Flarum\Core\Guest;
use Flarum\Core\User;
use Illuminate\Contracts\Container\Container;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Zend\Stratigility\MiddlewareInterface;
class LoginWithHeader implements MiddlewareInterface
class AuthenticateWithHeader implements MiddlewareInterface
{
/**
* @var Container
@@ -42,6 +43,17 @@ class LoginWithHeader implements MiddlewareInterface
* {@inheritdoc}
*/
public function __invoke(Request $request, Response $response, callable $out = null)
{
$request = $this->logIn($request);
return $out ? $out($request, $response) : $response;
}
/**
* @param Request $request
* @return Request
*/
protected function logIn(Request $request)
{
$header = $request->getHeaderLine('authorization');
@@ -51,18 +63,20 @@ class LoginWithHeader implements MiddlewareInterface
$token = substr($parts[0], strlen($this->prefix));
if (($accessToken = AccessToken::find($token)) && $accessToken->isValid()) {
$this->app->instance('flarum.actor', $user = $accessToken->user);
$user = $accessToken->user;
$user->updateLastSeen()->save();
return $request->withAttribute('actor', $user);
} elseif (isset($parts[1]) && ($apiKey = ApiKey::valid($token))) {
$userParts = explode('=', trim($parts[1]));
if (isset($userParts[0]) && $userParts[0] === 'userId') {
$this->app->instance('flarum.actor', $user = User::find($userParts[1]));
return $request->withAttribute('actor', $user = User::find($userParts[1]));
}
}
}
return $out ? $out($request, $response) : $response;
return $request->withAttribute('actor', new Guest);
}
}

View File

@@ -10,18 +10,34 @@
namespace Flarum\Api\Middleware;
use Flarum\Core\Exceptions\JsonApiSerializable;
use Flarum\Api\JsonApiResponse;
use Flarum\Foundation\Application;
use Illuminate\Contracts\Validation\ValidationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Tobscure\JsonApi\Document;
use Tobscure\JsonApi\Exception\JsonApiSerializableInterface;
use Zend\Diactoros\Response\JsonResponse;
use Zend\Stratigility\ErrorMiddlewareInterface;
use Flarum\Core;
use Exception;
class JsonApiErrors implements ErrorMiddlewareInterface
class HandleErrors implements ErrorMiddlewareInterface
{
/**
* @var Application
*/
private $app;
/**
* @param Application $app
*/
public function __construct(Application $app)
{
$this->app = $app;
}
/**
* {@inheritdoc}
*/
@@ -32,11 +48,11 @@ class JsonApiErrors implements ErrorMiddlewareInterface
public function handle(Exception $e)
{
if ($e instanceof JsonApiSerializable) {
if ($e instanceof JsonApiSerializableInterface) {
$status = $e->getStatusCode();
$errors = $e->getErrors();
} else if ($e instanceof ValidationException) {
} elseif ($e instanceof ValidationException) {
$status = 422;
$errors = $e->errors()->toArray();
@@ -46,7 +62,7 @@ class JsonApiErrors implements ErrorMiddlewareInterface
'source' => ['pointer' => '/data/attributes/' . $field],
];
}, array_keys($errors), $errors);
} else if ($e instanceof ModelNotFoundException) {
} elseif ($e instanceof ModelNotFoundException) {
$status = 404;
$errors = [];
@@ -58,19 +74,16 @@ class JsonApiErrors implements ErrorMiddlewareInterface
'title' => 'Internal Server Error'
];
if (Core::inDebugMode()) {
if ($this->app->inDebugMode()) {
$error['detail'] = (string) $e;
}
$errors = [$error];
}
// JSON API errors must be collected in an array under the
// "errors" key in the top level of the document
$data = [
'errors' => $errors,
];
$document = new Document;
$document->setErrors($errors);
return new JsonResponse($data, $status);
return new JsonApiResponse($document, $status);
}
}

View File

@@ -1,38 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Middleware;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Zend\Stratigility\MiddlewareInterface;
class ReadJsonParameters implements MiddlewareInterface
{
/**
* {@inheritdoc}
*/
public function __invoke(Request $request, Response $response, callable $out = null)
{
if (str_contains($request->getHeaderLine('content-type'), 'json')) {
$input = json_decode($request->getBody(), true);
if (! is_array($input)) {
$input = [];
}
foreach ($input as $name => $value) {
$request = $request->withAttribute($name, $value);
}
}
return $out ? $out($request, $response) : $response;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Relationship;
use Flarum\Core\User;
use Illuminate\Contracts\Container\Container;
trait BuilderTrait
{
/**
* @var Container
*/
protected $container;
/**
* @var string|\Closure
*/
protected $relation;
/**
* @var User
*/
protected $actor;
/**
* {@inheritdoc}
*/
protected function getRelationshipData($model)
{
$relation = $this->relation;
if (is_object($model)) {
return $model->$relation;
} elseif (is_array($model)) {
return $model[$relation];
}
}
/**
* {@inheritdoc}
*/
protected function resolveSerializerClass($class)
{
$serializer = $this->container->make($class);
$serializer->setActor($this->actor);
return $serializer;
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Relationship;
use Flarum\Core\User;
use Illuminate\Contracts\Container\Container;
use Tobscure\JsonApi\Relationship\AbstractHasManyBuilder;
class HasManyBuilder extends AbstractHasManyBuilder
{
use BuilderTrait;
/**
* @param string|\Closure|\Tobscure\JsonApi\SerializerInterface $serializer
* @param string|\Closure $relation
* @param User $actor
* @param Container $container
*/
public function __construct($serializer, $relation, User $actor, Container $container)
{
parent::__construct($serializer);
$this->relation = $relation;
$this->container = $container;
$this->actor = $actor;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Relationship;
use Flarum\Core\User;
use Illuminate\Contracts\Container\Container;
use Tobscure\JsonApi\Relationship\AbstractHasOneBuilder;
class HasOneBuilder extends AbstractHasOneBuilder
{
use BuilderTrait;
/**
* @param string|\Closure|\Tobscure\JsonApi\SerializerInterface $serializer
* @param string|\Closure $relation
* @param User $actor
* @param Container $container
*/
public function __construct($serializer, $relation, User $actor, Container $container)
{
parent::__construct($serializer);
$this->relation = $relation;
$this->container = $container;
$this->actor = $actor;
}
}

View File

@@ -1,63 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api;
use Flarum\Core\Users\Guest;
use Flarum\Core\Users\User;
use Psr\Http\Message\ServerRequestInterface;
class Request
{
/**
* @var array
*/
public $input;
/**
* @var User
*/
public $actor;
/**
* @var ServerRequestInterface
*/
public $http;
/**
* @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 ?: 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;
}
}

View File

@@ -1,31 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api;
use Psr\Http\Message\ResponseInterface;
class Response
{
public function __construct(ResponseInterface $response)
{
$this->response = $response;
}
public function getBody()
{
return json_decode($this->response->getBody());
}
public function getStatusCode()
{
return $this->response->getStatusCode();
}
}

View File

@@ -0,0 +1,203 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializer;
use DateTime;
use Closure;
use Flarum\Core\User;
use Flarum\Event\PrepareApiAttributes;
use Flarum\Event\GetApiRelationship;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Events\Dispatcher;
use LogicException;
use Tobscure\JsonApi\AbstractSerializer as BaseAbstractSerializer;
use Flarum\Api\Relationship\HasOneBuilder;
use Flarum\Api\Relationship\HasManyBuilder;
use Tobscure\JsonApi\Relationship\BuilderInterface;
abstract class AbstractSerializer extends BaseAbstractSerializer
{
/**
* @var User
*/
protected $actor;
/**
* @var Dispatcher
*/
protected static $dispatcher;
/**
* @var Container
*/
protected static $container;
/**
* @return User
*/
public function getActor()
{
return $this->actor;
}
/**
* @param User $actor
*/
public function setActor(User $actor)
{
$this->actor = $actor;
}
/**
* {@inheritdoc}
*/
public function getAttributes($model, array $fields = null)
{
if (! is_object($model) && ! is_array($model)) {
return [];
}
$attributes = $this->getDefaultAttributes($model);
static::$dispatcher->fire(
new PrepareApiAttributes($this, $model, $attributes)
);
return $attributes;
}
/**
* Get the default set of serialized attributes for a model.
*
* @param object|array $model
* @return array
*/
abstract protected function getDefaultAttributes($model);
/**
* @param DateTime|null $date
* @return string|null
*/
protected function formatDate(DateTime $date = null)
{
if ($date) {
return $date->format(DateTime::RFC3339);
}
}
/**
* {@inheritdoc}
*/
public function getRelationshipBuilder($name)
{
if ($relationship = $this->getCustomRelationship($name)) {
return $relationship;
}
return parent::getRelationshipBuilder($name);
}
/**
* Get a custom relationship.
*
* @param string $name
* @return BuilderInterface|null
*/
protected function getCustomRelationship($name)
{
$builder = static::$dispatcher->until(
new GetApiRelationship($this, $name)
);
if ($builder && ! ($builder instanceof BuilderInterface)) {
throw new LogicException('GetApiRelationship handler must return an instance of '
. BuilderInterface::class);
}
return $builder;
}
/**
* Get a relationship builder for a has-one relationship.
*
* @param string|Closure|\Tobscure\JsonApi\SerializerInterface $serializer
* @param string|Closure|null $relation
* @return HasOneBuilder
*/
public function hasOne($serializer, $relation = null)
{
if (is_null($relation)) {
$relation = $this->getRelationCaller();
}
return new HasOneBuilder($serializer, $relation, $this->actor, static::$container);
}
/**
* Get a relationship builder for a has-many relationship.
*
* @param string|Closure|\Tobscure\JsonApi\SerializerInterface $serializer
* @param string|Closure|null $relation
* @return HasManyBuilder
*/
public function hasMany($serializer, $relation = null)
{
if (is_null($relation)) {
$relation = $this->getRelationCaller();
}
return new HasManyBuilder($serializer, $relation, $this->actor, static::$container);
}
/**
* Guess the name of a relation from the stack trace.
*
* @return string
*/
protected function getRelationCaller()
{
list(, , $caller) = debug_backtrace(false, 3);
return $caller['function'];
}
/**
* @return Dispatcher
*/
public static function getEventDispatcher()
{
return static::$dispatcher;
}
/**
* @param Dispatcher $dispatcher
*/
public static function setEventDispatcher(Dispatcher $dispatcher)
{
static::$dispatcher = $dispatcher;
}
/**
* @return Container
*/
public static function getContainer()
{
return static::$container;
}
/**
* @param Container $container
*/
public static function setContainer(Container $container)
{
static::$container = $container;
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializer;
class CurrentUserSerializer extends UserSerializer
{
/**
* {@inheritdoc}
*/
protected function getDefaultAttributes($user)
{
$attributes = parent::getDefaultAttributes($user);
$attributes += [
'readTime' => $this->formatDate($user->read_time),
'unreadNotificationsCount' => (int) $user->getUnreadNotificationsCount(),
'newNotificationsCount' => (int) $user->getNewNotificationsCount(),
'preferences' => (array) $user->preferences
];
return $attributes;
}
}

View File

@@ -0,0 +1,88 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializer;
use Flarum\Core\Discussion;
use InvalidArgumentException;
class DiscussionBasicSerializer extends AbstractSerializer
{
/**
* {@inheritdoc}
*/
protected $type = 'discussions';
/**
* {@inheritdoc}
*
* @param Discussion $discussion
* @throws InvalidArgumentException
*/
protected function getDefaultAttributes($discussion)
{
if (! ($discussion instanceof Discussion)) {
throw new InvalidArgumentException(get_class($this)
. ' can only serialize instances of ' . Discussion::class);
}
return [
'title' => $discussion->title
];
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function startUser()
{
return $this->hasOne('Flarum\Api\Serializer\UserBasicSerializer');
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function startPost()
{
return $this->hasOne('Flarum\Api\Serializer\PostBasicSerializer');
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function lastUser()
{
return $this->hasOne('Flarum\Api\Serializer\UserBasicSerializer');
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function lastPost()
{
return $this->hasOne('Flarum\Api\Serializer\PostBasicSerializer');
}
/**
* @return \Flarum\Api\Relationship\HasManyBuilder
*/
protected function posts()
{
return $this->hasMany('Flarum\Api\Serializer\PostSerializer');
}
/**
* @return \Flarum\Api\Relationship\HasManyBuilder
*/
protected function relevantPosts()
{
return $this->hasMany('Flarum\Api\Serializer\PostBasicSerializer');
}
}

View File

@@ -0,0 +1,74 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializer;
use Flarum\Core\Discussion;
use Flarum\Core\Access\Gate;
class DiscussionSerializer extends DiscussionBasicSerializer
{
/**
* @var Gate
*/
protected $gate;
/**
* @param \Flarum\Core\Access\Gate $gate
*/
public function __construct(Gate $gate)
{
$this->gate = $gate;
}
/**
* {@inheritdoc}
*/
protected function getDefaultAttributes($discussion)
{
$gate = $this->gate->forUser($this->actor);
$attributes = parent::getDefaultAttributes($discussion) + [
'commentsCount' => (int) $discussion->comments_count,
'participantsCount' => (int) $discussion->participants_count,
'startTime' => $this->formatDate($discussion->start_time),
'lastTime' => $this->formatDate($discussion->last_time),
'lastPostNumber' => (int) $discussion->last_post_number,
'canReply' => $gate->allows('reply', $discussion),
'canRename' => $gate->allows('rename', $discussion),
'canDelete' => $gate->allows('delete', $discussion),
'canHide' => $gate->allows('hide', $discussion)
];
if ($discussion->hide_time) {
$attributes['isHidden'] = true;
$attributes['hideTime'] = $this->formatDate($discussion->hide_time);
}
Discussion::setStateUser($this->actor);
if ($state = $discussion->state) {
$attributes += [
'readTime' => $this->formatDate($state->read_time),
'readNumber' => (int) $state->read_number
];
}
return $attributes;
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function hideUser()
{
return $this->hasOne('Flarum\Api\Serializer\UserSerializer');
}
}

View File

@@ -0,0 +1,98 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializer;
use Flarum\Foundation\Application;
use Flarum\Core\Access\Gate;
use Flarum\Settings\SettingsRepository;
class ForumSerializer extends AbstractSerializer
{
/**
* {@inheritdoc}
*/
protected $type = 'forums';
/**
* @var Gate
*/
protected $gate;
/**
* @var Application
*/
protected $app;
/**
* @var SettingsRepository
*/
protected $settings;
/**
* @param Gate $gate
* @param Application $app
* @param SettingsRepository $settings
*/
public function __construct(Gate $gate, Application $app, SettingsRepository $settings)
{
$this->gate = $gate;
$this->app = $app;
$this->settings = $settings;
}
/**
* {@inheritdoc}
*/
public function getId($model)
{
return 1;
}
/**
* {@inheritdoc}
*/
protected function getDefaultAttributes($model)
{
$gate = $this->gate->forUser($this->actor);
$attributes = [
'title' => $this->settings->get('forum_title'),
'description' => $this->settings->get('forum_description'),
'baseUrl' => $url = $this->app->url(),
'basePath' => parse_url($url, PHP_URL_PATH) ?: '',
'debug' => $this->app->inDebugMode(),
'apiUrl' => $this->app->url('api'),
'welcomeTitle' => $this->settings->get('welcome_title'),
'welcomeMessage' => $this->settings->get('welcome_message'),
'themePrimaryColor' => $this->settings->get('theme_primary_color'),
'allowSignUp' => (bool) $this->settings->get('allow_sign_up'),
'defaultRoute' => $this->settings->get('default_route'),
'canViewDiscussions' => $gate->allows('viewDiscussions'),
'canViewUserList' => $gate->allows('viewUserList'),
'canStartDiscussion' => $gate->allows('startDiscussion')
];
if ($gate->allows('administrate')) {
$attributes['adminUrl'] = $this->app->url('admin');
$attributes['version'] = $this->app->version();
}
return $attributes;
}
/**
* @return \Flarum\Api\Relationship\HasManyBuilder
*/
protected function groups()
{
return $this->hasMany('Flarum\Api\Serializer\GroupSerializer');
}
}

View File

@@ -8,9 +8,13 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializers;
namespace Flarum\Api\Serializer;
class GroupSerializer extends Serializer
use Flarum\Api\Serializer\AbstractSerializer;
use Flarum\Core\Group;
use InvalidArgumentException;
class GroupSerializer extends AbstractSerializer
{
/**
* {@inheritdoc}
@@ -19,9 +23,17 @@ class GroupSerializer extends Serializer
/**
* {@inheritdoc}
*
* @param Group $group
* @throws InvalidArgumentException
*/
protected function getDefaultAttributes($group)
{
if (! ($group instanceof Group)) {
throw new InvalidArgumentException(get_class($this)
. ' can only serialize instances of ' . Group::class);
}
return [
'nameSingular' => $group->name_singular,
'namePlural' => $group->name_plural,
@@ -31,7 +43,7 @@ class GroupSerializer extends Serializer
}
/**
* @return callable
* @return \Flarum\Api\Relationship\HasManyBuilder
*/
protected function permissions()
{

View File

@@ -8,9 +8,12 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializers;
namespace Flarum\Api\Serializer;
class NotificationSerializer extends Serializer
use Flarum\Core\Notification;
use InvalidArgumentException;
class NotificationSerializer extends AbstractSerializer
{
/**
* {@inheritdoc}
@@ -27,39 +30,46 @@ class NotificationSerializer extends Serializer
/**
* {@inheritdoc}
*
* @param \Flarum\Core\Notification $notification
* @throws InvalidArgumentException
*/
protected function getDefaultAttributes($notification)
{
if (! ($notification instanceof Notification)) {
throw new InvalidArgumentException(get_class($this)
. ' can only serialize instances of ' . Notification::class);
}
return [
'id' => (int) $notification->id,
'contentType' => $notification->type,
'content' => $notification->data,
'time' => $notification->time->toRFC3339String(),
'isRead' => (bool) $notification->is_read,
'unreadCount' => $notification->unread_count
'time' => $this->formatDate($notification->time),
'isRead' => (bool) $notification->is_read
];
}
/**
* @return callable
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
public function user()
protected function user()
{
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
return $this->hasOne('Flarum\Api\Serializer\UserBasicSerializer');
}
/**
* @return callable
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
public function sender()
protected function sender()
{
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
return $this->hasOne('Flarum\Api\Serializer\UserBasicSerializer');
}
/**
* @return callable
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
public function subject()
protected function subject()
{
return $this->hasOne(function ($notification) {
return static::$subjectSerializers[$notification->type];

View File

@@ -0,0 +1,68 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializer;
use Flarum\Core\Post\CommentPost;
use Flarum\Core\Post;
use InvalidArgumentException;
class PostBasicSerializer extends AbstractSerializer
{
/**
* {@inheritdoc}
*/
protected $type = 'posts';
/**
* {@inheritdoc}
*
* @param \Flarum\Core\Post $post
* @throws InvalidArgumentException
*/
protected function getDefaultAttributes($post)
{
if (! ($post instanceof Post)) {
throw new InvalidArgumentException(get_class($this)
. ' can only serialize instances of ' . Post::class);
}
$attributes = [
'id' => (int) $post->id,
'number' => (int) $post->number,
'time' => $this->formatDate($post->time),
'contentType' => $post->type
];
if ($post instanceof CommentPost) {
$attributes['contentHtml'] = $post->content_html;
} else {
$attributes['content'] = $post->content;
}
return $attributes;
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function user()
{
return $this->hasOne('Flarum\Api\Serializer\UserBasicSerializer');
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function discussion()
{
return $this->hasOne('Flarum\Api\Serializer\DiscussionBasicSerializer');
}
}

View File

@@ -0,0 +1,102 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializer;
use Flarum\Core\Access\Gate;
use Flarum\Core\Post\CommentPost;
class PostSerializer extends PostBasicSerializer
{
/**
* @var \Flarum\Core\Access\Gate
*/
protected $gate;
/**
* @param \Flarum\Core\Access\Gate $gate
*/
public function __construct(Gate $gate)
{
$this->gate = $gate;
}
/**
* {@inheritdoc}
*/
protected function getDefaultAttributes($post)
{
$attributes = parent::getDefaultAttributes($post);
unset($attributes['content']);
$gate = $this->gate->forUser($this->actor);
$canEdit = $gate->allows('edit', $post);
if ($post instanceof CommentPost) {
$attributes['contentHtml'] = $post->content_html;
if ($canEdit) {
$attributes['content'] = $post->content;
}
} else {
$attributes['content'] = $post->content;
}
if ($post->edit_time) {
$attributes['editTime'] = $this->formatDate($post->edit_time);
}
if ($post->hide_time) {
$attributes['isHidden'] = true;
$attributes['hideTime'] = $this->formatDate($post->hide_time);
}
$attributes += [
'canEdit' => $canEdit,
'canDelete' => $gate->allows('delete', $post)
];
return $attributes;
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function user()
{
return $this->hasOne('Flarum\Api\Serializer\UserSerializer');
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function discussion()
{
return $this->hasOne('Flarum\Api\Serializer\DiscussionSerializer');
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function editUser()
{
return $this->hasOne('Flarum\Api\Serializer\UserSerializer');
}
/**
* @return \Flarum\Api\Relationship\HasOneBuilder
*/
protected function hideUser()
{
return $this->hasOne('Flarum\Api\Serializer\UserSerializer');
}
}

View File

@@ -8,9 +8,12 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializers;
namespace Flarum\Api\Serializer;
class UserBasicSerializer extends Serializer
use Flarum\Core\User;
use InvalidArgumentException;
class UserBasicSerializer extends AbstractSerializer
{
/**
* {@inheritdoc}
@@ -19,9 +22,17 @@ class UserBasicSerializer extends Serializer
/**
* {@inheritdoc}
*
* @param User $user
* @throws InvalidArgumentException
*/
protected function getDefaultAttributes($user)
{
if (! ($user instanceof User)) {
throw new InvalidArgumentException(get_class($this)
. ' can only serialize instances of ' . User::class);
}
return [
'username' => $user->username,
'avatarUrl' => $user->avatar_url
@@ -29,10 +40,10 @@ class UserBasicSerializer extends Serializer
}
/**
* @return callable
* @return \Flarum\Api\Relationship\HasManyBuilder
*/
protected function groups()
{
return $this->hasMany('Flarum\Api\Serializers\GroupSerializer');
return $this->hasMany('Flarum\Api\Serializer\GroupSerializer');
}
}

View File

@@ -8,10 +8,25 @@
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializers;
namespace Flarum\Api\Serializer;
use Flarum\Core\Access\Gate;
class UserSerializer extends UserBasicSerializer
{
/**
* @var Gate
*/
protected $gate;
/**
* @param Gate $gate
*/
public function __construct(Gate $gate)
{
$this->gate = $gate;
}
/**
* {@inheritdoc}
*/
@@ -19,20 +34,22 @@ class UserSerializer extends UserBasicSerializer
{
$attributes = parent::getDefaultAttributes($user);
$canEdit = $user->can($this->actor, 'edit');
$gate = $this->gate->forUser($this->actor);
$canEdit = $gate->allows('edit', $user);
$attributes += [
'bio' => $user->bio,
'joinTime' => $user->join_time ? $user->join_time->toRFC3339String() : null,
'joinTime' => $this->formatDate($user->join_time),
'discussionsCount' => (int) $user->discussions_count,
'commentsCount' => (int) $user->comments_count,
'canEdit' => $canEdit,
'canDelete' => $user->can($this->actor, 'delete'),
'canDelete' => $gate->allows('delete', $user),
];
if ($user->getPreference('discloseOnline')) {
$attributes += [
'lastSeenTime' => $user->last_seen_time ? $user->last_seen_time->toRFC3339String() : null
'lastSeenTime' => $this->formatDate($user->last_seen_time)
];
}

View File

@@ -1,73 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializers;
class ActivitySerializer extends Serializer
{
/**
* @inheritdoc
*/
protected $type = 'activity';
/**
* A map of activity types (key) to the serializer that should be used to
* output the activity's subject (value).
*
* @var array
*/
protected static $subjectSerializers = [];
/**
* {@inheritdoc}
*/
protected function getDefaultAttributes($activity)
{
return [
'contentType' => $activity->type,
'time' => $activity->time->toRFC3339String()
];
}
/**
* @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::$subjectSerializers[$activity->type];
});
}
/**
* @param $type
* @param $serializer
*/
public static function setSubjectSerializer($type, $serializer)
{
static::$subjectSerializers[$type] = $serializer;
}
}

View File

@@ -1,33 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializers;
class CurrentUserSerializer extends UserSerializer
{
/**
* {@inheritdoc}
*/
protected function getDefaultAttributes($user)
{
$attributes = parent::getDefaultAttributes($user);
if ($user->id == $this->actor->id) {
$attributes += [
'readTime' => $user->read_time ? $user->read_time->toRFC3339String() : null,
'unreadNotificationsCount' => $user->getUnreadNotificationsCount(),
'newNotificationsCount' => $user->getNewNotificationsCount(),
'preferences' => $user->preferences
];
}
return $attributes;
}
}

View File

@@ -1,77 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializers;
class DiscussionBasicSerializer extends Serializer
{
/**
* {@inheritdoc}
*/
protected $type = 'discussions';
/**
* {@inheritdoc}
*/
protected function getDefaultAttributes($discussion)
{
return [
'title' => $discussion->title
];
}
/**
* @return callable
*/
protected function startUser()
{
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
}
/**
* @return callable
*/
protected function startPost()
{
return $this->hasOne('Flarum\Api\Serializers\PostBasicSerializer');
}
/**
* @return callable
*/
protected function lastUser()
{
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
}
/**
* @return callable
*/
protected function lastPost()
{
return $this->hasOne('Flarum\Api\Serializers\PostBasicSerializer');
}
/**
* @return callable
*/
protected function posts()
{
return $this->hasMany('Flarum\Api\Serializers\PostSerializer');
}
/**
* @return callable
*/
protected function relevantPosts()
{
return $this->hasMany('Flarum\Api\Serializers\PostBasicSerializer');
}
}

View File

@@ -1,58 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializers;
use Flarum\Core\Discussions\Discussion;
class DiscussionSerializer extends DiscussionBasicSerializer
{
/**
* {@inheritdoc}
*/
protected function getDefaultAttributes($discussion)
{
$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($this->actor, 'reply'),
'canRename' => $discussion->can($this->actor, 'rename'),
'canDelete' => $discussion->can($this->actor, 'delete'),
'canHide' => $discussion->can($this->actor, 'hide')
];
if ($discussion->hide_time) {
$attributes['isHidden'] = true;
$attributes['hideTime'] = $discussion->hide_time->toRFC3339String();
}
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;
}
/**
* @return callable
*/
public function hideUser()
{
return $this->hasOne('Flarum\Api\Serializers\UserSerializer');
}
}

View File

@@ -1,67 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Flarum\Api\Serializers;
use Flarum\Core;
use Flarum\Core\Application;
class ForumSerializer extends Serializer
{
/**
* {@inheritdoc}
*/
protected $type = 'forums';
/**
* {@inheritdoc}
*/
protected function getId($forum)
{
return 1;
}
/**
* {@inheritdoc}
*/
protected function getDefaultAttributes($forum)
{
$attributes = [
'title' => Core::config('forum_title'),
'description' => Core::config('forum_description'),
'baseUrl' => Core::url(),
'basePath' => parse_url(Core::url(), PHP_URL_PATH) ?: '',
'debug' => Core::inDebugMode(),
'apiUrl' => Core::url('api'),
'welcomeTitle' => Core::config('welcome_title'),
'welcomeMessage' => Core::config('welcome_message'),
'themePrimaryColor' => Core::config('theme_primary_color'),
'canView' => $forum->can($this->actor, 'view'),
'canStartDiscussion' => $forum->can($this->actor, 'startDiscussion'),
'allowSignUp' => (bool) Core::config('allow_sign_up'),
'defaultRoute' => Core::config('default_route')
];
if ($this->actor->isAdmin()) {
$attributes['adminUrl'] = Core::url('admin');
$attributes['version'] = Application::VERSION;
}
return $attributes;
}
/**
* @return callable
*/
protected function groups()
{
return $this->hasMany('Flarum\Api\Serializers\GroupSerializer');
}
}

Some files were not shown because too many files have changed in this diff Show More