mirror of
https://github.com/flarum/core.git
synced 2025-08-11 02:44:04 +02:00
Hello world!
This commit is contained in:
215
src/Flarum/Api/Actions/Base.php
Normal file
215
src/Flarum/Api/Actions/Base.php
Normal file
@@ -0,0 +1,215 @@
|
||||
<?php namespace Flarum\Api\Actions;
|
||||
|
||||
use Illuminate\Routing\Controller;
|
||||
use Tobscure\JsonApi\Document;
|
||||
use Laracasts\Commander\CommandBus;
|
||||
use Response;
|
||||
use Event;
|
||||
use App;
|
||||
use Config;
|
||||
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Flarum\Core\Support\Exceptions\ValidationFailureException;
|
||||
|
||||
abstract class Base extends Controller
|
||||
{
|
||||
protected $request;
|
||||
|
||||
protected $document;
|
||||
|
||||
protected $commandBus;
|
||||
|
||||
public function __construct(CommandBus $commandBus)
|
||||
{
|
||||
$this->commandBus = $commandBus;
|
||||
}
|
||||
|
||||
abstract protected function run();
|
||||
|
||||
public function handle($request, $parameters)
|
||||
{
|
||||
$this->registerErrorHandlers();
|
||||
|
||||
$this->request = $request;
|
||||
$this->parameters = $parameters;
|
||||
|
||||
$this->document = new Document;
|
||||
$this->document->addMeta('profile', '?');
|
||||
|
||||
return $this->run();
|
||||
}
|
||||
|
||||
public function param($key, $default = null)
|
||||
{
|
||||
return array_get($this->parameters, $key, $default);
|
||||
}
|
||||
|
||||
public function input($key, $default = null)
|
||||
{
|
||||
return $this->request->input($key, $default);
|
||||
}
|
||||
|
||||
public function fillCommandWithInput($command, $inputKey = null)
|
||||
{
|
||||
$input = $inputKey ? $this->input($inputKey) : $this->request->input->all();
|
||||
|
||||
foreach ($input as $k => $v) {
|
||||
$command->$k = $v;
|
||||
}
|
||||
}
|
||||
|
||||
protected function inputRange($key, $default = null, $min = null, $max = null)
|
||||
{
|
||||
$value = (int) $this->input($key, $default);
|
||||
|
||||
if (! is_null($min)) {
|
||||
$value = max($value, $min);
|
||||
}
|
||||
if (! is_null($max)) {
|
||||
$value = min($value, $max);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function included($available)
|
||||
{
|
||||
$requested = explode(',', $this->input('include'));
|
||||
return array_intersect($available, $requested);
|
||||
}
|
||||
|
||||
protected function explodeIds($ids)
|
||||
{
|
||||
return array_unique(array_map('intval', array_filter(explode(',', $ids))));
|
||||
}
|
||||
|
||||
protected function inputIn($key, $options)
|
||||
{
|
||||
$value = $this->input($key);
|
||||
|
||||
if (array_key_exists($key, $options)) {
|
||||
return $options[$key];
|
||||
}
|
||||
if (! in_array($value, $options)) {
|
||||
$value = reset($options);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function sort($options)
|
||||
{
|
||||
$criteria = (string) $this->input('sort', '');
|
||||
$order = $criteria ? 'asc' : null;
|
||||
|
||||
if ($criteria && $criteria[0] == '-') {
|
||||
$order = 'desc';
|
||||
$criteria = substr($criteria, 1);
|
||||
}
|
||||
|
||||
if (! in_array($criteria, $options)) {
|
||||
$criteria = reset($options);
|
||||
}
|
||||
|
||||
return [
|
||||
'by' => $criteria,
|
||||
'order' => $order,
|
||||
'string' => ($order == 'desc' ? '-' : '').$criteria
|
||||
];
|
||||
}
|
||||
|
||||
protected function start()
|
||||
{
|
||||
return $this->inputRange('start', 0, 0);
|
||||
}
|
||||
|
||||
protected function count($default, $max = 100)
|
||||
{
|
||||
return $this->inputRange('count', $default, 1, $max);
|
||||
}
|
||||
|
||||
protected function buildUrl($route, $params = [], $input = [])
|
||||
{
|
||||
$url = route('flarum.api.'.$route, $params);
|
||||
$queryString = $input ? '?'.http_build_query($input) : '';
|
||||
|
||||
return $url.$queryString;
|
||||
}
|
||||
|
||||
protected function respondWithoutContent($statusCode = 204, $headers = [])
|
||||
{
|
||||
return Response::make('', $statusCode, $headers);
|
||||
}
|
||||
|
||||
protected function respondWithArray($array, $statusCode = 200, $headers = [])
|
||||
{
|
||||
// @todo remove this
|
||||
$headers['Access-Control-Allow-Origin'] = 'http://0.0.0.0:4200';
|
||||
|
||||
return Response::json($array, $statusCode, $headers);
|
||||
}
|
||||
|
||||
protected function respondWithDocument($statusCode = 200, $headers = [])
|
||||
{
|
||||
// @todo remove this
|
||||
$this->document->addMeta('pageload', microtime(true) - LARAVEL_START);
|
||||
|
||||
Event::fire('flarum.api.willRespondWithDocument', [$this->document]);
|
||||
|
||||
$headers['Content-Type'] = 'application/vnd.api+json';
|
||||
|
||||
return $this->respondWithArray($this->document->toArray(), $statusCode, $headers);
|
||||
}
|
||||
|
||||
// @todo fix this
|
||||
protected function call($name, $params, $method, $input)
|
||||
{
|
||||
Input::replace($input);
|
||||
|
||||
$url = URL::action('\\Flarum\\Api\\Controllers\\'.$name, $params, false);
|
||||
$request = Request::create($url, $method);
|
||||
$json = Route::dispatch($request)->getContent();
|
||||
|
||||
return json_decode($json, true);
|
||||
}
|
||||
|
||||
protected function registerErrorHandlers()
|
||||
{
|
||||
if (! Config::get('app.debug')) {
|
||||
App::error(function ($exception, $code) {
|
||||
return $this->respondWithError('ApplicationError', $code);
|
||||
});
|
||||
}
|
||||
|
||||
App::error(function (ModelNotFoundException $exception) {
|
||||
return $this->respondWithError('ResourceNotFound', 404);
|
||||
});
|
||||
|
||||
App::error(function (ValidationFailureException $exception) {
|
||||
$errors = [];
|
||||
foreach ($exception->getErrors()->getMessages() as $field => $messages) {
|
||||
$errors[] = [
|
||||
'code' => 'ValidationFailure',
|
||||
'detail' => implode("\n", $messages),
|
||||
'path' => $field
|
||||
];
|
||||
}
|
||||
return $this->respondWithErrors($errors, 422);
|
||||
});
|
||||
}
|
||||
|
||||
protected function respondWithErrors($errors, $httpCode = 500)
|
||||
{
|
||||
return Response::json(['errors' => $errors], $httpCode);
|
||||
}
|
||||
|
||||
protected function respondWithError($error, $httpCode = 500, $detail = null)
|
||||
{
|
||||
$error = ['code' => $error];
|
||||
|
||||
if ($detail) {
|
||||
$error['detail'] = $detail;
|
||||
}
|
||||
|
||||
return $this->respondWithErrors([$error], $httpCode);
|
||||
}
|
||||
}
|
35
src/Flarum/Api/Actions/Discussions/Create.php
Normal file
35
src/Flarum/Api/Actions/Discussions/Create.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php namespace Flarum\Api\Actions\Discussions;
|
||||
|
||||
use Event;
|
||||
|
||||
use Flarum\Core\Discussions\Commands\StartDiscussionCommand;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\DiscussionSerializer;
|
||||
|
||||
class Create extends Base
|
||||
{
|
||||
/**
|
||||
* Start a new discussion.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
// By default, the only required attributes of a discussion are the
|
||||
// title and the content. We'll extract these from the request data
|
||||
// and pass them through to the StartDiscussionCommand.
|
||||
$title = $this->input('discussions.title');
|
||||
$content = $this->input('discussions.content');
|
||||
$command = new StartDiscussionCommand($title, $content, User::current());
|
||||
|
||||
Event::fire('Flarum.Api.Actions.Discussions.Create.WillExecuteCommand', [$command, $this->document]);
|
||||
|
||||
$discussion = $this->commandBus->execute($command);
|
||||
|
||||
$serializer = new DiscussionSerializer(['posts']);
|
||||
$this->document->setPrimaryElement($serializer->resource($discussion));
|
||||
|
||||
return $this->respondWithDocument();
|
||||
}
|
||||
}
|
26
src/Flarum/Api/Actions/Discussions/Delete.php
Normal file
26
src/Flarum/Api/Actions/Discussions/Delete.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php namespace Flarum\Api\Actions\Discussions;
|
||||
|
||||
use Event;
|
||||
use Flarum\Core\Discussions\Commands\DeleteDiscussionCommand;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Api\Actions\Base;
|
||||
|
||||
class Delete extends Base
|
||||
{
|
||||
/**
|
||||
* Delete a discussion.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
$discussionId = $this->param('id');
|
||||
$command = new DeleteDiscussionCommand($discussionId, User::current());
|
||||
|
||||
Event::fire('Flarum.Api.Actions.Discussions.Delete.WillExecuteCommand', [$command]);
|
||||
|
||||
$this->commandBus->execute($command);
|
||||
|
||||
return $this->respondWithoutContent();
|
||||
}
|
||||
}
|
84
src/Flarum/Api/Actions/Discussions/Index.php
Normal file
84
src/Flarum/Api/Actions/Discussions/Index.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php namespace Flarum\Api\Actions\Discussions;
|
||||
|
||||
use Flarum\Core\Discussions\Discussion;
|
||||
use Flarum\Core\Discussions\DiscussionFinder;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\DiscussionSerializer;
|
||||
|
||||
class Index extends Base
|
||||
{
|
||||
/**
|
||||
* The discussion finder.
|
||||
*
|
||||
* @var DiscussionFinder
|
||||
*/
|
||||
protected $finder;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param DiscussionFinder $finder
|
||||
*/
|
||||
public function __construct(DiscussionFinder $finder)
|
||||
{
|
||||
$this->finder = $finder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a list of discussions.
|
||||
*
|
||||
* @todo custom rate limit for this function? determined by if $key was valid?
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
$query = $this->input('q');
|
||||
$key = $this->input('key');
|
||||
$start = $this->start();
|
||||
$include = $this->included(['startPost', 'lastPost', 'relevantPosts']);
|
||||
$count = $this->count($include ? 20 : 50, 50);
|
||||
$sort = $this->sort(['', 'lastPost', 'replies', 'created']);
|
||||
|
||||
$relations = array_merge(['startUser', 'lastUser'], $include);
|
||||
|
||||
// Set up the discussion finder with our search criteria, and get the
|
||||
// requested range of results with the necessary relations loaded.
|
||||
$this->finder->setUser(User::current());
|
||||
$this->finder->setQuery($query);
|
||||
$this->finder->setSort($sort['by']);
|
||||
$this->finder->setOrder($sort['order']);
|
||||
$this->finder->setKey($key);
|
||||
|
||||
$discussions = $this->finder->results($count, $start, array_merge($relations, ['state']));
|
||||
|
||||
if (($total = $this->finder->getCount()) !== null) {
|
||||
$this->document->addMeta('total', $total);
|
||||
}
|
||||
if (($key = $this->finder->getKey()) !== null) {
|
||||
$this->document->addMeta('key', $key);
|
||||
}
|
||||
|
||||
// If there are more results, then we need to construct a URL to the
|
||||
// next results page and add that to the metadata. We do this by
|
||||
// compacting all of the valid query parameters which have been
|
||||
// specified.
|
||||
if ($this->finder->areMoreResults()) {
|
||||
$start += $count;
|
||||
$include = implode(',', $include);
|
||||
$sort = $sort['string'];
|
||||
$input = array_filter(compact('query', 'key', 'sort', 'start', 'count', 'include'));
|
||||
$moreUrl = $this->buildUrl('discussions.index', [], $input);
|
||||
} else {
|
||||
$moreUrl = '';
|
||||
}
|
||||
$this->document->addMeta('moreUrl', $moreUrl);
|
||||
|
||||
// Finally, we can set up the discussion serializer and use it to create
|
||||
// a collection of discussion results.
|
||||
$serializer = new DiscussionSerializer($relations);
|
||||
$this->document->setPrimaryElement($serializer->collection($discussions));
|
||||
|
||||
return $this->respondWithDocument();
|
||||
}
|
||||
}
|
29
src/Flarum/Api/Actions/Discussions/Show.php
Normal file
29
src/Flarum/Api/Actions/Discussions/Show.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php namespace Flarum\Api\Actions\Discussions;
|
||||
|
||||
use Flarum\Core\Discussions\Discussion;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\DiscussionSerializer;
|
||||
|
||||
class Show extends Base
|
||||
{
|
||||
/**
|
||||
* Show a single discussion.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
$include = $this->included(['startPost', 'lastPost']);
|
||||
|
||||
$discussion = Discussion::whereCanView()->findOrFail($this->param('id'));
|
||||
|
||||
// Set up the discussion serializer, which we will use to create the
|
||||
// document's primary resource. As well as including the requested
|
||||
// relations, we will specify that we want the 'posts' relation to be
|
||||
// linked so that a list of post IDs will show up in the response.
|
||||
$serializer = new DiscussionSerializer($include, ['posts']);
|
||||
$this->document->setPrimaryElement($serializer->resource($discussion));
|
||||
|
||||
return $this->respondWithDocument();
|
||||
}
|
||||
}
|
51
src/Flarum/Api/Actions/Discussions/Update.php
Normal file
51
src/Flarum/Api/Actions/Discussions/Update.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php namespace Flarum\Api\Actions\Discussions;
|
||||
|
||||
use Event;
|
||||
|
||||
use Flarum\Core\Discussions\Commands\EditDiscussionCommand;
|
||||
use Flarum\Core\Discussions\Commands\ReadDiscussionCommand;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\DiscussionSerializer;
|
||||
|
||||
class Update extends Base
|
||||
{
|
||||
/**
|
||||
* Edit a discussion. Allows renaming the discussion, and updating its read
|
||||
* state with regards to the current user.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
$discussionId = $this->param('id');
|
||||
$readNumber = $this->input('discussions.readNumber');
|
||||
|
||||
// First, we will run the EditDiscussionCommand. This will update the
|
||||
// discussion's direct properties; by default, this is just the title.
|
||||
// As usual, however, we will fire an event to allow plugins to update
|
||||
// additional properties.
|
||||
$command = new EditDiscussionCommand($discussionId, User::current());
|
||||
$this->fillCommandWithInput($command, 'discussions');
|
||||
|
||||
Event::fire('Flarum.Api.Actions.Discussions.Update.WillExecuteCommand', [$command]);
|
||||
|
||||
$discussion = $this->commandBus->execute($command);
|
||||
|
||||
// Next, if a read number was specified in the request, we will run the
|
||||
// ReadDiscussionCommand. We won't bother firing an event for this one,
|
||||
// because it's pretty specific. (This may need to change in the future.)
|
||||
if ($readNumber) {
|
||||
$command = new ReadDiscussionCommand($discussionId, User::current(), $readNumber);
|
||||
$discussion = $this->commandBus->execute($command);
|
||||
}
|
||||
|
||||
// Presumably, the discussion was updated successfully. (One of the command
|
||||
// handlers would have thrown an exception if not.) We set this
|
||||
// discussion as our document's primary element.
|
||||
$serializer = new DiscussionSerializer;
|
||||
$this->document->setPrimaryElement($serializer->resource($discussion));
|
||||
|
||||
return $this->respondWithDocument();
|
||||
}
|
||||
}
|
18
src/Flarum/Api/Actions/Groups/Index.php
Normal file
18
src/Flarum/Api/Actions/Groups/Index.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php namespace Flarum\Api\Actions\Groups;
|
||||
|
||||
use Flarum\Core\Models\Group;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\GroupSerializer;
|
||||
|
||||
class Index extends Base
|
||||
{
|
||||
protected function run()
|
||||
{
|
||||
$groups = Group::get();
|
||||
|
||||
$serializer = new GroupSerializer;
|
||||
$this->document->setPrimaryElement($serializer->collection($groups));
|
||||
|
||||
return $this->respondWithDocument();
|
||||
}
|
||||
}
|
39
src/Flarum/Api/Actions/Posts/Create.php
Normal file
39
src/Flarum/Api/Actions/Posts/Create.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php namespace Flarum\Api\Actions\Posts;
|
||||
|
||||
use Event;
|
||||
use Flarum\Core\Posts\Commands\PostReplyCommand;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\PostSerializer;
|
||||
|
||||
class Create extends Base
|
||||
{
|
||||
/**
|
||||
* Reply to a discussion.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
// We've received a request to post a reply. By default, the only
|
||||
// required attributes of a post is the ID of the discussion to post in,
|
||||
// the post content, and the author's user account. Let's set up a
|
||||
// command with this information. We also fire an event to allow plugins
|
||||
// to add data to the command.
|
||||
$discussionId = $this->input('posts.links.discussions');
|
||||
$content = $this->input('posts.content');
|
||||
$command = new PostReplyCommand($discussionId, $content, User::current());
|
||||
|
||||
Event::fire('Flarum.Api.Actions.Posts.Create.WillExecuteCommand', [$command]);
|
||||
|
||||
$post = $this->commandBus->execute($command);
|
||||
|
||||
// Presumably, the post was created successfully. (The command handler
|
||||
// would have thrown an exception if not.) We set this post as our
|
||||
// document's primary element.
|
||||
$serializer = new PostSerializer;
|
||||
$this->document->setPrimaryElement($serializer->resource($post));
|
||||
|
||||
return $this->respondWithDocument(201);
|
||||
}
|
||||
}
|
26
src/Flarum/Api/Actions/Posts/Delete.php
Normal file
26
src/Flarum/Api/Actions/Posts/Delete.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php namespace Flarum\Api\Actions\Posts;
|
||||
|
||||
use Event;
|
||||
use Flarum\Core\Posts\Commands\DeletePostCommand;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Api\Actions\Base;
|
||||
|
||||
class Delete extends Base
|
||||
{
|
||||
/**
|
||||
* Delete a post.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
$postId = $this->param('id');
|
||||
$command = new DeletePostCommand($postId, User::current());
|
||||
|
||||
Event::fire('Flarum.Api.Actions.Posts.Delete.WillExecuteCommand', [$command]);
|
||||
|
||||
$this->commandBus->execute($command);
|
||||
|
||||
return $this->respondWithoutContent();
|
||||
}
|
||||
}
|
62
src/Flarum/Api/Actions/Posts/Index.php
Normal file
62
src/Flarum/Api/Actions/Posts/Index.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php namespace Flarum\Api\Actions\Posts;
|
||||
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\PostSerializer;
|
||||
|
||||
class Index extends Base
|
||||
{
|
||||
/**
|
||||
* Show posts from a discussion.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
$discussionId = $this->input('discussions');
|
||||
|
||||
$count = $this->count(20, 50);
|
||||
|
||||
if ($near = $this->input('near')) {
|
||||
// fetch the nearest post
|
||||
$post = Post::orderByRaw('ABS(CAST(number AS SIGNED) - ?)', [$near])->whereNotNull('number')->where('discussion_id', $discussionId)->take(1)->first();
|
||||
|
||||
$start = max(
|
||||
0,
|
||||
Post::whereCanView()
|
||||
->where('discussion_id', $discussionId)
|
||||
->where('time', '<=', $post->time)
|
||||
->count() - round($count / 2)
|
||||
);
|
||||
} else {
|
||||
$start = $this->start();
|
||||
}
|
||||
|
||||
$include = $this->included([]);
|
||||
$sort = $this->sort(['time']);
|
||||
|
||||
$relations = array_merge(['user', 'user.groups', 'editUser', 'deleteUser'], $include);
|
||||
|
||||
// @todo move to post repository
|
||||
$posts = Post::with($relations)
|
||||
->whereCanView()
|
||||
->where('discussion_id', $discussionId)
|
||||
->skip($start)
|
||||
->take($count)
|
||||
->orderBy($sort['by'], $sort['order'] ?: 'asc')
|
||||
->get();
|
||||
|
||||
if (! count($posts)) {
|
||||
throw new ModelNotFoundException;
|
||||
}
|
||||
|
||||
// Finally, we can set up the post serializer and use it to create
|
||||
// a post resource or collection, depending on how many posts were
|
||||
// requested.
|
||||
$serializer = new PostSerializer($relations);
|
||||
$this->document->setPrimaryElement($serializer->collection($posts));
|
||||
|
||||
return $this->respondWithDocument();
|
||||
}
|
||||
}
|
39
src/Flarum/Api/Actions/Posts/Show.php
Normal file
39
src/Flarum/Api/Actions/Posts/Show.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php namespace Flarum\Api\Actions\Posts;
|
||||
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\PostSerializer;
|
||||
|
||||
class Show extends Base
|
||||
{
|
||||
/**
|
||||
* Show a single or multiple posts by ID.
|
||||
* @todo put a cap on how many can be requested
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
$ids = $this->explodeIds($this->param('id'));
|
||||
$posts = Post::whereCanView()->whereIn('id', $ids)->get();
|
||||
|
||||
if (! count($posts)) {
|
||||
throw new ModelNotFoundException;
|
||||
}
|
||||
|
||||
$include = $this->included(['discussion', 'replyTo']);
|
||||
$relations = array_merge(['user', 'editUser', 'deleteUser'], $include);
|
||||
$posts->load($relations);
|
||||
|
||||
// Finally, we can set up the post serializer and use it to create
|
||||
// a post resource or collection, depending on how many posts were
|
||||
// requested.
|
||||
$serializer = new PostSerializer($relations);
|
||||
$this->document->setPrimaryElement(
|
||||
count($ids) == 1 ? $serializer->resource($posts->first()) : $serializer->collection($posts)
|
||||
);
|
||||
|
||||
return $this->respondWithDocument();
|
||||
}
|
||||
}
|
39
src/Flarum/Api/Actions/Posts/Update.php
Normal file
39
src/Flarum/Api/Actions/Posts/Update.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php namespace Flarum\Api\Actions\Posts;
|
||||
|
||||
use Event;
|
||||
|
||||
use Flarum\Core\Posts\Commands\EditPostCommand;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\PostSerializer;
|
||||
|
||||
class Update extends Base
|
||||
{
|
||||
/**
|
||||
* Edit a post. Allows revision of content, and hiding/unhiding.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
$postId = $this->param('id');
|
||||
|
||||
// EditPost is a single command because we don't want to allow partial
|
||||
// updates (i.e. if we were to run one command and then another, if the
|
||||
// second one failed, the first one would still have succeeded.)
|
||||
$command = new EditPostCommand($postId, User::current());
|
||||
$this->fillCommandWithInput($command, 'posts');
|
||||
|
||||
Event::fire('Flarum.Api.Actions.Posts.Update.WillExecuteCommand', [$command]);
|
||||
|
||||
$post = $this->commandBus->execute($command);
|
||||
|
||||
// Presumably, the post was updated successfully. (The command handler
|
||||
// would have thrown an exception if not.) We set this post as our
|
||||
// document's primary element.
|
||||
$serializer = new PostSerializer;
|
||||
$this->document->setPrimaryElement($serializer->resource($post));
|
||||
|
||||
return $this->respondWithDocument();
|
||||
}
|
||||
}
|
39
src/Flarum/Api/Actions/Users/Create.php
Normal file
39
src/Flarum/Api/Actions/Users/Create.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php namespace Flarum\Api\Actions\Users;
|
||||
|
||||
use Event;
|
||||
use Flarum\Core\Users\Commands\RegisterUserCommand;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\UserSerializer;
|
||||
|
||||
class Create extends Base
|
||||
{
|
||||
/**
|
||||
* Register a user.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
// We've received a request to register a user. By default, the only
|
||||
// required attributes of a user is the username, email, and password.
|
||||
// Let's set up a command with this information. We also fire an event
|
||||
// to allow plugins to add data to the command.
|
||||
$username = $this->input('users.username');
|
||||
$email = $this->input('users.email');
|
||||
$password = $this->input('users.password');
|
||||
$command = new RegisterUserCommand($username, $email, $password, User::current());
|
||||
|
||||
Event::fire('Flarum.Api.Actions.Users.Create.WillExecuteCommand', [$command]);
|
||||
|
||||
$user = $this->commandBus->execute($command);
|
||||
|
||||
// Presumably, the user was created successfully. (The command handler
|
||||
// would have thrown an exception if not.) We set this post as our
|
||||
// document's primary element.
|
||||
$serializer = new UserSerializer;
|
||||
$this->document->setPrimaryElement($serializer->resource($user));
|
||||
|
||||
return $this->respondWithDocument(201);
|
||||
}
|
||||
}
|
26
src/Flarum/Api/Actions/Users/Delete.php
Normal file
26
src/Flarum/Api/Actions/Users/Delete.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php namespace Flarum\Api\Actions\Users;
|
||||
|
||||
use Event;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Core\Users\Commands\DeleteUserCommand;
|
||||
use Flarum\Api\Actions\Base;
|
||||
|
||||
class Delete extends Base
|
||||
{
|
||||
/**
|
||||
* Delete a user.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
$userId = $this->param('id');
|
||||
$command = new DeleteUserCommand($userId, User::current());
|
||||
|
||||
Event::fire('Flarum.Api.Actions.Users.Delete.WillExecuteCommand', [$command]);
|
||||
|
||||
$this->commandBus->execute($command);
|
||||
|
||||
return $this->respondWithoutContent();
|
||||
}
|
||||
}
|
83
src/Flarum/Api/Actions/Users/Index.php
Normal file
83
src/Flarum/Api/Actions/Users/Index.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php namespace Flarum\Api\Actions\Users;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Core\Users\UserFinder;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\UserSerializer;
|
||||
|
||||
class Index extends Base
|
||||
{
|
||||
/**
|
||||
* The user finder.
|
||||
*
|
||||
* @var UserFinder
|
||||
*/
|
||||
protected $finder;
|
||||
|
||||
/**
|
||||
* Instantiate the action.
|
||||
*
|
||||
* @param UserFinder $finder
|
||||
*/
|
||||
public function __construct(UserFinder $finder)
|
||||
{
|
||||
$this->finder = $finder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a list of users.
|
||||
*
|
||||
* @todo custom rate limit for this function? determined by if $key was valid?
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
$query = $this->input('q');
|
||||
$key = $this->input('key');
|
||||
$sort = $this->sort(['', 'username', 'posts', 'discussions', 'lastActive', 'created']);
|
||||
$start = $this->start();
|
||||
$count = $this->count(50, 100);
|
||||
$include = $this->included(['groups']);
|
||||
$relations = array_merge(['groups'], $include);
|
||||
|
||||
// Set up the user finder with our search criteria, and get the
|
||||
// requested range of results with the necessary relations loaded.
|
||||
$this->finder->setUser(User::current());
|
||||
$this->finder->setQuery($query);
|
||||
$this->finder->setSort($sort['by']);
|
||||
$this->finder->setOrder($sort['order']);
|
||||
$this->finder->setKey($key);
|
||||
|
||||
$users = $this->finder->results($count, $start);
|
||||
$users->load($relations);
|
||||
|
||||
if (($total = $this->finder->getCount()) !== null) {
|
||||
$this->document->addMeta('total', $total);
|
||||
}
|
||||
if (($key = $this->finder->getKey()) !== null) {
|
||||
$this->document->addMeta('key', $key);
|
||||
}
|
||||
|
||||
// If there are more results, then we need to construct a URL to the
|
||||
// next results page and add that to the metadata. We do this by
|
||||
// compacting all of the valid query parameters which have been
|
||||
// specified.
|
||||
if ($this->finder->areMoreResults()) {
|
||||
$start += $count;
|
||||
$include = implode(',', $include);
|
||||
$sort = $sort['string'];
|
||||
$input = array_filter(compact('query', 'key', 'sort', 'start', 'count', 'include'));
|
||||
$moreUrl = $this->buildUrl('users.index', [], $input);
|
||||
} else {
|
||||
$moreUrl = '';
|
||||
}
|
||||
$this->document->addMeta('moreUrl', $moreUrl);
|
||||
|
||||
// Finally, we can set up the user serializer and use it to create
|
||||
// a collection of user results.
|
||||
$serializer = new UserSerializer($relations);
|
||||
$this->document->setPrimaryElement($serializer->collection($users));
|
||||
|
||||
return $this->respondWithDocument();
|
||||
}
|
||||
}
|
26
src/Flarum/Api/Actions/Users/Show.php
Normal file
26
src/Flarum/Api/Actions/Users/Show.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php namespace Flarum\Api\Actions\Users;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\UserSerializer;
|
||||
|
||||
class Show extends Base
|
||||
{
|
||||
/**
|
||||
* Show a single user.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
$user = User::whereCanView()->findOrFail($this->param('id'));
|
||||
|
||||
// Set up the user serializer, which we will use to create the
|
||||
// document's primary resource. We will specify that we want the
|
||||
// 'groups' relation to be included by default.
|
||||
$serializer = new UserSerializer(['groups']);
|
||||
$this->document->setPrimaryElement($serializer->resource($user));
|
||||
|
||||
return $this->respondWithDocument();
|
||||
}
|
||||
}
|
40
src/Flarum/Api/Actions/Users/Update.php
Normal file
40
src/Flarum/Api/Actions/Users/Update.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php namespace Flarum\Api\Actions\Users;
|
||||
|
||||
use Event;
|
||||
|
||||
use Flarum\Core\Users\Commands\EditUserCommand;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Api\Actions\Base;
|
||||
use Flarum\Api\Serializers\UserSerializer;
|
||||
|
||||
class Update extends Base
|
||||
{
|
||||
/**
|
||||
* Edit a user. Allows renaming the user, changing their email, and setting
|
||||
* their password.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function run()
|
||||
{
|
||||
$userId = $this->param('id');
|
||||
|
||||
// EditUser is a single command because we don't want to allow partial
|
||||
// updates (i.e. if we were to run one command and then another, if the
|
||||
// second one failed, the first one would still have succeeded.)
|
||||
$command = new EditUserCommand($userId, User::current());
|
||||
$this->fillCommandWithInput($command, 'users');
|
||||
|
||||
Event::fire('Flarum.Api.Actions.Users.Update.WillExecuteCommand', [$command]);
|
||||
|
||||
$user = $this->commandBus->execute($command);
|
||||
|
||||
// Presumably, the user was updated successfully. (The command handler
|
||||
// would have thrown an exception if not.) We set this user as our
|
||||
// document's primary element.
|
||||
$serializer = new UserSerializer;
|
||||
$this->document->setPrimaryElement($serializer->resource($user));
|
||||
|
||||
return $this->respondWithDocument();
|
||||
}
|
||||
}
|
46
src/Flarum/Api/ApiServiceProvider.php
Normal file
46
src/Flarum/Api/ApiServiceProvider.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php namespace Flarum\Api;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Response;
|
||||
|
||||
class ApiServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Indicates if loading of the provider is deferred.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defer = false;
|
||||
|
||||
/**
|
||||
* Bootstrap the application events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->package('flarum/api', 'flarum.api');
|
||||
|
||||
include __DIR__.'/../../routes.api.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
19
src/Flarum/Api/Serializers/ActivitySerializer.php
Normal file
19
src/Flarum/Api/Serializers/ActivitySerializer.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Flarum\Core\Models\Activity;
|
||||
use Event;
|
||||
|
||||
class ActivitySerializer extends BaseSerializer {
|
||||
|
||||
public function serialize(Activity $activity)
|
||||
{
|
||||
$serialized = [
|
||||
'id' => (int) $activity->id
|
||||
];
|
||||
|
||||
Event::fire('flarum.api.serialize.activity', [&$serialized]);
|
||||
|
||||
return $serialized;
|
||||
}
|
||||
|
||||
}
|
102
src/Flarum/Api/Serializers/BaseSerializer.php
Normal file
102
src/Flarum/Api/Serializers/BaseSerializer.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Tobscure\JsonApi\SerializerAbstract;
|
||||
use Event;
|
||||
|
||||
/**
|
||||
* A base serializer to call Flarum events at common serialization points.
|
||||
*/
|
||||
abstract class BaseSerializer extends SerializerAbstract
|
||||
{
|
||||
/**
|
||||
* The name to use for Flarum events.
|
||||
* @var string
|
||||
*/
|
||||
protected static $eventName;
|
||||
|
||||
/**
|
||||
* Fire an event to allow default links and includes to be changed upon
|
||||
* serializer instantiation.
|
||||
*
|
||||
* @param array $include
|
||||
*/
|
||||
public function __construct($include = null, $link = null)
|
||||
{
|
||||
parent::__construct($include, $link);
|
||||
Event::fire('Flarum.Api.Serializers.'.static::$eventName.'.Initialize', [$this]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire an event to allow custom serialization of attributes.
|
||||
*
|
||||
* @param mixed $model The model to serialize.
|
||||
* @param array $attributes Attributes that have already been serialized.
|
||||
* @return array
|
||||
*/
|
||||
protected function attributesEvent($model, $attributes = [])
|
||||
{
|
||||
if (static::$eventName) {
|
||||
Event::fire('Flarum.Api.Serializers.'.static::$eventName.'.Attributes', [$model, &$attributes]);
|
||||
}
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire an event to allow custom URL templates to be specified.
|
||||
*
|
||||
* @param array $href URL templates that have already been specified.
|
||||
* @return array
|
||||
*/
|
||||
protected function hrefEvent($href = [])
|
||||
{
|
||||
if (static::$eventName) {
|
||||
Event::fire('Flarum.Api.Serializers.'.static::$eventName.'.Href', [&$href]);
|
||||
}
|
||||
return $href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a URL for a certain controller action.
|
||||
*
|
||||
* @param string $controllerMethod The name of the controller and its
|
||||
* method, separated by '@'. eg. UsersController@show
|
||||
*
|
||||
* @param array $params An array of route parameters to fill.
|
||||
* @return string
|
||||
*/
|
||||
protected function action($controllerMethod, $params)
|
||||
{
|
||||
$controllerPrefix = '\\Flarum\\Api\\Controllers\\';
|
||||
|
||||
// For compatibility with JSON-API, serializers will usually pass a
|
||||
// param containing a value of, for example, {discussions.id}. This is
|
||||
// problematic because after substituting named parameters, Laravel
|
||||
// substitutes remaining {placeholders} sequentially (thus removing
|
||||
// {discussions.id} from the URL.) To work around this, we opt to
|
||||
// initially replace parameters with an asterisk, and afterwards swap
|
||||
// the asterisk for the {discussions.id} placeholder.
|
||||
|
||||
$starredParams = array_combine(
|
||||
array_keys($params),
|
||||
array_fill(0, count($params), '*')
|
||||
);
|
||||
// $url = action($controllerPrefix.$controllerMethod, $starredParams);
|
||||
$url = '';
|
||||
|
||||
return preg_replace_sub('/\*/', $params, $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire an event to allow for custom links and includes.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return void
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if (static::$eventName && (substr($name, 0, 4) == 'link' || substr($name, 0, 7) == 'include')) {
|
||||
Event::fire('Flarum.Api.Serializers.'.static::$eventName.'.'.ucfirst($name), $arguments);
|
||||
}
|
||||
}
|
||||
}
|
50
src/Flarum/Api/Serializers/DiscussionBasicSerializer.php
Normal file
50
src/Flarum/Api/Serializers/DiscussionBasicSerializer.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Flarum\Core\Discussions\Discussion;
|
||||
|
||||
class DiscussionBasicSerializer extends BaseSerializer
|
||||
{
|
||||
/**
|
||||
* The name to use for Flarum events.
|
||||
* @var string
|
||||
*/
|
||||
protected static $eventName = 'DiscussionBasic';
|
||||
|
||||
/**
|
||||
* The resource type.
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'discussions';
|
||||
|
||||
/**
|
||||
* Serialize attributes of a Discussion model for JSON output.
|
||||
*
|
||||
* @param Discussion $discussion The Discussion model to serialize.
|
||||
* @return array
|
||||
*/
|
||||
protected function attributes(Discussion $discussion)
|
||||
{
|
||||
$attributes = [
|
||||
'id' => (int) $discussion->id,
|
||||
'title' => $discussion->title,
|
||||
];
|
||||
|
||||
return $this->attributesEvent($discussion, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL templates where this resource and its related resources can
|
||||
* be accessed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function href()
|
||||
{
|
||||
$href = [
|
||||
'discussions' => $this->action('DiscussionsController@show', ['id' => '{discussions.id}']),
|
||||
'posts' => $this->action('PostsController@indexForDiscussion', ['id' => '{discussions.id}'])
|
||||
];
|
||||
|
||||
return $this->hrefEvent($href);
|
||||
}
|
||||
}
|
137
src/Flarum/Api/Serializers/DiscussionSerializer.php
Normal file
137
src/Flarum/Api/Serializers/DiscussionSerializer.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Flarum\Core\Discussions\Discussion;
|
||||
use Flarum\Core\Discussions\DiscussionState;
|
||||
|
||||
class DiscussionSerializer extends DiscussionBasicSerializer
|
||||
{
|
||||
/**
|
||||
* The name to use for Flarum events.
|
||||
* @var string
|
||||
*/
|
||||
protected static $eventName = 'Discussion';
|
||||
|
||||
/**
|
||||
* Default relations to include.
|
||||
* @var array
|
||||
*/
|
||||
protected $include = ['startUser', 'lastUser'];
|
||||
|
||||
/**
|
||||
* Serialize attributes of a Discussion model for JSON output.
|
||||
*
|
||||
* @param Discussion $discussion The Discussion model to serialize.
|
||||
* @return array
|
||||
*/
|
||||
protected function attributes(Discussion $discussion)
|
||||
{
|
||||
$attributes = parent::attributes($discussion);
|
||||
|
||||
$attributes += [
|
||||
'postsCount' => (int) $discussion->posts_count,
|
||||
'startTime' => $discussion->start_time->toRFC3339String(),
|
||||
'lastTime' => $discussion->last_time ? $discussion->last_time->toRFC3339String() : null,
|
||||
'lastPostNumber' => $discussion->last_post_number,
|
||||
'canEdit' => $discussion->permission('edit'),
|
||||
'canDelete' => $discussion->permission('delete'),
|
||||
|
||||
// temp
|
||||
'sticky' => $discussion->sticky,
|
||||
'category' => $discussion->category
|
||||
];
|
||||
|
||||
if ($state = $discussion->state) {
|
||||
$attributes += [
|
||||
'readTime' => $state->read_time ? $state->read_time->toRFC3339String() : null,
|
||||
'readNumber' => (int) $state->read_number
|
||||
];
|
||||
}
|
||||
|
||||
return $this->attributesEvent($discussion, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection containing a discussion's viewable post IDs.
|
||||
*
|
||||
* @param Discussion $discussion
|
||||
* @return Tobscure\JsonApi\Collection
|
||||
*/
|
||||
public function linkPosts(Discussion $discussion)
|
||||
{
|
||||
return (new PostBasicSerializer)->collection($discussion->posts()->whereCanView()->ids());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection containing a discussion's viewable posts.
|
||||
*
|
||||
* @param Discussion $discussion
|
||||
* @param array $relations
|
||||
* @return Tobscure\JsonApi\Collection
|
||||
*/
|
||||
public function includePosts(Discussion $discussion, $relations)
|
||||
{
|
||||
return (new PostSerializer($relations))->collection($discussion->posts()->whereCanView()->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection containing a discussion's relevant posts. Assumes that
|
||||
* the discussion model's relevantPosts attributes has been filled (this
|
||||
* happens in the DiscussionFinder.)
|
||||
*
|
||||
* @param Discussion $discussion
|
||||
* @param array $relations
|
||||
* @return Tobscure\JsonApi\Collection
|
||||
*/
|
||||
public function includeRelevantPosts(Discussion $discussion, $relations)
|
||||
{
|
||||
return (new PostBasicSerializer($relations))->collection($discussion->relevantPosts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource containing a discussion's start user.
|
||||
*
|
||||
* @param Discussion $discussion
|
||||
* @param array $relations
|
||||
* @return Tobscure\JsonApi\Resource
|
||||
*/
|
||||
public function includeStartUser(Discussion $discussion, $relations)
|
||||
{
|
||||
return (new UserBasicSerializer($relations))->resource($discussion->startUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource containing a discussion's starting post.
|
||||
*
|
||||
* @param Discussion $discussion
|
||||
* @param array $relations
|
||||
* @return Tobscure\JsonApi\Resource
|
||||
*/
|
||||
public function includeStartPost(Discussion $discussion, $relations)
|
||||
{
|
||||
return (new PostBasicSerializer($relations))->resource($discussion->startPost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource containing a discussion's last user.
|
||||
*
|
||||
* @param Discussion $discussion
|
||||
* @param array $relations
|
||||
* @return Tobscure\JsonApi\Resource
|
||||
*/
|
||||
public function includeLastUser(Discussion $discussion, $relations)
|
||||
{
|
||||
return (new UserBasicSerializer($relations))->resource($discussion->lastUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource containing a discussion's last post.
|
||||
*
|
||||
* @param Discussion $discussion
|
||||
* @param array $relations
|
||||
* @return Tobscure\JsonApi\Resource
|
||||
*/
|
||||
public function includeLastPost(Discussion $discussion, $relations)
|
||||
{
|
||||
return (new PostBasicSerializer($relations))->resource($discussion->lastPost);
|
||||
}
|
||||
}
|
48
src/Flarum/Api/Serializers/GroupSerializer.php
Normal file
48
src/Flarum/Api/Serializers/GroupSerializer.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Flarum\Core\Groups\Group;
|
||||
|
||||
class GroupSerializer extends BaseSerializer
|
||||
{
|
||||
/**
|
||||
* The name to use for Flarum events.
|
||||
* @var string
|
||||
*/
|
||||
protected static $eventName = 'Group';
|
||||
|
||||
/**
|
||||
* The resource type.
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'groups';
|
||||
|
||||
/**
|
||||
* Serialize attributes of a Group model for JSON output.
|
||||
*
|
||||
* @param Group $group The Group model to serialize.
|
||||
* @return array
|
||||
*/
|
||||
protected function attributes(Group $group)
|
||||
{
|
||||
$attributes = [
|
||||
'id' => (int) $group->id,
|
||||
'name' => $group->name
|
||||
];
|
||||
|
||||
return $this->attributesEvent($group, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL templates where this resource and its related resources can
|
||||
* be accessed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function href()
|
||||
{
|
||||
return [
|
||||
'groups' => $this->action('GroupsController@show', ['id' => '{groups.id}']),
|
||||
'users' => $this->action('UsersController@indexForGroup', ['id' => '{groups.id}'])
|
||||
];
|
||||
}
|
||||
}
|
85
src/Flarum/Api/Serializers/PostBasicSerializer.php
Normal file
85
src/Flarum/Api/Serializers/PostBasicSerializer.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Flarum\Core\Posts\Post;
|
||||
|
||||
class PostBasicSerializer extends BaseSerializer
|
||||
{
|
||||
/**
|
||||
* The name to use for Flarum events.
|
||||
* @var string
|
||||
*/
|
||||
protected static $eventName = 'PostBasic';
|
||||
|
||||
/**
|
||||
* The resource type.
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'posts';
|
||||
|
||||
/**
|
||||
* Default relations to link.
|
||||
* @var array
|
||||
*/
|
||||
protected $link = ['discussion'];
|
||||
|
||||
/**
|
||||
* Default relations to include.
|
||||
* @var array
|
||||
*/
|
||||
protected $include = ['user'];
|
||||
|
||||
/**
|
||||
* Serialize attributes of a Post model for JSON output.
|
||||
*
|
||||
* @param Post $post The Post model to serialize.
|
||||
* @return array
|
||||
*/
|
||||
protected function attributes(Post $post)
|
||||
{
|
||||
$attributes = [
|
||||
'id' => (int) $post->id,
|
||||
'number' => (int) $post->number,
|
||||
'time' => $post->time->toRFC3339String(),
|
||||
'type' => $post->type,
|
||||
'content' => str_limit($post->content, 200)
|
||||
];
|
||||
|
||||
return $this->attributesEvent($post, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL templates where this resource and its related resources can
|
||||
* be accessed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function href()
|
||||
{
|
||||
return [
|
||||
'posts' => $this->action('PostsController@show', ['id' => '{posts.id}'])
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource containing a post's user.
|
||||
*
|
||||
* @param Post $post
|
||||
* @param array $relations
|
||||
* @return Tobscure\JsonApi\Resource
|
||||
*/
|
||||
public function includeUser(Post $post, $relations)
|
||||
{
|
||||
return (new UserBasicSerializer($relations))->resource($post->user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource containing a post's discussion ID.
|
||||
*
|
||||
* @param Post $post
|
||||
* @return Tobscure\JsonApi\Resource
|
||||
*/
|
||||
public function linkDiscussion(Post $post)
|
||||
{
|
||||
return (new DiscussionBasicSerializer)->resource($post->discussion_id);
|
||||
}
|
||||
}
|
109
src/Flarum/Api/Serializers/PostSerializer.php
Normal file
109
src/Flarum/Api/Serializers/PostSerializer.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class PostSerializer extends PostBasicSerializer
|
||||
{
|
||||
/**
|
||||
* The name to use for Flarum events.
|
||||
* @var string
|
||||
*/
|
||||
protected static $eventName = 'Post';
|
||||
|
||||
/**
|
||||
* Default relations to link.
|
||||
* @var array
|
||||
*/
|
||||
protected $link = ['discussion'];
|
||||
|
||||
/**
|
||||
* Default relations to include.
|
||||
* @var array
|
||||
*/
|
||||
protected $include = ['user', 'editUser', 'deleteUser'];
|
||||
|
||||
/**
|
||||
* Serialize attributes of a Post model for JSON output.
|
||||
*
|
||||
* @param Post $post The Post model to serialize.
|
||||
* @return array
|
||||
*/
|
||||
protected function attributes(Post $post)
|
||||
{
|
||||
$attributes = parent::attributes($post);
|
||||
|
||||
unset($attributes['content']);
|
||||
if ($post->type != 'comment') {
|
||||
$attributes['content'] = $post->content;
|
||||
} else {
|
||||
// @todo move to a formatter class
|
||||
$attributes['contentHtml'] = $post->content_html ?: '<p>'.nl2br(htmlspecialchars(trim($post->content))).'</p>';
|
||||
}
|
||||
|
||||
if ($post->edit_time) {
|
||||
$attributes['editTime'] = (string) $post->edit_time;
|
||||
}
|
||||
|
||||
if ($post->delete_time) {
|
||||
$attributes['deleteTime'] = (string) $post->delete_time;
|
||||
}
|
||||
|
||||
$user = User::current();
|
||||
|
||||
$attributes += [
|
||||
'canEdit' => $post->can($user, 'edit'),
|
||||
'canDelete' => $post->can($user, 'delete')
|
||||
];
|
||||
|
||||
return $this->attributesEvent($post, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource containing a post's user.
|
||||
*
|
||||
* @param Post $post
|
||||
* @param array $relations
|
||||
* @return Tobscure\JsonApi\Resource
|
||||
*/
|
||||
public function includeUser(Post $post, $relations = [])
|
||||
{
|
||||
return (new UserSerializer($relations))->resource($post->user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource containing a post's discussion.
|
||||
*
|
||||
* @param Post $post
|
||||
* @param array $relations
|
||||
* @return Tobscure\JsonApi\Resource
|
||||
*/
|
||||
public function includeDiscussion(Post $post, $relations = [])
|
||||
{
|
||||
return (new DiscussionBasicSerializer($relations))->resource($post->discussion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource containing a post's edit user.
|
||||
*
|
||||
* @param Post $post
|
||||
* @param array $relations
|
||||
* @return Tobscure\JsonApi\Resource
|
||||
*/
|
||||
public function includeEditUser(Post $post, $relations = [])
|
||||
{
|
||||
return (new UserBasicSerializer($relations))->resource($post->editUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource containing a post's delete user.
|
||||
*
|
||||
* @param Post $post
|
||||
* @param array $relations
|
||||
* @return Tobscure\JsonApi\Resource
|
||||
*/
|
||||
public function includeDeleteUser(Post $post, $relations = [])
|
||||
{
|
||||
return (new UserBasicSerializer($relations))->resource($post->deleteUser);
|
||||
}
|
||||
}
|
21
src/Flarum/Api/Serializers/UserAdminSerializer.php
Normal file
21
src/Flarum/Api/Serializers/UserAdminSerializer.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
use Event;
|
||||
|
||||
class UserAdminSerializer extends UserSerializer {
|
||||
|
||||
public function serialize(User $user)
|
||||
{
|
||||
$serialized = parent::serialize($user);
|
||||
|
||||
$serialized += [
|
||||
'email' => $user->email,
|
||||
];
|
||||
|
||||
Event::fire('flarum.api.serialize.user.admin', [&$serialized]);
|
||||
|
||||
return $serialized;
|
||||
}
|
||||
|
||||
}
|
50
src/Flarum/Api/Serializers/UserBasicSerializer.php
Normal file
50
src/Flarum/Api/Serializers/UserBasicSerializer.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class UserBasicSerializer extends BaseSerializer
|
||||
{
|
||||
/**
|
||||
* The name to use for Flarum events.
|
||||
* @var string
|
||||
*/
|
||||
protected static $eventName = 'UserBasic';
|
||||
|
||||
/**
|
||||
* The resource type.
|
||||
* @var string
|
||||
*/
|
||||
protected $type = 'users';
|
||||
|
||||
/**
|
||||
* Serialize attributes of a User model for JSON output.
|
||||
*
|
||||
* @param User $user The User model to serialize.
|
||||
* @return array
|
||||
*/
|
||||
protected function attributes(User $user)
|
||||
{
|
||||
$attributes = [
|
||||
'id' => (int) $user->id,
|
||||
'username' => $user->username,
|
||||
'avatarUrl' => $user->avatar_url
|
||||
];
|
||||
|
||||
return $this->attributesEvent($user, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL templates where this resource and its related resources can
|
||||
* be accessed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function href()
|
||||
{
|
||||
$href = [
|
||||
'users' => $this->action('UsersController@show', ['id' => '{users.id}'])
|
||||
];
|
||||
|
||||
return $this->hrefEvent($href);
|
||||
}
|
||||
}
|
29
src/Flarum/Api/Serializers/UserCurrentSerializer.php
Normal file
29
src/Flarum/Api/Serializers/UserCurrentSerializer.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
use Event;
|
||||
|
||||
use DateTime, DateTimeZone;
|
||||
|
||||
class UserCurrentSerializer extends UserSerializer {
|
||||
|
||||
public function serialize(User $user)
|
||||
{
|
||||
$serialized = parent::serialize($user);
|
||||
|
||||
// TODO: make UserCurrentSerializer and UserSerializer work better with guests
|
||||
if ($user->id)
|
||||
{
|
||||
$serialized += [
|
||||
'time_zone' => $user->time_zone,
|
||||
'time_zone_offset' => with(new DateTimeZone($user->time_zone))->getOffset(new DateTime('now'))
|
||||
// other user preferences. probably mostly from external sources (e.g. flarum/web)
|
||||
];
|
||||
}
|
||||
|
||||
Event::fire('flarum.api.serialize.user.current', [&$serialized]);
|
||||
|
||||
return $serialized;
|
||||
}
|
||||
|
||||
}
|
52
src/Flarum/Api/Serializers/UserSerializer.php
Normal file
52
src/Flarum/Api/Serializers/UserSerializer.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php namespace Flarum\Api\Serializers;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class UserSerializer extends UserBasicSerializer
|
||||
{
|
||||
/**
|
||||
* The name to use for Flarum events.
|
||||
* @var string
|
||||
*/
|
||||
protected static $eventName = 'User';
|
||||
|
||||
/**
|
||||
* Default relations to include.
|
||||
* @var array
|
||||
*/
|
||||
protected $include = ['groups'];
|
||||
|
||||
/**
|
||||
* Serialize attributes of a User model for JSON output.
|
||||
*
|
||||
* @param User $user The User model to serialize.
|
||||
* @return array
|
||||
*/
|
||||
protected function attributes(User $user)
|
||||
{
|
||||
$attributes = parent::attributes($user);
|
||||
|
||||
$attributes += [
|
||||
'joinTime' => $user->join_time ? $user->join_time->toRFC3339String() : '',
|
||||
'lastSeenTime' => $user->last_seen_time ? $user->last_seen_time->toRFC3339String() : '',
|
||||
'discussionsCount' => (int) $user->discussions_count,
|
||||
'postsCount' => (int) $user->posts_count,
|
||||
'canEdit' => $user->permission('edit'),
|
||||
'canDelete' => $user->permission('delete'),
|
||||
];
|
||||
|
||||
return $this->attributesEvent($user, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection containing a user's groups.
|
||||
*
|
||||
* @param User $user
|
||||
* @param array $relations
|
||||
* @return Tobscure\JsonApi\Collection
|
||||
*/
|
||||
protected function includeGroups(User $user, $relations)
|
||||
{
|
||||
return (new GroupSerializer($relations))->collection($user->groups);
|
||||
}
|
||||
}
|
36
src/Flarum/Core/Activity/Activity.php
Normal file
36
src/Flarum/Core/Activity/Activity.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php namespace Flarum\Core\Activity;
|
||||
|
||||
use Flarum\Core\Entity;
|
||||
use Illuminate\Support\Str;
|
||||
use Auth;
|
||||
|
||||
class Activity extends Entity {
|
||||
|
||||
protected $table = 'activity';
|
||||
|
||||
public function getDates()
|
||||
{
|
||||
return ['time'];
|
||||
}
|
||||
|
||||
public function fromUser()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Users\User', 'from_user_id');
|
||||
}
|
||||
|
||||
public function permission($permission)
|
||||
{
|
||||
return User::current()->can($permission, 'activity', $this);
|
||||
}
|
||||
|
||||
public function editable()
|
||||
{
|
||||
return $this->permission('edit');
|
||||
}
|
||||
|
||||
public function deletable()
|
||||
{
|
||||
return $this->permission('delete');
|
||||
}
|
||||
|
||||
}
|
98
src/Flarum/Core/CoreServiceProvider.php
Normal file
98
src/Flarum/Core/CoreServiceProvider.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php namespace Flarum\Core;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Config;
|
||||
use Event;
|
||||
|
||||
class CoreServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Indicates if loading of the provider is deferred.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defer = false;
|
||||
|
||||
/**
|
||||
* Bootstrap the application events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->package('flarum/core', 'flarum');
|
||||
|
||||
Config::set('database.connections.flarum', Config::get('flarum::database'));
|
||||
|
||||
$this->app->make('validator')->extend('username', 'Flarum\Core\Users\UsernameValidator@validate');
|
||||
|
||||
Event::listen('Flarum.Core.*', 'Flarum\Core\Listeners\DiscussionMetadataUpdater');
|
||||
Event::listen('Flarum.Core.*', 'Flarum\Core\Listeners\UserMetadataUpdater');
|
||||
Event::listen('Flarum.Core.*', 'Flarum\Core\Listeners\PostFormatter');
|
||||
Event::listen('Flarum.Core.*', 'Flarum\Core\Listeners\TitleChangePostCreator');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Start up the Laracasts Commander package. This is used as the basis
|
||||
// for the Commands & Domain Events architecture used to structure
|
||||
// Flarum's domain.
|
||||
$this->app->register('Laracasts\Commander\CommanderServiceProvider');
|
||||
|
||||
// Register a singleton entity that represents this forum. This entity
|
||||
// will be used to check for global forum permissions (like viewing the
|
||||
// forum, registering, and starting discussions.)
|
||||
$this->app->singleton('flarum.forum', 'Flarum\Core\Forum');
|
||||
|
||||
// Register the extensions manager object. This manages a list of
|
||||
// available extensions, and provides functionality to enable/disable
|
||||
// them.
|
||||
$this->app->singleton('flarum.extensions', 'Flarum\Core\Support\Extensions\Manager');
|
||||
|
||||
// Register the permissions manager object. This reads the permissions
|
||||
// from the permissions repository and can determine whether or not a
|
||||
// user has explicitly been granted a certain permission.
|
||||
$this->app->singleton('flarum.permissions', 'Flarum\Core\Permissions\Manager');
|
||||
|
||||
|
||||
|
||||
$this->app->bind('flarum.discussionFinder', 'Flarum\Core\Discussions\DiscussionFinder');
|
||||
|
||||
|
||||
// $this->app->singleton(
|
||||
// 'Flarum\Core\Repositories\Contracts\DiscussionRepository',
|
||||
// function($app)
|
||||
// {
|
||||
// $discussion = new \Flarum\Core\Repositories\EloquentDiscussionRepository;
|
||||
// return new DiscussionCacheDecorator($discussion);
|
||||
// }
|
||||
// );
|
||||
// $this->app->singleton(
|
||||
// 'Flarum\Core\Repositories\Contracts\UserRepository',
|
||||
// 'Flarum\Core\Repositories\EloquentUserRepository'
|
||||
// );
|
||||
// $this->app->singleton(
|
||||
// 'Flarum\Core\Repositories\Contracts\PostRepository',
|
||||
// 'Flarum\Core\Repositories\EloquentPostRepository'
|
||||
// );
|
||||
// $this->app->singleton(
|
||||
// 'Flarum\Core\Repositories\Contracts\GroupRepository',
|
||||
// 'Flarum\Core\Repositories\EloquentGroupRepository'
|
||||
// );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
class DeleteDiscussionCommand
|
||||
{
|
||||
public $discussionId;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($discussionId, $user)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Flarum\Core\Discussions\DiscussionRepository;
|
||||
use Laracasts\Commander\CommandHandler;
|
||||
use Laracasts\Commander\Events\DispatchableTrait;
|
||||
use Event;
|
||||
|
||||
class DeleteDiscussionCommandHandler implements CommandHandler
|
||||
{
|
||||
use DispatchableTrait;
|
||||
|
||||
protected $discussions;
|
||||
|
||||
public function __construct(DiscussionRepository $discussions)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$user = $command->user;
|
||||
$discussion = $this->discussions->findOrFail($command->discussionId, $user);
|
||||
|
||||
$discussion->assertCan($user, 'delete');
|
||||
|
||||
Event::fire('Flarum.Core.Discussions.Commands.DeleteDiscussion.DiscussionWillBeDeleted', [$discussion, $command]);
|
||||
|
||||
$this->discussions->delete($discussion);
|
||||
$this->dispatchEventsFor($discussion);
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Flarum\Core\Support\CommandValidator;
|
||||
|
||||
class DeleteDiscussionValidator extends CommandValidator
|
||||
{
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
class EditDiscussionCommand
|
||||
{
|
||||
public $discussionId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $title;
|
||||
|
||||
public function __construct($discussionId, $user)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Laracasts\Commander\CommandHandler;
|
||||
use Laracasts\Commander\Events\DispatchableTrait;
|
||||
use Event;
|
||||
|
||||
use Flarum\Core\Discussions\DiscussionRepository;
|
||||
|
||||
class EditDiscussionCommandHandler implements CommandHandler
|
||||
{
|
||||
use DispatchableTrait;
|
||||
|
||||
protected $discussions;
|
||||
|
||||
public function __construct(DiscussionRepository $discussions)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$user = $command->user;
|
||||
$discussion = $this->discussions->findOrFail($command->discussionId, $user);
|
||||
|
||||
$discussion->assertCan($user, 'edit');
|
||||
|
||||
if (isset($command->title)) {
|
||||
$discussion->rename($command->title, $user);
|
||||
}
|
||||
|
||||
Event::fire('Flarum.Core.Discussions.Commands.EditDiscussion.DiscussionWillBeSaved', [$discussion, $command]);
|
||||
|
||||
$this->discussions->save($discussion);
|
||||
$this->dispatchEventsFor($discussion);
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<?php namespace Flarum\Core\Posts\Commands;
|
||||
|
||||
use Flarum\Core\Support\CommandValidator;
|
||||
|
||||
class EditDiscussionValidator extends CommandValidator
|
||||
{
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
class ReadDiscussionCommand
|
||||
{
|
||||
public $discussionId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $readNumber;
|
||||
|
||||
public function __construct($discussionId, $user, $readNumber)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->user = $user;
|
||||
$this->readNumber = $readNumber;
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Laracasts\Commander\CommandHandler;
|
||||
use Laracasts\Commander\Events\DispatchableTrait;
|
||||
use Event;
|
||||
|
||||
use Flarum\Core\Discussions\DiscussionRepository;
|
||||
|
||||
class ReadDiscussionCommandHandler implements CommandHandler
|
||||
{
|
||||
use DispatchableTrait;
|
||||
|
||||
protected $discussions;
|
||||
|
||||
public function __construct(DiscussionRepository $discussions)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$user = $command->user;
|
||||
$discussion = $this->discussions->findOrFail($command->discussionId, $user);
|
||||
|
||||
$discussion->state = $this->discussions->getState($discussion, $user);
|
||||
$discussion->state->read($command->readNumber);
|
||||
|
||||
Event::fire('Flarum.Core.Discussions.Commands.ReadDiscussion.StateWillBeSaved', [$discussion, $command]);
|
||||
|
||||
$this->discussions->saveState($discussion->state);
|
||||
$this->dispatchEventsFor($discussion->state);
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
<?php namespace Flarum\Core\Posts\Commands;
|
||||
|
||||
use Flarum\Core\Support\CommandValidator;
|
||||
use Flarum\Core\Support\Exceptions\PermissionDeniedException;
|
||||
|
||||
class ReadDiscussionValidator extends CommandValidator
|
||||
{
|
||||
public function validate($command)
|
||||
{
|
||||
// The user must be logged in (not a guest) to have state data about
|
||||
// a discussion. Thus, we deny permission to mark a discussion as read
|
||||
// if the user doesn't exist in the database.
|
||||
if (! $command->user->exists) {
|
||||
throw new PermissionDeniedException;
|
||||
}
|
||||
|
||||
parent::validate($command);
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
class StartDiscussionCommand
|
||||
{
|
||||
public $title;
|
||||
|
||||
public $content;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($title, $content, $user)
|
||||
{
|
||||
$this->title = $title;
|
||||
$this->content = $content;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Laracasts\Commander\CommandBus;
|
||||
use Laracasts\Commander\CommandHandler;
|
||||
use Laracasts\Commander\Events\DispatchableTrait;
|
||||
use Event;
|
||||
|
||||
use Flarum\Core\Forum;
|
||||
use Flarum\Core\Discussions\Discussion;
|
||||
use Flarum\Core\Discussions\DiscussionRepository;
|
||||
use Flarum\Core\Posts\Commands\PostReplyCommand;
|
||||
|
||||
class StartDiscussionCommandHandler implements CommandHandler
|
||||
{
|
||||
use DispatchableTrait;
|
||||
|
||||
protected $forum;
|
||||
|
||||
protected $discussionRepo;
|
||||
|
||||
protected $commandBus;
|
||||
|
||||
public function __construct(Forum $forum, DiscussionRepository $discussionRepo, CommandBus $commandBus)
|
||||
{
|
||||
$this->forum = $forum;
|
||||
$this->discussionRepo = $discussionRepo;
|
||||
$this->commandBus = $commandBus;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$this->forum->assertCan($command->user, 'startDiscussion');
|
||||
|
||||
// Create a new Discussion entity, persist it, and dispatch domain
|
||||
// events. Before persistance, though, fire an event to give plugins
|
||||
// an opportunity to alter the discussion entity based on data in the
|
||||
// command they may have passed through in the controller.
|
||||
$discussion = Discussion::start(
|
||||
$command->title,
|
||||
$command->user
|
||||
);
|
||||
|
||||
Event::fire('Flarum.Core.Discussions.Commands.StartDiscussion.DiscussionWillBeSaved', [$discussion, $command]);
|
||||
|
||||
$this->discussionRepo->save($discussion);
|
||||
|
||||
// Now that the discussion has been created, we can add the first post.
|
||||
// For now we will do this by running the PostReply command, but as this
|
||||
// will trigger a domain event that is slightly semantically incorrect
|
||||
// in this situation (ReplyWasPosted), we may need to reconsider someday.
|
||||
$this->commandBus->execute(
|
||||
new PostReplyCommand($discussion->id, $command->content, $command->user)
|
||||
);
|
||||
|
||||
$this->dispatchEventsFor($discussion);
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<?php namespace Flarum\Core\Discussions\Commands;
|
||||
|
||||
use Flarum\Core\Support\CommandValidator;
|
||||
|
||||
class StartDiscussionValidator extends CommandValidator
|
||||
{
|
||||
}
|
190
src/Flarum/Core/Discussions/Discussion.php
Executable file
190
src/Flarum/Core/Discussions/Discussion.php
Executable file
@@ -0,0 +1,190 @@
|
||||
<?php namespace Flarum\Core\Discussions;
|
||||
|
||||
use Laracasts\Commander\Events\EventGenerator;
|
||||
use Tobscure\Permissible\Permissible;
|
||||
|
||||
use Flarum\Core\Entity;
|
||||
use Flarum\Core\Forum;
|
||||
use Flarum\Core\Permission;
|
||||
use Flarum\Core\Support\Exceptions\PermissionDeniedException;
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class Discussion extends Entity
|
||||
{
|
||||
use Permissible;
|
||||
use EventGenerator;
|
||||
|
||||
protected $table = 'discussions';
|
||||
|
||||
protected static $rules = [
|
||||
'title' => 'required',
|
||||
'start_time' => 'required|date',
|
||||
'posts_count' => 'integer',
|
||||
'start_user_id' => 'integer',
|
||||
'start_post_id' => 'integer',
|
||||
'last_time' => 'date',
|
||||
'last_user_id' => 'integer',
|
||||
'last_post_id' => 'integer',
|
||||
'last_post_number' => 'integer'
|
||||
];
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::grant(function ($grant, $user, $permission) {
|
||||
return app('flarum.permissions')->granted($user, $permission, 'discussion');
|
||||
});
|
||||
|
||||
// Grant view access to a discussion if the user can view the forum.
|
||||
static::grant('view', function ($grant, $user) {
|
||||
return app('flarum.forum')->can($user, 'view');
|
||||
});
|
||||
|
||||
// Allow a user to edit their own discussion.
|
||||
static::grant('edit', function ($grant, $user) {
|
||||
if (app('flarum.permissions')->granted($user, 'editOwn', 'discussion')) {
|
||||
$grant->where('user_id', $user->id);
|
||||
}
|
||||
});
|
||||
|
||||
static::deleted(function ($discussion) {
|
||||
$discussion->raise(new Events\DiscussionWasDeleted($discussion));
|
||||
|
||||
$discussion->posts()->delete();
|
||||
$discussion->readers()->detach();
|
||||
});
|
||||
}
|
||||
|
||||
public static function start($title, $user)
|
||||
{
|
||||
$discussion = new static;
|
||||
|
||||
$discussion->title = $title;
|
||||
$discussion->start_time = time();
|
||||
$discussion->start_user_id = $user->id;
|
||||
|
||||
$discussion->raise(new Events\DiscussionWasStarted($discussion));
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
|
||||
public function setLastPost($post)
|
||||
{
|
||||
$this->last_time = $post->time;
|
||||
$this->last_user_id = $post->user_id;
|
||||
$this->last_post_id = $post->id;
|
||||
$this->last_post_number = $post->number;
|
||||
}
|
||||
|
||||
public function refreshLastPost()
|
||||
{
|
||||
$lastPost = $this->dialog()->orderBy('time', 'desc')->first();
|
||||
$this->setLastPost($lastPost);
|
||||
}
|
||||
|
||||
public function refreshPostsCount()
|
||||
{
|
||||
$this->posts_count = $this->dialog()->count();
|
||||
}
|
||||
|
||||
public function rename($title, $user)
|
||||
{
|
||||
if ($this->title === $title) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->title = $title;
|
||||
|
||||
$this->raise(new Events\DiscussionWasRenamed($this, $user));
|
||||
}
|
||||
|
||||
public function getDates()
|
||||
{
|
||||
return ['start_time', 'last_time'];
|
||||
}
|
||||
|
||||
public function posts()
|
||||
{
|
||||
return $this->hasMany('Flarum\Core\Posts\Post')->orderBy('time', 'asc');
|
||||
}
|
||||
|
||||
public function dialog()
|
||||
{
|
||||
return $this->posts()->where('type', 'comment')->whereNull('delete_time');
|
||||
}
|
||||
|
||||
public function startPost()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Posts\Post', 'start_post_id');
|
||||
}
|
||||
|
||||
public function startUser()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Users\User', 'start_user_id');
|
||||
}
|
||||
|
||||
public function lastPost()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Posts\Post', 'last_post_id');
|
||||
}
|
||||
|
||||
public function lastUser()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Users\User', 'last_user_id');
|
||||
}
|
||||
|
||||
public function readers()
|
||||
{
|
||||
return $this->belongsToMany('Flarum\Core\Users\User', 'users_discussions');
|
||||
}
|
||||
|
||||
public function state($userId = null)
|
||||
{
|
||||
if (is_null($userId)) {
|
||||
$userId = User::current()->id;
|
||||
}
|
||||
return $this->hasOne('Flarum\Core\Discussions\DiscussionState')->where('user_id', $userId);
|
||||
}
|
||||
|
||||
public function stateFor($user)
|
||||
{
|
||||
$state = $this->state($user->id)->first();
|
||||
|
||||
if (! $state) {
|
||||
$state = new DiscussionState;
|
||||
$state->discussion_id = $this->id;
|
||||
$state->user_id = $user->id;
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
|
||||
public function scopePermission($query, $permission, $user = null)
|
||||
{
|
||||
if (is_null($user)) {
|
||||
$user = User::current();
|
||||
}
|
||||
return $this->scopeWhereCan($query, $user, $permission);
|
||||
}
|
||||
|
||||
public function scopeWhereCanView($query, $user = null)
|
||||
{
|
||||
return $this->scopePermission($query, 'view', $user);
|
||||
}
|
||||
|
||||
public function permission($permission, $user = null)
|
||||
{
|
||||
if (is_null($user)) {
|
||||
$user = User::current();
|
||||
}
|
||||
return $this->can($user, $permission);
|
||||
}
|
||||
|
||||
public function assertCan($user, $permission)
|
||||
{
|
||||
if (! $this->can($user, $permission)) {
|
||||
throw new PermissionDeniedException;
|
||||
}
|
||||
}
|
||||
}
|
258
src/Flarum/Core/Discussions/DiscussionFinder.php
Normal file
258
src/Flarum/Core/Discussions/DiscussionFinder.php
Normal file
@@ -0,0 +1,258 @@
|
||||
<?php namespace Flarum\Core\Discussions;
|
||||
|
||||
use Flarum\Core\Search\Tokenizer;
|
||||
use Flarum\Core\Posts\Post;
|
||||
|
||||
use Cache;
|
||||
use DB;
|
||||
|
||||
class DiscussionFinder
|
||||
{
|
||||
protected $user;
|
||||
|
||||
protected $tokens;
|
||||
|
||||
protected $sort;
|
||||
|
||||
protected $sortMap = [
|
||||
'lastPost' => ['last_time', 'desc'],
|
||||
'replies' => ['posts_count', 'desc'],
|
||||
'created' => ['start_time', 'desc']
|
||||
];
|
||||
|
||||
protected $order;
|
||||
|
||||
protected $key;
|
||||
|
||||
protected $count;
|
||||
|
||||
protected $areMoreResults;
|
||||
|
||||
protected $fulltext;
|
||||
|
||||
public function __construct($user = null, $tokens = null, $sort = null, $order = null, $key = null)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->tokens = $tokens;
|
||||
$this->sort = $sort;
|
||||
$this->order = $order;
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser($user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function getTokens()
|
||||
{
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
public function setTokens($tokens)
|
||||
{
|
||||
$this->tokens = $tokens;
|
||||
}
|
||||
|
||||
public function setQuery($query)
|
||||
{
|
||||
$tokenizer = new Tokenizer($query);
|
||||
$this->setTokens($tokenizer->tokenize());
|
||||
}
|
||||
|
||||
public function getSort()
|
||||
{
|
||||
return $this->sort;
|
||||
}
|
||||
|
||||
public function setSort($sort)
|
||||
{
|
||||
$this->sort = $sort;
|
||||
}
|
||||
|
||||
public function getOrder()
|
||||
{
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
public function setOrder($order)
|
||||
{
|
||||
$this->order = $order;
|
||||
}
|
||||
|
||||
public function getKey()
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function setKey($key)
|
||||
{
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
protected function getCacheKey()
|
||||
{
|
||||
return 'discussions.'.$this->key;
|
||||
}
|
||||
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
public function areMoreResults()
|
||||
{
|
||||
return $this->areMoreResults;
|
||||
}
|
||||
|
||||
public function fulltext()
|
||||
{
|
||||
return $this->fulltext;
|
||||
}
|
||||
|
||||
public function results($count = null, $start = 0, $load = [])
|
||||
{
|
||||
$relevantPosts = false;
|
||||
|
||||
if (in_array('relevantPosts', $load)) {
|
||||
$load = array_diff($load, ['relevantPosts', 'relevantPosts.user']);
|
||||
$relevantPosts = true;
|
||||
}
|
||||
|
||||
$ids = null;
|
||||
$query = Discussion::whereCan($this->user, 'view');
|
||||
$query->with($load);
|
||||
|
||||
if ($this->key and Cache::has($key = $this->getCacheKey())) {
|
||||
$ids = Cache::get($key);
|
||||
} elseif (count($this->tokens)) {
|
||||
// foreach ($tokens as $type => $value)
|
||||
// {
|
||||
// switch ($type)
|
||||
// {
|
||||
// case 'flag:draft':
|
||||
// case 'flag:muted':
|
||||
// case 'flag:subscribed':
|
||||
// case 'flag:private':
|
||||
// // pre-process
|
||||
// $ids = $this->discussions->getDraftIdsForUser(Auth::user());
|
||||
// $ids = $this->discussions->getMutedIdsForUser(Auth::user());
|
||||
// $ids = $this->discussions->getSubscribedIdsForUser(Auth::user());
|
||||
// $ids = $this->discussions->getPrivateIdsForUser(Auth::user());
|
||||
// // $user->permissions['discussion']['view'] = [1,2,3]
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// $search = $this->search->create();
|
||||
// $search->limitToIds($ids);
|
||||
// $search->setQuery($query);
|
||||
// $search->setSort($sort);
|
||||
// $search->setSortOrder($sortOrder);
|
||||
// $results = $search->results();
|
||||
|
||||
// process flag:unread here?
|
||||
|
||||
// parse the tokens.
|
||||
// run ID filters.
|
||||
|
||||
// TESTING lol
|
||||
$this->fulltext = reset($this->tokens);
|
||||
$posts = Post::whereRaw('MATCH (`content`) AGAINST (? IN BOOLEAN MODE)', [$this->fulltext])
|
||||
->orderByRaw('MATCH (`content`) AGAINST (?) DESC', [$this->fulltext]);
|
||||
|
||||
$posts = $posts->select('id', 'discussion_id');
|
||||
|
||||
$posts = $posts->get();
|
||||
|
||||
$ids = [];
|
||||
foreach ($posts as $post) {
|
||||
if (empty($ids[$post->discussion_id])) {
|
||||
$ids[$post->discussion_id] = [];
|
||||
}
|
||||
$ids[$post->discussion_id][] = $post->id;
|
||||
}
|
||||
|
||||
if ($this->fulltext and ! $this->sort) {
|
||||
$this->sort = 'relevance';
|
||||
}
|
||||
|
||||
if (! is_null($ids)) {
|
||||
$this->key = str_random();
|
||||
}
|
||||
|
||||
// run other tokens
|
||||
// $discussions->where('');
|
||||
}
|
||||
|
||||
if (! is_null($ids)) {
|
||||
Cache::put($this->getCacheKey(), $ids, 10); // recache
|
||||
$this->count = count($ids);
|
||||
|
||||
if (! $ids) {
|
||||
return [];
|
||||
}
|
||||
$query->whereIn('id', array_keys($ids));
|
||||
|
||||
// If we're sorting by relevance, assume that the IDs we've been provided
|
||||
// are already sorted by relevance. Therefore, we'll get discussions in
|
||||
// the order that they are in.
|
||||
if ($this->sort == 'relevance') {
|
||||
foreach ($ids as $id) {
|
||||
$query->orderBy(DB::raw('id != '.(int) $id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->sort)) {
|
||||
reset($this->sortMap);
|
||||
$this->sort = key($this->sortMap);
|
||||
}
|
||||
if (! empty($this->sortMap[$this->sort])) {
|
||||
list($column, $order) = $this->sortMap[$this->sort];
|
||||
$query->orderBy($column, $this->order ?: $order);
|
||||
}
|
||||
|
||||
if ($start > 0) {
|
||||
$query->skip($start);
|
||||
}
|
||||
if ($count > 0) {
|
||||
$query->take($count + 1);
|
||||
$results = $query->get();
|
||||
$this->areMoreResults = $results->count() > $count;
|
||||
if ($this->areMoreResults) {
|
||||
$results->pop();
|
||||
}
|
||||
} else {
|
||||
$results = $query->get();
|
||||
}
|
||||
|
||||
if (!empty($relevantPosts)) {
|
||||
$postIds = [];
|
||||
foreach ($ids as $id => &$posts) {
|
||||
$postIds = array_merge($postIds, array_slice($posts, 0, 2));
|
||||
}
|
||||
$posts = Post::with('user')->whereCan($this->user, 'view')->whereIn('id', $postIds)->get();
|
||||
|
||||
foreach ($results as $discussion) {
|
||||
$discussion->relevantPosts = $posts->filter(function ($post) use ($discussion) {
|
||||
return $post->discussion_id == $discussion->id;
|
||||
})
|
||||
->slice(0, 2)
|
||||
->each(function ($post) {
|
||||
$pos = strpos(strtolower($post->content), strtolower($this->fulltext));
|
||||
// TODO: make clipping more intelligent (full words only)
|
||||
$start = max(0, $pos - 50);
|
||||
$post->content = ($start > 0 ? '...' : '').str_limit(substr($post->content, $start), 300);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
43
src/Flarum/Core/Discussions/DiscussionRepository.php
Executable file
43
src/Flarum/Core/Discussions/DiscussionRepository.php
Executable file
@@ -0,0 +1,43 @@
|
||||
<?php namespace Flarum\Core\Discussions;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class DiscussionRepository
|
||||
{
|
||||
public function find($id)
|
||||
{
|
||||
return Discussion::find($id);
|
||||
}
|
||||
|
||||
public function findOrFail($id, User $user = null)
|
||||
{
|
||||
$query = Discussion::query();
|
||||
|
||||
if ($user !== null) {
|
||||
$query = $query->whereCanView($user);
|
||||
}
|
||||
|
||||
return $query->findOrFail($id);
|
||||
}
|
||||
|
||||
public function save(Discussion $discussion)
|
||||
{
|
||||
$discussion->assertValid();
|
||||
$discussion->save();
|
||||
}
|
||||
|
||||
public function delete(Discussion $discussion)
|
||||
{
|
||||
$discussion->delete();
|
||||
}
|
||||
|
||||
public function getState(Discussion $discussion, User $user)
|
||||
{
|
||||
return $discussion->stateFor($user);
|
||||
}
|
||||
|
||||
public function saveState(DiscussionState $state)
|
||||
{
|
||||
$state->save();
|
||||
}
|
||||
}
|
49
src/Flarum/Core/Discussions/DiscussionState.php
Normal file
49
src/Flarum/Core/Discussions/DiscussionState.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php namespace Flarum\Core\Discussions;
|
||||
|
||||
use Laracasts\Commander\Events\EventGenerator;
|
||||
|
||||
use Flarum\Core\Entity;
|
||||
|
||||
class DiscussionState extends Entity
|
||||
{
|
||||
use EventGenerator;
|
||||
|
||||
protected $table = 'users_discussions';
|
||||
|
||||
public function getDates()
|
||||
{
|
||||
return ['read_time'];
|
||||
}
|
||||
|
||||
public function discussion()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Discussions\Discussion', 'discussion_id');
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Users\User', 'user_id');
|
||||
}
|
||||
|
||||
public function read($number)
|
||||
{
|
||||
$this->read_number = $number; // only if it's greater than the old one
|
||||
$this->read_time = time();
|
||||
|
||||
$this->raise(new Events\DiscussionWasRead($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the keys for a save update query.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
protected function setKeysForSaveQuery(\Illuminate\Database\Eloquent\Builder $query)
|
||||
{
|
||||
$query->where('discussion_id', $this->discussion_id)
|
||||
->where('user_id', $this->user_id);
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
13
src/Flarum/Core/Discussions/Events/DiscussionWasDeleted.php
Normal file
13
src/Flarum/Core/Discussions/Events/DiscussionWasDeleted.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Discussions\Events;
|
||||
|
||||
use Flarum\Core\Discussions\Discussion;
|
||||
|
||||
class DiscussionWasDeleted
|
||||
{
|
||||
public $discussion;
|
||||
|
||||
public function __construct(Discussion $discussion)
|
||||
{
|
||||
$this->discussion = $discussion;
|
||||
}
|
||||
}
|
13
src/Flarum/Core/Discussions/Events/DiscussionWasRead.php
Normal file
13
src/Flarum/Core/Discussions/Events/DiscussionWasRead.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Discussions\Events;
|
||||
|
||||
use Flarum\Core\Discussions\DiscussionState;
|
||||
|
||||
class DiscussionWasRead
|
||||
{
|
||||
public $state;
|
||||
|
||||
public function __construct(DiscussionState $state)
|
||||
{
|
||||
$this->state = $state;
|
||||
}
|
||||
}
|
17
src/Flarum/Core/Discussions/Events/DiscussionWasRenamed.php
Normal file
17
src/Flarum/Core/Discussions/Events/DiscussionWasRenamed.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php namespace Flarum\Core\Discussions\Events;
|
||||
|
||||
use Flarum\Core\Discussions\Discussion;
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class DiscussionWasRenamed
|
||||
{
|
||||
public $discussion;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct(Discussion $discussion, User $user)
|
||||
{
|
||||
$this->discussion = $discussion;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
13
src/Flarum/Core/Discussions/Events/DiscussionWasStarted.php
Normal file
13
src/Flarum/Core/Discussions/Events/DiscussionWasStarted.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Discussions\Events;
|
||||
|
||||
use Flarum\Core\Discussions\Discussion;
|
||||
|
||||
class DiscussionWasStarted
|
||||
{
|
||||
public $discussion;
|
||||
|
||||
public function __construct(Discussion $discussion)
|
||||
{
|
||||
$this->discussion = $discussion;
|
||||
}
|
||||
}
|
86
src/Flarum/Core/Entity.php
Executable file
86
src/Flarum/Core/Entity.php
Executable file
@@ -0,0 +1,86 @@
|
||||
<?php namespace Flarum\Core;
|
||||
|
||||
use Illuminate\Validation\Validator;
|
||||
use Illuminate\Database\Eloquent\Model as Eloquent;
|
||||
use Flarum\Core\Support\Exceptions\ValidationFailureException;
|
||||
|
||||
class Entity extends Eloquent
|
||||
{
|
||||
protected static $rules = [];
|
||||
|
||||
protected static $messages = [];
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* Validator instance
|
||||
*
|
||||
* @var Illuminate\Validation\Validators
|
||||
*/
|
||||
protected $validator;
|
||||
|
||||
public function __construct(array $attributes = [], Validator $validator = null)
|
||||
{
|
||||
parent::__construct($attributes);
|
||||
|
||||
$this->validator = $validator ?: \App::make('validator');
|
||||
}
|
||||
|
||||
public function getConnection()
|
||||
{
|
||||
return static::resolveConnection('flarum');
|
||||
}
|
||||
|
||||
public function valid()
|
||||
{
|
||||
return $this->getValidator()->passes();
|
||||
}
|
||||
|
||||
public function assertValid()
|
||||
{
|
||||
$validation = $this->getValidator();
|
||||
|
||||
if ($validation->fails()) {
|
||||
$this->throwValidationException($validation->errors(), $validation->getData());
|
||||
}
|
||||
}
|
||||
|
||||
protected function getValidator()
|
||||
{
|
||||
$rules = $this->expandUniqueRules(static::$rules);
|
||||
|
||||
return $this->validator->make($this->attributes, $rules, static::$messages);
|
||||
}
|
||||
|
||||
protected function expandUniqueRules($rules)
|
||||
{
|
||||
foreach ($rules as $column => &$ruleset) {
|
||||
if (is_string($ruleset)) {
|
||||
$ruleset = explode('|', $ruleset);
|
||||
}
|
||||
foreach ($ruleset as &$rule) {
|
||||
if (strpos($rule, 'unique') === 0) {
|
||||
$parts = explode(':', $rule);
|
||||
$key = $this->getKey() ?: 'NULL';
|
||||
$rule = 'unique:'.$this->getTable().','.$column.','.$key.','.$this->getKeyName();
|
||||
if (! empty($parts[1])) {
|
||||
$wheres = explode(',', $parts[1]);
|
||||
foreach ($wheres as &$where) {
|
||||
$where .= ','.$this->$where;
|
||||
}
|
||||
$rule .= ','.implode(',', $wheres);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
protected function throwValidationException($errors, $input)
|
||||
{
|
||||
$exception = new ValidationFailureException;
|
||||
$exception->setErrors($errors)->setInput($input);
|
||||
throw $exception;
|
||||
}
|
||||
}
|
26
src/Flarum/Core/Forum.php
Executable file
26
src/Flarum/Core/Forum.php
Executable file
@@ -0,0 +1,26 @@
|
||||
<?php namespace Flarum\Core;
|
||||
|
||||
use Tobscure\Permissible\Permissible;
|
||||
|
||||
use Flarum\Core\Support\Exceptions\PermissionDeniedException;
|
||||
|
||||
class Forum extends Entity
|
||||
{
|
||||
use Permissible;
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::grant(function ($grant, $user, $permission) {
|
||||
return app('flarum.permissions')->granted($user, $permission, 'forum');
|
||||
});
|
||||
}
|
||||
|
||||
public function assertCan($user, $permission)
|
||||
{
|
||||
if (! $this->can($user, $permission)) {
|
||||
throw new PermissionDeniedException;
|
||||
}
|
||||
}
|
||||
}
|
18
src/Flarum/Core/Groups/Group.php
Executable file
18
src/Flarum/Core/Groups/Group.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php namespace Flarum\Core\Groups;
|
||||
|
||||
use Flarum\Core\Entity;
|
||||
|
||||
class Group extends Entity {
|
||||
|
||||
protected $table = 'groups';
|
||||
|
||||
const ADMINISTRATOR_ID = 1;
|
||||
const GUEST_ID = 2;
|
||||
const MEMBER_ID = 3;
|
||||
|
||||
public function users()
|
||||
{
|
||||
return $this->belongsToMany('Flarum\Core\Users\User', 'users_groups');
|
||||
}
|
||||
|
||||
}
|
15
src/Flarum/Core/Groups/GroupRepository.php
Executable file
15
src/Flarum/Core/Groups/GroupRepository.php
Executable file
@@ -0,0 +1,15 @@
|
||||
<?php namespace Flarum\Core\Repositories;
|
||||
|
||||
class GroupRepository
|
||||
{
|
||||
|
||||
public function save(Group $group)
|
||||
{
|
||||
$group->save();
|
||||
}
|
||||
|
||||
public function delete(Group $group)
|
||||
{
|
||||
$group->delete();
|
||||
}
|
||||
}
|
63
src/Flarum/Core/Listeners/DiscussionMetadataUpdater.php
Executable file
63
src/Flarum/Core/Listeners/DiscussionMetadataUpdater.php
Executable file
@@ -0,0 +1,63 @@
|
||||
<?php namespace Flarum\Core\Listeners;
|
||||
|
||||
use Laracasts\Commander\Events\EventListener;
|
||||
|
||||
use Flarum\Core\Discussions\DiscussionRepository;
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Core\Posts\Events\ReplyWasPosted;
|
||||
use Flarum\Core\Posts\Events\PostWasDeleted;
|
||||
use Flarum\Core\Posts\Events\PostWasHidden;
|
||||
use Flarum\Core\Posts\Events\PostWasRestored;
|
||||
|
||||
class DiscussionMetadataUpdater extends EventListener
|
||||
{
|
||||
protected $discussionRepo;
|
||||
|
||||
public function __construct(DiscussionRepository $discussionRepo)
|
||||
{
|
||||
$this->discussionRepo = $discussionRepo;
|
||||
}
|
||||
|
||||
public function whenReplyWasPosted(ReplyWasPosted $event)
|
||||
{
|
||||
$discussion = $this->discussionRepo->find($event->post->discussion_id);
|
||||
|
||||
$discussion->replies_count++;
|
||||
$discussion->setLastPost($event->post);
|
||||
|
||||
$this->discussionRepo->save($discussion);
|
||||
}
|
||||
|
||||
public function whenPostWasDeleted(PostWasDeleted $event)
|
||||
{
|
||||
$this->removePost($event->post);
|
||||
}
|
||||
|
||||
public function whenPostWasHidden(PostWasHidden $event)
|
||||
{
|
||||
$this->removePost($event->post);
|
||||
}
|
||||
|
||||
public function whenPostWasRestored(PostWasRestored $event)
|
||||
{
|
||||
$discussion = $this->discussionRepo->find($event->post->discussion_id);
|
||||
|
||||
$discussion->replies_count++;
|
||||
$discussion->refreshLastPost();
|
||||
|
||||
$this->discussionRepo->save($discussion);
|
||||
}
|
||||
|
||||
protected function removePost(Post $post)
|
||||
{
|
||||
$discussion = $this->discussionRepo->find($post->discussion_id);
|
||||
|
||||
$discussion->replies_count--;
|
||||
|
||||
if ($discussion->last_post_id == $post->id) {
|
||||
$discussion->refreshLastPost();
|
||||
}
|
||||
|
||||
$this->discussionRepo->save($discussion);
|
||||
}
|
||||
}
|
48
src/Flarum/Core/Listeners/PostFormatter.php
Executable file
48
src/Flarum/Core/Listeners/PostFormatter.php
Executable file
@@ -0,0 +1,48 @@
|
||||
<?php namespace Flarum\Core\Listeners;
|
||||
|
||||
use Laracasts\Commander\Events\EventListener;
|
||||
|
||||
use Flarum\Core\Posts\PostRepository;
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Core\Posts\Events\ReplyWasPosted;
|
||||
use Flarum\Core\Posts\Events\PostWasRevised;
|
||||
|
||||
class PostFormatter extends EventListener
|
||||
{
|
||||
protected $postRepo;
|
||||
|
||||
public function __construct(PostRepository $postRepo)
|
||||
{
|
||||
$this->postRepo = $postRepo;
|
||||
}
|
||||
|
||||
protected function formatPost($post)
|
||||
{
|
||||
$post = $this->postRepo->find($post->id);
|
||||
|
||||
// By default, we want to convert paragraphs of text into <p> tags.
|
||||
// And maybe also wrap URLs in <a> tags.
|
||||
// However, we want to allow plugins to completely override this, and/or
|
||||
// just do some superficial formatting afterwards.
|
||||
|
||||
$html = htmlspecialchars($post->content);
|
||||
|
||||
// Primary formatter
|
||||
$html = '<p>'.$html.'</p>'; // Move this to Flarum\Core\Support\Formatters\BasicFormatter < FormatterInterface
|
||||
|
||||
// Run additional formatters
|
||||
|
||||
$post->content_html = $html;
|
||||
$this->postRepo->save($post);
|
||||
}
|
||||
|
||||
public function whenReplyWasPosted(ReplyWasPosted $event)
|
||||
{
|
||||
$this->formatPost($event->post);
|
||||
}
|
||||
|
||||
public function whenPostWasRevised(PostWasRevised $event)
|
||||
{
|
||||
$this->formatPost($event->post);
|
||||
}
|
||||
}
|
28
src/Flarum/Core/Listeners/TitleChangePostCreator.php
Executable file
28
src/Flarum/Core/Listeners/TitleChangePostCreator.php
Executable file
@@ -0,0 +1,28 @@
|
||||
<?php namespace Flarum\Core\Listeners;
|
||||
|
||||
use Laracasts\Commander\Events\EventListener;
|
||||
|
||||
use Flarum\Core\Posts\PostRepository;
|
||||
use Flarum\Core\Posts\TitleChangePost;
|
||||
use Flarum\Core\Discussions\Events\DiscussionWasRenamed;
|
||||
|
||||
class TitleChangePostCreator extends EventListener
|
||||
{
|
||||
protected $postRepo;
|
||||
|
||||
public function __construct(PostRepository $postRepo)
|
||||
{
|
||||
$this->postRepo = $postRepo;
|
||||
}
|
||||
|
||||
public function whenDiscussionWasRenamed(DiscussionWasRenamed $event)
|
||||
{
|
||||
$post = TitleChangePost::reply(
|
||||
$event->discussion->id,
|
||||
$event->discussion->title,
|
||||
$event->user->id
|
||||
);
|
||||
|
||||
$this->postRepo->save($post);
|
||||
}
|
||||
}
|
70
src/Flarum/Core/Listeners/UserMetadataUpdater.php
Executable file
70
src/Flarum/Core/Listeners/UserMetadataUpdater.php
Executable file
@@ -0,0 +1,70 @@
|
||||
<?php namespace Flarum\Core\Listeners;
|
||||
|
||||
use Laracasts\Commander\Events\EventListener;
|
||||
|
||||
use Flarum\Core\Users\UserRepository;
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Core\Posts\Events\ReplyWasPosted;
|
||||
use Flarum\Core\Posts\Events\PostWasDeleted;
|
||||
use Flarum\Core\Posts\Events\PostWasHidden;
|
||||
use Flarum\Core\Posts\Events\PostWasRestored;
|
||||
use Flarum\Core\Discussions\Events\DiscussionWasStarted;
|
||||
use Flarum\Core\Discussions\Events\DiscussionWasDeleted;
|
||||
|
||||
class UserMetadataUpdater extends EventListener
|
||||
{
|
||||
protected $userRepo;
|
||||
|
||||
public function __construct(UserRepository $userRepo)
|
||||
{
|
||||
$this->userRepo = $userRepo;
|
||||
}
|
||||
|
||||
protected function updateRepliesCount($userId, $amount)
|
||||
{
|
||||
$user = $this->userRepo->find($userId);
|
||||
|
||||
$user->posts_count += $amount;
|
||||
|
||||
$this->userRepo->save($user);
|
||||
}
|
||||
|
||||
protected function updateDiscussionsCount($userId, $amount)
|
||||
{
|
||||
$user = $this->userRepo->find($userId);
|
||||
|
||||
$user->discussions_count += $amount;
|
||||
|
||||
$this->userRepo->save($user);
|
||||
}
|
||||
|
||||
public function whenReplyWasPosted(ReplyWasPosted $event)
|
||||
{
|
||||
$this->updateRepliesCount($event->post->user_id, 1);
|
||||
}
|
||||
|
||||
public function whenPostWasDeleted(PostWasDeleted $event)
|
||||
{
|
||||
$this->updateRepliesCount($event->post->user_id, -1);
|
||||
}
|
||||
|
||||
public function whenPostWasHidden(PostWasHidden $event)
|
||||
{
|
||||
$this->updateRepliesCount($event->post->user_id, -1);
|
||||
}
|
||||
|
||||
public function whenPostWasRestored(PostWasRestored $event)
|
||||
{
|
||||
$this->updateRepliesCount($event->post->user_id, 1);
|
||||
}
|
||||
|
||||
public function whenDiscussionWasStarted(DiscussionWasStarted $event)
|
||||
{
|
||||
$this->updateDiscussionsCount($event->discussion->start_user_id, 1);
|
||||
}
|
||||
|
||||
public function whenDiscussionWasDeleted(DiscussionWasDeleted $event)
|
||||
{
|
||||
$this->updateDiscussionsCount($event->discussion->start_user_id, -1);
|
||||
}
|
||||
}
|
42
src/Flarum/Core/Permissions/Manager.php
Executable file
42
src/Flarum/Core/Permissions/Manager.php
Executable file
@@ -0,0 +1,42 @@
|
||||
<?php namespace Flarum\Core\Permissions;
|
||||
|
||||
class Manager
|
||||
{
|
||||
protected $map;
|
||||
|
||||
protected $permissions;
|
||||
|
||||
public function __construct(PermissionRepository $permissions)
|
||||
{
|
||||
$this->permissions = $permissions;
|
||||
}
|
||||
|
||||
public function getMap()
|
||||
{
|
||||
if (is_null($this->map)) {
|
||||
$permissions = $this->permissions->get();
|
||||
foreach ($permissions as $permission) {
|
||||
$this->map[$permission->entity.'.'.$permission->permission][] = $permission->grantee;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->map;
|
||||
}
|
||||
|
||||
public function granted($user, $permission, $entity)
|
||||
{
|
||||
$grantees = $user->getGrantees();
|
||||
|
||||
// If user has admin, then yes!
|
||||
if (in_array('group.1', $grantees)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$permission = $entity.'.'.$permission;
|
||||
|
||||
$map = $this->getMap();
|
||||
$mappedGrantees = isset($map[$permission]) ? $map[$permission] : [];
|
||||
|
||||
return (bool) array_intersect($grantees, $mappedGrantees);
|
||||
}
|
||||
}
|
7
src/Flarum/Core/Permissions/Permission.php
Normal file
7
src/Flarum/Core/Permissions/Permission.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php namespace Flarum\Core\Permissions;
|
||||
|
||||
use Flarum\Core\Entity;
|
||||
|
||||
class Permission extends Entity
|
||||
{
|
||||
}
|
20
src/Flarum/Core/Permissions/PermissionRepository.php
Executable file
20
src/Flarum/Core/Permissions/PermissionRepository.php
Executable file
@@ -0,0 +1,20 @@
|
||||
<?php namespace Flarum\Core\Permissions;
|
||||
|
||||
class PermissionRepository
|
||||
{
|
||||
public function get()
|
||||
{
|
||||
return Permission::all();
|
||||
}
|
||||
|
||||
public function save(Permission $permission)
|
||||
{
|
||||
$permission->assertValid();
|
||||
$permission->save();
|
||||
}
|
||||
|
||||
public function delete(Permission $permission)
|
||||
{
|
||||
$permission->delete();
|
||||
}
|
||||
}
|
14
src/Flarum/Core/Posts/Commands/DeletePostCommand.php
Normal file
14
src/Flarum/Core/Posts/Commands/DeletePostCommand.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php namespace Flarum\Core\Posts\Commands;
|
||||
|
||||
class DeletePostCommand
|
||||
{
|
||||
public $postId;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($postId, $user)
|
||||
{
|
||||
$this->postId = $postId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
33
src/Flarum/Core/Posts/Commands/DeletePostCommandHandler.php
Normal file
33
src/Flarum/Core/Posts/Commands/DeletePostCommandHandler.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php namespace Flarum\Core\Posts\Commands;
|
||||
|
||||
use Flarum\Core\Posts\PostRepository;
|
||||
use Laracasts\Commander\CommandHandler;
|
||||
use Laracasts\Commander\Events\DispatchableTrait;
|
||||
use Event;
|
||||
|
||||
class DeletePostCommandHandler implements CommandHandler
|
||||
{
|
||||
use DispatchableTrait;
|
||||
|
||||
protected $posts;
|
||||
|
||||
public function __construct(PostRepository $posts)
|
||||
{
|
||||
$this->posts = $posts;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$user = $command->user;
|
||||
$post = $this->posts->findOrFail($command->postId, $user);
|
||||
|
||||
$post->assertCan($user, 'delete');
|
||||
|
||||
Event::fire('Flarum.Core.Posts.Commands.DeletePost.PostWillBeDeleted', [$post, $command]);
|
||||
|
||||
$this->posts->delete($post);
|
||||
$this->dispatchEventsFor($post);
|
||||
|
||||
return $post;
|
||||
}
|
||||
}
|
7
src/Flarum/Core/Posts/Commands/DeletePostValidator.php
Normal file
7
src/Flarum/Core/Posts/Commands/DeletePostValidator.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php namespace Flarum\Core\Posts\Commands;
|
||||
|
||||
use Flarum\Core\Support\CommandValidator;
|
||||
|
||||
class DeletePostValidator extends CommandValidator
|
||||
{
|
||||
}
|
18
src/Flarum/Core/Posts/Commands/EditPostCommand.php
Normal file
18
src/Flarum/Core/Posts/Commands/EditPostCommand.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php namespace Flarum\Core\Posts\Commands;
|
||||
|
||||
class EditPostCommand
|
||||
{
|
||||
public $postId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $content;
|
||||
|
||||
public $hidden;
|
||||
|
||||
public function __construct($postId, $user)
|
||||
{
|
||||
$this->postId = $postId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
44
src/Flarum/Core/Posts/Commands/EditPostCommandHandler.php
Normal file
44
src/Flarum/Core/Posts/Commands/EditPostCommandHandler.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php namespace Flarum\Core\Posts\Commands;
|
||||
|
||||
use Laracasts\Commander\CommandHandler;
|
||||
use Laracasts\Commander\Events\DispatchableTrait;
|
||||
use Event;
|
||||
|
||||
use Flarum\Core\Posts\PostRepository;
|
||||
|
||||
class EditPostCommandHandler implements CommandHandler
|
||||
{
|
||||
use DispatchableTrait;
|
||||
|
||||
protected $posts;
|
||||
|
||||
public function __construct(PostRepository $posts)
|
||||
{
|
||||
$this->posts = $posts;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$user = $command->user;
|
||||
$post = $this->posts->findOrFail($command->postId, $user);
|
||||
|
||||
$post->assertCan($user, 'edit');
|
||||
|
||||
if (isset($command->content)) {
|
||||
$post->revise($command->content, $user);
|
||||
}
|
||||
|
||||
if ($command->hidden === true) {
|
||||
$post->hide($user);
|
||||
} elseif ($command->hidden === false) {
|
||||
$post->restore($user);
|
||||
}
|
||||
|
||||
Event::fire('Flarum.Core.Posts.Commands.EditPost.PostWillBeSaved', [$post, $command]);
|
||||
|
||||
$this->posts->save($post);
|
||||
$this->dispatchEventsFor($post);
|
||||
|
||||
return $post;
|
||||
}
|
||||
}
|
7
src/Flarum/Core/Posts/Commands/EditPostValidator.php
Normal file
7
src/Flarum/Core/Posts/Commands/EditPostValidator.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php namespace Flarum\Core\Posts\Commands;
|
||||
|
||||
use Flarum\Core\Support\CommandValidator;
|
||||
|
||||
class EditPostValidator extends CommandValidator
|
||||
{
|
||||
}
|
17
src/Flarum/Core/Posts/Commands/PostReplyCommand.php
Normal file
17
src/Flarum/Core/Posts/Commands/PostReplyCommand.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php namespace Flarum\Core\Posts\Commands;
|
||||
|
||||
class PostReplyCommand
|
||||
{
|
||||
public $discussionId;
|
||||
|
||||
public $content;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($discussionId, $content, $user)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->content = $content;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
53
src/Flarum/Core/Posts/Commands/PostReplyCommandHandler.php
Normal file
53
src/Flarum/Core/Posts/Commands/PostReplyCommandHandler.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php namespace Flarum\Core\Posts\Commands;
|
||||
|
||||
use Flarum\Core\Discussions\DiscussionRepository;
|
||||
use Flarum\Core\Posts\CommentPost;
|
||||
use Flarum\Core\Posts\PostRepository;
|
||||
use Laracasts\Commander\CommandHandler;
|
||||
use Laracasts\Commander\Events\DispatchableTrait;
|
||||
use Event;
|
||||
|
||||
class PostReplyCommandHandler implements CommandHandler
|
||||
{
|
||||
use DispatchableTrait;
|
||||
|
||||
protected $discussions;
|
||||
|
||||
protected $posts;
|
||||
|
||||
public function __construct(DiscussionRepository $discussions, PostRepository $posts)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
$this->posts = $posts;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$user = $command->user;
|
||||
|
||||
// Make sure the user has permission to reply to this discussion. First,
|
||||
// make sure the discussion exists and that the user has permission to
|
||||
// view it; if not, fail with a ModelNotFound exception so we don't give
|
||||
// away the existence of the discussion. If the user is allowed to view
|
||||
// it, check if they have permission to reply.
|
||||
$discussion = $this->discussions->findOrFail($command->discussionId, $user);
|
||||
|
||||
$discussion->assertCan($user, 'reply');
|
||||
|
||||
// Create a new Post entity, persist it, and dispatch domain events.
|
||||
// Before persistance, though, fire an event to give plugins an
|
||||
// opportunity to alter the post entity based on data in the command.
|
||||
$post = CommentPost::reply(
|
||||
$command->discussionId,
|
||||
$command->content,
|
||||
$user->id
|
||||
);
|
||||
|
||||
Event::fire('Flarum.Core.Posts.Commands.PostReply.PostWillBeSaved', [$post, $command]);
|
||||
|
||||
$this->posts->save($post);
|
||||
$this->dispatchEventsFor($post);
|
||||
|
||||
return $post;
|
||||
}
|
||||
}
|
7
src/Flarum/Core/Posts/Commands/PostReplyValidator.php
Normal file
7
src/Flarum/Core/Posts/Commands/PostReplyValidator.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php namespace Flarum\Core\Posts\Commands;
|
||||
|
||||
use Flarum\Core\Support\CommandValidator;
|
||||
|
||||
class PostReplyValidator extends CommandValidator
|
||||
{
|
||||
}
|
63
src/Flarum/Core/Posts/CommentPost.php
Executable file
63
src/Flarum/Core/Posts/CommentPost.php
Executable file
@@ -0,0 +1,63 @@
|
||||
<?php namespace Flarum\Core\Posts;
|
||||
|
||||
use Laracasts\Commander\Events\EventGenerator;
|
||||
use Tobscure\Permissible\Permissible;
|
||||
|
||||
use Flarum\Core\Entity;
|
||||
use Flarum\Core\Permission;
|
||||
use Flarum\Core\Support\Exceptions\PermissionDeniedException;
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class CommentPost extends Post
|
||||
{
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::saving(function ($post) {
|
||||
$post->number = $post->discussion->number_index++;
|
||||
$post->discussion->save();
|
||||
});
|
||||
}
|
||||
|
||||
public static function reply($discussionId, $content, $userId)
|
||||
{
|
||||
$post = new static;
|
||||
|
||||
$post->content = $content;
|
||||
$post->time = time();
|
||||
$post->discussion_id = $discussionId;
|
||||
$post->user_id = $userId;
|
||||
$post->type = 'comment';
|
||||
|
||||
$post->raise(new Events\ReplyWasPosted($post));
|
||||
|
||||
return $post;
|
||||
}
|
||||
|
||||
public function revise($content, $user)
|
||||
{
|
||||
$this->content = $content;
|
||||
|
||||
$this->edit_time = time();
|
||||
$this->edit_user_id = $user->id;
|
||||
|
||||
$this->raise(new Events\PostWasRevised($this));
|
||||
}
|
||||
|
||||
public function hide($user)
|
||||
{
|
||||
$this->delete_time = time();
|
||||
$this->delete_user_id = $user->id;
|
||||
|
||||
$this->raise(new Events\PostWasHidden($this));
|
||||
}
|
||||
|
||||
public function restore($user)
|
||||
{
|
||||
$this->delete_time = null;
|
||||
$this->delete_user_id = null;
|
||||
|
||||
$this->raise(new Events\PostWasRestored($this));
|
||||
}
|
||||
}
|
13
src/Flarum/Core/Posts/Events/PostWasDeleted.php
Normal file
13
src/Flarum/Core/Posts/Events/PostWasDeleted.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Posts\Events;
|
||||
|
||||
use Flarum\Core\Posts\Post;
|
||||
|
||||
class PostWasDeleted
|
||||
{
|
||||
public $post;
|
||||
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
}
|
13
src/Flarum/Core/Posts/Events/PostWasHidden.php
Normal file
13
src/Flarum/Core/Posts/Events/PostWasHidden.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Posts\Events;
|
||||
|
||||
use Flarum\Core\Posts\Post;
|
||||
|
||||
class PostWasHidden
|
||||
{
|
||||
public $post;
|
||||
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
}
|
13
src/Flarum/Core/Posts/Events/PostWasRestored.php
Normal file
13
src/Flarum/Core/Posts/Events/PostWasRestored.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Posts\Events;
|
||||
|
||||
use Flarum\Core\Posts\Post;
|
||||
|
||||
class PostWasRestored
|
||||
{
|
||||
public $post;
|
||||
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
}
|
13
src/Flarum/Core/Posts/Events/PostWasRevised.php
Normal file
13
src/Flarum/Core/Posts/Events/PostWasRevised.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Posts\Events;
|
||||
|
||||
use Flarum\Core\Posts\Post;
|
||||
|
||||
class PostWasRevised
|
||||
{
|
||||
public $post;
|
||||
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
}
|
13
src/Flarum/Core/Posts/Events/ReplyWasPosted.php
Normal file
13
src/Flarum/Core/Posts/Events/ReplyWasPosted.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Posts\Events;
|
||||
|
||||
use Flarum\Core\Posts\Post;
|
||||
|
||||
class ReplyWasPosted
|
||||
{
|
||||
public $post;
|
||||
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
}
|
113
src/Flarum/Core/Posts/Post.php
Executable file
113
src/Flarum/Core/Posts/Post.php
Executable file
@@ -0,0 +1,113 @@
|
||||
<?php namespace Flarum\Core\Posts;
|
||||
|
||||
use Laracasts\Commander\Events\EventGenerator;
|
||||
use Tobscure\Permissible\Permissible;
|
||||
|
||||
use Flarum\Core\Entity;
|
||||
use Flarum\Core\Permission;
|
||||
use Flarum\Core\Support\Exceptions\PermissionDeniedException;
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class Post extends Entity
|
||||
{
|
||||
use EventGenerator;
|
||||
use Permissible;
|
||||
|
||||
protected $table = 'posts';
|
||||
|
||||
protected static $rules = [
|
||||
'discussion_id' => 'required|integer',
|
||||
'time' => 'required|date',
|
||||
'content' => 'required',
|
||||
'number' => 'integer',
|
||||
'user_id' => 'integer',
|
||||
'edit_time' => 'date',
|
||||
'edit_user_id' => 'integer',
|
||||
'delete_time' => 'date',
|
||||
'delete_user_id' => 'integer',
|
||||
];
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::grant(function ($grant, $user, $permission) {
|
||||
return app('flarum.permissions')->granted($user, $permission, 'post');
|
||||
});
|
||||
|
||||
// Grant view access to a post only if the user can also view the
|
||||
// discussion which the post is in. Also, the if the post is hidden,
|
||||
// the user must have edit permissions too.
|
||||
static::grant('view', function ($grant, $user) {
|
||||
$grant->whereCan('view', 'discussion');
|
||||
});
|
||||
|
||||
static::check('view', function ($check, $user) {
|
||||
$check->whereNull('delete_user_id')
|
||||
->orWhereCan('edit');
|
||||
});
|
||||
|
||||
// Allow a user to edit their own post, unless it has been hidden by
|
||||
// someone else.
|
||||
static::grant('edit', function ($grant, $user) {
|
||||
$grant->whereCan('editOwn')
|
||||
->where('user_id', $user->id);
|
||||
});
|
||||
|
||||
static::check('editOwn', function ($check, $user) {
|
||||
$check->whereNull('delete_user_id')
|
||||
->orWhere('delete_user_id', $user->id);
|
||||
});
|
||||
|
||||
static::deleted(function ($post) {
|
||||
$post->raise(new Events\PostWasDeleted($post));
|
||||
});
|
||||
}
|
||||
|
||||
public function discussion()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Discussions\Discussion', 'discussion_id');
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Users\User', 'user_id');
|
||||
}
|
||||
|
||||
public function editUser()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Users\User', 'edit_user_id');
|
||||
}
|
||||
|
||||
public function deleteUser()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Users\User', 'delete_user_id');
|
||||
}
|
||||
|
||||
public function getDates()
|
||||
{
|
||||
return ['time', 'edit_time', 'delete_time'];
|
||||
}
|
||||
|
||||
// Terminates the query and returns an array of matching IDs.
|
||||
// Example usage: $discussion->posts()->ids();
|
||||
public function scopeIds($query)
|
||||
{
|
||||
return array_map('intval', $query->get(['id'])->fetch('id')->all());
|
||||
}
|
||||
|
||||
public function scopeWhereCanView($query, $user = null)
|
||||
{
|
||||
if (is_null($user)) {
|
||||
$user = User::current();
|
||||
}
|
||||
return $this->scopeWhereCan($query, $user, 'view');
|
||||
}
|
||||
|
||||
public function assertCan($user, $permission)
|
||||
{
|
||||
if (! $this->can($user, $permission)) {
|
||||
throw new PermissionDeniedException;
|
||||
}
|
||||
}
|
||||
}
|
31
src/Flarum/Core/Posts/PostRepository.php
Executable file
31
src/Flarum/Core/Posts/PostRepository.php
Executable file
@@ -0,0 +1,31 @@
|
||||
<?php namespace Flarum\Core\Posts;
|
||||
|
||||
class PostRepository
|
||||
{
|
||||
public function find($id)
|
||||
{
|
||||
return Post::find($id);
|
||||
}
|
||||
|
||||
public function findOrFail($id, $user = null)
|
||||
{
|
||||
$query = Post::query();
|
||||
|
||||
if ($user !== null) {
|
||||
$query = $query->whereCanView($user);
|
||||
}
|
||||
|
||||
return $query->findOrFail($id);
|
||||
}
|
||||
|
||||
public function save(Post $post)
|
||||
{
|
||||
$post->assertValid();
|
||||
$post->save();
|
||||
}
|
||||
|
||||
public function delete(Post $post)
|
||||
{
|
||||
$post->delete();
|
||||
}
|
||||
}
|
25
src/Flarum/Core/Posts/TitleChangePost.php
Executable file
25
src/Flarum/Core/Posts/TitleChangePost.php
Executable file
@@ -0,0 +1,25 @@
|
||||
<?php namespace Flarum\Core\Posts;
|
||||
|
||||
use Laracasts\Commander\Events\EventGenerator;
|
||||
use Tobscure\Permissible\Permissible;
|
||||
|
||||
use Flarum\Core\Entity;
|
||||
use Flarum\Core\Permission;
|
||||
use Flarum\Core\Support\Exceptions\PermissionDeniedException;
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class TitleChangePost extends Post
|
||||
{
|
||||
public static function reply($discussionId, $content, $userId)
|
||||
{
|
||||
$post = new static;
|
||||
|
||||
$post->content = $content;
|
||||
$post->time = time();
|
||||
$post->discussion_id = $discussionId;
|
||||
$post->user_id = $userId;
|
||||
$post->type = 'titleChange';
|
||||
|
||||
return $post;
|
||||
}
|
||||
}
|
48
src/Flarum/Core/Search/FulltextSearchDriver.php
Normal file
48
src/Flarum/Core/Search/FulltextSearchDriver.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php namespace Flarum\Core\Search;
|
||||
|
||||
use Illuminate\Database\Query;
|
||||
|
||||
use Flarum\Core\Search\ConditionCollection;
|
||||
use Flarum\Core\Search\ConditionNegate;
|
||||
use Flarum\Core\Search\ConditionOr;
|
||||
use Flarum\Core\Search\Conditions\ConditionComparison;
|
||||
use Flarum\Core\Search\Conditions\ConditionNull;
|
||||
|
||||
class FulltextSearchDriver implements SearchDriverInterface {
|
||||
|
||||
protected $table;
|
||||
|
||||
public function __construct($table)
|
||||
{
|
||||
$this->table = $table;
|
||||
// inject db connection?
|
||||
// pass primary key name?
|
||||
}
|
||||
|
||||
public function results(SearchCriteria $criteria)
|
||||
{
|
||||
$query = DB::table($this->table);
|
||||
|
||||
$this->parseConditions($criteria->conditions, $query);
|
||||
|
||||
return $query->get('id');
|
||||
}
|
||||
|
||||
protected function parseConditions(ConditionCollection $conditions, Query $query)
|
||||
{
|
||||
foreach ($conditions as $condition)
|
||||
{
|
||||
if ($condition instanceof ConditionOr)
|
||||
{
|
||||
$query->orWhere(function($query)
|
||||
{
|
||||
$this->parseConditions($condition->conditions, $query);
|
||||
})
|
||||
}
|
||||
elseif ($condition instanceof ConditionComparison)
|
||||
{
|
||||
// etc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
src/Flarum/Core/Search/SearchDriverInterface.php
Normal file
8
src/Flarum/Core/Search/SearchDriverInterface.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php namespace Flarum\Core\Search;
|
||||
|
||||
interface SearchDriverInterface {
|
||||
|
||||
// returns an array of matching conversation IDs
|
||||
public function results(SearchCriteria $criteria);
|
||||
|
||||
}
|
34
src/Flarum/Core/Search/SphinxSearchDriver.php
Normal file
34
src/Flarum/Core/Search/SphinxSearchDriver.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php namespace Flarum\Core\Search;
|
||||
|
||||
use Sphinx\SphinxClient;
|
||||
|
||||
use Flarum\Core\Search\ConditionCollection;
|
||||
use Flarum\Core\Search\ConditionNegate;
|
||||
use Flarum\Core\Search\ConditionOr;
|
||||
use Flarum\Core\Search\Conditions\ConditionComparison;
|
||||
use Flarum\Core\Search\Conditions\ConditionNull;
|
||||
|
||||
class SphinxSearchDriver implements SearchDriverInterface {
|
||||
|
||||
protected $client;
|
||||
|
||||
public function __construct(SphinxClient $client, $index)
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->index = $index;
|
||||
}
|
||||
|
||||
public function results(SearchCriteria $criteria)
|
||||
{
|
||||
foreach ($query->conditions as $condition)
|
||||
{
|
||||
if ($condition instanceof ConditionOr)
|
||||
{
|
||||
// $search->setSelect("*, IF(code = 1 OR productid = 2, 1,0) AS filter");
|
||||
// $->setFilter('filter',array(1));
|
||||
}
|
||||
}
|
||||
|
||||
// etc
|
||||
}
|
||||
}
|
17
src/Flarum/Core/Search/Tokenizer.php
Normal file
17
src/Flarum/Core/Search/Tokenizer.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php namespace Flarum\Core\Search;
|
||||
|
||||
class Tokenizer {
|
||||
|
||||
protected $query;
|
||||
|
||||
public function __construct($query)
|
||||
{
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
public function tokenize()
|
||||
{
|
||||
return $this->query ? [$this->query] : [];
|
||||
}
|
||||
|
||||
}
|
25
src/Flarum/Core/Search/Tokens/AuthorToken.php
Normal file
25
src/Flarum/Core/Search/Tokens/AuthorToken.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php namespace Flarum\Core\Search\Tokens;
|
||||
|
||||
class AuthorToken extends TokenAbstract
|
||||
{
|
||||
/**
|
||||
* The token's regex pattern.
|
||||
* @var string
|
||||
*/
|
||||
protected $pattern = 'author:(\d+)';
|
||||
|
||||
public function matches()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function action()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
27
src/Flarum/Core/Search/Tokens/TokenAbstract.php
Normal file
27
src/Flarum/Core/Search/Tokens/TokenAbstract.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php namespace Flarum\Core\Search\Tokens;
|
||||
|
||||
abstract class TokenAbstract {
|
||||
|
||||
protected $pattern;
|
||||
|
||||
public function getPattern()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function matches()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function action()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
12
src/Flarum/Core/Search/Tokens/TokenInterface.php
Normal file
12
src/Flarum/Core/Search/Tokens/TokenInterface.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php namespace Flarum\Core\Search\Tokens;
|
||||
|
||||
interface TokenInterface
|
||||
{
|
||||
public function getPattern();
|
||||
|
||||
public function matches();
|
||||
|
||||
public function action();
|
||||
|
||||
public function serialize();
|
||||
}
|
44
src/Flarum/Core/Support/CommandValidator.php
Normal file
44
src/Flarum/Core/Support/CommandValidator.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php namespace Flarum\Core\Support;
|
||||
|
||||
use Illuminate\Validation\Factory;
|
||||
use Flarum\Core\Support\Exceptions\ValidationFailureException;
|
||||
use Event;
|
||||
|
||||
class CommandValidator
|
||||
{
|
||||
protected $rules = [];
|
||||
|
||||
protected $validator;
|
||||
|
||||
public function __construct(Factory $validator)
|
||||
{
|
||||
$this->validator = $validator;
|
||||
}
|
||||
|
||||
public function validate($command)
|
||||
{
|
||||
if (! $command->user) {
|
||||
throw new InvalidArgumentException('Empty argument [user] in command ['.get_class($command).']');
|
||||
}
|
||||
|
||||
$validator = $this->validator->make(get_object_vars($command), $this->rules);
|
||||
|
||||
$this->fireValidationEvent([$validator, $command]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
$this->throwValidationException($validator->errors(), $validator->getData());
|
||||
}
|
||||
}
|
||||
|
||||
protected function fireValidationEvent(array $arguments)
|
||||
{
|
||||
Event::fire(str_replace('\\', '.', get_class($this)), $arguments);
|
||||
}
|
||||
|
||||
protected function throwValidationException($errors, $input)
|
||||
{
|
||||
$exception = new ValidationFailureException;
|
||||
$exception->setErrors($errors)->setInput($input);
|
||||
throw $exception;
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<?php namespace Flarum\Core\Support\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class PermissionDeniedException extends Exception
|
||||
{
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
<?php namespace Flarum\Core\Support\Exceptions;
|
||||
|
||||
use Illuminate\Support\MessageBag;
|
||||
|
||||
class ValidationFailureException extends \InvalidArgumentException
|
||||
{
|
||||
protected $errors;
|
||||
|
||||
protected $input = array();
|
||||
|
||||
public function __construct($message = '', $code = 0, Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->errors = new MessageBag;
|
||||
}
|
||||
|
||||
public function setErrors(MessageBag $errors)
|
||||
{
|
||||
$this->errors = $errors;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
public function setInput(array $input)
|
||||
{
|
||||
$this->input = $input;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInput()
|
||||
{
|
||||
return $this->input;
|
||||
}
|
||||
}
|
6
src/Flarum/Core/Support/Extensions/Extension.php
Executable file
6
src/Flarum/Core/Support/Extensions/Extension.php
Executable file
@@ -0,0 +1,6 @@
|
||||
<?php namespace Flarum\Core\Support\Extensions;
|
||||
|
||||
class Extension
|
||||
{
|
||||
|
||||
}
|
6
src/Flarum/Core/Support/Extensions/Manager.php
Executable file
6
src/Flarum/Core/Support/Extensions/Manager.php
Executable file
@@ -0,0 +1,6 @@
|
||||
<?php namespace Flarum\Core\Support\Extensions\Manager;
|
||||
|
||||
class Manager
|
||||
{
|
||||
|
||||
}
|
14
src/Flarum/Core/Users/Commands/DeleteUserCommand.php
Normal file
14
src/Flarum/Core/Users/Commands/DeleteUserCommand.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php namespace Flarum\Core\Users\Commands;
|
||||
|
||||
class DeleteUserCommand
|
||||
{
|
||||
public $userId;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($userId, $user)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
33
src/Flarum/Core/Users/Commands/DeleteUserCommandHandler.php
Normal file
33
src/Flarum/Core/Users/Commands/DeleteUserCommandHandler.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php namespace Flarum\Core\Users\Commands;
|
||||
|
||||
use Flarum\Core\Users\UserRepository;
|
||||
use Laracasts\Commander\CommandHandler;
|
||||
use Laracasts\Commander\Events\DispatchableTrait;
|
||||
use Event;
|
||||
|
||||
class DeleteUserCommandHandler implements CommandHandler
|
||||
{
|
||||
use DispatchableTrait;
|
||||
|
||||
protected $userRepo;
|
||||
|
||||
public function __construct(UserRepository $userRepo)
|
||||
{
|
||||
$this->userRepo = $userRepo;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$user = $command->user;
|
||||
$userToDelete = $this->userRepo->findOrFail($command->userId, $user);
|
||||
|
||||
$userToDelete->assertCan($user, 'delete');
|
||||
|
||||
Event::fire('Flarum.Core.Users.Commands.DeleteUser.UserWillBeDeleted', [$userToDelete, $command]);
|
||||
|
||||
$this->userRepo->delete($userToDelete);
|
||||
$this->dispatchEventsFor($userToDelete);
|
||||
|
||||
return $userToDelete;
|
||||
}
|
||||
}
|
7
src/Flarum/Core/Users/Commands/DeleteUserValidator.php
Normal file
7
src/Flarum/Core/Users/Commands/DeleteUserValidator.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php namespace Flarum\Core\Users\Commands;
|
||||
|
||||
use Flarum\Core\Support\CommandValidator;
|
||||
|
||||
class DeleteUserValidator extends CommandValidator
|
||||
{
|
||||
}
|
20
src/Flarum/Core/Users/Commands/EditUserCommand.php
Normal file
20
src/Flarum/Core/Users/Commands/EditUserCommand.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php namespace Flarum\Core\Users\Commands;
|
||||
|
||||
class EditUserCommand
|
||||
{
|
||||
public $userId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $username;
|
||||
|
||||
public $email;
|
||||
|
||||
public $password;
|
||||
|
||||
public function __construct($userId, $user)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
46
src/Flarum/Core/Users/Commands/EditUserCommandHandler.php
Normal file
46
src/Flarum/Core/Users/Commands/EditUserCommandHandler.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php namespace Flarum\Core\Users\Commands;
|
||||
|
||||
use Laracasts\Commander\CommandHandler;
|
||||
use Laracasts\Commander\Events\DispatchableTrait;
|
||||
use Event;
|
||||
|
||||
use Flarum\Core\Users\UserRepository;
|
||||
|
||||
class EditUserCommandHandler implements CommandHandler
|
||||
{
|
||||
use DispatchableTrait;
|
||||
|
||||
protected $userRepo;
|
||||
|
||||
public function __construct(UserRepository $userRepo)
|
||||
{
|
||||
$this->userRepo = $userRepo;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$user = $command->user;
|
||||
$userToEdit = $this->userRepo->findOrFail($command->userId, $user);
|
||||
|
||||
$userToEdit->assertCan($user, 'edit');
|
||||
|
||||
if (isset($command->username)) {
|
||||
$userToEdit->username = $command->username;
|
||||
}
|
||||
|
||||
if (isset($command->email)) {
|
||||
$userToEdit->email = $command->email;
|
||||
}
|
||||
|
||||
if (isset($command->password)) {
|
||||
$userToEdit->password = $command->password;
|
||||
}
|
||||
|
||||
Event::fire('Flarum.Core.Users.Commands.EditUser.UserWillBeSaved', [$userToEdit, $command]);
|
||||
|
||||
$this->userRepo->save($userToEdit);
|
||||
$this->dispatchEventsFor($userToEdit);
|
||||
|
||||
return $userToEdit;
|
||||
}
|
||||
}
|
7
src/Flarum/Core/Users/Commands/EditUserValidator.php
Normal file
7
src/Flarum/Core/Users/Commands/EditUserValidator.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php namespace Flarum\Core\Users\Commands;
|
||||
|
||||
use Flarum\Core\Support\CommandValidator;
|
||||
|
||||
class EditUserValidator extends CommandValidator
|
||||
{
|
||||
}
|
20
src/Flarum/Core/Users/Commands/RegisterUserCommand.php
Normal file
20
src/Flarum/Core/Users/Commands/RegisterUserCommand.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php namespace Flarum\Core\Users\Commands;
|
||||
|
||||
class RegisterUserCommand
|
||||
{
|
||||
public $user;
|
||||
|
||||
public $username;
|
||||
|
||||
public $email;
|
||||
|
||||
public $password;
|
||||
|
||||
public function __construct($username, $email, $password, $user)
|
||||
{
|
||||
$this->username = $username;
|
||||
$this->email = $email;
|
||||
$this->password = $password;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
<?php namespace Flarum\Core\Users\Commands;
|
||||
|
||||
use Flarum\Core\Forum;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Core\Users\UserRepository;
|
||||
use Laracasts\Commander\CommandHandler;
|
||||
use Laracasts\Commander\Events\DispatchableTrait;
|
||||
use Event;
|
||||
|
||||
class RegisterUserCommandHandler implements CommandHandler
|
||||
{
|
||||
use DispatchableTrait;
|
||||
|
||||
protected $forum;
|
||||
|
||||
protected $userRepo;
|
||||
|
||||
public function __construct(Forum $forum, UserRepository $userRepo)
|
||||
{
|
||||
$this->forum = $forum;
|
||||
$this->userRepo = $userRepo;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
// Assert the the current user has permission to create a user. In the
|
||||
// case of a guest trying to register an account, this will depend on
|
||||
// whether or not registration is open. If the user is an admin, though,
|
||||
// it will be allowed.
|
||||
$this->forum->assertCan($command->user, 'register');
|
||||
|
||||
// Create a new User entity, persist it, and dispatch domain events.
|
||||
// Before persistance, though, fire an event to give plugins an
|
||||
// opportunity to alter the post entity based on data in the command.
|
||||
$user = User::register(
|
||||
$command->username,
|
||||
$command->email,
|
||||
$command->password
|
||||
);
|
||||
|
||||
Event::fire('Flarum.Core.Users.Commands.RegisterUser.UserWillBeSaved', [$user, $command]);
|
||||
|
||||
$this->userRepo->save($user);
|
||||
$this->userRepo->syncGroups($user, [3]); // default groups
|
||||
$this->dispatchEventsFor($user);
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user