mirror of
https://github.com/flarum/core.git
synced 2025-08-01 22:20:21 +02:00
Upgrade to L5 + huge refactor + more. closes #2
New stuff: - Signup + email confirmation. - Updated authentication strategy with remember cookies. closes #5 - New search system with some example gambits! This is cool - check out the source. Fulltext drivers will be implemented as decorators overriding the EloquentPostRepository’s findByContent method. - Lay down the foundation for bootstrapping the Ember app. - Update Web layer’s asset manager to properly publish CSS/JS files. - Console commands to run installation migrations and seeds. Refactoring: - New structure: move models, repositories, commands, and events into their own namespaces, rather than grouping by entity. - All events are classes. - Use L5 middleware and command bus implementations. - Clearer use of repositories and the Active Record pattern. Repositories are used only for retrieval of ActiveRecord objects, and then save/delete operations are called directly on those ActiveRecords. This way, we don’t over-abstract at the cost of Eloquent magic, but testing is still easy. - Refactor of Web layer so that it uses the Actions routing architecture. - “Actor” concept instead of depending on Laravel’s Auth. - General cleanup!
This commit is contained in:
14
src/Core/Commands/ConfirmEmailCommand.php
Normal file
14
src/Core/Commands/ConfirmEmailCommand.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class ConfirmEmailCommand
|
||||
{
|
||||
public $userId;
|
||||
|
||||
public $token;
|
||||
|
||||
public function __construct($userId, $token)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
$this->token = $token;
|
||||
}
|
||||
}
|
14
src/Core/Commands/DeleteDiscussionCommand.php
Normal file
14
src/Core/Commands/DeleteDiscussionCommand.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class DeleteDiscussionCommand
|
||||
{
|
||||
public $discussionId;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($discussionId, $user)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
14
src/Core/Commands/DeletePostCommand.php
Normal file
14
src/Core/Commands/DeletePostCommand.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class DeletePostCommand
|
||||
{
|
||||
public $postId;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($postId, $user)
|
||||
{
|
||||
$this->postId = $postId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
14
src/Core/Commands/DeleteUserCommand.php
Normal file
14
src/Core/Commands/DeleteUserCommand.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class DeleteUserCommand
|
||||
{
|
||||
public $userId;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($userId, $user)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
16
src/Core/Commands/EditDiscussionCommand.php
Normal file
16
src/Core/Commands/EditDiscussionCommand.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class EditDiscussionCommand
|
||||
{
|
||||
public $discussionId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $title;
|
||||
|
||||
public function __construct($discussionId, $user)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
18
src/Core/Commands/EditPostCommand.php
Normal file
18
src/Core/Commands/EditPostCommand.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class EditPostCommand
|
||||
{
|
||||
public $postId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $content;
|
||||
|
||||
public $isHidden;
|
||||
|
||||
public function __construct($postId, $user)
|
||||
{
|
||||
$this->postId = $postId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
22
src/Core/Commands/EditUserCommand.php
Normal file
22
src/Core/Commands/EditUserCommand.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class EditUserCommand
|
||||
{
|
||||
public $userId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $username;
|
||||
|
||||
public $email;
|
||||
|
||||
public $password;
|
||||
|
||||
public $readTime;
|
||||
|
||||
public function __construct($userId, $user)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
11
src/Core/Commands/GenerateAccessTokenCommand.php
Normal file
11
src/Core/Commands/GenerateAccessTokenCommand.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class GenerateAccessTokenCommand
|
||||
{
|
||||
public $userId;
|
||||
|
||||
public function __construct($userId)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
}
|
||||
}
|
17
src/Core/Commands/PostReplyCommand.php
Normal file
17
src/Core/Commands/PostReplyCommand.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class PostReplyCommand
|
||||
{
|
||||
public $discussionId;
|
||||
|
||||
public $content;
|
||||
|
||||
public $user;
|
||||
|
||||
public function __construct($discussionId, $content, $user)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->content = $content;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
17
src/Core/Commands/ReadDiscussionCommand.php
Normal file
17
src/Core/Commands/ReadDiscussionCommand.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class ReadDiscussionCommand
|
||||
{
|
||||
public $discussionId;
|
||||
|
||||
public $user;
|
||||
|
||||
public $readNumber;
|
||||
|
||||
public function __construct($discussionId, $user, $readNumber)
|
||||
{
|
||||
$this->discussionId = $discussionId;
|
||||
$this->user = $user;
|
||||
$this->readNumber = $readNumber;
|
||||
}
|
||||
}
|
23
src/Core/Commands/RegisterUserCommand.php
Normal file
23
src/Core/Commands/RegisterUserCommand.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class RegisterUserCommand
|
||||
{
|
||||
public $forum;
|
||||
|
||||
public $user;
|
||||
|
||||
public $username;
|
||||
|
||||
public $email;
|
||||
|
||||
public $password;
|
||||
|
||||
public function __construct($username, $email, $password, $user, $forum)
|
||||
{
|
||||
$this->username = $username;
|
||||
$this->email = $email;
|
||||
$this->password = $password;
|
||||
$this->user = $user;
|
||||
$this->forum = $forum;
|
||||
}
|
||||
}
|
20
src/Core/Commands/StartDiscussionCommand.php
Normal file
20
src/Core/Commands/StartDiscussionCommand.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php namespace Flarum\Core\Commands;
|
||||
|
||||
class StartDiscussionCommand
|
||||
{
|
||||
public $title;
|
||||
|
||||
public $content;
|
||||
|
||||
public $user;
|
||||
|
||||
public $forum;
|
||||
|
||||
public function __construct($title, $content, $user, $forum)
|
||||
{
|
||||
$this->title = $title;
|
||||
$this->content = $content;
|
||||
$this->user = $user;
|
||||
$this->forum = $forum;
|
||||
}
|
||||
}
|
179
src/Core/CoreServiceProvider.php
Normal file
179
src/Core/CoreServiceProvider.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php namespace Flarum\Core;
|
||||
|
||||
use Illuminate\Bus\Dispatcher as Bus;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Flarum\Core\Formatter\FormatterManager;
|
||||
use Flarum\Core\Models\CommentPost;
|
||||
use Flarum\Core\Models\Post;
|
||||
use Flarum\Core\Models\Model;
|
||||
use Flarum\Core\Models\Forum;
|
||||
use Flarum\Core\Models\User;
|
||||
use Flarum\Core\Models\Discussion;
|
||||
use Flarum\Core\Search\GambitManager;
|
||||
|
||||
class CoreServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application events.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot(Dispatcher $events, Bus $bus)
|
||||
{
|
||||
$this->loadViewsFrom(__DIR__.'../../views', 'flarum');
|
||||
|
||||
$this->registerEventHandlers($events);
|
||||
$this->registerPostTypes();
|
||||
$this->registerPermissions();
|
||||
$this->registerGambits();
|
||||
$this->setupModels();
|
||||
|
||||
$bus->mapUsing(function ($command) {
|
||||
return Bus::simpleMapping(
|
||||
$command, 'Flarum\Core\Commands', 'Flarum\Core\Handlers\Commands'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// Register a singleton entity that represents this forum. This entity
|
||||
// will be used to check for global forum permissions (like viewing the
|
||||
// forum, registering, and starting discussions.)
|
||||
$this->app->singleton('flarum.forum', 'Flarum\Core\Models\Forum');
|
||||
|
||||
// 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');
|
||||
|
||||
$this->app->bind('flarum.discussionFinder', 'Flarum\Core\Discussions\DiscussionFinder');
|
||||
|
||||
$this->app->singleton('flarum.formatter', function () {
|
||||
$formatter = new FormatterManager($this->app);
|
||||
$formatter->add('basic', 'Flarum\Core\Formatter\BasicFormatter');
|
||||
return $formatter;
|
||||
});
|
||||
|
||||
$this->app->bind(
|
||||
'Flarum\Core\Repositories\DiscussionRepositoryInterface',
|
||||
'Flarum\Core\Repositories\EloquentDiscussionRepository'
|
||||
);
|
||||
$this->app->bind(
|
||||
'Flarum\Core\Repositories\PostRepositoryInterface',
|
||||
'Flarum\Core\Repositories\EloquentPostRepository'
|
||||
);
|
||||
$this->app->bind(
|
||||
'Flarum\Core\Repositories\UserRepositoryInterface',
|
||||
'Flarum\Core\Repositories\EloquentUserRepository'
|
||||
);
|
||||
}
|
||||
|
||||
public function registerGambits()
|
||||
{
|
||||
$this->app->bind('Flarum\Core\Search\GambitManager', function () {
|
||||
$gambits = new GambitManager($this->app);
|
||||
$gambits->add('Flarum\Core\Search\Discussions\Gambits\AuthorGambit');
|
||||
$gambits->add('Flarum\Core\Search\Discussions\Gambits\UnreadGambit');
|
||||
$gambits->setFulltextGambit('Flarum\Core\Search\Discussions\Gambits\FulltextGambit');
|
||||
return $gambits;
|
||||
});
|
||||
}
|
||||
|
||||
public function registerPostTypes()
|
||||
{
|
||||
Post::addType('comment', 'Flarum\Core\Models\CommentPost');
|
||||
Post::addType('renamed', 'Flarum\Core\Models\RenamedPost');
|
||||
|
||||
CommentPost::setFormatter($this->app['flarum.formatter']);
|
||||
}
|
||||
|
||||
public function registerEventHandlers($events)
|
||||
{
|
||||
$events->subscribe('Flarum\Core\Handlers\Events\DiscussionMetadataUpdater');
|
||||
$events->subscribe('Flarum\Core\Handlers\Events\UserMetadataUpdater');
|
||||
$events->subscribe('Flarum\Core\Handlers\Events\RenamedPostCreator');
|
||||
$events->subscribe('Flarum\Core\Handlers\Events\EmailConfirmationMailer');
|
||||
}
|
||||
|
||||
public function setupModels()
|
||||
{
|
||||
Model::setForum($this->app['flarum.forum']);
|
||||
Model::setValidator($this->app['validator']);
|
||||
|
||||
User::setHasher($this->app['hash']);
|
||||
}
|
||||
|
||||
public function registerPermissions()
|
||||
{
|
||||
Forum::grantPermission(function ($grant, $user, $permission) {
|
||||
return $user->hasPermission($permission, 'forum');
|
||||
});
|
||||
|
||||
Post::grantPermission(function ($grant, $user, $permission) {
|
||||
return $user->hasPermission($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.
|
||||
Post::grantPermission('view', function ($grant) {
|
||||
$grant->whereCan('view', 'discussion');
|
||||
});
|
||||
|
||||
Post::demandPermission('view', function ($demand) {
|
||||
$demand->whereNull('hide_user_id')
|
||||
->orWhereCan('edit');
|
||||
});
|
||||
|
||||
// Allow a user to edit their own post, unless it has been hidden by
|
||||
// someone else.
|
||||
Post::grantPermission('edit', function ($grant, $user) {
|
||||
$grant->whereCan('editOwn')
|
||||
->where('user_id', $user->id);
|
||||
});
|
||||
|
||||
Post::demandPermission('editOwn', function ($demand, $user) {
|
||||
$demand->whereNull('hide_user_id');
|
||||
if ($user) {
|
||||
$demand->orWhere('hide_user_id', $user->id);
|
||||
}
|
||||
});
|
||||
|
||||
User::grantPermission(function ($grant, $user, $permission) {
|
||||
return $user->hasPermission($permission, 'forum');
|
||||
});
|
||||
|
||||
// Grant view access to a user if the user can view the forum.
|
||||
User::grantPermission('view', function ($grant, $user) {
|
||||
$grant->whereCan('view', 'forum');
|
||||
});
|
||||
|
||||
// Allow a user to edit their own account.
|
||||
User::grantPermission('edit', function ($grant, $user) {
|
||||
$grant->where('id', $user->id);
|
||||
});
|
||||
|
||||
Discussion::grantPermission(function ($grant, $user, $permission) {
|
||||
return $user->hasPermission($permission, 'discussion');
|
||||
});
|
||||
|
||||
// Grant view access to a discussion if the user can view the forum.
|
||||
Discussion::grantPermission('view', function ($grant, $user) {
|
||||
$grant->whereCan('view', 'forum');
|
||||
});
|
||||
|
||||
// Allow a user to edit their own discussion.
|
||||
Discussion::grantPermission('edit', function ($grant, $user) {
|
||||
if ($user->hasPermission('editOwn', 'discussion')) {
|
||||
$grant->where('start_user_id', $user->id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
16
src/Core/Events/DiscussionStateWillBeSaved.php
Normal file
16
src/Core/Events/DiscussionStateWillBeSaved.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\DiscussionState;
|
||||
|
||||
class DiscussionStateWillBeSaved
|
||||
{
|
||||
public $state;
|
||||
|
||||
public $command;
|
||||
|
||||
public function __construct(DiscussionState $state, $command)
|
||||
{
|
||||
$this->state = $state;
|
||||
$this->command = $command;
|
||||
}
|
||||
}
|
13
src/Core/Events/DiscussionWasDeleted.php
Normal file
13
src/Core/Events/DiscussionWasDeleted.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\Discussion;
|
||||
|
||||
class DiscussionWasDeleted
|
||||
{
|
||||
public $discussion;
|
||||
|
||||
public function __construct(Discussion $discussion)
|
||||
{
|
||||
$this->discussion = $discussion;
|
||||
}
|
||||
}
|
13
src/Core/Events/DiscussionWasRead.php
Normal file
13
src/Core/Events/DiscussionWasRead.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\DiscussionState;
|
||||
|
||||
class DiscussionWasRead
|
||||
{
|
||||
public $state;
|
||||
|
||||
public function __construct(DiscussionState $state)
|
||||
{
|
||||
$this->state = $state;
|
||||
}
|
||||
}
|
20
src/Core/Events/DiscussionWasRenamed.php
Normal file
20
src/Core/Events/DiscussionWasRenamed.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\Discussion;
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class DiscussionWasRenamed
|
||||
{
|
||||
public $discussion;
|
||||
|
||||
public $user;
|
||||
|
||||
public $oldTitle;
|
||||
|
||||
public function __construct(Discussion $discussion, User $user, $oldTitle)
|
||||
{
|
||||
$this->discussion = $discussion;
|
||||
$this->user = $user;
|
||||
$this->oldTitle = $oldTitle;
|
||||
}
|
||||
}
|
13
src/Core/Events/DiscussionWasStarted.php
Normal file
13
src/Core/Events/DiscussionWasStarted.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\Discussion;
|
||||
|
||||
class DiscussionWasStarted
|
||||
{
|
||||
public $discussion;
|
||||
|
||||
public function __construct(Discussion $discussion)
|
||||
{
|
||||
$this->discussion = $discussion;
|
||||
}
|
||||
}
|
16
src/Core/Events/DiscussionWillBeDeleted.php
Normal file
16
src/Core/Events/DiscussionWillBeDeleted.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\Discussion;
|
||||
|
||||
class DiscussionWillBeDeleted
|
||||
{
|
||||
public $discussion;
|
||||
|
||||
public $command;
|
||||
|
||||
public function __construct(Discussion $discussion, $command)
|
||||
{
|
||||
$this->discussion = $discussion;
|
||||
$this->command = $command;
|
||||
}
|
||||
}
|
16
src/Core/Events/DiscussionWillBeSaved.php
Normal file
16
src/Core/Events/DiscussionWillBeSaved.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\Discussion;
|
||||
|
||||
class DiscussionWillBeSaved
|
||||
{
|
||||
public $discussion;
|
||||
|
||||
public $command;
|
||||
|
||||
public function __construct(Discussion $discussion, $command)
|
||||
{
|
||||
$this->discussion = $discussion;
|
||||
$this->command = $command;
|
||||
}
|
||||
}
|
13
src/Core/Events/PostWasDeleted.php
Normal file
13
src/Core/Events/PostWasDeleted.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\Post;
|
||||
|
||||
class PostWasDeleted
|
||||
{
|
||||
public $post;
|
||||
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
}
|
13
src/Core/Events/PostWasHidden.php
Normal file
13
src/Core/Events/PostWasHidden.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\Post;
|
||||
|
||||
class PostWasHidden
|
||||
{
|
||||
public $post;
|
||||
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
}
|
13
src/Core/Events/PostWasPosted.php
Normal file
13
src/Core/Events/PostWasPosted.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\Post;
|
||||
|
||||
class PostWasPosted
|
||||
{
|
||||
public $post;
|
||||
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
}
|
13
src/Core/Events/PostWasRestored.php
Normal file
13
src/Core/Events/PostWasRestored.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\Post;
|
||||
|
||||
class PostWasRestored
|
||||
{
|
||||
public $post;
|
||||
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
}
|
13
src/Core/Events/PostWasRevised.php
Normal file
13
src/Core/Events/PostWasRevised.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\Post;
|
||||
|
||||
class PostWasRevised
|
||||
{
|
||||
public $post;
|
||||
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
}
|
16
src/Core/Events/PostWillBeDeleted.php
Normal file
16
src/Core/Events/PostWillBeDeleted.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\Post;
|
||||
|
||||
class PostWillBeDeleted
|
||||
{
|
||||
public $post;
|
||||
|
||||
public $command;
|
||||
|
||||
public function __construct(Post $post, $command)
|
||||
{
|
||||
$this->post = $post;
|
||||
$this->command = $command;
|
||||
}
|
||||
}
|
16
src/Core/Events/PostWillBeSaved.php
Normal file
16
src/Core/Events/PostWillBeSaved.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\Post;
|
||||
|
||||
class PostWillBeSaved
|
||||
{
|
||||
public $post;
|
||||
|
||||
public $command;
|
||||
|
||||
public function __construct(Post $post, $command)
|
||||
{
|
||||
$this->post = $post;
|
||||
$this->command = $command;
|
||||
}
|
||||
}
|
13
src/Core/Events/UserEmailWasChanged.php
Normal file
13
src/Core/Events/UserEmailWasChanged.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class EmailWasChanged
|
||||
{
|
||||
public $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
13
src/Core/Events/UserEmailWasConfirmed.php
Normal file
13
src/Core/Events/UserEmailWasConfirmed.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class EmailWasConfirmed
|
||||
{
|
||||
public $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
13
src/Core/Events/UserPasswordWasChanged.php
Normal file
13
src/Core/Events/UserPasswordWasChanged.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class PasswordWasChanged
|
||||
{
|
||||
public $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
13
src/Core/Events/UserWasActivated.php
Normal file
13
src/Core/Events/UserWasActivated.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class UserWasActivated
|
||||
{
|
||||
public $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
13
src/Core/Events/UserWasDeleted.php
Normal file
13
src/Core/Events/UserWasDeleted.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class UserWasDeleted
|
||||
{
|
||||
public $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
13
src/Core/Events/UserWasRegistered.php
Normal file
13
src/Core/Events/UserWasRegistered.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class UserWasRegistered
|
||||
{
|
||||
public $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
13
src/Core/Events/UserWasRenamed.php
Normal file
13
src/Core/Events/UserWasRenamed.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class UserWasRenamed
|
||||
{
|
||||
public $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
16
src/Core/Events/UserWillBeDeleted.php
Normal file
16
src/Core/Events/UserWillBeDeleted.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class UserWillBeDeleted
|
||||
{
|
||||
public $user;
|
||||
|
||||
public $command;
|
||||
|
||||
public function __construct(User $user, $command)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->command = $command;
|
||||
}
|
||||
}
|
16
src/Core/Events/UserWillBeSaved.php
Normal file
16
src/Core/Events/UserWillBeSaved.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php namespace Flarum\Core\Events;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class UserWillBeSaved
|
||||
{
|
||||
public $user;
|
||||
|
||||
public $command;
|
||||
|
||||
public function __construct(User $user, $command)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->command = $command;
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<?php namespace Flarum\Core\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidConfirmationTokenException extends Exception
|
||||
{
|
||||
}
|
7
src/Core/Exceptions/PermissionDeniedException.php
Normal file
7
src/Core/Exceptions/PermissionDeniedException.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php namespace Flarum\Core\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class PermissionDeniedException extends Exception
|
||||
{
|
||||
}
|
40
src/Core/Exceptions/ValidationFailureException.php
Normal file
40
src/Core/Exceptions/ValidationFailureException.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php namespace Flarum\Core\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/Core/Extensions/Extension.php
Executable file
6
src/Core/Extensions/Extension.php
Executable file
@@ -0,0 +1,6 @@
|
||||
<?php namespace Flarum\Core\Extensions;
|
||||
|
||||
class Extension
|
||||
{
|
||||
|
||||
}
|
6
src/Core/Extensions/ExtensionManager.php
Executable file
6
src/Core/Extensions/ExtensionManager.php
Executable file
@@ -0,0 +1,6 @@
|
||||
<?php namespace Flarum\Core\Extensions;
|
||||
|
||||
class ExtensionManager
|
||||
{
|
||||
|
||||
}
|
25
src/Core/Formatter/BasicFormatter.php
Normal file
25
src/Core/Formatter/BasicFormatter.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php namespace Flarum\Core\Formatter;
|
||||
|
||||
use Misd\Linkify\Linkify;
|
||||
|
||||
class BasicFormatter
|
||||
{
|
||||
public function format($text)
|
||||
{
|
||||
$text = htmlspecialchars($text);
|
||||
|
||||
$linkify = new Linkify;
|
||||
$text = $linkify->process($text, ['attr' => ['target' => '_blank']]);
|
||||
|
||||
$text = preg_replace_callback('/(?:^ *[-*]\s*([^\n]*)(?:\n|$)){2,}/m', function ($matches) {
|
||||
return '</p><ul>'.preg_replace('/^ *[-*]\s*([^\n]*)(?:\n|$)/m', '<li>$1</li>', trim($matches[0])).'</ul><p>';
|
||||
}, $text);
|
||||
|
||||
$text = '<p>'.preg_replace(['/[\n]{2,}/', '/\n/'], ['</p><p>', '<br>'], trim($text)).'</p>';
|
||||
|
||||
$text = preg_replace(array("/<p>\s*<\/p>/i", "/(?<=<p>)\s*(?:<br>)*/i", "/\s*(?:<br>)*\s*(?=<\/p>)/i"), "", $text);
|
||||
$text = str_replace("<p></p>", "", $text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
67
src/Core/Formatter/FormatterManager.php
Normal file
67
src/Core/Formatter/FormatterManager.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php namespace Flarum\Core\Formatter;
|
||||
|
||||
use Illuminate\Container\Container;
|
||||
|
||||
class FormatterManager
|
||||
{
|
||||
protected $formatters = [];
|
||||
|
||||
/**
|
||||
* The IoC container instance.
|
||||
*
|
||||
* @var \Illuminate\Container\Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Create a new formatter manager instance.
|
||||
*
|
||||
* @param \Illuminate\Container\Container $container
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Container $container = null)
|
||||
{
|
||||
$this->container = $container ?: new Container;
|
||||
}
|
||||
|
||||
public function add($name, $formatter, $priority = 0)
|
||||
{
|
||||
$this->remove($name);
|
||||
|
||||
if (is_string($formatter)) {
|
||||
$container = $this->container;
|
||||
$formatter = function () use ($container, $formatter) {
|
||||
$callable = array($container->make($formatter), 'format');
|
||||
$data = func_get_args();
|
||||
return call_user_func_array($callable, $data);
|
||||
};
|
||||
}
|
||||
|
||||
$this->formatters[$name] = [$formatter, $priority];
|
||||
}
|
||||
|
||||
public function remove($name)
|
||||
{
|
||||
unset($this->formatters[$name]);
|
||||
}
|
||||
|
||||
public function format($text)
|
||||
{
|
||||
$sorted = [];
|
||||
|
||||
foreach ($this->formatters as $array) {
|
||||
list($formatter, $priority) = $array;
|
||||
$sorted[$priority][] = $formatter;
|
||||
}
|
||||
|
||||
ksort($sorted);
|
||||
|
||||
foreach ($sorted as $formatters) {
|
||||
foreach ($formatters as $formatter) {
|
||||
$text = $formatter($text);
|
||||
}
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
36
src/Core/Handlers/Commands/ConfirmEmailCommandHandler.php
Normal file
36
src/Core/Handlers/Commands/ConfirmEmailCommandHandler.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
|
||||
use Flarum\Core\Repositories\UserRepositoryInterface as UserRepository;
|
||||
use Flarum\Core\Events\UserWillBeSaved;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class ConfirmEmailCommandHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
protected $users;
|
||||
|
||||
public function __construct(UserRepository $users)
|
||||
{
|
||||
$this->users = $users;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$user = $this->users->findOrFail($command->userId);
|
||||
|
||||
$user->assertConfirmationTokenValid($command->token);
|
||||
$user->confirmEmail();
|
||||
|
||||
if (! $user->is_activated) {
|
||||
$user->activate();
|
||||
}
|
||||
|
||||
event(new UserWillBeSaved($user, $command));
|
||||
|
||||
$user->save();
|
||||
$this->dispatchEventsFor($user);
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
|
||||
use Flarum\Core\Repositories\DiscussionRepositoryInterface as DiscussionRepository;
|
||||
use Flarum\Core\Events\DiscussionWillBeDeleted;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class DeleteDiscussionCommandHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
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(new DiscussionWillBeDeleted($discussion, $command));
|
||||
|
||||
$discussion->delete();
|
||||
$this->dispatchEventsFor($discussion);
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
}
|
32
src/Core/Handlers/Commands/DeletePostCommandHandler.php
Normal file
32
src/Core/Handlers/Commands/DeletePostCommandHandler.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
|
||||
use Flarum\Core\Repositories\PostRepositoryInterface as PostRepository;
|
||||
use Flarum\Core\Events\PostWillBeDeleted;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class DeletePostCommandHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
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(new PostWillBeDeleted($post, $command));
|
||||
|
||||
$post->delete();
|
||||
$this->dispatchEventsFor($post);
|
||||
|
||||
return $post;
|
||||
}
|
||||
}
|
32
src/Core/Handlers/Commands/DeleteUserCommandHandler.php
Normal file
32
src/Core/Handlers/Commands/DeleteUserCommandHandler.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
|
||||
use Flarum\Core\Repositories\UserRepositoryInterface as UserRepository;
|
||||
use Flarum\Core\Events\UserWillBeDeleted;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class DeleteUserCommandHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
protected $users;
|
||||
|
||||
public function __construct(UserRepository $users)
|
||||
{
|
||||
$this->users = $users;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$user = $command->user;
|
||||
$userToDelete = $this->users->findOrFail($command->userId, $user);
|
||||
|
||||
$userToDelete->assertCan($user, 'delete');
|
||||
|
||||
event(new UserWillBeDeleted($userToDelete, $command));
|
||||
|
||||
$userToDelete->delete();
|
||||
$this->dispatchEventsFor($userToDelete);
|
||||
|
||||
return $userToDelete;
|
||||
}
|
||||
}
|
36
src/Core/Handlers/Commands/EditDiscussionCommandHandler.php
Normal file
36
src/Core/Handlers/Commands/EditDiscussionCommandHandler.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
|
||||
use Flarum\Core\Repositories\DiscussionRepositoryInterface as DiscussionRepository;
|
||||
use Flarum\Core\Events\DiscussionWillBeSaved;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class EditDiscussionCommandHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
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(new DiscussionWillBeSaved($discussion, $command));
|
||||
|
||||
$discussion->save();
|
||||
$this->dispatchEventsFor($discussion);
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
}
|
42
src/Core/Handlers/Commands/EditPostCommandHandler.php
Normal file
42
src/Core/Handlers/Commands/EditPostCommandHandler.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
|
||||
use Flarum\Core\Repositories\PostRepositoryInterface as PostRepository;
|
||||
use Flarum\Core\Events\PostWillBeSaved;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class EditPostCommandHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
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->isHidden === true) {
|
||||
$post->hide($user);
|
||||
} elseif ($command->isHidden === false) {
|
||||
$post->restore($user);
|
||||
}
|
||||
|
||||
event(new PostWillBeSaved($post, $command));
|
||||
|
||||
$post->save();
|
||||
$this->dispatchEventsFor($post);
|
||||
|
||||
return $post;
|
||||
}
|
||||
}
|
43
src/Core/Handlers/Commands/EditUserCommandHandler.php
Normal file
43
src/Core/Handlers/Commands/EditUserCommandHandler.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
|
||||
use Flarum\Core\Repositories\UserRepositoryInterface as UserRepository;
|
||||
|
||||
class EditUserCommandHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
protected $users;
|
||||
|
||||
public function __construct(UserRepository $users)
|
||||
{
|
||||
$this->users = $users;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$user = $command->user;
|
||||
$userToEdit = $this->users->findOrFail($command->userId, $user);
|
||||
|
||||
$userToEdit->assertCan($user, 'edit');
|
||||
|
||||
if (isset($command->username)) {
|
||||
$userToEdit->rename($command->username);
|
||||
}
|
||||
if (isset($command->email)) {
|
||||
$userToEdit->changeEmail($command->email);
|
||||
}
|
||||
if (isset($command->password)) {
|
||||
$userToEdit->changePassword($command->password);
|
||||
}
|
||||
if (! empty($command->readTime)) {
|
||||
$userToEdit->markAllAsRead();
|
||||
}
|
||||
|
||||
event(new UserWillBeSaved($userToEdit, $command));
|
||||
|
||||
$userToEdit->save();
|
||||
$this->dispatchEventsFor($userToEdit);
|
||||
|
||||
return $userToEdit;
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
|
||||
use Flarum\Core\Models\AccessToken;
|
||||
|
||||
class GenerateAccessTokenCommandHandler
|
||||
{
|
||||
public function handle($command)
|
||||
{
|
||||
$token = AccessToken::generate($command->userId);
|
||||
$token->save();
|
||||
|
||||
return $token;
|
||||
}
|
||||
}
|
48
src/Core/Handlers/Commands/PostReplyCommandHandler.php
Normal file
48
src/Core/Handlers/Commands/PostReplyCommandHandler.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
|
||||
use Flarum\Core\Events\PostWillBeSaved;
|
||||
use Flarum\Core\Repositories\DiscussionRepositoryInterface as DiscussionRepository;
|
||||
use Flarum\Core\Models\CommentPost;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class PostReplyCommandHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
protected $discussions;
|
||||
|
||||
public function __construct(DiscussionRepository $discussions)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
}
|
||||
|
||||
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(new PostWillBeSaved($post, $command));
|
||||
|
||||
$post->save();
|
||||
$this->dispatchEventsFor($post);
|
||||
|
||||
return $post;
|
||||
}
|
||||
}
|
39
src/Core/Handlers/Commands/ReadDiscussionCommandHandler.php
Normal file
39
src/Core/Handlers/Commands/ReadDiscussionCommandHandler.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
|
||||
use Flarum\Core\Repositories\DiscussionRepositoryInterface as DiscussionRepository;
|
||||
use Flarum\Core\Events\DiscussionStateWillBeSaved;
|
||||
use Flarum\Core\Exceptions\PermissionDeniedException;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class ReadDiscussionCommandHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
protected $discussions;
|
||||
|
||||
public function __construct(DiscussionRepository $discussions)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$user = $command->user;
|
||||
|
||||
if (! $user->exists) {
|
||||
throw new PermissionDeniedException;
|
||||
}
|
||||
|
||||
$discussion = $this->discussions->findOrFail($command->discussionId, $user);
|
||||
|
||||
$state = $discussion->stateFor($user);
|
||||
$state->read($command->readNumber);
|
||||
|
||||
event(new DiscussionStateWillBeSaved($state, $command));
|
||||
|
||||
$state->save();
|
||||
$this->dispatchEventsFor($state);
|
||||
|
||||
return $state;
|
||||
}
|
||||
}
|
33
src/Core/Handlers/Commands/RegisterUserCommandHandler.php
Normal file
33
src/Core/Handlers/Commands/RegisterUserCommandHandler.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class RegisterUserCommandHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
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.
|
||||
$command->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(new UserWillBeSaved($user, $command));
|
||||
|
||||
$user->save();
|
||||
$this->dispatchEventsFor($user);
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
53
src/Core/Handlers/Commands/StartDiscussionCommandHandler.php
Normal file
53
src/Core/Handlers/Commands/StartDiscussionCommandHandler.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php namespace Flarum\Core\Handlers\Commands;
|
||||
|
||||
use Illuminate\Contracts\Bus\Dispatcher;
|
||||
use Flarum\Core\Models\Discussion;
|
||||
use Flarum\Core\Events\DiscussionWillBeSaved;
|
||||
use Flarum\Core\Commands\PostReplyCommand;
|
||||
use Flarum\Core\Support\DispatchesEvents;
|
||||
|
||||
class StartDiscussionCommandHandler
|
||||
{
|
||||
use DispatchesEvents;
|
||||
|
||||
protected $bus;
|
||||
|
||||
public function __construct(Dispatcher $bus)
|
||||
{
|
||||
$this->bus = $bus;
|
||||
}
|
||||
|
||||
public function handle($command)
|
||||
{
|
||||
$command->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(new DiscussionWillBeSaved($discussion, $command));
|
||||
|
||||
$discussion->save();
|
||||
|
||||
// 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 (PostWasPosted), we may need to reconsider someday.
|
||||
$post = $this->bus->dispatch(
|
||||
new PostReplyCommand($discussion->id, $command->content, $command->user)
|
||||
);
|
||||
|
||||
// The discussion may have been updated by the PostReplyCommand; we need
|
||||
// to refresh its data.
|
||||
$discussion = $post->discussion;
|
||||
|
||||
$this->dispatchEventsFor($discussion);
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
}
|
65
src/Core/Handlers/Events/DiscussionMetadataUpdater.php
Executable file
65
src/Core/Handlers/Events/DiscussionMetadataUpdater.php
Executable file
@@ -0,0 +1,65 @@
|
||||
<?php namespace Flarum\Core\Handlers\Events;
|
||||
|
||||
use Flarum\Core\Models\Post;
|
||||
use Flarum\Core\Events\PostWasPosted;
|
||||
use Flarum\Core\Events\PostWasDeleted;
|
||||
use Flarum\Core\Events\PostWasHidden;
|
||||
use Flarum\Core\Events\PostWasRestored;
|
||||
|
||||
class DiscussionMetadataUpdater
|
||||
{
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
*
|
||||
* @param Illuminate\Events\Dispatcher $events
|
||||
* @return array
|
||||
*/
|
||||
public function subscribe($events)
|
||||
{
|
||||
$events->listen('Flarum\Core\Events\PostWasPosted', __CLASS__.'@whenPostWasPosted');
|
||||
$events->listen('Flarum\Core\Events\PostWasDeleted', __CLASS__.'@whenPostWasDeleted');
|
||||
$events->listen('Flarum\Core\Events\PostWasHidden', __CLASS__.'@whenPostWasHidden');
|
||||
$events->listen('Flarum\Core\Events\PostWasRestored', __CLASS__.'@whenPostWasRestored');
|
||||
}
|
||||
|
||||
public function whenPostWasPosted(PostWasPosted $event)
|
||||
{
|
||||
$discussion = $event->post->discussion;
|
||||
|
||||
$discussion->comments_count++;
|
||||
$discussion->setLastPost($event->post);
|
||||
$discussion->save();
|
||||
}
|
||||
|
||||
public function whenPostWasDeleted(PostWasDeleted $event)
|
||||
{
|
||||
$this->removePost($event->post);
|
||||
}
|
||||
|
||||
public function whenPostWasHidden(PostWasHidden $event)
|
||||
{
|
||||
$this->removePost($event->post);
|
||||
}
|
||||
|
||||
public function whenPostWasRestored(PostWasRestored $event)
|
||||
{
|
||||
$discussion = $event->post->discussion;
|
||||
|
||||
$discussion->refreshCommentsCount();
|
||||
$discussion->refreshLastPost();
|
||||
$discussion->save();
|
||||
}
|
||||
|
||||
protected function removePost(Post $post)
|
||||
{
|
||||
$discussion = $post->discussion;
|
||||
|
||||
$discussion->refreshCommentsCount();
|
||||
|
||||
if ($discussion->last_post_id == $post->id) {
|
||||
$discussion->refreshLastPost();
|
||||
}
|
||||
|
||||
$discussion->save();
|
||||
}
|
||||
}
|
49
src/Core/Handlers/Events/EmailConfirmationMailer.php
Executable file
49
src/Core/Handlers/Events/EmailConfirmationMailer.php
Executable file
@@ -0,0 +1,49 @@
|
||||
<?php namespace Flarum\Core\Handlers\Events;
|
||||
|
||||
use Illuminate\Mail\Mailer;
|
||||
use Flarum\Core\Events\UserWasRegistered;
|
||||
use Flarum\Core\Events\EmailWasChanged;
|
||||
|
||||
class EmailConfirmationMailer
|
||||
{
|
||||
protected $mailer;
|
||||
|
||||
public function __construct(Mailer $mailer)
|
||||
{
|
||||
$this->mailer = $mailer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
*
|
||||
* @param Illuminate\Events\Dispatcher $events
|
||||
* @return array
|
||||
*/
|
||||
public function subscribe($events)
|
||||
{
|
||||
$events->listen('Flarum\Core\Events\UserWasRegistered', __CLASS__.'@whenUserWasRegistered');
|
||||
$events->listen('Flarum\Core\Events\EmailWasChanged', __CLASS__.'@whenEmailWasChanged');
|
||||
}
|
||||
|
||||
public function whenUserWasRegistered(UserWasRegistered $event)
|
||||
{
|
||||
$user = $event->user;
|
||||
|
||||
$forumTitle = Config::get('flarum::forum_tite');
|
||||
|
||||
$data = [
|
||||
'username' => $user->username,
|
||||
'forumTitle' => $forumTitle,
|
||||
'url' => route('flarum.confirm', ['id' => $user->id, 'token' => $user->confirmation_token])
|
||||
];
|
||||
|
||||
$this->mailer->send(['text' => 'flarum::emails.confirm'], $data, function ($message) use ($user) {
|
||||
$message->to($user->email)->subject('['.$forumTitle.'] Email Address Confirmation');
|
||||
});
|
||||
}
|
||||
|
||||
public function whenEmailWasChanged(EmailWasChanged $event)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
32
src/Core/Handlers/Events/RenamedPostCreator.php
Executable file
32
src/Core/Handlers/Events/RenamedPostCreator.php
Executable file
@@ -0,0 +1,32 @@
|
||||
<?php namespace Flarum\Core\Handlers\Events;
|
||||
|
||||
use Flarum\Core\Events\DiscussionWasRenamed;
|
||||
use Flarum\Core\Models\RenamedPost;
|
||||
|
||||
class RenamedPostCreator
|
||||
{
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
*
|
||||
* @param Illuminate\Events\Dispatcher $events
|
||||
* @return array
|
||||
*/
|
||||
public function subscribe($events)
|
||||
{
|
||||
$events->listen('Flarum\Core\Events\DiscussionWasRenamed', __CLASS__.'@whenDiscussionWasRenamed');
|
||||
}
|
||||
|
||||
public function whenDiscussionWasRenamed(DiscussionWasRenamed $event)
|
||||
{
|
||||
$post = RenamedPost::reply(
|
||||
$event->discussion->id,
|
||||
$event->user->id,
|
||||
$event->oldTitle,
|
||||
$event->discussion->title
|
||||
);
|
||||
|
||||
$post->save();
|
||||
|
||||
$event->discussion->postWasAdded($post);
|
||||
}
|
||||
}
|
70
src/Core/Handlers/Events/UserMetadataUpdater.php
Executable file
70
src/Core/Handlers/Events/UserMetadataUpdater.php
Executable file
@@ -0,0 +1,70 @@
|
||||
<?php namespace Flarum\Core\Handlers\Events;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
use Flarum\Core\Events\PostWasPosted;
|
||||
use Flarum\Core\Events\PostWasDeleted;
|
||||
use Flarum\Core\Events\PostWasHidden;
|
||||
use Flarum\Core\Events\PostWasRestored;
|
||||
use Flarum\Core\Events\DiscussionWasStarted;
|
||||
use Flarum\Core\Events\DiscussionWasDeleted;
|
||||
|
||||
class UserMetadataUpdater
|
||||
{
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
*
|
||||
* @param Illuminate\Events\Dispatcher $events
|
||||
* @return array
|
||||
*/
|
||||
public function subscribe($events)
|
||||
{
|
||||
$events->listen('Flarum\Core\Events\PostWasPosted', __CLASS__.'@whenPostWasPosted');
|
||||
$events->listen('Flarum\Core\Events\PostWasDeleted', __CLASS__.'@whenPostWasDeleted');
|
||||
$events->listen('Flarum\Core\Events\PostWasHidden', __CLASS__.'@whenPostWasHidden');
|
||||
$events->listen('Flarum\Core\Events\PostWasRestored', __CLASS__.'@whenPostWasRestored');
|
||||
$events->listen('Flarum\Core\Events\DiscussionWasStarted', __CLASS__.'@whenDiscussionWasStarted');
|
||||
$events->listen('Flarum\Core\Events\DiscussionWasDeleted', __CLASS__.'@whenDiscussionWasDeleted');
|
||||
}
|
||||
|
||||
public function whenPostWasPosted(PostWasPosted $event)
|
||||
{
|
||||
$this->updateRepliesCount($event->post->user, 1);
|
||||
}
|
||||
|
||||
public function whenPostWasDeleted(PostWasDeleted $event)
|
||||
{
|
||||
$this->updateRepliesCount($event->post->user, -1);
|
||||
}
|
||||
|
||||
public function whenPostWasHidden(PostWasHidden $event)
|
||||
{
|
||||
$this->updateRepliesCount($event->post->user, -1);
|
||||
}
|
||||
|
||||
public function whenPostWasRestored(PostWasRestored $event)
|
||||
{
|
||||
$this->updateRepliesCount($event->post->user, 1);
|
||||
}
|
||||
|
||||
public function whenDiscussionWasStarted(DiscussionWasStarted $event)
|
||||
{
|
||||
$this->updateDiscussionsCount($event->discussion->startUser, 1);
|
||||
}
|
||||
|
||||
public function whenDiscussionWasDeleted(DiscussionWasDeleted $event)
|
||||
{
|
||||
$this->updateDiscussionsCount($event->discussion->startUser, -1);
|
||||
}
|
||||
|
||||
protected function updateRepliesCount(User $user, $amount)
|
||||
{
|
||||
$user->posts_count += $amount;
|
||||
$user->save();
|
||||
}
|
||||
|
||||
protected function updateDiscussionsCount(User $user, $amount)
|
||||
{
|
||||
$user->discussions_count += $amount;
|
||||
$user->save();
|
||||
}
|
||||
}
|
44
src/Core/Models/AccessToken.php
Normal file
44
src/Core/Models/AccessToken.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
class AccessToken extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'access_tokens';
|
||||
|
||||
/**
|
||||
* Use a custom primary key for this model.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $incrementing = false;
|
||||
|
||||
/**
|
||||
* Generate an access token for the specified user.
|
||||
*
|
||||
* @param int $userId
|
||||
* @return static
|
||||
*/
|
||||
public static function generate($userId)
|
||||
{
|
||||
$token = new static;
|
||||
|
||||
$token->id = str_random(40);
|
||||
$token->user_id = $userId;
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the owner of this access token.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\User');
|
||||
}
|
||||
}
|
36
src/Core/Models/Activity.php
Normal file
36
src/Core/Models/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\Models\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');
|
||||
}
|
||||
|
||||
}
|
162
src/Core/Models/CommentPost.php
Executable file
162
src/Core/Models/CommentPost.php
Executable file
@@ -0,0 +1,162 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
use Flarum\Core\Formatter\FormatterManager;
|
||||
use Flarum\Core\Events\PostWasPosted;
|
||||
use Flarum\Core\Events\PostWasRevised;
|
||||
use Flarum\Core\Events\PostWasHidden;
|
||||
use Flarum\Core\Events\PostWasRestored;
|
||||
|
||||
class CommentPost extends Post
|
||||
{
|
||||
/**
|
||||
* The text formatter instance.
|
||||
*
|
||||
* @var \Flarum\Core\Formatter\Formatter
|
||||
*/
|
||||
protected static $formatter;
|
||||
|
||||
/**
|
||||
* Add an event listener to set the post's number, and update the
|
||||
* discussion's number index, when inserting a post.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($post) {
|
||||
$post->number = ++$post->discussion->number_index;
|
||||
$post->discussion->save();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance in reply to a discussion.
|
||||
*
|
||||
* @param int $discussionId
|
||||
* @param string $content
|
||||
* @param int $userId
|
||||
* @return static
|
||||
*/
|
||||
public static function reply($discussionId, $content, $userId)
|
||||
{
|
||||
$post = new static;
|
||||
|
||||
$post->content = $content;
|
||||
$post->content_html = static::formatContent($post->content);
|
||||
$post->time = time();
|
||||
$post->discussion_id = $discussionId;
|
||||
$post->user_id = $userId;
|
||||
$post->type = 'comment';
|
||||
|
||||
$post->raise(new PostWasPosted($post));
|
||||
|
||||
return $post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revise the post's content.
|
||||
*
|
||||
* @param string $content
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return $this
|
||||
*/
|
||||
public function revise($content, $user)
|
||||
{
|
||||
if ($this->content !== $content) {
|
||||
$this->content = $content;
|
||||
$this->content_html = static::formatContent($this->content);
|
||||
|
||||
$this->edit_time = time();
|
||||
$this->edit_user_id = $user->id;
|
||||
|
||||
$this->raise(new PostWasRevised($this));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the post.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return $this
|
||||
*/
|
||||
public function hide($user)
|
||||
{
|
||||
if (! $this->hide_time) {
|
||||
$this->hide_time = time();
|
||||
$this->hide_user_id = $user->id;
|
||||
|
||||
$this->raise(new PostWasHidden($this));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the post.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return $this
|
||||
*/
|
||||
public function restore($user)
|
||||
{
|
||||
if ($this->hide_time !== null) {
|
||||
$this->hide_time = null;
|
||||
$this->hide_user_id = null;
|
||||
|
||||
$this->raise(new PostWasRestored($this));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content formatter as HTML.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public function getContentHtmlAttribute($value)
|
||||
{
|
||||
if (! $value) {
|
||||
$this->content_html = $value = static::formatContent($this->content);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get text formatter instance.
|
||||
*
|
||||
* @return \Flarum\Core\Formatter\FormatterManager
|
||||
*/
|
||||
public static function getFormatter()
|
||||
{
|
||||
return static::$formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text formatter instance.
|
||||
*
|
||||
* @param \Flarum\Core\Formatter\FormatterManager $formatter
|
||||
*/
|
||||
public static function setFormatter(FormatterManager $formatter)
|
||||
{
|
||||
static::$formatter = $formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a string of post content using the set formatter.
|
||||
*
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
protected static function formatContent($content)
|
||||
{
|
||||
return static::$formatter->format($content);
|
||||
}
|
||||
}
|
292
src/Core/Models/Discussion.php
Executable file
292
src/Core/Models/Discussion.php
Executable file
@@ -0,0 +1,292 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
use Tobscure\Permissible\Permissible;
|
||||
use Flarum\Core\Support\EventGenerator;
|
||||
use Flarum\Core\Events\DiscussionWasDeleted;
|
||||
use Flarum\Core\Events\DiscussionWasStarted;
|
||||
use Flarum\Core\Events\DiscussionWasRenamed;
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class Discussion extends Model
|
||||
{
|
||||
use Permissible;
|
||||
|
||||
/**
|
||||
* The validation rules for this model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $rules = [
|
||||
'title' => 'required',
|
||||
'start_time' => 'required|date',
|
||||
'comments_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'
|
||||
];
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'discussions';
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['start_time', 'last_time'];
|
||||
|
||||
/**
|
||||
* An array of posts that have been added during this request.
|
||||
*
|
||||
* @var \Flarum\Core\Models\Post[]
|
||||
*/
|
||||
protected $addedPosts = [];
|
||||
|
||||
/**
|
||||
* The user for which the state relationship should be loaded.
|
||||
*
|
||||
* @var \Flarum\Core\Models\User
|
||||
*/
|
||||
protected static $stateUser;
|
||||
|
||||
/**
|
||||
* Raise an event when a discussion is deleted.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleted(function ($discussion) {
|
||||
$discussion->raise(new DiscussionWasDeleted($discussion));
|
||||
|
||||
$discussion->posts()->delete();
|
||||
$discussion->readers()->detach();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param string $title
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return static
|
||||
*/
|
||||
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 DiscussionWasStarted($discussion));
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename the discussion.
|
||||
*
|
||||
* @param string $title
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return $this
|
||||
*/
|
||||
public function rename($title, $user)
|
||||
{
|
||||
if ($this->title !== $title) {
|
||||
$oldTitle = $this->title;
|
||||
$this->title = $title;
|
||||
|
||||
$this->raise(new DiscussionWasRenamed($this, $user, $oldTitle));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the discussion's last post details.
|
||||
*
|
||||
* @param \Flarum\Core\Models\Post $post
|
||||
* @return $this
|
||||
*/
|
||||
public function setLastPost(Post $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;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh a discussion's last post details.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function refreshLastPost()
|
||||
{
|
||||
if ($lastPost = $this->comments()->orderBy('time', 'desc')->first()) {
|
||||
$this->setLastPost($lastPost);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the discussion's comments count.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function refreshCommentsCount()
|
||||
{
|
||||
$this->comments_count = $this->comments()->count();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the posts that have been added to this discussion during
|
||||
* this request.
|
||||
*
|
||||
* @return \Flarum\Core\Models\Post[]
|
||||
*/
|
||||
public function getAddedPosts()
|
||||
{
|
||||
return $this->addedPosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that a post was added to this discussion during this request
|
||||
* for later retrieval.
|
||||
*
|
||||
* @param \Flarum\Core\Models\Post $post
|
||||
* @return void
|
||||
*/
|
||||
public function postWasAdded(Post $post)
|
||||
{
|
||||
$this->addedPosts[] = $post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's posts.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function posts()
|
||||
{
|
||||
return $this->hasMany('Flarum\Core\Models\Post');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's comments.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function comments()
|
||||
{
|
||||
return $this->posts()->where('type', 'comment')->whereNull('hide_time');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's first post.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function startPost()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\Post', 'start_post_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's author.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function startUser()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\User', 'start_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's last post.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function lastPost()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\Post', 'last_post_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's last post's author.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function lastUser()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\User', 'last_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's readers.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function readers()
|
||||
{
|
||||
return $this->belongsToMany('Flarum\Core\Models\User', 'users_discussions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's state for a particular user.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function state(User $user = null)
|
||||
{
|
||||
$user = $user ?: static::$stateUser;
|
||||
|
||||
return $this->hasOne('Flarum\Core\Models\DiscussionState')->where('user_id', $user->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state model for a user, or instantiate a new one if it does not
|
||||
* exist.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Flarum\Core\Models\DiscussionState
|
||||
*/
|
||||
public function stateFor(User $user)
|
||||
{
|
||||
$state = $this->state($user)->first();
|
||||
|
||||
if (! $state) {
|
||||
$state = new DiscussionState;
|
||||
$state->discussion_id = $this->id;
|
||||
$state->user_id = $user->id;
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user for which the state relationship should be loaded.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
*/
|
||||
public static function setStateUser(User $user)
|
||||
{
|
||||
static::$stateUser = $user;
|
||||
}
|
||||
}
|
73
src/Core/Models/DiscussionState.php
Normal file
73
src/Core/Models/DiscussionState.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
use Flarum\Core\Events\DiscussionWasRead;
|
||||
|
||||
class DiscussionState extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'users_discussions';
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['read_time'];
|
||||
|
||||
/**
|
||||
* Mark the discussion as read to a certain point by updating that state's
|
||||
* data.
|
||||
*
|
||||
* @param int $number
|
||||
* @return $this
|
||||
*/
|
||||
public function read($number)
|
||||
{
|
||||
if ($number > $this->read_number) {
|
||||
$this->read_number = $number;
|
||||
$this->read_time = time();
|
||||
|
||||
$this->raise(new DiscussionWasRead($this));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion that this state is for.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function discussion()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\Discussion', 'discussion_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the user that this state is for.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\User', 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
8
src/Core/Models/Forum.php
Executable file
8
src/Core/Models/Forum.php
Executable file
@@ -0,0 +1,8 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
use Tobscure\Permissible\Permissible;
|
||||
|
||||
class Forum extends Model
|
||||
{
|
||||
use Permissible;
|
||||
}
|
42
src/Core/Models/Group.php
Executable file
42
src/Core/Models/Group.php
Executable file
@@ -0,0 +1,42 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
class Group extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'groups';
|
||||
|
||||
/**
|
||||
* The ID of the administrator group.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const ADMINISTRATOR_ID = 1;
|
||||
|
||||
/**
|
||||
* The ID of the guest group.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const GUEST_ID = 2;
|
||||
|
||||
/**
|
||||
* The ID of the member group.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const MEMBER_ID = 3;
|
||||
|
||||
/**
|
||||
* Define the relationship with the group's users.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return $this->belongsToMany('Flarum\Core\Models\User', 'users_groups');
|
||||
}
|
||||
}
|
30
src/Core/Models/Guest.php
Executable file
30
src/Core/Models/Guest.php
Executable file
@@ -0,0 +1,30 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
class Guest extends User
|
||||
{
|
||||
public $id = 0;
|
||||
|
||||
/**
|
||||
* Return an array containing the 'guests' group model.
|
||||
*
|
||||
* @return \Flarum\Core\Models\Group
|
||||
*/
|
||||
public function getGroupsAttribute()
|
||||
{
|
||||
if (! isset($this->attributes['groups'])) {
|
||||
$this->attributes['groups'] = $this->relations['groups'] = Group::where('id', Group::GUEST_ID)->get();
|
||||
}
|
||||
|
||||
return $this->attributes['groups'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether or not the user is a guest.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function guest()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
173
src/Core/Models/Model.php
Executable file
173
src/Core/Models/Model.php
Executable file
@@ -0,0 +1,173 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
use Illuminate\Contracts\Validation\Factory;
|
||||
use Illuminate\Database\Eloquent\Model as Eloquent;
|
||||
use Flarum\Core\Exceptions\ValidationFailureException;
|
||||
use Flarum\Core\Exceptions\PermissionDeniedException;
|
||||
use Flarum\Core\Support\EventGenerator;
|
||||
|
||||
class Model extends Eloquent
|
||||
{
|
||||
use EventGenerator;
|
||||
|
||||
/**
|
||||
* Disable timestamps.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* The validation rules for this model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $rules = [];
|
||||
|
||||
/**
|
||||
* The forum model instance.
|
||||
*
|
||||
* @var \Flarum\Core\Models\Forum
|
||||
*/
|
||||
protected static $forum;
|
||||
|
||||
/**
|
||||
* The validation factory instance.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Validation\Factory
|
||||
*/
|
||||
protected static $validator;
|
||||
|
||||
/**
|
||||
* Define the relationship with the forum.
|
||||
*
|
||||
* @return \Flarum\Core\Models\Forum
|
||||
*/
|
||||
public function forum()
|
||||
{
|
||||
return static::$forum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the forum model instance.
|
||||
*
|
||||
* @param \Flarum\Core\Models\Forum $forum
|
||||
*/
|
||||
public static function setForum(Forum $forum)
|
||||
{
|
||||
static::$forum = $forum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the validation factory instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Validation\Factory $validator
|
||||
*/
|
||||
public static function setValidator(Factory $validator)
|
||||
{
|
||||
static::$validator = $validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the model is valid in its current state.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->makeValidator()->passes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an exception if the model is not valid in its current state.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Flarum\Core\ValidationFailureException
|
||||
*/
|
||||
public function assertValid()
|
||||
{
|
||||
if ($this->makeValidator()->fails()) {
|
||||
throw (new ValidationFailureException)
|
||||
->setErrors($validation->errors())
|
||||
->setInput($validation->getData());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a new validator instance for this model.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Validation\Validator
|
||||
*/
|
||||
protected function makeValidator()
|
||||
{
|
||||
$rules = $this->expandUniqueRules(static::$rules);
|
||||
|
||||
return $this->validator->make($this->attributes, $rules, static::$messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand 'unique' rules in a set of validation rules into a fuller form
|
||||
* that Laravel's validator can understand.
|
||||
*
|
||||
* @param array $rules
|
||||
* @return array
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the user has permission to view this model, throwing an
|
||||
* exception if they don't.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return void
|
||||
*
|
||||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public function assertVisibleTo(User $user)
|
||||
{
|
||||
if (! $this->can($user, 'view')) {
|
||||
throw new ModelNotFoundException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the user has a certain permission for this model, throwing
|
||||
* an exception if they don't.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @param string $permission
|
||||
* @return void
|
||||
*
|
||||
* @throws \Flarum\Core\Exceptions\PermissionDeniedException
|
||||
*/
|
||||
public function assertCan(User $user, $permission)
|
||||
{
|
||||
if (! $this->can($user, $permission)) {
|
||||
throw new PermissionDeniedException;
|
||||
}
|
||||
}
|
||||
}
|
5
src/Core/Models/Permission.php
Normal file
5
src/Core/Models/Permission.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
class Permission extends Model
|
||||
{
|
||||
}
|
151
src/Core/Models/Post.php
Executable file
151
src/Core/Models/Post.php
Executable file
@@ -0,0 +1,151 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
use Tobscure\Permissible\Permissible;
|
||||
use Flarum\Core\Events\PostWasDeleted;
|
||||
|
||||
class Post extends Model
|
||||
{
|
||||
use Permissible;
|
||||
|
||||
/**
|
||||
* The validation rules for this model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $rules = [
|
||||
'discussion_id' => 'required|integer',
|
||||
'time' => 'required|date',
|
||||
'content' => 'required',
|
||||
'number' => 'integer',
|
||||
'user_id' => 'integer',
|
||||
'edit_time' => 'date',
|
||||
'edit_user_id' => 'integer',
|
||||
'hide_time' => 'date',
|
||||
'hide_user_id' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'posts';
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['time', 'edit_time', 'hide_time'];
|
||||
|
||||
/**
|
||||
* A map of post types, as specified in the `type` column, to their
|
||||
* classes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $types = [];
|
||||
|
||||
/**
|
||||
* Raise an event when a post is deleted.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleted(function ($post) {
|
||||
$post->raise(new PostWasDeleted($post));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the post's discussion.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function discussion()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\Discussion', 'discussion_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the post's author.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\User', 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the user who edited the post.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function editUser()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\User', 'edit_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the user who hid the post.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function hideUser()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Models\User', 'hide_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate the query and return an array of matching IDs.
|
||||
* Example usage: `$ids = $discussion->posts()->ids()`
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @return array
|
||||
*/
|
||||
public function scopeIds($query)
|
||||
{
|
||||
return array_map('intval', $query->get(['id'])->fetch('id')->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new model instance according to the post's type.
|
||||
*
|
||||
* @param array $attributes
|
||||
* @return static|object
|
||||
*/
|
||||
public function newFromBuilder($attributes = [], $connection = null)
|
||||
{
|
||||
if (!empty($attributes->type)) {
|
||||
$type = $attributes->type;
|
||||
if (isset(static::$types[$type])) {
|
||||
$class = static::$types[$type];
|
||||
if (class_exists($class)) {
|
||||
$instance = new $class;
|
||||
$instance->exists = true;
|
||||
$instance->setRawAttributes((array) $attributes, true);
|
||||
$instance->setConnection($connection ?: $this->connection);
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::newFromBuilder($attributes, $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a post type and its model class.
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $class
|
||||
* @return void
|
||||
*/
|
||||
public static function addType($type, $class)
|
||||
{
|
||||
static::$types[$type] = $class;
|
||||
}
|
||||
}
|
47
src/Core/Models/RenamedPost.php
Executable file
47
src/Core/Models/RenamedPost.php
Executable file
@@ -0,0 +1,47 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
class RenamedPost extends Post
|
||||
{
|
||||
/**
|
||||
* Create a new instance in reply to a discussion.
|
||||
*
|
||||
* @param int $discussionId
|
||||
* @param int $userId
|
||||
* @param string $oldTitle
|
||||
* @param string $newTitle
|
||||
* @return static
|
||||
*/
|
||||
public static function reply($discussionId, $userId, $oldTitle, $newTitle)
|
||||
{
|
||||
$post = new static;
|
||||
|
||||
$post->content = [$oldTitle, $newTitle];
|
||||
$post->time = time();
|
||||
$post->discussion_id = $discussionId;
|
||||
$post->user_id = $userId;
|
||||
$post->type = 'renamed';
|
||||
|
||||
return $post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserialize the content attribute.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public function getContentAttribute($value)
|
||||
{
|
||||
return json_decode($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the content attribute.
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
public function setContentAttribute($value)
|
||||
{
|
||||
$this->attributes['content'] = json_encode($value);
|
||||
}
|
||||
}
|
314
src/Core/Models/User.php
Executable file
314
src/Core/Models/User.php
Executable file
@@ -0,0 +1,314 @@
|
||||
<?php namespace Flarum\Core\Models;
|
||||
|
||||
use Illuminate\Contracts\Hashing\Hasher;
|
||||
use Tobscure\Permissible\Permissible;
|
||||
use Flarum\Core\Exceptions\InvalidConfirmationTokenException;
|
||||
use Flarum\Core\Events\UserWasDeleted;
|
||||
use Flarum\Core\Events\UserWasRegistered;
|
||||
use Flarum\Core\Events\UserWasRenamed;
|
||||
use Flarum\Core\Events\EmailWasChanged;
|
||||
use Flarum\Core\Events\PasswordWasChanged;
|
||||
use Flarum\Core\Events\UserWasActivated;
|
||||
use Flarum\Core\Events\EmailWasConfirmed;
|
||||
|
||||
class User extends Model
|
||||
{
|
||||
use Permissible;
|
||||
|
||||
/**
|
||||
* The validation rules for this model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $rules = [
|
||||
'username' => 'required|username|unique',
|
||||
'email' => 'required|email|unique',
|
||||
'password' => 'required',
|
||||
'join_time' => 'date',
|
||||
'last_seen_time' => 'date',
|
||||
'discussions_count' => 'integer',
|
||||
'posts_count' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'users';
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['join_time', 'last_seen_time', 'read_time'];
|
||||
|
||||
/**
|
||||
* The hasher with which to hash passwords.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Hashing\Hasher
|
||||
*/
|
||||
protected static $hasher;
|
||||
|
||||
/**
|
||||
* Raise an event when a post is deleted.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleted(function ($user) {
|
||||
$user->raise(new UserWasDeleted($user));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new user.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $email
|
||||
* @param string $password
|
||||
* @return static
|
||||
*/
|
||||
public static function register($username, $email, $password)
|
||||
{
|
||||
$user = new static;
|
||||
|
||||
$user->username = $username;
|
||||
$user->email = $email;
|
||||
$user->password = $password;
|
||||
$user->join_time = time();
|
||||
|
||||
$user->refreshConfirmationToken();
|
||||
|
||||
$user->raise(new UserWasRegistered($user));
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename the user.
|
||||
*
|
||||
* @param string $username
|
||||
* @return $this
|
||||
*/
|
||||
public function rename($username)
|
||||
{
|
||||
if ($username !== $this->username) {
|
||||
$this->username = $username;
|
||||
$this->raise(new UserWasRenamed($this));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the user's email.
|
||||
*
|
||||
* @param string $email
|
||||
* @return $this
|
||||
*/
|
||||
public function changeEmail($email)
|
||||
{
|
||||
if ($email !== $this->email) {
|
||||
$this->email = $email;
|
||||
$this->raise(new EmailWasChanged($this));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the user's password.
|
||||
*
|
||||
* @param string $password
|
||||
* @return $this
|
||||
*/
|
||||
public function changePassword($password)
|
||||
{
|
||||
$this->password = $password ? static::$hasher->make($password) : null;
|
||||
$this->raise(new PasswordWasChanged($this));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark all discussions as read by setting the user's read_time.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function markAllAsRead()
|
||||
{
|
||||
$this->read_time = time();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given password matches the user's password.
|
||||
*
|
||||
* @param string $password
|
||||
* @return boolean
|
||||
*/
|
||||
public function checkPassword($password)
|
||||
{
|
||||
return static::$hasher->check($password, $this->password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the user's account.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function activate()
|
||||
{
|
||||
$this->is_activated = true;
|
||||
$this->groups()->sync([3]);
|
||||
|
||||
$this->raise(new UserWasActivated($this));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given confirmation token is valid for this user.
|
||||
*
|
||||
* @param string $token
|
||||
* @return boolean
|
||||
*/
|
||||
public function assertConfirmationTokenValid($token)
|
||||
{
|
||||
if ($this->is_confirmed ||
|
||||
! $token ||
|
||||
$this->confirmation_token !== $token) {
|
||||
throw new InvalidConfirmationTokenException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new confirmation token for the user.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function refreshConfirmationToken()
|
||||
{
|
||||
$this->is_confirmed = false;
|
||||
$this->confirmation_token = str_random(30);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm the user's email.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function confirmEmail()
|
||||
{
|
||||
$this->is_confirmed = true;
|
||||
$this->confirmation_token = null;
|
||||
|
||||
$this->raise(new EmailWasConfirmed($this));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the user's grantees according to their ID and groups.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGrantees()
|
||||
{
|
||||
$grantees = ['group.'.GROUP::GUEST_ID]; // guests
|
||||
if ($this->id) {
|
||||
$grantees[] = 'user.'.$this->id;
|
||||
}
|
||||
foreach ($this->groups as $group) {
|
||||
$grantees[] = 'group.'.$group->id;
|
||||
}
|
||||
|
||||
return $grantees;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user has a certain permission based on their groups.
|
||||
*
|
||||
* @param string $permission
|
||||
* @param string $entity
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasPermission($permission, $entity)
|
||||
{
|
||||
if ($this->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$count = $this->permissions()->where('entity', $entity)->where('permission', $permission)->count();
|
||||
|
||||
return (bool) $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether or not the user is an administrator.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isAdmin()
|
||||
{
|
||||
return $this->groups->contains(Group::ADMINISTRATOR_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether or not the user is a guest.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isGuest()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the user's activity.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function activity()
|
||||
{
|
||||
return $this->hasMany('Flarum\Core\Models\Activity');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the user's groups.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function groups()
|
||||
{
|
||||
return $this->belongsToMany('Flarum\Core\Models\Group', 'users_groups');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the user's permissions.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function permissions()
|
||||
{
|
||||
return Permission::whereIn('grantee', $this->getGrantees());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hasher with which to hash passwords.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Hashing\Hasher $hasher
|
||||
*/
|
||||
public static function setHasher(Hasher $hasher)
|
||||
{
|
||||
static::$hasher = $hasher;
|
||||
}
|
||||
}
|
33
src/Core/Repositories/DiscussionRepositoryInterface.php
Normal file
33
src/Core/Repositories/DiscussionRepositoryInterface.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php namespace Flarum\Core\Repositories;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
interface DiscussionRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Get a new query builder for ths discussions table.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function query();
|
||||
|
||||
/**
|
||||
* Find a discussion by ID, optionally making sure it is visible to a certain
|
||||
* user, or throw an exception.
|
||||
*
|
||||
* @param integer $id
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Flarum\Core\Models\Discussion
|
||||
*
|
||||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public function findOrFail($id, User $user = null);
|
||||
|
||||
/**
|
||||
* Get the IDs of discussions which a user has read completely.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return array
|
||||
*/
|
||||
public function getReadIds(User $user);
|
||||
}
|
65
src/Core/Repositories/EloquentDiscussionRepository.php
Normal file
65
src/Core/Repositories/EloquentDiscussionRepository.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php namespace Flarum\Core\Repositories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Flarum\Core\Models\Discussion;
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class EloquentDiscussionRepository implements DiscussionRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Get a new query builder for ths discussions table.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function query()
|
||||
{
|
||||
return Discussion::query();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a discussion by ID, optionally making sure it is visible to a certain
|
||||
* user, or throw an exception.
|
||||
*
|
||||
* @param integer $id
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Flarum\Core\Models\Discussion
|
||||
*
|
||||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public function findOrFail($id, User $user = null)
|
||||
{
|
||||
$query = Discussion::where('id', $id);
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->firstOrFail();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IDs of discussions which a user has read completely.
|
||||
*
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return array
|
||||
*/
|
||||
public function getReadIds(User $user)
|
||||
{
|
||||
return Discussion::leftJoin('users_discussions', 'users_discussions.discussion_id', '=', 'discussions.id')
|
||||
->where('user_id', $user->id)
|
||||
->where('read_number', '<', 'last_post_number')
|
||||
->lists('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include records that are visible to a user.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
protected function scopeVisibleForUser(Builder $query, User $user = null)
|
||||
{
|
||||
if ($user !== null) {
|
||||
$query->whereCan($user, 'view');
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
121
src/Core/Repositories/EloquentPostRepository.php
Normal file
121
src/Core/Repositories/EloquentPostRepository.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php namespace Flarum\Core\Repositories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Flarum\Core\Models\Post;
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class EloquentPostRepository implements PostRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Find a post by ID, optionally making sure it is visible to a certain
|
||||
* user, or throw an exception.
|
||||
*
|
||||
* @param integer $id
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Flarum\Core\Models\Post
|
||||
*
|
||||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public function findOrFail($id, User $user = null)
|
||||
{
|
||||
$query = Post::where('id', $id);
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->firstOrFail();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find posts in a discussion, optionally making sure they are visible to
|
||||
* a certain user, and/or using other criteria.
|
||||
*
|
||||
* @param integer $discussionId
|
||||
* @param \Flarum\Core\Models\User|null $user
|
||||
* @param string $sort
|
||||
* @param string $order
|
||||
* @param integer $count
|
||||
* @param integer $start
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function findByDiscussion($discussionId, User $user = null, $sort = 'time', $order = 'asc', $count = null, $start = 0)
|
||||
{
|
||||
$query = Post::where('discussion_id', $discussionId)
|
||||
->orderBy($sort, $order)
|
||||
->skip($start)
|
||||
->take($count);
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find posts by their IDs, optionally making sure they are visible to a
|
||||
* certain user.
|
||||
*
|
||||
* @param array $ids
|
||||
* @param \Flarum\Core\Models\User|null $user
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function findByIds(array $ids, User $user = null)
|
||||
{
|
||||
$query = Post::whereIn('id', (array) $ids);
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find posts by matching a string of words against their content,
|
||||
* optionally making sure they are visible to a certain user.
|
||||
*
|
||||
* @param string $string
|
||||
* @param \Flarum\Core\Models\User|null $user
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function findByContent($string, User $user = null)
|
||||
{
|
||||
$query = Post::select('id', 'discussion_id')
|
||||
->where('content', 'like', '%'.$string.'%');
|
||||
// ->whereRaw('MATCH (`content`) AGAINST (? IN BOOLEAN MODE)', [$string])
|
||||
// ->orderByRaw('MATCH (`content`) AGAINST (?) DESC', [$string])
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the position within a discussion where a post with a certain number
|
||||
* is. If the post with that number does not exist, the index of the
|
||||
* closest post to it will be returned.
|
||||
*
|
||||
* @param integer $discussionId
|
||||
* @param integer $number
|
||||
* @param \Flarum\Core\Models\User|null $user
|
||||
* @return integer
|
||||
*/
|
||||
public function getIndexForNumber($discussionId, $number, User $user = null)
|
||||
{
|
||||
$query = Post::where('discussion_id', $discussionId)
|
||||
->where('time', '<', function ($query) use ($discussionId, $number) {
|
||||
$query->select('time')
|
||||
->from('posts')
|
||||
->where('discussion_id', $discussionId)
|
||||
->whereNotNull('number')
|
||||
->orderByRaw('ABS(CAST(number AS SIGNED) - ?)', [$number])
|
||||
->take(1);
|
||||
});
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include records that are visible to a user.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
protected function scopeVisibleForUser(Builder $query, User $user = null)
|
||||
{
|
||||
if ($user !== null) {
|
||||
$query->whereCan($user, 'view');
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
67
src/Core/Repositories/EloquentUserRepository.php
Normal file
67
src/Core/Repositories/EloquentUserRepository.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php namespace Flarum\Core\Repositories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class EloquentUserRepository implements UserRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Find a user by ID, optionally making sure it is visible to a certain
|
||||
* user, or throw an exception.
|
||||
*
|
||||
* @param integer $id
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Flarum\Core\Models\User
|
||||
*
|
||||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public function findOrFail($id, User $user = null)
|
||||
{
|
||||
$query = User::where('id', $id);
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->firstOrFail();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a user by an identification (username or email).
|
||||
*
|
||||
* @param string $identification
|
||||
* @return \Flarum\Core\Models\User|null
|
||||
*/
|
||||
public function findByIdentification($identification)
|
||||
{
|
||||
$field = filter_var($identification, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
|
||||
|
||||
return User::where($field, $identification)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID of a user with the given username.
|
||||
*
|
||||
* @param string $username
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return integer|null
|
||||
*/
|
||||
public function getIdForUsername($username, User $user = null)
|
||||
{
|
||||
$query = User::where('username', 'like', $username);
|
||||
|
||||
return $this->scopeVisibleForUser($query, $user)->pluck('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include records that are visible to a user.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
protected function scopeVisibleForUser(Builder $query, User $user = null)
|
||||
{
|
||||
if ($user !== null) {
|
||||
$query->whereCan($user, 'view');
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
64
src/Core/Repositories/PostRepositoryInterface.php
Normal file
64
src/Core/Repositories/PostRepositoryInterface.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php namespace Flarum\Core\Repositories;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
interface PostRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Find a post by ID, optionally making sure it is visible to a certain
|
||||
* user, or throw an exception.
|
||||
*
|
||||
* @param integer $id
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Flarum\Core\Models\Post
|
||||
*
|
||||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public function findOrFail($id, User $user = null);
|
||||
|
||||
/**
|
||||
* Find posts in a discussion, optionally making sure they are visible to
|
||||
* a certain user, and/or using other criteria.
|
||||
*
|
||||
* @param integer $discussionId
|
||||
* @param \Flarum\Core\Models\User|null $user
|
||||
* @param string $sort
|
||||
* @param string $order
|
||||
* @param integer $count
|
||||
* @param integer $start
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function findByDiscussion($discussionId, User $user = null, $sort = 'time', $order = 'asc', $count = null, $start = 0);
|
||||
|
||||
/**
|
||||
* Find posts by their IDs, optionally making sure they are visible to a
|
||||
* certain user.
|
||||
*
|
||||
* @param array $ids
|
||||
* @param \Flarum\Core\Models\User|null $user
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function findByIds(array $ids, User $user = null);
|
||||
|
||||
/**
|
||||
* Find posts by matching a string of words against their content,
|
||||
* optionally making sure they are visible to a certain user.
|
||||
*
|
||||
* @param string $string
|
||||
* @param \Flarum\Core\Models\User|null $user
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function findByContent($string, User $user = null);
|
||||
|
||||
/**
|
||||
* Get the position within a discussion where a post with a certain number
|
||||
* is. If the post with that number does not exist, the index of the
|
||||
* closest post to it will be returned.
|
||||
*
|
||||
* @param integer $discussionId
|
||||
* @param integer $number
|
||||
* @param \Flarum\Core\Models\User|null $user
|
||||
* @return integer
|
||||
*/
|
||||
public function getIndexForNumber($discussionId, $number, User $user = null);
|
||||
}
|
35
src/Core/Repositories/UserRepositoryInterface.php
Normal file
35
src/Core/Repositories/UserRepositoryInterface.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php namespace Flarum\Core\Repositories;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
interface UserRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Find a user by ID, optionally making sure it is visible to a certain
|
||||
* user, or throw an exception.
|
||||
*
|
||||
* @param integer $id
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return \Flarum\Core\Models\User
|
||||
*
|
||||
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
||||
*/
|
||||
public function findOrFail($id, User $user = null);
|
||||
|
||||
/**
|
||||
* Find a user by an identification (username or email).
|
||||
*
|
||||
* @param string $identification
|
||||
* @return \Flarum\Core\Models\User|null
|
||||
*/
|
||||
public function findByIdentification($identification);
|
||||
|
||||
/**
|
||||
* Get the ID of a user with the given username.
|
||||
*
|
||||
* @param string $username
|
||||
* @param \Flarum\Core\Models\User $user
|
||||
* @return integer|null
|
||||
*/
|
||||
public function getIdForUsername($username, User $user = null);
|
||||
}
|
20
src/Core/Search/Discussions/DiscussionSearchCriteria.php
Normal file
20
src/Core/Search/Discussions/DiscussionSearchCriteria.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php namespace Flarum\Core\Search\Discussions;
|
||||
|
||||
class DiscussionSearchCriteria
|
||||
{
|
||||
public $user;
|
||||
|
||||
public $query;
|
||||
|
||||
public $sort;
|
||||
|
||||
public $order;
|
||||
|
||||
public function __construct($user, $query, $sort, $order)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->query = $query;
|
||||
$this->sort = $sort;
|
||||
$this->order = $order;
|
||||
}
|
||||
}
|
32
src/Core/Search/Discussions/DiscussionSearchResults.php
Normal file
32
src/Core/Search/Discussions/DiscussionSearchResults.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php namespace Flarum\Core\Search\Discussions;
|
||||
|
||||
class DiscussionSearchResults
|
||||
{
|
||||
protected $discussions;
|
||||
|
||||
protected $areMoreResults;
|
||||
|
||||
protected $total;
|
||||
|
||||
public function __construct($discussions, $areMoreResults, $total)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
$this->areMoreResults = $areMoreResults;
|
||||
$this->total = $total;
|
||||
}
|
||||
|
||||
public function getDiscussions()
|
||||
{
|
||||
return $this->discussions;
|
||||
}
|
||||
|
||||
public function getTotal()
|
||||
{
|
||||
return $this->total;
|
||||
}
|
||||
|
||||
public function areMoreResults()
|
||||
{
|
||||
return $this->areMoreResults;
|
||||
}
|
||||
}
|
109
src/Core/Search/Discussions/DiscussionSearcher.php
Normal file
109
src/Core/Search/Discussions/DiscussionSearcher.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php namespace Flarum\Core\Search\Discussions;
|
||||
|
||||
use Flarum\Core\Models\Discussion;
|
||||
use Flarum\Core\Search\GambitManager;
|
||||
use Flarum\Core\Repositories\DiscussionRepositoryInterface;
|
||||
use Flarum\Core\Repositories\PostRepositoryInterface;
|
||||
|
||||
class DiscussionSearcher
|
||||
{
|
||||
public $query;
|
||||
|
||||
protected $sortMap = [
|
||||
'lastPost' => ['last_time', 'desc'],
|
||||
'replies' => ['comments_count', 'desc'],
|
||||
'created' => ['start_time', 'desc']
|
||||
];
|
||||
|
||||
protected $defaultSort = 'lastPost';
|
||||
|
||||
protected $relevantPosts = [];
|
||||
|
||||
protected $gambits;
|
||||
|
||||
protected $discussions;
|
||||
|
||||
public function __construct(GambitManager $gambits, DiscussionRepositoryInterface $discussions, PostRepositoryInterface $posts)
|
||||
{
|
||||
$this->gambits = $gambits;
|
||||
$this->discussions = $discussions;
|
||||
$this->posts = $posts;
|
||||
}
|
||||
|
||||
public function addRelevantPost($discussionId, $postId)
|
||||
{
|
||||
if (empty($this->relevantPosts[$discussionId])) {
|
||||
$this->relevantPosts[$discussionId] = [];
|
||||
}
|
||||
$this->relevantPosts[$discussionId][] = $postId;
|
||||
}
|
||||
|
||||
public function setDefaultSort($defaultSort)
|
||||
{
|
||||
$this->defaultSort = $defaultSort;
|
||||
}
|
||||
|
||||
public function search(DiscussionSearchCriteria $criteria, $count = null, $start = 0, $load = [])
|
||||
{
|
||||
$this->user = $criteria->user;
|
||||
$this->query = $this->discussions->query()->whereCan($criteria->user, 'view');
|
||||
|
||||
$this->gambits->apply($criteria->query, $this);
|
||||
|
||||
$total = $this->query->count();
|
||||
|
||||
$sort = $criteria->sort;
|
||||
if (empty($sort)) {
|
||||
$sort = $this->defaultSort;
|
||||
}
|
||||
// dd($sort);
|
||||
if (is_array($sort)) {
|
||||
foreach ($sort as $id) {
|
||||
$this->query->orderByRaw('id != '.(int) $id);
|
||||
}
|
||||
} else {
|
||||
list($column, $order) = $this->sortMap[$sort];
|
||||
$this->query->orderBy($column, $criteria->order ?: $order);
|
||||
}
|
||||
|
||||
if ($start > 0) {
|
||||
$this->query->skip($start);
|
||||
}
|
||||
if ($count > 0) {
|
||||
$this->query->take($count + 1);
|
||||
}
|
||||
|
||||
$discussions = $this->query->get();
|
||||
|
||||
if ($count > 0 && $areMoreResults = $discussions->count() > $count) {
|
||||
$discussions->pop();
|
||||
}
|
||||
|
||||
if (in_array('relevantPosts', $load) && count($this->relevantPosts)) {
|
||||
$load = array_diff($load, ['relevantPosts']);
|
||||
|
||||
$postIds = [];
|
||||
foreach ($this->relevantPosts as $id => $posts) {
|
||||
$postIds = array_merge($postIds, array_slice($posts, 0, 2));
|
||||
}
|
||||
$posts = $this->posts->findByIds($postIds, $this->user)->load('user');
|
||||
|
||||
foreach ($discussions as $discussion) {
|
||||
$discussion->relevantPosts = $posts->filter(function ($post) use ($discussion) {
|
||||
return $post->discussion_id == $discussion->id;
|
||||
})
|
||||
->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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Discussion::setStateUser($this->user);
|
||||
$discussions->load($load);
|
||||
|
||||
return new DiscussionSearchResults($discussions, $areMoreResults, $total);
|
||||
}
|
||||
}
|
30
src/Core/Search/Discussions/Gambits/AuthorGambit.php
Normal file
30
src/Core/Search/Discussions/Gambits/AuthorGambit.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php namespace Flarum\Core\Search\Discussions\Gambits;
|
||||
|
||||
use Flarum\Core\Repositories\UserRepositoryInterface as UserRepository;
|
||||
use Flarum\Core\Search\Discussions\DiscussionSearcher;
|
||||
use Flarum\Core\Search\GambitAbstract;
|
||||
|
||||
class AuthorGambit extends GambitAbstract
|
||||
{
|
||||
/**
|
||||
* The gambit's regex pattern.
|
||||
* @var string
|
||||
*/
|
||||
protected $pattern = 'author:(.+)';
|
||||
|
||||
protected $users;
|
||||
|
||||
public function __construct(UserRepository $users)
|
||||
{
|
||||
$this->users = $users;
|
||||
}
|
||||
|
||||
public function conditions($matches, DiscussionSearcher $searcher)
|
||||
{
|
||||
$username = trim($matches[1], '"');
|
||||
|
||||
$id = $this->users->getIdForUsername($username);
|
||||
|
||||
$searcher->query->where('start_user_id', $id);
|
||||
}
|
||||
}
|
31
src/Core/Search/Discussions/Gambits/FulltextGambit.php
Normal file
31
src/Core/Search/Discussions/Gambits/FulltextGambit.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php namespace Flarum\Core\Search\Discussions\Gambits;
|
||||
|
||||
use Flarum\Core\Repositories\PostRepositoryInterface;
|
||||
use Flarum\Core\Search\Discussions\DiscussionSearcher;
|
||||
use Flarum\Core\Search\GambitAbstract;
|
||||
|
||||
class FulltextGambit extends GambitAbstract
|
||||
{
|
||||
protected $posts;
|
||||
|
||||
public function __construct(PostRepositoryInterface $posts)
|
||||
{
|
||||
$this->posts = $posts;
|
||||
}
|
||||
|
||||
public function apply($string, DiscussionSearcher $searcher)
|
||||
{
|
||||
$posts = $this->posts->findByContent($string, $searcher->user);
|
||||
|
||||
$discussions = [];
|
||||
foreach ($posts as $post) {
|
||||
$discussions[] = $id = $post->discussion_id;
|
||||
$searcher->addRelevantPost($id, $post->id);
|
||||
}
|
||||
$discussions = array_unique($discussions);
|
||||
|
||||
$searcher->query->whereIn('id', $discussions);
|
||||
|
||||
$searcher->setDefaultSort($discussions);
|
||||
}
|
||||
}
|
36
src/Core/Search/Discussions/Gambits/UnreadGambit.php
Normal file
36
src/Core/Search/Discussions/Gambits/UnreadGambit.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php namespace Flarum\Core\Search\Discussions\Gambits;
|
||||
|
||||
use Flarum\Core\Repositories\DiscussionRepositoryInterface as DiscussionRepository;
|
||||
use Flarum\Core\Search\Discussions\DiscussionSearcher;
|
||||
use Flarum\Core\Search\GambitAbstract;
|
||||
|
||||
class UnreadGambit extends GambitAbstract
|
||||
{
|
||||
/**
|
||||
* The gambit's regex pattern.
|
||||
* @var string
|
||||
*/
|
||||
protected $pattern = 'unread:(true|false)';
|
||||
|
||||
protected $discussions;
|
||||
|
||||
public function __construct(DiscussionRepository $discussions)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
}
|
||||
|
||||
protected function conditions($matches, DiscussionSearcher $searcher)
|
||||
{
|
||||
$user = $searcher->user;
|
||||
|
||||
if ($user->exists) {
|
||||
$readIds = $this->discussions->getReadIds($user);
|
||||
|
||||
if ($matches[1] === 'true') {
|
||||
$searcher->query->whereNotIn('id', $readIds)->where('last_time', '>', $user->read_time ?: 0);
|
||||
} else {
|
||||
$searcher->query->whereIn('id', $readIds)->orWhere('last_time', '<=', $user->read_time ?: 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
src/Core/Search/GambitAbstract.php
Normal file
23
src/Core/Search/GambitAbstract.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php namespace Flarum\Core\Search;
|
||||
|
||||
use Flarum\Core\Search\Discussions\DiscussionSearcher;
|
||||
|
||||
abstract class GambitAbstract
|
||||
{
|
||||
protected $pattern;
|
||||
|
||||
public function apply($bit, DiscussionSearcher $searcher)
|
||||
{
|
||||
if ($matches = $this->match($bit)) {
|
||||
$this->conditions($matches, $searcher);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function match($bit)
|
||||
{
|
||||
if (preg_match('/^'.$this->pattern.'$/i', $bit, $matches)) {
|
||||
return $matches;
|
||||
}
|
||||
}
|
||||
}
|
6
src/Core/Search/GambitInterface.php
Normal file
6
src/Core/Search/GambitInterface.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php namespace Flarum\Core\Search;
|
||||
|
||||
interface GambitInterface
|
||||
{
|
||||
public function apply($string, $searcher);
|
||||
}
|
71
src/Core/Search/GambitManager.php
Normal file
71
src/Core/Search/GambitManager.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php namespace Flarum\Core\Search;
|
||||
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
|
||||
class GambitManager
|
||||
{
|
||||
protected $gambits = [];
|
||||
|
||||
protected $fulltextGambit;
|
||||
|
||||
protected $container;
|
||||
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
public function add($gambit)
|
||||
{
|
||||
$this->gambits[] = $gambit;
|
||||
}
|
||||
|
||||
public function apply($string, $searcher)
|
||||
{
|
||||
$string = $this->applyGambits($string, $searcher);
|
||||
|
||||
if ($string) {
|
||||
$this->applyFulltext($string, $searcher);
|
||||
}
|
||||
}
|
||||
|
||||
public function setFulltextGambit($gambit)
|
||||
{
|
||||
$this->fulltextGambit = $gambit;
|
||||
}
|
||||
|
||||
protected function bits($string)
|
||||
{
|
||||
return str_getcsv($string, ' ');
|
||||
}
|
||||
|
||||
protected function applyGambits($string, $searcher)
|
||||
{
|
||||
$bits = $this->bits($string);
|
||||
|
||||
$gambits = array_map([$this->container, 'make'], $this->gambits);
|
||||
|
||||
foreach ($bits as $k => $bit) {
|
||||
foreach ($gambits as $gambit) {
|
||||
if ($gambit->apply($bit, $searcher)) {
|
||||
unset($bits[$k]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return implode(' ', $bits);
|
||||
}
|
||||
|
||||
protected function applyFulltext($string, $searcher)
|
||||
{
|
||||
if (! $this->fulltextGambit) {
|
||||
return;
|
||||
}
|
||||
|
||||
$gambit = $this->container->make($this->fulltextGambit);
|
||||
|
||||
$gambit->apply($string, $searcher);
|
||||
}
|
||||
|
||||
}
|
17
src/Core/Seeders/ConfigTableSeeder.php
Normal file
17
src/Core/Seeders/ConfigTableSeeder.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php namespace Flarum\Core\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class ConfigTableSeeder extends Seeder {
|
||||
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
}
|
137
src/Core/Seeders/DiscussionsTableSeeder.php
Normal file
137
src/Core/Seeders/DiscussionsTableSeeder.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php namespace Flarum\Core\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use DB;
|
||||
use Flarum\Core\Models\Discussion;
|
||||
use Flarum\Core\Models\Post;
|
||||
use Flarum\Core\Models\User;
|
||||
use Flarum\Core\Models\DiscussionState;
|
||||
|
||||
class DiscussionsTableSeeder extends Seeder
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Discussion::unguard();
|
||||
Post::unguard();
|
||||
|
||||
$faker = \Faker\Factory::create();
|
||||
|
||||
$users = User::count();
|
||||
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$posts_count = $i == 1 ? 400 : rand(1, rand(1, rand(1, 100)));
|
||||
$discussion = Discussion::create([
|
||||
'title' => str_replace("'", '', rtrim($faker->realText(rand(20, 80)), '.')),
|
||||
'start_time' => $faker->dateTimeThisYear,
|
||||
'start_user_id' => rand(1, $users)
|
||||
]);
|
||||
$discussion->comments_count = $posts_count;
|
||||
|
||||
$post = Post::create([
|
||||
'discussion_id' => $discussion->id,
|
||||
'number' => 1,
|
||||
'time' => $discussion->start_time,
|
||||
'user_id' => $discussion->start_user_id,
|
||||
'type' => 'comment',
|
||||
'content' => $faker->realText(rand(100, 1000))
|
||||
]);
|
||||
|
||||
$discussion->start_post_id = $post->id;
|
||||
|
||||
$discussion->last_time = $post->time;
|
||||
$discussion->last_user_id = $post->user_id;
|
||||
$discussion->last_post_id = $post->id;
|
||||
$discussion->last_post_number = $post->number;
|
||||
$discussion->number_index = $post->number;
|
||||
|
||||
$lastPost = null;
|
||||
$count = $posts_count;
|
||||
$posts = [];
|
||||
$startTime = $discussion->start_time;
|
||||
$numberOffset = 0;
|
||||
|
||||
for ($j = 0; $j < $count - 1; $j++) {
|
||||
if (rand(1, 100) == 1) {
|
||||
$discussion->comments_count--;
|
||||
|
||||
$post = Post::create([
|
||||
'discussion_id' => $discussion->id,
|
||||
'time' => $startTime = date_add($startTime, date_interval_create_from_date_string('1 second')),
|
||||
'user_id' => rand(1, $users),
|
||||
'type' => 'renamed',
|
||||
'content' => json_encode(array($faker->realText(rand(20, 40)), $discussion->title))
|
||||
]);
|
||||
} else {
|
||||
$edited = rand(1, 20) == 1;
|
||||
$hidden = rand(1, 100) == 1;
|
||||
|
||||
if ($hidden) {
|
||||
$discussion->comments_count--;
|
||||
}
|
||||
|
||||
$post = Post::create([
|
||||
'discussion_id' => $discussion->id,
|
||||
'number' => $j + 2 + $numberOffset,
|
||||
'time' => $startTime = date_add($startTime, date_interval_create_from_date_string('1 second')),
|
||||
'user_id' => rand(1, $users),
|
||||
'type' => 'comment',
|
||||
'content' => $faker->realText(rand(50, 500)),
|
||||
'edit_time' => $edited ? $startTime = date_add($startTime, date_interval_create_from_date_string('1 second')) : null,
|
||||
'edit_user_id' => $edited ? rand(1, $users) : null,
|
||||
'hide_time' => $hidden ? $startTime = date_add($startTime, date_interval_create_from_date_string('1 second')) : null,
|
||||
'hide_user_id' => $hidden ? rand(1, $users) : null,
|
||||
]);
|
||||
|
||||
$posts[] = $post;
|
||||
|
||||
if (! $lastPost or $post->time >= $lastPost->time) {
|
||||
$lastPost = $post;
|
||||
}
|
||||
}
|
||||
|
||||
if (rand(1, 20) == 1) {
|
||||
$numberOffset += rand(0, 3);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the discussion's last post details.
|
||||
if ($lastPost) {
|
||||
$discussion->last_time = $lastPost->time;
|
||||
$discussion->last_user_id = $lastPost->user_id;
|
||||
$discussion->last_post_id = $lastPost->id;
|
||||
$discussion->last_post_number = $lastPost->number;
|
||||
}
|
||||
|
||||
$discussion->number_index = $j + 1 + $numberOffset;
|
||||
|
||||
$discussion->save();
|
||||
|
||||
// Give some users some random discussion state data.
|
||||
for ($j = rand(0, 100); $j < 100; $j++) {
|
||||
try {
|
||||
DiscussionState::create([
|
||||
'discussion_id' => $discussion->id,
|
||||
'user_id' => rand(1, $users),
|
||||
'read_number' => rand(0, $posts_count - 1),
|
||||
'read_time' => $faker->dateTimeBetween($discussion->start_time, 'now')
|
||||
]);
|
||||
} catch (\Illuminate\Database\QueryException $e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update user post and discussion counts.
|
||||
$prefix = DB::getTablePrefix();
|
||||
DB::table('users')->update([
|
||||
'discussions_count' => DB::raw('(SELECT COUNT(id) FROM '.$prefix.'discussions WHERE start_user_id = '.$prefix.'users.id)'),
|
||||
'posts_count' => DB::raw('(SELECT COUNT(id) FROM '.$prefix.'posts WHERE user_id = '.$prefix.'users.id and type = "comment")'),
|
||||
]);
|
||||
}
|
||||
}
|
24
src/Core/Seeders/GroupsTableSeeder.php
Normal file
24
src/Core/Seeders/GroupsTableSeeder.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php namespace Flarum\Core\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Flarum\Core\Models\Group;
|
||||
|
||||
class GroupsTableSeeder extends Seeder {
|
||||
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Group::unguard();
|
||||
Group::truncate();
|
||||
|
||||
$groups = ['Administrator', 'Guest', 'Member', 'Moderator', 'Staff'];
|
||||
foreach ($groups as $group) {
|
||||
Group::create(['name' => $group]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
47
src/Core/Seeders/PermissionsTableSeeder.php
Normal file
47
src/Core/Seeders/PermissionsTableSeeder.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php namespace Flarum\Core\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Flarum\Core\Models\Permission;
|
||||
|
||||
class PermissionsTableSeeder extends Seeder {
|
||||
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Permission::truncate();
|
||||
|
||||
$permissions = [
|
||||
|
||||
// Guests can view the forum
|
||||
['group.2' , 'forum' , 'view'],
|
||||
['group.2' , 'forum' , 'register'],
|
||||
|
||||
// Members can create and reply to discussions + edit their own stuff
|
||||
['group.3' , 'forum' , 'startDiscussion'],
|
||||
['group.3' , 'discussion' , 'editOwn'],
|
||||
['group.3' , 'discussion' , 'reply'],
|
||||
['group.3' , 'post' , 'editOwn'],
|
||||
|
||||
// Moderators can edit + delete stuff and suspend users
|
||||
['group.4' , 'discussion' , 'delete'],
|
||||
['group.4' , 'discussion' , 'edit'],
|
||||
['group.4' , 'post' , 'delete'],
|
||||
['group.4' , 'post' , 'edit'],
|
||||
['group.4' , 'user' , 'suspend'],
|
||||
|
||||
];
|
||||
foreach ($permissions as &$permission) {
|
||||
$permission = [
|
||||
'grantee' => $permission[0],
|
||||
'entity' => $permission[1],
|
||||
'permission' => $permission[2]
|
||||
];
|
||||
}
|
||||
Permission::insert($permissions);
|
||||
}
|
||||
|
||||
}
|
42
src/Core/Seeders/UsersTableSeeder.php
Normal file
42
src/Core/Seeders/UsersTableSeeder.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php namespace Flarum\Core\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Flarum\Core\Models\User;
|
||||
|
||||
class UsersTableSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
User::unguard();
|
||||
|
||||
$faker = \Faker\Factory::create();
|
||||
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$user = User::create([
|
||||
'username' => $faker->userName,
|
||||
'email' => $faker->safeEmail,
|
||||
'is_confirmed' => true,
|
||||
'is_activated' => true,
|
||||
'password' => 'password',
|
||||
'join_time' => $faker->dateTimeThisYear
|
||||
]);
|
||||
|
||||
// Assign the users to the 'Member' group, and possibly some others.
|
||||
$user->groups()->attach(3);
|
||||
if (rand(1, 50) == 1) {
|
||||
$user->groups()->attach(4);
|
||||
}
|
||||
if (rand(1, 20) == 1) {
|
||||
$user->groups()->attach(5);
|
||||
}
|
||||
if (rand(1, 20) == 1) {
|
||||
$user->groups()->attach(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
src/Core/Support/Actor.php
Executable file
19
src/Core/Support/Actor.php
Executable file
@@ -0,0 +1,19 @@
|
||||
<?php namespace Flarum\Core\Support;
|
||||
|
||||
use Flarum\Core\Models\User;
|
||||
use Flarum\Core\Models\Guest;
|
||||
|
||||
class Actor
|
||||
{
|
||||
protected $user;
|
||||
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user ?: new Guest;
|
||||
}
|
||||
|
||||
public function setUser(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
16
src/Core/Support/DispatchesEvents.php
Normal file
16
src/Core/Support/DispatchesEvents.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php namespace Flarum\Core\Support;
|
||||
|
||||
trait DispatchesEvents
|
||||
{
|
||||
/**
|
||||
* Dispatch all events for an entity.
|
||||
*
|
||||
* @param object $entity
|
||||
*/
|
||||
public function dispatchEventsFor($entity)
|
||||
{
|
||||
foreach ($entity->releaseEvents() as $event) {
|
||||
event($event);
|
||||
}
|
||||
}
|
||||
}
|
33
src/Core/Support/EventGenerator.php
Normal file
33
src/Core/Support/EventGenerator.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php namespace Flarum\Core\Support;
|
||||
|
||||
trait EventGenerator
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $pendingEvents = [];
|
||||
|
||||
/**
|
||||
* Raise a new event
|
||||
*
|
||||
* @param $event
|
||||
*/
|
||||
public function raise($event)
|
||||
{
|
||||
$this->pendingEvents[] = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return and reset all pending events
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function releaseEvents()
|
||||
{
|
||||
$events = $this->pendingEvents;
|
||||
|
||||
$this->pendingEvents = [];
|
||||
|
||||
return $events;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user