Merge branch 'evented-api'

This commit is contained in:
Toby Zerner 2015-07-21 10:40:38 +09:30
commit 474a7db6a5
175 changed files with 1626 additions and 1453 deletions

View File

@ -30,7 +30,6 @@ gulp({
'src/**/*.js',
'../lib/**/*.js'
],
bootstrapFiles: [],
modulePrefix: 'flarum',
externalHelpers: true,
outputFile: 'dist/app.js'

View File

@ -31,7 +31,7 @@ export default class DiscussionHero extends Component {
const badges = discussion.badges().toArray();
if (badges.length) {
items.add('badges', <ul className="DiscussionHero-badges">{listItems(badges)}</ul>);
items.add('badges', <ul className="DiscussionHero-badges badges">{listItems(badges)}</ul>);
}
items.add('title', <h2 className="DiscussionHero-title">{discussion.title()}</h2>);

View File

@ -82,8 +82,9 @@ export default class DiscussionList extends Component {
* discussion results.
*
* @return {Object}
* @api
*/
params() {
requestParams() {
const params = Object.assign({include: ['startUser', 'lastUser']}, this.props.params);
params.sort = this.sortMap()[params.sort];
@ -111,8 +112,8 @@ export default class DiscussionList extends Component {
if (this.props.params.q) {
map.relevance = '';
}
map.recent = '-lastTime';
map.replies = '-commentsCount';
map.latest = '-lastTime';
map.top = '-commentsCount';
map.newest = '-startTime';
map.oldest = '+startTime';
@ -150,7 +151,7 @@ export default class DiscussionList extends Component {
return m.deferred().resolve(preloadedDiscussions).promise;
}
const params = this.params();
const params = this.requestParams();
params.page = {offset};
params.include = params.include.join(',');

View File

@ -1,5 +1,4 @@
import Notification from 'flarum/components/Notification';
import username from 'flarum/helpers/username';
/**
* The `DiscussionRenamedNotification` component displays a notification which

View File

@ -13,15 +13,18 @@ export default class DiscussionRenamedPost extends EventPost {
return 'pencil';
}
description() {
descriptionKey() {
return 'core.discussion_renamed_post';
}
descriptionData() {
const post = this.props.post;
const oldTitle = post.content()[0];
const newTitle = post.content()[1];
return app.trans('core.discussion_renamed', {
user: this.props.post.user(),
return {
old: <strong className="DiscussionRenamedPost-old">{oldTitle}</strong>,
new: <strong className="DiscussionRenamedPost-new">{newTitle}</strong>
});
};
}
}

View File

@ -1,4 +1,5 @@
import Post from 'flarum/components/Post';
import { ucfirst } from 'flarum/utils/string';
import usernameHelper from 'flarum/helpers/username';
import icon from 'flarum/helpers/icon';
@ -16,19 +17,24 @@ import icon from 'flarum/helpers/icon';
export default class EventPost extends Post {
attrs() {
return {
className: 'EventPost EventPost--' + this.props.post.contentType()
className: 'EventPost ' + ucfirst(this.props.post.contentType()) + 'Post'
};
}
content() {
const user = this.props.post.user();
const username = usernameHelper(user);
const data = Object.assign(this.descriptionData(), {
user,
username: user
? <a className="EventPost-user" href={app.route.user(user)} config={m.route}>{username}</a>
: username
});
return [
icon(this.icon(), {className: 'EventPost-icon'}),
<div class="EventPost-info">
{user ? <a className="EventPost-user" href={app.route.user(user)} config={m.route}>{username}</a> : username}{' '}
{this.description()}
{app.trans(this.descriptionKey(), data)}
</div>
];
}
@ -39,13 +45,24 @@ export default class EventPost extends Post {
* @return {String}
*/
icon() {
return '';
}
/**
* Get the description of the event.
* Get the translation key for the description of the event.
*
* @return {VirtualElement}
* @return {String}
*/
description() {
descriptionKey() {
return '';
}
/**
* Get the translation data for the description of the event.
*
* @return {Object}
*/
descriptionData() {
return {};
}
}

View File

@ -74,7 +74,7 @@ export default class NotificationGrid extends Component {
{this.types.map(type => (
<tr>
<td className="NotificationGrid-groupToggle" onclick={this.toggleType.bind(this, type.name)}>
{type.label}
{icon(type.icon)} {type.label}
</td>
{this.methods.map(method => (
<td className="NotificationGrid-checkbox">
@ -181,7 +181,8 @@ export default class NotificationGrid extends Component {
items.add('discussionRenamed', {
name: 'discussionRenamed',
label: [icon('pencil'), ' ', app.trans('core.notify_discussion_renamed')]
icon: 'pencil',
label: app.trans('core.notify_discussion_renamed')
});
return items;

View File

@ -167,7 +167,11 @@ class PostStream extends mixin(Component, evented) {
posts() {
return this.discussion.postIds()
.slice(this.visibleStart, this.visibleEnd)
.map(id => app.store.getById('posts', id));
.map(id => {
const post = app.store.getById('posts', id);
return post && post.discussion() ? post : null;
});
}
view() {
@ -365,10 +369,10 @@ class PostStream extends mixin(Component, evented) {
this.discussion.postIds().slice(start, end).forEach(id => {
const post = app.store.getById('posts', id);
if (!post) {
loadIds.push(id);
} else {
if (post && post.discussion()) {
loaded.push(post);
} else {
loadIds.push(id);
}
});

View File

@ -61,7 +61,9 @@ export default class Translator {
// translation key. This will allow a gender property to determine which
// translation key is used.
if (input.user instanceof User) {
input.username = username(extract(input, 'user'));
const user = extract(input, 'user');
if (!input.username) input.username = username(user);
}
// If we've found the appropriate translation string, then we'll sub in the

View File

@ -59,17 +59,3 @@ export function override(object, method, newMethod) {
return newMethod.apply(this, [original.bind(this)].concat(args));
};
}
/**
* Register a notification type.
*
* @param {String} name The name of the notification type (equivalent to the
* serialized `contentType`)
* @param {Object} Component The constructor of the component that this
* notification type should be rendered with
* @param {String|Array} label vDOM to render a label in the notification
* preferences grid for this notification type
*/
export function notificationType(name, Component, label) {
// TODO
}

View File

@ -36,3 +36,13 @@ export function slug(string) {
export function getPlainContent(string) {
return $('<div/>').html(string.replace(/(<\/p>|<br>)/g, '$1 ')).text();
}
/**
* Make a string's first character uppercase.
*
* @param {String} string
* @return {String}
*/
export function ucfirst(string) {
return string.substr(0, 1).toUpperCase() + string.substr(1);
}

View File

@ -1,6 +1,6 @@
.DiscussionHero {
.badges {
margin-right: 5px;
margin-right: 10px;
margin-bottom: -2px;
}
}

View File

@ -25,7 +25,7 @@
white-space: nowrap;
pointer-events: none;
.badge {
.Badge {
margin-left: -15px;
position: relative;
pointer-events: auto;

View File

@ -1,6 +1,8 @@
.App {
position: relative !important;
padding-top: @header-height;
overflow-x: hidden;
min-height: 100vh;
@media @phone {
padding-top: @header-height-phone;

View File

@ -32,7 +32,7 @@
.transition(background-color 0.2s);
.on& {
background: @primary-color;
background: #58A400;
}
&:before {

View File

@ -72,11 +72,6 @@ p {
margin: 0 auto;
}
.global-page {
overflow-x: hidden;
min-height: 100vh;
}
mark {
background: #FFE300;
padding: 1px;

View File

@ -25,7 +25,7 @@ core:
delete_forever: Delete Forever
deleted: "[deleted]"
disclose_online: Allow others to see when I am online
discussion_renamed: "changed the title from {old} to {new}"
discussion_renamed_post: "{username} changed the title from {old} to {new}."
discussion_renamed_notification: "{username} changed the title"
discussion_replied: "{username} replied {ago}"
discussion_started: "{username} started {ago}"
@ -93,10 +93,11 @@ core:
send_password_reset_email: Send Password Reset Email
settings: Settings
sign_up: Sign Up
sort_latest: Latest
sort_newest: Newest
sort_oldest: Oldest
sort_recent: Recent
sort_replies: Replies
sort_relevance: Relevance
sort_top: Top
start_a_discussion: Start a Discussion
started_a_discussion: Started a discussion
unread_posts: "{count} unread"

View File

@ -21,12 +21,12 @@ class IndexAction extends SerializeCollectionAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\ActivitySerializer';
public $serializer = 'Flarum\Api\Serializers\ActivitySerializer';
/**
* @inheritdoc
*/
public static $include = [
public $include = [
'subject' => true,
'subject.user' => true,
'subject.discussion' => true
@ -35,27 +35,27 @@ class IndexAction extends SerializeCollectionAction
/**
* @inheritdoc
*/
public static $link = ['user'];
public $link = ['user'];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* @param UserRepository $users

View File

@ -18,12 +18,12 @@ class CreateAction extends BaseCreateAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\DiscussionSerializer';
public $serializer = 'Flarum\Api\Serializers\DiscussionSerializer';
/**
* @inheritdoc
*/
public static $include = [
public $include = [
'posts' => true,
'startUser' => true,
'lastUser' => true,
@ -34,27 +34,27 @@ class CreateAction extends BaseCreateAction
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* Instantiate the action.

View File

@ -22,12 +22,12 @@ class IndexAction extends SerializeCollectionAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\DiscussionSerializer';
public $serializer = 'Flarum\Api\Serializers\DiscussionSerializer';
/**
* @inheritdoc
*/
public static $include = [
public $include = [
'startUser' => true,
'lastUser' => true,
'startPost' => false,
@ -40,27 +40,27 @@ class IndexAction extends SerializeCollectionAction
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = ['lastTime', 'commentsCount', 'startTime'];
public $sortFields = ['lastTime', 'commentsCount', 'startTime'];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* @param DiscussionSearcher $searcher
@ -93,7 +93,7 @@ class IndexAction extends SerializeCollectionAction
$results = $this->searcher->search($criteria, $request->limit, $request->offset, $load);
// TODO: add query params (filter, sort, include) to the pagination URLs
static::addPaginationLinks(
$this->addPaginationLinks(
$document,
$request,
$request->http ? $this->url->toRoute('flarum.api.discussions.index') : '',

View File

@ -19,12 +19,12 @@ class ShowAction extends SerializeResourceAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\DiscussionSerializer';
public $serializer = 'Flarum\Api\Serializers\DiscussionSerializer';
/**
* @inheritdoc
*/
public static $include = [
public $include = [
'startUser' => false,
'lastUser' => false,
'startPost' => false,
@ -39,27 +39,27 @@ class ShowAction extends SerializeResourceAction
/**
* @inheritdoc
*/
public static $link = ['posts', 'posts.discussion'];
public $link = ['posts', 'posts.discussion'];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = ['time'];
public $sortFields = ['time'];
/**
* @inheritdoc
*/
public static $sort = ['time' => 'asc'];
public $sort = ['time' => 'asc'];
/**
* Instantiate the action.

View File

@ -17,37 +17,37 @@ class UpdateAction extends SerializeResourceAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\DiscussionSerializer';
public $serializer = 'Flarum\Api\Serializers\DiscussionSerializer';
/**
* @inheritdoc
*/
public static $include = [];
public $include = [];
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* @param Dispatcher $bus

View File

@ -9,37 +9,37 @@ class ShowAction extends SerializeResourceAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\ForumSerializer';
public $serializer = 'Flarum\Api\Serializers\ForumSerializer';
/**
* @inheritdoc
*/
public static $include = [];
public $include = [];
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* Get the forum, ready to be serialized and assigned to the JsonApi

View File

@ -10,37 +10,37 @@ class IndexAction extends SerializeCollectionAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\GroupSerializer';
public $serializer = 'Flarum\Api\Serializers\GroupSerializer';
/**
* @inheritdoc
*/
public static $include = [];
public $include = [];
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* Get the groups, ready to be serialized and assigned to the document

View File

@ -16,12 +16,12 @@ class IndexAction extends SerializeCollectionAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\NotificationSerializer';
public $serializer = 'Flarum\Api\Serializers\NotificationSerializer';
/**
* @inheritdoc
*/
public static $include = [
public $include = [
'sender' => true,
'subject' => true,
'subject.discussion' => true
@ -30,27 +30,27 @@ class IndexAction extends SerializeCollectionAction
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 10;
public $limit = 10;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* Instantiate the action.

View File

@ -16,37 +16,37 @@ class UpdateAction extends SerializeResourceAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\NotificationSerializer';
public $serializer = 'Flarum\Api\Serializers\NotificationSerializer';
/**
* @inheritdoc
*/
public static $include = [];
public $include = [];
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* @param Dispatcher $bus

View File

@ -16,12 +16,12 @@ class CreateAction extends BaseCreateAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\PostSerializer';
public $serializer = 'Flarum\Api\Serializers\PostSerializer';
/**
* @inheritdoc
*/
public static $include = [
public $include = [
'user' => true,
'discussion' => true
];
@ -29,27 +29,27 @@ class CreateAction extends BaseCreateAction
/**
* @inheritdoc
*/
public static $link = ['discussion.posts'];
public $link = ['discussion.posts'];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* Instantiate the action.

View File

@ -12,12 +12,12 @@ class IndexAction extends SerializeCollectionAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\PostSerializer';
public $serializer = 'Flarum\Api\Serializers\PostSerializer';
/**
* @inheritdoc
*/
public static $include = [
public $include = [
'user' => true,
'user.groups' => true,
'editUser' => true,
@ -28,27 +28,27 @@ class IndexAction extends SerializeCollectionAction
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* @param PostRepository $posts

View File

@ -16,12 +16,12 @@ class ShowAction extends SerializeResourceAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\PostSerializer';
public $serializer = 'Flarum\Api\Serializers\PostSerializer';
/**
* @inheritdoc
*/
public static $include = [
public $include = [
'user' => true,
'user.groups' => true,
'editUser' => true,
@ -32,27 +32,27 @@ class ShowAction extends SerializeResourceAction
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* @param PostRepository $posts

View File

@ -16,37 +16,37 @@ class UpdateAction extends SerializeResourceAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\PostSerializer';
public $serializer = 'Flarum\Api\Serializers\PostSerializer';
/**
* @inheritdoc
*/
public static $include = [];
public $include = [];
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* @param Dispatcher $bus

View File

@ -1,6 +1,7 @@
<?php namespace Flarum\Api\Actions;
use Flarum\Api\Events\WillSerializeData;
use Flarum\Events\BuildApiAction;
use Flarum\Events\WillSerializeData;
use Flarum\Api\Request;
use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Criteria;
@ -15,7 +16,7 @@ abstract class SerializeAction extends JsonApiAction
*
* @var string
*/
public static $serializer;
public $serializer;
/**
* The relationships that are available to be included (keys), and which
@ -23,42 +24,42 @@ abstract class SerializeAction extends JsonApiAction
*
* @var array
*/
public static $include = [];
public $include = [];
/**
* The relationships that are linked by default.
*
* @var array
*/
public static $link = [];
public $link = [];
/**
* The maximum number of records that can be requested.
*
* @var integer
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* The number of records included by default.
*
* @var integer
*/
public static $limit = 20;
public $limit = 20;
/**
* The fields that are available to be sorted by.
*
* @var array
*/
public static $sortFields = [];
public $sortFields = [];
/**
* The default sort field and order to user.
*
* @var string
*/
public static $sort;
public $sort;
/**
* Handle an API request and return an API response.
@ -68,14 +69,14 @@ abstract class SerializeAction extends JsonApiAction
*/
public function respond(Request $request)
{
$request = static::buildJsonApiRequest($request);
$request = $this->buildJsonApiRequest($request);
$document = new Document();
$data = $this->data($request, $document);
event(new WillSerializeData($this, $data, $request));
$serializer = new static::$serializer($request->actor, $request->include, $request->link);
$serializer = new $this->serializer($request->actor, $request->include, $request->link);
$document->setData($this->serialize($serializer, $data));
@ -107,17 +108,19 @@ abstract class SerializeAction extends JsonApiAction
* @param Request $request
* @return JsonApiRequest
*/
protected static function buildJsonApiRequest(Request $request)
protected function buildJsonApiRequest(Request $request)
{
$request = new JsonApiRequest($request->input, $request->actor, $request->http);
$criteria = new Criteria($request->input);
$request->include = static::sanitizeInclude($criteria->getInclude());
$request->sort = static::sanitizeSort($criteria->getSort());
event(new BuildApiAction($this));
$request->include = $this->sanitizeInclude($criteria->getInclude());
$request->sort = $this->sanitizeSort($criteria->getSort());
$request->offset = $criteria->getOffset();
$request->limit = static::sanitizeLimit($criteria->getLimit());
$request->link = static::$link;
$request->limit = $this->sanitizeLimit($criteria->getLimit());
$request->link = $this->link;
return $request;
}
@ -129,9 +132,9 @@ abstract class SerializeAction extends JsonApiAction
* @param array $include
* @return array
*/
protected static function sanitizeInclude(array $include)
protected function sanitizeInclude(array $include)
{
return array_intersect($include, array_keys(static::$include)) ?: array_keys(array_filter(static::$include));
return array_intersect($include, array_keys($this->include)) ?: array_keys(array_filter($this->include));
}
/**
@ -141,9 +144,9 @@ abstract class SerializeAction extends JsonApiAction
* @param array $sort
* @return array
*/
protected static function sanitizeSort(array $sort)
protected function sanitizeSort(array $sort)
{
return array_intersect_key($sort, array_flip(static::$sortFields)) ?: static::$sort;
return array_intersect_key($sort, array_flip($this->sortFields)) ?: $this->sort;
}
/**
@ -152,9 +155,9 @@ abstract class SerializeAction extends JsonApiAction
* @param int $limit
* @return int
*/
protected static function sanitizeLimit($limit)
protected function sanitizeLimit($limit)
{
return min($limit, static::$limitMax) ?: static::$limit;
return min($limit, $this->limitMax) ?: $this->limit;
}
/**
@ -169,10 +172,10 @@ abstract class SerializeAction extends JsonApiAction
* is unknown ('last' link is ommitted).
* @return void
*/
protected static function addPaginationLinks(Document $document, JsonApiRequest $request, $url, $total = true)
protected function addPaginationLinks(Document $document, JsonApiRequest $request, $url, $total = true)
{
$input = [];
if ($request->limit != static::$limit) {
if ($request->limit != $this->limit) {
array_set($input, 'page.limit', $request->limit);
}

View File

@ -4,7 +4,7 @@ use Flarum\Api\Commands\GenerateAccessToken;
use Flarum\Api\Request;
use Flarum\Core\Users\UserRepository;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Core\Users\Events\UserEmailChangeWasRequested;
use Flarum\Events\UserEmailChangeWasRequested;
use Illuminate\Contracts\Bus\Dispatcher;
use Zend\Diactoros\Response\JsonResponse;

View File

@ -15,37 +15,37 @@ class CreateAction extends BaseCreateAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\UserSerializer';
public $serializer = 'Flarum\Api\Serializers\UserSerializer';
/**
* @inheritdoc
*/
public static $include = [];
public $include = [];
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* @param Dispatcher $bus

View File

@ -16,7 +16,7 @@ class DeleteAvatarAction extends SerializeResourceAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\UserSerializer';
public $serializer = 'Flarum\Api\Serializers\UserSerializer';
/**
* @param Dispatcher $bus

View File

@ -22,39 +22,39 @@ class IndexAction extends SerializeCollectionAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\UserSerializer';
public $serializer = 'Flarum\Api\Serializers\UserSerializer';
/**
* @inheritdoc
*/
public static $include = [
public $include = [
'groups' => true
];
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = ['username', 'postsCount', 'discussionsCount', 'lastSeenTime', 'joinTime'];
public $sortFields = ['username', 'postsCount', 'discussionsCount', 'lastSeenTime', 'joinTime'];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* @param UserSearcher $searcher
@ -84,7 +84,7 @@ class IndexAction extends SerializeCollectionAction
$results = $this->searcher->search($criteria, $request->limit, $request->offset, $request->include);
static::addPaginationLinks(
$this->addPaginationLinks(
$document,
$request,
$this->url->toRoute('flarum.api.users.index'),

View File

@ -15,39 +15,39 @@ class ShowAction extends SerializeResourceAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\CurrentUserSerializer';
public $serializer = 'Flarum\Api\Serializers\CurrentUserSerializer';
/**
* @inheritdoc
*/
public static $include = [
public $include = [
'groups' => true
];
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* @param UserRepository $users

View File

@ -16,37 +16,37 @@ class UpdateAction extends SerializeResourceAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\CurrentUserSerializer';
public $serializer = 'Flarum\Api\Serializers\CurrentUserSerializer';
/**
* @inheritdoc
*/
public static $include = [];
public $include = [];
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* @param Dispatcher $bus

View File

@ -16,37 +16,37 @@ class UploadAvatarAction extends SerializeResourceAction
/**
* @inheritdoc
*/
public static $serializer = 'Flarum\Api\Serializers\UserSerializer';
public $serializer = 'Flarum\Api\Serializers\UserSerializer';
/**
* @inheritdoc
*/
public static $include = [];
public $include = [];
/**
* @inheritdoc
*/
public static $link = [];
public $link = [];
/**
* @inheritdoc
*/
public static $limitMax = 50;
public $limitMax = 50;
/**
* @inheritdoc
*/
public static $limit = 20;
public $limit = 20;
/**
* @inheritdoc
*/
public static $sortFields = [];
public $sortFields = [];
/**
* @inheritdoc
*/
public static $sort;
public $sort;
/**
* @param Dispatcher $bus

View File

@ -1,6 +1,11 @@
<?php namespace Flarum\Api;
use Flarum\Api\Serializers\ActivitySerializer;
use Flarum\Api\Serializers\NotificationSerializer;
use Flarum\Core\Users\Guest;
use Flarum\Events\RegisterApiRoutes;
use Flarum\Events\RegisterActivityTypes;
use Flarum\Events\RegisterNotificationTypes;
use Flarum\Http\RouteCollection;
use Flarum\Http\UrlGenerator;
use Illuminate\Support\ServiceProvider;
@ -37,6 +42,45 @@ class ApiServiceProvider extends ServiceProvider
public function boot()
{
$this->routes();
$this->registerActivitySerializers();
$this->registerNotificationSerializers();
}
/**
* Register activity serializers.
*/
protected function registerActivitySerializers()
{
$blueprints = [];
$serializers = [
'posted' => 'Flarum\Api\Serializers\PostBasicSerializer',
'startedDiscussion' => 'Flarum\Api\Serializers\PostBasicSerializer',
'joined' => 'Flarum\Api\Serializers\UserSerializer'
];
event(new RegisterActivityTypes($blueprints, $serializers));
foreach ($serializers as $type => $serializer) {
ActivitySerializer::setSubjectSerializer($type, $serializer);
}
}
/**
* Register notification serializers.
*/
protected function registerNotificationSerializers()
{
$blueprints = [];
$serializers = [
'discussionRenamed' => 'Flarum\Api\Serializers\DiscussionBasicSerializer'
];
event(new RegisterNotificationTypes($blueprints, $serializers));
foreach ($serializers as $type => $serializer) {
NotificationSerializer::setSubjectSerializer($type, $serializer);
}
}
protected function routes()
@ -268,6 +312,8 @@ class ApiServiceProvider extends ServiceProvider
'flarum.api.groups.delete',
$this->action('Flarum\Api\Actions\Groups\DeleteAction')
);
event(new RegisterApiRoutes($routes));
}
protected function action($class)

View File

@ -1,10 +1,10 @@
<?php namespace Flarum\Api\Serializers;
use BadMethodCallException;
use Closure;
use Flarum\Core\Users\User;
use Flarum\Events\ApiAttributes;
use Flarum\Events\ApiRelationship;
use Tobscure\JsonApi\SerializerAbstract;
use Flarum\Api\Events\SerializeAttributes;
abstract class Serializer extends SerializerAbstract
{
@ -13,13 +13,6 @@ abstract class Serializer extends SerializerAbstract
*/
public $actor;
/**
* An array of custom relation methods, grouped by subclass.
*
* @var array
*/
protected static $relationMethods = [];
/**
* @param User $actor
* @param array|null $include
@ -39,7 +32,7 @@ abstract class Serializer extends SerializerAbstract
{
$attributes = $this->getDefaultAttributes($model);
event(new SerializeAttributes($this, $model, $attributes));
event(new ApiAttributes($this, $model, $attributes));
return $attributes;
}
@ -57,13 +50,26 @@ abstract class Serializer extends SerializerAbstract
*/
protected function getRelationshipFromMethod($name)
{
if (isset(static::$relationMethods[$name])) {
return call_user_func(static::$relationMethods[$name], $this);
if ($relationship = $this->getCustomRelationship($name)) {
return $relationship;
}
return parent::getRelationshipFromMethod($name);
}
/**
* Get a custom relationship.
*
* @param string $name
* @return mixed
*/
protected function getCustomRelationship($name)
{
return app('events')->until(
new ApiRelationship($this, $name)
);
}
/**
* Get a closure that returns a Collection/Resource representing a relation.
*
@ -154,16 +160,4 @@ abstract class Serializer extends SerializerAbstract
{
return $this->getRelationship($serializer, $relation, true);
}
/**
* Add a custom relation to the model.
*
* @param string $name The name of the relation.
* @param callable $callback The callback to execute. This should return a
* relation closure {@see Serializer::getRelationship()}
*/
public static function setRelationMethod($name, callable $callback)
{
static::$relationMethods[get_called_class()][$name] = $callback;
}
}

View File

@ -42,10 +42,12 @@ class GenerateExtensionCommand extends Command
public function fire()
{
do {
$name = $this->ask('Extension name (<vendor>-<name>):');
} while (! preg_match('/^([a-z0-9]+)-([a-z0-9-]+)$/i', $name, $match));
$vendor = $this->ask('Vendor name:');
} while (! preg_match('/^([a-z0-9-]+)$/i', $vendor));
list(, $vendor, $package) = $match;
do {
$name = $this->ask('Extension name:');
} while (! preg_match('/^([a-z0-9-]+)$/i', $name));
do {
$title = $this->ask('Title:');
@ -64,9 +66,8 @@ class GenerateExtensionCommand extends Command
$dir = public_path().'/extensions/'.$name;
$replacements = [
'{{namespace}}' => ucfirst($vendor).'\\'.ucfirst($package),
'{{escapedNamespace}}' => ucfirst($vendor).'\\\\'.ucfirst($package),
'{{classPrefix}}' => ucfirst($package),
'{{namespace}}' => ucfirst($vendor).'\\'.ucfirst($name),
'{{escapedNamespace}}' => ucfirst($vendor).'\\\\'.ucfirst($name),
'{{name}}' => $name
];
@ -78,7 +79,7 @@ class GenerateExtensionCommand extends Command
'name' => $name,
'title' => $title,
'description' => $description,
'tags' => [],
'keywords' => [],
'version' => '0.1.0',
'author' => [
'name' => $authorName,

View File

@ -9,7 +9,7 @@ use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Discussions\DiscussionState;
use Flarum\Core\Posts\CommentPost;
use Flarum\Tags\Tag;
use Flarum\Core\Posts\Events\PostWasPosted;
use Flarum\Events\PostWasPosted;
use Symfony\Component\Console\Helper\ProgressBar;
class ImportCommand extends Command

View File

@ -27,7 +27,7 @@ class Activity extends Model
/**
* {@inheritdoc}
*/
protected static $dateAttributes = ['time'];
protected $dates = ['time'];
/**
* A map of activity types and the model classes to use for their subjects.

View File

@ -1,5 +1,7 @@
<?php namespace Flarum\Core\Activity;
use Flarum\Core\Users\User;
use Flarum\Events\RegisterActivityTypes;
use Flarum\Support\ServiceProvider;
use Flarum\Extend;
@ -12,18 +14,33 @@ class ActivityServiceProvider extends ServiceProvider
*/
public function boot()
{
$this->extend([
(new Extend\EventSubscriber('Flarum\Core\Activity\Listeners\UserActivitySyncer')),
$this->registerActivityTypes();
(new Extend\ActivityType('Flarum\Core\Activity\PostedBlueprint'))
->subjectSerializer('Flarum\Api\Serializers\PostBasicSerializer'),
$events = $this->app->make('events');
$events->subscribe('Flarum\Core\Activity\Listeners\UserActivitySyncer');
}
(new Extend\ActivityType('Flarum\Core\Activity\StartedDiscussionBlueprint'))
->subjectSerializer('Flarum\Api\Serializers\PostBasicSerializer'),
/**
* Register activity types.
*
* @return void
*/
public function registerActivityTypes()
{
$blueprints = [
'Flarum\Core\Activity\PostedBlueprint',
'Flarum\Core\Activity\StartedDiscussionBlueprint',
'Flarum\Core\Activity\JoinedBlueprint'
];
(new Extend\ActivityType('Flarum\Core\Activity\JoinedBlueprint'))
->subjectSerializer('Flarum\Api\Serializers\UserBasicSerializer')
]);
event(new RegisterActivityTypes($blueprints));
foreach ($blueprints as $blueprint) {
Activity::setSubjectModel(
$blueprint::getType(),
$blueprint::getSubjectModel()
);
}
}
/**

View File

@ -5,11 +5,11 @@ use Flarum\Core\Activity\PostedBlueprint;
use Flarum\Core\Activity\StartedDiscussionBlueprint;
use Flarum\Core\Activity\JoinedBlueprint;
use Flarum\Core\Posts\Post;
use Flarum\Core\Posts\Events\PostWasPosted;
use Flarum\Core\Posts\Events\PostWasDeleted;
use Flarum\Core\Posts\Events\PostWasHidden;
use Flarum\Core\Posts\Events\PostWasRestored;
use Flarum\Core\Users\Events\UserWasRegistered;
use Flarum\Events\PostWasPosted;
use Flarum\Events\PostWasDeleted;
use Flarum\Events\PostWasHidden;
use Flarum\Events\PostWasRestored;
use Flarum\Events\UserWasRegistered;
use Illuminate\Contracts\Events\Dispatcher;
class UserActivitySyncer
@ -33,15 +33,15 @@ class UserActivitySyncer
*/
public function subscribe(Dispatcher $events)
{
$events->listen('Flarum\Core\Posts\Events\PostWasPosted', __CLASS__.'@whenPostWasPosted');
$events->listen('Flarum\Core\Posts\Events\PostWasHidden', __CLASS__.'@whenPostWasHidden');
$events->listen('Flarum\Core\Posts\Events\PostWasRestored', __CLASS__.'@whenPostWasRestored');
$events->listen('Flarum\Core\Posts\Events\PostWasDeleted', __CLASS__.'@whenPostWasDeleted');
$events->listen('Flarum\Core\Users\Events\UserWasRegistered', __CLASS__.'@whenUserWasRegistered');
$events->listen('Flarum\Events\PostWasPosted', __CLASS__.'@whenPostWasPosted');
$events->listen('Flarum\Events\PostWasHidden', __CLASS__.'@whenPostWasHidden');
$events->listen('Flarum\Events\PostWasRestored', __CLASS__.'@whenPostWasRestored');
$events->listen('Flarum\Events\PostWasDeleted', __CLASS__.'@whenPostWasDeleted');
$events->listen('Flarum\Events\UserWasRegistered', __CLASS__.'@whenUserWasRegistered');
}
/**
* @param \Flarum\Core\Posts\Events\PostWasPosted $event
* @param \Flarum\Events\PostWasPosted $event
* @return void
*/
public function whenPostWasPosted(PostWasPosted $event)
@ -50,7 +50,7 @@ class UserActivitySyncer
}
/**
* @param \Flarum\Core\Posts\Events\PostWasHidden $event
* @param \Flarum\Events\PostWasHidden $event
* @return void
*/
public function whenPostWasHidden(PostWasHidden $event)
@ -59,7 +59,7 @@ class UserActivitySyncer
}
/**
* @param \Flarum\Core\Posts\Events\PostWasRestored $event
* @param \Flarum\Events\PostWasRestored $event
* @return void
*/
public function whenPostWasRestored(PostWasRestored $event)
@ -68,7 +68,7 @@ class UserActivitySyncer
}
/**
* @param \Flarum\Core\Posts\Events\PostWasDeleted $event
* @param \Flarum\Events\PostWasDeleted $event
* @return void
*/
public function whenPostWasDeleted(PostWasDeleted $event)
@ -77,7 +77,7 @@ class UserActivitySyncer
}
/**
* @param \Flarum\Core\Users\Events\UserWasRegistered $event
* @param \Flarum\Events\UserWasRegistered $event
* @return void
*/
public function whenUserWasRegistered(UserWasRegistered $event)

View File

@ -1,8 +1,7 @@
<?php namespace Flarum\Core;
use Flarum\Core\Settings\MemoryCacheSettingsRepository;
use Flarum\Core\Settings\DatabaseSettingsRepository;
use Flarum\Core\Users\User;
use Flarum\Events\ModelAllow;
use Flarum\Support\ServiceProvider;
use Flarum\Extend;
@ -21,8 +20,13 @@ class CoreServiceProvider extends ServiceProvider
return get_class($command).'Handler@handle';
});
Forum::allow('*', function (Forum $forum, User $user, $action) {
return $user->hasPermission('forum.'.$action) ?: null;
$events = $this->app->make('events');
$events->listen(ModelAllow::class, function (ModelAllow $event) {
if ($event->model instanceof Forum &&
$event->actor->hasPermission('forum.'.$event->action)) {
return true;
}
});
}
@ -33,14 +37,6 @@ class CoreServiceProvider extends ServiceProvider
*/
public function register()
{
$this->app->singleton('Flarum\Core\Settings\SettingsRepository', function() {
return new MemoryCacheSettingsRepository(
new DatabaseSettingsRepository(
$this->app->make('Illuminate\Database\ConnectionInterface')
)
);
});
$this->app->singleton('flarum.forum', 'Flarum\Core\Forum');
// TODO: probably use Illuminate's AggregateServiceProvider

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Core\Discussions\Commands;
use Flarum\Core\Discussions\DiscussionRepository;
use Flarum\Core\Discussions\Events\DiscussionWillBeDeleted;
use Flarum\Events\DiscussionWillBeDeleted;
use Flarum\Core\Support\DispatchesEvents;
class DeleteDiscussionHandler

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Core\Discussions\Commands;
use Flarum\Core\Discussions\DiscussionRepository;
use Flarum\Core\Discussions\Events\DiscussionWillBeSaved;
use Flarum\Events\DiscussionWillBeSaved;
use Flarum\Core\Support\DispatchesEvents;
class EditDiscussionHandler

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Core\Discussions\Commands;
use Flarum\Core\Discussions\DiscussionRepository;
use Flarum\Core\Discussions\Events\DiscussionStateWillBeSaved;
use Flarum\Events\DiscussionStateWillBeSaved;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Core\Support\DispatchesEvents;

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Core\Discussions\Commands;
use Flarum\Core\Discussions\Events\DiscussionWillBeSaved;
use Flarum\Events\DiscussionWillBeSaved;
use Flarum\Core\Forum;
use Illuminate\Contracts\Bus\Dispatcher;
use Flarum\Core\Discussions\Discussion;

View File

@ -1,10 +1,10 @@
<?php namespace Flarum\Core\Discussions;
use Flarum\Core\Model;
use Flarum\Core\Discussions\Events\DiscussionWasDeleted;
use Flarum\Core\Discussions\Events\DiscussionWasStarted;
use Flarum\Core\Discussions\Events\DiscussionWasRenamed;
use Flarum\Core\Posts\Events\PostWasDeleted;
use Flarum\Events\DiscussionWasDeleted;
use Flarum\Events\DiscussionWasStarted;
use Flarum\Events\DiscussionWasRenamed;
use Flarum\Events\PostWasDeleted;
use Flarum\Core\Posts\Post;
use Flarum\Core\Posts\MergeablePost;
use Flarum\Core\Users\Guest;
@ -21,13 +21,10 @@ class Discussion extends Model
{
use EventGenerator;
use Locked;
use VisibleScope;
use ValidatesBeforeSave;
/**
* The table associated with the model.
*
* @var string
* {@inheritdoc}
*/
protected $table = 'discussions';
@ -57,11 +54,9 @@ class Discussion extends Model
protected $modifiedPosts = [];
/**
* The attributes that should be mutated to dates.
*
* @var array
* {@inheritdoc}
*/
protected static $dateAttributes = ['start_time', 'last_time'];
protected $dates = ['start_time', 'last_time'];
/**
* The user for which the state relationship should be loaded.

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Core\Discussions;
use Flarum\Core\Discussions\Events\DiscussionWasRead;
use Flarum\Events\DiscussionWasRead;
use Flarum\Core\Model;
use Flarum\Core\Support\EventGenerator;
use Illuminate\Database\Eloquent\Builder;
@ -26,7 +26,7 @@ class DiscussionState extends Model
/**
* {@inheritdoc}
*/
protected static $dateAttributes = ['read_time'];
protected $dates = ['read_time'];
/**
* Mark the discussion as being read up to a certain point. Raises the

View File

@ -2,6 +2,8 @@
use Flarum\Core\Search\GambitManager;
use Flarum\Core\Users\User;
use Flarum\Events\ModelAllow;
use Flarum\Events\RegisterDiscussionGambits;
use Flarum\Support\ServiceProvider;
use Flarum\Extend;
use Illuminate\Contracts\Container\Container;
@ -15,25 +17,29 @@ class DiscussionsServiceProvider extends ServiceProvider
*/
public function boot()
{
$this->extend([
new Extend\EventSubscriber('Flarum\Core\Discussions\Listeners\DiscussionMetadataUpdater')
]);
Discussion::setValidator($this->app->make('validator'));
Discussion::allow('*', function (Discussion $discussion, User $user, $action) {
return $user->hasPermission('discussion.'.$action) ?: null;
});
$events = $this->app->make('events');
// Allow a user to rename their own discussion.
Discussion::allow('rename', function (Discussion $discussion, User $user) {
return $discussion->start_user_id == $user->id ?: null;
// TODO: add limitations to time etc. according to a config setting
});
$events->subscribe('Flarum\Core\Discussions\Listeners\DiscussionMetadataUpdater');
Discussion::allow('delete', function (Discussion $discussion, User $user) {
return $discussion->start_user_id == $user->id && $discussion->participants_count == 1 ?: null;
// TODO: add limitations to time etc. according to a config setting
$events->listen(ModelAllow::class, function (ModelAllow $event) {
if ($event->model instanceof Discussion) {
if ($event->action === 'rename' &&
$event->model->start_user_id == $event->actor->id) {
return true;
}
if ($event->action === 'delete' &&
$event->model->start_user_id == $event->actor->id &&
$event->model->participants_count == 1) {
return true;
}
if ($event->actor->hasPermission('discussion.'.$event->action)) {
return true;
}
}
});
}
@ -49,21 +55,15 @@ class DiscussionsServiceProvider extends ServiceProvider
'Flarum\Core\Discussions\Search\Fulltext\MySqlFulltextDriver'
);
$this->app->instance('flarum.discussionGambits', [
'Flarum\Core\Discussions\Search\Gambits\AuthorGambit',
'Flarum\Core\Discussions\Search\Gambits\UnreadGambit'
]);
$this->app->when('Flarum\Core\Discussions\Search\DiscussionSearcher')
->needs('Flarum\Core\Search\GambitManager')
->give(function (Container $app) {
$gambits = new GambitManager($app);
foreach ($app->make('flarum.discussionGambits') as $gambit) {
$gambits->add($gambit);
}
$gambits->setFulltextGambit('Flarum\Core\Discussions\Search\Gambits\FulltextGambit');
$gambits->add('Flarum\Core\Discussions\Search\Gambits\AuthorGambit');
$gambits->add('Flarum\Core\Discussions\Search\Gambits\UnreadGambit');
event(new RegisterDiscussionGambits($gambits));
return $gambits;
});

View File

@ -1,10 +1,10 @@
<?php namespace Flarum\Core\Discussions\Listeners;
use Flarum\Core\Posts\Post;
use Flarum\Core\Posts\Events\PostWasPosted;
use Flarum\Core\Posts\Events\PostWasDeleted;
use Flarum\Core\Posts\Events\PostWasHidden;
use Flarum\Core\Posts\Events\PostWasRestored;
use Flarum\Events\PostWasPosted;
use Flarum\Events\PostWasDeleted;
use Flarum\Events\PostWasHidden;
use Flarum\Events\PostWasRestored;
use Illuminate\Contracts\Events\Dispatcher;
class DiscussionMetadataUpdater
@ -34,7 +34,7 @@ class DiscussionMetadataUpdater
}
/**
* @param PostWasDeleted $event
* @param \Flarum\Events\PostWasDeleted $event
*/
public function whenPostWasDeleted(PostWasDeleted $event)
{

View File

@ -7,7 +7,7 @@ use Flarum\Core\Search\SearcherInterface;
use Flarum\Core\Search\GambitManager;
use Flarum\Core\Discussions\DiscussionRepository;
use Flarum\Core\Posts\PostRepository;
use Flarum\Core\Discussions\Events\DiscussionSearchWillBePerformed;
use Flarum\Events\DiscussionSearchWillBePerformed;
use Flarum\Core\Search\SearchResults;
use Illuminate\Database\Eloquent\Collection;

View File

@ -12,9 +12,6 @@ class FormatterServiceProvider extends ServiceProvider
*/
public function boot()
{
$this->extend([
new Extend\PostFormatter('Flarum\Core\Formatter\LinkifyFormatter')
]);
}
/**

View File

@ -1,6 +1,13 @@
<?php namespace Flarum\Core;
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Core\Users\User;
use Flarum\Events\ModelAllow;
use Flarum\Events\ModelDates;
use Flarum\Events\ModelRelationship;
use Flarum\Events\ScopeModelVisibility;
use Illuminate\Contracts\Validation\Factory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model as Eloquent;
use Illuminate\Database\Eloquent\Relations\Relation;
use LogicException;
@ -11,6 +18,9 @@ use LogicException;
* Adds the ability for custom relations to be added to a model during runtime.
* These relations behave in the same way that you would expect; they can be
* queried, eager loaded, and accessed as an attribute.
*
* Also has a scope method `whereVisibleTo` that scopes a query to only include
* records that the user has permission to see.
*/
abstract class Model extends Eloquent
{
@ -21,20 +31,6 @@ abstract class Model extends Eloquent
*/
public $timestamps = false;
/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected static $dateAttributes = [];
/**
* An array of custom relation methods, grouped by subclass.
*
* @var array
*/
protected static $relationMethods = [];
/**
* Get the attributes that should be converted to dates.
*
@ -42,17 +38,56 @@ abstract class Model extends Eloquent
*/
public function getDates()
{
return array_merge(static::$dateAttributes, $this->dates);
$dates = $this->dates;
event(new ModelDates($this, $dates));
return $dates;
}
/**
* Add an attribute to be converted to a date.
* Check whether or not a user has permission to perform an action,
* according to the collected conditions.
*
* @param string $attribute
* @param User $actor
* @param string $action
* @return bool
*/
public static function addDateAttribute($attribute)
public function can(User $actor, $action)
{
static::$dateAttributes[] = $attribute;
$can = static::$dispatcher->until(new ModelAllow($this, $actor, $action));
if ($can !== null) {
return $can;
}
return false;
}
/**
* Assert that the user has a certain permission for this model, throwing
* an exception if they don't.
*
* @param User $actor
* @param string $action
* @throws PermissionDeniedException
*/
public function assertCan(User $actor, $action)
{
if (! $this->can($actor, $action)) {
throw new PermissionDeniedException;
}
}
/**
* Scope a query to only include records that are visible to a user.
*
* @param Builder $query
* @param User $actor
*/
public function scopeWhereVisibleTo(Builder $query, User $actor)
{
event(new ScopeModelVisibility($this, $query, $actor));
}
/**
@ -71,40 +106,38 @@ abstract class Model extends Eloquent
// If a custom relation with this key has been set up, then we will load
// and return results from the query and hydrate the relationship's
// value on the "relationships" array.
if (isset(static::$relationMethods[get_called_class()][$key])) {
return $this->getCustomRelationship($key);
if ($relation = $this->getCustomRelation($key)) {
if (! $relation instanceof Relation) {
throw new LogicException('Relationship method must return an object of type '
. 'Illuminate\Database\Eloquent\Relations\Relation');
}
return $this->relations[$key] = $relation->getResults();
}
}
/**
* Get a relationship value from a custom relationship method.
* Get a custom relation object.
*
* @param string $name
* @return mixed
*
* @throws \LogicException
*/
protected function getCustomRelationship($name)
protected function getCustomRelation($name)
{
$relation = static::$relationMethods[get_called_class()][$name]($this);
if (! $relation instanceof Relation) {
throw new LogicException('Relationship method must return an object of type '
. 'Illuminate\Database\Eloquent\Relations\Relation');
}
return $this->relations[$name] = $relation->getResults();
return static::$dispatcher->until(
new ModelRelationship($this, $name)
);
}
/**
* Add a custom relation to the model.
*
* @param string $name The name of the relation.
* @param callable $callback The callback to execute. This should return an
* object of type Illuminate\Database\Eloquent\Relations\Relation.
* @inheritdoc
*/
public static function setRelationMethod($name, callable $callback)
public function __call($method, $arguments)
{
static::$relationMethods[get_called_class()][$name] = $callback;
if ($relation = $this->getCustomRelation($method)) {
return $relation;
}
return parent::__call($method, $arguments);
}
}

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Core\Notifications\Listeners;
use Flarum\Core\Discussions\Events\DiscussionWasRenamed;
use Flarum\Events\DiscussionWasRenamed;
use Flarum\Core\Posts\DiscussionRenamedPost;
use Flarum\Core\Notifications\DiscussionRenamedBlueprint;
use Flarum\Core\Notifications\NotificationSyncer;
@ -30,7 +30,7 @@ class DiscussionRenamedNotifier
}
/**
* @param DiscussionWasRenamed $event
* @param \Flarum\Events\DiscussionWasRenamed $event
*/
public function whenDiscussionWasRenamed(DiscussionWasRenamed $event)
{

View File

@ -31,7 +31,7 @@ class Notification extends Model
/**
* {@inheritdoc}
*/
protected static $dateAttributes = ['time'];
protected $dates = ['time'];
/**
* A map of notification types and the model classes to use for their

View File

@ -22,7 +22,7 @@ class NotificationRepository
->whereIn('type', $user->getAlertableNotificationTypes())
->where('is_deleted', false)
->groupBy('type', 'subject_id')
->latest('time')
->orderByRaw('MAX(time) DESC')
->skip($offset)
->take($limit);

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Core\Notifications;
use Flarum\Core\Notifications\Events\NotificationWillBeSent;
use Flarum\Events\NotificationWillBeSent;
use Flarum\Core\Users\User;
use Carbon\Carbon;

View File

@ -1,7 +1,10 @@
<?php namespace Flarum\Core\Notifications;
use Flarum\Core\Users\User;
use Flarum\Events\RegisterNotificationTypes;
use Flarum\Support\ServiceProvider;
use Flarum\Extend;
use ReflectionClass;
class NotificationsServiceProvider extends ServiceProvider
{
@ -12,13 +15,46 @@ class NotificationsServiceProvider extends ServiceProvider
*/
public function boot()
{
$this->extend([
(new Extend\EventSubscriber('Flarum\Core\Notifications\Listeners\DiscussionRenamedNotifier')),
$events = $this->app->make('events');
(new Extend\NotificationType('Flarum\Core\Notifications\DiscussionRenamedBlueprint'))
->subjectSerializer('Flarum\Api\Serializers\DiscussionBasicSerializer')
->enableByDefault('alert')
]);
$events->subscribe('Flarum\Core\Notifications\Listeners\DiscussionRenamedNotifier');
$this->registerNotificationTypes();
}
/**
* Register notification types.
*
* @return void
*/
public function registerNotificationTypes()
{
$blueprints = [
'Flarum\Core\Notifications\DiscussionRenamedBlueprint' => ['alert']
];
event(new RegisterNotificationTypes($blueprints));
foreach ($blueprints as $blueprint => $enabled) {
Notification::setSubjectModel(
$type = $blueprint::getType(),
$blueprint::getSubjectModel()
);
User::addPreference(
User::getNotificationPreferenceKey($type, 'alert'),
'boolval',
in_array('alert', $enabled)
);
if ((new ReflectionClass($blueprint))->implementsInterface('Flarum\Core\Notifications\MailableBlueprint')) {
User::addPreference(
User::getNotificationPreferenceKey($type, 'email'),
'boolval',
in_array('email', $enabled)
);
}
}
}
/**

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Core\Posts\Commands;
use Flarum\Core\Posts\PostRepository;
use Flarum\Core\Posts\Events\PostWillBeDeleted;
use Flarum\Events\PostWillBeDeleted;
use Flarum\Core\Support\DispatchesEvents;
class DeletePostHandler

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Core\Posts\Commands;
use Flarum\Core\Posts\PostRepository;
use Flarum\Core\Posts\Events\PostWillBeSaved;
use Flarum\Events\PostWillBeSaved;
use Flarum\Core\Support\DispatchesEvents;
use Flarum\Core\Posts\CommentPost;

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Core\Posts\Commands;
use Flarum\Core\Posts\Events\PostWillBeSaved;
use Flarum\Events\PostWillBeSaved;
use Flarum\Core\Discussions\DiscussionRepository;
use Flarum\Core\Posts\CommentPost;
use Flarum\Core\Support\DispatchesEvents;

View File

@ -2,10 +2,10 @@
use DomainException;
use Flarum\Core\Formatter\FormatterManager;
use Flarum\Core\Posts\Events\PostWasPosted;
use Flarum\Core\Posts\Events\PostWasRevised;
use Flarum\Core\Posts\Events\PostWasHidden;
use Flarum\Core\Posts\Events\PostWasRestored;
use Flarum\Events\PostWasPosted;
use Flarum\Events\PostWasRevised;
use Flarum\Events\PostWasHidden;
use Flarum\Events\PostWasRestored;
use Flarum\Core\Users\User;
/**

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Core\Posts;
use DomainException;
use Flarum\Core\Posts\Events\PostWasDeleted;
use Flarum\Events\PostWasDeleted;
use Flarum\Core\Model;
use Flarum\Core\Support\Locked;
use Flarum\Core\Support\EventGenerator;
@ -42,7 +42,7 @@ class Post extends Model
/**
* {@inheritdoc}
*/
protected static $dateAttributes = ['time', 'edit_time', 'hide_time'];
protected $dates = ['time', 'edit_time', 'hide_time'];
/**
* A map of post types, as specified in the `type` column, to their
@ -161,6 +161,7 @@ class Post extends Model
&& isset(static::$models[$attributes['type']])
&& class_exists($class = static::$models[$attributes['type']])
) {
/** @var Post $instance */
$instance = new $class;
$instance->exists = true;
$instance->setRawAttributes($attributes, true);

View File

@ -2,6 +2,9 @@
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Users\User;
use Flarum\Events\ModelAllow;
use Flarum\Events\RegisterPostTypes;
use Flarum\Events\ScopePostVisibility;
use Flarum\Support\ServiceProvider;
use Flarum\Extend;
@ -14,45 +17,73 @@ class PostsServiceProvider extends ServiceProvider
*/
public function boot()
{
$this->extend([
new Extend\PostType('Flarum\Core\Posts\CommentPost'),
new Extend\PostType('Flarum\Core\Posts\DiscussionRenamedPost')
]);
Post::setValidator($this->app->make('validator'));
CommentPost::setFormatter($this->app->make('flarum.formatter'));
Post::allow('*', function ($post, $user, $action) {
return $post->discussion->can($user, $action.'Posts') ?: null;
$this->registerPostTypes();
$events = $this->app->make('events');
$events->listen(ModelAllow::class, function (ModelAllow $event) {
if ($event->model instanceof Post) {
$post = $event->model;
$action = $event->action;
$actor = $event->actor;
if ($action === 'view' &&
(! $post->hide_user_id || $post->can($actor, 'edit'))) {
return true;
}
// A post is allowed to be edited if the user has permission to moderate
// the discussion which it's in, or if they are the author and the post
// hasn't been deleted by someone else.
if ($action === 'edit' &&
($post->discussion->can($actor, 'editPosts') ||
($post->user_id == $actor->id &&
(! $post->hide_user_id || $post->hide_user_id == $actor->id)))) {
return true;
}
if ($post->discussion->can($actor, $action.'Posts')) {
return true;
}
}
});
// When fetching a discussion's posts: if the user doesn't have permission
// to moderate the discussion, then they can't see posts that have been
// hidden by someone other than themself.
Discussion::addPostVisibilityScope(function ($query, User $user, Discussion $discussion) {
if (! $discussion->can($user, 'editPosts')) {
$query->where(function ($query) use ($user) {
$events->listen(ScopePostVisibility::class, function (ScopePostVisibility $event) {
$user = $event->actor;
if (! $event->discussion->can($user, 'editPosts')) {
$event->query->where(function ($query) use ($user) {
$query->whereNull('hide_user_id')
->orWhere('hide_user_id', $user->id);
->orWhere('hide_user_id', $user->id);
});
}
});
}
Post::allow('view', function ($post, $user) {
return ! $post->hide_user_id || $post->can($user, 'edit') ?: null;
});
/**
* Register post types.
*
* @return void
*/
public function registerPostTypes()
{
$models = [
'Flarum\Core\Posts\CommentPost',
'Flarum\Core\Posts\DiscussionRenamedPost'
];
// A post is allowed to be edited if the user has permission to moderate
// the discussion which it's in, or if they are the author and the post
// hasn't been deleted by someone else.
Post::allow('edit', function ($post, $user) {
if ($post->discussion->can($user, 'editPosts') ||
($post->user_id == $user->id && (! $post->hide_user_id || $post->hide_user_id == $user->id))
) {
return true;
}
});
event(new RegisterPostTypes($models));
foreach ($models as $model) {
Post::setModel($model::$type, $model);
}
}
/**

View File

@ -17,7 +17,7 @@ abstract class RegexGambit implements Gambit
if ($matches = $this->match($bit)) {
list($negate) = array_splice($matches, 1, 1);
$this->conditions($searcher, $matches, !! $negate);
$this->conditions($search, $matches, !! $negate);
}
return !! $matches;

View File

@ -0,0 +1,22 @@
<?php namespace Flarum\Core\Settings;
use Flarum\Support\ServiceProvider;
class SettingsServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('Flarum\Core\Settings\SettingsRepository', function () {
return new MemoryCacheSettingsRepository(
new DatabaseSettingsRepository(
$this->app->make('Illuminate\Database\ConnectionInterface')
)
);
});
}
}

View File

@ -2,6 +2,8 @@
use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Core\Users\User;
use Flarum\Events\ModelAllow;
use Illuminate\Contracts\Events\Dispatcher;
/**
* 'Lock' an object, allowing the permission of a user to perform an action to
@ -9,75 +11,5 @@ use Flarum\Core\Users\User;
*/
trait Locked
{
/**
* @var callable[]
*/
protected static $conditions = [];
/**
* Get the condition callbacks for the specified action.
*
* @param string $action
* @return callable[]
*/
protected static function getConditions($action)
{
$conditions = array_get(static::$conditions, $action, []);
$all = array_get(static::$conditions, '*', []);
return array_merge($conditions, $all);
}
/**
* Allow the specified action if the given condition is satisfied.
*
* @param string $action
* @param callable $condition The condition callback. Parameters are the
* object that is locked, the user performing the action,
* and the name of the action. This condition will be ignored if it
* returns null; otherwise, the return value will determine whether or
* not the action is allowed.
*/
public static function allow($action, callable $condition)
{
foreach ((array)$action as $action) {
static::$conditions[$action][] = $condition;
}
}
/**
* Check whether or not a user has permission to perform an action,
* according to the collected conditions.
*
* @param User $actor
* @param string $action
* @return bool
*/
public function can(User $actor, $action)
{
foreach ($this->getConditions($action) as $condition) {
$can = $condition($this, $actor, $action);
if ($can !== null) {
return $can;
}
}
return false;
}
/**
* Assert that the user has a certain permission for this model, throwing
* an exception if they don't.
*
* @param User $actor
* @param string $action
* @throws PermissionDeniedException
*/
public function assertCan(User $actor, $action)
{
if (! $this->can($actor, $action)) {
throw new PermissionDeniedException;
}
}
}

View File

@ -1,6 +1,7 @@
<?php namespace Flarum\Core\Support;
use Illuminate\Contracts\Validation\Factory;
use Flarum\Events\ModelValidator;
use Illuminate\Validation\Factory;
use Illuminate\Contracts\Validation\ValidationException;
use Illuminate\Validation\Validator;
@ -79,13 +80,11 @@ trait ValidatesBeforeSave
*/
protected function makeValidator()
{
$dirty = $this->getDirty();
$rules = $this->expandUniqueRules($this->rules);
$rules = $this->expandUniqueRules(array_only($this->rules, array_keys($dirty)));
$validator = static::$validator->make($this->getAttributes(), $rules);
$validator = static::$validator->make($dirty, $rules);
// TODO: event
event(new ModelValidator($this, $validator));
return $validator;
}

View File

@ -1,40 +0,0 @@
<?php namespace Flarum\Core\Support;
use Flarum\Core\Users\User;
use Illuminate\Database\Eloquent\Builder;
/**
* Add a query scope to an Eloquent model that filters out records that a user
* is not allowed to view.
*/
trait VisibleScope
{
/**
* @var callable[]
*/
protected static $visibleScopes = [];
/**
* Add a callback to scope a query to only include records that are visible
* to a user.
*
* @param callable $scope
*/
public static function addVisibleScope(callable $scope)
{
static::$visibleScopes[] = $scope;
}
/**
* Scope a query to only include records that are visible to a user.
*
* @param Builder $query
* @param User $user
*/
public function scopeWhereVisibleTo(Builder $query, User $user)
{
foreach (static::$visibleScopes as $scope) {
$scope($query, $user);
}
}
}

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Core\Users\Commands;
use Flarum\Core\Users\UserRepository;
use Flarum\Core\Users\Events\UserWillBeSaved;
use Flarum\Events\UserWillBeSaved;
use Flarum\Core\Support\DispatchesEvents;
use Flarum\Core\Exceptions\InvalidConfirmationTokenException;
use Flarum\Core\Users\EmailToken;

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Core\Users\Commands;
use Flarum\Core\Users\Events\AvatarWillBeDeleted;
use Flarum\Events\AvatarWillBeDeleted;
use Flarum\Core\Users\UserRepository;
use Flarum\Core\Support\DispatchesEvents;
use League\Flysystem\FilesystemInterface;

View File

@ -2,7 +2,7 @@
use Flarum\Core\Users\User;
use Flarum\Core\Users\UserRepository;
use Flarum\Core\Users\Events\UserWillBeDeleted;
use Flarum\Events\UserWillBeDeleted;
use Flarum\Core\Support\DispatchesEvents;
class DeleteUserHandler

View File

@ -2,7 +2,7 @@
use Flarum\Core\Users\User;
use Flarum\Core\Users\UserRepository;
use Flarum\Core\Users\Events\UserWillBeSaved;
use Flarum\Events\UserWillBeSaved;
use Flarum\Core\Support\DispatchesEvents;
class EditUserHandler

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Core\Users\Commands;
use Flarum\Core\Users\User;
use Flarum\Core\Users\Events\UserWillBeSaved;
use Flarum\Events\UserWillBeSaved;
use Flarum\Core\Support\DispatchesEvents;
class RegisterUserHandler

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Core\Users\Commands;
use Flarum\Core\Users\Events\AvatarWillBeSaved;
use Flarum\Events\AvatarWillBeSaved;
use Flarum\Core\Users\UserRepository;
use Flarum\Core\Support\DispatchesEvents;
use Illuminate\Support\Str;

View File

@ -1,8 +1,8 @@
<?php namespace Flarum\Core\Users\Listeners;
use Flarum\Core\Settings\SettingsRepository;
use Flarum\Core\Users\Events\UserWasRegistered;
use Flarum\Core\Users\Events\UserEmailChangeWasRequested;
use Flarum\Events\UserWasRegistered;
use Flarum\Events\UserEmailChangeWasRequested;
use Flarum\Core;
use Flarum\Core\Users\EmailToken;
use Flarum\Core\Users\User;
@ -42,7 +42,7 @@ class EmailConfirmationMailer
}
/**
* @param UserWasRegistered $event
* @param \Flarum\Events\UserWasRegistered $event
*/
public function whenUserWasRegistered(UserWasRegistered $event)
{
@ -56,7 +56,7 @@ class EmailConfirmationMailer
}
/**
* @param UserEmailChangeWasRequested $event
* @param \Flarum\Events\UserEmailChangeWasRequested $event
*/
public function whenUserEmailChangeWasRequested(UserEmailChangeWasRequested $event)
{

View File

@ -1,12 +1,12 @@
<?php namespace Flarum\Core\Users\Listeners;
use Flarum\Core\Users\User;
use Flarum\Core\Posts\Events\PostWasPosted;
use Flarum\Core\Posts\Events\PostWasDeleted;
use Flarum\Core\Posts\Events\PostWasHidden;
use Flarum\Core\Posts\Events\PostWasRestored;
use Flarum\Core\Discussions\Events\DiscussionWasStarted;
use Flarum\Core\Discussions\Events\DiscussionWasDeleted;
use Flarum\Events\PostWasPosted;
use Flarum\Events\PostWasDeleted;
use Flarum\Events\PostWasHidden;
use Flarum\Events\PostWasRestored;
use Flarum\Events\DiscussionWasStarted;
use Flarum\Events\DiscussionWasDeleted;
use Illuminate\Contracts\Events\Dispatcher;
class UserMetadataUpdater
@ -49,7 +49,7 @@ class UserMetadataUpdater
}
/**
* @param PostWasRestored $event
* @param \Flarum\Events\PostWasRestored $event
*/
public function whenPostWasRestored(PostWasRestored $event)
{
@ -57,7 +57,7 @@ class UserMetadataUpdater
}
/**
* @param DiscussionWasStarted $event
* @param \Flarum\Events\DiscussionWasStarted $event
*/
public function whenDiscussionWasStarted(DiscussionWasStarted $event)
{
@ -65,7 +65,7 @@ class UserMetadataUpdater
}
/**
* @param DiscussionWasDeleted $event
* @param \Flarum\Events\DiscussionWasDeleted $event
*/
public function whenDiscussionWasDeleted(DiscussionWasDeleted $event)
{

View File

@ -5,7 +5,7 @@ use Flarum\Core\Search\GambitManager;
use Flarum\Core\Search\SearchCriteria;
use Flarum\Core\Search\SearchResults;
use Flarum\Core\Users\UserRepository;
use Flarum\Core\Users\Events\UserSearchWillBePerformed;
use Flarum\Events\UserSearchWillBePerformed;
/**
* Takes a UserSearchCriteria object, performs a search using gambits,

View File

@ -4,17 +4,18 @@ use Flarum\Core;
use Flarum\Core\Groups\Group;
use Flarum\Core\Model;
use Flarum\Core\Notifications\Notification;
use Flarum\Events\RegisterUserPreferences;
use Illuminate\Contracts\Hashing\Hasher;
use Flarum\Core\Formatter\FormatterManager;
use Flarum\Core\Users\Events\UserWasDeleted;
use Flarum\Core\Users\Events\UserWasRegistered;
use Flarum\Core\Users\Events\UserWasRenamed;
use Flarum\Core\Users\Events\UserEmailWasChanged;
use Flarum\Core\Users\Events\UserPasswordWasChanged;
use Flarum\Core\Users\Events\UserBioWasChanged;
use Flarum\Core\Users\Events\UserAvatarWasChanged;
use Flarum\Core\Users\Events\UserWasActivated;
use Flarum\Core\Users\Events\UserEmailChangeWasRequested;
use Flarum\Events\UserWasDeleted;
use Flarum\Events\UserWasRegistered;
use Flarum\Events\UserWasRenamed;
use Flarum\Events\UserEmailWasChanged;
use Flarum\Events\UserPasswordWasChanged;
use Flarum\Events\UserBioWasChanged;
use Flarum\Events\UserAvatarWasChanged;
use Flarum\Events\UserWasActivated;
use Flarum\Events\UserEmailChangeWasRequested;
use Flarum\Core\Support\Locked;
use Flarum\Core\Support\VisibleScope;
use Flarum\Core\Support\EventGenerator;
@ -27,7 +28,6 @@ class User extends Model
{
use EventGenerator;
use Locked;
use VisibleScope;
use ValidatesBeforeSave;
/**
@ -53,7 +53,7 @@ class User extends Model
/**
* {@inheritdoc}
*/
protected static $dateAttributes = [
protected $dates = [
'join_time',
'last_seen_time',
'read_time',
@ -97,6 +97,8 @@ class User extends Model
static::deleted(function ($user) {
$user->raise(new UserWasDeleted($user));
});
event(new RegisterUserPreferences);
}
/**

View File

@ -1,6 +1,9 @@
<?php namespace Flarum\Core\Users;
use Flarum\Core\Search\GambitManager;
use Flarum\Events\ModelAllow;
use Flarum\Events\RegisterUserGambits;
use Flarum\Events\RegisterUserPreferences;
use Flarum\Support\ServiceProvider;
use Flarum\Extend;
use Illuminate\Contracts\Container\Container;
@ -14,25 +17,32 @@ class UsersServiceProvider extends ServiceProvider
*/
public function boot()
{
$this->extend([
new Extend\EventSubscriber('Flarum\Core\Users\Listeners\UserMetadataUpdater'),
new Extend\EventSubscriber('Flarum\Core\Users\Listeners\EmailConfirmationMailer')
]);
User::setHasher($this->app->make('hash'));
User::setFormatter($this->app->make('flarum.formatter'));
User::setValidator($this->app->make('validator'));
User::addPreference('discloseOnline', 'boolval', true);
User::addPreference('indexProfile', 'boolval', true);
$events = $this->app->make('events');
User::allow('*', function (User $user, User $actor, $action) {
return $actor->hasPermission('user.'.$action) ?: null;
$events->listen(RegisterUserPreferences::class, function (RegisterUserPreferences $event) {
$event->register('discloseOnline', 'boolval', true);
$event->register('indexProfile', 'boolval', true);
});
User::allow(['edit', 'delete'], function (User $user, User $actor) {
return $user->id == $actor->id ?: null;
$events->listen(ModelAllow::class, function (ModelAllow $event) {
if ($event->model instanceof User) {
if (in_array($event->action, ['edit', 'delete']) &&
$event->model->id == $event->actor->id) {
return true;
}
if ($event->actor->hasPermission('user.'.$event->action)) {
return true;
}
}
});
$events->subscribe('Flarum\Core\Users\Listeners\UserMetadataUpdater');
$events->subscribe('Flarum\Core\Users\Listeners\EmailConfirmationMailer');
}
/**
@ -63,19 +73,14 @@ class UsersServiceProvider extends ServiceProvider
public function registerGambits()
{
$this->app->instance('flarum.userGambits', []);
$this->app->when('Flarum\Core\Users\Search\UserSearcher')
->needs('Flarum\Core\Search\GambitManager')
->give(function (Container $app) {
$gambits = new GambitManager($app);
foreach ($app->make('flarum.userGambits') as $gambit) {
$gambits->add($gambit);
}
$gambits->setFulltextGambit('Flarum\Core\Users\Search\Gambits\FulltextGambit');
event(new RegisterUserGambits($gambits));
return $gambits;
});
}

View File

@ -1,8 +1,8 @@
<?php namespace Flarum\Api\Events;
<?php namespace Flarum\Events;
use Flarum\Api\Serializers\Serializer;
class SerializeAttributes
class ApiAttributes
{
/**
* The class doing the serializing.
@ -25,6 +25,11 @@ class SerializeAttributes
*/
public $attributes;
/**
* @var \Flarum\Core\Users\User
*/
public $actor;
/**
* @param Serializer $serializer The class doing the serializing.
* @param object $model The model being serialized.
@ -35,5 +40,6 @@ class SerializeAttributes
$this->serializer = $serializer;
$this->model = $model;
$this->attributes = &$attributes;
$this->actor = $serializer->actor;
}
}

View File

@ -0,0 +1,26 @@
<?php namespace Flarum\Events;
use Flarum\Api\Serializers\Serializer;
class ApiRelationship
{
/**
* @var Serializer
*/
public $serializer;
/**
* @var string
*/
public $relationship;
/**
* @param Serializer $serializer
* @param string $relationship
*/
public function __construct(Serializer $serializer, $relationship)
{
$this->serializer = $serializer;
$this->relationship = $relationship;
}
}

View File

@ -1,4 +1,4 @@
<?php namespace Flarum\Core\Users\Events;
<?php namespace Flarum\Events;
use Flarum\Core\Users\User;

View File

@ -1,4 +1,4 @@
<?php namespace Flarum\Core\Users\Events;
<?php namespace Flarum\Events;
use Flarum\Core\Users\User;

View File

@ -0,0 +1,70 @@
<?php namespace Flarum\Events;
use Flarum\Api\Actions\SerializeAction;
class BuildApiAction
{
public $action;
/**
* @param SerializeAction $action
*/
public function __construct($action)
{
$this->action = $action;
}
public function serializer($serializer)
{
$this->action->serializer = $serializer;
}
public function addInclude($relation, $default = true)
{
$this->action->include[$relation] = $default;
}
public function removeInclude($relation)
{
unset($this->action->include[$relation]);
}
public function addLink($relation)
{
$this->action->link[] = $relation;
}
public function removeLink($relation)
{
if (($k = array_search($relation, $this->action->link)) !== false) {
unset($this->action->link[$k]);
}
}
public function limitMax($limitMax)
{
$this->action->limitMax = $limitMax;
}
public function limit($limit)
{
$this->action->limit = $limit;
}
public function addSortField($field)
{
$this->action->sortFields[] = $field;
}
public function removeSortField($field)
{
if (($k = array_search($field, $this->action->sortFields)) !== false) {
unset($this->action->sortFields[$k]);
}
}
public function sort($sort)
{
$this->action->sort = $sort;
}
}

View File

@ -0,0 +1,57 @@
<?php namespace Flarum\Events;
use Flarum\Support\ClientAction;
use Flarum\Support\ClientView;
use Flarum\Forum\Actions\ClientAction as ForumClientAction;
class BuildClientView
{
/**
* @var ClientAction
*/
public $action;
/**
* @var ClientView
*/
public $view;
/**
* @var array
*/
public $keys;
/**
* @param ClientAction $action
* @param ClientView $view
* @param array $keys
*/
public function __construct($action, $view, &$keys)
{
$this->action = $action;
$this->view = $view;
$this->keys = &$keys;
}
public function forumAssets($files)
{
if ($this->action instanceof ForumClientAction) {
$this->view->getAssets()->addFiles((array) $files);
}
}
public function forumBootstrapper($bootstrapper)
{
if ($this->action instanceof ForumClientAction) {
$this->view->addBootstrapper($bootstrapper);
}
}
public function forumTranslations(array $keys)
{
if ($this->action instanceof ForumClientAction) {
foreach ($keys as $key) {
$this->keys[] = $key;
}
}
}}

View File

@ -1,4 +1,4 @@
<?php namespace Flarum\Core\Discussions\Events;
<?php namespace Flarum\Events;
use Flarum\Core\Discussions\Search\DiscussionSearch;
use Flarum\Core\Search\SearchCriteria;

View File

@ -1,4 +1,4 @@
<?php namespace Flarum\Core\Discussions\Events;
<?php namespace Flarum\Events;
use Flarum\Core\Discussions\DiscussionState;

View File

@ -1,4 +1,4 @@
<?php namespace Flarum\Core\Discussions\Events;
<?php namespace Flarum\Events;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Users\User;

View File

@ -1,4 +1,4 @@
<?php namespace Flarum\Core\Discussions\Events;
<?php namespace Flarum\Events;
use Flarum\Core\Discussions\DiscussionState;

View File

@ -1,4 +1,4 @@
<?php namespace Flarum\Core\Discussions\Events;
<?php namespace Flarum\Events;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Users\User;

View File

@ -1,4 +1,4 @@
<?php namespace Flarum\Core\Discussions\Events;
<?php namespace Flarum\Events;
use Flarum\Core\Discussions\Discussion;

View File

@ -1,4 +1,4 @@
<?php namespace Flarum\Core\Discussions\Events;
<?php namespace Flarum\Events;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Users\User;

View File

@ -1,4 +1,4 @@
<?php namespace Flarum\Core\Discussions\Events;
<?php namespace Flarum\Events;
use Flarum\Core\Discussions\Discussion;
use Flarum\Core\Users\User;

View File

@ -0,0 +1,37 @@
<?php namespace Flarum\Events;
use Flarum\Core\Model;
use Flarum\Core\Users\User;
/**
* The `ModelAllow` event
*/
class ModelAllow
{
/**
* @var Model
*/
public $model;
/**
* @var array
*/
public $actor;
/**
* @var string
*/
public $action;
/**
* @param Model $model
* @param User $actor
* @param $action
*/
public function __construct(Model $model, User $actor, $action)
{
$this->model = $model;
$this->actor = $actor;
$this->action = $action;
}
}

View File

@ -0,0 +1,30 @@
<?php namespace Flarum\Events;
use Flarum\Core\Model;
/**
* The `ModelDates` event is called to retrieve a list of fields for a model
* that should be converted into date objects.
*/
class ModelDates
{
/**
* @var Model
*/
public $model;
/**
* @var array
*/
public $dates;
/**
* @param Model $model
* @param array $dates
*/
public function __construct(Model $model, array &$dates)
{
$this->model = $model;
$this->dates = &$dates;
}
}

Some files were not shown because too many files have changed in this diff Show More