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 {
- - {avatar(startUser, {title: ''})} + {avatar(user, {title: ''})}
@@ -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();