mirror of
https://github.com/flarum/core.git
synced 2025-08-04 23:47:32 +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:
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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user