Massive refactor

- Use contextual namespaces within Flarum\Core
- Clean up and docblock everything
- Refactor Activity/Notification blueprint stuff
- Refactor Formatter stuff
- Refactor Search stuff
- Upgrade to JSON-API 1.0
- Removed “addedPosts” and “removedPosts” relationships from discussion
API. This was used for adding/removing event posts after renaming a
discussion etc. Instead we should make an additional request to get all
new posts

Todo:
- Fix Extenders and extensions
- Get rid of repository interfaces
- Fix other bugs I’ve inevitably introduced
This commit is contained in:
Toby Zerner
2015-07-04 12:24:48 +09:30
parent 12dd550a14
commit a74b40fe47
324 changed files with 6443 additions and 4197 deletions

View File

@ -46,9 +46,11 @@ export default class ActivityPage extends UserPage {
loadResults(offset) { loadResults(offset) {
return app.store.find('activity', { return app.store.find('activity', {
users: this.user().id(), filter: {
page: {offset, limit: this.loadLimit}, user: this.user().id(),
type: this.props.filter type: this.props.filter
},
page: {offset, limit: this.loadLimit}
}) })
} }

View File

@ -208,6 +208,7 @@ class Composer extends Component {
if (flexible.length) { if (flexible.length) {
flexible.height(height - flexible.height(height -
(flexible.offset().top - this.$().offset().top) - (flexible.offset().top - this.$().offset().top) -
parseInt(flexible.css('padding-bottom')) -
this.$('.text-editor-controls').outerHeight(true)); this.$('.text-editor-controls').outerHeight(true));
} }
} }

View File

@ -65,7 +65,7 @@ export default class DiscussionPage extends mixin(Component, evented) {
params() { params() {
return { return {
near: this.currentNear, page: {near: this.currentNear},
include: ['posts', 'posts.user', 'posts.user.groups'] include: ['posts', 'posts.user', 'posts.user.groups']
}; };
} }
@ -91,7 +91,7 @@ export default class DiscussionPage extends mixin(Component, evented) {
var includedPosts = []; var includedPosts = [];
if (discussion.payload && discussion.payload.included) { if (discussion.payload && discussion.payload.included) {
discussion.payload.included.forEach(record => { discussion.payload.included.forEach(record => {
if (record.type === 'posts' && record.links && record.links.discussion) { if (record.type === 'posts' && record.relationships && record.relationships.discussion) {
includedPosts.push(app.store.getById('posts', record.id)); includedPosts.push(app.store.getById('posts', record.id));
} }
}); });

View File

@ -9,7 +9,7 @@ export default class DiscussionsSearchResults {
search(string) { search(string) {
this.results[string] = []; this.results[string] = [];
return app.store.find('discussions', {q: string, page: {limit: 3}, include: 'relevantPosts,relevantPosts.discussion'}).then(results => { return app.store.find('discussions', {filter: {q: string}, page: {limit: 3}, include: 'relevantPosts,relevantPosts.discussion'}).then(results => {
this.results[string] = results; this.results[string] = results;
}); });
} }

View File

@ -79,7 +79,7 @@ export default class NotificationList extends Component {
this.loading(true); this.loading(true);
m.redraw(); m.redraw();
app.store.find('notifications').then(notifications => { app.store.find('notifications').then(notifications => {
app.session.user().pushData({unreadNotificationsCount: 0}); app.session.user().pushAttributes({unreadNotificationsCount: 0});
this.loading(false); this.loading(false);
app.cache.notifications = notifications.sort((a, b) => b.time() - a.time()); app.cache.notifications = notifications.sort((a, b) => b.time() - a.time());
m.redraw(); m.redraw();

View File

@ -76,11 +76,11 @@ class PostStream extends mixin(Component, evented) {
sync() { sync() {
var addedPosts = this.discussion.addedPosts(); var addedPosts = this.discussion.addedPosts();
if (addedPosts) addedPosts.forEach(this.pushPost.bind(this)); if (addedPosts) addedPosts.forEach(this.pushPost.bind(this));
this.discussion.pushData({links: {addedPosts: null}}); this.discussion.pushAttributes({links: {addedPosts: null}});
var removedPosts = this.discussion.removedPosts(); var removedPosts = this.discussion.removedPosts();
if (removedPosts) removedPosts.forEach(this.removePost.bind(this)); if (removedPosts) removedPosts.forEach(this.removePost.bind(this));
this.discussion.pushData({removedPosts: null}); this.discussion.pushAttributes({removedPosts: null});
} }
/** /**
@ -352,8 +352,8 @@ class PostStream extends mixin(Component, evented) {
this.clear(); this.clear();
return app.store.find('posts', { return app.store.find('posts', {
discussions: this.discussion.id(), filter: {discussion: this.discussion.id()},
near: number page: {near: number}
}).then(this.setup.bind(this)); }).then(this.setup.bind(this));
} }

View File

@ -32,7 +32,7 @@ export default class ReplyComposer extends ComposerBody {
data() { data() {
return { return {
content: this.content(), content: this.content(),
links: {discussion: this.props.discussion} relationships: {discussion: this.props.discussion}
}; };
} }
@ -47,8 +47,8 @@ export default class ReplyComposer extends ComposerBody {
app.store.createRecord('posts').save(data).then((post) => { app.store.createRecord('posts').save(data).then((post) => {
app.composer.hide(); app.composer.hide();
discussion.pushData({ discussion.pushAttributes({
links: { relationships: {
lastUser: post.user(), lastUser: post.user(),
lastPost: post lastPost: post
}, },
@ -58,7 +58,7 @@ export default class ReplyComposer extends ComposerBody {
readTime: post.time(), readTime: post.time(),
readNumber: post.number() readNumber: post.number()
}); });
discussion.data().links.posts.linkage.push({type: 'posts', id: post.id()}); discussion.data().relationships.posts.data.push({type: 'posts', id: post.id()});
// If we're currently viewing the discussion which this reply was made // If we're currently viewing the discussion which this reply was made
// in, then we can add the post to the end of the post stream. // in, then we can add the post to the end of the post stream.

View File

@ -113,7 +113,7 @@ export default class SettingsPage extends UserPage {
label: 'Allow others to see when I am online', label: 'Allow others to see when I am online',
state: this.user().preferences().discloseOnline, state: this.user().preferences().discloseOnline,
onchange: (value, component) => { onchange: (value, component) => {
this.user().pushData({lastSeenTime: null}); this.user().pushAttributes({lastSeenTime: null});
this.save('discloseOnline')(value, component); this.save('discloseOnline')(value, component);
} }
}) })

View File

@ -3,7 +3,7 @@ import avatar from 'flarum/helpers/avatar';
export default class UsersSearchResults { export default class UsersSearchResults {
search(string) { search(string) {
return app.store.find('users', {q: string, page: {limit: 5}}); return app.store.find('users', {filter: {q: string}, page: {limit: 5}});
} }
view(string) { view(string) {

View File

@ -13,17 +13,17 @@ export default function(app) {
function hideAction() { function hideAction() {
this.save({ isHidden: true }); this.save({ isHidden: true });
this.pushData({ hideTime: new Date(), hideUser: app.session.user() }); this.pushAttributes({ hideTime: new Date(), hideUser: app.session.user() });
} }
function restoreAction() { function restoreAction() {
this.save({ isHidden: false }); this.save({ isHidden: false });
this.pushData({ hideTime: null, hideUser: null }); this.pushAttributes({ hideTime: null, hideUser: null });
} }
function deleteAction() { function deleteAction() {
this.delete(); this.delete();
this.discussion().pushData({removedPosts: [this.id()]}); // this.discussion().pushAttributes({removedPosts: [this.id()]});
if (app.current instanceof DiscussionPage) { if (app.current instanceof DiscussionPage) {
app.current.stream.removePost(this.id()); app.current.stream.removePost(this.id());
} }

View File

@ -6,18 +6,27 @@ export default class Model {
this.store = store; this.store = store;
} }
id() {
return this.data().id;
}
pushData(newData) { pushData(newData) {
var data = this.data(); var data = this.data();
for (var i in newData) { for (var i in newData) {
if (i === 'links') { if (i === 'relationships') {
data[i] = data[i] || {}; data[i] = data[i] || {};
for (var j in newData[i]) { for (var j in newData[i]) {
if (newData[i][j] instanceof Model) { if (newData[i][j] instanceof Model) {
newData[i][j] = {linkage: {type: newData[i][j].data().type, id: newData[i][j].data().id}}; newData[i][j] = {data: {type: newData[i][j].data().type, id: newData[i][j].data().id}};
} }
data[i][j] = newData[i][j]; data[i][j] = newData[i][j];
} }
} else if (i === 'attributes') {
data[i] = data[i] || {};
for (var j in newData[i]) {
data[i][j] = newData[i][j];
}
} else { } else {
data[i] = newData[i]; data[i] = newData[i];
} }
@ -26,19 +35,40 @@ export default class Model {
this.freshness = new Date(); this.freshness = new Date();
} }
save(data) { pushAttributes(attributes) {
if (data.links) { var data = {attributes};
for (var i in data.links) {
var model = data.links[i]; if (attributes.relationships) {
var linkage = model => { data.relationships = attributes.relationships;
delete attributes.relationships;
}
this.pushData(data);
}
save(attributes) {
var data = {
type: this.data().type,
id: this.data().id,
attributes
};
if (attributes.relationships) {
data.relationships = {};
for (var i in attributes.relationships) {
var model = attributes.relationships[i];
var relationshipData = model => {
return {type: model.data().type, id: model.data().id}; return {type: model.data().type, id: model.data().id};
}; };
if (model instanceof Array) { if (model instanceof Array) {
data.links[i] = {linkage: model.map(linkage)}; data.relationships[i] = {data: model.map(relationshipData)};
} else { } else {
data.links[i] = {linkage: linkage(model)}; data.relationships[i] = {data: relationshipData(model)};
} }
} }
delete attributes.relationships;
} }
// clone the relevant parts of the model's old data so that we can revert // clone the relevant parts of the model's old data so that we can revert
@ -46,7 +76,7 @@ export default class Model {
var oldData = {}; var oldData = {};
var currentData = this.data(); var currentData = this.data();
for (var i in data) { for (var i in data) {
if (i === 'links') { if (i === 'relationships') {
oldData[i] = oldData[i] || {}; oldData[i] = oldData[i] || {};
for (var j in currentData[i]) { for (var j in currentData[i]) {
oldData[i][j] = currentData[i][j]; oldData[i][j] = currentData[i][j];
@ -59,7 +89,7 @@ export default class Model {
this.pushData(data); this.pushData(data);
return app.request({ return app.request({
method: this.exists ? 'PUT' : 'POST', method: this.exists ? 'PATCH' : 'POST',
url: app.config['api_url']+'/'+this.data().type+(this.exists ? '/'+this.data().id : ''), url: app.config['api_url']+'/'+this.data().type+(this.exists ? '/'+this.data().id : ''),
data: {data}, data: {data},
background: true, background: true,
@ -84,36 +114,36 @@ export default class Model {
}).then(() => this.exists = false); }).then(() => this.exists = false);
} }
static prop(name, transform) { static attribute(name, transform) {
return function() { return function() {
var data = this.data()[name]; var data = this.data().attributes[name];
return transform ? transform(data) : data; return transform ? transform(data) : data;
} }
} }
static one(name) { static hasOne(name) {
return function() { return function() {
var data = this.data(); var data = this.data();
if (data.links) { if (data.relationships) {
var link = data.links[name]; var relationship = data.relationships[name];
return link && app.store.getById(link.linkage.type, link.linkage.id); return relationship && app.store.getById(relationship.data.type, relationship.data.id);
} }
} }
} }
static many(name) { static hasMany(name) {
return function() { return function() {
var data = this.data(); var data = this.data();
if (data.links) { if (data.relationships) {
var link = this.data().links[name]; var relationship = this.data().relationships[name];
return link && link.linkage.map(function(link) { return relationship && relationship.data.map(function(link) {
return app.store.getById(link.type, link.id) return app.store.getById(link.type, link.id);
}); });
} }
} }
} }
static date(data) { static transformDate(data) {
return data ? new Date(data) : null; return data ? new Date(data) : null;
} }
} }

View File

@ -2,12 +2,11 @@ import Model from 'flarum/model';
class Activity extends Model {} class Activity extends Model {}
Activity.prototype.id = Model.prop('id'); Activity.prototype.contentType = Model.attribute('contentType');
Activity.prototype.contentType = Model.prop('contentType'); Activity.prototype.content = Model.attribute('content');
Activity.prototype.content = Model.prop('content'); Activity.prototype.time = Model.attribute('time', Model.transformDate);
Activity.prototype.time = Model.prop('time', Model.date);
Activity.prototype.user = Model.one('user'); Activity.prototype.user = Model.hasOne('user');
Activity.prototype.subject = Model.one('subject'); Activity.prototype.subject = Model.hasOne('subject');
export default Activity; export default Activity;

View File

@ -6,25 +6,25 @@ class Discussion extends Model {
pushData(newData) { pushData(newData) {
super.pushData(newData); super.pushData(newData);
var links = this.data().links; // var links = this.data().links;
var posts = links && links.posts; // var posts = links && links.posts;
if (posts) { // if (posts) {
if (newData.removedPosts) { // if (newData.removedPosts) {
posts.linkage.forEach((linkage, i) => { // posts.linkage.forEach((linkage, i) => {
if (newData.removedPosts.indexOf(linkage.id) !== -1) { // if (newData.removedPosts.indexOf(linkage.id) !== -1) {
posts.linkage.splice(i, 1); // posts.linkage.splice(i, 1);
} // }
}); // });
} // }
if (newData.links && newData.links.addedPosts) { // if (newData.links && newData.links.addedPosts) {
newData.links.addedPosts.linkage.forEach(linkage => { // newData.links.addedPosts.linkage.forEach(linkage => {
if (posts.linkage[posts.linkage.length - 1].id != linkage.id) { // if (posts.linkage[posts.linkage.length - 1].id != linkage.id) {
posts.linkage.push(linkage); // posts.linkage.push(linkage);
} // }
}); // });
} // }
} // }
} }
unreadCount() { unreadCount() {
@ -40,34 +40,33 @@ class Discussion extends Model {
} }
} }
Discussion.prototype.id = Model.prop('id'); Discussion.prototype.title = Model.attribute('title');
Discussion.prototype.title = Model.prop('title');
Discussion.prototype.slug = computed('title', title => title.toLowerCase().replace(/[^a-z0-9]/gi, '-').replace(/-+/g, '-').replace(/-$|^-/g, '') || '-'); Discussion.prototype.slug = computed('title', title => title.toLowerCase().replace(/[^a-z0-9]/gi, '-').replace(/-+/g, '-').replace(/-$|^-/g, '') || '-');
Discussion.prototype.startTime = Model.prop('startTime', Model.date); Discussion.prototype.startTime = Model.attribute('startTime', Model.transformDate);
Discussion.prototype.startUser = Model.one('startUser'); Discussion.prototype.startUser = Model.hasOne('startUser');
Discussion.prototype.startPost = Model.one('startPost'); Discussion.prototype.startPost = Model.hasOne('startPost');
Discussion.prototype.lastTime = Model.prop('lastTime', Model.date); Discussion.prototype.lastTime = Model.attribute('lastTime', Model.transformDate);
Discussion.prototype.lastUser = Model.one('lastUser'); Discussion.prototype.lastUser = Model.hasOne('lastUser');
Discussion.prototype.lastPost = Model.one('lastPost'); Discussion.prototype.lastPost = Model.hasOne('lastPost');
Discussion.prototype.lastPostNumber = Model.prop('lastPostNumber'); Discussion.prototype.lastPostNumber = Model.attribute('lastPostNumber');
Discussion.prototype.canReply = Model.prop('canReply'); Discussion.prototype.canReply = Model.attribute('canReply');
Discussion.prototype.canRename = Model.prop('canRename'); Discussion.prototype.canRename = Model.attribute('canRename');
Discussion.prototype.canDelete = Model.prop('canDelete'); Discussion.prototype.canDelete = Model.attribute('canDelete');
Discussion.prototype.commentsCount = Model.prop('commentsCount'); Discussion.prototype.commentsCount = Model.attribute('commentsCount');
Discussion.prototype.repliesCount = computed('commentsCount', commentsCount => Math.max(0, commentsCount - 1)); Discussion.prototype.repliesCount = computed('commentsCount', commentsCount => Math.max(0, commentsCount - 1));
Discussion.prototype.posts = Model.many('posts'); Discussion.prototype.posts = Model.hasMany('posts');
Discussion.prototype.postIds = function() { return this.data().links.posts.linkage.map((link) => link.id); }; Discussion.prototype.postIds = function() { return this.data().relationships.posts.data.map((link) => link.id); };
Discussion.prototype.relevantPosts = Model.many('relevantPosts'); Discussion.prototype.relevantPosts = Model.hasMany('relevantPosts');
Discussion.prototype.addedPosts = Model.many('addedPosts'); Discussion.prototype.addedPosts = Model.hasMany('addedPosts');
Discussion.prototype.removedPosts = Model.prop('removedPosts'); Discussion.prototype.removedPosts = Model.attribute('removedPosts');
Discussion.prototype.readTime = Model.prop('readTime', Model.date); Discussion.prototype.readTime = Model.attribute('readTime', Model.transformDate);
Discussion.prototype.readNumber = Model.prop('readNumber'); Discussion.prototype.readNumber = Model.attribute('readNumber');
Discussion.prototype.isUnread = computed('unreadCount', unreadCount => !!unreadCount); Discussion.prototype.isUnread = computed('unreadCount', unreadCount => !!unreadCount);

View File

@ -2,10 +2,9 @@ import Model from 'flarum/model';
class Group extends Model {} class Group extends Model {}
Group.prototype.id = Model.prop('id'); Group.prototype.nameSingular = Model.attribute('nameSingular');
Group.prototype.nameSingular = Model.prop('nameSingular'); Group.prototype.namePlural = Model.attribute('namePlural');
Group.prototype.namePlural = Model.prop('namePlural'); Group.prototype.color = Model.attribute('color');
Group.prototype.color = Model.prop('color'); Group.prototype.icon = Model.attribute('icon');
Group.prototype.icon = Model.prop('icon');
export default Group; export default Group;

View File

@ -3,17 +3,16 @@ import computed from 'flarum/utils/computed';
class Notification extends Model {} class Notification extends Model {}
Notification.prototype.id = Model.prop('id'); Notification.prototype.contentType = Model.attribute('contentType');
Notification.prototype.contentType = Model.prop('contentType'); Notification.prototype.subjectId = Model.attribute('subjectId');
Notification.prototype.subjectId = Model.prop('subjectId'); Notification.prototype.content = Model.attribute('content');
Notification.prototype.content = Model.prop('content'); Notification.prototype.time = Model.attribute('time', Model.date);
Notification.prototype.time = Model.prop('time', Model.date); Notification.prototype.isRead = Model.attribute('isRead');
Notification.prototype.isRead = Model.prop('isRead'); Notification.prototype.unreadCount = Model.attribute('unreadCount');
Notification.prototype.unreadCount = Model.prop('unreadCount');
Notification.prototype.additionalUnreadCount = computed('unreadCount', unreadCount => Math.max(0, unreadCount - 1)); Notification.prototype.additionalUnreadCount = computed('unreadCount', unreadCount => Math.max(0, unreadCount - 1));
Notification.prototype.user = Model.one('user'); Notification.prototype.user = Model.hasOne('user');
Notification.prototype.sender = Model.one('sender'); Notification.prototype.sender = Model.hasOne('sender');
Notification.prototype.subject = Model.one('subject'); Notification.prototype.subject = Model.hasOne('subject');
export default Notification; export default Notification;

View File

@ -3,26 +3,25 @@ import computed from 'flarum/utils/computed';
class Post extends Model {} class Post extends Model {}
Post.prototype.id = Model.prop('id'); Post.prototype.number = Model.attribute('number');
Post.prototype.number = Model.prop('number'); Post.prototype.discussion = Model.hasOne('discussion');
Post.prototype.discussion = Model.one('discussion');
Post.prototype.time = Model.prop('time', Model.date); Post.prototype.time = Model.attribute('time', Model.transformDate);
Post.prototype.user = Model.one('user'); Post.prototype.user = Model.hasOne('user');
Post.prototype.contentType = Model.prop('contentType'); Post.prototype.contentType = Model.attribute('contentType');
Post.prototype.content = Model.prop('content'); Post.prototype.content = Model.attribute('content');
Post.prototype.contentHtml = Model.prop('contentHtml'); Post.prototype.contentHtml = Model.attribute('contentHtml');
Post.prototype.contentPlain = computed('contentHtml', contentHtml => $('<div/>').html(contentHtml.replace(/(<\/p>|<br>)/g, '$1 ')).text()); Post.prototype.contentPlain = computed('contentHtml', contentHtml => $('<div/>').html(contentHtml.replace(/(<\/p>|<br>)/g, '$1 ')).text());
Post.prototype.editTime = Model.prop('editTime', Model.date); Post.prototype.editTime = Model.attribute('editTime', Model.transformDate);
Post.prototype.editUser = Model.one('editUser'); Post.prototype.editUser = Model.hasOne('editUser');
Post.prototype.isEdited = computed('editTime', editTime => !!editTime); Post.prototype.isEdited = computed('editTime', editTime => !!editTime);
Post.prototype.hideTime = Model.prop('hideTime', Model.date); Post.prototype.hideTime = Model.attribute('hideTime', Model.transformDate);
Post.prototype.hideUser = Model.one('hideUser'); Post.prototype.hideUser = Model.hasOne('hideUser');
Post.prototype.isHidden = computed('hideTime', hideTime => !!hideTime); Post.prototype.isHidden = computed('hideTime', hideTime => !!hideTime);
Post.prototype.canEdit = Model.prop('canEdit'); Post.prototype.canEdit = Model.attribute('canEdit');
Post.prototype.canDelete = Model.prop('canDelete'); Post.prototype.canDelete = Model.attribute('canDelete');
export default Post; export default Post;

View File

@ -6,29 +6,28 @@ import Badge from 'flarum/components/badge';
class User extends Model {} class User extends Model {}
User.prototype.id = Model.prop('id'); User.prototype.username = Model.attribute('username');
User.prototype.username = Model.prop('username'); User.prototype.email = Model.attribute('email');
User.prototype.email = Model.prop('email'); User.prototype.isConfirmed = Model.attribute('isConfirmed');
User.prototype.isConfirmed = Model.prop('isConfirmed'); User.prototype.password = Model.attribute('password');
User.prototype.password = Model.prop('password'); User.prototype.avatarUrl = Model.attribute('avatarUrl');
User.prototype.avatarUrl = Model.prop('avatarUrl'); User.prototype.bio = Model.attribute('bio');
User.prototype.bio = Model.prop('bio'); User.prototype.bioHtml = Model.attribute('bioHtml');
User.prototype.bioHtml = Model.prop('bioHtml'); User.prototype.preferences = Model.attribute('preferences');
User.prototype.preferences = Model.prop('preferences');
User.prototype.groups = Model.many('groups'); User.prototype.groups = Model.hasMany('groups');
User.prototype.joinTime = Model.prop('joinTime', Model.date); User.prototype.joinTime = Model.attribute('joinTime', Model.transformDate);
User.prototype.lastSeenTime = Model.prop('lastSeenTime', Model.date); User.prototype.lastSeenTime = Model.attribute('lastSeenTime', Model.transformDate);
User.prototype.online = function() { return this.lastSeenTime() > moment().subtract(5, 'minutes').toDate(); }; User.prototype.online = function() { return this.lastSeenTime() > moment().subtract(5, 'minutes').toDate(); };
User.prototype.readTime = Model.prop('readTime', Model.date); User.prototype.readTime = Model.attribute('readTime', Model.transformDate);
User.prototype.unreadNotificationsCount = Model.prop('unreadNotificationsCount'); User.prototype.unreadNotificationsCount = Model.attribute('unreadNotificationsCount');
User.prototype.discussionsCount = Model.prop('discussionsCount'); User.prototype.discussionsCount = Model.attribute('discussionsCount');
User.prototype.commentsCount = Model.prop('commentsCount'); User.prototype.commentsCount = Model.attribute('commentsCount');
; ;
User.prototype.canEdit = Model.prop('canEdit'); User.prototype.canEdit = Model.attribute('canEdit');
User.prototype.canDelete = Model.prop('canDelete'); User.prototype.canDelete = Model.attribute('canDelete');
User.prototype.color = computed('username', 'avatarUrl', 'avatarColor', function(username, avatarUrl, avatarColor) { User.prototype.color = computed('username', 'avatarUrl', 'avatarColor', function(username, avatarUrl, avatarColor) {
if (avatarColor) { if (avatarColor) {

View File

@ -1,5 +1,6 @@
export default function truncate(string, length, start) { export default function truncate(string, length, start) {
start = start || 0; start = start || 0;
string = string || '';
return (start > 0 ? '...' : '')+string.substring(start, start + length)+(string.length > start + length ? '...' : ''); return (start > 0 ? '...' : '')+string.substring(start, start + length)+(string.length > start + length ? '...' : '');
} }

View File

@ -1,25 +1,23 @@
<?php namespace Flarum\Core\Models; <?php namespace Flarum\Api;
use Flarum\Core\Model;
class AccessToken extends Model class AccessToken extends Model
{ {
/** /**
* The table associated with the model. * {@inheritdoc}
*
* @var string
*/ */
protected $table = 'access_tokens'; protected $table = 'access_tokens';
/** /**
* Use a custom primary key for this model. * Use a custom primary key for this model.
* *
* @var boolean * @var bool
*/ */
public $incrementing = false; public $incrementing = false;
/** /**
* The attributes that should be mutated to dates. * {@inheritdoc}
*
* @var array
*/ */
protected $dates = ['created_at', 'expires_at']; protected $dates = ['created_at', 'expires_at'];
@ -49,6 +47,6 @@ class AccessToken extends Model
*/ */
public function user() public function user()
{ {
return $this->belongsTo('Flarum\Core\Models\User'); return $this->belongsTo('Flarum\Core\Users\User');
} }
} }

View File

@ -7,7 +7,7 @@ interface ActionInterface
/** /**
* Handle a request to the API, returning an HTTP response. * Handle a request to the API, returning an HTTP response.
* *
* @param \Flarum\Api\Request $request * @param Request $request
* @return \Psr\Http\Message\ResponseInterface * @return \Psr\Http\Message\ResponseInterface
*/ */
public function handle(Request $request); public function handle(Request $request);

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Api\Actions\Activity; <?php namespace Flarum\Api\Actions\Activity;
use Flarum\Core\Repositories\UserRepositoryInterface; use Flarum\Core\Users\UserRepositoryInterface;
use Flarum\Core\Repositories\ActivityRepositoryInterface; use Flarum\Core\Activity\ActivityRepositoryInterface;
use Flarum\Api\Actions\SerializeCollectionAction; use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Document; use Tobscure\JsonApi\Document;
@ -9,12 +9,12 @@ use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction class IndexAction extends SerializeCollectionAction
{ {
/** /**
* @var \Flarum\Core\Repositories\UserRepositoryInterface * @var UserRepositoryInterface
*/ */
protected $users; protected $users;
/** /**
* @var \Flarum\Core\Repositories\ActivityRepositoryInterface * @var ActivityRepositoryInterface
*/ */
protected $activity; protected $activity;
@ -58,10 +58,8 @@ class IndexAction extends SerializeCollectionAction
public static $sort; public static $sort;
/** /**
* Instantiate the action. * @param UserRepositoryInterface $users
* * @param ActivityRepositoryInterface $activity
* @param \Flarum\Core\Repositories\UserRepositoryInterface $users
* @param \Flarum\Core\Repositories\ActivityRepositoryInterface $activity
*/ */
public function __construct(UserRepositoryInterface $users, ActivityRepositoryInterface $activity) public function __construct(UserRepositoryInterface $users, ActivityRepositoryInterface $activity)
{ {
@ -73,17 +71,24 @@ class IndexAction extends SerializeCollectionAction
* Get the activity results, ready to be serialized and assigned to the * Get the activity results, ready to be serialized and assigned to the
* document response. * document response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Illuminate\Database\Eloquent\Collection * @return \Illuminate\Database\Eloquent\Collection
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
$actor = $request->actor->getUser(); $userId = $request->get('filter.user');
$actor = $request->actor;
$user = $this->users->findOrFail($request->get('users'), $actor); $user = $this->users->findOrFail($userId, $actor);
return $this->activity->findByUser($user->id, $actor, $request->limit, $request->offset, $request->get('type')) return $this->activity->findByUser(
$user->id,
$actor,
$request->limit,
$request->offset,
$request->get('filter.type')
)
->load($request->include); ->load($request->include);
} }
} }

View File

@ -1,8 +1,7 @@
<?php namespace Flarum\Api\Actions\Discussions; <?php namespace Flarum\Api\Actions\Discussions;
use Flarum\Core\Commands\StartDiscussionCommand; use Flarum\Core\Discussions\Commands\StartDiscussion;
use Flarum\Core\Commands\ReadDiscussionCommand; use Flarum\Core\Discussions\Commands\ReadDiscussion;
use Flarum\Core\Models\Forum;
use Flarum\Api\Actions\CreateAction as BaseCreateAction; use Flarum\Api\Actions\CreateAction as BaseCreateAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
@ -12,17 +11,10 @@ class CreateAction extends BaseCreateAction
/** /**
* The command bus. * The command bus.
* *
* @var \Illuminate\Contracts\Bus\Dispatcher * @var Dispatcher
*/ */
protected $bus; protected $bus;
/**
* The default forum instance.
*
* @var \Flarum\Core\Models\Forum
*/
protected $forum;
/** /**
* @inheritdoc * @inheritdoc
*/ */
@ -67,35 +59,33 @@ class CreateAction extends BaseCreateAction
/** /**
* Instantiate the action. * Instantiate the action.
* *
* @param \Illuminate\Contracts\Bus\Dispatcher $bus * @param Dispatcher $bus
* @param \Flarum\Core\Models\Forum $forum
*/ */
public function __construct(Dispatcher $bus, Forum $forum) public function __construct(Dispatcher $bus)
{ {
$this->bus = $bus; $this->bus = $bus;
$this->forum = $forum;
} }
/** /**
* Create a discussion according to input from the API request. * Create a discussion according to input from the API request.
* *
* @param JsonApiRequest $request * @param JsonApiRequest $request
* @return \Flarum\Core\Models\Model * @return \Flarum\Core\Discussions\Discussion
*/ */
protected function create(JsonApiRequest $request) protected function create(JsonApiRequest $request)
{ {
$user = $request->actor->getUser(); $actor = $request->actor;
$discussion = $this->bus->dispatch( $discussion = $this->bus->dispatch(
new StartDiscussionCommand($user, $this->forum, $request->get('data')) new StartDiscussion($actor, $request->get('data'))
); );
// After creating the discussion, we assume that the user has seen all // After creating the discussion, we assume that the user has seen all
// of the posts in the discussion; thus, we will mark the discussion // of the posts in the discussion; thus, we will mark the discussion
// as read if they are logged in. // as read if they are logged in.
if ($user->exists) { if ($actor->exists) {
$this->bus->dispatch( $this->bus->dispatch(
new ReadDiscussionCommand($discussion->id, $user, 1) new ReadDiscussion($discussion->id, $actor, 1)
); );
} }

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Actions\Discussions; <?php namespace Flarum\Api\Actions\Discussions;
use Flarum\Core\Commands\DeleteDiscussionCommand; use Flarum\Core\Discussions\Commands\DeleteDiscussion;
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction; use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
use Flarum\Api\Request; use Flarum\Api\Request;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
@ -8,16 +8,12 @@ use Illuminate\Contracts\Bus\Dispatcher;
class DeleteAction extends BaseDeleteAction class DeleteAction extends BaseDeleteAction
{ {
/** /**
* The command bus. * @var Dispatcher
*
* @var \Illuminate\Contracts\Bus\Dispatcher
*/ */
protected $bus; protected $bus;
/** /**
* Instantiate the action. * @param Dispatcher $bus
*
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
*/ */
public function __construct(Dispatcher $bus) public function __construct(Dispatcher $bus)
{ {
@ -25,15 +21,16 @@ class DeleteAction extends BaseDeleteAction
} }
/** /**
* Delete a discussion. * {@inheritdoc}
*
* @param \Flarum\Api\Request $request
* @return void
*/ */
protected function delete(Request $request) protected function delete(Request $request)
{ {
$id = $request->get('id');
$actor = $request->actor;
$input = $request->all();
$this->bus->dispatch( $this->bus->dispatch(
new DeleteDiscussionCommand($request->get('id'), $request->actor->getUser()) new DeleteDiscussion($id, $actor, $input)
); );
} }
} }

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Api\Actions\Discussions; <?php namespace Flarum\Api\Actions\Discussions;
use Flarum\Core\Search\Discussions\DiscussionSearchCriteria; use Flarum\Core\Search\SearchCriteria;
use Flarum\Core\Search\Discussions\DiscussionSearcher; use Flarum\Core\Discussions\Search\DiscussionSearcher;
use Flarum\Api\Actions\SerializeCollectionAction; use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Flarum\Http\UrlGeneratorInterface; use Flarum\Http\UrlGeneratorInterface;
@ -10,16 +10,12 @@ use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction class IndexAction extends SerializeCollectionAction
{ {
/** /**
* The discussion searcher. * @var DiscussionSearcher
*
* @var \Flarum\Core\Search\Discussions\DiscussionSearcher
*/ */
protected $searcher; protected $searcher;
/** /**
* The URL generator. * @var UrlGeneratorInterface
*
* @var \Flarum\Http\UrlGeneratorInterface
*/ */
protected $url; protected $url;
@ -67,10 +63,8 @@ class IndexAction extends SerializeCollectionAction
public static $sort; public static $sort;
/** /**
* Instantiate the action. * @param DiscussionSearcher $searcher
* * @param UrlGeneratorInterface $url
* @param \Flarum\Core\Search\Discussions\DiscussionSearcher $searcher
* @param \Flarum\Http\UrlGeneratorInterface $url
*/ */
public function __construct(DiscussionSearcher $searcher, UrlGeneratorInterface $url) public function __construct(DiscussionSearcher $searcher, UrlGeneratorInterface $url)
{ {
@ -82,21 +76,23 @@ class IndexAction extends SerializeCollectionAction
* Get the discussion results, ready to be serialized and assigned to the * Get the discussion results, ready to be serialized and assigned to the
* document response. * document response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Illuminate\Database\Eloquent\Collection * @return \Illuminate\Database\Eloquent\Collection
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
$criteria = new DiscussionSearchCriteria( $criteria = new SearchCriteria(
$request->actor->getUser(), $request->actor,
$request->get('q'), $request->get('filter.q'),
$request->sort $request->sort
); );
$load = array_merge($request->include, ['state']); $load = array_merge($request->include, ['state']);
$results = $this->searcher->search($criteria, $request->limit, $request->offset, $load); $results = $this->searcher->search($criteria, $request->limit, $request->offset, $load);
// TODO: add query params (filter, sort, include) to the pagination URLs
static::addPaginationLinks( static::addPaginationLinks(
$document, $document,
$request, $request,
@ -104,6 +100,6 @@ class IndexAction extends SerializeCollectionAction
$results->areMoreResults() $results->areMoreResults()
); );
return $results->getDiscussions(); return $results->getResults();
} }
} }

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Api\Actions\Discussions; <?php namespace Flarum\Api\Actions\Discussions;
use Flarum\Core\Repositories\DiscussionRepositoryInterface; use Flarum\Core\Discussions\DiscussionRepositoryInterface;
use Flarum\Core\Repositories\PostRepositoryInterface; use Flarum\Core\Posts\PostRepositoryInterface;
use Flarum\Api\Actions\SerializeResourceAction; use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\Actions\Posts\GetsPosts; use Flarum\Api\Actions\Posts\GetsPosts;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
@ -12,15 +12,10 @@ class ShowAction extends SerializeResourceAction
use GetsPosts; use GetsPosts;
/** /**
* @var \Flarum\Core\Repositories\DiscussionRepositoryInterface * @var \Flarum\Core\Discussions\DiscussionRepositoryInterface
*/ */
protected $discussions; protected $discussions;
/**
* @var \Flarum\Core\Repositories\PostRepositoryInterface
*/
protected $posts;
/** /**
* @inheritdoc * @inheritdoc
*/ */
@ -69,8 +64,8 @@ class ShowAction extends SerializeResourceAction
/** /**
* Instantiate the action. * Instantiate the action.
* *
* @param \Flarum\Core\Repositories\DiscussionRepositoryInterface $discussions * @param DiscussionRepositoryInterface $discussions
* @param \Flarum\Core\Repositories\PostRepositoryInterface $posts * @param PostRepositoryInterface $posts
*/ */
public function __construct(DiscussionRepositoryInterface $discussions, PostRepositoryInterface $posts) public function __construct(DiscussionRepositoryInterface $discussions, PostRepositoryInterface $posts)
{ {
@ -82,25 +77,29 @@ class ShowAction extends SerializeResourceAction
* Get a single discussion, ready to be serialized and assigned to the * Get a single discussion, ready to be serialized and assigned to the
* JsonApi response. * JsonApi response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Illuminate\Database\Eloquent\Collection * @return \Flarum\Core\Discussions\Discussion
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
$user = $request->actor->getUser(); $discussionId = $request->get('id');
$actor = $request->actor;
$discussion = $this->discussions->findOrFail($request->get('id'), $user); $discussion = $this->discussions->findOrFail($discussionId, $actor);
$discussion->posts_ids = $discussion->visiblePosts($user)->orderBy('time')->lists('id'); $discussion->posts_ids = $discussion->postsVisibleTo($actor)->orderBy('time')->lists('id');
// TODO: Refactor to be simpler, and get posts straight from the
// discussion's postsVisibleTo relation method.
if (in_array('posts', $request->include)) { if (in_array('posts', $request->include)) {
$length = strlen($prefix = 'posts.'); $prefixLength = strlen($prefix = 'posts.');
$relations = array_filter(array_map(function ($relationship) use ($prefix, $length) {
return substr($relationship, 0, $length) === $prefix ? substr($relationship, $length) : false; $postRelations = array_filter(array_map(function ($relation) use ($prefix, $prefixLength) {
return substr($relation, 0, $prefixLength) === $prefix ? substr($relation, $prefixLength) : false;
}, $request->include)); }, $request->include));
$discussion->posts = $this->getPosts($request, ['discussion_id' => $discussion->id])->load($relations); $discussion->posts = $this->getPosts($request, ['discussion_id' => $discussion->id])->load($postRelations);
} }
return $discussion; return $discussion;

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Api\Actions\Discussions; <?php namespace Flarum\Api\Actions\Discussions;
use Flarum\Core\Commands\EditDiscussionCommand; use Flarum\Core\Discussions\Commands\EditDiscussion;
use Flarum\Core\Commands\ReadDiscussionCommand; use Flarum\Core\Discussions\Commands\ReadDiscussion;
use Flarum\Api\Actions\SerializeResourceAction; use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
@ -10,7 +10,7 @@ use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction class UpdateAction extends SerializeResourceAction
{ {
/** /**
* @var \Illuminate\Contracts\Bus\Dispatcher * @var Dispatcher
*/ */
protected $bus; protected $bus;
@ -22,11 +22,7 @@ class UpdateAction extends SerializeResourceAction
/** /**
* @inheritdoc * @inheritdoc
*/ */
public static $include = [ public static $include = [];
'addedPosts' => true,
'addedPosts.user' => true,
'addedPosts.discussion' => true
];
/** /**
* @inheritdoc * @inheritdoc
@ -54,9 +50,7 @@ class UpdateAction extends SerializeResourceAction
public static $sort; public static $sort;
/** /**
* Instantiate the action. * @param Dispatcher $bus
*
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
*/ */
public function __construct(Dispatcher $bus) public function __construct(Dispatcher $bus)
{ {
@ -67,24 +61,25 @@ class UpdateAction extends SerializeResourceAction
* Update a discussion according to input from the API request, and return * Update a discussion according to input from the API request, and return
* it ready to be serialized and assigned to the JsonApi response. * it ready to be serialized and assigned to the JsonApi response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Illuminate\Database\Eloquent\Collection * @return \Flarum\Core\Discussions\Discussion
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
$user = $request->actor->getUser(); $actor = $request->actor;
$discussionId = $request->get('id'); $discussionId = $request->get('id');
$data = $request->get('data');
if ($data = array_except($request->get('data'), ['readNumber'])) {
$discussion = $this->bus->dispatch( $discussion = $this->bus->dispatch(
new EditDiscussionCommand($discussionId, $user, $data) new EditDiscussion($discussionId, $actor, $data)
); );
}
if ($readNumber = $request->get('data.readNumber')) { // TODO: Refactor the ReadDiscussion (state) command into EditDiscussion?
// That's what extensions will do anyway.
if ($readNumber = array_get($data, 'attributes.readNumber')) {
$state = $this->bus->dispatch( $state = $this->bus->dispatch(
new ReadDiscussionCommand($discussionId, $user, $readNumber) new ReadDiscussion($discussionId, $actor, $readNumber)
); );
$discussion = $state->discussion; $discussion = $state->discussion;

View File

@ -2,7 +2,7 @@
use Flarum\Api\Request; use Flarum\Api\Request;
use Flarum\Core\Repositories\UserRepositoryInterface; use Flarum\Core\Repositories\UserRepositoryInterface;
use Flarum\Core\Commands\RequestPasswordResetCommand; use Flarum\Core\Commands\RequestPasswordReset;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
use Zend\Diactoros\Response\EmptyResponse; use Zend\Diactoros\Response\EmptyResponse;
@ -29,7 +29,7 @@ class ForgotAction extends JsonApiAction
$email = $request->get('email'); $email = $request->get('email');
$this->bus->dispatch( $this->bus->dispatch(
new RequestPasswordResetCommand($email) new RequestPasswordReset($email)
); );
return new EmptyResponse(); return new EmptyResponse();

View File

@ -45,9 +45,9 @@ class ShowAction extends SerializeResourceAction
* Get the forum, ready to be serialized and assigned to the JsonApi * Get the forum, ready to be serialized and assigned to the JsonApi
* response. * response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return array * @return \Flarum\Core\Forum
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Actions\Groups; <?php namespace Flarum\Api\Actions\Groups;
use Flarum\Core\Models\Group; use Flarum\Core\Users\Group;
use Flarum\Api\Actions\SerializeCollectionAction; use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Document; use Tobscure\JsonApi\Document;
@ -46,12 +46,12 @@ class IndexAction extends SerializeCollectionAction
* Get the groups, ready to be serialized and assigned to the document * Get the groups, ready to be serialized and assigned to the document
* response. * response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Illuminate\Database\Eloquent\Collection * @return \Illuminate\Database\Eloquent\Collection
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
return Group::get(); return Group::all();
} }
} }

View File

@ -12,11 +12,12 @@ abstract class JsonApiAction implements ActionInterface
* Handle an API request and return an API response, handling any relevant * Handle an API request and return an API response, handling any relevant
* (API-related) exceptions that are thrown. * (API-related) exceptions that are thrown.
* *
* @param \Flarum\Api\Request $request * @param Request $request
* @return \Psr\Http\Message\ResponseInterface * @return \Psr\Http\Message\ResponseInterface
*/ */
public function handle(Request $request) public function handle(Request $request)
{ {
// TODO: Move this error handling code to middleware?
try { try {
return $this->respond($request); return $this->respond($request);
} catch (ValidationFailureException $e) { } catch (ValidationFailureException $e) {
@ -38,7 +39,7 @@ abstract class JsonApiAction implements ActionInterface
/** /**
* Handle an API request and return an API response. * Handle an API request and return an API response.
* *
* @param \Flarum\Api\Request $request * @param Request $request
* @return \Psr\Http\Message\ResponseInterface * @return \Psr\Http\Message\ResponseInterface
*/ */
abstract protected function respond(Request $request); abstract protected function respond(Request $request);

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Actions\Notifications; <?php namespace Flarum\Api\Actions\Notifications;
use Flarum\Core\Repositories\NotificationRepositoryInterface; use Flarum\Core\Notifications\NotificationRepositoryInterface;
use Flarum\Core\Exceptions\PermissionDeniedException; use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Api\Actions\SerializeCollectionAction; use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
@ -9,7 +9,7 @@ use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction class IndexAction extends SerializeCollectionAction
{ {
/** /**
* @var \Flarum\Core\Repositories\NotificationRepositoryInterface * @var NotificationRepositoryInterface
*/ */
protected $notifications; protected $notifications;
@ -55,7 +55,7 @@ class IndexAction extends SerializeCollectionAction
/** /**
* Instantiate the action. * Instantiate the action.
* *
* @param \Flarum\Core\Repositories\NotificationRepositoryInterface $notifications * @param NotificationRepositoryInterface $notifications
*/ */
public function __construct(NotificationRepositoryInterface $notifications) public function __construct(NotificationRepositoryInterface $notifications)
{ {
@ -66,22 +66,22 @@ class IndexAction extends SerializeCollectionAction
* Get the notification results, ready to be serialized and assigned to the * Get the notification results, ready to be serialized and assigned to the
* document response. * document response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Illuminate\Database\Eloquent\Collection * @return \Illuminate\Database\Eloquent\Collection
* @throws PermissionDeniedException * @throws PermissionDeniedException
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
if (! $request->actor->isAuthenticated()) { $actor = $request->actor;
if ($actor->isGuest()) {
throw new PermissionDeniedException; throw new PermissionDeniedException;
} }
$user = $request->actor->getUser(); $actor->markNotificationsAsRead()->save();
$user->markNotificationsAsRead()->save(); return $this->notifications->findByUser($actor, $request->limit, $request->offset)
return $this->notifications->findByUser($user, $request->limit, $request->offset)
->load($request->include); ->load($request->include);
} }
} }

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Actions\Notifications; <?php namespace Flarum\Api\Actions\Notifications;
use Flarum\Core\Commands\ReadNotificationCommand; use Flarum\Core\Notifications\Commands\ReadNotification;
use Flarum\Api\Actions\SerializeResourceAction; use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
@ -9,7 +9,7 @@ use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction class UpdateAction extends SerializeResourceAction
{ {
/** /**
* @var \Illuminate\Contracts\Bus\Dispatcher * @var Dispatcher
*/ */
protected $bus; protected $bus;
@ -49,9 +49,7 @@ class UpdateAction extends SerializeResourceAction
public static $sort; public static $sort;
/** /**
* Instantiate the action. * @param Dispatcher $bus
*
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
*/ */
public function __construct(Dispatcher $bus) public function __construct(Dispatcher $bus)
{ {
@ -62,14 +60,14 @@ class UpdateAction extends SerializeResourceAction
* Mark a notification as read, and return it ready to be serialized and * Mark a notification as read, and return it ready to be serialized and
* assigned to the JsonApi response. * assigned to the JsonApi response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Illuminate\Database\Eloquent\Collection * @return \Flarum\Core\Notifications\Notification
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
return $this->bus->dispatch( return $this->bus->dispatch(
new ReadNotificationCommand($request->get('id'), $request->actor->getUser()) new ReadNotification($request->get('id'), $request->actor)
); );
} }
} }

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Api\Actions\Posts; <?php namespace Flarum\Api\Actions\Posts;
use Flarum\Core\Commands\PostReplyCommand; use Flarum\Core\Posts\Commands\PostReply;
use Flarum\Core\Commands\ReadDiscussionCommand; use Flarum\Core\Discussions\Commands\ReadDiscussion;
use Flarum\Api\Actions\CreateAction as BaseCreateAction; use Flarum\Api\Actions\CreateAction as BaseCreateAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
@ -9,7 +9,7 @@ use Illuminate\Contracts\Bus\Dispatcher;
class CreateAction extends BaseCreateAction class CreateAction extends BaseCreateAction
{ {
/** /**
* @var \Illuminate\Contracts\Bus\Dispatcher * @var Dispatcher
*/ */
protected $bus; protected $bus;
@ -53,7 +53,7 @@ class CreateAction extends BaseCreateAction
/** /**
* Instantiate the action. * Instantiate the action.
* *
* @param \Illuminate\Contracts\Bus\Dispatcher $bus * @param Dispatcher $bus
*/ */
public function __construct(Dispatcher $bus) public function __construct(Dispatcher $bus)
{ {
@ -64,24 +64,23 @@ class CreateAction extends BaseCreateAction
* Reply to a discussion according to input from the API request. * Reply to a discussion according to input from the API request.
* *
* @param JsonApiRequest $request * @param JsonApiRequest $request
* @return \Flarum\Core\Models\Model * @return \Flarum\Core\Posts\Post
*/ */
protected function create(JsonApiRequest $request) protected function create(JsonApiRequest $request)
{ {
$user = $request->actor->getUser(); $actor = $request->actor;
$discussionId = $request->get('data.relationships.discussion.data.id');
$discussionId = $request->get('data.links.discussion.linkage.id');
$post = $this->bus->dispatch( $post = $this->bus->dispatch(
new PostReplyCommand($discussionId, $user, $request->get('data')) new PostReply($discussionId, $actor, $request->get('data'))
); );
// After replying, we assume that the user has seen all of the posts // After replying, we assume that the user has seen all of the posts
// in the discussion; thus, we will mark the discussion as read if // in the discussion; thus, we will mark the discussion as read if
// they are logged in. // they are logged in.
if ($user->exists) { if ($actor->exists) {
$this->bus->dispatch( $this->bus->dispatch(
new ReadDiscussionCommand($discussionId, $user, $post->number) new ReadDiscussion($discussionId, $actor, $post->number)
); );
} }

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Actions\Posts; <?php namespace Flarum\Api\Actions\Posts;
use Flarum\Core\Commands\DeletePostCommand; use Flarum\Core\Posts\Commands\DeletePost;
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction; use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
use Flarum\Api\Request; use Flarum\Api\Request;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
@ -8,14 +8,12 @@ use Illuminate\Contracts\Bus\Dispatcher;
class DeleteAction extends BaseDeleteAction class DeleteAction extends BaseDeleteAction
{ {
/** /**
* @var \Illuminate\Contracts\Bus\Dispatcher * @var Dispatcher
*/ */
protected $bus; protected $bus;
/** /**
* Instantiate the action. * @param Dispatcher $bus
*
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
*/ */
public function __construct(Dispatcher $bus) public function __construct(Dispatcher $bus)
{ {
@ -25,13 +23,13 @@ class DeleteAction extends BaseDeleteAction
/** /**
* Delete a post. * Delete a post.
* *
* @param \Flarum\Api\Request $request * @param Request $request
* @return void * @return void
*/ */
protected function delete(Request $request) protected function delete(Request $request)
{ {
$this->bus->dispatch( $this->bus->dispatch(
new DeletePostCommand($request->get('id'), $request->actor->getUser()) new DeletePost($request->get('id'), $request->actor)
); );
} }
} }

View File

@ -4,12 +4,22 @@ use Flarum\Api\JsonApiRequest;
trait GetsPosts trait GetsPosts
{ {
/**
* @var \Flarum\Core\Posts\PostRepositoryInterface
*/
protected $posts;
/**
* @param JsonApiRequest $request
* @param array $where
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function getPosts(JsonApiRequest $request, array $where) protected function getPosts(JsonApiRequest $request, array $where)
{ {
$user = $request->actor->getUser(); $actor = $request->actor;
if (isset($where['discussion_id']) && ($near = $request->get('near')) > 1) { if (isset($where['discussion_id']) && ($near = $request->get('page.near')) > 1) {
$offset = $this->posts->getIndexForNumber($where['discussion_id'], $near, $user); $offset = $this->posts->getIndexForNumber($where['discussion_id'], $near, $actor);
$offset = max(0, $offset - $request->limit / 2); $offset = max(0, $offset - $request->limit / 2);
} else { } else {
$offset = 0; $offset = 0;
@ -17,7 +27,7 @@ trait GetsPosts
return $this->posts->findWhere( return $this->posts->findWhere(
$where, $where,
$user, $actor,
$request->sort, $request->sort,
$request->limit, $request->limit,
$offset $offset

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Actions\Posts; <?php namespace Flarum\Api\Actions\Posts;
use Flarum\Core\Repositories\PostRepositoryInterface; use Flarum\Core\Posts\PostRepositoryInterface;
use Flarum\Api\Actions\SerializeCollectionAction; use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Document; use Tobscure\JsonApi\Document;
@ -9,11 +9,6 @@ class IndexAction extends SerializeCollectionAction
{ {
use GetsPosts; use GetsPosts;
/**
* @var \Flarum\Core\Repositories\PostRepositoryInterface
*/
protected $posts;
/** /**
* @inheritdoc * @inheritdoc
*/ */
@ -56,9 +51,7 @@ class IndexAction extends SerializeCollectionAction
public static $sort; public static $sort;
/** /**
* Instantiate the action. * @param PostRepositoryInterface $posts
*
* @param \Flarum\Core\Repositories\PostRepositoryInterface $posts
*/ */
public function __construct(PostRepositoryInterface $posts) public function __construct(PostRepositoryInterface $posts)
{ {
@ -69,26 +62,26 @@ class IndexAction extends SerializeCollectionAction
* Get the post results, ready to be serialized and assigned to the * Get the post results, ready to be serialized and assigned to the
* document response. * document response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Illuminate\Database\Eloquent\Collection * @return \Illuminate\Database\Eloquent\Collection
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
$postIds = (array) $request->get('ids'); $postIds = (array) $request->get('ids');
$user = $request->actor->getUser(); $actor = $request->actor;
if (count($postIds)) { if (count($postIds)) {
$posts = $this->posts->findByIds($postIds, $user); $posts = $this->posts->findByIds($postIds, $actor);
} else { } else {
$where = []; $where = [];
if ($discussionId = $request->get('discussions')) { if ($discussionId = $request->get('filter.discussion')) {
$where['discussion_id'] = $discussionId; $where['discussion_id'] = $discussionId;
} }
if ($number = $request->get('number')) { if ($number = $request->get('page.number')) {
$where['number'] = $number; $where['number'] = $number;
} }
if ($userId = $request->get('users')) { if ($userId = $request->get('filter.user')) {
$where['user_id'] = $userId; $where['user_id'] = $userId;
} }
$posts = $this->getPosts($request, $where); $posts = $this->getPosts($request, $where);

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Api\Actions\Posts; <?php namespace Flarum\Api\Actions\Posts;
use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\ModelNotFoundException;
use Flarum\Core\Repositories\PostRepositoryInterface; use Flarum\Core\Posts\PostRepositoryInterface;
use Flarum\Api\Actions\SerializeResourceAction; use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Document; use Tobscure\JsonApi\Document;
@ -9,7 +9,7 @@ use Tobscure\JsonApi\Document;
class ShowAction extends SerializeResourceAction class ShowAction extends SerializeResourceAction
{ {
/** /**
* @var \Flarum\Core\Repositories\PostRepositoryInterface * @var PostRepositoryInterface
*/ */
protected $posts; protected $posts;
@ -55,9 +55,7 @@ class ShowAction extends SerializeResourceAction
public static $sort; public static $sort;
/** /**
* Instantiate the action. * @param PostRepositoryInterface $posts
*
* @param \Flarum\Core\Repositories\PostRepositoryInterface $posts
*/ */
public function __construct(PostRepositoryInterface $posts) public function __construct(PostRepositoryInterface $posts)
{ {
@ -68,12 +66,12 @@ class ShowAction extends SerializeResourceAction
* Get a single post, ready to be serialized and assigned to the JsonApi * Get a single post, ready to be serialized and assigned to the JsonApi
* response. * response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Illuminate\Database\Eloquent\Collection * @return \Flarum\Core\Posts\Post
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
return $this->posts->findOrFail($request->get('id'), $request->actor->getUser()); return $this->posts->findOrFail($request->get('id'), $request->actor);
} }
} }

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Actions\Posts; <?php namespace Flarum\Api\Actions\Posts;
use Flarum\Core\Commands\EditPostCommand; use Flarum\Core\Posts\Commands\EditPost;
use Flarum\Api\Actions\SerializeResourceAction; use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
@ -9,7 +9,7 @@ use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction class UpdateAction extends SerializeResourceAction
{ {
/** /**
* @var \Illuminate\Contracts\Bus\Dispatcher * @var Dispatcher
*/ */
protected $bus; protected $bus;
@ -49,9 +49,7 @@ class UpdateAction extends SerializeResourceAction
public static $sort; public static $sort;
/** /**
* Instantiate the action. * @param Dispatcher $bus
*
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
*/ */
public function __construct(Dispatcher $bus) public function __construct(Dispatcher $bus)
{ {
@ -62,14 +60,14 @@ class UpdateAction extends SerializeResourceAction
* Update a post according to input from the API request, and return it * Update a post according to input from the API request, and return it
* ready to be serialized and assigned to the JsonApi response. * ready to be serialized and assigned to the JsonApi response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Illuminate\Database\Eloquent\Collection * @return \Illuminate\Database\Eloquent\Collection
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
return $this->bus->dispatch( return $this->bus->dispatch(
new EditPostCommand($request->get('id'), $request->actor->getUser(), $request->get('data')) new EditPost($request->get('id'), $request->actor, $request->get('data'))
); );
} }
} }

View File

@ -63,8 +63,8 @@ abstract class SerializeAction extends JsonApiAction
/** /**
* Handle an API request and return an API response. * Handle an API request and return an API response.
* *
* @param \Flarum\Api\Request $request * @param Request $request
* @return \Psr\Http\Message\ResponseInterface * @return JsonResponse
*/ */
public function respond(Request $request) public function respond(Request $request)
{ {
@ -78,14 +78,15 @@ abstract class SerializeAction extends JsonApiAction
$serializer = new static::$serializer($request->actor, $request->include, $request->link); $serializer = new static::$serializer($request->actor, $request->include, $request->link);
$document->setData($this->serialize($serializer, $data)); $document->setData($this->serialize($serializer, $data));
return new JsonResponse($document, 200, ['content-type' => 'application/vnd.api+json']); return new JsonResponse($document, 200, ['content-type' => 'application/vnd.api+json']);
} }
/** /**
* Get the data to be serialized and assigned to the response document. * Get the data to be serialized and assigned to the response document.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return array * @return array
*/ */
abstract protected function data(JsonApiRequest $request, Document $document); abstract protected function data(JsonApiRequest $request, Document $document);
@ -93,7 +94,7 @@ abstract class SerializeAction extends JsonApiAction
/** /**
* Serialize the data as appropriate. * Serialize the data as appropriate.
* *
* @param \Tobscure\JsonApi\SerializerInterface $serializer * @param SerializerInterface $serializer
* @param array $data * @param array $data
* @return \Tobscure\JsonApi\Elements\ElementInterface * @return \Tobscure\JsonApi\Elements\ElementInterface
*/ */
@ -103,8 +104,8 @@ abstract class SerializeAction extends JsonApiAction
* Extract parameters from the request input and assign them to the * Extract parameters from the request input and assign them to the
* request, restricted by the action's specifications. * request, restricted by the action's specifications.
* *
* @param \Flarum\Api\Request $request * @param Request $request
* @return void * @return JsonApiRequest
*/ */
protected static function buildJsonApiRequest(Request $request) protected static function buildJsonApiRequest(Request $request)
{ {
@ -160,8 +161,8 @@ abstract class SerializeAction extends JsonApiAction
* Add pagination links to a JSON-API response, based on input parameters * Add pagination links to a JSON-API response, based on input parameters
* and the default parameters of this action. * and the default parameters of this action.
* *
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param string $url The base URL to build pagination links with. * @param string $url The base URL to build pagination links with.
* @param integer|boolean $total The total number of results (used to build * @param integer|boolean $total The total number of results (used to build
* a 'last' link), or just true if there are more results but how many * a 'last' link), or just true if there are more results but how many

View File

@ -7,7 +7,7 @@ abstract class SerializeCollectionAction extends SerializeAction
/** /**
* Serialize the data as appropriate. * Serialize the data as appropriate.
* *
* @param \Tobscure\JsonApi\SerializerInterface $serializer * @param SerializerInterface $serializer
* @param array $data * @param array $data
* @return \Tobscure\JsonApi\Elements\Collection * @return \Tobscure\JsonApi\Elements\Collection
*/ */

View File

@ -7,7 +7,7 @@ abstract class SerializeResourceAction extends SerializeAction
/** /**
* Serialize the data as appropriate. * Serialize the data as appropriate.
* *
* @param \Tobscure\JsonApi\SerializerInterface $serializer * @param SerializerInterface $serializer
* @param array $data * @param array $data
* @return \Tobscure\JsonApi\Elements\Resource * @return \Tobscure\JsonApi\Elements\Resource
*/ */

View File

@ -1,10 +1,10 @@
<?php namespace Flarum\Api\Actions; <?php namespace Flarum\Api\Actions;
use Flarum\Api\Commands\GenerateAccessToken;
use Flarum\Api\Request; use Flarum\Api\Request;
use Flarum\Core\Commands\GenerateAccessTokenCommand; use Flarum\Core\Users\UserRepositoryInterface;
use Flarum\Core\Repositories\UserRepositoryInterface;
use Flarum\Core\Exceptions\PermissionDeniedException; use Flarum\Core\Exceptions\PermissionDeniedException;
use Flarum\Core\Events\UserEmailChangeWasRequested; use Flarum\Core\Users\Events\UserEmailChangeWasRequested;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
use Zend\Diactoros\Response\JsonResponse; use Zend\Diactoros\Response\JsonResponse;
@ -23,7 +23,7 @@ class TokenAction extends JsonApiAction
/** /**
* Log in and return a token. * Log in and return a token.
* *
* @param \Flarum\Api\Request $request * @param Request $request
* @return \Psr\Http\Message\ResponseInterface * @return \Psr\Http\Message\ResponseInterface
* @throws PermissionDeniedException * @throws PermissionDeniedException
*/ */
@ -40,6 +40,7 @@ class TokenAction extends JsonApiAction
if (! $user->is_activated) { if (! $user->is_activated) {
event(new UserEmailChangeWasRequested($user, $user->email)); event(new UserEmailChangeWasRequested($user, $user->email));
return new JsonResponse([ return new JsonResponse([
'code' => 'confirm_email', 'code' => 'confirm_email',
'email' => $user->email 'email' => $user->email
@ -47,7 +48,7 @@ class TokenAction extends JsonApiAction
} }
$token = $this->bus->dispatch( $token = $this->bus->dispatch(
new GenerateAccessTokenCommand($user->id) new GenerateAccessToken($user->id)
); );
return new JsonResponse([ return new JsonResponse([

View File

@ -1,7 +1,6 @@
<?php namespace Flarum\Api\Actions\Users; <?php namespace Flarum\Api\Actions\Users;
use Flarum\Core\Models\Forum; use Flarum\Core\Users\Commands\RegisterUser;
use Flarum\Core\Commands\RegisterUserCommand;
use Flarum\Api\Actions\CreateAction as BaseCreateAction; use Flarum\Api\Actions\CreateAction as BaseCreateAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
@ -9,19 +8,10 @@ use Illuminate\Contracts\Bus\Dispatcher;
class CreateAction extends BaseCreateAction class CreateAction extends BaseCreateAction
{ {
/** /**
* The command bus. * @var Dispatcher
*
* @var \Illuminate\Contracts\Bus\Dispatcher
*/ */
protected $bus; protected $bus;
/**
* The default forum instance.
*
* @var \Flarum\Core\Models\Forum
*/
protected $forum;
/** /**
* @inheritdoc * @inheritdoc
*/ */
@ -58,27 +48,23 @@ class CreateAction extends BaseCreateAction
public static $sort; public static $sort;
/** /**
* Instantiate the action. * @param Dispatcher $bus
*
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
* @param \Flarum\Core\Models\Forum $forum
*/ */
public function __construct(Dispatcher $bus, Forum $forum) public function __construct(Dispatcher $bus)
{ {
$this->bus = $bus; $this->bus = $bus;
$this->forum = $forum;
} }
/** /**
* Register a user according to input from the API request. * Register a user according to input from the API request.
* *
* @param JsonApiRequest $request * @param JsonApiRequest $request
* @return \Flarum\Core\Models\Model * @return \Flarum\Core\Users\User
*/ */
protected function create(JsonApiRequest $request) protected function create(JsonApiRequest $request)
{ {
return $this->bus->dispatch( return $this->bus->dispatch(
new RegisterUserCommand($request->actor->getUser(), $this->forum, $request->get('data')) new RegisterUser($request->actor, $request->get('data'))
); );
} }
} }

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Actions\Users; <?php namespace Flarum\Api\Actions\Users;
use Flarum\Core\Commands\DeleteUserCommand; use Flarum\Core\Users\Commands\DeleteUser;
use Flarum\Api\Actions\DeleteAction as BaseDeleteAction; use Flarum\Api\Actions\DeleteAction as BaseDeleteAction;
use Flarum\Api\Request; use Flarum\Api\Request;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
@ -8,16 +8,12 @@ use Illuminate\Contracts\Bus\Dispatcher;
class DeleteAction extends BaseDeleteAction class DeleteAction extends BaseDeleteAction
{ {
/** /**
* The command bus. * @var Dispatcher
*
* @var \Illuminate\Contracts\Bus\Dispatcher
*/ */
protected $bus; protected $bus;
/** /**
* Instantiate the action. * @param Dispatcher $bus
*
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
*/ */
public function __construct(Dispatcher $bus) public function __construct(Dispatcher $bus)
{ {
@ -27,13 +23,12 @@ class DeleteAction extends BaseDeleteAction
/** /**
* Delete a user. * Delete a user.
* *
* @param \Flarum\Api\Request $request * @param Request $request
* @return void
*/ */
protected function delete(Request $request) protected function delete(Request $request)
{ {
$this->bus->dispatch( $this->bus->dispatch(
new DeleteUserCommand($request->get('id'), $request->actor->getUser()) new DeleteUser($request->get('id'), $request->actor)
); );
} }
} }

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Actions\Users; <?php namespace Flarum\Api\Actions\Users;
use Flarum\Core\Commands\DeleteAvatarCommand; use Flarum\Core\Users\Commands\DeleteAvatar;
use Flarum\Api\Actions\SerializeResourceAction; use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
@ -9,7 +9,7 @@ use Tobscure\JsonApi\Document;
class DeleteAvatarAction extends SerializeResourceAction class DeleteAvatarAction extends SerializeResourceAction
{ {
/** /**
* @var \Illuminate\Contracts\Bus\Dispatcher * @var Dispatcher
*/ */
protected $bus; protected $bus;
@ -19,9 +19,7 @@ class DeleteAvatarAction extends SerializeResourceAction
public static $serializer = 'Flarum\Api\Serializers\UserSerializer'; public static $serializer = 'Flarum\Api\Serializers\UserSerializer';
/** /**
* Instantiate the action. * @param Dispatcher $bus
*
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
*/ */
public function __construct(Dispatcher $bus) public function __construct(Dispatcher $bus)
{ {
@ -32,14 +30,14 @@ class DeleteAvatarAction extends SerializeResourceAction
* Delete a user's avatar, and return the user ready to be serialized and * Delete a user's avatar, and return the user ready to be serialized and
* assigned to the JsonApi response. * assigned to the JsonApi response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Flarum\Core\Models\Discussion * @return \Flarum\Core\Users\User
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
return $this->bus->dispatch( return $this->bus->dispatch(
new DeleteAvatarCommand($request->get('id'), $request->actor->getUser()) new DeleteAvatar($request->get('id'), $request->actor)
); );
} }
} }

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Api\Actions\Users; <?php namespace Flarum\Api\Actions\Users;
use Flarum\Core\Search\Users\UserSearchCriteria; use Flarum\Core\Search\SearchCriteria;
use Flarum\Core\Search\Users\UserSearcher; use Flarum\Core\Users\Search\UserSearcher;
use Flarum\Api\Actions\SerializeCollectionAction; use Flarum\Api\Actions\SerializeCollectionAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Flarum\Http\UrlGeneratorInterface; use Flarum\Http\UrlGeneratorInterface;
@ -10,16 +10,12 @@ use Tobscure\JsonApi\Document;
class IndexAction extends SerializeCollectionAction class IndexAction extends SerializeCollectionAction
{ {
/** /**
* The user searcher. * @var UserSearcher
*
* @var \Flarum\Core\Search\Users\UserSearcher
*/ */
protected $searcher; protected $searcher;
/** /**
* The URL generator. * @var UrlGeneratorInterface
*
* @var \Flarum\Http\UrlGeneratorInterface
*/ */
protected $url; protected $url;
@ -61,10 +57,8 @@ class IndexAction extends SerializeCollectionAction
public static $sort; public static $sort;
/** /**
* Instantiate the action. * @param UserSearcher $searcher
* * @param UrlGeneratorInterface $url
* @param \Flarum\Core\Search\Users\UserSearcher $searcher
* @param \Flarum\Http\UrlGeneratorInterface $url
*/ */
public function __construct(UserSearcher $searcher, UrlGeneratorInterface $url) public function __construct(UserSearcher $searcher, UrlGeneratorInterface $url)
{ {
@ -76,15 +70,15 @@ class IndexAction extends SerializeCollectionAction
* Get the user results, ready to be serialized and assigned to the * Get the user results, ready to be serialized and assigned to the
* document response. * document response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Illuminate\Database\Eloquent\Collection * @return \Illuminate\Database\Eloquent\Collection
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
$criteria = new UserSearchCriteria( $criteria = new SearchCriteria(
$request->actor->getUser(), $request->actor,
$request->get('q'), $request->get('filter.q'),
$request->sort $request->sort
); );
@ -97,6 +91,6 @@ class IndexAction extends SerializeCollectionAction
$results->areMoreResults() $results->areMoreResults()
); );
return $results->getUsers(); return $results->getResults();
} }
} }

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Actions\Users; <?php namespace Flarum\Api\Actions\Users;
use Flarum\Core\Repositories\UserRepositoryInterface; use Flarum\Core\Users\UserRepositoryInterface;
use Flarum\Api\Actions\SerializeResourceAction; use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Tobscure\JsonApi\Document; use Tobscure\JsonApi\Document;
@ -8,7 +8,7 @@ use Tobscure\JsonApi\Document;
class ShowAction extends SerializeResourceAction class ShowAction extends SerializeResourceAction
{ {
/** /**
* @var \Flarum\Core\Repositories\UserRepositoryInterface * @var UserRepositoryInterface
*/ */
protected $users; protected $users;
@ -50,9 +50,7 @@ class ShowAction extends SerializeResourceAction
public static $sort; public static $sort;
/** /**
* Instantiate the action. * @param UserRepositoryInterface $users
*
* @param \Flarum\Core\Repositories\UserRepositoryInterface $users
*/ */
public function __construct(UserRepositoryInterface $users) public function __construct(UserRepositoryInterface $users)
{ {
@ -63,9 +61,9 @@ class ShowAction extends SerializeResourceAction
* Get a single user, ready to be serialized and assigned to the JsonApi * Get a single user, ready to be serialized and assigned to the JsonApi
* response. * response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Flarum\Core\Models\Discussion * @return \Flarum\Core\Users\User
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
@ -75,6 +73,6 @@ class ShowAction extends SerializeResourceAction
$id = $this->users->getIdForUsername($id); $id = $this->users->getIdForUsername($id);
} }
return $this->users->findOrFail($id, $request->actor->getUser()); return $this->users->findOrFail($id, $request->actor);
} }
} }

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Actions\Users; <?php namespace Flarum\Api\Actions\Users;
use Flarum\Core\Commands\EditUserCommand; use Flarum\Core\Users\Commands\EditUser;
use Flarum\Api\Actions\SerializeResourceAction; use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
@ -9,14 +9,14 @@ use Tobscure\JsonApi\Document;
class UpdateAction extends SerializeResourceAction class UpdateAction extends SerializeResourceAction
{ {
/** /**
* @var \Illuminate\Contracts\Bus\Dispatcher * @var Dispatcher
*/ */
protected $bus; protected $bus;
/** /**
* @inheritdoc * @inheritdoc
*/ */
public static $serializer = 'Flarum\Api\Serializers\UserSerializer'; public static $serializer = 'Flarum\Api\Serializers\CurrentUserSerializer';
/** /**
* @inheritdoc * @inheritdoc
@ -49,9 +49,7 @@ class UpdateAction extends SerializeResourceAction
public static $sort; public static $sort;
/** /**
* Instantiate the action. * @param Dispatcher $bus
*
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
*/ */
public function __construct(Dispatcher $bus) public function __construct(Dispatcher $bus)
{ {
@ -62,14 +60,14 @@ class UpdateAction extends SerializeResourceAction
* Update a user according to input from the API request, and return it * Update a user according to input from the API request, and return it
* ready to be serialized and assigned to the JsonApi response. * ready to be serialized and assigned to the JsonApi response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Flarum\Core\Models\Discussion * @return \Flarum\Core\Users\User
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
return $this->bus->dispatch( return $this->bus->dispatch(
new EditUserCommand($request->get('id'), $request->actor->getUser(), $request->get('data')) new EditUser($request->get('id'), $request->actor, $request->get('data'))
); );
} }
} }

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Actions\Users; <?php namespace Flarum\Api\Actions\Users;
use Flarum\Core\Commands\UploadAvatarCommand; use Flarum\Core\Users\Commands\UploadAvatar;
use Flarum\Api\Actions\SerializeResourceAction; use Flarum\Api\Actions\SerializeResourceAction;
use Flarum\Api\JsonApiRequest; use Flarum\Api\JsonApiRequest;
use Illuminate\Contracts\Bus\Dispatcher; use Illuminate\Contracts\Bus\Dispatcher;
@ -9,7 +9,7 @@ use Tobscure\JsonApi\Document;
class UploadAvatarAction extends SerializeResourceAction class UploadAvatarAction extends SerializeResourceAction
{ {
/** /**
* @var \Illuminate\Contracts\Bus\Dispatcher * @var Dispatcher
*/ */
protected $bus; protected $bus;
@ -49,9 +49,7 @@ class UploadAvatarAction extends SerializeResourceAction
public static $sort; public static $sort;
/** /**
* Instantiate the action. * @param Dispatcher $bus
*
* @param \Illuminate\Contracts\Bus\Dispatcher $bus
*/ */
public function __construct(Dispatcher $bus) public function __construct(Dispatcher $bus)
{ {
@ -62,17 +60,17 @@ class UploadAvatarAction extends SerializeResourceAction
* Upload an avatar for a user, and return the user ready to be serialized * Upload an avatar for a user, and return the user ready to be serialized
* and assigned to the JsonApi response. * and assigned to the JsonApi response.
* *
* @param \Flarum\Api\JsonApiRequest $request * @param JsonApiRequest $request
* @param \Tobscure\JsonApi\Document $document * @param Document $document
* @return \Flarum\Core\Models\Discussion * @return \Flarum\Core\Users\User
*/ */
protected function data(JsonApiRequest $request, Document $document) protected function data(JsonApiRequest $request, Document $document)
{ {
return $this->bus->dispatch( return $this->bus->dispatch(
new UploadAvatarCommand( new UploadAvatar(
$request->get('id'), $request->get('id'),
$request->http->getUploadedFiles()['avatar'], $request->http->getUploadedFiles()['avatar'],
$request->actor->getUser() $request->actor
) )
); );
} }

View File

@ -1,5 +1,6 @@
<?php namespace Flarum\Api; <?php namespace Flarum\Api;
use Flarum\Core\Users\Guest;
use Flarum\Http\RouteCollection; use Flarum\Http\RouteCollection;
use Flarum\Http\UrlGenerator; use Flarum\Http\UrlGenerator;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
@ -14,7 +15,9 @@ class ApiServiceProvider extends ServiceProvider
*/ */
public function register() public function register()
{ {
$this->app->singleton('Flarum\Support\Actor'); $this->app->bind('flarum.actor', function () {
return new Guest;
});
$this->app->singleton( $this->app->singleton(
'Flarum\Http\UrlGeneratorInterface', 'Flarum\Http\UrlGeneratorInterface',
@ -33,11 +36,6 @@ class ApiServiceProvider extends ServiceProvider
*/ */
public function boot() public function boot()
{ {
$this->app->singleton(
'Illuminate\Contracts\Debug\ExceptionHandler',
'Flarum\Api\ExceptionHandler'
);
$this->routes(); $this->routes();
} }
@ -94,7 +92,7 @@ class ApiServiceProvider extends ServiceProvider
); );
// Edit a user // Edit a user
$routes->put( $routes->patch(
'/users/{id}', '/users/{id}',
'flarum.api.users.update', 'flarum.api.users.update',
$this->action('Flarum\Api\Actions\Users\UpdateAction') $this->action('Flarum\Api\Actions\Users\UpdateAction')
@ -142,7 +140,7 @@ class ApiServiceProvider extends ServiceProvider
); );
// Mark a single notification as read // Mark a single notification as read
$routes->put( $routes->patch(
'/notifications/{id}', '/notifications/{id}',
'flarum.api.notifications.update', 'flarum.api.notifications.update',
$this->action('Flarum\Api\Actions\Notifications\UpdateAction') $this->action('Flarum\Api\Actions\Notifications\UpdateAction')
@ -175,7 +173,7 @@ class ApiServiceProvider extends ServiceProvider
); );
// Edit a discussion // Edit a discussion
$routes->put( $routes->patch(
'/discussions/{id}', '/discussions/{id}',
'flarum.api.discussions.update', 'flarum.api.discussions.update',
$this->action('Flarum\Api\Actions\Discussions\UpdateAction') $this->action('Flarum\Api\Actions\Discussions\UpdateAction')
@ -202,7 +200,6 @@ class ApiServiceProvider extends ServiceProvider
); );
// Create a post // Create a post
// @todo consider 'discussions/{id}/links/posts'?
$routes->post( $routes->post(
'/posts', '/posts',
'flarum.api.posts.create', 'flarum.api.posts.create',
@ -217,7 +214,7 @@ class ApiServiceProvider extends ServiceProvider
); );
// Edit a post // Edit a post
$routes->put( $routes->patch(
'/posts/{id}', '/posts/{id}',
'flarum.api.posts.update', 'flarum.api.posts.update',
$this->action('Flarum\Api\Actions\Posts\UpdateAction') $this->action('Flarum\Api\Actions\Posts\UpdateAction')
@ -258,7 +255,7 @@ class ApiServiceProvider extends ServiceProvider
); );
// Edit a group // Edit a group
$routes->put( $routes->patch(
'/groups/{id}', '/groups/{id}',
'flarum.api.groups.update', 'flarum.api.groups.update',
$this->action('Flarum\Api\Actions\Groups\UpdateAction') $this->action('Flarum\Api\Actions\Groups\UpdateAction')
@ -276,7 +273,7 @@ class ApiServiceProvider extends ServiceProvider
{ {
return function (ServerRequestInterface $httpRequest, $routeParams) use ($class) { return function (ServerRequestInterface $httpRequest, $routeParams) use ($class) {
$action = app($class); $action = app($class);
$actor = app('Flarum\Support\Actor'); $actor = app('flarum.actor');
$input = array_merge($httpRequest->getQueryParams(), $httpRequest->getAttributes(), $routeParams); $input = array_merge($httpRequest->getQueryParams(), $httpRequest->getAttributes(), $routeParams);
$request = new Request($input, $actor, $httpRequest); $request = new Request($input, $actor, $httpRequest);

View File

@ -1,35 +1,37 @@
<?php <?php namespace Flarum\Api;
namespace Flarum\Api; use Flarum\Core\Users\User;
use Flarum\Support\Actor;
use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Container\Container;
class Client class Client
{ {
/**
* @var Container
*/
protected $container; protected $container;
protected $actor; /**
* @param Container $container
public function __construct(Container $container, Actor $actor) */
public function __construct(Container $container)
{ {
$this->container = $container; $this->container = $container;
$this->actor = $actor;
} }
/** /**
* Execute the given API action class, pass the input and return its response. * Execute the given API action class, pass the input and return its response.
* *
* @param User $actor
* @param string $actionClass * @param string $actionClass
* @param array $input * @param array $input
* @return object * @return object
*/ */
public function send($actionClass, array $input = []) public function send(User $actor, $actionClass, array $input = [])
{ {
/** @var \Flarum\Api\Actions\JsonApiAction $action */ /** @var \Flarum\Api\Actions\JsonApiAction $action */
$action = $this->container->make($actionClass); $action = $this->container->make($actionClass);
$response = $action->handle(new Request($input, $this->actor)); $response = $action->handle(new Request($input, $actor));
return json_decode($response->getBody()); return json_decode($response->getBody());
} }

View File

@ -0,0 +1,19 @@
<?php namespace Flarum\Api\Commands;
class GenerateAccessToken
{
/**
* The ID of the user to generate an access token for.
*
* @var int
*/
public $userId;
/**
* @param int $userId The ID of the user to generate an access token for.
*/
public function __construct($userId)
{
$this->userId = $userId;
}
}

View File

@ -0,0 +1,15 @@
<?php namespace Flarum\Api\Commands;
use Flarum\Api\AccessToken;
class GenerateAccessTokenHandler
{
public function handle(GenerateAccessToken $command)
{
$token = AccessToken::generate($command->userId);
$token->save();
return $token;
}
}

View File

@ -1,14 +1,36 @@
<?php namespace Flarum\Api\Events; <?php namespace Flarum\Api\Events;
use Flarum\Api\Serializers\Serializer;
class SerializeAttributes class SerializeAttributes
{ {
/**
* The class doing the serializing.
*
* @var Serializer
*/
public $serializer; public $serializer;
/**
* The model being serialized.
*
* @var object
*/
public $model; public $model;
/**
* The serialized attributes of the resource.
*
* @var array
*/
public $attributes; public $attributes;
public function __construct($serializer, $model, &$attributes) /**
* @param Serializer $serializer The class doing the serializing.
* @param object $model The model being serialized.
* @param array $attributes The serialized attributes of the resource.
*/
public function __construct(Serializer $serializer, $model, array &$attributes)
{ {
$this->serializer = $serializer; $this->serializer = $serializer;
$this->model = $model; $this->model = $model;

View File

@ -1,54 +0,0 @@
<?php namespace Flarum\Api;
use Exception;
use Illuminate\Foundation\Exceptions\Handler;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Config;
class ExceptionHandler extends Handler
{
/**
* A list of the exception types that should not be reported.
*
* @var array
*/
protected $dontReport = [
'Symfony\Component\HttpKernel\Exception\HttpException'
];
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
if ($request->is('api/*')) {
$error = [];
if (Config::get('app.debug')) {
$error['code'] = (new \ReflectionClass($e))->getShortName();
}
if ($detail = $e->getMessage()) {
$error['detail'] = $detail;
}
$statusCode = $e instanceof HttpException ? $e->getStatusCode() : 500;
if (count($error)) {
return $this->renderErrors([$error], $statusCode);
} else {
return new Response(null, $statusCode);
}
}
return parent::render($request, $e);
}
protected function renderErrors($errors, $httpCode = 500)
{
return new JsonResponse(['errors' => $errors], $httpCode);
}
}

View File

@ -1,7 +1,7 @@
<?php namespace Flarum\Api\Middleware; <?php namespace Flarum\Api\Middleware;
use Flarum\Core\Models\AccessToken; use Flarum\Api\AccessToken;
use Flarum\Support\Actor; use Illuminate\Contracts\Container\Container;
use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use Zend\Stratigility\MiddlewareInterface; use Zend\Stratigility\MiddlewareInterface;
@ -9,20 +9,21 @@ use Zend\Stratigility\MiddlewareInterface;
class LoginWithHeader implements MiddlewareInterface class LoginWithHeader implements MiddlewareInterface
{ {
/** /**
* @var Actor * @var Container
*/ */
protected $actor; protected $app;
/** /**
* @var string * @var string
*/ */
protected $prefix = 'Token '; protected $prefix = 'Token ';
// @todo rather than using a singleton, we should have our own HTTP /**
// Request class and store the actor on that? somehow? * @param Container $app
public function __construct(Actor $actor) */
public function __construct(Container $app)
{ {
$this->actor = $actor; $this->app = $app;
} }
/** /**
@ -35,7 +36,7 @@ class LoginWithHeader implements MiddlewareInterface
($token = substr($header, strlen($this->prefix))) && ($token = substr($header, strlen($this->prefix))) &&
($accessToken = AccessToken::where('id', $token)->first()) ($accessToken = AccessToken::where('id', $token)->first())
) { ) {
$this->actor->setUser($user = $accessToken->user); $this->app->instance('flarum.actor', $user = $accessToken->user);
$user->updateLastSeen()->save(); $user->updateLastSeen()->save();
} }

View File

@ -1,12 +1,19 @@
<?php namespace Flarum\Api; <?php namespace Flarum\Api;
use Flarum\Support\Actor; use Flarum\Core\Users\Guest;
use Flarum\Core\Users\User;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
class Request class Request
{ {
/**
* @var array
*/
public $input; public $input;
/**
* @var Guest
*/
public $actor; public $actor;
/** /**
@ -14,18 +21,31 @@ class Request
*/ */
public $http; public $http;
public function __construct(array $input, Actor $actor = null, ServerRequestInterface $http = null) /**
* @param array $input
* @param User $actor
* @param ServerRequestInterface $http
*/
public function __construct(array $input, User $actor = null, ServerRequestInterface $http = null)
{ {
$this->input = $input; $this->input = $input;
$this->actor = $actor; $this->actor = $actor ?: new Guest;
$this->http = $http; $this->http = $http;
} }
/**
* @param $key
* @param null $default
* @return mixed
*/
public function get($key, $default = null) public function get($key, $default = null)
{ {
return array_get($this->input, $key, $default); return array_get($this->input, $key, $default);
} }
/**
* @return array
*/
public function all() public function all()
{ {
return $this->input; return $this->input;

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Api\Serializers; <?php namespace Flarum\Api\Serializers;
class ActivitySerializer extends BaseSerializer class ActivitySerializer extends Serializer
{ {
/** /**
* @inheritdoc * @inheritdoc
@ -16,38 +16,46 @@ class ActivitySerializer extends BaseSerializer
protected static $subjectSerializers = []; protected static $subjectSerializers = [];
/** /**
* Serialize attributes of an Activity model for JSON output. * {@inheritdoc}
*
* @param Activity $activity The Activity model to serialize.
* @return array
*/ */
protected function attributes($activity) protected function getDefaultAttributes($activity)
{ {
$attributes = [ return [
'contentType' => $activity->type, 'contentType' => $activity->type,
'time' => $activity->time->toRFC3339String() 'time' => $activity->time->toRFC3339String()
]; ];
return $this->extendAttributes($activity, $attributes);
} }
/**
* @return callable
*/
public function user() public function user()
{ {
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer'); return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
} }
/**
* @return callable
*/
public function sender() public function sender()
{ {
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer'); return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
} }
/**
* @return callable
*/
public function subject() public function subject()
{ {
return $this->hasOne(function ($activity) { return $this->hasOne(function ($activity) {
return static::$subjects[$activity->type]; return static::$subjectSerializers[$activity->type];
}); });
} }
/**
* @param $type
* @param $serializer
*/
public static function setSubjectSerializer($type, $serializer) public static function setSubjectSerializer($type, $serializer)
{ {
static::$subjectSerializers[$type] = $serializer; static::$subjectSerializers[$type] = $serializer;

View File

@ -1,112 +0,0 @@
<?php namespace Flarum\Api\Serializers;
use Tobscure\JsonApi\SerializerAbstract;
use Flarum\Api\Events\SerializeAttributes;
use Flarum\Api\Events\SerializeRelationship;
use Flarum\Support\Actor;
use Illuminate\Database\Eloquent\Relations\Relation;
use Closure;
/**
* A base serializer to call Flarum events at common serialization points.
*/
abstract class BaseSerializer extends SerializerAbstract
{
public $actor;
/**
* The custom relationships on this serializer.
*
* @var array
*/
protected static $relationships = [];
public function __construct(Actor $actor, $include = null, $link = null)
{
parent::__construct($include, $link);
$this->actor = $actor;
}
/**
* Fire an event to allow custom serialization of attributes.
*
* @param mixed $model The model to serialize.
* @param array $attributes Attributes that have already been serialized.
* @return array
*/
protected function extendAttributes($model, &$attributes = [])
{
event(new SerializeAttributes($this, $model, $attributes));
return $attributes;
}
protected function relationship($serializer, $relation = null, $many = false)
{
// Get the relationship name from the stack trace.
if (is_null($relation)) {
list(, , $caller) = debug_backtrace(false, 3);
$relation = $caller['function'];
}
return function ($model, $include, $included, $links) use ($serializer, $many, $relation) {
if ($relation instanceof Closure) {
$data = $relation($model, $include);
} else {
if ($include) {
$data = $model->$relation;
} elseif ($many) {
$relationIds = $relation.'_ids';
$data = isset($model->$relationIds) ? $model->$relationIds : $model->$relation()->lists('id');
} else {
$relationId = $relation.'_id';
$data = $model->$relationId;
}
}
if ($serializer instanceof Closure) {
$serializer = $serializer($model, $data);
}
$serializer = new $serializer($this->actor, $included, $links);
return $many ? $serializer->collection($data) : $serializer->resource($data);
};
}
public function hasOne($serializer, $relation = null)
{
return $this->relationship($serializer, $relation);
}
public function hasMany($serializer, $relation = null)
{
return $this->relationship($serializer, $relation, true);
}
/**
* Add a custom relationship to the serializer.
*
* @param string $name The name of the relationship.
* @param Closure $callback The callback to execute.
* @return void
*/
public static function addRelationship($name, $callback)
{
static::$relationships[$name] = $callback;
}
/**
* Check for and execute custom relationships.
*
* @param string $name
* @param array $arguments
* @return mixed
*/
public function __call($name, $arguments)
{
if (isset(static::$relationships[$name])) {
array_unshift($arguments, $this);
return call_user_func_array(static::$relationships[$name], $arguments);
}
}
}

View File

@ -2,13 +2,14 @@
class CurrentUserSerializer extends UserSerializer class CurrentUserSerializer extends UserSerializer
{ {
protected function attributes($user) /**
* {@inheritdoc}
*/
protected function getDefaultAttributes($user)
{ {
$attributes = parent::attributes($user); $attributes = parent::getDefaultAttributes($user);
$actingUser = $this->actor->getUser(); if ($user->id == $this->actor->id) {
if ($user->id === $actingUser->id) {
$attributes += [ $attributes += [
'readTime' => $user->read_time ? $user->read_time->toRFC3339String() : null, 'readTime' => $user->read_time ? $user->read_time->toRFC3339String() : null,
'unreadNotificationsCount' => $user->getUnreadNotificationsCount(), 'unreadNotificationsCount' => $user->getUnreadNotificationsCount(),
@ -16,6 +17,6 @@ class CurrentUserSerializer extends UserSerializer
]; ];
} }
return $this->extendAttributes($user, $attributes); return $attributes;
} }
} }

View File

@ -1,67 +1,67 @@
<?php namespace Flarum\Api\Serializers; <?php namespace Flarum\Api\Serializers;
class DiscussionBasicSerializer extends BaseSerializer class DiscussionBasicSerializer extends Serializer
{ {
protected static $relationships = [];
/** /**
* The resource type. * {@inheritdoc}
*
* @var string
*/ */
protected $type = 'discussions'; protected $type = 'discussions';
/** /**
* Serialize attributes of a Discussion model for JSON output. * {@inheritdoc}
*
* @param Discussion $discussion The Discussion model to serialize.
* @return array
*/ */
protected function attributes($discussion) protected function getDefaultAttributes($discussion)
{ {
$attributes = [ return [
'title' => $discussion->title 'title' => $discussion->title
]; ];
if (count($discussion->removedPosts)) {
$attributes['removedPosts'] = $discussion->removedPosts;
} }
return $this->extendAttributes($discussion, $attributes); /**
} * @return callable
*/
public function startUser() protected function startUser()
{ {
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer'); return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
} }
public function startPost() /**
* @return callable
*/
protected function startPost()
{ {
return $this->hasOne('Flarum\Api\Serializers\PostBasicSerializer'); return $this->hasOne('Flarum\Api\Serializers\PostBasicSerializer');
} }
public function lastUser() /**
* @return callable
*/
protected function lastUser()
{ {
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer'); return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
} }
public function lastPost() /**
* @return callable
*/
protected function lastPost()
{ {
return $this->hasOne('Flarum\Api\Serializers\PostBasicSerializer'); return $this->hasOne('Flarum\Api\Serializers\PostBasicSerializer');
} }
public function posts() /**
* @return callable
*/
protected function posts()
{ {
return $this->hasMany('Flarum\Api\Serializers\PostSerializer'); return $this->hasMany('Flarum\Api\Serializers\PostSerializer');
} }
public function relevantPosts() /**
* @return callable
*/
protected function relevantPosts()
{ {
return $this->hasMany('Flarum\Api\Serializers\PostBasicSerializer'); return $this->hasMany('Flarum\Api\Serializers\PostBasicSerializer');
} }
public function addedPosts()
{
return $this->hasMany('Flarum\Api\Serializers\PostSerializer');
}
} }

View File

@ -1,34 +1,34 @@
<?php namespace Flarum\Api\Serializers; <?php namespace Flarum\Api\Serializers;
use Flarum\Core\Discussions\Discussion;
class DiscussionSerializer extends DiscussionBasicSerializer class DiscussionSerializer extends DiscussionBasicSerializer
{ {
/** /**
* Serialize attributes of a Discussion model for JSON output. * {@inheritdoc}
*
* @param Discussion $discussion The Discussion model to serialize.
* @return array
*/ */
protected function attributes($discussion) protected function getDefaultAttributes($discussion)
{ {
$attributes = parent::attributes($discussion); $attributes = parent::getDefaultAttributes($discussion) + [
$user = $this->actor->getUser();
$state = $discussion->state;
$attributes += [
'commentsCount' => (int) $discussion->comments_count, 'commentsCount' => (int) $discussion->comments_count,
'participantsCount' => (int) $discussion->participants_count, 'participantsCount' => (int) $discussion->participants_count,
'startTime' => $discussion->start_time->toRFC3339String(), 'startTime' => $discussion->start_time->toRFC3339String(),
'lastTime' => $discussion->last_time ? $discussion->last_time->toRFC3339String() : null, 'lastTime' => $discussion->last_time ? $discussion->last_time->toRFC3339String() : null,
'lastPostNumber' => $discussion->last_post_number, 'lastPostNumber' => $discussion->last_post_number,
'canReply' => $discussion->can($user, 'reply'), 'canReply' => $discussion->can($this->actor, 'reply'),
'canRename' => $discussion->can($user, 'rename'), 'canRename' => $discussion->can($this->actor, 'rename'),
'canDelete' => $discussion->can($user, 'delete'), 'canDelete' => $discussion->can($this->actor, 'delete')
'readTime' => $state && $state->read_time ? $state->read_time->toRFC3339String() : null,
'readNumber' => $state ? (int) $state->read_number : 0
]; ];
return $this->extendAttributes($discussion, $attributes); Discussion::setStateUser($this->actor);
if ($state = $discussion->state) {
$attributes += [
'readTime' => $state->read_time ? $state->read_time->toRFC3339String() : null,
'readNumber' => (int) $state->read_number
];
}
return $attributes;
} }
} }

View File

@ -1,31 +1,27 @@
<?php namespace Flarum\Api\Serializers; <?php namespace Flarum\Api\Serializers;
class ForumSerializer extends BaseSerializer class ForumSerializer extends Serializer
{ {
/** /**
* The resource type. * {@inheritdoc}
*
* @var string
*/ */
protected $type = 'forums'; protected $type = 'forums';
protected function id($forum) /**
* {@inheritdoc}
*/
protected function getId($forum)
{ {
return 1; return 1;
} }
/** /**
* Serialize attributes of a Forum model for JSON output. * {@inheritdoc}
*
* @param Forum $forum The Forum model to serialize.
* @return array
*/ */
protected function attributes($forum) protected function getDefaultAttributes($forum)
{ {
$attributes = [ return [
'title' => $forum->title 'title' => $forum->title
]; ];
return $this->extendAttributes($forum, $attributes);
} }
} }

View File

@ -1,30 +1,23 @@
<?php namespace Flarum\Api\Serializers; <?php namespace Flarum\Api\Serializers;
class GroupSerializer extends BaseSerializer class GroupSerializer extends Serializer
{ {
/** /**
* The resource type. * {@inheritdoc}
*
* @var string
*/ */
protected $type = 'groups'; protected $type = 'groups';
/** /**
* Serialize attributes of a Group model for JSON output. * {@inheritdoc}
*
* @param Group $group The Group model to serialize.
* @return array
*/ */
protected function attributes($group) protected function getDefaultAttributes($group)
{ {
$attributes = [ return [
'id' => (int) $group->id, 'id' => (int) $group->id,
'nameSingular' => $group->name_singular, 'nameSingular' => $group->name_singular,
'namePlural' => $group->name_plural, 'namePlural' => $group->name_plural,
'color' => $group->color, 'color' => $group->color,
'icon' => $group->icon, 'icon' => $group->icon,
]; ];
return $this->extendAttributes($group, $attributes);
} }
} }

View File

@ -1,11 +1,9 @@
<?php namespace Flarum\Api\Serializers; <?php namespace Flarum\Api\Serializers;
class NotificationSerializer extends BaseSerializer class NotificationSerializer extends Serializer
{ {
/** /**
* The resource type. * {@inheritdoc}
*
* @var string
*/ */
protected $type = 'notifications'; protected $type = 'notifications';
@ -15,19 +13,14 @@ class NotificationSerializer extends BaseSerializer
* *
* @var array * @var array
*/ */
public static $subjects = [ protected static $subjectSerializers = [];
'discussionRenamed' => 'Flarum\Api\Serializers\DiscussionBasicSerializer'
];
/** /**
* Serialize attributes of an notification model for JSON output. * {@inheritdoc}
*
* @param Notification $notification The notification model to serialize.
* @return array
*/ */
protected function attributes($notification) protected function getDefaultAttributes($notification)
{ {
$attributes = [ return [
'id' => (int) $notification->id, 'id' => (int) $notification->id,
'contentType' => $notification->type, 'contentType' => $notification->type,
'content' => $notification->data, 'content' => $notification->data,
@ -35,24 +28,40 @@ class NotificationSerializer extends BaseSerializer
'isRead' => (bool) $notification->is_read, 'isRead' => (bool) $notification->is_read,
'unreadCount' => $notification->unread_count 'unreadCount' => $notification->unread_count
]; ];
return $this->extendAttributes($notification, $attributes);
} }
/**
* @return callable
*/
public function user() public function user()
{ {
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer'); return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
} }
/**
* @return callable
*/
public function sender() public function sender()
{ {
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer'); return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
} }
/**
* @return callable
*/
public function subject() public function subject()
{ {
return $this->hasOne(function ($notification) { return $this->hasOne(function ($notification) {
return static::$subjects[$notification->type]; return static::$subjectSerializers[$notification->type];
}); });
} }
/**
* @param $type
* @param $serializer
*/
public static function setSubjectSerializer($type, $serializer)
{
static::$subjectSerializers[$type] = $serializer;
}
} }

View File

@ -1,23 +1,16 @@
<?php namespace Flarum\Api\Serializers; <?php namespace Flarum\Api\Serializers;
class PostBasicSerializer extends BaseSerializer class PostBasicSerializer extends Serializer
{ {
protected static $relationships = [];
/** /**
* The resource type. * {@inheritdoc}
*
* @var string
*/ */
protected $type = 'posts'; protected $type = 'posts';
/** /**
* Serialize attributes of a Post model for JSON output. * {@inheritdoc}
*
* @param Post $post The Post model to serialize.
* @return array
*/ */
protected function attributes($post) protected function getDefaultAttributes($post)
{ {
$attributes = [ $attributes = [
'id' => (int) $post->id, 'id' => (int) $post->id,
@ -32,14 +25,20 @@ class PostBasicSerializer extends BaseSerializer
$attributes['content'] = $post->content; $attributes['content'] = $post->content;
} }
return $this->extendAttributes($post, $attributes); return $attributes;
} }
/**
* @return callable
*/
public function user() public function user()
{ {
return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer'); return $this->hasOne('Flarum\Api\Serializers\UserBasicSerializer');
} }
/**
* @return callable
*/
public function discussion() public function discussion()
{ {
return $this->hasOne('Flarum\Api\Serializers\DiscussionBasicSerializer'); return $this->hasOne('Flarum\Api\Serializers\DiscussionBasicSerializer');

View File

@ -3,22 +3,19 @@
class PostSerializer extends PostBasicSerializer class PostSerializer extends PostBasicSerializer
{ {
/** /**
* Serialize attributes of a Post model for JSON output. * {@inheritdoc}
*
* @param Post $post The Post model to serialize.
* @return array
*/ */
protected function attributes($post) protected function getDefaultAttributes($post)
{ {
$attributes = parent::attributes($post); $attributes = parent::getDefaultAttributes($post);
$user = $this->actor->getUser();
unset($attributes['content']); unset($attributes['content']);
$canEdit = $post->can($user, 'edit'); $canEdit = $post->can($this->actor, 'edit');
if ($post->type === 'comment') { if ($post->type === 'comment') {
$attributes['contentHtml'] = $post->content_html; $attributes['contentHtml'] = $post->content_html;
if ($canEdit) { if ($canEdit) {
$attributes['content'] = $post->content; $attributes['content'] = $post->content;
} }
@ -37,27 +34,39 @@ class PostSerializer extends PostBasicSerializer
$attributes += [ $attributes += [
'canEdit' => $canEdit, 'canEdit' => $canEdit,
'canDelete' => $post->can($user, 'delete') 'canDelete' => $post->can($this->actor, 'delete')
]; ];
return $this->extendAttributes($post, $attributes); return $attributes;
} }
/**
* @return callable
*/
public function user() public function user()
{ {
return $this->hasOne('Flarum\Api\Serializers\UserSerializer'); return $this->hasOne('Flarum\Api\Serializers\UserSerializer');
} }
/**
* @return callable
*/
public function discussion() public function discussion()
{ {
return $this->hasOne('Flarum\Api\Serializers\DiscussionSerializer'); return $this->hasOne('Flarum\Api\Serializers\DiscussionSerializer');
} }
/**
* @return callable
*/
public function editUser() public function editUser()
{ {
return $this->hasOne('Flarum\Api\Serializers\UserSerializer'); return $this->hasOne('Flarum\Api\Serializers\UserSerializer');
} }
/**
* @return callable
*/
public function hideUser() public function hideUser()
{ {
return $this->hasOne('Flarum\Api\Serializers\UserSerializer'); return $this->hasOne('Flarum\Api\Serializers\UserSerializer');

View File

@ -0,0 +1,178 @@
<?php namespace Flarum\Api\Serializers;
use BadMethodCallException;
use Closure;
use Flarum\Core\Users\User;
use Tobscure\JsonApi\SerializerAbstract;
use Flarum\Api\Events\SerializeAttributes;
abstract class Serializer extends SerializerAbstract
{
/**
* @var User
*/
public $actor;
/**
* An array of custom relation methods, grouped by subclass.
*
* @var array
*/
protected static $relationMethods = [];
/**
* @param User $actor
* @param array|null $include
* @param array|null $link
*/
public function __construct(User $actor, $include = null, $link = null)
{
parent::__construct($include, $link);
$this->actor = $actor;
}
/**
* {@inheritdoc}
*/
protected function getAttributes($model)
{
$attributes = $this->getDefaultAttributes($model);
event(new SerializeAttributes($this, $model, $attributes));
return $attributes;
}
/**
* Get the default set of serialized attributes for a model.
*
* @param $model
* @return array
*/
abstract protected function getDefaultAttributes($model);
/**
* Get a closure that returns a Collection/Resource representing a relation.
*
* @param string|Closure $serializer The name of the serializer, or a
* Closure returning the name of the serializer, to use for the related
* items.
* @param string|Closure|null $relation If a string is provided, it will be
* used to retrieve the relation data from the model:
* - If the relation is being included, the relation will be accessed
* as a property on the model.
* - If the relation is not being included and is a to-many relation, a
* list of IDs will be accessed as a property on the model with the
* suffix '_ids', otherwise by querying the relation method.
* - If the relation is not being included and is a to-one relation,
* the ID will be accessed as a property on the model with the suffix
* '_id'.
* If a closure is provided, it will be passed the model and
* whether or not the relation is being included. It is expected to
* return the relation data.
* @param bool $many Whether or not this is a to-many relation.
* @return callable
*/
protected function getRelationship($serializer, $relation = null, $many = false)
{
// If no relationship name was provided, we can guess it from the
// stack trace. The assumes that one of the hasOne or hasMany methods
// was called from directly inside a serializer method.
if (is_null($relation)) {
list(, , $caller) = debug_backtrace(false, 3);
$relation = $caller['function'];
}
return function ($model, $include, $included, $links) use ($serializer, $many, $relation) {
// If the passed relation was a closure, we can let that take care
// of retrieving the relation data from the model. Otherwise, we
// need to get the data from the model itself, using the relation
// name provided.
if ($relation instanceof Closure) {
$data = $relation($model, $include);
} else {
if ($include) {
$data = $model->$relation;
} elseif ($many) {
$relationIds = $relation.'_ids';
$data = isset($model->$relationIds) ? $model->$relationIds : $model->$relation()->lists('id');
} else {
$relationId = $relation.'_id';
$data = $model->$relationId;
}
}
// If the passed serializer was a closure, we'll need to run
// that in order to find out which serializer class to instantiate.
// This is useful for polymorphic relations.
if ($serializer instanceof Closure) {
$serializer = $serializer($model, $data);
}
/** @var \Tobscure\JsonApi\SerializerInterface $serializer */
$serializer = new $serializer($this->actor, $included, $links);
return $many ? $serializer->collection($data) : $serializer->resource($data);
};
}
/**
* Get a closure that returns a Resource representing a relation.
*
* @param string $serializer
* @param string|Closure|null $relation
* @see Serializer::getRelationship()
* @return callable
*/
public function hasOne($serializer, $relation = null)
{
return $this->getRelationship($serializer, $relation);
}
/**
* Get a closure that returns a Collection representing a relation.
*
* @param string $serializer
* @param string|Closure|null $relation
* @see Serializer::getRelationship()
* @return callable
*/
public function hasMany($serializer, $relation = null)
{
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;
}
/**
* Check for and execute custom relationships.
*
* @param string $method
* @param array $parameters
* @return mixed
* @throws BadMethodCallException
*/
public function __call($method, $parameters)
{
if (isset(static::$relationMethods[$method])) {
array_unshift($parameters, $this);
return call_user_func_array(static::$relationMethods[$method], $parameters);
}
$className = get_class($this);
throw new BadMethodCallException("Call to undefined method {$className}::{$method}()");
}
}

View File

@ -1,30 +1,26 @@
<?php namespace Flarum\Api\Serializers; <?php namespace Flarum\Api\Serializers;
class UserBasicSerializer extends BaseSerializer class UserBasicSerializer extends Serializer
{ {
/** /**
* The resource type. * {@inheritdoc}
*
* @var string
*/ */
protected $type = 'users'; protected $type = 'users';
/** /**
* Serialize attributes of a User model for JSON output. * {@inheritdoc}
*
* @param User $user The User model to serialize.
* @return array
*/ */
protected function attributes($user) protected function getDefaultAttributes($user)
{ {
$attributes = [ return [
'username' => $user->username, 'username' => $user->username,
'avatarUrl' => $user->avatar_url 'avatarUrl' => $user->avatar_url
]; ];
return $this->extendAttributes($user, $attributes);
} }
/**
* @return callable
*/
protected function groups() protected function groups()
{ {
return $this->hasMany('Flarum\Api\Serializers\GroupSerializer'); return $this->hasMany('Flarum\Api\Serializers\GroupSerializer');

View File

@ -3,17 +3,13 @@
class UserSerializer extends UserBasicSerializer class UserSerializer extends UserBasicSerializer
{ {
/** /**
* Serialize attributes of a User model for JSON output. * {@inheritdoc}
*
* @param User $user The User model to serialize.
* @return array
*/ */
protected function attributes($user) protected function getDefaultAttributes($user)
{ {
$attributes = parent::attributes($user); $attributes = parent::getDefaultAttributes($user);
$actingUser = $this->actor->getUser(); $canEdit = $user->can($this->actor, 'edit');
$canEdit = $user->can($actingUser, 'edit');
$attributes += [ $attributes += [
'bioHtml' => $user->bio_html, 'bioHtml' => $user->bio_html,
@ -21,10 +17,10 @@ class UserSerializer extends UserBasicSerializer
'discussionsCount' => (int) $user->discussions_count, 'discussionsCount' => (int) $user->discussions_count,
'commentsCount' => (int) $user->comments_count, 'commentsCount' => (int) $user->comments_count,
'canEdit' => $canEdit, 'canEdit' => $canEdit,
'canDelete' => $user->can($actingUser, 'delete'), 'canDelete' => $user->can($this->actor, 'delete'),
]; ];
if ($user->preference('discloseOnline')) { if ($user->getPreference('discloseOnline')) {
$attributes += [ $attributes += [
'lastSeenTime' => $user->last_seen_time ? $user->last_seen_time->toRFC3339String() : null 'lastSeenTime' => $user->last_seen_time ? $user->last_seen_time->toRFC3339String() : null
]; ];
@ -39,6 +35,6 @@ class UserSerializer extends UserBasicSerializer
]; ];
} }
return $this->extendAttributes($user, $attributes); return $attributes;
} }
} }

View File

@ -1,6 +1,6 @@
<?php namespace Flarum\Assets; <?php namespace Flarum\Assets;
use RuntimeException; use DomainException;
class AssetManager class AssetManager
{ {
@ -29,7 +29,7 @@ class AssetManager
break; break;
default: default:
throw new RuntimeException('Unsupported asset type: '.$ext); throw new DomainException('Unsupported asset type: '.$ext);
} }
} }

View File

@ -13,6 +13,7 @@ class ConsoleServiceProvider extends ServiceProvider
{ {
$this->commands('Flarum\Console\InstallCommand'); $this->commands('Flarum\Console\InstallCommand');
$this->commands('Flarum\Console\SeedCommand'); $this->commands('Flarum\Console\SeedCommand');
$this->commands('Flarum\Console\ImportCommand');
$this->commands('Flarum\Console\GenerateExtensionCommand'); $this->commands('Flarum\Console\GenerateExtensionCommand');
} }

View File

@ -0,0 +1,278 @@
<?php namespace Flarum\Console;
use Illuminate\Console\Command;
use Illuminate\Foundation\Application;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Flarum\Core\Exceptions\ValidationFailureException;
use Flarum\Core\Models\User;
use Flarum\Core\Models\Discussion;
use Flarum\Core\Models\DiscussionState;
use Flarum\Core\Models\CommentPost;
use Flarum\Tags\Tag;
use Flarum\Core\Events\PostWasPosted;
use Flarum\Core\Events\UserWasRegistered;
use Flarum\Core\Activity\JoinedActivity;
use Symfony\Component\Console\Helper\ProgressBar;
class ImportCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'flarum:import';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Import from esoTalk.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(Application $app)
{
parent::__construct();
$this->app = $app;
}
/**
* Execute the console command.
*
* @return mixed
*/
public function fire()
{
if (!$this->confirm('Warning: all Flarum tables will be truncated. Proceed? [y|N]', false)) {
return;
}
app('config')->set('database.connections.esotalk', [
'driver' => 'mysql',
'host' => env('DB_HOST'),
'database' => 'esotalk',
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => 'et_',
'strict' => false,
]);
User::$rules = [];
$from = app('db')->connection('esotalk');
$to = app('db')->connection();
$this->importTags($from, $to);
$this->importUsers($from, $to);
$this->importDiscussions($from, $to);
$to->table('notifications')->update(['is_read' => true]);
}
protected function importTags($from, $to)
{
$colors = ['#F16655', '#F59B66', '#4E89DA', '#5AC169', '#96A2AF'];
$this->info('Importing tags...');
$to->table('tags')->truncate();
$channels = $from->table('channel')->orderBy('lft')->get();
$progress = new ProgressBar($this->output, count($channels));
$i = 0;
foreach ($channels as $c) {
$tag = new Tag;
$tag->id = $c->channelId;
$tag->name = $c->title;
$tag->slug = $c->slug;
$tag->description = $c->description;
$tag->color = $colors[$i++ % count($colors)];
$tag->discussions_count = $c->countConversations;
$tag->position = $c->lft;
$tag->save();
$progress->advance();
}
$progress->finish();
$this->info("\n");
}
protected function importUsers($from, $to)
{
$this->info('Importing users...');
$to->table('users')->truncate();
$to->table('email_tokens')->truncate();
$to->table('password_tokens')->truncate();
$to->table('access_tokens')->truncate();
$to->table('users_groups')->truncate();
$members = $from->table('member')->get();
$progress = new ProgressBar($this->output, count($members));
foreach ($members as $m) {
$preferences = unserialize($m->preferences);
$user = new User;
$user->id = $m->memberId;
$user->username = $m->username;
$user->email = $m->email;
$user->is_activated = true;
$user->password = '';
$user->join_time = $m->joinTime;
$user->last_seen_time = $m->lastActionTime;
$user->avatar_path = $m->avatarFormat ? $m->memberId.'.'.$m->avatarFormat : null;
$user->username = $m->username;
$user->read_time = array_get($preferences, 'markedAllConversationsAsRead');
$user->notification_read_time = array_get($preferences, 'notificationCheckTime');
$user->preferences = ['discloseOnline' => !array_get($preferences, 'hideOnline')];
$user->discussions_count = $m->countConversations;
$user->comments_count = $m->countPosts;
$user->save();
$this->app->make('Flarum\Core\Activity\ActivitySyncer')
->sync(new JoinedActivity($user), [$user]);
$progress->advance();
}
$progress->finish();
$this->info("\n");
}
protected function importDiscussions($from, $to)
{
$this->info('Importing discussions...');
$to->table('discussions')->truncate();
$to->table('discussions_tags')->truncate();
$to->table('posts')->truncate();
$to->table('notifications')->truncate();
$to->table('users_discussions')->truncate();
$to->table('activity')->truncate();
$to->table('mentions_posts')->truncate();
$to->table('mentions_users')->truncate();
$conversations = $from->table('conversation')->where('private', 0)->get();
$progress = new ProgressBar($this->output, count($conversations));
foreach ($conversations as $c) {
$discussion = new Discussion;
$discussion->id = $c->conversationId;
$discussion->title = $c->title;
$discussion->is_sticky = $c->sticky;
$discussion->start_user_id = $c->startMemberId;
$discussion->start_time = $c->startTime;
$discussion->last_user_id = $c->lastPostMemberId;
$discussion->last_time = $c->lastPostTime;
$discussion->save();
$discussion->tags()->sync([$c->channelId]);
foreach ($from->table('post')->where('conversationId', $c->conversationId)->get() as $p) {
$post = new CommentPost;
$post->id = $p->postId;
$post->discussion_id = $p->conversationId;
$post->user_id = $p->memberId;
$post->time = $p->time;
$post->edit_user_id = $p->editMemberId;
$post->edit_time = $p->editTime;
$post->hide_user_id = $p->deleteMemberId;
$post->hide_time = $p->deleteTime;
$post->content = $p->content;
$this->formatPost($post);
$post->save();
if (!$post->hide_time) {
event(new PostWasPosted($post));
}
}
$discussion->last_post_id = $p->postId;
$discussion->last_post_number = $post->number;
$discussion->comments_count = $post->number;
$discussion->save();
$states = $from->table('member_conversation')
->where('conversationId', $c->conversationId)
->where('type', 'member')
->get();
foreach ($states as $s) {
$state = new DiscussionState;
$state->discussion_id = $s->conversationId;
$state->user_id = $s->id;
$state->read_time = time();
$state->read_number = $discussion->posts()->orderBy('time', 'asc')->skip(min($discussion->comments_count, $s->lastRead) - 1)->pluck('number');
$state->save();
}
$progress->advance();
}
$progress->finish();
$this->info("\n");
}
protected function formatPost($post)
{
// Code blocks
$regexp = "/(.*)^\s*\[code\]\n?(.*?)\n?\[\/code]$/ims";
while (preg_match($regexp, $post->content)) {
$post->content = preg_replace($regexp, "$1```\n$2\n```", $post->content);
}
// Inline tags
$replace = [
'/\[url=(.*?)\](.*?)\[\/url\]/i' => '[$2]($1)',
'/\[b\](.*?)\[\/b\]/i' => '**$1**',
'/\[i\](.*?)\[\/i\]/i' => '*$1*',
'/\[h\](.*?)\[\/h\]/i' => '# $1',
'/\[img\](.*?)\[\/img\]/i' => '![]($1)',
'/\[code\](.*?)\[\/code\]/i' => '`$1`'
];
$post->content = preg_replace(array_keys($replace), array_values($replace), $post->content);
// Quotes
$regexp = "/(.*?)\n?\[quote(?:=(.*?)(]?))?\]\n?(.*?)\n?\[\/quote\]\n{0,2}/is";
while (preg_match($regexp, $post->content)) {
$post->content = preg_replace_callback($regexp, function ($matches) use ($post) {
if (strpos($matches[2], ':') !== false) {
list($postId, $user) = explode(':', $matches[2]);
$mentionedPost = CommentPost::find($postId);
return $matches[1]."\n@".$mentionedPost->user->username.'#'.$mentionedPost->number.' ';
} else {
return $matches[1].'> '.str_replace("\n", "\n> ", $matches[4])."\n\n";
}
}, $post->content);
}
}
}

View File

@ -1,4 +1,6 @@
<?php namespace Flarum\Core\Models; <?php namespace Flarum\Core\Activity;
use Flarum\Core\Model;
/** /**
* Models a user activity record in the database. * Models a user activity record in the database.
@ -16,23 +18,19 @@
class Activity extends Model class Activity extends Model
{ {
/** /**
* The table associated with the model. * {@inheritdoc}
*
* @var string
*/ */
protected $table = 'activity'; protected $table = 'activity';
/** /**
* The attributes that should be mutated to dates. * {@inheritdoc}
*
* @var array
*/ */
protected $dates = ['time']; protected static $dateAttributes = ['time'];
/** /**
* A map of activity types and the model classes to use for their subjects. * A map of activity types and the model classes to use for their subjects.
* For example, the 'posted' activity type, which represents that a user * For example, the 'posted' activity type, which represents that a user
* made a post, has the subject model class 'Flarum\Core\Models\Post'. * made a post, has the subject model class 'Flarum\Core\Posts\Post'.
* *
* @var array * @var array
*/ */
@ -79,7 +77,7 @@ class Activity extends Model
*/ */
public function user() public function user()
{ {
return $this->belongsTo('Flarum\Core\Models\User', 'user_id'); return $this->belongsTo('Flarum\Core\Users\User', 'user_id');
} }
/** /**
@ -106,7 +104,8 @@ class Activity extends Model
* Set the subject model for the given activity type. * Set the subject model for the given activity type.
* *
* @param string $type The activity type. * @param string $type The activity type.
* @param string $class The class name of the subject model for that type. * @param string $subjectModel The class name of the subject model for that
* type.
* @return void * @return void
*/ */
public static function setSubjectModel($type, $subjectModel) public static function setSubjectModel($type, $subjectModel)

View File

@ -1,5 +0,0 @@
<?php namespace Flarum\Core\Activity;
abstract class ActivityAbstract implements ActivityInterface
{
}

View File

@ -0,0 +1,18 @@
<?php namespace Flarum\Core\Activity;
use Flarum\Core\Users\User;
interface ActivityRepositoryInterface
{
/**
* Find a user's activity.
*
* @param integer $userId
* @param \Flarum\Core\Users\User $actor
* @param null|integer $count
* @param integer $start
* @param null|string $type
* @return \Illuminate\Database\Eloquent\Collection
*/
public function findByUser($userId, User $actor, $count = null, $start = 0, $type = null);
}

View File

@ -13,24 +13,29 @@ class ActivityServiceProvider extends ServiceProvider
public function boot() public function boot()
{ {
$this->extend([ $this->extend([
(new Extend\EventSubscriber('Flarum\Core\Handlers\Events\UserActivitySyncer')), (new Extend\EventSubscriber('Flarum\Core\Activity\Listeners\UserActivitySyncer')),
(new Extend\ActivityType('Flarum\Core\Activity\PostedActivity')) (new Extend\ActivityType('Flarum\Core\Activity\PostedBlueprint'))
->subjectSerializer('Flarum\Api\Serializers\PostBasicSerializer'), ->subjectSerializer('Flarum\Api\Serializers\PostBasicSerializer'),
(new Extend\ActivityType('Flarum\Core\Activity\StartedDiscussionActivity')) (new Extend\ActivityType('Flarum\Core\Activity\StartedDiscussionBlueprint'))
->subjectSerializer('Flarum\Api\Serializers\PostBasicSerializer'), ->subjectSerializer('Flarum\Api\Serializers\PostBasicSerializer'),
(new Extend\ActivityType('Flarum\Core\Activity\JoinedActivity')) (new Extend\ActivityType('Flarum\Core\Activity\JoinedBlueprint'))
->subjectSerializer('Flarum\Api\Serializers\UserBasicSerializer') ->subjectSerializer('Flarum\Api\Serializers\UserBasicSerializer')
]); ]);
} }
/**
* Register the service provider.
*
* @return void
*/
public function register() public function register()
{ {
$this->app->bind( $this->app->bind(
'Flarum\Core\Repositories\ActivityRepositoryInterface', 'Flarum\Core\Activity\ActivityRepositoryInterface',
'Flarum\Core\Repositories\EloquentActivityRepository' 'Flarum\Core\Activity\EloquentActivityRepository'
); );
} }
} }

View File

@ -1,12 +1,22 @@
<?php namespace Flarum\Core\Activity; <?php namespace Flarum\Core\Activity;
use Flarum\Core\Repositories\ActivityRepositoryInterface; /**
use Flarum\Core\Models\Activity; * The Activity Syncer commits activity blueprints to the database. Where a
* blueprint represents a single piece of activity, the syncer associates it
* with a particular user(s) and makes it available on their activity feed.
*/
class ActivitySyncer class ActivitySyncer
{ {
/**
* @var ActivityRepositoryInterface
*/
protected $activity; protected $activity;
/**
* Create a new instance of the activity syncer.
*
* @param ActivityRepositoryInterface $activity
*/
public function __construct(ActivityRepositoryInterface $activity) public function __construct(ActivityRepositoryInterface $activity)
{ {
$this->activity = $activity; $this->activity = $activity;
@ -16,38 +26,92 @@ class ActivitySyncer
* Sync a piece of activity so that it is present for the specified users, * Sync a piece of activity so that it is present for the specified users,
* and not present for anyone else. * and not present for anyone else.
* *
* @param \Flarum\Core\Activity\ActivityInterface $activity * @param Blueprint $blueprint
* @param \Flarum\Core\Models\User[] $users * @param \Flarum\Core\Models\User[] $users
* @return void * @return void
*/ */
public function sync(ActivityInterface $activity, array $users) public function sync(Blueprint $blueprint, array $users)
{ {
Activity::unguard(); $attributes = $this->getAttributes($blueprint);
$attributes = [
'type' => $activity::getType(),
'subject_id' => $activity->getSubject()->id,
'time' => $activity->getTime()
];
// Find all existing activity records in the database matching this
// blueprint. We will begin by assuming that they all need to be
// deleted in order to match the provided list of users.
$toDelete = Activity::where($attributes)->get(); $toDelete = Activity::where($attributes)->get();
$toInsert = []; $toInsert = [];
// For each of the provided users, check to see if they already have
// an activity record in the database. If they do, we can leave it be;
// otherwise, we will need to create a new one for them.
foreach ($users as $user) { foreach ($users as $user) {
$existing = $toDelete->where('user_id', $user->id)->first(); $existing = $toDelete->first(function ($activity) use ($user) {
return $activity->user_id === $user->id;
});
if ($k = $toDelete->search($existing)) { if ($existing) {
$toDelete->pull($k); $toDelete->forget($toDelete->search($existing));
} else { } else {
$toInsert[] = $attributes + ['user_id' => $user->id]; $toInsert[] = $attributes + ['user_id' => $user->id];
} }
} }
// Finally, delete all of the remaining activity records which weren't
// removed from this collection by the above loop. Insert the records
// we need to insert as well.
if (count($toDelete)) { if (count($toDelete)) {
Activity::whereIn('id', $toDelete->lists('id'))->delete(); $this->deleteActivity($toDelete->lists('id'));
} }
if (count($toInsert)) { if (count($toInsert)) {
Activity::insert($toInsert); $this->createActivity($toInsert);
} }
} }
/**
* Delete a piece of activity for all users.
*
* @param Blueprint $blueprint
* @return void
*/
public function delete(Blueprint $blueprint)
{
Activity::where($this->getAttributes($blueprint))->delete();
}
/**
* Delete a list of activity records.
*
* @param int[] $ids
*/
protected function deleteActivity(array $ids)
{
Activity::whereIn('id', $ids)->delete();
}
/**
* Insert a list of activity record into the database.
*
* @param array[] $records An array containing arrays of activity record
* attributes to insert.
*/
protected function createActivity(array $records)
{
Activity::insert($records);
}
/**
* Construct an array of attributes to be stored in an activity record in
* the database, given an activity blueprint.
*
* @param Blueprint $blueprint
* @return array
*/
protected function getAttributes(Blueprint $blueprint)
{
return [
'type' => $blueprint::getType(),
'subject_id' => $blueprint->getSubject()->id,
'time' => $blueprint->getTime()
];
}
} }

View File

@ -1,11 +1,16 @@
<?php namespace Flarum\Core\Activity; <?php namespace Flarum\Core\Activity;
interface ActivityInterface /**
* An activity Blueprint, when instantiated, represents a single piece of
* activity. The blueprint is used by the ActivitySyncer to commit the activity
* to the database.
*/
interface Blueprint
{ {
/** /**
* Get the model that is the subject of this activity. * Get the model that is the subject of this activity.
* *
* @return \Flarum\Core\Models\Model * @return \Flarum\Core\Model
*/ */
public function getSubject(); public function getSubject();

View File

@ -0,0 +1,35 @@
<?php namespace Flarum\Core\Activity;
use Flarum\Core\Users\User;
class EloquentActivityRepository implements ActivityRepositoryInterface
{
/**
* {@inheritdoc}
*/
public function findByUser($userId, User $actor, $limit = null, $offset = 0, $type = null)
{
$query = Activity::where('user_id', $userId)
->whereIn('type', $this->getRegisteredTypes())
->latest('time')
->skip($offset)
->take($limit);
if ($type !== null) {
$query->where('type', $type);
}
return $query->get();
}
/**
* Get a list of activity types that have been registered with the activity
* model.
*
* @return array
*/
protected function getRegisteredTypes()
{
return array_keys(Activity::getSubjectModels());
}
}

View File

@ -1,33 +0,0 @@
<?php namespace Flarum\Core\Activity;
use Flarum\Core\Models\User;
class JoinedActivity extends ActivityAbstract
{
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function getSubject()
{
return $this->user;
}
public function getTime()
{
return $this->user->join_time;
}
public static function getType()
{
return 'joined';
}
public static function getSubjectModel()
{
return 'Flarum\Core\Models\User';
}
}

View File

@ -0,0 +1,59 @@
<?php namespace Flarum\Core\Activity;
use Flarum\Core\Users\User;
/**
* An activity blueprint for the 'joined' activity type, which represents a user
* joining the forum.
*/
class JoinedBlueprint implements Blueprint
{
/**
* The user who joined the forum.
*
* @var User
*/
protected $user;
/**
* Create a new 'joined' activity blueprint.
*
* @param User $user The user who joined the forum.
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* {@inheritdoc}
*/
public function getSubject()
{
return $this->user;
}
/**
* {@inheritdoc}
*/
public function getTime()
{
return $this->user->join_time;
}
/**
* {@inheritdoc}
*/
public static function getType()
{
return 'joined';
}
/**
* {@inheritdoc}
*/
public static function getSubjectModel()
{
return 'Flarum\Core\Users\User';
}
}

View File

@ -0,0 +1,126 @@
<?php namespace Flarum\Core\Activity\Listeners;
use Flarum\Core\Activity\ActivitySyncer;
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 Illuminate\Contracts\Events\Dispatcher;
class UserActivitySyncer
{
/**
* @var \Flarum\Core\Activity\ActivitySyncer
*/
protected $activity;
/**
* @param \Flarum\Core\Activity\ActivitySyncer $activity
*/
public function __construct(ActivitySyncer $activity)
{
$this->activity = $activity;
}
/**
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @return void
*/
public function subscribe(Dispatcher $events)
{
$events->listen('Flarum\Core\Events\PostWasPosted', __CLASS__.'@whenPostWasPosted');
$events->listen('Flarum\Core\Events\PostWasHidden', __CLASS__.'@whenPostWasHidden');
$events->listen('Flarum\Core\Events\PostWasRestored', __CLASS__.'@whenPostWasRestored');
$events->listen('Flarum\Core\Events\PostWasDeleted', __CLASS__.'@whenPostWasDeleted');
$events->listen('Flarum\Core\Events\UserWasRegistered', __CLASS__.'@whenUserWasRegistered');
}
/**
* @param \Flarum\Core\Posts\Events\PostWasPosted $event
* @return void
*/
public function whenPostWasPosted(PostWasPosted $event)
{
$this->postBecameVisible($event->post);
}
/**
* @param \Flarum\Core\Posts\Events\PostWasHidden $event
* @return void
*/
public function whenPostWasHidden(PostWasHidden $event)
{
$this->postBecameInvisible($event->post);
}
/**
* @param \Flarum\Core\Posts\Events\PostWasRestored $event
* @return void
*/
public function whenPostWasRestored(PostWasRestored $event)
{
$this->postBecameVisible($event->post);
}
/**
* @param \Flarum\Core\Posts\Events\PostWasDeleted $event
* @return void
*/
public function whenPostWasDeleted(PostWasDeleted $event)
{
$this->postBecameInvisible($event->post);
}
/**
* @param \Flarum\Core\Users\Events\UserWasRegistered $event
* @return void
*/
public function whenUserWasRegistered(UserWasRegistered $event)
{
$blueprint = new JoinedBlueprint($event->user);
$this->activity->sync($blueprint, [$event->user]);
}
/**
* Sync activity to a post's author when a post becomes visible.
*
* @param \Flarum\Core\Posts\Post $post
* @return void
*/
protected function postBecameVisible(Post $post)
{
$blueprint = $this->postedBlueprint($post);
$this->activity->sync($blueprint, [$post->user]);
}
/**
* Delete activity when a post becomes invisible.
*
* @param \Flarum\Core\Posts\Post $post
* @return void
*/
protected function postBecameInvisible(Post $post)
{
$blueprint = $this->postedBlueprint($post);
$this->activity->delete($blueprint);
}
/**
* Create the appropriate activity blueprint for a post.
*
* @param \Flarum\Core\Posts\Post $post
* @return \Flarum\Core\Activity\Blueprint
*/
protected function postedBlueprint(Post $post)
{
return $post->number == 1 ? new StartedDiscussionBlueprint($post) : new PostedBlueprint($post);
}
}

View File

@ -1,33 +0,0 @@
<?php namespace Flarum\Core\Activity;
use Flarum\Core\Models\Post;
class PostedActivity extends ActivityAbstract
{
protected $post;
public function __construct(Post $post)
{
$this->post = $post;
}
public function getSubject()
{
return $this->post;
}
public function getTime()
{
return $this->post->time;
}
public static function getType()
{
return 'posted';
}
public static function getSubjectModel()
{
return 'Flarum\Core\Models\Post';
}
}

View File

@ -0,0 +1,59 @@
<?php namespace Flarum\Core\Activity;
use Flarum\Core\Posts\Post;
/**
* An activity blueprint for the 'posted' activity type, which represents a user
* posting in a discussion.
*/
class PostedBlueprint implements Blueprint
{
/**
* The user who joined the forum.
*
* @var Post
*/
protected $post;
/**
* Create a new 'posted' activity blueprint.
*
* @param Post $post The post that was made.
*/
public function __construct(Post $post)
{
$this->post = $post;
}
/**
* {@inheritdoc}
*/
public function getSubject()
{
return $this->post;
}
/**
* {@inheritdoc}
*/
public function getTime()
{
return $this->post->time;
}
/**
* {@inheritdoc}
*/
public static function getType()
{
return 'posted';
}
/**
* {@inheritdoc}
*/
public static function getSubjectModel()
{
return 'Flarum\Core\Posts\Post';
}
}

View File

@ -1,9 +0,0 @@
<?php namespace Flarum\Core\Activity;
class StartedDiscussionActivity extends PostedActivity
{
public static function getType()
{
return 'startedDiscussion';
}
}

View File

@ -0,0 +1,16 @@
<?php namespace Flarum\Core\Activity;
/**
* An activity blueprint for the 'startedDiscussion' activity type, which
* represents a user starting a discussion.
*/
class StartedDiscussionBlueprint extends PostedBlueprint
{
/**
* {@inheritdoc}
*/
public static function getType()
{
return 'startedDiscussion';
}
}

View File

@ -1,11 +0,0 @@
<?php namespace Flarum\Core\Commands;
class ConfirmEmailCommand
{
public $token;
public function __construct($token)
{
$this->token = $token;
}
}

View File

@ -1,20 +0,0 @@
<?php namespace Flarum\Core\Commands;
use RuntimeException;
class DeleteAvatarCommand
{
public $userId;
public $actor;
public function __construct($userId, $actor)
{
if (empty($userId) || !intval($userId)) {
throw new RuntimeException('No valid user ID specified.');
}
$this->userId = $userId;
$this->actor = $actor;
}
}

View File

@ -1,14 +0,0 @@
<?php namespace Flarum\Core\Commands;
class DeleteDiscussionCommand
{
public $discussionId;
public $user;
public function __construct($discussionId, $user)
{
$this->discussionId = $discussionId;
$this->user = $user;
}
}

View File

@ -1,14 +0,0 @@
<?php namespace Flarum\Core\Commands;
class DeletePostCommand
{
public $postId;
public $user;
public function __construct($postId, $user)
{
$this->postId = $postId;
$this->user = $user;
}
}

View File

@ -1,14 +0,0 @@
<?php namespace Flarum\Core\Commands;
class DeleteUserCommand
{
public $userId;
public $user;
public function __construct($userId, $user)
{
$this->userId = $userId;
$this->user = $user;
}
}

View File

@ -1,17 +0,0 @@
<?php namespace Flarum\Core\Commands;
class EditDiscussionCommand
{
public $discussionId;
public $user;
public $data;
public function __construct($discussionId, $user, $data)
{
$this->discussionId = $discussionId;
$this->user = $user;
$this->data = $data;
}
}

View File

@ -1,17 +0,0 @@
<?php namespace Flarum\Core\Commands;
class EditPostCommand
{
public $postId;
public $user;
public $data;
public function __construct($postId, $user, $data)
{
$this->postId = $postId;
$this->user = $user;
$this->data = $data;
}
}

View File

@ -1,17 +0,0 @@
<?php namespace Flarum\Core\Commands;
class EditUserCommand
{
public $userId;
public $user;
public $data;
public function __construct($userId, $user, $data)
{
$this->userId = $userId;
$this->user = $user;
$this->data = $data;
}
}

View File

@ -1,11 +0,0 @@
<?php namespace Flarum\Core\Commands;
class GenerateAccessTokenCommand
{
public $userId;
public function __construct($userId)
{
$this->userId = $userId;
}
}

View File

@ -1,17 +0,0 @@
<?php namespace Flarum\Core\Commands;
class PostReplyCommand
{
public $discussionId;
public $user;
public $data;
public function __construct($discussionId, $user, $data)
{
$this->discussionId = $discussionId;
$this->user = $user;
$this->data = $data;
}
}

View File

@ -1,17 +0,0 @@
<?php namespace Flarum\Core\Commands;
class ReadDiscussionCommand
{
public $discussionId;
public $user;
public $readNumber;
public function __construct($discussionId, $user, $readNumber)
{
$this->discussionId = $discussionId;
$this->user = $user;
$this->readNumber = $readNumber;
}
}

View File

@ -1,14 +0,0 @@
<?php namespace Flarum\Core\Commands;
class ReadNotificationCommand
{
public $notificationId;
public $user;
public function __construct($notificationId, $user)
{
$this->notificationId = $notificationId;
$this->user = $user;
}
}

View File

@ -1,17 +0,0 @@
<?php namespace Flarum\Core\Commands;
class RegisterUserCommand
{
public $forum;
public $user;
public $data;
public function __construct($user, $forum, $data)
{
$this->user = $user;
$this->forum = $forum;
$this->data = $data;
}
}

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