1
0
mirror of https://github.com/flarum/core.git synced 2025-10-17 09:46:14 +02:00

Merge branch 'master' into psr-7

Conflicts:
	composer.json
	composer.lock
	src/Api/Actions/TokenAction.php
	src/Core/Formatter/FormatterManager.php
	src/Core/Handlers/Events/EmailConfirmationMailer.php
	src/Forum/Actions/ConfirmEmailAction.php
	src/Forum/Actions/IndexAction.php
	src/Forum/Actions/ResetPasswordAction.php
	src/Forum/Actions/SavePasswordAction.php
	src/Forum/routes.php
This commit is contained in:
Franz Liedke
2015-06-06 13:59:59 +02:00
115 changed files with 2286 additions and 1619 deletions

View File

@@ -41,7 +41,9 @@ class IndexAction extends SerializeCollectionAction
'lastUser' => true,
'startPost' => false,
'lastPost' => false,
'relevantPosts' => false
'relevantPosts' => false,
'relevantPosts.discussion' => false,
'relevantPosts.user' => false
];
/**

View File

@@ -51,7 +51,7 @@ class ShowAction extends SerializeResourceAction
*
* @var array
*/
public static $link = ['posts'];
public static $link = ['posts', 'posts.discussion'];
/**
* The fields that are available to be sorted by.

View File

@@ -74,6 +74,7 @@ class IndexAction extends SerializeCollectionAction
$user->markNotificationsAsRead()->save();
return $this->notifications->findByUser($user, $request->limit, $request->offset);
return $this->notifications->findByUser($user, $request->limit, $request->offset)
->load($request->include);
}
}

View File

@@ -4,6 +4,7 @@ use Flarum\Api\Request;
use Flarum\Core\Commands\GenerateAccessTokenCommand;
use Flarum\Core\Repositories\UserRepositoryInterface;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Core\Events\UserEmailChangeWasRequested;
use Illuminate\Contracts\Bus\Dispatcher;
class TokenAction extends JsonApiAction
@@ -36,6 +37,14 @@ class TokenAction extends JsonApiAction
throw new PermissionDeniedException;
}
if (! $user->is_activated) {
event(new UserEmailChangeWasRequested($user, $user->email));
return $this->json([
'code' => 'confirm_email',
'email' => $user->email
], 401);
}
$token = $this->bus->dispatch(
new GenerateAccessTokenCommand($user->id)
);

View File

@@ -17,7 +17,7 @@ class ShowAction extends SerializeResourceAction
*
* @var string
*/
public static $serializer = 'Flarum\Api\Serializers\UserSerializer';
public static $serializer = 'Flarum\Api\Serializers\CurrentUserSerializer';
/**
* The relationships that are available to be included, and which ones are

View File

@@ -49,12 +49,12 @@ abstract class BaseSerializer extends SerializerAbstract
$relation = $caller['function'];
}
return function ($model, $include, $links) use ($serializer, $many, $relation) {
return function ($model, $include, $included, $links) use ($serializer, $many, $relation) {
if ($relation instanceof Closure) {
$data = $relation($model, $include);
} else {
if ($include) {
$data = !is_null($model->$relation) ? $model->$relation : ($many ? $model->$relation()->get() : $model->$relation()->first());
$data = !is_null($model->$relation) ? $model->$relation : $model->$relation()->getResults();
} elseif ($many) {
$relationIds = $relation.'_ids';
$data = $model->$relationIds ?: $model->$relation()->get(['id'])->fetch('id')->all();
@@ -67,7 +67,7 @@ abstract class BaseSerializer extends SerializerAbstract
if ($serializer instanceof Closure) {
$serializer = $serializer($model, $data);
}
$serializer = new $serializer($this->actor, $links);
$serializer = new $serializer($this->actor, $included, $links);
return $many ? $serializer->collection($data) : $serializer->resource($data);
};
}

View File

@@ -0,0 +1,21 @@
<?php namespace Flarum\Api\Serializers;
class CurrentUserSerializer extends UserSerializer
{
protected function attributes($user)
{
$attributes = parent::attributes($user);
$actingUser = $this->actor->getUser();
if ($user->id === $actingUser->id) {
$attributes += [
'readTime' => $user->read_time ? $user->read_time->toRFC3339String() : null,
'unreadNotificationsCount' => $user->getUnreadNotificationsCount(),
'preferences' => $user->preferences
];
}
return $this->extendAttributes($user, $attributes);
}
}

View File

@@ -2,13 +2,6 @@
class DiscussionSerializer extends DiscussionBasicSerializer
{
/**
* Default relations to include.
*
* @var array
*/
protected $include = ['startUser', 'lastUser'];
/**
* Serialize attributes of a Discussion model for JSON output.
*

View File

@@ -9,20 +9,6 @@ class PostBasicSerializer extends BaseSerializer
*/
protected $type = 'posts';
/**
* Default relations to link.
*
* @var array
*/
protected $link = ['discussion'];
/**
* Default relations to include.
*
* @var array
*/
protected $include = ['user'];
/**
* Serialize attributes of a Post model for JSON output.
*
@@ -39,7 +25,7 @@ class PostBasicSerializer extends BaseSerializer
];
if ($post->type === 'comment') {
$attributes['excerpt'] = str_limit($post->contentPlain, 200);
$attributes['contentHtml'] = $post->content_html;
} else {
$attributes['content'] = $post->content;
}

View File

@@ -2,13 +2,6 @@
class PostSerializer extends PostBasicSerializer
{
/**
* Default relations to include.
*
* @var array
*/
protected $include = ['user', 'editUser', 'hideUser'];
/**
* Serialize attributes of a Post model for JSON output.
*

View File

@@ -2,13 +2,6 @@
class UserSerializer extends UserBasicSerializer
{
/**
* Default relations to include.
*
* @var array
*/
protected $include = ['groups'];
/**
* Serialize attributes of a User model for JSON output.
*
@@ -19,8 +12,8 @@ class UserSerializer extends UserBasicSerializer
{
$attributes = parent::attributes($user);
$actorUser = $this->actor->getUser();
$canEdit = $user->can($actorUser, 'edit');
$actingUser = $this->actor->getUser();
$canEdit = $user->can($actingUser, 'edit');
$attributes += [
'bioHtml' => $user->bio_html,
@@ -28,7 +21,7 @@ class UserSerializer extends UserBasicSerializer
'discussionsCount' => (int) $user->discussions_count,
'commentsCount' => (int) $user->comments_count,
'canEdit' => $canEdit,
'canDelete' => $user->can($actorUser, 'delete'),
'canDelete' => $user->can($actingUser, 'delete'),
];
if ($user->preference('discloseOnline')) {
@@ -46,14 +39,6 @@ class UserSerializer extends UserBasicSerializer
];
}
if ($user->id === $actorUser->id) {
$attributes += [
'readTime' => $user->read_time ? $user->read_time->toRFC3339String() : null,
'unreadNotificationsCount' => $user->getUnreadNotificationsCount(),
'preferences' => $user->preferences
];
}
return $this->extendAttributes($user, $attributes);
}
}

View File

@@ -28,8 +28,8 @@ class SeedCommand extends Command
*/
public function fire()
{
$this->call('db:seed', ['--class' => 'Flarum\Core\Seeders\DiscussionsTableSeeder']);
$this->call('db:seed', ['--class' => 'Flarum\Core\Seeders\UsersTableSeeder']);
$this->call('db:seed', ['--class' => 'Flarum\Core\Seeders\DiscussionsTableSeeder']);
}
/**

View File

@@ -11,6 +11,10 @@ class Core
public static function config($key, $default = null)
{
if (! static::isInstalled()) {
return $default;
}
if (is_null($value = DB::table('config')->where('key', $key)->pluck('value'))) {
$value = $default;
}

View File

@@ -2,13 +2,10 @@
class ConfirmEmailCommand
{
public $userId;
public $token;
public function __construct($userId, $token)
public function __construct($token)
{
$this->userId = $userId;
$this->token = $token;
}
}

View File

@@ -92,6 +92,11 @@ class CoreServiceProvider extends ServiceProvider
'Flarum\Core\Repositories\EloquentActivityRepository'
);
$this->app->bind(
'Flarum\Core\Search\Discussions\Fulltext\DriverInterface',
'Flarum\Core\Search\Discussions\Fulltext\MySqlFulltextDriver'
);
$avatarFilesystem = function (Container $app) {
return $app->make('Illuminate\Contracts\Filesystem\Factory')->disk('flarum-avatars')->getDriver();
};

View File

@@ -0,0 +1,16 @@
<?php namespace Flarum\Core\Events;
use Flarum\Core\Notifications\NotificationInterface;
class NotificationWillBeSent
{
public $notification;
public $users;
public function __construct(NotificationInterface $notification, array &$users)
{
$this->notification = $notification;
$this->users = $users;
}
}

View File

@@ -0,0 +1,16 @@
<?php namespace Flarum\Core\Events;
use Flarum\Core\Models\User;
class UserEmailChangeWasRequested
{
public $user;
public $email;
public function __construct(User $user, $email)
{
$this->user = $user;
$this->email = $email;
}
}

View File

@@ -0,0 +1,48 @@
<?php namespace Flarum\Core\Formatter;
use Flarum\Core\Models\Post;
use Closure;
abstract class FormatterAbstract
{
public function beforePurification($text, Post $post = null)
{
return $text;
}
public function afterPurification($text, Post $post = null)
{
return $text;
}
protected function ignoreTags($text, array $tags, Closure $callback)
{
$chunks = preg_split('/(<.+?>)/is', $text, 0, PREG_SPLIT_DELIM_CAPTURE);
$openTag = null;
for ($i = 0; $i < count($chunks); $i++) {
if ($i % 2 === 0) { // even numbers are text
// Only process this chunk if there are no unclosed $ignoreTags
if (null === $openTag) {
$chunks[$i] = $callback($chunks[$i]);
}
} else { // odd numbers are tags
// Only process this tag if there are no unclosed $ignoreTags
if (null === $openTag) {
// Check whether this tag is contained in $ignoreTags and is not self-closing
if (preg_match("`<(" . implode('|', $tags) . ").*(?<!/)>$`is", $chunks[$i], $matches)) {
$openTag = $matches[1];
}
} else {
// Otherwise, check whether this is the closing tag for $openTag.
if (preg_match('`</\s*' . $openTag . '>`i', $chunks[$i], $matches)) {
$openTag = null;
}
}
}
}
return implode($chunks);
}
}

View File

@@ -0,0 +1,10 @@
<?php namespace Flarum\Core\Formatter;
use Flarum\Core\Models\Post;
interface FormatterInterface
{
public function beforePurification($text, Post $post = null);
public function afterPurification($text, Post $post = null);
}

View File

@@ -1,6 +1,8 @@
<?php namespace Flarum\Core\Formatter;
use Illuminate\Contracts\Container\Container;
use HTMLPurifier;
use HTMLPurifier_Config;
class FormatterManager
{
@@ -55,20 +57,32 @@ class FormatterManager
public function format($text, $post = null)
{
$formatters = [];
foreach ($this->getFormatters() as $formatter) {
$text = $this->container->make($formatter)->format($text, $post);
$formatters[] = $this->container->make($formatter);
}
return $text;
}
foreach ($formatters as $formatter) {
$text = $formatter->beforePurification($text, $post);
}
public function strip($text)
{
foreach ($this->getFormatters() as $formatter) {
$formatter = $this->container->make($formatter);
if (method_exists($formatter, 'strip')) {
$text = $formatter->strip($text);
}
// Studio does not yet merge autoload_files...
// https://github.com/franzliedke/studio/commit/4f0f4314db4ed3e36c869a5f79b855c97bdd1be7
require __DIR__.'/../../../vendor/ezyang/htmlpurifier/library/HTMLPurifier.composer.php';
$config = HTMLPurifier_Config::createDefault();
$config->set('Core.Encoding', 'UTF-8');
$config->set('Core.EscapeInvalidTags', true);
$config->set('HTML.Doctype', 'HTML 4.01 Strict');
$config->set('HTML.Allowed', 'p,em,strong,a[href|title],ul,ol,li,code,pre,blockquote,h1,h2,h3,h4,h5,h6,br,hr');
$config->set('HTML.Nofollow', true);
$purifier = new HTMLPurifier($config);
$text = $purifier->purify($text);
foreach ($formatters as $formatter) {
$text = $formatter->afterPurification($text, $post);
}
return $text;

View File

@@ -1,8 +1,9 @@
<?php namespace Flarum\Core\Formatter;
use Flarum\Core\Models\Post;
use Misd\Linkify\Linkify;
class LinkifyFormatter
class LinkifyFormatter extends FormatterAbstract
{
protected $linkify;
@@ -11,7 +12,7 @@ class LinkifyFormatter
$this->linkify = $linkify;
}
public function format($text)
public function beforePurification($text, Post $post = null)
{
return $this->linkify->process($text, ['attr' => ['target' => '_blank']]);
}

View File

@@ -3,6 +3,8 @@
use Flarum\Core\Repositories\UserRepositoryInterface as UserRepository;
use Flarum\Core\Events\UserWillBeSaved;
use Flarum\Core\Support\DispatchesEvents;
use Flarum\Core\Exceptions\InvalidConfirmationTokenException;
use Flarum\Core\Models\EmailToken;
class ConfirmEmailCommandHandler
{
@@ -17,10 +19,14 @@ class ConfirmEmailCommandHandler
public function handle($command)
{
$user = $this->users->findOrFail($command->userId);
$token = EmailToken::find($command->token)->first();
$user->assertConfirmationTokenValid($command->token);
$user->confirmEmail();
if (! $token) {
throw new InvalidConfirmationTokenException;
}
$user = $token->user;
$user->changeEmail($token->email);
if (! $user->is_activated) {
$user->activate();
@@ -31,6 +37,8 @@ class ConfirmEmailCommandHandler
$user->save();
$this->dispatchEventsFor($user);
$token->delete();
return $user;
}
}

View File

@@ -23,11 +23,12 @@ class EditUserCommandHandler
$userToEdit->assertCan($user, 'edit');
if (isset($command->data['username'])) {
$userToEdit->assertCan($user, 'rename');
$userToEdit->rename($command->data['username']);
}
if (isset($command->data['email'])) {
$userToEdit->changeEmail($command->data['email']);
$userToEdit->requestEmailChange($command->data['email']);
}
if (isset($command->data['password'])) {

View File

@@ -1,10 +1,11 @@
<?php namespace Flarum\Core\Handlers\Commands;
use Flarum\Core\Commands\RequestPasswordResetCommand;
use Flarum\Core\Models\ResetToken;
use Flarum\Core\Models\PasswordToken;
use Flarum\Core\Repositories\UserRepositoryInterface;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Flarum\Core;
class RequestPasswordResetCommandHandler
{
@@ -34,15 +35,16 @@ class RequestPasswordResetCommandHandler
throw new ModelNotFoundException;
}
$token = ResetToken::generate($user->id);
$token = PasswordToken::generate($user->id);
$token->save();
$data = [
'username' => $user->username,
'url' => route('flarum.forum.resetPassword', ['token' => $token->id])
'url' => route('flarum.forum.resetPassword', ['token' => $token->id]),
'forumTitle' => Core::config('forum_title')
];
$this->mailer->send(['text' => 'flarum::emails.reset'], $data, function ($message) use ($user) {
$this->mailer->send(['text' => 'flarum::emails.resetPassword'], $data, function ($message) use ($user) {
$message->to($user->email);
$message->subject('Reset Your Password');
});

View File

@@ -1,8 +1,9 @@
<?php namespace Flarum\Core\Handlers\Events;
use Config;
use Flarum\Core\Events\UserWasRegistered;
use Flarum\Core\Events\EmailWasChanged;
use Flarum\Core\Events\UserEmailChangeWasRequested;
use Flarum\Core;
use Flarum\Core\Models\EmailToken;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Mail\Mailer;
@@ -23,28 +24,47 @@ class EmailConfirmationMailer
public function subscribe(Dispatcher $events)
{
$events->listen('Flarum\Core\Events\UserWasRegistered', __CLASS__.'@whenUserWasRegistered');
$events->listen('Flarum\Core\Events\EmailWasChanged', __CLASS__.'@whenEmailWasChanged');
$events->listen('Flarum\Core\Events\UserEmailChangeWasRequested', __CLASS__.'@whenUserEmailChangeWasRequested');
}
public function whenUserWasRegistered(UserWasRegistered $event)
{
$user = $event->user;
$data = $this->getPayload($user, $user->email);
$forumTitle = Config::get('flarum::forum_title');
$data = [
'username' => $user->username,
'forumTitle' => $forumTitle,
'url' => route('flarum.forum.confirm', ['id' => $user->id, 'token' => $user->confirmation_token])
];
$this->mailer->send(['text' => 'flarum::emails.confirm'], $data, function ($message) use ($user) {
$this->mailer->send(['text' => 'flarum::emails.activateAccount'], $data, function ($message) use ($user) {
$message->to($user->email);
$message->subject('Confirm Your Email Address');
$message->subject('Activate Your New Account');
});
}
public function whenEmailWasChanged(EmailWasChanged $event)
public function whenUserEmailChangeWasRequested(UserEmailChangeWasRequested $event)
{
$email = $event->email;
$data = $this->getPayload($event->user, $email);
$this->mailer->send(['text' => 'flarum::emails.confirmEmail'], $data, function ($message) use ($email) {
$message->to($email);
$message->subject('Confirm Your New Email Address');
});
}
protected function generateToken($user, $email)
{
$token = EmailToken::generate($user->id, $email);
$token->save();
return $token;
}
protected function getPayload($user, $email)
{
$token = $this->generateToken($user, $email);
return [
'username' => $user->username,
'url' => route('flarum.forum.confirmEmail', ['token' => $token->id]),
'forumTitle' => Core::config('forum_title')
];
}
}

View File

@@ -16,18 +16,28 @@ class AccessToken extends Model
*/
public $incrementing = false;
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = ['created_at', 'expires_at'];
/**
* Generate an access token for the specified user.
*
* @param int $userId
* @param int $minutes
* @return static
*/
public static function generate($userId)
public static function generate($userId, $minutes = 60)
{
$token = new static;
$token->id = str_random(40);
$token->user_id = $userId;
$token->created_at = time();
$token->expires_at = time() + $minutes * 60;
return $token;
}

View File

@@ -119,17 +119,6 @@ class CommentPost extends Post
return $value;
}
/**
* Get the content formatter as HTML.
*
* @param string $value
* @return string
*/
public function getContentPlainAttribute()
{
return static::$formatter->strip($this->content);
}
/**
* Get text formatter instance.
*

View File

@@ -295,6 +295,11 @@ class Discussion extends Model
*/
public function stateFor(User $user)
{
$loadedState = array_get($this->relations, 'state');
if ($loadedState && $loadedState->user_id === $user->id) {
return $loadedState;
}
$state = $this->state($user)->first();
if (! $state) {

View File

@@ -0,0 +1,46 @@
<?php namespace Flarum\Core\Models;
class EmailToken extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'email_tokens';
/**
* Use a custom primary key for this model.
*
* @var boolean
*/
public $incrementing = false;
/**
* Generate a reset token for the specified user.
*
* @param int $userId
* @return static
*/
public static function generate($userId, $email)
{
$token = new static;
$token->id = str_random(40);
$token->user_id = $userId;
$token->email = $email;
$token->created_at = time();
return $token;
}
/**
* Define the relationship with the owner of this reset token.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('Flarum\Core\Models\User');
}
}

View File

@@ -110,14 +110,19 @@ class Model extends Eloquent
*/
public function assertValid()
{
$validation = $this->makeValidator();
if ($validation->fails()) {
throw (new ValidationFailureException)
->setErrors($validation->errors())
->setInput($validation->getData());
$validator = $this->makeValidator();
if ($validator->fails()) {
$this->throwValidationFailureException($validator);
}
}
protected function throwValidationFailureException($validator)
{
throw (new ValidationFailureException)
->setErrors($validator->errors())
->setInput($validator->getData());
}
/**
* Make a new validator instance for this model.
*
@@ -125,9 +130,11 @@ class Model extends Eloquent
*/
protected function makeValidator()
{
$rules = $this->expandUniqueRules(static::$rules);
$dirty = $this->getDirty();
return static::$validator->make($this->attributes, $rules);
$rules = $this->expandUniqueRules(array_only(static::$rules, array_keys($dirty)));
return static::$validator->make($dirty, $rules);
}
/**

View File

@@ -1,13 +1,13 @@
<?php namespace Flarum\Core\Models;
class ResetToken extends Model
class PasswordToken extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'reset_tokens';
protected $table = 'password_tokens';
/**
* Use a custom primary key for this model.
@@ -28,6 +28,7 @@ class ResetToken extends Model
$token->id = str_random(40);
$token->user_id = $userId;
$token->created_at = time();
return $token;
}

View File

@@ -13,6 +13,7 @@ use Flarum\Core\Events\UserBioWasChanged;
use Flarum\Core\Events\UserAvatarWasChanged;
use Flarum\Core\Events\UserWasActivated;
use Flarum\Core\Events\UserEmailWasConfirmed;
use Flarum\Core\Events\UserEmailChangeWasRequested;
class User extends Model
{
@@ -31,7 +32,7 @@ class User extends Model
* @var array
*/
public static $rules = [
'username' => 'required|unique',
'username' => 'required|alpha_dash|unique',
'email' => 'required|email|unique',
'password' => 'required',
'join_time' => 'date',
@@ -94,8 +95,6 @@ class User extends Model
$user->password = $password;
$user->join_time = time();
$user->refreshConfirmationToken();
$user->raise(new UserWasRegistered($user));
return $user;
@@ -111,6 +110,7 @@ class User extends Model
{
if ($username !== $this->username) {
$this->username = $username;
$this->raise(new UserWasRenamed($this));
}
@@ -127,12 +127,31 @@ class User extends Model
{
if ($email !== $this->email) {
$this->email = $email;
$this->raise(new UserEmailWasChanged($this));
}
return $this;
}
public function requestEmailChange($email)
{
if ($email !== $this->email) {
$validator = static::$validator->make(
compact('email'),
$this->expandUniqueRules(array_only(static::$rules, 'email'))
);
if ($validator->fails()) {
$this->throwValidationFailureException($validator);
}
$this->raise(new UserEmailChangeWasRequested($this, $email));
}
return $this;
}
/**
* Change the user's password.
*
@@ -155,7 +174,7 @@ class User extends Model
*/
public function setPasswordAttribute($value)
{
$this->attributes['password'] = $value ? static::$hasher->make($value) : null;
$this->attributes['password'] = $value ? static::$hasher->make($value) : '';
}
/**
@@ -257,41 +276,12 @@ class User extends Model
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.
*
@@ -461,7 +451,13 @@ class User extends Model
*/
public function permissions()
{
return Permission::whereIn('group_id', array_merge([Group::GUEST_ID], $this->groups->lists('id')));
$groupIds = [Group::GUEST_ID];
if ($this->is_activated) {
$groupIds = array_merge($groupIds, [Group::MEMBER_ID], $this->groups->lists('id'));
}
return Permission::whereIn('group_id', $groupIds);
}
/**

View File

@@ -2,6 +2,7 @@
use Flarum\Core\Repositories\NotificationRepositoryInterface;
use Flarum\Core\Models\Notification;
use Flarum\Core\Events\NotificationWillBeSent;
use Carbon\Carbon;
use Closure;
@@ -66,6 +67,8 @@ class NotificationSyncer
if (count($newRecipients)) {
$now = Carbon::now('utc')->toDateTimeString();
event(new NotificationWillBeSent($notification, $newRecipients));
Notification::insert(
array_map(function ($user) use ($attributes, $notification, $now) {
return $attributes + ['user_id' => $user->id, 'time' => $now];

View File

@@ -3,9 +3,17 @@
use Illuminate\Database\Eloquent\Builder;
use Flarum\Core\Models\Post;
use Flarum\Core\Models\User;
use Flarum\Core\Search\Discussions\Fulltext\DriverInterface;
class EloquentPostRepository implements PostRepositoryInterface
{
protected $fulltext;
public function __construct(DriverInterface $fulltext)
{
$this->fulltext = $fulltext;
}
/**
* Find a post by ID, optionally making sure it is visible to a certain
* user, or throw an exception.
@@ -72,10 +80,13 @@ class EloquentPostRepository implements PostRepositoryInterface
*/
public function findByContent($string, User $user = null)
{
$query = Post::select('id', 'discussion_id')
->where('content', 'like', '%'.$string.'%');
// ->whereRaw('MATCH (`content`) AGAINST (? IN BOOLEAN MODE)', [$string])
// ->orderByRaw('MATCH (`content`) AGAINST (?) DESC', [$string])
$ids = $this->fulltext->match($string);
$query = Post::select('id', 'discussion_id')->whereIn('id', $ids);
foreach ($ids as $id) {
$query->orderByRaw('id != ?', [$id]);
}
return $this->scopeVisibleForUser($query, $user)->get();
}

View File

@@ -93,7 +93,7 @@ class DiscussionSearcher implements SearcherInterface
}
if (in_array('relevantPosts', $load) && count($this->relevantPosts)) {
$load = array_diff($load, ['relevantPosts']);
$load = array_diff($load, ['relevantPosts', 'relevantPosts.discussion', 'relevantPosts.user']);
$postIds = [];
foreach ($this->relevantPosts as $id => $posts) {
@@ -104,12 +104,6 @@ class DiscussionSearcher implements SearcherInterface
foreach ($discussions as $discussion) {
$discussion->relevantPosts = $posts->filter(function ($post) use ($discussion) {
return $post->discussion_id == $discussion->id;
})
->each(function ($post) {
$pos = strpos(strtolower($post->content), strtolower($this->fulltext));
// TODO: make clipping more intelligent (full words only)
$start = max(0, $pos - 50);
$post->content = ($start > 0 ? '...' : '').str_limit(substr($post->content, $start), 300);
});
}
}

View File

@@ -0,0 +1,6 @@
<?php namespace Flarum\Core\Search\Discussions\Fulltext;
interface DriverInterface
{
public function match($string);
}

View File

@@ -0,0 +1,13 @@
<?php namespace Flarum\Core\Search\Discussions\Fulltext;
use Flarum\Core\Models\Post;
class MySqlFulltextDriver implements DriverInterface
{
public function match($string)
{
return Post::whereRaw('MATCH (`content`) AGAINST (? IN BOOLEAN MODE)', [$string])
->orderByRaw('MATCH (`content`) AGAINST (?) DESC', [$string])
->lists('id');
}
}

View File

@@ -14,12 +14,16 @@ class ConfigTableSeeder extends Seeder
public function run()
{
$config = [
'api_url' => 'http://flarum.dev/api',
'base_url' => 'http://flarum.dev',
'forum_title' => 'Flarum Demo Forum',
'welcome_message' => 'Flarum is now at a point where you can have basic conversations, so here is a little demo for you to break.',
'welcome_title' => 'Welcome to Flarum Demo Forum',
'extensions_enabled' => '[]',
'api_url' => 'http://flarum.dev/api',
'base_url' => 'http://flarum.dev',
'forum_title' => 'Flarum Demo Forum',
'welcome_message' => 'Flarum is now at a point where you can have basic conversations, so here is a little demo for you to break.',
'welcome_title' => 'Welcome to Flarum Demo Forum',
'extensions_enabled' => '[]',
'theme_primary_color' => '#536F90',
'theme_secondary_color' => '#536F90',
'theme_dark_mode' => false,
'theme_colored_header' => false,
];
DB::table('config')->insert(array_map(function ($key, $value) {

View File

@@ -5,16 +5,15 @@ use Flarum\Core\Commands\GenerateAccessTokenCommand;
use Flarum\Core\Exceptions\InvalidConfirmationTokenException;
use Psr\Http\Message\ServerRequestInterface as Request;
class ConfirmAction extends BaseAction
class ConfirmEmailAction extends BaseAction
{
use WritesRememberCookie;
public function handle(Request $request, $routeParams = [])
{
try {
$userId = array_get($routeParams, 'id');
$token = array_get($routeParams, 'token');
$command = new ConfirmEmailCommand($userId, $token);
$command = new ConfirmEmailCommand($token);
$user = $this->dispatch($command);
} catch (InvalidConfirmationTokenException $e) {
return 'Invalid confirmation token';
@@ -23,7 +22,7 @@ class ConfirmAction extends BaseAction
$token = $this->dispatch(new GenerateAccessTokenCommand($user->id));
return $this->withRememberCookie(
$this->redirectTo(''),
$this->redirectTo('/'),
$token->id
);
// TODO: ->with('alert', ['type' => 'success', 'message' => 'Thanks for confirming!']);

View File

@@ -1,13 +1,12 @@
<?php namespace Flarum\Forum\Actions;
use Flarum\Api\Client;
use Flarum\Core;
use Flarum\Support\Actor;
use Flarum\Support\HtmlAction;
use Flarum\Forum\Events\RenderView;
use Psr\Http\Message\ServerRequestInterface as Request;
use Session;
use Auth;
use Config;
use DB;
class IndexAction extends HtmlAction
@@ -44,7 +43,7 @@ class IndexAction extends HtmlAction
}
$view = view('flarum.forum::index')
->with('title', Config::get('flarum::forum_title', 'Flarum Demo Forum'))
->with('title', Core::config('forum_title'))
->with('config', $config)
->with('layout', 'flarum.forum::forum')
->with('data', $data)
@@ -57,6 +56,12 @@ class IndexAction extends HtmlAction
$root.'/js/forum/dist/app.js',
$root.'/less/forum/app.less'
]);
$assetManager->addLess('
@fl-primary-color: '.Core::config('theme_primary_color').';
@fl-secondary-color: '.Core::config('theme_secondary_color').';
@fl-dark-mode: '.(Core::config('theme_dark_mode') ? 'true' : 'false').';
@fl-colored_header: '.(Core::config('theme_colored_header') ? 'true' : 'false').';
');
event(new RenderView($view, $assetManager, $this));

View File

@@ -1,6 +1,6 @@
<?php namespace Flarum\Forum\Actions;
use Flarum\Core\Models\ResetToken;
use Flarum\Core\Models\PasswordToken;
use Flarum\Support\HtmlAction;
use Psr\Http\Message\ServerRequestInterface as Request;
@@ -10,7 +10,7 @@ class ResetPasswordAction extends HtmlAction
{
$token = array_get($routeParams, 'token');
$token = ResetToken::findOrFail($token);
$token = PasswordToken::findOrFail($token);
return view('flarum::reset')->with('token', $token->id);
}

View File

@@ -1,6 +1,6 @@
<?php namespace Flarum\Forum\Actions;
use Flarum\Core\Models\ResetToken;
use Flarum\Core\Models\PasswordToken;
use Flarum\Core\Commands\EditUserCommand;
use Psr\Http\Message\ServerRequestInterface as Request;
@@ -8,7 +8,7 @@ class SavePasswordAction extends BaseAction
{
public function handle(Request $request, $routeParams = [])
{
$token = ResetToken::findOrFail($request->getAttribute('token'));
$token = PasswordToken::findOrFail($request->getAttribute('token'));
$password = $request->getAttribute('password');
$confirmation = $request->getAttribute('password_confirmation');

View File

@@ -24,7 +24,7 @@ $router->get('/logout', 'flarum.forum.logout', $action('Flarum\Forum\Actions\Log
$router->post('/login', 'flarum.forum.login', $action('Flarum\Forum\Actions\LoginAction'));
$router->get('/confirm/{id}/{token}', 'flarum.forum.confirm', $action('Flarum\Forum\Actions\ConfirmAction'));
$router->get('/confirm/{token}', 'flarum.forum.confirmEmail', $action('Flarum\Forum\Actions\ConfirmEmailAction'));
$router->get('/reset/{token}', 'flarum.forum.resetPassword', $action('Flarum\Forum\Actions\ResetPasswordAction'));