diff --git a/framework/core/js/src/common/components/Navigation.js b/framework/core/js/src/common/components/Navigation.js
index f9c752574..77f52995c 100644
--- a/framework/core/js/src/common/components/Navigation.js
+++ b/framework/core/js/src/common/components/Navigation.js
@@ -95,7 +95,7 @@ export default class Navigation extends Component {
return Button.component({
className: 'Button Button--icon Navigation-drawer' +
- (user && user.newNotificationsCount() ? ' new' : ''),
+ (user && user.newNotificationCount() ? ' new' : ''),
onclick: e => {
e.stopPropagation();
drawer.show();
diff --git a/framework/core/js/src/common/helpers/userOnline.js b/framework/core/js/src/common/helpers/userOnline.js
index 8a7052c89..fcbef1673 100644
--- a/framework/core/js/src/common/helpers/userOnline.js
+++ b/framework/core/js/src/common/helpers/userOnline.js
@@ -7,7 +7,7 @@ import icon from './icon';
* @return {Object}
*/
export default function userOnline(user) {
- if (user.lastSeenTime() && user.isOnline()) {
+ if (user.lastSeenAt() && user.isOnline()) {
return {icon('fas fa-circle')};
}
}
diff --git a/framework/core/js/src/common/models/Discussion.js b/framework/core/js/src/common/models/Discussion.js
index ab6664df7..344151f30 100644
--- a/framework/core/js/src/common/models/Discussion.js
+++ b/framework/core/js/src/common/models/Discussion.js
@@ -9,28 +9,28 @@ Object.assign(Discussion.prototype, {
title: Model.attribute('title'),
slug: Model.attribute('slug'),
- startTime: Model.attribute('startTime', Model.transformDate),
- startUser: Model.hasOne('startUser'),
- startPost: Model.hasOne('startPost'),
+ createdAt: Model.attribute('createdAt', Model.transformDate),
+ user: Model.hasOne('user'),
+ firstPost: Model.hasOne('firstPost'),
- lastTime: Model.attribute('lastTime', Model.transformDate),
- lastUser: Model.hasOne('lastUser'),
+ lastPostedAt: Model.attribute('lastPostedAt', Model.transformDate),
+ lastPostedUser: Model.hasOne('lastPostedUser'),
lastPost: Model.hasOne('lastPost'),
lastPostNumber: Model.attribute('lastPostNumber'),
- commentsCount: Model.attribute('commentsCount'),
- repliesCount: computed('commentsCount', commentsCount => Math.max(0, commentsCount - 1)),
+ commentCount: Model.attribute('commentCount'),
+ replyCount: computed('commentCount', commentCount => Math.max(0, commentCount - 1)),
posts: Model.hasMany('posts'),
mostRelevantPost: Model.hasOne('mostRelevantPost'),
- readTime: Model.attribute('readTime', Model.transformDate),
- readNumber: Model.attribute('readNumber'),
+ lastReadAt: Model.attribute('lastReadAt', Model.transformDate),
+ lastReadPostNumber: Model.attribute('lastReadPostNumber'),
isUnread: computed('unreadCount', unreadCount => !!unreadCount),
isRead: computed('unreadCount', unreadCount => app.session.user && !unreadCount),
- hideTime: Model.attribute('hideTime', Model.transformDate),
- hideUser: Model.hasOne('hideUser'),
- isHidden: computed('hideTime', hideTime => !!hideTime),
+ hiddenAt: Model.attribute('hiddenAt', Model.transformDate),
+ hiddenUser: Model.hasOne('hiddenUser'),
+ isHidden: computed('hiddenAt', hiddenAt => !!hiddenAt),
canReply: Model.attribute('canReply'),
canRename: Model.attribute('canRename'),
@@ -67,8 +67,8 @@ Object.assign(Discussion.prototype, {
unreadCount() {
const user = app.session.user;
- if (user && user.readTime() < this.lastTime()) {
- return Math.max(0, this.lastPostNumber() - (this.readNumber() || 0));
+ if (user && user.markedAllAsReadAt() < this.lastPostedAt()) {
+ return Math.max(0, this.lastPostNumber() - (this.lastReadPostNumber() || 0));
}
return 0;
diff --git a/framework/core/js/src/common/models/Notification.js b/framework/core/js/src/common/models/Notification.js
index 8ff48f30d..b324addcd 100644
--- a/framework/core/js/src/common/models/Notification.js
+++ b/framework/core/js/src/common/models/Notification.js
@@ -1,19 +1,15 @@
import Model from '../Model';
-import computed from '../utils/computed';
export default class Notification extends Model {}
Object.assign(Notification.prototype, {
contentType: Model.attribute('contentType'),
- subjectId: Model.attribute('subjectId'),
content: Model.attribute('content'),
- time: Model.attribute('time', Model.date),
+ createdAt: Model.attribute('createdAt', Model.transformDate),
isRead: Model.attribute('isRead'),
- unreadCount: Model.attribute('unreadCount'),
- additionalUnreadCount: computed('unreadCount', unreadCount => Math.max(0, unreadCount - 1)),
user: Model.hasOne('user'),
- sender: Model.hasOne('sender'),
+ fromUser: Model.hasOne('fromUser'),
subject: Model.hasOne('subject')
});
diff --git a/framework/core/js/src/common/models/Post.js b/framework/core/js/src/common/models/Post.js
index 848434dab..fa0c434f6 100644
--- a/framework/core/js/src/common/models/Post.js
+++ b/framework/core/js/src/common/models/Post.js
@@ -8,20 +8,20 @@ Object.assign(Post.prototype, {
number: Model.attribute('number'),
discussion: Model.hasOne('discussion'),
- time: Model.attribute('time', Model.transformDate),
+ createdAt: Model.attribute('createdAt', Model.transformDate),
user: Model.hasOne('user'),
contentType: Model.attribute('contentType'),
content: Model.attribute('content'),
contentHtml: Model.attribute('contentHtml'),
contentPlain: computed('contentHtml', getPlainContent),
- editTime: Model.attribute('editTime', Model.transformDate),
- editUser: Model.hasOne('editUser'),
- isEdited: computed('editTime', editTime => !!editTime),
+ editedAt: Model.attribute('editedAt', Model.transformDate),
+ editedUser: Model.hasOne('editedUser'),
+ isEdited: computed('editedAt', editedAt => !!editedAt),
- hideTime: Model.attribute('hideTime', Model.transformDate),
- hideUser: Model.hasOne('hideUser'),
- isHidden: computed('hideTime', hideTime => !!hideTime),
+ hiddenAt: Model.attribute('hiddenAt', Model.transformDate),
+ hiddenUser: Model.hasOne('hiddenUser'),
+ isHidden: computed('hiddenAt', hiddenAt => !!hiddenAt),
canEdit: Model.attribute('canEdit'),
canHide: Model.attribute('canHide'),
diff --git a/framework/core/js/src/common/models/User.js b/framework/core/js/src/common/models/User.js
index 35e692467..d9754606f 100644
--- a/framework/core/js/src/common/models/User.js
+++ b/framework/core/js/src/common/models/User.js
@@ -12,7 +12,7 @@ Object.assign(User.prototype, {
username: Model.attribute('username'),
displayName: Model.attribute('displayName'),
email: Model.attribute('email'),
- isActivated: Model.attribute('isActivated'),
+ isEmailConfirmed: Model.attribute('isEmailConfirmed'),
password: Model.attribute('password'),
avatarUrl: Model.attribute('avatarUrl'),
@@ -20,13 +20,13 @@ Object.assign(User.prototype, {
groups: Model.hasMany('groups'),
joinTime: Model.attribute('joinTime', Model.transformDate),
- lastSeenTime: Model.attribute('lastSeenTime', Model.transformDate),
- readTime: Model.attribute('readTime', Model.transformDate),
- unreadNotificationsCount: Model.attribute('unreadNotificationsCount'),
- newNotificationsCount: Model.attribute('newNotificationsCount'),
+ lastSeenAt: Model.attribute('lastSeenAt', Model.transformDate),
+ markedAllAsReadAt: Model.attribute('markedAllAsReadAt', Model.transformDate),
+ unreadNotificationCount: Model.attribute('unreadNotificationCount'),
+ newNotificationCount: Model.attribute('newNotificationCount'),
- discussionsCount: Model.attribute('discussionsCount'),
- commentsCount: Model.attribute('commentsCount'),
+ discussionCount: Model.attribute('discussionCount'),
+ commentCount: Model.attribute('commentCount'),
canEdit: Model.attribute('canEdit'),
canDelete: Model.attribute('canDelete'),
@@ -54,7 +54,7 @@ Object.assign(User.prototype, {
* @public
*/
isOnline() {
- return this.lastSeenTime() > moment().subtract(5, 'minutes').toDate();
+ return this.lastSeenAt() > moment().subtract(5, 'minutes').toDate();
},
/**
diff --git a/framework/core/js/src/forum/components/DiscussionList.js b/framework/core/js/src/forum/components/DiscussionList.js
index d393665bd..dd0f155cc 100644
--- a/framework/core/js/src/forum/components/DiscussionList.js
+++ b/framework/core/js/src/forum/components/DiscussionList.js
@@ -87,7 +87,7 @@ export default class DiscussionList extends Component {
* @api
*/
requestParams() {
- const params = {include: ['startUser', 'lastUser'], filter: {}};
+ const params = {include: ['user', 'lastPostedUser'], filter: {}};
params.sort = this.sortMap()[this.props.params.sort];
@@ -112,10 +112,10 @@ export default class DiscussionList extends Component {
if (this.props.params.q) {
map.relevance = '';
}
- map.latest = '-lastTime';
- map.top = '-commentsCount';
- map.newest = '-startTime';
- map.oldest = 'startTime';
+ map.latest = '-lastPostedAt';
+ map.top = '-commentCount';
+ map.newest = '-createdAt';
+ map.oldest = 'createdAt';
return map;
}
diff --git a/framework/core/js/src/forum/components/DiscussionListItem.js b/framework/core/js/src/forum/components/DiscussionListItem.js
index c395c72f9..d11abf4f8 100644
--- a/framework/core/js/src/forum/components/DiscussionListItem.js
+++ b/framework/core/js/src/forum/components/DiscussionListItem.js
@@ -35,7 +35,7 @@ export default class DiscussionListItem extends Component {
this.subtree = new SubtreeRetainer(
() => this.props.discussion.freshness,
() => {
- const time = app.session.user && app.session.user.readTime();
+ const time = app.session.user && app.session.user.markedAllAsReadAt();
return time && time.getTime();
},
() => this.active()
@@ -58,7 +58,7 @@ export default class DiscussionListItem extends Component {
if (retain) return retain;
const discussion = this.props.discussion;
- const startUser = discussion.startUser();
+ const user = discussion.user();
const isUnread = discussion.isUnread();
const isRead = discussion.isRead();
const showUnread = !this.showRepliesCount() && isUnread;
@@ -75,7 +75,7 @@ export default class DiscussionListItem extends Component {
const phrase = this.props.params.q;
this.highlightRegExp = new RegExp(phrase+'|'+phrase.trim().replace(/\s+/g, '|'), 'gi');
} else {
- jumpTo = Math.min(discussion.lastPostNumber(), (discussion.readNumber() || 0) + 1);
+ jumpTo = Math.min(discussion.lastPostNumber(), (discussion.lastReadPostNumber() || 0) + 1);
}
return (
@@ -93,14 +93,14 @@ export default class DiscussionListItem extends Component {
@@ -156,7 +156,7 @@ export default class DiscussionListItem extends Component {
*
* @return {Boolean}
*/
- showStartPost() {
+ showFirstPost() {
return ['newest', 'oldest'].indexOf(this.props.params.sort) !== -1;
}
@@ -177,7 +177,7 @@ export default class DiscussionListItem extends Component {
const discussion = this.props.discussion;
if (discussion.isUnread()) {
- discussion.save({readNumber: discussion.lastPostNumber()});
+ discussion.save({lastReadPostNumber: discussion.lastPostNumber()});
m.redraw();
}
}
@@ -192,7 +192,7 @@ export default class DiscussionListItem extends Component {
const items = new ItemList();
if (this.props.params.q) {
- const post = this.props.discussion.mostRelevantPost() || this.props.discussion.startPost();
+ const post = this.props.discussion.mostRelevantPost() || this.props.discussion.firstPost();
if (post && post.contentType() === 'comment') {
const excerpt = highlight(post.contentPlain(), this.highlightRegExp, 175);
@@ -202,7 +202,7 @@ export default class DiscussionListItem extends Component {
items.add('terminalPost',
TerminalPost.component({
discussion: this.props.discussion,
- lastPost: !this.showStartPost()
+ lastPost: !this.showFirstPost()
})
);
}
diff --git a/framework/core/js/src/forum/components/DiscussionPage.js b/framework/core/js/src/forum/components/DiscussionPage.js
index b03b87a74..edfda8523 100644
--- a/framework/core/js/src/forum/components/DiscussionPage.js
+++ b/framework/core/js/src/forum/components/DiscussionPage.js
@@ -288,8 +288,8 @@ export default class DiscussionPage extends Page {
// If the user hasn't read past here before, then we'll update their read
// state and redraw.
- if (app.session.user && endNumber > (discussion.readNumber() || 0)) {
- discussion.save({readNumber: endNumber});
+ if (app.session.user && endNumber > (discussion.lastReadPostNumber() || 0)) {
+ discussion.save({lastReadPostNumber: endNumber});
m.redraw();
}
}
diff --git a/framework/core/js/src/forum/components/DiscussionRenamedNotification.js b/framework/core/js/src/forum/components/DiscussionRenamedNotification.js
index d74b7966b..9979d7d4d 100644
--- a/framework/core/js/src/forum/components/DiscussionRenamedNotification.js
+++ b/framework/core/js/src/forum/components/DiscussionRenamedNotification.js
@@ -20,6 +20,6 @@ export default class DiscussionRenamedNotification extends Notification {
}
content() {
- return app.translator.trans('core.forum.notifications.discussion_renamed_text', {user: this.props.notification.sender()});
+ return app.translator.trans('core.forum.notifications.discussion_renamed_text', {user: this.props.notification.fromUser()});
}
}
diff --git a/framework/core/js/src/forum/components/EditUserModal.js b/framework/core/js/src/forum/components/EditUserModal.js
index 4cabb05c8..81b61dcc1 100644
--- a/framework/core/js/src/forum/components/EditUserModal.js
+++ b/framework/core/js/src/forum/components/EditUserModal.js
@@ -15,7 +15,7 @@ export default class EditUserModal extends Modal {
this.username = m.prop(user.username() || '');
this.email = m.prop(user.email() || '');
- this.isActivated = m.prop(user.isActivated() || false);
+ this.isEmailConfirmed = m.prop(user.isEmailConfirmed() || false);
this.setPassword = m.prop(false);
this.password = m.prop(user.password() || '');
this.groups = {};
@@ -50,7 +50,7 @@ export default class EditUserModal extends Modal {
- {!this.isActivated() ? (
+ {!this.isEmailConfirmed() ? (
{Button.component({
className: 'Button Button--block',
@@ -115,11 +115,11 @@ export default class EditUserModal extends Modal {
this.loading = true;
const data = {
username: this.username(),
- isActivated: true,
+ isEmailConfirmed: true,
};
this.props.user.save(data, {errorHandler: this.onerror.bind(this)})
.then(() => {
- this.isActivated(true);
+ this.isEmailConfirmed(true);
this.loading = false;
m.redraw();
})
diff --git a/framework/core/js/src/forum/components/IndexPage.js b/framework/core/js/src/forum/components/IndexPage.js
index bfe24de0b..16648663c 100644
--- a/framework/core/js/src/forum/components/IndexPage.js
+++ b/framework/core/js/src/forum/components/IndexPage.js
@@ -385,7 +385,7 @@ export default class IndexPage extends Page {
const confirmation = confirm(app.translator.trans('core.forum.index.mark_all_as_read_confirmation'));
if (confirmation) {
- app.session.user.save({readTime: new Date()});
+ app.session.user.save({markedAllAsReadAt: new Date()});
}
}
}
diff --git a/framework/core/js/src/forum/components/Notification.js b/framework/core/js/src/forum/components/Notification.js
index 63d9d668d..7ebc3fc28 100644
--- a/framework/core/js/src/forum/components/Notification.js
+++ b/framework/core/js/src/forum/components/Notification.js
@@ -26,10 +26,10 @@ export default class Notification extends Component {
if (!isInitialized) $(element).click(this.markAsRead.bind(this));
}}>
- {avatar(notification.sender())}
+ {avatar(notification.fromUser())}
{icon(this.icon(), {className: 'Notification-icon'})}
{this.content()}
- {humanTime(notification.time())}
+ {humanTime(notification.createdAt())}
{this.excerpt()}
@@ -79,7 +79,7 @@ export default class Notification extends Component {
markAsRead() {
if (this.props.notification.isRead()) return;
- app.session.user.pushAttributes({unreadNotificationsCount: app.session.user.unreadNotificationsCount() - 1});
+ app.session.user.pushAttributes({unreadNotificationCount: app.session.user.unreadNotificationCount() - 1});
this.props.notification.save({isRead: true});
}
diff --git a/framework/core/js/src/forum/components/NotificationList.js b/framework/core/js/src/forum/components/NotificationList.js
index 8d9d12488..eebd8032b 100644
--- a/framework/core/js/src/forum/components/NotificationList.js
+++ b/framework/core/js/src/forum/components/NotificationList.js
@@ -137,7 +137,7 @@ export default class NotificationList extends Component {
* been loaded.
*/
load() {
- if (app.session.user.newNotificationsCount()) {
+ if (app.session.user.newNotificationCount()) {
delete app.cache.notifications;
}
@@ -145,7 +145,7 @@ export default class NotificationList extends Component {
return;
}
- app.session.user.pushAttributes({newNotificationsCount: 0});
+ app.session.user.pushAttributes({newNotificationCount: 0});
this.loadMore();
}
@@ -191,7 +191,7 @@ export default class NotificationList extends Component {
markAllAsRead() {
if (!app.cache.notifications) return;
- app.session.user.pushAttributes({unreadNotificationsCount: 0});
+ app.session.user.pushAttributes({unreadNotificationCount: 0});
app.cache.notifications.forEach(notifications => {
notifications.forEach(notification => notification.pushAttributes({isRead: true}))
diff --git a/framework/core/js/src/forum/components/NotificationsDropdown.js b/framework/core/js/src/forum/components/NotificationsDropdown.js
index a8b716dfd..d185c4a5f 100644
--- a/framework/core/js/src/forum/components/NotificationsDropdown.js
+++ b/framework/core/js/src/forum/components/NotificationsDropdown.js
@@ -62,11 +62,11 @@ export default class NotificationsDropdown extends Dropdown {
}
getUnreadCount() {
- return app.session.user.unreadNotificationsCount();
+ return app.session.user.unreadNotificationCount();
}
getNewCount() {
- return app.session.user.newNotificationsCount();
+ return app.session.user.newNotificationCount();
}
menuClick(e) {
diff --git a/framework/core/js/src/forum/components/PostEdited.js b/framework/core/js/src/forum/components/PostEdited.js
index e35ced036..021307040 100644
--- a/framework/core/js/src/forum/components/PostEdited.js
+++ b/framework/core/js/src/forum/components/PostEdited.js
@@ -18,10 +18,10 @@ export default class PostEdited extends Component {
view() {
const post = this.props.post;
- const editUser = post.editUser();
+ const editedUser = post.editedUser();
const editedInfo = extractText(app.translator.trans(
'core.forum.post.edited_tooltip',
- {user: editUser, ago: humanTime(post.editTime())}
+ {user: editedUser, ago: humanTime(post.editedAt())}
));
if (editedInfo !== this.oldEditedInfo) {
this.shouldUpdateTooltip = true;
diff --git a/framework/core/js/src/forum/components/PostMeta.js b/framework/core/js/src/forum/components/PostMeta.js
index 965819d93..98df112aa 100644
--- a/framework/core/js/src/forum/components/PostMeta.js
+++ b/framework/core/js/src/forum/components/PostMeta.js
@@ -14,7 +14,7 @@ import fullTime from '../../common/helpers/fullTime';
export default class PostMeta extends Component {
view() {
const post = this.props.post;
- const time = post.time();
+ const time = post.createdAt();
const permalink = this.getPermalink(post);
const touch = 'ontouchstart' in document.documentElement;
diff --git a/framework/core/js/src/forum/components/PostStream.js b/framework/core/js/src/forum/components/PostStream.js
index 6175e20dd..335d0f441 100644
--- a/framework/core/js/src/forum/components/PostStream.js
+++ b/framework/core/js/src/forum/components/PostStream.js
@@ -205,7 +205,7 @@ class PostStream extends Component {
const attrs = {'data-index': this.visibleStart + i};
if (post) {
- const time = post.time();
+ const time = post.createdAt();
const PostComponent = app.postComponents[post.contentType()];
content = PostComponent ? PostComponent.component({post}) : '';
diff --git a/framework/core/js/src/forum/components/PostsUserPage.js b/framework/core/js/src/forum/components/PostsUserPage.js
index ca48652ec..95b9ed73c 100644
--- a/framework/core/js/src/forum/components/PostsUserPage.js
+++ b/framework/core/js/src/forum/components/PostsUserPage.js
@@ -114,7 +114,7 @@ export default class PostsUserPage extends UserPage {
type: 'comment'
},
page: {offset, limit: this.loadLimit},
- sort: '-time'
+ sort: '-createdAt'
});
}
diff --git a/framework/core/js/src/forum/components/SettingsPage.js b/framework/core/js/src/forum/components/SettingsPage.js
index 9ea3993a7..e58e1204c 100644
--- a/framework/core/js/src/forum/components/SettingsPage.js
+++ b/framework/core/js/src/forum/components/SettingsPage.js
@@ -134,7 +134,7 @@ export default class SettingsPage extends UserPage {
children: app.translator.trans('core.forum.settings.privacy_disclose_online_label'),
state: this.user.preferences().discloseOnline,
onchange: (value, component) => {
- this.user.pushAttributes({lastSeenTime: null});
+ this.user.pushAttributes({lastSeenAt: null});
this.preferenceSaver('discloseOnline')(value, component);
}
})
diff --git a/framework/core/js/src/forum/components/TerminalPost.js b/framework/core/js/src/forum/components/TerminalPost.js
index 0ce2735d4..7475f12b9 100644
--- a/framework/core/js/src/forum/components/TerminalPost.js
+++ b/framework/core/js/src/forum/components/TerminalPost.js
@@ -13,10 +13,10 @@ import icon from '../../common/helpers/icon';
export default class TerminalPost extends Component {
view() {
const discussion = this.props.discussion;
- const lastPost = this.props.lastPost && discussion.repliesCount();
+ const lastPost = this.props.lastPost && discussion.replyCount();
- const user = discussion[lastPost ? 'lastUser' : 'startUser']();
- const time = discussion[lastPost ? 'lastTime' : 'startTime']();
+ const user = discussion[lastPost ? 'lastPostedUser' : 'user']();
+ const time = discussion[lastPost ? 'lastPostedAt' : 'createdAt']();
return (
diff --git a/framework/core/js/src/forum/components/UserCard.js b/framework/core/js/src/forum/components/UserCard.js
index 68c743a00..fa042005c 100644
--- a/framework/core/js/src/forum/components/UserCard.js
+++ b/framework/core/js/src/forum/components/UserCard.js
@@ -79,16 +79,16 @@ export default class UserCard extends Component {
infoItems() {
const items = new ItemList();
const user = this.props.user;
- const lastSeenTime = user.lastSeenTime();
+ const lastSeenAt = user.lastSeenAt();
- if (lastSeenTime) {
+ if (lastSeenAt) {
const online = user.isOnline();
items.add('lastSeen', (
{online
? [icon('fas fa-circle'), ' ', app.translator.trans('core.forum.user.online_text')]
- : [icon('far fa-clock'), ' ', humanTime(lastSeenTime)]}
+ : [icon('far fa-clock'), ' ', humanTime(lastSeenAt)]}
));
}
diff --git a/framework/core/js/src/forum/components/UserPage.js b/framework/core/js/src/forum/components/UserPage.js
index cd2809063..67c27fb01 100644
--- a/framework/core/js/src/forum/components/UserPage.js
+++ b/framework/core/js/src/forum/components/UserPage.js
@@ -131,7 +131,7 @@ export default class UserPage extends Page {
items.add('posts',
LinkButton.component({
href: app.route('user.posts', {username: user.username()}),
- children: [app.translator.trans('core.forum.user.posts_link'), {user.commentsCount()}],
+ children: [app.translator.trans('core.forum.user.posts_link'), {user.commentCount()}],
icon: 'far fa-comment'
}),
100
@@ -140,7 +140,7 @@ export default class UserPage extends Page {
items.add('discussions',
LinkButton.component({
href: app.route('user.discussions', {username: user.username()}),
- children: [app.translator.trans('core.forum.user.discussions_link'), {user.discussionsCount()}],
+ children: [app.translator.trans('core.forum.user.discussions_link'), {user.discussionCount()}],
icon: 'fas fa-bars'
}),
90
diff --git a/framework/core/js/src/forum/utils/DiscussionControls.js b/framework/core/js/src/forum/utils/DiscussionControls.js
index 93bfef3c4..75f21a7e8 100644
--- a/framework/core/js/src/forum/utils/DiscussionControls.js
+++ b/framework/core/js/src/forum/utils/DiscussionControls.js
@@ -183,7 +183,7 @@ export default {
* @return {Promise}
*/
hideAction() {
- this.pushAttributes({ hideTime: new Date(), hideUser: app.session.user });
+ this.pushAttributes({ hiddenAt: new Date(), hiddenUser: app.session.user });
return this.save({ isHidden: true });
},
@@ -194,7 +194,7 @@ export default {
* @return {Promise}
*/
restoreAction() {
- this.pushAttributes({ hideTime: null, hideUser: null });
+ this.pushAttributes({ hiddenAt: null, hiddenUser: null });
return this.save({ isHidden: false });
},
diff --git a/framework/core/js/src/forum/utils/PostControls.js b/framework/core/js/src/forum/utils/PostControls.js
index 6140b96b8..574b039bf 100644
--- a/framework/core/js/src/forum/utils/PostControls.js
+++ b/framework/core/js/src/forum/utils/PostControls.js
@@ -123,7 +123,7 @@ export default {
* @return {Promise}
*/
hideAction() {
- this.pushAttributes({ hideTime: new Date(), hideUser: app.session.user });
+ this.pushAttributes({ hiddenAt: new Date(), hiddenUser: app.session.user });
return this.save({ isHidden: true }).then(() => m.redraw());
},
@@ -134,7 +134,7 @@ export default {
* @return {Promise}
*/
restoreAction() {
- this.pushAttributes({ hideTime: null, hideUser: null });
+ this.pushAttributes({ hiddenAt: null, hiddenUser: null });
return this.save({ isHidden: false }).then(() => m.redraw());
},
diff --git a/framework/core/js/src/forum/utils/alertEmailConfirmation.js b/framework/core/js/src/forum/utils/alertEmailConfirmation.js
index e3e4582ba..784770cdb 100644
--- a/framework/core/js/src/forum/utils/alertEmailConfirmation.js
+++ b/framework/core/js/src/forum/utils/alertEmailConfirmation.js
@@ -10,7 +10,7 @@ import icon from '../../common/helpers/icon';
export default function alertEmailConfirmation(app) {
const user = app.session.user;
- if (!user || user.isActivated()) return;
+ if (!user || user.isEmailConfirmed()) return;
const resendButton = Button.component({
className: 'Button Button--link',
diff --git a/framework/core/migrations/2015_02_25_000000_setup_default_permissions.php b/framework/core/migrations/2015_02_25_000000_setup_default_permissions.php
deleted file mode 100644
index 1349910e9..000000000
--- a/framework/core/migrations/2015_02_25_000000_setup_default_permissions.php
+++ /dev/null
@@ -1,30 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-use Flarum\Database\Migration;
-use Flarum\Group\Group;
-
-return Migration::addPermissions([
- // Guests can view the forum
- 'viewDiscussions' => Group::GUEST_ID,
-
- // Members can create and reply to discussions, and view the user list
- 'startDiscussion' => Group::MEMBER_ID,
- 'discussion.reply' => Group::MEMBER_ID,
- 'viewUserList' => Group::MEMBER_ID,
-
- // Moderators can edit + delete stuff
- 'discussion.hide' => Group::MODERATOR_ID,
- 'discussion.editPosts' => Group::MODERATOR_ID,
- 'discussion.hidePosts' => Group::MODERATOR_ID,
- 'discussion.rename' => Group::MODERATOR_ID,
- 'discussion.viewIpsPosts' => Group::MODERATOR_ID,
-]);
diff --git a/framework/core/migrations/2018_01_11_093900_change_access_tokens_columns.php b/framework/core/migrations/2018_01_11_093900_change_access_tokens_columns.php
new file mode 100644
index 000000000..a27bad442
--- /dev/null
+++ b/framework/core/migrations/2018_01_11_093900_change_access_tokens_columns.php
@@ -0,0 +1,45 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ $schema->table('access_tokens', function (Blueprint $table) {
+ $table->renameColumn('id', 'token');
+ $table->renameColumn('lifetime', 'lifetime_seconds');
+ $table->renameColumn('last_activity', 'last_activity_at');
+ $table->dateTime('created_at');
+ $table->integer('user_id')->unsigned()->change();
+ });
+
+ // Use a separate schema instance because this column gets renamed
+ // in the first one.
+ $schema->table('access_tokens', function (Blueprint $table) {
+ $table->dateTime('last_activity_at')->change();
+ });
+ },
+
+ 'down' => function (Builder $schema) {
+ $schema->table('access_tokens', function (Blueprint $table) {
+ $table->integer('last_activity_at')->change();
+ });
+
+ $schema->table('access_tokens', function (Blueprint $table) {
+ $table->renameColumn('token', 'id');
+ $table->renameColumn('lifetime_seconds', 'lifetime');
+ $table->renameColumn('last_activity_at', 'last_activity');
+ $table->dropColumn('created_at');
+ $table->integer('user_id')->change();
+ });
+ }
+];
diff --git a/framework/core/migrations/2018_01_11_094000_change_access_tokens_add_foreign_keys.php b/framework/core/migrations/2018_01_11_094000_change_access_tokens_add_foreign_keys.php
new file mode 100644
index 000000000..3103a963e
--- /dev/null
+++ b/framework/core/migrations/2018_01_11_094000_change_access_tokens_add_foreign_keys.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ // Delete rows with non-existent users so that we will be able to create
+ // foreign keys without any issues.
+ $schema->getConnection()
+ ->table('access_tokens')
+ ->whereNotExists(function ($query) {
+ $query->selectRaw(1)->from('users')->whereColumn('id', 'user_id');
+ })
+ ->delete();
+
+ $schema->table('access_tokens', function (Blueprint $table) {
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ });
+ },
+
+ 'down' => function (Builder $schema) {
+ $schema->table('access_tokens', function (Blueprint $table) {
+ $table->dropForeign(['user_id']);
+ });
+ }
+];
diff --git a/framework/core/migrations/2018_01_11_095000_change_api_keys_columns.php b/framework/core/migrations/2018_01_11_095000_change_api_keys_columns.php
new file mode 100644
index 000000000..eaa6246b4
--- /dev/null
+++ b/framework/core/migrations/2018_01_11_095000_change_api_keys_columns.php
@@ -0,0 +1,47 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ $schema->table('api_keys', function (Blueprint $table) {
+ $table->dropPrimary(['id']);
+ $table->renameColumn('id', 'key');
+ $table->unique('key');
+ });
+
+ $schema->table('api_keys', function (Blueprint $table) {
+ $table->increments('id');
+ $table->string('allowed_ips')->nullable();
+ $table->string('scopes')->nullable();
+ $table->integer('user_id')->unsigned()->nullable();
+ $table->dateTime('created_at');
+ $table->dateTime('last_activity_at')->nullable();
+
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ });
+ },
+
+ 'down' => function (Builder $schema) {
+ $schema->table('api_keys', function (Blueprint $table) {
+ $table->dropForeign(['user_id']);
+ $table->dropColumn('id', 'allowed_ips', 'user_id', 'scopes', 'created_at');
+ });
+
+ $schema->table('api_keys', function (Blueprint $table) {
+ $table->dropUnique(['key']);
+ $table->renameColumn('key', 'id');
+ $table->primary('id');
+ });
+ }
+];
diff --git a/framework/core/migrations/2018_07_16_003952_allow_hide_posts_for_moderators.php b/framework/core/migrations/2018_01_11_101800_rename_auth_tokens_to_registration_tokens.php
similarity index 69%
rename from framework/core/migrations/2018_07_16_003952_allow_hide_posts_for_moderators.php
rename to framework/core/migrations/2018_01_11_101800_rename_auth_tokens_to_registration_tokens.php
index 57203b7b1..eaf176a0d 100644
--- a/framework/core/migrations/2018_07_16_003952_allow_hide_posts_for_moderators.php
+++ b/framework/core/migrations/2018_01_11_101800_rename_auth_tokens_to_registration_tokens.php
@@ -10,8 +10,5 @@
*/
use Flarum\Database\Migration;
-use Flarum\Group\Group;
-return Migration::addPermissions([
- 'discussion.hidePosts' => Group::MODERATOR_ID
-]);
+return Migration::renameTable('auth_tokens', 'registration_tokens');
diff --git a/framework/core/migrations/2018_01_11_102000_change_registration_tokens_rename_id_to_token.php b/framework/core/migrations/2018_01_11_102000_change_registration_tokens_rename_id_to_token.php
new file mode 100644
index 000000000..3a630513e
--- /dev/null
+++ b/framework/core/migrations/2018_01_11_102000_change_registration_tokens_rename_id_to_token.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Database\Migration;
+
+return Migration::renameColumn('registration_tokens', 'id', 'token');
diff --git a/framework/core/migrations/2018_01_11_102100_change_registration_tokens_created_at_to_datetime.php b/framework/core/migrations/2018_01_11_102100_change_registration_tokens_created_at_to_datetime.php
new file mode 100644
index 000000000..f3276bf1d
--- /dev/null
+++ b/framework/core/migrations/2018_01_11_102100_change_registration_tokens_created_at_to_datetime.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ // do this manually because dbal doesn't recognize timestamp columns
+ $connection = $schema->getConnection();
+ $prefix = $connection->getTablePrefix();
+ $connection->statement("ALTER TABLE {$prefix}registration_tokens MODIFY created_at DATETIME");
+ },
+
+ 'down' => function (Builder $schema) {
+ $connection = $schema->getConnection();
+ $prefix = $connection->getTablePrefix();
+ $connection->statement("ALTER TABLE {$prefix}registration_tokens MODIFY created_at TIMESTAMP");
+ }
+];
diff --git a/framework/core/migrations/2018_01_11_155200_change_discussions_rename_columns.php b/framework/core/migrations/2018_01_11_155200_change_discussions_rename_columns.php
new file mode 100644
index 000000000..3d4e7467d
--- /dev/null
+++ b/framework/core/migrations/2018_01_11_155200_change_discussions_rename_columns.php
@@ -0,0 +1,25 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Database\Migration;
+
+return Migration::renameColumns('discussions', [
+ 'comments_count' => 'comment_count',
+ 'participants_count' => 'participant_count',
+ 'number_index' => 'post_number_index',
+ 'start_time' => 'created_at',
+ 'start_user_id' => 'user_id',
+ 'start_post_id' => 'first_post_id',
+ 'last_time' => 'last_posted_at',
+ 'last_user_id' => 'last_posted_user_id',
+ 'hide_time' => 'hidden_at',
+ 'hide_user_id' => 'hidden_user_id'
+]);
diff --git a/framework/core/migrations/2018_01_11_155300_change_discussions_add_foreign_keys.php b/framework/core/migrations/2018_01_11_155300_change_discussions_add_foreign_keys.php
new file mode 100644
index 000000000..23d6b338c
--- /dev/null
+++ b/framework/core/migrations/2018_01_11_155300_change_discussions_add_foreign_keys.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Query\Expression;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ // Set non-existent entity IDs to NULL so that we will be able to create
+ // foreign keys without any issues.
+ $connection = $schema->getConnection();
+
+ $selectId = function ($table, $column) use ($connection) {
+ return new Expression(
+ '('.$connection->table($table)->whereColumn('id', $column)->select('id')->toSql().')'
+ );
+ };
+
+ $connection->table('discussions')->update([
+ 'user_id' => $selectId('users', 'user_id'),
+ 'last_posted_user_id' => $selectId('users', 'last_posted_user_id'),
+ 'hidden_user_id' => $selectId('users', 'hidden_user_id'),
+ 'first_post_id' => $selectId('posts', 'first_post_id'),
+ 'last_post_id' => $selectId('posts', 'last_post_id'),
+ ]);
+
+ $schema->table('discussions', function (Blueprint $table) {
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('set null');
+ $table->foreign('last_posted_user_id')->references('id')->on('users')->onDelete('set null');
+ $table->foreign('hidden_user_id')->references('id')->on('users')->onDelete('set null');
+ $table->foreign('first_post_id')->references('id')->on('posts')->onDelete('set null');
+ $table->foreign('last_post_id')->references('id')->on('posts')->onDelete('set null');
+ });
+ },
+
+ 'down' => function (Builder $schema) {
+ $schema->table('discussions', function (Blueprint $table) {
+ $table->dropForeign([
+ 'user_id', 'last_posted_user_id', 'hidden_user_id',
+ 'first_post_id', 'last_post_id'
+ ]);
+ });
+ }
+];
diff --git a/framework/core/migrations/2018_01_15_071700_rename_users_discussions_to_discussion_user.php b/framework/core/migrations/2018_01_15_071700_rename_users_discussions_to_discussion_user.php
new file mode 100644
index 000000000..eaa51709d
--- /dev/null
+++ b/framework/core/migrations/2018_01_15_071700_rename_users_discussions_to_discussion_user.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Database\Migration;
+
+return Migration::renameTable('users_discussions', 'discussion_user');
diff --git a/framework/core/migrations/2018_01_15_071800_change_discussion_user_rename_columns.php b/framework/core/migrations/2018_01_15_071800_change_discussion_user_rename_columns.php
new file mode 100644
index 000000000..5fff7ea1e
--- /dev/null
+++ b/framework/core/migrations/2018_01_15_071800_change_discussion_user_rename_columns.php
@@ -0,0 +1,17 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Database\Migration;
+
+return Migration::renameColumns('discussion_user', [
+ 'read_time' => 'last_read_at',
+ 'read_number' => 'last_read_post_number'
+]);
diff --git a/framework/core/migrations/2018_01_15_071900_change_discussion_user_add_foreign_keys.php b/framework/core/migrations/2018_01_15_071900_change_discussion_user_add_foreign_keys.php
new file mode 100644
index 000000000..c3bf1d9fd
--- /dev/null
+++ b/framework/core/migrations/2018_01_15_071900_change_discussion_user_add_foreign_keys.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ // Delete rows with non-existent entities so that we will be able to create
+ // foreign keys without any issues.
+ $connection = $schema->getConnection();
+ $connection->table('discussion_user')
+ ->whereNotExists(function ($query) {
+ $query->selectRaw(1)->from('users')->whereColumn('id', 'user_id');
+ })
+ ->orWhereNotExists(function ($query) {
+ $query->selectRaw(1)->from('discussions')->whereColumn('id', 'discussion_id');
+ })
+ ->delete();
+
+ $schema->table('discussion_user', function (Blueprint $table) {
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ $table->foreign('discussion_id')->references('id')->on('discussions')->onDelete('cascade');
+ });
+ },
+
+ 'down' => function (Builder $schema) {
+ $schema->table('discussion_user', function (Blueprint $table) {
+ $table->dropForeign(['user_id', 'discussion_id']);
+ });
+ }
+];
diff --git a/framework/core/migrations/2018_01_15_072600_change_email_tokens_rename_id_to_token.php b/framework/core/migrations/2018_01_15_072600_change_email_tokens_rename_id_to_token.php
new file mode 100644
index 000000000..64586a4aa
--- /dev/null
+++ b/framework/core/migrations/2018_01_15_072600_change_email_tokens_rename_id_to_token.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Database\Migration;
+
+return Migration::renameColumn('email_tokens', 'id', 'token');
diff --git a/framework/core/migrations/2018_01_15_072700_change_email_tokens_add_foreign_keys.php b/framework/core/migrations/2018_01_15_072700_change_email_tokens_add_foreign_keys.php
new file mode 100644
index 000000000..243b706f3
--- /dev/null
+++ b/framework/core/migrations/2018_01_15_072700_change_email_tokens_add_foreign_keys.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ // Delete rows with non-existent users so that we will be able to create
+ // foreign keys without any issues.
+ $schema->getConnection()
+ ->table('email_tokens')
+ ->whereNotExists(function ($query) {
+ $query->selectRaw(1)->from('users')->whereColumn('id', 'user_id');
+ })
+ ->delete();
+
+ $schema->table('email_tokens', function (Blueprint $table) {
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ });
+ },
+
+ 'down' => function (Builder $schema) {
+ $schema->table('email_tokens', function (Blueprint $table) {
+ $table->dropForeign(['user_id']);
+ });
+ }
+];
diff --git a/framework/core/migrations/2018_01_15_072800_change_email_tokens_created_at_to_datetime.php b/framework/core/migrations/2018_01_15_072800_change_email_tokens_created_at_to_datetime.php
new file mode 100644
index 000000000..f393f72f2
--- /dev/null
+++ b/framework/core/migrations/2018_01_15_072800_change_email_tokens_created_at_to_datetime.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ // do this manually because dbal doesn't recognize timestamp columns
+ $connection = $schema->getConnection();
+ $prefix = $connection->getTablePrefix();
+ $connection->statement("ALTER TABLE {$prefix}email_tokens MODIFY created_at DATETIME");
+ },
+
+ 'down' => function (Builder $schema) {
+ $connection = $schema->getConnection();
+ $prefix = $connection->getTablePrefix();
+ $connection->statement("ALTER TABLE {$prefix}email_tokens MODIFY created_at TIMESTAMP");
+ }
+];
diff --git a/framework/core/migrations/2018_01_18_130400_rename_permissions_to_group_permission.php b/framework/core/migrations/2018_01_18_130400_rename_permissions_to_group_permission.php
new file mode 100644
index 000000000..c36d11e4e
--- /dev/null
+++ b/framework/core/migrations/2018_01_18_130400_rename_permissions_to_group_permission.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Database\Migration;
+
+return Migration::renameTable('permissions', 'group_permission');
diff --git a/framework/core/migrations/2018_01_18_130500_change_group_permission_add_foreign_keys.php b/framework/core/migrations/2018_01_18_130500_change_group_permission_add_foreign_keys.php
new file mode 100644
index 000000000..267a09dc0
--- /dev/null
+++ b/framework/core/migrations/2018_01_18_130500_change_group_permission_add_foreign_keys.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ // Delete rows with non-existent groups so that we will be able to create
+ // foreign keys without any issues.
+ $schema->getConnection()
+ ->table('group_permission')
+ ->whereNotExists(function ($query) {
+ $query->selectRaw(1)->from('groups')->whereColumn('id', 'group_id');
+ })
+ ->delete();
+
+ $schema->table('group_permission', function (Blueprint $table) {
+ $table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade');
+ });
+ },
+
+ 'down' => function (Builder $schema) {
+ $schema->table('group_permission', function (Blueprint $table) {
+ $table->dropForeign(['group_id']);
+ });
+ }
+];
diff --git a/framework/core/migrations/2018_01_18_130600_rename_users_groups_to_group_user.php b/framework/core/migrations/2018_01_18_130600_rename_users_groups_to_group_user.php
new file mode 100644
index 000000000..40f4178f9
--- /dev/null
+++ b/framework/core/migrations/2018_01_18_130600_rename_users_groups_to_group_user.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Database\Migration;
+
+return Migration::renameTable('users_groups', 'group_user');
diff --git a/framework/core/migrations/2018_01_18_130700_change_group_user_add_foreign_keys.php b/framework/core/migrations/2018_01_18_130700_change_group_user_add_foreign_keys.php
new file mode 100644
index 000000000..79a5ef2a5
--- /dev/null
+++ b/framework/core/migrations/2018_01_18_130700_change_group_user_add_foreign_keys.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ // Delete rows with non-existent entities so that we will be able to create
+ // foreign keys without any issues.
+ $schema->getConnection()
+ ->table('group_user')
+ ->whereNotExists(function ($query) {
+ $query->selectRaw(1)->from('users')->whereColumn('id', 'user_id');
+ })
+ ->orWhereNotExists(function ($query) {
+ $query->selectRaw(1)->from('groups')->whereColumn('id', 'group_id');
+ })
+ ->delete();
+
+ $schema->table('group_user', function (Blueprint $table) {
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ $table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade');
+ });
+ },
+
+ 'down' => function (Builder $schema) {
+ $schema->table('group_user', function (Blueprint $table) {
+ $table->dropForeign(['user_id', 'group_id']);
+ });
+ }
+];
diff --git a/framework/core/migrations/2018_01_18_133000_change_notifications_columns.php b/framework/core/migrations/2018_01_18_133000_change_notifications_columns.php
new file mode 100644
index 000000000..fd4a71fe8
--- /dev/null
+++ b/framework/core/migrations/2018_01_18_133000_change_notifications_columns.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Carbon\Carbon;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ $schema->table('notifications', function (Blueprint $table) {
+ $table->dropColumn('subject_type');
+
+ $table->renameColumn('time', 'created_at');
+ $table->renameColumn('sender_id', 'from_user_id');
+
+ $table->dateTime('read_at')->nullable();
+ });
+
+ $schema->getConnection()->table('notifications')
+ ->where('is_read', 1)
+ ->update(['read_at' => Carbon::now()]);
+
+ $schema->table('notifications', function (Blueprint $table) {
+ $table->dropColumn('is_read');
+ });
+ },
+
+ 'down' => function (Builder $schema) {
+ $schema->table('notifications', function (Blueprint $table) {
+ $table->string('subject_type', 200)->nullable();
+
+ $table->renameColumn('created_at', 'time');
+ $table->renameColumn('from_user_id', 'sender_id');
+
+ $table->boolean('is_read');
+ });
+
+ $schema->getConnection()->table('notifications')
+ ->whereNotNull('read_at')
+ ->update(['is_read' => 1]);
+
+ $schema->table('notifications', function (Blueprint $table) {
+ $table->dropColumn('read_at');
+ });
+ }
+];
diff --git a/framework/core/migrations/2018_01_18_133100_change_notifications_add_foreign_keys.php b/framework/core/migrations/2018_01_18_133100_change_notifications_add_foreign_keys.php
new file mode 100644
index 000000000..4845222cb
--- /dev/null
+++ b/framework/core/migrations/2018_01_18_133100_change_notifications_add_foreign_keys.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ // Delete rows with non-existent users so that we will be able to create
+ // foreign keys without any issues.
+ $schema->getConnection()
+ ->table('notifications')
+ ->whereNotExists(function ($query) {
+ $query->selectRaw(1)->from('users')->whereColumn('id', 'user_id');
+ })
+ ->delete();
+
+ $schema->getConnection()
+ ->table('notifications')
+ ->whereNotExists(function ($query) {
+ $query->selectRaw(1)->from('users')->whereColumn('id', 'from_user_id');
+ })
+ ->update(['from_user_id' => null]);
+
+ $schema->table('notifications', function (Blueprint $table) {
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ $table->foreign('from_user_id')->references('id')->on('users')->onDelete('set null');
+ });
+ },
+
+ 'down' => function (Builder $schema) {
+ $schema->table('notifications', function (Blueprint $table) {
+ $table->dropForeign(['user_id', 'from_user_id']);
+ });
+ }
+];
diff --git a/framework/core/migrations/2018_01_18_134400_change_password_tokens_rename_id_to_token.php b/framework/core/migrations/2018_01_18_134400_change_password_tokens_rename_id_to_token.php
new file mode 100644
index 000000000..e83f8692a
--- /dev/null
+++ b/framework/core/migrations/2018_01_18_134400_change_password_tokens_rename_id_to_token.php
@@ -0,0 +1,14 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Database\Migration;
+
+return Migration::renameColumn('password_tokens', 'id', 'token');
diff --git a/framework/core/migrations/2018_01_18_134500_change_password_tokens_add_foreign_keys.php b/framework/core/migrations/2018_01_18_134500_change_password_tokens_add_foreign_keys.php
new file mode 100644
index 000000000..cb7303e71
--- /dev/null
+++ b/framework/core/migrations/2018_01_18_134500_change_password_tokens_add_foreign_keys.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ // Delete rows with non-existent users so that we will be able to create
+ // foreign keys without any issues.
+ $connection = $schema->getConnection();
+ $connection->table('password_tokens')
+ ->whereNotExists(function ($query) {
+ $query->selectRaw(1)->from('users')->whereColumn('id', 'user_id');
+ })
+ ->delete();
+
+ $schema->table('password_tokens', function (Blueprint $table) {
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ });
+ },
+
+ 'down' => function (Builder $schema) {
+ $schema->table('password_tokens', function (Blueprint $table) {
+ $table->dropForeign(['user_id']);
+ });
+ }
+];
diff --git a/framework/core/migrations/2018_01_18_134600_change_password_tokens_created_at_to_datetime.php b/framework/core/migrations/2018_01_18_134600_change_password_tokens_created_at_to_datetime.php
new file mode 100644
index 000000000..9a411f5ff
--- /dev/null
+++ b/framework/core/migrations/2018_01_18_134600_change_password_tokens_created_at_to_datetime.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ // do this manually because dbal doesn't recognize timestamp columns
+ $connection = $schema->getConnection();
+ $prefix = $connection->getTablePrefix();
+ $connection->statement("ALTER TABLE {$prefix}password_tokens MODIFY created_at DATETIME");
+ },
+
+ 'down' => function (Builder $schema) {
+ $connection = $schema->getConnection();
+ $prefix = $connection->getTablePrefix();
+ $connection->statement("ALTER TABLE {$prefix}password_tokens MODIFY created_at TIMESTAMP");
+ }
+];
diff --git a/framework/core/migrations/2018_01_18_135000_change_posts_rename_columns.php b/framework/core/migrations/2018_01_18_135000_change_posts_rename_columns.php
new file mode 100644
index 000000000..701c58aa1
--- /dev/null
+++ b/framework/core/migrations/2018_01_18_135000_change_posts_rename_columns.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Database\Migration;
+
+return Migration::renameColumns('posts', [
+ 'time' => 'created_at',
+ 'edit_time' => 'edited_at',
+ 'hide_time' => 'hidden_at',
+ 'edit_user_id' => 'edited_user_id',
+ 'hide_user_id' => 'hidden_user_id'
+]);
diff --git a/framework/core/migrations/2018_01_18_135100_change_posts_add_foreign_keys.php b/framework/core/migrations/2018_01_18_135100_change_posts_add_foreign_keys.php
new file mode 100644
index 000000000..3ac447273
--- /dev/null
+++ b/framework/core/migrations/2018_01_18_135100_change_posts_add_foreign_keys.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Illuminate\Database\Query\Expression;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ // Set non-existent entity IDs to NULL so that we will be able to create
+ // foreign keys without any issues.
+ $connection = $schema->getConnection();
+
+ $selectId = function ($table, $column) use ($connection) {
+ return new Expression(
+ '('.$connection->table($table)->whereColumn('id', $column)->select('id')->toSql().')'
+ );
+ };
+
+ $connection->table('posts')->update([
+ 'user_id' => $selectId('users', 'user_id'),
+ 'edited_user_id' => $selectId('users', 'edited_user_id'),
+ 'hidden_user_id' => $selectId('users', 'hidden_user_id'),
+ ]);
+
+ $schema->table('posts', function (Blueprint $table) {
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('set null');
+ $table->foreign('edited_user_id')->references('id')->on('users')->onDelete('set null');
+ $table->foreign('hidden_user_id')->references('id')->on('users')->onDelete('set null');
+ });
+ },
+
+ 'down' => function (Builder $schema) {
+ $schema->table('posts', function (Blueprint $table) {
+ $table->dropForeign([
+ 'user_id', 'discussion_id',
+ 'edited_user_id', 'hidden_user_id'
+ ]);
+ });
+ }
+];
diff --git a/framework/core/migrations/2018_01_30_220100_create_post_user_table.php b/framework/core/migrations/2018_01_30_220100_create_post_user_table.php
new file mode 100644
index 000000000..577ce3511
--- /dev/null
+++ b/framework/core/migrations/2018_01_30_220100_create_post_user_table.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Database\Migration;
+use Illuminate\Database\Schema\Blueprint;
+
+return Migration::createTable(
+ 'post_user',
+ function (Blueprint $table) {
+ $table->integer('post_id')->unsigned();
+ $table->integer('user_id')->unsigned();
+
+ $table->primary(['post_id', 'user_id']);
+
+ $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ }
+);
diff --git a/framework/core/migrations/2018_01_30_222900_change_users_rename_columns.php b/framework/core/migrations/2018_01_30_222900_change_users_rename_columns.php
new file mode 100644
index 000000000..4b6965bde
--- /dev/null
+++ b/framework/core/migrations/2018_01_30_222900_change_users_rename_columns.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Database\Migration;
+
+return Migration::renameColumns('users', [
+ 'is_activated' => 'is_email_confirmed',
+ 'join_time' => 'joined_at',
+ 'last_seen_time' => 'last_seen_at',
+ 'discussions_count' => 'discussion_count',
+ 'comments_count' => 'comment_count',
+ 'read_time' => 'marked_all_as_read_at',
+ 'notifications_read_time' => 'read_notifications_at',
+ 'avatar_path' => 'avatar_url'
+]);
diff --git a/framework/core/migrations/2018_07_21_000000_seed_default_groups.php b/framework/core/migrations/2018_07_21_000000_seed_default_groups.php
new file mode 100644
index 000000000..12566ba02
--- /dev/null
+++ b/framework/core/migrations/2018_07_21_000000_seed_default_groups.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Group\Group;
+use Illuminate\Database\Schema\Builder;
+
+return [
+ 'up' => function (Builder $schema) {
+ $db = $schema->getConnection();
+
+ $groups = [
+ [Group::ADMINISTRATOR_ID, 'Admin', 'Admins', '#B72A2A', 'fas fa-wrench'],
+ [Group::GUEST_ID, 'Guest', 'Guests', null, null],
+ [Group::MEMBER_ID, 'Member', 'Members', null, null],
+ [Group::MODERATOR_ID, 'Mod', 'Mods', '#80349E', 'fas fa-bolt']
+ ];
+
+ foreach ($groups as $group) {
+ if ($db->table('groups')->where('id', $group[0])->exists()) {
+ continue;
+ }
+
+ $db->table('groups')->insert(array_combine(['id', 'name_singular', 'name_plural', 'color', 'icon'], $group));
+ }
+ },
+
+ 'down' => function (Builder $schema) {
+ // do nothing so as to preserve user data
+ }
+];
diff --git a/framework/core/migrations/2018_07_21_000100_seed_default_group_permissions.php b/framework/core/migrations/2018_07_21_000100_seed_default_group_permissions.php
new file mode 100644
index 000000000..5a82d70fe
--- /dev/null
+++ b/framework/core/migrations/2018_07_21_000100_seed_default_group_permissions.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Flarum\Group\Group;
+use Illuminate\Database\Schema\Builder;
+
+$rows = [
+ // Guests can view the forum
+ ['permission' => 'viewDiscussions', 'group_id' => Group::GUEST_ID],
+
+ // Members can create and reply to discussions, and view the user list
+ ['permission' => 'startDiscussion', 'group_id' => Group::MEMBER_ID],
+ ['permission' => 'discussion.reply', 'group_id' => Group::MEMBER_ID],
+ ['permission' => 'viewUserList', 'group_id' => Group::MEMBER_ID],
+
+ // Moderators can edit + delete stuff
+ ['permission' => 'discussion.hide', 'group_id' => Group::MODERATOR_ID],
+ ['permission' => 'discussion.editPosts', 'group_id' => Group::MODERATOR_ID],
+ ['permission' => 'discussion.hidePosts', 'group_id' => Group::MODERATOR_ID],
+ ['permission' => 'discussion.rename', 'group_id' => Group::MODERATOR_ID],
+ ['permission' => 'discussion.viewIpsPosts', 'group_id' => Group::MODERATOR_ID],
+];
+
+return [
+ 'up' => function (Builder $schema) use ($rows) {
+ $db = $schema->getConnection();
+
+ foreach ($rows as $row) {
+ if ($db->table('groups')->where('id', $row['group_id'])->doesntExist()) {
+ continue;
+ }
+
+ if ($db->table('group_permission')->where($row)->doesntExist()) {
+ $db->table('group_permission')->insert($row);
+ }
+ }
+ },
+
+ 'down' => function (Builder $schema) use ($rows) {
+ $db = $schema->getConnection();
+
+ foreach ($rows as $row) {
+ $db->table('group_permission')->where($row)->delete();
+ }
+ }
+];
diff --git a/framework/core/src/Api/ApiKey.php b/framework/core/src/Api/ApiKey.php
index baae2c01e..e8ebb48ec 100644
--- a/framework/core/src/Api/ApiKey.php
+++ b/framework/core/src/Api/ApiKey.php
@@ -14,22 +14,16 @@ namespace Flarum\Api;
use Flarum\Database\AbstractModel;
/**
- * @property string $id
+ * @property int $id
+ * @property string $key
+ * @property string|null $allowed_ips
+ * @property string|null $scopes
+ * @property int|null $user_id
+ * @property \Carbon\Carbon $created_at
+ * @property \Carbon\Carbon|null $last_activity_at
*/
class ApiKey extends AbstractModel
{
- /**
- * {@inheritdoc}
- */
- protected $table = 'api_keys';
-
- /**
- * Use a custom primary key for this model.
- *
- * @var bool
- */
- public $incrementing = false;
-
/**
* Generate an API key.
*
@@ -37,8 +31,10 @@ class ApiKey extends AbstractModel
*/
public static function generate()
{
- return new static([
- 'id' => str_random(40)
- ]);
+ $key = new static;
+
+ $key->key = str_random(40);
+
+ return $key;
}
}
diff --git a/framework/core/src/Api/Controller/CreateDiscussionController.php b/framework/core/src/Api/Controller/CreateDiscussionController.php
index a58418a1e..32caf0463 100644
--- a/framework/core/src/Api/Controller/CreateDiscussionController.php
+++ b/framework/core/src/Api/Controller/CreateDiscussionController.php
@@ -31,9 +31,9 @@ class CreateDiscussionController extends AbstractCreateController
*/
public $include = [
'posts',
- 'startUser',
- 'lastUser',
- 'startPost',
+ 'user',
+ 'lastPostedUser',
+ 'firstPost',
'lastPost'
];
diff --git a/framework/core/src/Api/Controller/CreatePostController.php b/framework/core/src/Api/Controller/CreatePostController.php
index b4c881137..03da2c4be 100644
--- a/framework/core/src/Api/Controller/CreatePostController.php
+++ b/framework/core/src/Api/Controller/CreatePostController.php
@@ -33,7 +33,7 @@ class CreatePostController extends AbstractCreateController
'user',
'discussion',
'discussion.posts',
- 'discussion.lastUser'
+ 'discussion.lastPostedUser'
];
/**
@@ -84,7 +84,7 @@ class CreatePostController extends AbstractCreateController
}
$discussion = $post->discussion;
- $discussion->posts = $discussion->posts()->whereVisibleTo($actor)->orderBy('time')->pluck('id');
+ $discussion->posts = $discussion->posts()->whereVisibleTo($actor)->orderBy('created_at')->pluck('id');
return $post;
}
diff --git a/framework/core/src/Api/Controller/CreateTokenController.php b/framework/core/src/Api/Controller/CreateTokenController.php
index 563bd9ed1..5be69724d 100644
--- a/framework/core/src/Api/Controller/CreateTokenController.php
+++ b/framework/core/src/Api/Controller/CreateTokenController.php
@@ -71,7 +71,7 @@ class CreateTokenController implements RequestHandlerInterface
$token->save();
return new JsonResponse([
- 'token' => $token->id,
+ 'token' => $token->token,
'userId' => $user->id
]);
}
diff --git a/framework/core/src/Api/Controller/ListDiscussionsController.php b/framework/core/src/Api/Controller/ListDiscussionsController.php
index 28a6e8bbc..c7022547f 100644
--- a/framework/core/src/Api/Controller/ListDiscussionsController.php
+++ b/framework/core/src/Api/Controller/ListDiscussionsController.php
@@ -30,8 +30,8 @@ class ListDiscussionsController extends AbstractListController
* {@inheritdoc}
*/
public $include = [
- 'startUser',
- 'lastUser',
+ 'user',
+ 'lastPostedUser',
'mostRelevantPost',
'mostRelevantPost.user'
];
@@ -40,14 +40,14 @@ class ListDiscussionsController extends AbstractListController
* {@inheritdoc}
*/
public $optionalInclude = [
- 'startPost',
+ 'firstPost',
'lastPost'
];
/**
* {@inheritdoc}
*/
- public $sortFields = ['lastTime', 'commentsCount', 'startTime'];
+ public $sortFields = ['lastPostedAt', 'commentCount', 'createdAt'];
/**
* @var DiscussionSearcher
@@ -98,7 +98,7 @@ class ListDiscussionsController extends AbstractListController
$results = $results->getResults()->load($load);
- if ($relations = array_intersect($load, ['startPost', 'lastPost'])) {
+ if ($relations = array_intersect($load, ['firstPost', 'lastPost'])) {
foreach ($results as $discussion) {
foreach ($relations as $relation) {
if ($discussion->$relation) {
diff --git a/framework/core/src/Api/Controller/ListNotificationsController.php b/framework/core/src/Api/Controller/ListNotificationsController.php
index b1bec9718..8a5a5ef7d 100644
--- a/framework/core/src/Api/Controller/ListNotificationsController.php
+++ b/framework/core/src/Api/Controller/ListNotificationsController.php
@@ -30,7 +30,7 @@ class ListNotificationsController extends AbstractListController
* {@inheritdoc}
*/
public $include = [
- 'sender',
+ 'fromUser',
'subject',
'subject.discussion'
];
diff --git a/framework/core/src/Api/Controller/ListPostsController.php b/framework/core/src/Api/Controller/ListPostsController.php
index 4818b54e9..f2740778d 100644
--- a/framework/core/src/Api/Controller/ListPostsController.php
+++ b/framework/core/src/Api/Controller/ListPostsController.php
@@ -32,15 +32,15 @@ class ListPostsController extends AbstractListController
public $include = [
'user',
'user.groups',
- 'editUser',
- 'hideUser',
+ 'editedUser',
+ 'hiddenUser',
'discussion'
];
/**
* {@inheritdoc}
*/
- public $sortFields = ['time'];
+ public $sortFields = ['createdAt'];
/**
* @var \Flarum\Post\PostRepository
@@ -120,7 +120,7 @@ class ListPostsController extends AbstractListController
$query->skip($offset)->take($limit);
foreach ((array) $sort as $field => $order) {
- $query->orderBy($field, $order);
+ $query->orderBy(snake_case($field), $order);
}
return $query->pluck('id')->all();
diff --git a/framework/core/src/Api/Controller/ListUsersController.php b/framework/core/src/Api/Controller/ListUsersController.php
index b04b17233..d37e191f9 100644
--- a/framework/core/src/Api/Controller/ListUsersController.php
+++ b/framework/core/src/Api/Controller/ListUsersController.php
@@ -36,10 +36,10 @@ class ListUsersController extends AbstractListController
*/
public $sortFields = [
'username',
- 'commentsCount',
- 'discussionsCount',
- 'lastSeenTime',
- 'joinTime'
+ 'commentCount',
+ 'discussionCount',
+ 'lastSeenAt',
+ 'joinedAt'
];
/**
diff --git a/framework/core/src/Api/Controller/SendConfirmationEmailController.php b/framework/core/src/Api/Controller/SendConfirmationEmailController.php
index 54bde47db..91244dd01 100644
--- a/framework/core/src/Api/Controller/SendConfirmationEmailController.php
+++ b/framework/core/src/Api/Controller/SendConfirmationEmailController.php
@@ -81,7 +81,7 @@ class SendConfirmationEmailController implements RequestHandlerInterface
$data = [
'{username}' => $actor->username,
- '{url}' => $this->url->to('forum')->route('confirmEmail', ['token' => $token->id]),
+ '{url}' => $this->url->to('forum')->route('confirmEmail', ['token' => $token->token]),
'{forum}' => $this->settings->get('forum_title')
];
diff --git a/framework/core/src/Api/Controller/ShowDiscussionController.php b/framework/core/src/Api/Controller/ShowDiscussionController.php
index ba1c43684..ae30f47fc 100644
--- a/framework/core/src/Api/Controller/ShowDiscussionController.php
+++ b/framework/core/src/Api/Controller/ShowDiscussionController.php
@@ -44,17 +44,17 @@ class ShowDiscussionController extends AbstractShowController
'posts.discussion',
'posts.user',
'posts.user.groups',
- 'posts.editUser',
- 'posts.hideUser'
+ 'posts.editedUser',
+ 'posts.hiddenUser'
];
/**
* {@inheritdoc}
*/
public $optionalInclude = [
- 'startUser',
- 'lastUser',
- 'startPost',
+ 'user',
+ 'lastPostedUser',
+ 'firstPost',
'lastPost'
];
@@ -118,7 +118,7 @@ class ShowDiscussionController extends AbstractShowController
*/
private function loadPostIds(Discussion $discussion, User $actor)
{
- return $discussion->posts()->whereVisibleTo($actor)->orderBy('time')->pluck('id')->all();
+ return $discussion->posts()->whereVisibleTo($actor)->orderBy('created_at')->pluck('id')->all();
}
/**
@@ -172,7 +172,7 @@ class ShowDiscussionController extends AbstractShowController
{
$query = $discussion->posts()->whereVisibleTo($actor);
- $query->orderBy('time')->skip($offset)->take($limit)->with($include);
+ $query->orderBy('created_at')->skip($offset)->take($limit)->with($include);
$posts = $query->get()->all();
diff --git a/framework/core/src/Api/Controller/ShowPostController.php b/framework/core/src/Api/Controller/ShowPostController.php
index f269b7ebc..dcec04ba9 100644
--- a/framework/core/src/Api/Controller/ShowPostController.php
+++ b/framework/core/src/Api/Controller/ShowPostController.php
@@ -29,8 +29,8 @@ class ShowPostController extends AbstractShowController
public $include = [
'user',
'user.groups',
- 'editUser',
- 'hideUser',
+ 'editedUser',
+ 'hiddenUser',
'discussion'
];
diff --git a/framework/core/src/Api/Controller/UpdateDiscussionController.php b/framework/core/src/Api/Controller/UpdateDiscussionController.php
index 86b0f8152..5cf67a5d5 100644
--- a/framework/core/src/Api/Controller/UpdateDiscussionController.php
+++ b/framework/core/src/Api/Controller/UpdateDiscussionController.php
@@ -54,7 +54,7 @@ class UpdateDiscussionController extends AbstractShowController
// TODO: Refactor the ReadDiscussion (state) command into EditDiscussion?
// That's what extensions will do anyway.
- if ($readNumber = array_get($data, 'attributes.readNumber')) {
+ if ($readNumber = array_get($data, 'attributes.lastReadPostNumber')) {
$state = $this->bus->dispatch(
new ReadDiscussion($discussionId, $actor, $readNumber)
);
@@ -64,7 +64,7 @@ class UpdateDiscussionController extends AbstractShowController
if ($posts = $discussion->getModifiedPosts()) {
$posts = (new Collection($posts))->load('discussion', 'user');
- $discussionPosts = $discussion->posts()->whereVisibleTo($actor)->orderBy('time')->pluck('id')->all();
+ $discussionPosts = $discussion->posts()->whereVisibleTo($actor)->oldest()->pluck('id')->all();
foreach ($discussionPosts as &$id) {
foreach ($posts as $post) {
diff --git a/framework/core/src/Api/Controller/UpdatePostController.php b/framework/core/src/Api/Controller/UpdatePostController.php
index fe2db5ed9..7ec36e8e5 100644
--- a/framework/core/src/Api/Controller/UpdatePostController.php
+++ b/framework/core/src/Api/Controller/UpdatePostController.php
@@ -28,7 +28,7 @@ class UpdatePostController extends AbstractShowController
* {@inheritdoc}
*/
public $include = [
- 'editUser',
+ 'editedUser',
'discussion'
];
diff --git a/framework/core/src/Api/Serializer/BasicDiscussionSerializer.php b/framework/core/src/Api/Serializer/BasicDiscussionSerializer.php
index 026082a23..2157fd3f7 100644
--- a/framework/core/src/Api/Serializer/BasicDiscussionSerializer.php
+++ b/framework/core/src/Api/Serializer/BasicDiscussionSerializer.php
@@ -44,7 +44,7 @@ class BasicDiscussionSerializer extends AbstractSerializer
/**
* @return \Tobscure\JsonApi\Relationship
*/
- protected function startUser($discussion)
+ protected function user($discussion)
{
return $this->hasOne($discussion, BasicUserSerializer::class);
}
@@ -52,7 +52,7 @@ class BasicDiscussionSerializer extends AbstractSerializer
/**
* @return \Tobscure\JsonApi\Relationship
*/
- protected function startPost($discussion)
+ protected function firstPost($discussion)
{
return $this->hasOne($discussion, BasicPostSerializer::class);
}
@@ -60,7 +60,7 @@ class BasicDiscussionSerializer extends AbstractSerializer
/**
* @return \Tobscure\JsonApi\Relationship
*/
- protected function lastUser($discussion)
+ protected function lastPostedUser($discussion)
{
return $this->hasOne($discussion, BasicUserSerializer::class);
}
@@ -92,7 +92,7 @@ class BasicDiscussionSerializer extends AbstractSerializer
/**
* @return \Tobscure\JsonApi\Relationship
*/
- protected function hideUser($discussion)
+ protected function hiddenUser($discussion)
{
return $this->hasOne($discussion, BasicUserSerializer::class);
}
diff --git a/framework/core/src/Api/Serializer/BasicPostSerializer.php b/framework/core/src/Api/Serializer/BasicPostSerializer.php
index 103bd8a06..b12c28175 100644
--- a/framework/core/src/Api/Serializer/BasicPostSerializer.php
+++ b/framework/core/src/Api/Serializer/BasicPostSerializer.php
@@ -39,7 +39,7 @@ class BasicPostSerializer extends AbstractSerializer
$attributes = [
'id' => (int) $post->id,
'number' => (int) $post->number,
- 'time' => $this->formatDate($post->time),
+ 'createdAt' => $this->formatDate($post->created_at),
'contentType' => $post->type
];
diff --git a/framework/core/src/Api/Serializer/CurrentUserSerializer.php b/framework/core/src/Api/Serializer/CurrentUserSerializer.php
index 87dd1490c..cea92bca2 100644
--- a/framework/core/src/Api/Serializer/CurrentUserSerializer.php
+++ b/framework/core/src/Api/Serializer/CurrentUserSerializer.php
@@ -14,18 +14,19 @@ namespace Flarum\Api\Serializer;
class CurrentUserSerializer extends UserSerializer
{
/**
- * {@inheritdoc}
+ * @param \Flarum\User\User $user
+ * @return array
*/
protected function getDefaultAttributes($user)
{
$attributes = parent::getDefaultAttributes($user);
$attributes += [
- 'isActivated' => (bool) $user->is_activated,
+ 'isEmailConfirmed' => (bool) $user->is_email_confirmed,
'email' => $user->email,
- 'readTime' => $this->formatDate($user->read_time),
- 'unreadNotificationsCount' => (int) $user->getUnreadNotificationsCount(),
- 'newNotificationsCount' => (int) $user->getNewNotificationsCount(),
+ 'markedAllAsReadAt' => $this->formatDate($user->marked_all_as_read_at),
+ 'unreadNotificationCount' => (int) $user->getUnreadNotificationCount(),
+ 'newNotificationCount' => (int) $user->getNewNotificationCount(),
'preferences' => (array) $user->preferences
];
diff --git a/framework/core/src/Api/Serializer/DiscussionSerializer.php b/framework/core/src/Api/Serializer/DiscussionSerializer.php
index 88e5fb8fe..09e8b1256 100644
--- a/framework/core/src/Api/Serializer/DiscussionSerializer.php
+++ b/framework/core/src/Api/Serializer/DiscussionSerializer.php
@@ -37,10 +37,10 @@ class DiscussionSerializer extends BasicDiscussionSerializer
$gate = $this->gate->forUser($this->actor);
$attributes = parent::getDefaultAttributes($discussion) + [
- 'commentsCount' => (int) $discussion->comments_count,
- 'participantsCount' => (int) $discussion->participants_count,
- 'startTime' => $this->formatDate($discussion->start_time),
- 'lastTime' => $this->formatDate($discussion->last_time),
+ 'commentCount' => (int) $discussion->comment_count,
+ 'participantCount' => (int) $discussion->participant_count,
+ 'createdAt' => $this->formatDate($discussion->created_at),
+ 'lastPostedAt' => $this->formatDate($discussion->last_posted_at),
'lastPostNumber' => (int) $discussion->last_post_number,
'canReply' => $gate->allows('reply', $discussion),
'canRename' => $gate->allows('rename', $discussion),
@@ -48,17 +48,17 @@ class DiscussionSerializer extends BasicDiscussionSerializer
'canHide' => $gate->allows('hide', $discussion)
];
- if ($discussion->hide_time) {
+ if ($discussion->hidden_at) {
$attributes['isHidden'] = true;
- $attributes['hideTime'] = $this->formatDate($discussion->hide_time);
+ $attributes['hiddenAt'] = $this->formatDate($discussion->hidden_at);
}
Discussion::setStateUser($this->actor);
if ($state = $discussion->state) {
$attributes += [
- 'readTime' => $this->formatDate($state->read_time),
- 'readNumber' => (int) $state->read_number
+ 'lastReadAt' => $this->formatDate($state->last_read_at),
+ 'lastReadPostNumber' => (int) $state->last_read_post_number
];
}
diff --git a/framework/core/src/Api/Serializer/NotificationSerializer.php b/framework/core/src/Api/Serializer/NotificationSerializer.php
index 55b809079..deaeeddbe 100644
--- a/framework/core/src/Api/Serializer/NotificationSerializer.php
+++ b/framework/core/src/Api/Serializer/NotificationSerializer.php
@@ -47,12 +47,13 @@ class NotificationSerializer extends AbstractSerializer
'id' => (int) $notification->id,
'contentType' => $notification->type,
'content' => $notification->data,
- 'time' => $this->formatDate($notification->time),
- 'isRead' => (bool) $notification->is_read
+ 'createdAt' => $this->formatDate($notification->created_at),
+ 'isRead' => (bool) $notification->read_at
];
}
/**
+ * @param Notification $notification
* @return \Tobscure\JsonApi\Relationship
*/
protected function user($notification)
@@ -61,14 +62,16 @@ class NotificationSerializer extends AbstractSerializer
}
/**
+ * @param Notification $notification
* @return \Tobscure\JsonApi\Relationship
*/
- protected function sender($notification)
+ protected function fromUser($notification)
{
return $this->hasOne($notification, BasicUserSerializer::class);
}
/**
+ * @param Notification $notification
* @return \Tobscure\JsonApi\Relationship
*/
protected function subject($notification)
diff --git a/framework/core/src/Api/Serializer/PostSerializer.php b/framework/core/src/Api/Serializer/PostSerializer.php
index ca2cfaf13..44ec76460 100644
--- a/framework/core/src/Api/Serializer/PostSerializer.php
+++ b/framework/core/src/Api/Serializer/PostSerializer.php
@@ -55,13 +55,13 @@ class PostSerializer extends BasicPostSerializer
$attributes['content'] = $post->content;
}
- if ($post->edit_time) {
- $attributes['editTime'] = $this->formatDate($post->edit_time);
+ if ($post->edited_at) {
+ $attributes['editedAt'] = $this->formatDate($post->edited_at);
}
- if ($post->hide_time) {
+ if ($post->hidden_at) {
$attributes['isHidden'] = true;
- $attributes['hideTime'] = $this->formatDate($post->hide_time);
+ $attributes['hiddenAt'] = $this->formatDate($post->hidden_at);
}
$attributes += [
@@ -92,7 +92,7 @@ class PostSerializer extends BasicPostSerializer
/**
* @return \Tobscure\JsonApi\Relationship
*/
- protected function editUser($post)
+ protected function editedUser($post)
{
return $this->hasOne($post, BasicUserSerializer::class);
}
@@ -100,7 +100,7 @@ class PostSerializer extends BasicPostSerializer
/**
* @return \Tobscure\JsonApi\Relationship
*/
- protected function hideUser($post)
+ protected function hiddenUser($post)
{
return $this->hasOne($post, BasicUserSerializer::class);
}
diff --git a/framework/core/src/Api/Serializer/UserSerializer.php b/framework/core/src/Api/Serializer/UserSerializer.php
index 8a521f6cb..95792699d 100644
--- a/framework/core/src/Api/Serializer/UserSerializer.php
+++ b/framework/core/src/Api/Serializer/UserSerializer.php
@@ -29,7 +29,8 @@ class UserSerializer extends BasicUserSerializer
}
/**
- * {@inheritdoc}
+ * @param \Flarum\User\User $user
+ * @return array
*/
protected function getDefaultAttributes($user)
{
@@ -40,23 +41,23 @@ class UserSerializer extends BasicUserSerializer
$canEdit = $gate->allows('edit', $user);
$attributes += [
- 'joinTime' => $this->formatDate($user->join_time),
- 'discussionsCount' => (int) $user->discussions_count,
- 'commentsCount' => (int) $user->comments_count,
+ 'joinTime' => $this->formatDate($user->joined_at),
+ 'discussionCount' => (int) $user->discussion_count,
+ 'commentCount' => (int) $user->comment_count,
'canEdit' => $canEdit,
'canDelete' => $gate->allows('delete', $user),
];
if ($user->getPreference('discloseOnline')) {
$attributes += [
- 'lastSeenTime' => $this->formatDate($user->last_seen_time)
+ 'lastSeenAt' => $this->formatDate($user->last_seen_at)
];
}
if ($canEdit || $this->actor->id === $user->id) {
$attributes += [
- 'isActivated' => (bool) $user->is_activated,
- 'email' => $user->email
+ 'isEmailConfirmed' => (bool) $user->is_email_confirmed,
+ 'email' => $user->email
];
}
diff --git a/framework/core/src/Database/Migration.php b/framework/core/src/Database/Migration.php
index 79332813d..58f23acba 100644
--- a/framework/core/src/Database/Migration.php
+++ b/framework/core/src/Database/Migration.php
@@ -78,16 +78,28 @@ abstract class Migration
* Rename a column.
*/
public static function renameColumn($tableName, $from, $to)
+ {
+ return static::renameColumns($tableName, [$from => $to]);
+ }
+
+ /**
+ * Rename multiple columns.
+ */
+ public static function renameColumns($tableName, array $columnNames)
{
return [
- 'up' => function (Builder $schema) use ($tableName, $from, $to) {
- $schema->table($tableName, function (Blueprint $table) use ($from, $to) {
- $table->renameColumn($from, $to);
+ 'up' => function (Builder $schema) use ($tableName, $columnNames) {
+ $schema->table($tableName, function (Blueprint $table) use ($columnNames) {
+ foreach ($columnNames as $from => $to) {
+ $table->renameColumn($from, $to);
+ }
});
},
- 'down' => function (Builder $schema) use ($tableName, $from, $to) {
- $schema->table($tableName, function (Blueprint $table) use ($from, $to) {
- $table->renameColumn($to, $from);
+ 'down' => function (Builder $schema) use ($tableName, $columnNames) {
+ $schema->table($tableName, function (Blueprint $table) use ($columnNames) {
+ foreach ($columnNames as $to => $from) {
+ $table->renameColumn($from, $to);
+ }
});
}
];
@@ -125,11 +137,11 @@ abstract class Migration
*/
public static function addPermissions(array $permissions)
{
- $keys = [];
+ $rows = [];
foreach ($permissions as $permission => $groups) {
foreach ((array) $groups as $group) {
- $keys[] = [
+ $rows[] = [
'group_id' => $group,
'permission' => $permission,
];
@@ -137,23 +149,27 @@ abstract class Migration
}
return [
- 'up' => function (Builder $schema) use ($keys) {
+ 'up' => function (Builder $schema) use ($rows) {
$db = $schema->getConnection();
- foreach ($keys as $key) {
- $instance = $db->table('permissions')->where($key)->first();
-
- if (is_null($instance)) {
- $db->table('permissions')->insert($key);
+ foreach ($rows as $row) {
+ if ($db->table('group_permission')->where($row)->exists()) {
+ continue;
}
+
+ if ($db->table('groups')->where('id', $row['group_id'])->doesntExist()) {
+ continue;
+ }
+
+ $db->table('group_permission')->insert($row);
}
},
- 'down' => function (Builder $schema) use ($keys) {
+ 'down' => function (Builder $schema) use ($rows) {
$db = $schema->getConnection();
- foreach ($keys as $key) {
- $db->table('permissions')->where($key)->delete();
+ foreach ($rows as $row) {
+ $db->table('group_permission')->where($row)->delete();
}
}
];
diff --git a/framework/core/src/Database/Migrator.php b/framework/core/src/Database/Migrator.php
index 2c1272fc0..8d411887c 100644
--- a/framework/core/src/Database/Migrator.php
+++ b/framework/core/src/Database/Migrator.php
@@ -63,6 +63,9 @@ class Migrator
$this->repository = $repository;
$this->schemaBuilder = $connection->getSchemaBuilder();
+
+ // Workaround for https://github.com/laravel/framework/issues/1186
+ $connection->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
}
/**
diff --git a/framework/core/src/Discussion/Command/ReadDiscussion.php b/framework/core/src/Discussion/Command/ReadDiscussion.php
index c6a8826e3..27a2fce9a 100644
--- a/framework/core/src/Discussion/Command/ReadDiscussion.php
+++ b/framework/core/src/Discussion/Command/ReadDiscussion.php
@@ -34,17 +34,17 @@ class ReadDiscussion
*
* @var int
*/
- public $readNumber;
+ public $lastReadPostNumber;
/**
* @param int $discussionId The ID of the discussion to mark as read.
* @param User $actor The user to mark the discussion as read for.
- * @param int $readNumber The number of the post to mark as read.
+ * @param int $lastReadPostNumber The number of the post to mark as read.
*/
- public function __construct($discussionId, User $actor, $readNumber)
+ public function __construct($discussionId, User $actor, $lastReadPostNumber)
{
$this->discussionId = $discussionId;
$this->actor = $actor;
- $this->readNumber = $readNumber;
+ $this->lastReadPostNumber = $lastReadPostNumber;
}
}
diff --git a/framework/core/src/Discussion/Command/ReadDiscussionHandler.php b/framework/core/src/Discussion/Command/ReadDiscussionHandler.php
index d29529b1e..9aad6ab92 100644
--- a/framework/core/src/Discussion/Command/ReadDiscussionHandler.php
+++ b/framework/core/src/Discussion/Command/ReadDiscussionHandler.php
@@ -51,7 +51,7 @@ class ReadDiscussionHandler
$discussion = $this->discussions->findOrFail($command->discussionId, $actor);
$state = $discussion->stateFor($actor);
- $state->read($command->readNumber);
+ $state->read($command->lastReadPostNumber);
$this->events->dispatch(
new UserDataSaving($state)
diff --git a/framework/core/src/Discussion/Command/StartDiscussion.php b/framework/core/src/Discussion/Command/StartDiscussion.php
index 3b564c0f1..adb99a34e 100644
--- a/framework/core/src/Discussion/Command/StartDiscussion.php
+++ b/framework/core/src/Discussion/Command/StartDiscussion.php
@@ -30,10 +30,18 @@ class StartDiscussion
public $data;
/**
- * @param User $actor The user authoring the discussion.
- * @param array $data The discussion attributes.
+ * The current ip address of the actor.
+ *
+ * @var string
*/
- public function __construct(User $actor, array $data, $ipAddress)
+ public $ipAddress;
+
+ /**
+ * @param User $actor The user authoring the discussion.
+ * @param array $data The discussion attributes.
+ * @param string $ipAddress The current ip address of the actor.
+ */
+ public function __construct(User $actor, array $data, string $ipAddress)
{
$this->actor = $actor;
$this->data = $data;
diff --git a/framework/core/src/Discussion/Command/StartDiscussionHandler.php b/framework/core/src/Discussion/Command/StartDiscussionHandler.php
index bc4fbb47b..cbc7f9559 100644
--- a/framework/core/src/Discussion/Command/StartDiscussionHandler.php
+++ b/framework/core/src/Discussion/Command/StartDiscussionHandler.php
@@ -94,7 +94,7 @@ class StartDiscussionHandler
// attributes as posting the reply will have changed some of them (e.g.
// last_time.)
$discussion->setRawAttributes($post->discussion->getAttributes(), true);
- $discussion->setStartPost($post);
+ $discussion->setFirstPost($post);
$discussion->setLastPost($post);
$this->dispatchEventsFor($discussion, $actor);
diff --git a/framework/core/src/Discussion/Discussion.php b/framework/core/src/Discussion/Discussion.php
index 116f1484c..930cf3084 100644
--- a/framework/core/src/Discussion/Discussion.php
+++ b/framework/core/src/Discussion/Discussion.php
@@ -11,6 +11,7 @@
namespace Flarum\Discussion;
+use Carbon\Carbon;
use Flarum\Database\AbstractModel;
use Flarum\Database\ScopeVisibilityTrait;
use Flarum\Discussion\Event\Deleted;
@@ -20,7 +21,6 @@ use Flarum\Discussion\Event\Restored;
use Flarum\Discussion\Event\Started;
use Flarum\Event\GetModelIsPrivate;
use Flarum\Foundation\EventGeneratorTrait;
-use Flarum\Post\Event\Deleted as PostDeleted;
use Flarum\Post\MergeableInterface;
use Flarum\Post\Post;
use Flarum\User\User;
@@ -30,26 +30,26 @@ use Flarum\Util\Str;
* @property int $id
* @property string $title
* @property string $slug
- * @property int $comments_count
- * @property int $participants_count
- * @property int $number_index
- * @property \Carbon\Carbon $start_time
- * @property int|null $start_user_id
- * @property int|null $start_post_id
- * @property \Carbon\Carbon|null $last_time
- * @property int|null $last_user_id
+ * @property int $comment_count
+ * @property int $participant_count
+ * @property int $post_number_index
+ * @property \Carbon\Carbon $created_at
+ * @property int|null $user_id
+ * @property int|null $first_post_id
+ * @property \Carbon\Carbon|null $last_posted_at
+ * @property int|null $last_posted_user_id
* @property int|null $last_post_id
* @property int|null $last_post_number
- * @property \Carbon\Carbon|null $hide_time
- * @property int|null $hide_user_id
+ * @property \Carbon\Carbon|null $hidden_at
+ * @property int|null $hidden_user_id
* @property UserState|null $state
* @property \Illuminate\Database\Eloquent\Collection $posts
* @property \Illuminate\Database\Eloquent\Collection $comments
* @property \Illuminate\Database\Eloquent\Collection $participants
- * @property Post|null $startPost
- * @property User|null $startUser
+ * @property Post|null $firstPost
+ * @property User|null $user
* @property Post|null $lastPost
- * @property User|null $lastUser
+ * @property User|null $lastPostedUser
* @property \Illuminate\Database\Eloquent\Collection $readers
* @property bool $is_private
*/
@@ -58,11 +58,6 @@ class Discussion extends AbstractModel
use EventGeneratorTrait;
use ScopeVisibilityTrait;
- /**
- * {@inheritdoc}
- */
- protected $table = 'discussions';
-
/**
* An array of posts that have been modified during this request.
*
@@ -71,12 +66,14 @@ class Discussion extends AbstractModel
protected $modifiedPosts = [];
/**
- * {@inheritdoc}
+ * The attributes that should be mutated to dates.
+ *
+ * @var array
*/
- protected $dates = ['start_time', 'last_time', 'hide_time'];
+ protected $dates = ['created_at', 'last_posted_at', 'hidden_at'];
/**
- * Casts properties to a specific type.
+ * The attributes that should be cast to native types.
*
* @var array
*/
@@ -102,21 +99,6 @@ class Discussion extends AbstractModel
static::deleted(function (Discussion $discussion) {
$discussion->raise(new Deleted($discussion));
-
- // Delete all of the posts in the discussion. Before we delete them
- // in a big batch query, we will loop through them and raise a
- // PostWasDeleted event for each post.
- $posts = $discussion->posts()->allTypes();
-
- foreach ($posts->cursor() as $post) {
- $discussion->raise(new PostDeleted($post));
- }
-
- $posts->delete();
-
- // Delete all of the 'state' records for all of the users who have
- // read the discussion.
- $discussion->readers()->detach();
});
static::saving(function (Discussion $discussion) {
@@ -138,10 +120,10 @@ class Discussion extends AbstractModel
$discussion = new static;
$discussion->title = $title;
- $discussion->start_time = time();
- $discussion->start_user_id = $user->id;
+ $discussion->created_at = Carbon::now();
+ $discussion->user_id = $user->id;
- $discussion->setRelation('startUser', $user);
+ $discussion->setRelation('user', $user);
$discussion->raise(new Started($discussion));
@@ -174,9 +156,9 @@ class Discussion extends AbstractModel
*/
public function hide(User $actor = null)
{
- if (! $this->hide_time) {
- $this->hide_time = time();
- $this->hide_user_id = $actor ? $actor->id : null;
+ if (! $this->hidden_at) {
+ $this->hidden_at = Carbon::now();
+ $this->hidden_user_id = $actor ? $actor->id : null;
$this->raise(new Hidden($this));
}
@@ -191,9 +173,9 @@ class Discussion extends AbstractModel
*/
public function restore()
{
- if ($this->hide_time !== null) {
- $this->hide_time = null;
- $this->hide_user_id = null;
+ if ($this->hidden_at !== null) {
+ $this->hidden_at = null;
+ $this->hidden_user_id = null;
$this->raise(new Restored($this));
}
@@ -202,16 +184,16 @@ class Discussion extends AbstractModel
}
/**
- * Set the discussion's start post details.
+ * Set the discussion's first post details.
*
* @param Post $post
* @return $this
*/
- public function setStartPost(Post $post)
+ public function setFirstPost(Post $post)
{
- $this->start_time = $post->time;
- $this->start_user_id = $post->user_id;
- $this->start_post_id = $post->id;
+ $this->created_at = $post->created_at;
+ $this->user_id = $post->user_id;
+ $this->first_post_id = $post->id;
return $this;
}
@@ -224,8 +206,8 @@ class Discussion extends AbstractModel
*/
public function setLastPost(Post $post)
{
- $this->last_time = $post->time;
- $this->last_user_id = $post->user_id;
+ $this->last_posted_at = $post->created_at;
+ $this->last_posted_user_id = $post->user_id;
$this->last_post_id = $post->id;
$this->last_post_number = $post->number;
@@ -240,7 +222,7 @@ class Discussion extends AbstractModel
public function refreshLastPost()
{
/** @var Post $lastPost */
- if ($lastPost = $this->comments()->latest('time')->first()) {
+ if ($lastPost = $this->comments()->latest()->first()) {
$this->setLastPost($lastPost);
}
@@ -248,25 +230,25 @@ class Discussion extends AbstractModel
}
/**
- * Refresh the discussion's comments count.
+ * Refresh the discussion's comment count.
*
* @return $this
*/
- public function refreshCommentsCount()
+ public function refreshCommentCount()
{
- $this->comments_count = $this->comments()->count();
+ $this->comment_count = $this->comments()->count();
return $this;
}
/**
- * Refresh the discussion's participants count.
+ * Refresh the discussion's participant count.
*
* @return $this
*/
- public function refreshParticipantsCount()
+ public function refreshParticipantCount()
{
- $this->participants_count = $this->participants()->count('users.id');
+ $this->participant_count = $this->participants()->count('users.id');
return $this;
}
@@ -286,7 +268,7 @@ class Discussion extends AbstractModel
*/
public function mergePost(MergeableInterface $post)
{
- $lastPost = $this->posts()->latest('time')->first();
+ $lastPost = $this->posts()->latest()->first();
$post = $post->saveAfter($lastPost);
@@ -322,7 +304,7 @@ class Discussion extends AbstractModel
{
return $this->posts()
->where('is_private', false)
- ->whereNull('hide_time')
+ ->whereNull('hidden_at')
->where('type', 'comment');
}
@@ -347,9 +329,9 @@ class Discussion extends AbstractModel
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
- public function startPost()
+ public function firstPost()
{
- return $this->belongsTo(Post::class, 'start_post_id');
+ return $this->belongsTo(Post::class, 'first_post_id');
}
/**
@@ -357,9 +339,9 @@ class Discussion extends AbstractModel
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
- public function startUser()
+ public function user()
{
- return $this->belongsTo(User::class, 'start_user_id');
+ return $this->belongsTo(User::class, 'user_id');
}
/**
@@ -377,9 +359,9 @@ class Discussion extends AbstractModel
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
- public function lastUser()
+ public function lastPostedUser()
{
- return $this->belongsTo(User::class, 'last_user_id');
+ return $this->belongsTo(User::class, 'last_posted_user_id');
}
/**
@@ -399,7 +381,7 @@ class Discussion extends AbstractModel
*/
public function readers()
{
- return $this->belongsToMany(User::class, 'users_discussions');
+ return $this->belongsToMany(User::class);
}
/**
diff --git a/framework/core/src/Discussion/DiscussionMetadataUpdater.php b/framework/core/src/Discussion/DiscussionMetadataUpdater.php
index b1f894f4c..27a162fd0 100644
--- a/framework/core/src/Discussion/DiscussionMetadataUpdater.php
+++ b/framework/core/src/Discussion/DiscussionMetadataUpdater.php
@@ -39,9 +39,9 @@ class DiscussionMetadataUpdater
$discussion = $event->post->discussion;
if ($discussion && $discussion->exists) {
- $discussion->refreshCommentsCount();
+ $discussion->refreshCommentCount();
$discussion->refreshLastPost();
- $discussion->refreshParticipantsCount();
+ $discussion->refreshParticipantCount();
$discussion->save();
}
}
@@ -76,8 +76,8 @@ class DiscussionMetadataUpdater
$discussion = $event->post->discussion;
if ($discussion && $discussion->exists) {
- $discussion->refreshCommentsCount();
- $discussion->refreshParticipantsCount();
+ $discussion->refreshCommentCount();
+ $discussion->refreshParticipantCount();
$discussion->refreshLastPost();
$discussion->save();
}
@@ -91,8 +91,8 @@ class DiscussionMetadataUpdater
$discussion = $post->discussion;
if ($discussion && $discussion->exists) {
- $discussion->refreshCommentsCount();
- $discussion->refreshParticipantsCount();
+ $discussion->refreshCommentCount();
+ $discussion->refreshParticipantCount();
if ($discussion->last_post_id == $post->id) {
$discussion->refreshLastPost();
diff --git a/framework/core/src/Discussion/DiscussionPolicy.php b/framework/core/src/Discussion/DiscussionPolicy.php
index d0b4e1c18..b0d462f80 100644
--- a/framework/core/src/Discussion/DiscussionPolicy.php
+++ b/framework/core/src/Discussion/DiscussionPolicy.php
@@ -91,8 +91,8 @@ class DiscussionPolicy extends AbstractPolicy
// user, or the current user has permission to view hidden discussions.
if (! $actor->hasPermission('discussion.hide')) {
$query->where(function ($query) use ($actor) {
- $query->whereNull('discussions.hide_time')
- ->orWhere('start_user_id', $actor->id)
+ $query->whereNull('discussions.hidden_at')
+ ->orWhere('discussions.user_id', $actor->id)
->orWhere(function ($query) use ($actor) {
$this->events->dispatch(
new ScopeModelVisibility($query, $actor, 'hide')
@@ -105,8 +105,8 @@ class DiscussionPolicy extends AbstractPolicy
// current user, or the user is allowed to edit the discussion's posts.
if (! $actor->hasPermission('discussion.editPosts')) {
$query->where(function ($query) use ($actor) {
- $query->where('comments_count', '>', 0)
- ->orWhere('start_user_id', $actor->id)
+ $query->where('discussions.comment_count', '>', 0)
+ ->orWhere('discussions.user_id', $actor->id)
->orWhere(function ($query) use ($actor) {
$this->events->dispatch(
new ScopeModelVisibility($query, $actor, 'editPosts')
@@ -123,12 +123,12 @@ class DiscussionPolicy extends AbstractPolicy
*/
public function rename(User $actor, Discussion $discussion)
{
- if ($discussion->start_user_id == $actor->id) {
+ if ($discussion->user_id == $actor->id) {
$allowRenaming = $this->settings->get('allow_renaming');
if ($allowRenaming === '-1'
- || ($allowRenaming === 'reply' && $discussion->participants_count <= 1)
- || ($discussion->start_time->diffInMinutes() < $allowRenaming)) {
+ || ($allowRenaming === 'reply' && $discussion->participant_count <= 1)
+ || ($discussion->created_at->diffInMinutes() < $allowRenaming)) {
return true;
}
}
@@ -141,7 +141,7 @@ class DiscussionPolicy extends AbstractPolicy
*/
public function hide(User $actor, Discussion $discussion)
{
- if ($discussion->start_user_id == $actor->id && $discussion->participants_count <= 1) {
+ if ($discussion->user_id == $actor->id && $discussion->participant_count <= 1) {
return true;
}
}
diff --git a/framework/core/src/Discussion/DiscussionRenamedLogger.php b/framework/core/src/Discussion/DiscussionRenamedLogger.php
index 961bde5fb..8c014794e 100644
--- a/framework/core/src/Discussion/DiscussionRenamedLogger.php
+++ b/framework/core/src/Discussion/DiscussionRenamedLogger.php
@@ -54,11 +54,11 @@ class DiscussionRenamedLogger
$post = $event->discussion->mergePost($post);
- if ($event->discussion->start_user_id !== $event->actor->id) {
+ if ($event->discussion->user_id !== $event->actor->id) {
$blueprint = new DiscussionRenamedBlueprint($post);
if ($post->exists) {
- $this->notifications->sync($blueprint, [$event->discussion->startUser]);
+ $this->notifications->sync($blueprint, [$event->discussion->user]);
} else {
$this->notifications->delete($blueprint);
}
diff --git a/framework/core/src/Discussion/DiscussionRepository.php b/framework/core/src/Discussion/DiscussionRepository.php
index 21abd39a0..588132d7b 100644
--- a/framework/core/src/Discussion/DiscussionRepository.php
+++ b/framework/core/src/Discussion/DiscussionRepository.php
@@ -49,9 +49,9 @@ class DiscussionRepository
*/
public function getReadIds(User $user)
{
- return Discussion::leftJoin('users_discussions', 'users_discussions.discussion_id', '=', 'discussions.id')
+ return Discussion::leftJoin('discussions_users', 'discussions_users.discussion_id', '=', 'discussions.id')
->where('user_id', $user->id)
- ->whereRaw('read_number >= last_post_number')
+ ->whereColumn('last_read_post_number', '>=', 'last_post_number')
->pluck('id')
->all();
}
diff --git a/framework/core/src/Discussion/Search/DiscussionSearch.php b/framework/core/src/Discussion/Search/DiscussionSearch.php
index 8cb886e29..3b1fba7e1 100644
--- a/framework/core/src/Discussion/Search/DiscussionSearch.php
+++ b/framework/core/src/Discussion/Search/DiscussionSearch.php
@@ -23,7 +23,7 @@ class DiscussionSearch extends AbstractSearch
/**
* {@inheritdoc}
*/
- protected $defaultSort = ['lastTime' => 'desc'];
+ protected $defaultSort = ['lastPostedAt' => 'desc'];
/**
* @var array
diff --git a/framework/core/src/Discussion/Search/Gambit/AuthorGambit.php b/framework/core/src/Discussion/Search/Gambit/AuthorGambit.php
index d3f4544ab..0b321448d 100644
--- a/framework/core/src/Discussion/Search/Gambit/AuthorGambit.php
+++ b/framework/core/src/Discussion/Search/Gambit/AuthorGambit.php
@@ -54,6 +54,6 @@ class AuthorGambit extends AbstractRegexGambit
$ids[] = $this->users->getIdForUsername($username);
}
- $search->getQuery()->whereIn('start_user_id', $ids, 'and', $negate);
+ $search->getQuery()->whereIn('user_id', $ids, 'and', $negate);
}
}
diff --git a/framework/core/src/Discussion/Search/Gambit/CreatedGambit.php b/framework/core/src/Discussion/Search/Gambit/CreatedGambit.php
index 6241c0e16..0fb908156 100644
--- a/framework/core/src/Discussion/Search/Gambit/CreatedGambit.php
+++ b/framework/core/src/Discussion/Search/Gambit/CreatedGambit.php
@@ -37,9 +37,9 @@ class CreatedGambit extends AbstractRegexGambit
// provided with a YYYY-MM-DD..YYYY-MM-DD range, then find discussions
// that were started during that period.
if (empty($matches[3])) {
- $search->getQuery()->whereDate('start_time', $negate ? '!=' : '=', $matches[1]);
+ $search->getQuery()->whereDate('created_at', $negate ? '!=' : '=', $matches[1]);
} else {
- $search->getQuery()->whereBetween('start_time', [$matches[1], $matches[3]], 'and', $negate);
+ $search->getQuery()->whereBetween('created_at', [$matches[1], $matches[3]], 'and', $negate);
}
}
}
diff --git a/framework/core/src/Discussion/Search/Gambit/FulltextGambit.php b/framework/core/src/Discussion/Search/Gambit/FulltextGambit.php
index 81f5e5976..d925e0ec8 100644
--- a/framework/core/src/Discussion/Search/Gambit/FulltextGambit.php
+++ b/framework/core/src/Discussion/Search/Gambit/FulltextGambit.php
@@ -33,22 +33,29 @@ class FulltextGambit implements GambitInterface
// See https://bugs.mysql.com/bug.php?id=74042
$bit = str_replace('@', '*', $bit);
- $search->getQuery()
- ->selectRaw('SUBSTRING_INDEX(GROUP_CONCAT(posts.id ORDER BY MATCH(posts.content) AGAINST (?) DESC), \',\', 1) as most_relevant_post_id', [$bit])
+ $query = $search->getQuery();
+ $grammar = $query->getGrammar();
+
+ $query
+ ->selectRaw('SUBSTRING_INDEX(GROUP_CONCAT('.$grammar->wrap('posts.id').' ORDER BY MATCH('.$grammar->wrap('posts.content').') AGAINST (?) DESC), \',\', 1) as most_relevant_post_id', [$bit])
->leftJoin('posts', 'posts.discussion_id', '=', 'discussions.id')
->where('posts.type', 'comment')
->where(function ($query) use ($search) {
event(new ScopeModelVisibility(Post::query()->setQuery($query), $search->getActor(), 'view'));
})
->where(function ($query) use ($bit) {
- $query->whereRaw('MATCH(discussions.title) AGAINST (? IN BOOLEAN MODE)', [$bit])
- ->orWhereRaw('MATCH(posts.content) AGAINST (? IN BOOLEAN MODE)', [$bit]);
+ $grammar = $query->getGrammar();
+
+ $query->whereRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (? IN BOOLEAN MODE)', [$bit])
+ ->orWhereRaw('MATCH('.$grammar->wrap('posts.content').') AGAINST (? IN BOOLEAN MODE)', [$bit]);
})
->groupBy('posts.discussion_id');
$search->setDefaultSort(function ($query) use ($bit) {
- $query->orderByRaw('MATCH(discussions.title) AGAINST (?) desc', [$bit]);
- $query->orderByRaw('MATCH(posts.content) AGAINST (?) desc', [$bit]);
+ $grammar = $query->getGrammar();
+
+ $query->orderByRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (?) desc', [$bit]);
+ $query->orderByRaw('MATCH('.$grammar->wrap('posts.content').') AGAINST (?) desc', [$bit]);
});
}
}
diff --git a/framework/core/src/Discussion/Search/Gambit/HiddenGambit.php b/framework/core/src/Discussion/Search/Gambit/HiddenGambit.php
index 907991546..95a2bee55 100644
--- a/framework/core/src/Discussion/Search/Gambit/HiddenGambit.php
+++ b/framework/core/src/Discussion/Search/Gambit/HiddenGambit.php
@@ -34,9 +34,9 @@ class HiddenGambit extends AbstractRegexGambit
$search->getQuery()->where(function ($query) use ($negate) {
if ($negate) {
- $query->whereNull('hide_time')->where('comments_count', '>', 0);
+ $query->whereNull('hidden_at')->where('comment_count', '>', 0);
} else {
- $query->whereNotNull('hide_time')->orWhere('comments_count', 0);
+ $query->whereNotNull('hidden_at')->orWhere('comment_count', 0);
}
});
}
diff --git a/framework/core/src/Discussion/Search/Gambit/UnreadGambit.php b/framework/core/src/Discussion/Search/Gambit/UnreadGambit.php
index bd9a60629..ef8b202dd 100644
--- a/framework/core/src/Discussion/Search/Gambit/UnreadGambit.php
+++ b/framework/core/src/Discussion/Search/Gambit/UnreadGambit.php
@@ -53,9 +53,9 @@ class UnreadGambit extends AbstractRegexGambit
$search->getQuery()->where(function ($query) use ($readIds, $negate, $actor) {
if (! $negate) {
- $query->whereNotIn('id', $readIds)->where('last_time', '>', $actor->read_time ?: 0);
+ $query->whereNotIn('id', $readIds)->where('last_posted_at', '>', $actor->marked_all_as_read_at ?: 0);
} else {
- $query->whereIn('id', $readIds)->orWhere('last_time', '<=', $actor->read_time ?: 0);
+ $query->whereIn('id', $readIds)->orWhere('last_posted_at', '<=', $actor->marked_all_as_read_at ?: 0);
}
});
}
diff --git a/framework/core/src/Discussion/UserState.php b/framework/core/src/Discussion/UserState.php
index a8df0651c..fc964d450 100644
--- a/framework/core/src/Discussion/UserState.php
+++ b/framework/core/src/Discussion/UserState.php
@@ -11,6 +11,7 @@
namespace Flarum\Discussion;
+use Carbon\Carbon;
use Flarum\Database\AbstractModel;
use Flarum\Discussion\Event\UserRead;
use Flarum\Foundation\EventGeneratorTrait;
@@ -26,8 +27,8 @@ use Illuminate\Database\Eloquent\Builder;
*
* @property int $user_id
* @property int $discussion_id
- * @property \Carbon\Carbon|null $read_time
- * @property int|null $read_number
+ * @property \Carbon\Carbon|null $last_read_at
+ * @property int|null $last_read_post_number
* @property Discussion $discussion
* @property \Flarum\User\User $user
*/
@@ -38,12 +39,14 @@ class UserState extends AbstractModel
/**
* {@inheritdoc}
*/
- protected $table = 'users_discussions';
+ protected $table = 'discussion_user';
/**
- * {@inheritdoc}
+ * The attributes that should be mutated to dates.
+ *
+ * @var array
*/
- protected $dates = ['read_time'];
+ protected $dates = ['last_read_at'];
/**
* Mark the discussion as being read up to a certain point. Raises the
@@ -54,9 +57,9 @@ class UserState extends AbstractModel
*/
public function read($number)
{
- if ($number > $this->read_number) {
- $this->read_number = $number;
- $this->read_time = time();
+ if ($number > $this->last_read_post_number) {
+ $this->last_read_post_number = $number;
+ $this->last_read_at = Carbon::now();
$this->raise(new UserRead($this));
}
@@ -71,7 +74,7 @@ class UserState extends AbstractModel
*/
public function discussion()
{
- return $this->belongsTo(Discussion::class, 'discussion_id');
+ return $this->belongsTo(Discussion::class);
}
/**
@@ -81,7 +84,7 @@ class UserState extends AbstractModel
*/
public function user()
{
- return $this->belongsTo(User::class, 'user_id');
+ return $this->belongsTo(User::class);
}
/**
diff --git a/framework/core/src/Forum/Content/Index.php b/framework/core/src/Forum/Content/Index.php
index 77e0a1f15..d83de36e6 100644
--- a/framework/core/src/Forum/Content/Index.php
+++ b/framework/core/src/Forum/Content/Index.php
@@ -76,10 +76,10 @@ class Index implements ContentInterface
private function getSortMap()
{
return [
- 'latest' => '-lastTime',
- 'top' => '-commentsCount',
- 'newest' => '-startTime',
- 'oldest' => 'startTime'
+ 'latest' => '-lastPostedAt',
+ 'top' => '-commentCount',
+ 'newest' => '-createdAt',
+ 'oldest' => 'createdAt'
];
}
diff --git a/framework/core/src/Forum/Controller/ResetPasswordController.php b/framework/core/src/Forum/Controller/ResetPasswordController.php
index a95dd07cc..5b9811657 100644
--- a/framework/core/src/Forum/Controller/ResetPasswordController.php
+++ b/framework/core/src/Forum/Controller/ResetPasswordController.php
@@ -49,7 +49,7 @@ class ResetPasswordController extends AbstractHtmlController
}
return $this->view->make('flarum.forum::reset-password')
- ->with('passwordToken', $token->id)
+ ->with('passwordToken', $token->token)
->with('csrfToken', $request->getAttribute('session')->token());
}
}
diff --git a/framework/core/src/Group/Group.php b/framework/core/src/Group/Group.php
index 14daf1b69..103dd4fea 100644
--- a/framework/core/src/Group/Group.php
+++ b/framework/core/src/Group/Group.php
@@ -33,11 +33,6 @@ class Group extends AbstractModel
use EventGeneratorTrait;
use ScopeVisibilityTrait;
- /**
- * {@inheritdoc}
- */
- protected $table = 'groups';
-
/**
* The ID of the administrator group.
*/
@@ -69,8 +64,6 @@ class Group extends AbstractModel
static::deleted(function (Group $group) {
$group->raise(new Deleted($group));
-
- $group->permissions()->delete();
});
}
@@ -121,7 +114,7 @@ class Group extends AbstractModel
*/
public function users()
{
- return $this->belongsToMany(User::class, 'users_groups');
+ return $this->belongsToMany(User::class);
}
/**
diff --git a/framework/core/src/Group/Permission.php b/framework/core/src/Group/Permission.php
index 110c365b0..745022567 100644
--- a/framework/core/src/Group/Permission.php
+++ b/framework/core/src/Group/Permission.php
@@ -15,14 +15,15 @@ use Flarum\Database\AbstractModel;
use Illuminate\Database\Eloquent\Builder;
/**
- * @todo document database columns with @property
+ * @property int $group_id
+ * @property string $permission
*/
class Permission extends AbstractModel
{
/**
* {@inheritdoc}
*/
- protected $table = 'permissions';
+ protected $table = 'group_permission';
/**
* Define the relationship with the group that this permission is for.
@@ -31,7 +32,7 @@ class Permission extends AbstractModel
*/
public function group()
{
- return $this->belongsTo(Group::class, 'group_id');
+ return $this->belongsTo(Group::class);
}
/**
diff --git a/framework/core/src/Http/AccessToken.php b/framework/core/src/Http/AccessToken.php
index dad24ae3b..f3d573710 100644
--- a/framework/core/src/Http/AccessToken.php
+++ b/framework/core/src/Http/AccessToken.php
@@ -11,22 +11,19 @@
namespace Flarum\Http;
+use Carbon\Carbon;
use Flarum\Database\AbstractModel;
+use Flarum\User\User;
/**
- * @property string $id
+ * @property string $token
* @property int $user_id
- * @property int $last_activity
- * @property int $lifetime
+ * @property int $last_activity_at
+ * @property int $lifetime_seconds
* @property \Flarum\User\User|null $user
*/
class AccessToken extends AbstractModel
{
- /**
- * {@inheritdoc}
- */
- protected $table = 'access_tokens';
-
/**
* Use a custom primary key for this model.
*
@@ -34,6 +31,10 @@ class AccessToken extends AbstractModel
*/
public $incrementing = false;
+ protected $primaryKey = 'token';
+
+ protected $dates = ['last_activity_at'];
+
/**
* Generate an access token for the specified user.
*
@@ -45,17 +46,17 @@ class AccessToken extends AbstractModel
{
$token = new static;
- $token->id = str_random(40);
+ $token->token = str_random(40);
$token->user_id = $userId;
- $token->last_activity = time();
- $token->lifetime = $lifetime;
+ $token->last_activity_at = Carbon::now();
+ $token->lifetime_seconds = $lifetime;
return $token;
}
public function touch()
{
- $this->last_activity = time();
+ $this->last_activity_at = Carbon::now();
return $this->save();
}
@@ -67,6 +68,6 @@ class AccessToken extends AbstractModel
*/
public function user()
{
- return $this->belongsTo('Flarum\User\User');
+ return $this->belongsTo(User::class);
}
}
diff --git a/framework/core/src/Http/Middleware/CollectGarbage.php b/framework/core/src/Http/Middleware/CollectGarbage.php
index e32b99c1b..9b0da016c 100644
--- a/framework/core/src/Http/Middleware/CollectGarbage.php
+++ b/framework/core/src/Http/Middleware/CollectGarbage.php
@@ -11,6 +11,7 @@
namespace Flarum\Http\Middleware;
+use Carbon\Carbon;
use Flarum\Http\AccessToken;
use Flarum\User\AuthToken;
use Flarum\User\EmailToken;
@@ -55,9 +56,11 @@ class CollectGarbage implements Middleware
return;
}
- AccessToken::whereRaw('last_activity <= ? - lifetime', [time()])->delete();
+ $time = Carbon::now()->timestamp;
- $earliestToKeep = date('Y-m-d H:i:s', time() - 24 * 60 * 60);
+ AccessToken::whereRaw('last_activity_at <= ? - lifetime_seconds', [$time])->delete();
+
+ $earliestToKeep = date('Y-m-d H:i:s', $time - 24 * 60 * 60);
EmailToken::where('created_at', '<=', $earliestToKeep)->delete();
PasswordToken::where('created_at', '<=', $earliestToKeep)->delete();
diff --git a/framework/core/src/Http/Rememberer.php b/framework/core/src/Http/Rememberer.php
index 1d25df007..17b1673d2 100644
--- a/framework/core/src/Http/Rememberer.php
+++ b/framework/core/src/Http/Rememberer.php
@@ -33,12 +33,12 @@ class Rememberer
public function remember(ResponseInterface $response, AccessToken $token)
{
- $token->lifetime = 5 * 365 * 24 * 60 * 60; // 5 years
+ $token->lifetime_seconds = 5 * 365 * 24 * 60 * 60; // 5 years
$token->save();
return FigResponseCookies::set(
$response,
- $this->cookie->make(self::COOKIE_NAME, $token->id, $token->lifetime)
+ $this->cookie->make(self::COOKIE_NAME, $token->token, $token->lifetime_seconds)
);
}
diff --git a/framework/core/src/Install/Console/InstallCommand.php b/framework/core/src/Install/Console/InstallCommand.php
index 00149cb94..e36ab06c5 100644
--- a/framework/core/src/Install/Console/InstallCommand.php
+++ b/framework/core/src/Install/Console/InstallCommand.php
@@ -184,8 +184,6 @@ class InstallCommand extends AbstractCommand
$this->writeSettings();
- $this->seedGroups();
-
$this->createAdminUser();
$this->enableBundledExtensions();
@@ -272,26 +270,6 @@ class InstallCommand extends AbstractCommand
}
}
- protected function seedGroups()
- {
- $groups = [
- [Group::ADMINISTRATOR_ID, 'Admin', 'Admins', '#B72A2A', 'fas fa-wrench'],
- [Group::GUEST_ID, 'Guest', 'Guests', null, null],
- [Group::MEMBER_ID, 'Member', 'Members', null, null],
- [Group::MODERATOR_ID, 'Mod', 'Mods', '#80349E', 'fas fa-bolt']
- ];
-
- foreach ($groups as $group) {
- $this->db->table('groups')->insert([
- 'id' => $group[0],
- 'name_singular' => $group[1],
- 'name_plural' => $group[2],
- 'color' => $group[3],
- 'icon' => $group[4],
- ]);
- }
- }
-
protected function createAdminUser()
{
$admin = $this->adminUser;
@@ -306,11 +284,11 @@ class InstallCommand extends AbstractCommand
'username' => $admin['username'],
'email' => $admin['email'],
'password' => (new BcryptHasher)->make($admin['password']),
- 'join_time' => time(),
- 'is_activated' => 1,
+ 'joined_at' => time(),
+ 'is_email_confirmed' => 1,
]);
- $this->db->table('users_groups')->insert([
+ $this->db->table('group_user')->insert([
'user_id' => $uid,
'group_id' => Group::ADMINISTRATOR_ID,
]);
diff --git a/framework/core/src/Notification/Blueprint/BlueprintInterface.php b/framework/core/src/Notification/Blueprint/BlueprintInterface.php
index ed8273396..600a8d941 100644
--- a/framework/core/src/Notification/Blueprint/BlueprintInterface.php
+++ b/framework/core/src/Notification/Blueprint/BlueprintInterface.php
@@ -23,7 +23,7 @@ interface BlueprintInterface
*
* @return \Flarum\User\User|null
*/
- public function getSender();
+ public function getFromUser();
/**
* Get the model that is the subject of this activity.
diff --git a/framework/core/src/Notification/Blueprint/DiscussionRenamedBlueprint.php b/framework/core/src/Notification/Blueprint/DiscussionRenamedBlueprint.php
index 50d2e6146..7e79026b9 100644
--- a/framework/core/src/Notification/Blueprint/DiscussionRenamedBlueprint.php
+++ b/framework/core/src/Notification/Blueprint/DiscussionRenamedBlueprint.php
@@ -32,7 +32,7 @@ class DiscussionRenamedBlueprint implements BlueprintInterface
/**
* {@inheritdoc}
*/
- public function getSender()
+ public function getFromUser()
{
return $this->post->user;
}
diff --git a/framework/core/src/Notification/Command/ReadNotificationHandler.php b/framework/core/src/Notification/Command/ReadNotificationHandler.php
index 41da8e781..f26cb7712 100644
--- a/framework/core/src/Notification/Command/ReadNotificationHandler.php
+++ b/framework/core/src/Notification/Command/ReadNotificationHandler.php
@@ -11,6 +11,7 @@
namespace Flarum\Notification\Command;
+use Carbon\Carbon;
use Flarum\Notification\Notification;
use Flarum\User\AssertPermissionTrait;
@@ -36,7 +37,7 @@ class ReadNotificationHandler
'type' => $notification->type,
'subject_id' => $notification->subject_id
])
- ->update(['is_read' => true]);
+ ->update(['read_at' => Carbon::now()]);
$notification->is_read = true;
diff --git a/framework/core/src/Notification/Notification.php b/framework/core/src/Notification/Notification.php
index ad289ca08..eab6ad5db 100644
--- a/framework/core/src/Notification/Notification.php
+++ b/framework/core/src/Notification/Notification.php
@@ -11,6 +11,7 @@
namespace Flarum\Notification;
+use Carbon\Carbon;
use Flarum\Database\AbstractModel;
use Flarum\User\User;
@@ -30,28 +31,25 @@ use Flarum\User\User;
*
* @property int $id
* @property int $user_id
- * @property int|null $sender_id
+ * @property int|null $from_user_id
* @property string $type
* @property int|null $subject_id
* @property mixed|null $data
- * @property \Carbon\Carbon $time
- * @property bool $is_read
- * @property bool $is_deleted
+ * @property \Carbon\Carbon $created_at
+ * @property \Carbon\Carbon $read_at
+ * @property \Carbon\Carbon $deleted_at
* @property \Flarum\User\User|null $user
- * @property \Flarum\User\User|null $sender
+ * @property \Flarum\User\User|null $fromUser
* @property \Flarum\Database\AbstractModel|null $subject
*/
class Notification extends AbstractModel
{
/**
- * {@inheritdoc}
+ * The attributes that should be mutated to dates.
+ *
+ * @var array
*/
- protected $table = 'notifications';
-
- /**
- * {@inheritdoc}
- */
- protected $dates = ['time'];
+ protected $dates = ['created_at', 'read_at'];
/**
* A map of notification types and the model classes to use for their
@@ -70,7 +68,7 @@ class Notification extends AbstractModel
*/
public function read()
{
- $this->is_read = true;
+ $this->read_at = Carbon::now();
}
/**
@@ -114,7 +112,7 @@ class Notification extends AbstractModel
*/
public function user()
{
- return $this->belongsTo(User::class, 'user_id');
+ return $this->belongsTo(User::class);
}
/**
@@ -122,9 +120,9 @@ class Notification extends AbstractModel
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
- public function sender()
+ public function fromUser()
{
- return $this->belongsTo(User::class, 'sender_id');
+ return $this->belongsTo(User::class, 'from_user_id');
}
/**
@@ -134,7 +132,7 @@ class Notification extends AbstractModel
*/
public function subject()
{
- return $this->morphTo('subject', 'subjectModel', 'subject_id');
+ return $this->morphTo('subject', 'subjectModel');
}
/**
diff --git a/framework/core/src/Notification/NotificationRepository.php b/framework/core/src/Notification/NotificationRepository.php
index ae70ea600..b8e2b6681 100644
--- a/framework/core/src/Notification/NotificationRepository.php
+++ b/framework/core/src/Notification/NotificationRepository.php
@@ -11,6 +11,7 @@
namespace Flarum\Notification;
+use Carbon\Carbon;
use Flarum\User\User;
class NotificationRepository
@@ -27,20 +28,20 @@ class NotificationRepository
{
$primaries = Notification::select(
app('flarum.db')->raw('MAX(id) AS id'),
- app('flarum.db')->raw('SUM(is_read = 0) AS unread_count')
+ app('flarum.db')->raw('SUM(read_at IS NULL) AS unread_count')
)
->where('user_id', $user->id)
->whereIn('type', $user->getAlertableNotificationTypes())
->where('is_deleted', false)
->groupBy('type', 'subject_id')
- ->orderByRaw('MAX(time) DESC')
+ ->orderByRaw('MAX(created_at) DESC')
->skip($offset)
->take($limit);
return Notification::select('notifications.*', app('flarum.db')->raw('p.unread_count'))
->mergeBindings($primaries->getQuery())
->join(app('flarum.db')->raw('('.$primaries->toSql().') p'), 'notifications.id', '=', app('flarum.db')->raw('p.id'))
- ->latest('time')
+ ->latest()
->get();
}
@@ -53,6 +54,6 @@ class NotificationRepository
*/
public function markAllAsRead(User $user)
{
- Notification::where('user_id', $user->id)->update(['is_read' => true]);
+ Notification::where('user_id', $user->id)->update(['read_at' => Carbon::now()]);
}
}
diff --git a/framework/core/src/Notification/NotificationSyncer.php b/framework/core/src/Notification/NotificationSyncer.php
index b8cd39b48..20bcca6a9 100644
--- a/framework/core/src/Notification/NotificationSyncer.php
+++ b/framework/core/src/Notification/NotificationSyncer.php
@@ -179,7 +179,7 @@ class NotificationSyncer
array_map(function (User $user) use ($attributes, $now) {
return $attributes + [
'user_id' => $user->id,
- 'time' => $now
+ 'created_at' => $now
];
}, $recipients)
);
@@ -225,10 +225,10 @@ class NotificationSyncer
protected function getAttributes(Blueprint\BlueprintInterface $blueprint)
{
return [
- 'type' => $blueprint::getType(),
- 'sender_id' => ($sender = $blueprint->getSender()) ? $sender->id : null,
+ 'type' => $blueprint::getType(),
+ 'from_user_id' => ($fromUser = $blueprint->getFromUser()) ? $fromUser->id : null,
'subject_id' => ($subject = $blueprint->getSubject()) ? $subject->id : null,
- 'data' => ($data = $blueprint->getData()) ? json_encode($data) : null
+ 'data' => ($data = $blueprint->getData()) ? json_encode($data) : null
];
}
}
diff --git a/framework/core/src/Post/Command/PostReplyHandler.php b/framework/core/src/Post/Command/PostReplyHandler.php
index a3e066c50..5817bbfcf 100644
--- a/framework/core/src/Post/Command/PostReplyHandler.php
+++ b/framework/core/src/Post/Command/PostReplyHandler.php
@@ -11,7 +11,7 @@
namespace Flarum\Post\Command;
-use DateTime;
+use Carbon\Carbon;
use Flarum\Discussion\DiscussionRepository;
use Flarum\Foundation\DispatchEventsTrait;
use Flarum\Notification\NotificationSyncer;
@@ -77,7 +77,7 @@ class PostReplyHandler
// If this is the first post in the discussion, it's technically not a
// "reply", so we won't check for that permission.
- if ($discussion->number_index > 0) {
+ if ($discussion->post_number_index > 0) {
$this->assertCan($actor, 'reply', $discussion);
}
@@ -91,8 +91,8 @@ class PostReplyHandler
$command->ipAddress
);
- if ($actor->isAdmin() && ($time = array_get($command->data, 'attributes.time'))) {
- $post->time = new DateTime($time);
+ if ($actor->isAdmin() && ($time = array_get($command->data, 'attributes.createdAt'))) {
+ $post->created_at = new Carbon($time);
}
$this->events->dispatch(
diff --git a/framework/core/src/Post/CommentPost.php b/framework/core/src/Post/CommentPost.php
index da835c8f7..588414b42 100644
--- a/framework/core/src/Post/CommentPost.php
+++ b/framework/core/src/Post/CommentPost.php
@@ -11,6 +11,7 @@
namespace Flarum\Post;
+use Carbon\Carbon;
use Flarum\Formatter\Formatter;
use Flarum\Post\Event\Hidden;
use Flarum\Post\Event\Posted;
@@ -51,7 +52,7 @@ class CommentPost extends Post
{
$post = new static;
- $post->time = time();
+ $post->created_at = Carbon::now();
$post->discussion_id = $discussionId;
$post->user_id = $userId;
$post->type = static::$type;
@@ -77,8 +78,8 @@ class CommentPost extends Post
if ($this->content !== $content) {
$this->content = $content;
- $this->edit_time = time();
- $this->edit_user_id = $actor->id;
+ $this->edited_at = Carbon::now();
+ $this->edited_user_id = $actor->id;
$this->raise(new Revised($this));
}
@@ -94,9 +95,9 @@ class CommentPost extends Post
*/
public function hide(User $actor = null)
{
- if (! $this->hide_time) {
- $this->hide_time = time();
- $this->hide_user_id = $actor ? $actor->id : null;
+ if (! $this->hidden_at) {
+ $this->hidden_at = Carbon::now();
+ $this->hidden_user_id = $actor ? $actor->id : null;
$this->raise(new Hidden($this));
}
@@ -111,9 +112,9 @@ class CommentPost extends Post
*/
public function restore()
{
- if ($this->hide_time !== null) {
- $this->hide_time = null;
- $this->hide_user_id = null;
+ if ($this->hidden_at !== null) {
+ $this->hidden_at = null;
+ $this->hidden_user_id = null;
$this->raise(new Restored($this));
}
diff --git a/framework/core/src/Post/DiscussionRenamedPost.php b/framework/core/src/Post/DiscussionRenamedPost.php
index 21243d902..3feb96e5d 100644
--- a/framework/core/src/Post/DiscussionRenamedPost.php
+++ b/framework/core/src/Post/DiscussionRenamedPost.php
@@ -11,6 +11,8 @@
namespace Flarum\Post;
+use Carbon\Carbon;
+
/**
* A post which indicates that a discussion's title was changed.
*
@@ -64,7 +66,7 @@ class DiscussionRenamedPost extends AbstractEventPost implements MergeableInterf
$post = new static;
$post->content = static::buildContent($oldTitle, $newTitle);
- $post->time = time();
+ $post->created_at = Carbon::now();
$post->discussion_id = $discussionId;
$post->user_id = $userId;
diff --git a/framework/core/src/Post/Floodgate.php b/framework/core/src/Post/Floodgate.php
index d3068053f..f7150d571 100644
--- a/framework/core/src/Post/Floodgate.php
+++ b/framework/core/src/Post/Floodgate.php
@@ -50,6 +50,6 @@ class Floodgate
new CheckingForFlooding($actor)
);
- return $isFlooding ?? Post::where('user_id', $actor->id)->where('time', '>=', new DateTime('-10 seconds'))->exists();
+ return $isFlooding ?? Post::where('user_id', $actor->id)->where('created_at', '>=', new DateTime('-10 seconds'))->exists();
}
}
diff --git a/framework/core/src/Post/Post.php b/framework/core/src/Post/Post.php
index 922bc5462..0a416d5bd 100644
--- a/framework/core/src/Post/Post.php
+++ b/framework/core/src/Post/Post.php
@@ -24,18 +24,18 @@ use Illuminate\Database\Eloquent\Builder;
* @property int $id
* @property int $discussion_id
* @property int $number
- * @property \Carbon\Carbon $time
+ * @property \Carbon\Carbon $created_at
* @property int|null $user_id
* @property string|null $type
* @property string|null $content
- * @property \Carbon\Carbon|null $edit_time
- * @property int|null $edit_user_id
- * @property \Carbon\Carbon|null $hide_time
- * @property int|null $hide_user_id
+ * @property \Carbon\Carbon|null $edited_at
+ * @property int|null $edited_user_id
+ * @property \Carbon\Carbon|null $hidden_at
+ * @property int|null $hidden_user_id
* @property \Flarum\Discussion\Discussion|null $discussion
* @property User|null $user
- * @property User|null $editUser
- * @property User|null $hideUser
+ * @property User|null $editedUser
+ * @property User|null $hiddenUser
* @property string $ip_address
* @property bool $is_private
*/
@@ -43,18 +43,17 @@ class Post extends AbstractModel
{
use EventGeneratorTrait;
- /**
- * {@inheritdoc}
- */
protected $table = 'posts';
/**
- * {@inheritdoc}
+ * The attributes that should be mutated to dates.
+ *
+ * @var array
*/
- protected $dates = ['time', 'edit_time', 'hide_time'];
+ protected $dates = ['created_at', 'edited_at', 'hidden_at'];
/**
- * Casts properties to a specific type.
+ * The attributes that should be cast to native types.
*
* @var array
*/
@@ -93,7 +92,7 @@ class Post extends AbstractModel
// discussion.
static::creating(function (Post $post) {
$post->type = $post::$type;
- $post->number = ++$post->discussion->number_index;
+ $post->number = ++$post->discussion->post_number_index;
$post->discussion->save();
});
@@ -122,13 +121,9 @@ class Post extends AbstractModel
// Make sure the post's discussion is visible as well
$query->whereExists(function ($query) use ($actor) {
- $grammar = $query->getGrammar();
- $column1 = $grammar->wrap('discussions.id');
- $column2 = $grammar->wrap('posts.discussion_id');
-
$query->selectRaw('1')
->from('discussions')
- ->whereRaw("$column1 = $column2");
+ ->whereColumn('discussions.id', 'posts.discussion_id');
static::$dispatcher->dispatch(
new ScopeModelVisibility(Discussion::query()->setQuery($query), $actor, 'view')
@@ -154,7 +149,7 @@ class Post extends AbstractModel
*/
public function discussion()
{
- return $this->belongsTo('Flarum\Discussion\Discussion', 'discussion_id');
+ return $this->belongsTo(Discussion::class);
}
/**
@@ -164,7 +159,7 @@ class Post extends AbstractModel
*/
public function user()
{
- return $this->belongsTo('Flarum\User\User', 'user_id');
+ return $this->belongsTo(User::class);
}
/**
@@ -172,9 +167,9 @@ class Post extends AbstractModel
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
- public function editUser()
+ public function editedUser()
{
- return $this->belongsTo('Flarum\User\User', 'edit_user_id');
+ return $this->belongsTo(User::class, 'edited_user_id');
}
/**
@@ -182,9 +177,9 @@ class Post extends AbstractModel
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
- public function hideUser()
+ public function hiddenUser()
{
- return $this->belongsTo('Flarum\User\User', 'hide_user_id');
+ return $this->belongsTo(User::class, 'hidden_user_id');
}
/**
diff --git a/framework/core/src/Post/PostPolicy.php b/framework/core/src/Post/PostPolicy.php
index c8167bb6f..0ed52e9d1 100644
--- a/framework/core/src/Post/PostPolicy.php
+++ b/framework/core/src/Post/PostPolicy.php
@@ -81,12 +81,12 @@ class PostPolicy extends AbstractPolicy
// discussion.
if (! $actor->hasPermission('discussion.hidePosts')) {
$query->where(function ($query) use ($actor) {
- $query->whereNull('posts.hide_time')
- ->orWhere('user_id', $actor->id)
+ $query->whereNull('posts.hidden_at')
+ ->orWhere('posts.user_id', $actor->id)
->orWhereExists(function ($query) use ($actor) {
$query->selectRaw('1')
->from('discussions')
- ->whereRaw('discussions.id = posts.discussion_id')
+ ->whereColumn('discussions.id', 'posts.discussion_id')
->where(function ($query) use ($actor) {
$this->events->dispatch(
new ScopeModelVisibility(Discussion::query()->setQuery($query), $actor, 'hidePosts')
@@ -107,12 +107,12 @@ class PostPolicy extends AbstractPolicy
// A post is allowed to be edited if the user has permission to moderate
// the discussion which it's in, or if they are the author and the post
// hasn't been deleted by someone else.
- if ($post->user_id == $actor->id && (! $post->hide_time || $post->hide_user_id == $actor->id)) {
+ if ($post->user_id == $actor->id && (! $post->hidden_at || $post->hidden_user_id == $actor->id)) {
$allowEditing = $this->settings->get('allow_post_editing');
if ($allowEditing === '-1'
|| ($allowEditing === 'reply' && $post->number >= $post->discussion->last_post_number)
- || ($post->time->diffInMinutes(new Carbon) < $allowEditing)) {
+ || ($post->created_at->diffInMinutes(new Carbon) < $allowEditing)) {
return true;
}
}
diff --git a/framework/core/src/Post/PostRepository.php b/framework/core/src/Post/PostRepository.php
index 8213be5e8..f6ed861c5 100644
--- a/framework/core/src/Post/PostRepository.php
+++ b/framework/core/src/Post/PostRepository.php
@@ -136,8 +136,8 @@ class PostRepository
$query = Discussion::find($discussionId)
->posts()
->whereVisibleTo($actor)
- ->where('time', '<', function ($query) use ($discussionId, $number) {
- $query->select('time')
+ ->where('created_at', '<', function ($query) use ($discussionId, $number) {
+ $query->select('created_at')
->from('posts')
->where('discussion_id', $discussionId)
->whereNotNull('number')
diff --git a/framework/core/src/User/AuthToken.php b/framework/core/src/User/AuthToken.php
index 2458baca5..877cd7c24 100644
--- a/framework/core/src/User/AuthToken.php
+++ b/framework/core/src/User/AuthToken.php
@@ -11,22 +11,26 @@
namespace Flarum\User;
-use DateTime;
+use Carbon\Carbon;
use Flarum\Database\AbstractModel;
use Flarum\User\Exception\InvalidConfirmationTokenException;
/**
- * @todo document database columns with @property
+ * @property string $token
+ * @property \Carbon\Carbon $created_at
+ * @property string $payload
*/
class AuthToken extends AbstractModel
{
/**
* {@inheritdoc}
*/
- protected $table = 'auth_tokens';
+ protected $table = 'registration_tokens';
/**
- * {@inheritdoc}
+ * The attributes that should be mutated to dates.
+ *
+ * @var array
*/
protected $dates = ['created_at'];
@@ -37,10 +41,15 @@ class AuthToken extends AbstractModel
*/
public $incrementing = false;
+ /**
+ * {@inheritdoc}
+ */
+ protected $primaryKey = 'token';
+
/**
* Generate an email token for the specified user.
*
- * @param string $email
+ * @param string $payload
*
* @return static
*/
@@ -48,9 +57,9 @@ class AuthToken extends AbstractModel
{
$token = new static;
- $token->id = str_random(40);
+ $token->token = str_random(40);
$token->payload = $payload;
- $token->created_at = time();
+ $token->created_at = Carbon::now();
return $token;
}
@@ -80,17 +89,18 @@ class AuthToken extends AbstractModel
* Find the token with the given ID, and assert that it has not expired.
*
* @param \Illuminate\Database\Eloquent\Builder $query
- * @param string $id
+ * @param string $token
*
- * @throws \Flarum\User\Exception\InvalidConfirmationTokenException
+ * @throws InvalidConfirmationTokenException
*
- * @return static
+ * @return AuthToken
*/
- public function scopeValidOrFail($query, $id)
+ public function scopeValidOrFail($query, string $token)
{
- $token = $query->find($id);
+ /** @var AuthToken $token */
+ $token = $query->find($token);
- if (! $token || $token->created_at < new DateTime('-1 day')) {
+ if (! $token || $token->created_at->lessThan(Carbon::now()->subDay())) {
throw new InvalidConfirmationTokenException;
}
diff --git a/framework/core/src/User/Command/EditUserHandler.php b/framework/core/src/User/Command/EditUserHandler.php
index 09bbd69e1..6d9d2485d 100644
--- a/framework/core/src/User/Command/EditUserHandler.php
+++ b/framework/core/src/User/Command/EditUserHandler.php
@@ -103,7 +103,7 @@ class EditUserHandler
}
}
- if ($actor->isAdmin() && ! empty($attributes['isActivated'])) {
+ if ($actor->isAdmin() && ! empty($attributes['isEmailConfirmed'])) {
$user->activate();
}
@@ -114,7 +114,7 @@ class EditUserHandler
$validate['password'] = $attributes['password'];
}
- if (! empty($attributes['readTime'])) {
+ if (! empty($attributes['markedAllAsReadAt'])) {
$this->assertPermission($isSelf);
$user->markAllAsRead();
}
diff --git a/framework/core/src/User/Command/RegisterUserHandler.php b/framework/core/src/User/Command/RegisterUserHandler.php
index 417fd79e0..05029bb70 100644
--- a/framework/core/src/User/Command/RegisterUserHandler.php
+++ b/framework/core/src/User/Command/RegisterUserHandler.php
@@ -112,7 +112,7 @@ class RegisterUserHandler
}
}
- if ($actor->isAdmin() && array_get($data, 'attributes.isActivated')) {
+ if ($actor->isAdmin() && array_get($data, 'attributes.isEmailConfirmed')) {
$user->activate();
}
diff --git a/framework/core/src/User/Command/RequestPasswordResetHandler.php b/framework/core/src/User/Command/RequestPasswordResetHandler.php
index 357c7963e..6b35d0e4a 100644
--- a/framework/core/src/User/Command/RequestPasswordResetHandler.php
+++ b/framework/core/src/User/Command/RequestPasswordResetHandler.php
@@ -107,7 +107,7 @@ class RequestPasswordResetHandler
$data = [
'{username}' => $user->display_name,
- '{url}' => $this->url->to('forum')->route('resetPassword', ['token' => $token->id]),
+ '{url}' => $this->url->to('forum')->route('resetPassword', ['token' => $token->token]),
'{forum}' => $this->settings->get('forum_title'),
];
diff --git a/framework/core/src/User/EmailConfirmationMailer.php b/framework/core/src/User/EmailConfirmationMailer.php
index 642a641a3..0bf96c4c2 100644
--- a/framework/core/src/User/EmailConfirmationMailer.php
+++ b/framework/core/src/User/EmailConfirmationMailer.php
@@ -128,7 +128,7 @@ class EmailConfirmationMailer
return [
'{username}' => $user->display_name,
- '{url}' => $this->url->to('forum')->route('confirmEmail', ['token' => $token->id]),
+ '{url}' => $this->url->to('forum')->route('confirmEmail', ['token' => $token->token]),
'{forum}' => $this->settings->get('forum_title')
];
}
diff --git a/framework/core/src/User/EmailToken.php b/framework/core/src/User/EmailToken.php
index 5de96e2e6..085efb101 100644
--- a/framework/core/src/User/EmailToken.php
+++ b/framework/core/src/User/EmailToken.php
@@ -11,22 +11,22 @@
namespace Flarum\User;
-use DateTime;
+use Carbon\Carbon;
use Flarum\Database\AbstractModel;
use Flarum\User\Exception\InvalidConfirmationTokenException;
/**
- * @todo document database columns with @property
+ * @property string $token
+ * @property int $user_id
+ * @property \Carbon\Carbon $created_at
+ * @property string $email
*/
class EmailToken extends AbstractModel
{
/**
- * {@inheritdoc}
- */
- protected $table = 'email_tokens';
-
- /**
- * {@inheritdoc}
+ * The attributes that should be mutated to dates.
+ *
+ * @var array
*/
protected $dates = ['created_at'];
@@ -37,6 +37,11 @@ class EmailToken extends AbstractModel
*/
public $incrementing = false;
+ /**
+ * {@inheritdoc}
+ */
+ protected $primaryKey = 'token';
+
/**
* Generate an email token for the specified user.
*
@@ -49,10 +54,10 @@ class EmailToken extends AbstractModel
{
$token = new static;
- $token->id = str_random(40);
+ $token->token = str_random(40);
$token->user_id = $userId;
$token->email = $email;
- $token->created_at = time();
+ $token->created_at = Carbon::now();
return $token;
}
@@ -77,9 +82,10 @@ class EmailToken extends AbstractModel
*/
public function scopeValidOrFail($query, $id)
{
+ /** @var EmailToken $token */
$token = $query->find($id);
- if (! $token || $token->created_at < new DateTime('-1 day')) {
+ if (! $token || $token->created_at->diffInDays() >= 1) {
throw new InvalidConfirmationTokenException;
}
diff --git a/framework/core/src/User/PasswordToken.php b/framework/core/src/User/PasswordToken.php
index 5c8070ead..54c9694b0 100644
--- a/framework/core/src/User/PasswordToken.php
+++ b/framework/core/src/User/PasswordToken.php
@@ -11,20 +11,20 @@
namespace Flarum\User;
+use Carbon\Carbon;
use Flarum\Database\AbstractModel;
/**
- * @todo document database columns with @property
+ * @property string $token
+ * @property \Carbon\Carbon $created_at
+ * @property int $user_id
*/
class PasswordToken extends AbstractModel
{
/**
- * {@inheritdoc}
- */
- protected $table = 'password_tokens';
-
- /**
- * {@inheritdoc}
+ * The attributes that should be mutated to dates.
+ *
+ * @var array
*/
protected $dates = ['created_at'];
@@ -35,19 +35,24 @@ class PasswordToken extends AbstractModel
*/
public $incrementing = false;
+ /**
+ * {@inheritdoc}
+ */
+ protected $primaryKey = 'token';
+
/**
* Generate a password token for the specified user.
*
* @param int $userId
* @return static
*/
- public static function generate($userId)
+ public static function generate(int $userId)
{
$token = new static;
- $token->id = str_random(40);
+ $token->token = str_random(40);
$token->user_id = $userId;
- $token->created_at = time();
+ $token->created_at = Carbon::now();
return $token;
}
diff --git a/framework/core/src/User/User.php b/framework/core/src/User/User.php
index 435c88301..b4742e212 100644
--- a/framework/core/src/User/User.php
+++ b/framework/core/src/User/User.php
@@ -11,6 +11,7 @@
namespace Flarum\User;
+use Carbon\Carbon;
use DomainException;
use Flarum\Database\AbstractModel;
use Flarum\Database\ScopeVisibilityTrait;
@@ -23,7 +24,6 @@ use Flarum\Group\Group;
use Flarum\Group\Permission;
use Flarum\Http\UrlGenerator;
use Flarum\Notification\Notification;
-use Flarum\Post\Event\Deleted as PostDeleted;
use Flarum\User\Event\Activated;
use Flarum\User\Event\AvatarChanged;
use Flarum\User\Event\CheckingPassword;
@@ -39,19 +39,19 @@ use Illuminate\Contracts\Session\Session;
/**
* @property int $id
* @property string $username
+ * @property string $display_name
* @property string $email
- * @property bool $is_activated
+ * @property bool $is_email_confirmed
* @property string $password
* @property string $locale
- * @property string|null $avatar_path
- * @property string $avatar_url
+ * @property string|null $avatar_url
* @property array $preferences
- * @property \Carbon\Carbon|null $join_time
- * @property \Carbon\Carbon|null $last_seen_time
- * @property \Carbon\Carbon|null $read_time
- * @property \Carbon\Carbon|null $notifications_read_time
- * @property int $discussions_count
- * @property int $comments_count
+ * @property \Carbon\Carbon|null $joined_at
+ * @property \Carbon\Carbon|null $last_seen_at
+ * @property \Carbon\Carbon|null $marked_all_as_read_at
+ * @property \Carbon\Carbon|null $read_notifications_at
+ * @property int $discussion_count
+ * @property int $comment_count
*/
class User extends AbstractModel
{
@@ -59,18 +59,15 @@ class User extends AbstractModel
use ScopeVisibilityTrait;
/**
- * {@inheritdoc}
- */
- protected $table = 'users';
-
- /**
- * {@inheritdoc}
+ * The attributes that should be mutated to dates.
+ *
+ * @var array
*/
protected $dates = [
- 'join_time',
- 'last_seen_time',
- 'read_time',
- 'notifications_read_time'
+ 'joined_at',
+ 'last_seen_at',
+ 'marked_all_as_read_at',
+ 'read_notifications_at'
];
/**
@@ -81,7 +78,7 @@ class User extends AbstractModel
protected $permissions = null;
/**
- * @var SessionInterface
+ * @var Session
*/
protected $session;
@@ -128,22 +125,6 @@ class User extends AbstractModel
static::deleted(function (User $user) {
$user->raise(new Deleted($user));
-
- // Delete all of the posts by the user. Before we delete them
- // in a big batch query, we will loop through them and raise a
- // PostWasDeleted event for each post.
- $posts = $user->posts()->allTypes();
-
- foreach ($posts->cursor() as $post) {
- $user->raise(new PostDeleted($post));
- }
-
- $posts->delete();
-
- $user->read()->detach();
- $user->groups()->detach();
- $user->accessTokens()->delete();
- $user->notifications()->delete();
});
static::$dispatcher->dispatch(
@@ -166,7 +147,7 @@ class User extends AbstractModel
$user->username = $username;
$user->email = $email;
$user->password = $password;
- $user->join_time = time();
+ $user->joined_at = Carbon::now();
$user->raise(new Registered($user));
@@ -271,7 +252,7 @@ class User extends AbstractModel
*/
public function markAllAsRead()
{
- $this->read_time = time();
+ $this->marked_all_as_read_at = Carbon::now();
return $this;
}
@@ -283,7 +264,7 @@ class User extends AbstractModel
*/
public function markNotificationsAsRead()
{
- $this->notifications_read_time = time();
+ $this->read_notifications_at = Carbon::now();
return $this;
}
@@ -296,7 +277,7 @@ class User extends AbstractModel
*/
public function changeAvatarPath($path)
{
- $this->avatar_path = $path;
+ $this->avatar_url = $path;
$this->raise(new AvatarChanged($this));
@@ -307,17 +288,16 @@ class User extends AbstractModel
* Get the URL of the user's avatar.
*
* @todo Allow different storage locations to be used
+ * @param string|null $value
* @return string
*/
- public function getAvatarUrlAttribute()
+ public function getAvatarUrlAttribute(string $value = null)
{
- if ($this->avatar_path) {
- if (strpos($this->avatar_path, '://') !== false) {
- return $this->avatar_path;
- }
-
- return app(UrlGenerator::class)->to('forum')->path('assets/avatars/'.$this->avatar_path);
+ if ($value && strpos($value, '://') === false) {
+ return app(UrlGenerator::class)->to('forum')->path('assets/avatars/'.$value);
}
+
+ return $value;
}
/**
@@ -366,8 +346,8 @@ class User extends AbstractModel
*/
public function activate()
{
- if ($this->is_activated !== true) {
- $this->is_activated = true;
+ if ($this->is_email_confirmed !== true) {
+ $this->is_email_confirmed = true;
$this->raise(new Activated($this));
}
@@ -438,7 +418,7 @@ class User extends AbstractModel
*
* @return int
*/
- public function getUnreadNotificationsCount()
+ public function getUnreadNotificationCount()
{
return $this->getUnreadNotifications()->count();
}
@@ -455,8 +435,8 @@ class User extends AbstractModel
if (is_null($cached)) {
$cached = $this->notifications()
->whereIn('type', $this->getAlertableNotificationTypes())
- ->where('is_read', 0)
- ->where('is_deleted', 0)
+ ->whereNull('read_at')
+ ->where('is_deleted', false)
->get();
}
@@ -468,10 +448,10 @@ class User extends AbstractModel
*
* @return int
*/
- public function getNewNotificationsCount()
+ public function getNewNotificationCount()
{
return $this->getUnreadNotifications()->filter(function ($notification) {
- return $notification->time > $this->notifications_read_time ?: 0;
+ return $notification->created_at > $this->read_notifications_at ?: 0;
})->count();
}
@@ -570,7 +550,7 @@ class User extends AbstractModel
*/
public function updateLastSeen()
{
- $this->last_seen_time = time();
+ $this->last_seen_at = Carbon::now();
return $this;
}
@@ -612,7 +592,7 @@ class User extends AbstractModel
*/
public function discussions()
{
- return $this->hasMany('Flarum\Discussion\Discussion', 'start_user_id');
+ return $this->hasMany('Flarum\Discussion\Discussion');
}
/**
@@ -622,7 +602,7 @@ class User extends AbstractModel
*/
public function read()
{
- return $this->belongsToMany('Flarum\Discussion\Discussion', 'users_discussions');
+ return $this->belongsToMany('Flarum\Discussion\Discussion');
}
/**
@@ -632,7 +612,7 @@ class User extends AbstractModel
*/
public function groups()
{
- return $this->belongsToMany('Flarum\Group\Group', 'users_groups');
+ return $this->belongsToMany('Flarum\Group\Group');
}
/**
@@ -659,7 +639,7 @@ class User extends AbstractModel
// more than a guest. If they are activated, we can give them the
// standard 'member' group, as well as any other groups they've been
// assigned to.
- if ($this->is_activated) {
+ if ($this->is_email_confirmed) {
$groupIds = array_merge($groupIds, [Group::MEMBER_ID], $this->groups->pluck('id')->all());
}
@@ -764,9 +744,9 @@ class User extends AbstractModel
*
* @return $this
*/
- public function refreshCommentsCount()
+ public function refreshCommentCount()
{
- $this->comments_count = $this->posts()->count();
+ $this->comment_count = $this->posts()->count();
return $this;
}
@@ -776,9 +756,9 @@ class User extends AbstractModel
*
* @return $this
*/
- public function refreshDiscussionsCount()
+ public function refreshDiscussionCount()
{
- $this->discussions_count = $this->discussions()->count();
+ $this->discussion_count = $this->discussions()->count();
return $this;
}
diff --git a/framework/core/src/User/UserMetadataUpdater.php b/framework/core/src/User/UserMetadataUpdater.php
index 1297ea6bf..713c3fa27 100644
--- a/framework/core/src/User/UserMetadataUpdater.php
+++ b/framework/core/src/User/UserMetadataUpdater.php
@@ -69,16 +69,16 @@ class UserMetadataUpdater
$user = $post->user;
if ($user && $user->exists) {
- $user->refreshCommentsCount()->save();
+ $user->refreshCommentCount()->save();
}
}
private function updateDiscussionsCount(Discussion $discussion)
{
- $user = $discussion->startUser;
+ $user = $discussion->user;
if ($user && $user->exists) {
- $user->refreshDiscussionsCount()->save();
+ $user->refreshDiscussionCount()->save();
}
}
}
diff --git a/framework/core/tests/Api/Controller/CreateUserControllerTest.php b/framework/core/tests/Api/Controller/CreateUserControllerTest.php
index dd51dc0da..bbb127943 100644
--- a/framework/core/tests/Api/Controller/CreateUserControllerTest.php
+++ b/framework/core/tests/Api/Controller/CreateUserControllerTest.php
@@ -66,7 +66,7 @@ class CreateUserControllerTest extends ApiControllerTestCase
$this->actor = $this->getAdminUser();
$response = $this->callWith(array_merge($this->data, [
- 'isActivated' => 1
+ 'isEmailConfirmed' => 1
]));
$this->assertEquals(201, $response->getStatusCode());
@@ -74,7 +74,7 @@ class CreateUserControllerTest extends ApiControllerTestCase
/** @var User $user */
$user = User::where('username', 'test')->firstOrFail();
- $this->assertEquals(1, $user->is_activated);
+ $this->assertEquals(1, $user->is_email_confirmed);
}
/**
diff --git a/framework/core/tests/Test/Concerns/ManagesContent.php b/framework/core/tests/Test/Concerns/ManagesContent.php
index 9d1d687cb..80437c7fc 100644
--- a/framework/core/tests/Test/Concerns/ManagesContent.php
+++ b/framework/core/tests/Test/Concerns/ManagesContent.php
@@ -31,8 +31,8 @@ trait ManagesContent
$post->save();
- if (! $this->discussion->startPost) {
- $this->discussion->setStartPost($post);
+ if (! $this->discussion->firstPost) {
+ $this->discussion->setFirstPost($post);
$this->discussion->setLastPost($post);
$this->discussion->save();