1
0
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:
Toby Zerner
2015-02-24 20:33:18 +10:30
parent 0e4e44c358
commit 2c46888db5
266 changed files with 5562 additions and 4658 deletions

View 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');
}
}

View 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
View 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
View 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;
}
}

View 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
View 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
View 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
View 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
View 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;
}
}
}

View File

@@ -0,0 +1,5 @@
<?php namespace Flarum\Core\Models;
class Permission extends Model
{
}

151
src/Core/Models/Post.php Executable file
View 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
View 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
View 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;
}
}