mirror of
https://github.com/flarum/core.git
synced 2025-10-12 07:24:27 +02:00
Extract new Flarum\Discussion namespace
This commit is contained in:
@@ -1,130 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Access;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Flarum\Core\Discussion;
|
||||
use Flarum\Event\ScopeHiddenDiscussionVisibility;
|
||||
use Flarum\Event\ScopePrivateDiscussionVisibility;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Flarum\User\AbstractPolicy;
|
||||
use Flarum\User\Gate;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class DiscussionPolicy extends AbstractPolicy
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $model = Discussion::class;
|
||||
|
||||
/**
|
||||
* @var SettingsRepositoryInterface
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* @var Gate
|
||||
*/
|
||||
protected $gate;
|
||||
|
||||
/**
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $events;
|
||||
|
||||
/**
|
||||
* @param SettingsRepositoryInterface $settings
|
||||
* @param Gate $gate
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function __construct(SettingsRepositoryInterface $settings, Gate $gate, Dispatcher $events)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
$this->gate = $gate;
|
||||
$this->events = $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $actor
|
||||
* @param string $ability
|
||||
* @return bool|null
|
||||
*/
|
||||
public function after(User $actor, $ability)
|
||||
{
|
||||
if ($actor->hasPermission('discussion.'.$ability)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $actor
|
||||
* @param Builder $query
|
||||
*/
|
||||
public function find(User $actor, Builder $query)
|
||||
{
|
||||
// Hide private discussions per default.
|
||||
$query->where(function ($query) use ($actor) {
|
||||
$query->where('discussions.is_private', false);
|
||||
|
||||
$this->events->fire(
|
||||
new ScopePrivateDiscussionVisibility($query, $actor)
|
||||
);
|
||||
});
|
||||
|
||||
if ($actor->cannot('viewDiscussions')) {
|
||||
$query->whereRaw('FALSE');
|
||||
} elseif (! $actor->hasPermission('discussion.hide')) {
|
||||
$query->where(function ($query) use ($actor) {
|
||||
$query->whereNull('discussions.hide_time')
|
||||
->where('comments_count', '>', 0)
|
||||
->orWhere('start_user_id', $actor->id);
|
||||
|
||||
$this->events->fire(
|
||||
new ScopeHiddenDiscussionVisibility($query, $actor, 'discussion.hide')
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $actor
|
||||
* @param Discussion $discussion
|
||||
* @return bool|null
|
||||
*/
|
||||
public function rename(User $actor, Discussion $discussion)
|
||||
{
|
||||
if ($discussion->start_user_id == $actor->id) {
|
||||
$allowRenaming = $this->settings->get('allow_renaming');
|
||||
|
||||
if ($allowRenaming === '-1'
|
||||
|| ($allowRenaming === 'reply' && $discussion->participants_count <= 1)
|
||||
|| ($discussion->start_time->diffInMinutes(new Carbon) < $allowRenaming)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $actor
|
||||
* @param Discussion $discussion
|
||||
* @return bool|null
|
||||
*/
|
||||
public function hide(User $actor, Discussion $discussion)
|
||||
{
|
||||
if ($discussion->start_user_id == $actor->id && $discussion->participants_count <= 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,9 +13,9 @@ namespace Flarum\Core\Command;
|
||||
|
||||
use Flarum\Core\Access\AssertPermissionTrait;
|
||||
use Flarum\User\Exception\PermissionDeniedException;
|
||||
use Flarum\Core\Repository\DiscussionRepository;
|
||||
use Flarum\Discussion\DiscussionRepository;
|
||||
use Flarum\Foundation\DispatchEventsTrait;
|
||||
use Flarum\Event\DiscussionWillBeDeleted;
|
||||
use Flarum\Discussion\Event\Deleting;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class DeleteDiscussionHandler
|
||||
@@ -24,7 +24,7 @@ class DeleteDiscussionHandler
|
||||
use AssertPermissionTrait;
|
||||
|
||||
/**
|
||||
* @var DiscussionRepository
|
||||
* @var \Flarum\Discussion\DiscussionRepository
|
||||
*/
|
||||
protected $discussions;
|
||||
|
||||
@@ -40,7 +40,7 @@ class DeleteDiscussionHandler
|
||||
|
||||
/**
|
||||
* @param DeleteDiscussion $command
|
||||
* @return \Flarum\Core\Discussion
|
||||
* @return \Flarum\Discussion\Discussion
|
||||
* @throws PermissionDeniedException
|
||||
*/
|
||||
public function handle(DeleteDiscussion $command)
|
||||
@@ -52,7 +52,7 @@ class DeleteDiscussionHandler
|
||||
$this->assertCan($actor, 'delete', $discussion);
|
||||
|
||||
$this->events->fire(
|
||||
new DiscussionWillBeDeleted($discussion, $actor, $command->data)
|
||||
new Deleting($discussion, $actor, $command->data)
|
||||
);
|
||||
|
||||
$discussion->delete();
|
||||
|
@@ -13,10 +13,10 @@ namespace Flarum\Core\Command;
|
||||
|
||||
use Flarum\Core\Access\AssertPermissionTrait;
|
||||
use Flarum\User\Exception\PermissionDeniedException;
|
||||
use Flarum\Core\Repository\DiscussionRepository;
|
||||
use Flarum\Discussion\DiscussionRepository;
|
||||
use Flarum\Foundation\DispatchEventsTrait;
|
||||
use Flarum\Core\Validator\DiscussionValidator;
|
||||
use Flarum\Event\DiscussionWillBeSaved;
|
||||
use Flarum\Discussion\DiscussionValidator;
|
||||
use Flarum\Discussion\Event\Saving;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class EditDiscussionHandler
|
||||
@@ -48,7 +48,7 @@ class EditDiscussionHandler
|
||||
|
||||
/**
|
||||
* @param EditDiscussion $command
|
||||
* @return \Flarum\Core\Discussion
|
||||
* @return \Flarum\Discussion\Discussion
|
||||
* @throws \Flarum\User\Exception\PermissionDeniedException
|
||||
*/
|
||||
public function handle(EditDiscussion $command)
|
||||
@@ -76,7 +76,7 @@ class EditDiscussionHandler
|
||||
}
|
||||
|
||||
$this->events->fire(
|
||||
new DiscussionWillBeSaved($discussion, $actor, $data)
|
||||
new Saving($discussion, $actor, $data)
|
||||
);
|
||||
|
||||
$this->validator->assertValid($discussion->getDirty());
|
||||
|
@@ -15,7 +15,7 @@ use DateTime;
|
||||
use Flarum\Core\Access\AssertPermissionTrait;
|
||||
use Flarum\Core\Notification\NotificationSyncer;
|
||||
use Flarum\Post\CommentPost;
|
||||
use Flarum\Core\Repository\DiscussionRepository;
|
||||
use Flarum\Discussion\DiscussionRepository;
|
||||
use Flarum\Foundation\DispatchEventsTrait;
|
||||
use Flarum\Post\PostValidator;
|
||||
use Flarum\Post\Event\Saving;
|
||||
|
@@ -12,9 +12,9 @@
|
||||
namespace Flarum\Core\Command;
|
||||
|
||||
use Flarum\Core\Access\AssertPermissionTrait;
|
||||
use Flarum\Core\Repository\DiscussionRepository;
|
||||
use Flarum\Discussion\DiscussionRepository;
|
||||
use Flarum\Foundation\DispatchEventsTrait;
|
||||
use Flarum\Event\DiscussionStateWillBeSaved;
|
||||
use Flarum\Discussion\Event\UserDataSaving;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class ReadDiscussionHandler
|
||||
@@ -54,7 +54,7 @@ class ReadDiscussionHandler
|
||||
$state->read($command->readNumber);
|
||||
|
||||
$this->events->fire(
|
||||
new DiscussionStateWillBeSaved($state)
|
||||
new UserDataSaving($state)
|
||||
);
|
||||
|
||||
$state->save();
|
||||
|
@@ -13,10 +13,10 @@ namespace Flarum\Core\Command;
|
||||
|
||||
use Exception;
|
||||
use Flarum\Core\Access\AssertPermissionTrait;
|
||||
use Flarum\Core\Discussion;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Foundation\DispatchEventsTrait;
|
||||
use Flarum\Core\Validator\DiscussionValidator;
|
||||
use Flarum\Event\DiscussionWillBeSaved;
|
||||
use Flarum\Discussion\DiscussionValidator;
|
||||
use Flarum\Discussion\Event\Saving;
|
||||
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
|
||||
use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
|
||||
|
||||
@@ -31,14 +31,14 @@ class StartDiscussionHandler
|
||||
protected $bus;
|
||||
|
||||
/**
|
||||
* @var DiscussionValidator
|
||||
* @var \Flarum\Discussion\DiscussionValidator
|
||||
*/
|
||||
protected $validator;
|
||||
|
||||
/**
|
||||
* @param EventDispatcher $events
|
||||
* @param BusDispatcher $bus
|
||||
* @param DiscussionValidator $validator
|
||||
* @param \Flarum\Discussion\DiscussionValidator $validator
|
||||
*/
|
||||
public function __construct(EventDispatcher $events, BusDispatcher $bus, DiscussionValidator $validator)
|
||||
{
|
||||
@@ -70,7 +70,7 @@ class StartDiscussionHandler
|
||||
);
|
||||
|
||||
$this->events->fire(
|
||||
new DiscussionWillBeSaved($discussion, $actor, $data)
|
||||
new Saving($discussion, $actor, $data)
|
||||
);
|
||||
|
||||
$this->validator->assertValid($discussion->getAttributes());
|
||||
|
@@ -107,13 +107,13 @@ class CoreServiceProvider extends AbstractServiceProvider
|
||||
$events = $this->app->make('events');
|
||||
|
||||
$events->subscribe('Flarum\Core\Listener\SelfDemotionGuard');
|
||||
$events->subscribe('Flarum\Core\Listener\DiscussionMetadataUpdater');
|
||||
$events->subscribe('Flarum\Discussion\DiscussionMetadataUpdater');
|
||||
$events->subscribe('Flarum\User\UserMetadataUpdater');
|
||||
$events->subscribe('Flarum\Core\Listener\ExtensionValidator');
|
||||
$events->subscribe('Flarum\User\EmailConfirmationMailer');
|
||||
$events->subscribe('Flarum\Core\Listener\DiscussionRenamedNotifier');
|
||||
$events->subscribe('Flarum\Discussion\DiscussionRenamedNotifier');
|
||||
|
||||
$events->subscribe('Flarum\Core\Access\DiscussionPolicy');
|
||||
$events->subscribe('Flarum\Discussion\DiscussionPolicy');
|
||||
$events->subscribe('Flarum\Core\Access\GroupPolicy');
|
||||
$events->subscribe('Flarum\Post\PostPolicy');
|
||||
$events->subscribe('Flarum\User\UserPolicy');
|
||||
|
@@ -1,463 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core;
|
||||
|
||||
use Flarum\Post\MergeableInterface;
|
||||
use Flarum\Foundation\EventGeneratorTrait;
|
||||
use Flarum\Database\ScopeVisibilityTrait;
|
||||
use Flarum\Database\AbstractModel;
|
||||
use Flarum\Event\DiscussionWasDeleted;
|
||||
use Flarum\Event\DiscussionWasHidden;
|
||||
use Flarum\Event\DiscussionWasRenamed;
|
||||
use Flarum\Event\DiscussionWasRestored;
|
||||
use Flarum\Event\DiscussionWasStarted;
|
||||
use Flarum\Post\Event\Deleted;
|
||||
use Flarum\Event\ScopePostVisibility;
|
||||
use Flarum\Post\Post;
|
||||
use Flarum\User\Guest;
|
||||
use Flarum\User\User;
|
||||
use Flarum\Util\Str;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $title
|
||||
* @property string $slug
|
||||
* @property int $comments_count
|
||||
* @property int $participants_count
|
||||
* @property int $number_index
|
||||
* @property \Carbon\Carbon $start_time
|
||||
* @property int|null $start_user_id
|
||||
* @property int|null $start_post_id
|
||||
* @property \Carbon\Carbon|null $last_time
|
||||
* @property int|null $last_user_id
|
||||
* @property int|null $last_post_id
|
||||
* @property int|null $last_post_number
|
||||
* @property \Carbon\Carbon|null $hide_time
|
||||
* @property int|null $hide_user_id
|
||||
* @property DiscussionState|null $state
|
||||
* @property \Illuminate\Database\Eloquent\Collection $posts
|
||||
* @property \Illuminate\Database\Eloquent\Collection $comments
|
||||
* @property \Illuminate\Database\Eloquent\Collection $participants
|
||||
* @property Post|null $startPost
|
||||
* @property User|null $startUser
|
||||
* @property Post|null $lastPost
|
||||
* @property User|null $lastUser
|
||||
* @property \Illuminate\Database\Eloquent\Collection $readers
|
||||
* @property bool $is_private
|
||||
*/
|
||||
class Discussion extends AbstractModel
|
||||
{
|
||||
use EventGeneratorTrait;
|
||||
use ScopeVisibilityTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $table = 'discussions';
|
||||
|
||||
/**
|
||||
* An array of posts that have been modified during this request.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $modifiedPosts = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $dates = ['start_time', 'last_time', 'hide_time'];
|
||||
|
||||
/**
|
||||
* Casts properties to a specific type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'is_private' => 'boolean'
|
||||
];
|
||||
|
||||
/**
|
||||
* The user for which the state relationship should be loaded.
|
||||
*
|
||||
* @var User
|
||||
*/
|
||||
protected static $stateUser;
|
||||
|
||||
/**
|
||||
* Boot the model.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleted(function ($discussion) {
|
||||
$discussion->raise(new DiscussionWasDeleted($discussion));
|
||||
|
||||
// Delete all of the posts in the discussion. Before we delete them
|
||||
// in a big batch query, we will loop through them and raise a
|
||||
// PostWasDeleted event for each post.
|
||||
$posts = $discussion->posts()->allTypes();
|
||||
|
||||
foreach ($posts->get() as $post) {
|
||||
$discussion->raise(new Deleted($post));
|
||||
}
|
||||
|
||||
$posts->delete();
|
||||
|
||||
// Delete all of the 'state' records for all of the users who have
|
||||
// read the discussion.
|
||||
$discussion->readers()->detach();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new discussion. Raises the DiscussionWasStarted event.
|
||||
*
|
||||
* @param string $title
|
||||
* @param User $user
|
||||
* @return static
|
||||
*/
|
||||
public static function start($title, User $user)
|
||||
{
|
||||
$discussion = new static;
|
||||
|
||||
$discussion->title = $title;
|
||||
$discussion->start_time = time();
|
||||
$discussion->start_user_id = $user->id;
|
||||
|
||||
$discussion->setRelation('startUser', $user);
|
||||
|
||||
$discussion->raise(new DiscussionWasStarted($discussion));
|
||||
|
||||
return $discussion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename the discussion. Raises the DiscussionWasRenamed event.
|
||||
*
|
||||
* @param string $title
|
||||
* @return $this
|
||||
*/
|
||||
public function rename($title)
|
||||
{
|
||||
if ($this->title !== $title) {
|
||||
$oldTitle = $this->title;
|
||||
$this->title = $title;
|
||||
|
||||
$this->raise(new DiscussionWasRenamed($this, $oldTitle));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the discussion.
|
||||
*
|
||||
* @param User $actor
|
||||
* @return $this
|
||||
*/
|
||||
public function hide(User $actor = null)
|
||||
{
|
||||
if (! $this->hide_time) {
|
||||
$this->hide_time = time();
|
||||
$this->hide_user_id = $actor ? $actor->id : null;
|
||||
|
||||
$this->raise(new DiscussionWasHidden($this));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the discussion.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function restore()
|
||||
{
|
||||
if ($this->hide_time !== null) {
|
||||
$this->hide_time = null;
|
||||
$this->hide_user_id = null;
|
||||
|
||||
$this->raise(new DiscussionWasRestored($this));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the discussion's start post details.
|
||||
*
|
||||
* @param Post $post
|
||||
* @return $this
|
||||
*/
|
||||
public function setStartPost(Post $post)
|
||||
{
|
||||
$this->start_time = $post->time;
|
||||
$this->start_user_id = $post->user_id;
|
||||
$this->start_post_id = $post->id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the discussion's last post details.
|
||||
*
|
||||
* @param 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()->latest('time')->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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the discussion's participants count.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function refreshParticipantsCount()
|
||||
{
|
||||
$this->participants_count = $this->participants()->count('users.id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a post, attempting to merge it with the discussion's last post.
|
||||
*
|
||||
* The merge logic is delegated to the new post. (As an example, a
|
||||
* DiscussionRenamedPost will merge if adjacent to another
|
||||
* DiscussionRenamedPost, and delete if the title has been reverted
|
||||
* completely.)
|
||||
*
|
||||
* @param \Flarum\Post\MergeableInterface $post The post to save.
|
||||
* @return Post The resulting post. It may or may not be the same post as
|
||||
* was originally intended to be saved. It also may not exist, if the
|
||||
* merge logic resulted in deletion.
|
||||
*/
|
||||
public function mergePost(MergeableInterface $post)
|
||||
{
|
||||
$lastPost = $this->posts()->latest('time')->first();
|
||||
|
||||
$post = $post->saveAfter($lastPost);
|
||||
|
||||
return $this->modifiedPosts[] = $post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the posts that have been modified during this request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getModifiedPosts()
|
||||
{
|
||||
return $this->modifiedPosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's posts.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function posts()
|
||||
{
|
||||
return $this->hasMany('Flarum\Post\Post');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's posts, but only ones which
|
||||
* are visible to the given user.
|
||||
*
|
||||
* @param User $user
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function postsVisibleTo(User $user)
|
||||
{
|
||||
$relation = $this->posts();
|
||||
|
||||
static::$dispatcher->fire(
|
||||
new ScopePostVisibility($this, $relation->getQuery(), $user)
|
||||
);
|
||||
|
||||
return $relation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's publicly-visible comments.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function comments()
|
||||
{
|
||||
return $this->postsVisibleTo(new Guest)->where('type', 'comment');
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the discussion's participants (a list of unique users who have
|
||||
* posted in the discussion).
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function participants()
|
||||
{
|
||||
return User::join('posts', 'posts.user_id', '=', 'users.id')
|
||||
->where('posts.discussion_id', $this->id)
|
||||
->select('users.*')
|
||||
->distinct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's first post.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function startPost()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Post\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\User\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\Post\Post', 'last_post_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's most recent author.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function lastUser()
|
||||
{
|
||||
return $this->belongsTo('Flarum\User\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\User\User', 'users_discussions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the relationship with the discussion's state for a particular
|
||||
* user.
|
||||
*
|
||||
* If no user is passed (i.e. in the case of eager loading the 'state'
|
||||
* relation), then the static `$stateUser` property is used.
|
||||
*
|
||||
* @see Discussion::setStateUser()
|
||||
*
|
||||
* @param User|null $user
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function state(User $user = null)
|
||||
{
|
||||
$user = $user ?: static::$stateUser;
|
||||
|
||||
return $this->hasOne('Flarum\Core\DiscussionState')->where('user_id', $user ? $user->id : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state model for a user, or instantiate a new one if it does not
|
||||
* exist.
|
||||
*
|
||||
* @param User $user
|
||||
* @return \Flarum\Core\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 User $user
|
||||
*/
|
||||
public static function setStateUser(User $user)
|
||||
{
|
||||
static::$stateUser = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the discussion title.
|
||||
*
|
||||
* This automatically creates a matching slug for the discussion.
|
||||
*
|
||||
* @param string $title
|
||||
*/
|
||||
protected function setTitleAttribute($title)
|
||||
{
|
||||
$this->attributes['title'] = $title;
|
||||
$this->slug = Str::slug($title);
|
||||
}
|
||||
}
|
@@ -11,9 +11,10 @@
|
||||
|
||||
namespace Flarum\Core;
|
||||
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Foundation\EventGeneratorTrait;
|
||||
use Flarum\Database\AbstractModel;
|
||||
use Flarum\Event\DiscussionWasRead;
|
||||
use Flarum\Discussion\Event\UserRead;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
@@ -57,7 +58,7 @@ class DiscussionState extends AbstractModel
|
||||
$this->read_number = $number;
|
||||
$this->read_time = time();
|
||||
|
||||
$this->raise(new DiscussionWasRead($this));
|
||||
$this->raise(new UserRead($this));
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -70,7 +71,7 @@ class DiscussionState extends AbstractModel
|
||||
*/
|
||||
public function discussion()
|
||||
{
|
||||
return $this->belongsTo('Flarum\Core\Discussion', 'discussion_id');
|
||||
return $this->belongsTo('Flarum\Discussion\Discussion', 'discussion_id');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,104 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Listener;
|
||||
|
||||
use Flarum\Post\Post;
|
||||
use Flarum\Post\Event\Deleted;
|
||||
use Flarum\Post\Event\Hidden;
|
||||
use Flarum\Post\Event\Posted;
|
||||
use Flarum\Post\Event\Restored;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class DiscussionMetadataUpdater
|
||||
{
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(Posted::class, [$this, 'whenPostWasPosted']);
|
||||
$events->listen(Deleted::class, [$this, 'whenPostWasDeleted']);
|
||||
$events->listen(Hidden::class, [$this, 'whenPostWasHidden']);
|
||||
$events->listen(Restored::class, [$this, 'whenPostWasRestored']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Posted $event
|
||||
*/
|
||||
public function whenPostWasPosted(Posted $event)
|
||||
{
|
||||
$discussion = $event->post->discussion;
|
||||
|
||||
if ($discussion && $discussion->exists) {
|
||||
$discussion->refreshCommentsCount();
|
||||
$discussion->refreshLastPost();
|
||||
$discussion->refreshParticipantsCount();
|
||||
$discussion->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\Post\Event\Deleted $event
|
||||
*/
|
||||
public function whenPostWasDeleted(Deleted $event)
|
||||
{
|
||||
$this->removePost($event->post);
|
||||
|
||||
$discussion = $event->post->discussion;
|
||||
|
||||
if ($discussion && $discussion->posts()->count() === 0) {
|
||||
$discussion->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\Post\Event\Hidden $event
|
||||
*/
|
||||
public function whenPostWasHidden(Hidden $event)
|
||||
{
|
||||
$this->removePost($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Restored $event
|
||||
*/
|
||||
public function whenPostWasRestored(Restored $event)
|
||||
{
|
||||
$discussion = $event->post->discussion;
|
||||
|
||||
if ($discussion && $discussion->exists) {
|
||||
$discussion->refreshCommentsCount();
|
||||
$discussion->refreshParticipantsCount();
|
||||
$discussion->refreshLastPost();
|
||||
$discussion->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Post $post
|
||||
*/
|
||||
protected function removePost(Post $post)
|
||||
{
|
||||
$discussion = $post->discussion;
|
||||
|
||||
if ($discussion && $discussion->exists) {
|
||||
$discussion->refreshCommentsCount();
|
||||
$discussion->refreshParticipantsCount();
|
||||
|
||||
if ($discussion->last_post_id == $post->id) {
|
||||
$discussion->refreshLastPost();
|
||||
}
|
||||
|
||||
$discussion->save();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,67 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Listener;
|
||||
|
||||
use Flarum\Core\Notification\DiscussionRenamedBlueprint;
|
||||
use Flarum\Core\Notification\NotificationSyncer;
|
||||
use Flarum\Post\DiscussionRenamedPost;
|
||||
use Flarum\Event\DiscussionWasRenamed;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class DiscussionRenamedNotifier
|
||||
{
|
||||
/**
|
||||
* @var NotificationSyncer
|
||||
*/
|
||||
protected $notifications;
|
||||
|
||||
/**
|
||||
* @param NotificationSyncer $notifications
|
||||
*/
|
||||
public function __construct(NotificationSyncer $notifications)
|
||||
{
|
||||
$this->notifications = $notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(DiscussionWasRenamed::class, [$this, 'whenDiscussionWasRenamed']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Flarum\Event\DiscussionWasRenamed $event
|
||||
*/
|
||||
public function whenDiscussionWasRenamed(DiscussionWasRenamed $event)
|
||||
{
|
||||
$post = DiscussionRenamedPost::reply(
|
||||
$event->discussion->id,
|
||||
$event->actor->id,
|
||||
$event->oldTitle,
|
||||
$event->discussion->title
|
||||
);
|
||||
|
||||
$post = $event->discussion->mergePost($post);
|
||||
|
||||
if ($event->discussion->start_user_id !== $event->actor->id) {
|
||||
$blueprint = new DiscussionRenamedBlueprint($post);
|
||||
|
||||
if ($post->exists) {
|
||||
$this->notifications->sync($blueprint, [$event->discussion->startUser]);
|
||||
} else {
|
||||
$this->notifications->delete($blueprint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -56,7 +56,7 @@ class Notification extends AbstractModel
|
||||
* A map of notification types and the model classes to use for their
|
||||
* subjects. For example, the 'discussionRenamed' notification type, which
|
||||
* represents that a user's discussion was renamed, has the subject model
|
||||
* class 'Flarum\Core\Discussion'.
|
||||
* class 'Flarum\Discussion\Discussion'.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
|
@@ -65,6 +65,6 @@ class DiscussionRenamedBlueprint implements BlueprintInterface
|
||||
*/
|
||||
public static function getSubjectModel()
|
||||
{
|
||||
return 'Flarum\Core\Discussion';
|
||||
return 'Flarum\Discussion\Discussion';
|
||||
}
|
||||
}
|
||||
|
@@ -1,76 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Repository;
|
||||
|
||||
use Flarum\Core\Discussion;
|
||||
use Flarum\User\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Query\Expression;
|
||||
|
||||
class DiscussionRepository
|
||||
{
|
||||
/**
|
||||
* Get a new query builder for the discussions table.
|
||||
*
|
||||
* @return 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 int $id
|
||||
* @param User $user
|
||||
* @return \Flarum\Core\Discussion
|
||||
*/
|
||||
public function findOrFail($id, User $user = null)
|
||||
{
|
||||
$query = Discussion::where('id', $id);
|
||||
|
||||
return $this->scopeVisibleTo($query, $user)->firstOrFail();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IDs of discussions which a user has read completely.
|
||||
*
|
||||
* @param 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', '>=', new Expression('last_post_number'))
|
||||
->lists('id')
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include records that are visible to a user.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param User $user
|
||||
* @return Builder
|
||||
*/
|
||||
protected function scopeVisibleTo(Builder $query, User $user = null)
|
||||
{
|
||||
if ($user !== null) {
|
||||
$query->whereVisibleTo($user);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Search\Discussion;
|
||||
|
||||
use Flarum\Core\Search\AbstractSearch;
|
||||
|
||||
/**
|
||||
* An object which represents the internal state of a search for discussions:
|
||||
* the search query, the user performing the search, the fallback sort order,
|
||||
* relevant post information, and a log of which gambits have been used.
|
||||
*/
|
||||
class DiscussionSearch extends AbstractSearch
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultSort = ['lastTime' => 'desc'];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $relevantPostIds = [];
|
||||
|
||||
/**
|
||||
* Get the related IDs for each result.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function getRelevantPostIds()
|
||||
{
|
||||
return $this->relevantPostIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the relevant post IDs for the results.
|
||||
*
|
||||
* @param array $relevantPostIds
|
||||
* @return void
|
||||
*/
|
||||
public function setRelevantPostIds(array $relevantPostIds)
|
||||
{
|
||||
$this->relevantPostIds = $relevantPostIds;
|
||||
}
|
||||
}
|
@@ -1,137 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Search\Discussion;
|
||||
|
||||
use Flarum\Core\Discussion;
|
||||
use Flarum\Core\Repository\DiscussionRepository;
|
||||
use Flarum\Post\PostRepository;
|
||||
use Flarum\Core\Search\ApplySearchParametersTrait;
|
||||
use Flarum\Core\Search\GambitManager;
|
||||
use Flarum\Core\Search\SearchCriteria;
|
||||
use Flarum\Core\Search\SearchResults;
|
||||
use Flarum\Event\ConfigureDiscussionSearch;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
/**
|
||||
* Takes a DiscussionSearchCriteria object, performs a search using gambits,
|
||||
* and spits out a DiscussionSearchResults object.
|
||||
*/
|
||||
class DiscussionSearcher
|
||||
{
|
||||
use ApplySearchParametersTrait;
|
||||
|
||||
/**
|
||||
* @var GambitManager
|
||||
*/
|
||||
protected $gambits;
|
||||
|
||||
/**
|
||||
* @var DiscussionRepository
|
||||
*/
|
||||
protected $discussions;
|
||||
|
||||
/**
|
||||
* @var PostRepository
|
||||
*/
|
||||
protected $posts;
|
||||
|
||||
/**
|
||||
* @param GambitManager $gambits
|
||||
* @param DiscussionRepository $discussions
|
||||
* @param PostRepository $posts
|
||||
*/
|
||||
public function __construct(
|
||||
GambitManager $gambits,
|
||||
DiscussionRepository $discussions,
|
||||
PostRepository $posts
|
||||
) {
|
||||
$this->gambits = $gambits;
|
||||
$this->discussions = $discussions;
|
||||
$this->posts = $posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SearchCriteria $criteria
|
||||
* @param int|null $limit
|
||||
* @param int $offset
|
||||
* @param array $load An array of relationships to load on the results.
|
||||
* @return SearchResults
|
||||
*/
|
||||
public function search(SearchCriteria $criteria, $limit = null, $offset = 0, array $load = [])
|
||||
{
|
||||
$actor = $criteria->actor;
|
||||
|
||||
$query = $this->discussions->query()->whereVisibleTo($actor);
|
||||
|
||||
// Construct an object which represents this search for discussions.
|
||||
// Apply gambits to it, sort, and paging criteria. Also give extensions
|
||||
// an opportunity to modify it.
|
||||
$search = new DiscussionSearch($query->getQuery(), $actor);
|
||||
|
||||
$this->gambits->apply($search, $criteria->query);
|
||||
$this->applySort($search, $criteria->sort);
|
||||
$this->applyOffset($search, $offset);
|
||||
$this->applyLimit($search, $limit + 1);
|
||||
|
||||
// TODO: inject dispatcher
|
||||
event(new ConfigureDiscussionSearch($search, $criteria));
|
||||
|
||||
// Execute the search query and retrieve the results. We get one more
|
||||
// results than the user asked for, so that we can say if there are more
|
||||
// results. If there are, we will get rid of that extra result.
|
||||
$discussions = $query->get();
|
||||
|
||||
$areMoreResults = $limit > 0 && $discussions->count() > $limit;
|
||||
|
||||
if ($areMoreResults) {
|
||||
$discussions->pop();
|
||||
}
|
||||
|
||||
// The relevant posts relationship isn't a typical Eloquent
|
||||
// relationship; rather, we need to extract that information from our
|
||||
// search object. We will delegate that task and prevent Eloquent
|
||||
// from trying to load it.
|
||||
if (in_array('relevantPosts', $load)) {
|
||||
$this->loadRelevantPosts($discussions, $search);
|
||||
|
||||
$load = array_diff($load, ['relevantPosts', 'relevantPosts.discussion', 'relevantPosts.user']);
|
||||
}
|
||||
|
||||
Discussion::setStateUser($actor);
|
||||
$discussions->load($load);
|
||||
|
||||
return new SearchResults($discussions, $areMoreResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load relevant posts onto each discussion using information from the
|
||||
* search.
|
||||
*
|
||||
* @param Collection $discussions
|
||||
* @param DiscussionSearch $search
|
||||
*/
|
||||
protected function loadRelevantPosts(Collection $discussions, DiscussionSearch $search)
|
||||
{
|
||||
$postIds = [];
|
||||
foreach ($search->getRelevantPostIds() as $relevantPostIds) {
|
||||
$postIds = array_merge($postIds, array_slice($relevantPostIds, 0, 2));
|
||||
}
|
||||
|
||||
$posts = $postIds ? $this->posts->findByIds($postIds, $search->getActor())->load('user')->all() : [];
|
||||
|
||||
foreach ($discussions as $discussion) {
|
||||
$discussion->relevantPosts = array_filter($posts, function ($post) use ($discussion) {
|
||||
return $post->discussion_id == $discussion->id;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Search\Discussion\Fulltext;
|
||||
|
||||
interface DriverInterface
|
||||
{
|
||||
/**
|
||||
* Return an array of arrays of post IDs, grouped by discussion ID, which
|
||||
* match the given string.
|
||||
*
|
||||
* @param string $string
|
||||
* @return array
|
||||
*/
|
||||
public function match($string);
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Search\Discussion\Fulltext;
|
||||
|
||||
use Flarum\Core\Post;
|
||||
|
||||
class MySqlFulltextDriver implements DriverInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function match($string)
|
||||
{
|
||||
$discussionIds = Post::where('type', 'comment')
|
||||
->whereRaw('MATCH (`content`) AGAINST (? IN BOOLEAN MODE)', [$string])
|
||||
->orderByRaw('MATCH (`content`) AGAINST (?) DESC', [$string])
|
||||
->lists('discussion_id', 'id');
|
||||
|
||||
$relevantPostIds = [];
|
||||
|
||||
foreach ($discussionIds as $postId => $discussionId) {
|
||||
$relevantPostIds[$discussionId][] = $postId;
|
||||
}
|
||||
|
||||
return $relevantPostIds;
|
||||
}
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Search\Discussion\Gambit;
|
||||
|
||||
use Flarum\User\UserRepository;
|
||||
use Flarum\Core\Search\AbstractRegexGambit;
|
||||
use Flarum\Core\Search\AbstractSearch;
|
||||
use Flarum\Core\Search\Discussion\DiscussionSearch;
|
||||
use LogicException;
|
||||
|
||||
class AuthorGambit extends AbstractRegexGambit
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $pattern = 'author:(.+)';
|
||||
|
||||
/**
|
||||
* @var \Flarum\User\UserRepository
|
||||
*/
|
||||
protected $users;
|
||||
|
||||
/**
|
||||
* @param \Flarum\User\UserRepository $users
|
||||
*/
|
||||
public function __construct(UserRepository $users)
|
||||
{
|
||||
$this->users = $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function conditions(AbstractSearch $search, array $matches, $negate)
|
||||
{
|
||||
if (! $search instanceof DiscussionSearch) {
|
||||
throw new LogicException('This gambit can only be applied on a DiscussionSearch');
|
||||
}
|
||||
|
||||
$usernames = trim($matches[1], '"');
|
||||
$usernames = explode(',', $usernames);
|
||||
|
||||
$ids = [];
|
||||
foreach ($usernames as $username) {
|
||||
$ids[] = $this->users->getIdForUsername($username);
|
||||
}
|
||||
|
||||
$search->getQuery()->whereIn('start_user_id', $ids, 'and', $negate);
|
||||
}
|
||||
}
|
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Search\Discussion\Gambit;
|
||||
|
||||
use Flarum\Core\Search\AbstractRegexGambit;
|
||||
use Flarum\Core\Search\AbstractSearch;
|
||||
use Flarum\Core\Search\Discussion\DiscussionSearch;
|
||||
use LogicException;
|
||||
|
||||
class CreatedGambit extends AbstractRegexGambit
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $pattern = 'created:(\d{4}\-\d\d\-\d\d)(\.\.(\d{4}\-\d\d\-\d\d))?';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function conditions(AbstractSearch $search, array $matches, $negate)
|
||||
{
|
||||
if (! $search instanceof DiscussionSearch) {
|
||||
throw new LogicException('This gambit can only be applied on a DiscussionSearch');
|
||||
}
|
||||
|
||||
// If we've just been provided with a single YYYY-MM-DD date, then find
|
||||
// discussions that were started on that exact date. But if we've been
|
||||
// provided with a YYYY-MM-DD..YYYY-MM-DD range, then find discussions
|
||||
// that were started during that period.
|
||||
if (empty($matches[3])) {
|
||||
$search->getQuery()->whereDate('start_time', $negate ? '!=' : '=', $matches[1]);
|
||||
} else {
|
||||
$search->getQuery()->whereBetween('start_time', [$matches[1], $matches[3]], 'and', $negate);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Search\Discussion\Gambit;
|
||||
|
||||
use Flarum\Core\Search\AbstractSearch;
|
||||
use Flarum\Core\Search\Discussion\DiscussionSearch;
|
||||
use Flarum\Core\Search\Discussion\Fulltext\DriverInterface;
|
||||
use Flarum\Core\Search\GambitInterface;
|
||||
use LogicException;
|
||||
|
||||
class FulltextGambit implements GambitInterface
|
||||
{
|
||||
/**
|
||||
* @var DriverInterface
|
||||
*/
|
||||
protected $fulltext;
|
||||
|
||||
/**
|
||||
* @param DriverInterface $fulltext
|
||||
*/
|
||||
public function __construct(DriverInterface $fulltext)
|
||||
{
|
||||
$this->fulltext = $fulltext;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply(AbstractSearch $search, $bit)
|
||||
{
|
||||
if (! $search instanceof DiscussionSearch) {
|
||||
throw new LogicException('This gambit can only be applied on a DiscussionSearch');
|
||||
}
|
||||
|
||||
$relevantPostIds = $this->fulltext->match($bit);
|
||||
|
||||
$discussionIds = array_keys($relevantPostIds);
|
||||
|
||||
$search->setRelevantPostIds($relevantPostIds);
|
||||
|
||||
$search->getQuery()->whereIn('id', $discussionIds);
|
||||
|
||||
$search->setDefaultSort(['id' => $discussionIds]);
|
||||
}
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Search\Discussion\Gambit;
|
||||
|
||||
use Flarum\Core\Search\AbstractRegexGambit;
|
||||
use Flarum\Core\Search\AbstractSearch;
|
||||
use Flarum\Core\Search\Discussion\DiscussionSearch;
|
||||
use LogicException;
|
||||
|
||||
class HiddenGambit extends AbstractRegexGambit
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $pattern = 'is:hidden';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function conditions(AbstractSearch $search, array $matches, $negate)
|
||||
{
|
||||
if (! $search instanceof DiscussionSearch) {
|
||||
throw new LogicException('This gambit can only be applied on a DiscussionSearch');
|
||||
}
|
||||
|
||||
$search->getQuery()->where(function ($query) use ($negate) {
|
||||
if ($negate) {
|
||||
$query->whereNull('hide_time')->where('comments_count', '>', 0);
|
||||
} else {
|
||||
$query->whereNotNull('hide_time')->orWhere('comments_count', 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Search\Discussion\Gambit;
|
||||
|
||||
use Flarum\Core\Repository\DiscussionRepository;
|
||||
use Flarum\Core\Search\AbstractRegexGambit;
|
||||
use Flarum\Core\Search\AbstractSearch;
|
||||
use Flarum\Core\Search\Discussion\DiscussionSearch;
|
||||
use LogicException;
|
||||
|
||||
class UnreadGambit extends AbstractRegexGambit
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $pattern = 'is:unread';
|
||||
|
||||
/**
|
||||
* @var \Flarum\Core\Repository\DiscussionRepository
|
||||
*/
|
||||
protected $discussions;
|
||||
|
||||
/**
|
||||
* @param DiscussionRepository $discussions
|
||||
*/
|
||||
public function __construct(DiscussionRepository $discussions)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function conditions(AbstractSearch $search, array $matches, $negate)
|
||||
{
|
||||
if (! $search instanceof DiscussionSearch) {
|
||||
throw new LogicException('This gambit can only be applied on a DiscussionSearch');
|
||||
}
|
||||
|
||||
$actor = $search->getActor();
|
||||
|
||||
if ($actor->exists) {
|
||||
$readIds = $this->discussions->getReadIds($actor);
|
||||
|
||||
$search->getQuery()->where(function ($query) use ($readIds, $negate, $actor) {
|
||||
if (! $negate) {
|
||||
$query->whereNotIn('id', $readIds)->where('last_time', '>', $actor->read_time ?: 0);
|
||||
} else {
|
||||
$query->whereIn('id', $readIds)->orWhere('last_time', '<=', $actor->read_time ?: 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -26,8 +26,8 @@ class SearchServiceProvider extends AbstractServiceProvider
|
||||
public function register()
|
||||
{
|
||||
$this->app->bind(
|
||||
'Flarum\Core\Search\Discussion\Fulltext\DriverInterface',
|
||||
'Flarum\Core\Search\Discussion\Fulltext\MySqlFulltextDriver'
|
||||
'Flarum\Discussion\Search\Fulltext\DriverInterface',
|
||||
'Flarum\Discussion\Search\Fulltext\MySqlFulltextDriver'
|
||||
);
|
||||
|
||||
$this->registerDiscussionGambits();
|
||||
@@ -56,16 +56,16 @@ class SearchServiceProvider extends AbstractServiceProvider
|
||||
|
||||
public function registerDiscussionGambits()
|
||||
{
|
||||
$this->app->when('Flarum\Core\Search\Discussion\DiscussionSearcher')
|
||||
$this->app->when('Flarum\Discussion\Search\DiscussionSearcher')
|
||||
->needs('Flarum\Core\Search\GambitManager')
|
||||
->give(function (Container $app) {
|
||||
$gambits = new GambitManager($app);
|
||||
|
||||
$gambits->setFulltextGambit('Flarum\Core\Search\Discussion\Gambit\FulltextGambit');
|
||||
$gambits->add('Flarum\Core\Search\Discussion\Gambit\AuthorGambit');
|
||||
$gambits->add('Flarum\Core\Search\Discussion\Gambit\CreatedGambit');
|
||||
$gambits->add('Flarum\Core\Search\Discussion\Gambit\HiddenGambit');
|
||||
$gambits->add('Flarum\Core\Search\Discussion\Gambit\UnreadGambit');
|
||||
$gambits->setFulltextGambit('Flarum\Discussion\Search\Gambit\FulltextGambit');
|
||||
$gambits->add('Flarum\Discussion\Search\Gambit\AuthorGambit');
|
||||
$gambits->add('Flarum\Discussion\Search\Gambit\CreatedGambit');
|
||||
$gambits->add('Flarum\Discussion\Search\Gambit\HiddenGambit');
|
||||
$gambits->add('Flarum\Discussion\Search\Gambit\UnreadGambit');
|
||||
|
||||
$app->make('events')->fire(
|
||||
new ConfigureDiscussionGambits($gambits)
|
||||
|
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Core\Validator;
|
||||
|
||||
use Flarum\Foundation\AbstractValidator;
|
||||
|
||||
class DiscussionValidator extends AbstractValidator
|
||||
{
|
||||
protected $rules = [
|
||||
'title' => [
|
||||
'required',
|
||||
'min:3',
|
||||
'max:80'
|
||||
]
|
||||
];
|
||||
}
|
Reference in New Issue
Block a user