mirror of
https://github.com/flarum/framework.git
synced 2025-04-01 21:55:16 +08:00
Merge pull request #1344 from flarum/1236-database-changes
Database changes
This commit is contained in:
commit
68afdd21ae
@ -95,7 +95,7 @@ export default class Navigation extends Component {
|
|||||||
|
|
||||||
return Button.component({
|
return Button.component({
|
||||||
className: 'Button Button--icon Navigation-drawer' +
|
className: 'Button Button--icon Navigation-drawer' +
|
||||||
(user && user.newNotificationsCount() ? ' new' : ''),
|
(user && user.newNotificationCount() ? ' new' : ''),
|
||||||
onclick: e => {
|
onclick: e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
drawer.show();
|
drawer.show();
|
||||||
|
@ -7,7 +7,7 @@ import icon from './icon';
|
|||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
export default function userOnline(user) {
|
export default function userOnline(user) {
|
||||||
if (user.lastSeenTime() && user.isOnline()) {
|
if (user.lastSeenAt() && user.isOnline()) {
|
||||||
return <span className="UserOnline">{icon('fas fa-circle')}</span>;
|
return <span className="UserOnline">{icon('fas fa-circle')}</span>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,28 +9,28 @@ Object.assign(Discussion.prototype, {
|
|||||||
title: Model.attribute('title'),
|
title: Model.attribute('title'),
|
||||||
slug: Model.attribute('slug'),
|
slug: Model.attribute('slug'),
|
||||||
|
|
||||||
startTime: Model.attribute('startTime', Model.transformDate),
|
createdAt: Model.attribute('createdAt', Model.transformDate),
|
||||||
startUser: Model.hasOne('startUser'),
|
user: Model.hasOne('user'),
|
||||||
startPost: Model.hasOne('startPost'),
|
firstPost: Model.hasOne('firstPost'),
|
||||||
|
|
||||||
lastTime: Model.attribute('lastTime', Model.transformDate),
|
lastPostedAt: Model.attribute('lastPostedAt', Model.transformDate),
|
||||||
lastUser: Model.hasOne('lastUser'),
|
lastPostedUser: Model.hasOne('lastPostedUser'),
|
||||||
lastPost: Model.hasOne('lastPost'),
|
lastPost: Model.hasOne('lastPost'),
|
||||||
lastPostNumber: Model.attribute('lastPostNumber'),
|
lastPostNumber: Model.attribute('lastPostNumber'),
|
||||||
|
|
||||||
commentsCount: Model.attribute('commentsCount'),
|
commentCount: Model.attribute('commentCount'),
|
||||||
repliesCount: computed('commentsCount', commentsCount => Math.max(0, commentsCount - 1)),
|
replyCount: computed('commentCount', commentCount => Math.max(0, commentCount - 1)),
|
||||||
posts: Model.hasMany('posts'),
|
posts: Model.hasMany('posts'),
|
||||||
mostRelevantPost: Model.hasOne('mostRelevantPost'),
|
mostRelevantPost: Model.hasOne('mostRelevantPost'),
|
||||||
|
|
||||||
readTime: Model.attribute('readTime', Model.transformDate),
|
lastReadAt: Model.attribute('lastReadAt', Model.transformDate),
|
||||||
readNumber: Model.attribute('readNumber'),
|
lastReadPostNumber: Model.attribute('lastReadPostNumber'),
|
||||||
isUnread: computed('unreadCount', unreadCount => !!unreadCount),
|
isUnread: computed('unreadCount', unreadCount => !!unreadCount),
|
||||||
isRead: computed('unreadCount', unreadCount => app.session.user && !unreadCount),
|
isRead: computed('unreadCount', unreadCount => app.session.user && !unreadCount),
|
||||||
|
|
||||||
hideTime: Model.attribute('hideTime', Model.transformDate),
|
hiddenAt: Model.attribute('hiddenAt', Model.transformDate),
|
||||||
hideUser: Model.hasOne('hideUser'),
|
hiddenUser: Model.hasOne('hiddenUser'),
|
||||||
isHidden: computed('hideTime', hideTime => !!hideTime),
|
isHidden: computed('hiddenAt', hiddenAt => !!hiddenAt),
|
||||||
|
|
||||||
canReply: Model.attribute('canReply'),
|
canReply: Model.attribute('canReply'),
|
||||||
canRename: Model.attribute('canRename'),
|
canRename: Model.attribute('canRename'),
|
||||||
@ -67,8 +67,8 @@ Object.assign(Discussion.prototype, {
|
|||||||
unreadCount() {
|
unreadCount() {
|
||||||
const user = app.session.user;
|
const user = app.session.user;
|
||||||
|
|
||||||
if (user && user.readTime() < this.lastTime()) {
|
if (user && user.markedAllAsReadAt() < this.lastPostedAt()) {
|
||||||
return Math.max(0, this.lastPostNumber() - (this.readNumber() || 0));
|
return Math.max(0, this.lastPostNumber() - (this.lastReadPostNumber() || 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1,19 +1,15 @@
|
|||||||
import Model from '../Model';
|
import Model from '../Model';
|
||||||
import computed from '../utils/computed';
|
|
||||||
|
|
||||||
export default class Notification extends Model {}
|
export default class Notification extends Model {}
|
||||||
|
|
||||||
Object.assign(Notification.prototype, {
|
Object.assign(Notification.prototype, {
|
||||||
contentType: Model.attribute('contentType'),
|
contentType: Model.attribute('contentType'),
|
||||||
subjectId: Model.attribute('subjectId'),
|
|
||||||
content: Model.attribute('content'),
|
content: Model.attribute('content'),
|
||||||
time: Model.attribute('time', Model.date),
|
createdAt: Model.attribute('createdAt', Model.transformDate),
|
||||||
|
|
||||||
isRead: Model.attribute('isRead'),
|
isRead: Model.attribute('isRead'),
|
||||||
unreadCount: Model.attribute('unreadCount'),
|
|
||||||
additionalUnreadCount: computed('unreadCount', unreadCount => Math.max(0, unreadCount - 1)),
|
|
||||||
|
|
||||||
user: Model.hasOne('user'),
|
user: Model.hasOne('user'),
|
||||||
sender: Model.hasOne('sender'),
|
fromUser: Model.hasOne('fromUser'),
|
||||||
subject: Model.hasOne('subject')
|
subject: Model.hasOne('subject')
|
||||||
});
|
});
|
||||||
|
@ -8,20 +8,20 @@ Object.assign(Post.prototype, {
|
|||||||
number: Model.attribute('number'),
|
number: Model.attribute('number'),
|
||||||
discussion: Model.hasOne('discussion'),
|
discussion: Model.hasOne('discussion'),
|
||||||
|
|
||||||
time: Model.attribute('time', Model.transformDate),
|
createdAt: Model.attribute('createdAt', Model.transformDate),
|
||||||
user: Model.hasOne('user'),
|
user: Model.hasOne('user'),
|
||||||
contentType: Model.attribute('contentType'),
|
contentType: Model.attribute('contentType'),
|
||||||
content: Model.attribute('content'),
|
content: Model.attribute('content'),
|
||||||
contentHtml: Model.attribute('contentHtml'),
|
contentHtml: Model.attribute('contentHtml'),
|
||||||
contentPlain: computed('contentHtml', getPlainContent),
|
contentPlain: computed('contentHtml', getPlainContent),
|
||||||
|
|
||||||
editTime: Model.attribute('editTime', Model.transformDate),
|
editedAt: Model.attribute('editedAt', Model.transformDate),
|
||||||
editUser: Model.hasOne('editUser'),
|
editedUser: Model.hasOne('editedUser'),
|
||||||
isEdited: computed('editTime', editTime => !!editTime),
|
isEdited: computed('editedAt', editedAt => !!editedAt),
|
||||||
|
|
||||||
hideTime: Model.attribute('hideTime', Model.transformDate),
|
hiddenAt: Model.attribute('hiddenAt', Model.transformDate),
|
||||||
hideUser: Model.hasOne('hideUser'),
|
hiddenUser: Model.hasOne('hiddenUser'),
|
||||||
isHidden: computed('hideTime', hideTime => !!hideTime),
|
isHidden: computed('hiddenAt', hiddenAt => !!hiddenAt),
|
||||||
|
|
||||||
canEdit: Model.attribute('canEdit'),
|
canEdit: Model.attribute('canEdit'),
|
||||||
canHide: Model.attribute('canHide'),
|
canHide: Model.attribute('canHide'),
|
||||||
|
@ -12,7 +12,7 @@ Object.assign(User.prototype, {
|
|||||||
username: Model.attribute('username'),
|
username: Model.attribute('username'),
|
||||||
displayName: Model.attribute('displayName'),
|
displayName: Model.attribute('displayName'),
|
||||||
email: Model.attribute('email'),
|
email: Model.attribute('email'),
|
||||||
isActivated: Model.attribute('isActivated'),
|
isEmailConfirmed: Model.attribute('isEmailConfirmed'),
|
||||||
password: Model.attribute('password'),
|
password: Model.attribute('password'),
|
||||||
|
|
||||||
avatarUrl: Model.attribute('avatarUrl'),
|
avatarUrl: Model.attribute('avatarUrl'),
|
||||||
@ -20,13 +20,13 @@ Object.assign(User.prototype, {
|
|||||||
groups: Model.hasMany('groups'),
|
groups: Model.hasMany('groups'),
|
||||||
|
|
||||||
joinTime: Model.attribute('joinTime', Model.transformDate),
|
joinTime: Model.attribute('joinTime', Model.transformDate),
|
||||||
lastSeenTime: Model.attribute('lastSeenTime', Model.transformDate),
|
lastSeenAt: Model.attribute('lastSeenAt', Model.transformDate),
|
||||||
readTime: Model.attribute('readTime', Model.transformDate),
|
markedAllAsReadAt: Model.attribute('markedAllAsReadAt', Model.transformDate),
|
||||||
unreadNotificationsCount: Model.attribute('unreadNotificationsCount'),
|
unreadNotificationCount: Model.attribute('unreadNotificationCount'),
|
||||||
newNotificationsCount: Model.attribute('newNotificationsCount'),
|
newNotificationCount: Model.attribute('newNotificationCount'),
|
||||||
|
|
||||||
discussionsCount: Model.attribute('discussionsCount'),
|
discussionCount: Model.attribute('discussionCount'),
|
||||||
commentsCount: Model.attribute('commentsCount'),
|
commentCount: Model.attribute('commentCount'),
|
||||||
|
|
||||||
canEdit: Model.attribute('canEdit'),
|
canEdit: Model.attribute('canEdit'),
|
||||||
canDelete: Model.attribute('canDelete'),
|
canDelete: Model.attribute('canDelete'),
|
||||||
@ -54,7 +54,7 @@ Object.assign(User.prototype, {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
isOnline() {
|
isOnline() {
|
||||||
return this.lastSeenTime() > moment().subtract(5, 'minutes').toDate();
|
return this.lastSeenAt() > moment().subtract(5, 'minutes').toDate();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,7 +87,7 @@ export default class DiscussionList extends Component {
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
requestParams() {
|
requestParams() {
|
||||||
const params = {include: ['startUser', 'lastUser'], filter: {}};
|
const params = {include: ['user', 'lastPostedUser'], filter: {}};
|
||||||
|
|
||||||
params.sort = this.sortMap()[this.props.params.sort];
|
params.sort = this.sortMap()[this.props.params.sort];
|
||||||
|
|
||||||
@ -112,10 +112,10 @@ export default class DiscussionList extends Component {
|
|||||||
if (this.props.params.q) {
|
if (this.props.params.q) {
|
||||||
map.relevance = '';
|
map.relevance = '';
|
||||||
}
|
}
|
||||||
map.latest = '-lastTime';
|
map.latest = '-lastPostedAt';
|
||||||
map.top = '-commentsCount';
|
map.top = '-commentCount';
|
||||||
map.newest = '-startTime';
|
map.newest = '-createdAt';
|
||||||
map.oldest = 'startTime';
|
map.oldest = 'createdAt';
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
this.subtree = new SubtreeRetainer(
|
this.subtree = new SubtreeRetainer(
|
||||||
() => this.props.discussion.freshness,
|
() => 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();
|
return time && time.getTime();
|
||||||
},
|
},
|
||||||
() => this.active()
|
() => this.active()
|
||||||
@ -58,7 +58,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
if (retain) return retain;
|
if (retain) return retain;
|
||||||
|
|
||||||
const discussion = this.props.discussion;
|
const discussion = this.props.discussion;
|
||||||
const startUser = discussion.startUser();
|
const user = discussion.user();
|
||||||
const isUnread = discussion.isUnread();
|
const isUnread = discussion.isUnread();
|
||||||
const isRead = discussion.isRead();
|
const isRead = discussion.isRead();
|
||||||
const showUnread = !this.showRepliesCount() && isUnread;
|
const showUnread = !this.showRepliesCount() && isUnread;
|
||||||
@ -75,7 +75,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
const phrase = this.props.params.q;
|
const phrase = this.props.params.q;
|
||||||
this.highlightRegExp = new RegExp(phrase+'|'+phrase.trim().replace(/\s+/g, '|'), 'gi');
|
this.highlightRegExp = new RegExp(phrase+'|'+phrase.trim().replace(/\s+/g, '|'), 'gi');
|
||||||
} else {
|
} else {
|
||||||
jumpTo = Math.min(discussion.lastPostNumber(), (discussion.readNumber() || 0) + 1);
|
jumpTo = Math.min(discussion.lastPostNumber(), (discussion.lastReadPostNumber() || 0) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -93,14 +93,14 @@ export default class DiscussionListItem extends Component {
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div className={'DiscussionListItem-content Slidable-content' + (isUnread ? ' unread' : '') + (isRead ? ' read' : '')}>
|
<div className={'DiscussionListItem-content Slidable-content' + (isUnread ? ' unread' : '') + (isRead ? ' read' : '')}>
|
||||||
<a href={startUser ? app.route.user(startUser) : '#'}
|
<a href={user ? app.route.user(user) : '#'}
|
||||||
className="DiscussionListItem-author"
|
className="DiscussionListItem-author"
|
||||||
title={extractText(app.translator.trans('core.forum.discussion_list.started_text', {user: startUser, ago: humanTime(discussion.startTime())}))}
|
title={extractText(app.translator.trans('core.forum.discussion_list.started_text', {user: user, ago: humanTime(discussion.createdAt())}))}
|
||||||
config={function(element) {
|
config={function(element) {
|
||||||
$(element).tooltip({placement: 'right'});
|
$(element).tooltip({placement: 'right'});
|
||||||
m.route.apply(this, arguments);
|
m.route.apply(this, arguments);
|
||||||
}}>
|
}}>
|
||||||
{avatar(startUser, {title: ''})}
|
{avatar(user, {title: ''})}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<ul className="DiscussionListItem-badges badges">
|
<ul className="DiscussionListItem-badges badges">
|
||||||
@ -117,7 +117,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
<span className="DiscussionListItem-count"
|
<span className="DiscussionListItem-count"
|
||||||
onclick={this.markAsRead.bind(this)}
|
onclick={this.markAsRead.bind(this)}
|
||||||
title={showUnread ? app.translator.trans('core.forum.discussion_list.mark_as_read_tooltip') : ''}>
|
title={showUnread ? app.translator.trans('core.forum.discussion_list.mark_as_read_tooltip') : ''}>
|
||||||
{abbreviateNumber(discussion[showUnread ? 'unreadCount' : 'repliesCount']())}
|
{abbreviateNumber(discussion[showUnread ? 'unreadCount' : 'replyCount']())}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -156,7 +156,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
*
|
*
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
showStartPost() {
|
showFirstPost() {
|
||||||
return ['newest', 'oldest'].indexOf(this.props.params.sort) !== -1;
|
return ['newest', 'oldest'].indexOf(this.props.params.sort) !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
const discussion = this.props.discussion;
|
const discussion = this.props.discussion;
|
||||||
|
|
||||||
if (discussion.isUnread()) {
|
if (discussion.isUnread()) {
|
||||||
discussion.save({readNumber: discussion.lastPostNumber()});
|
discussion.save({lastReadPostNumber: discussion.lastPostNumber()});
|
||||||
m.redraw();
|
m.redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -192,7 +192,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
if (this.props.params.q) {
|
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') {
|
if (post && post.contentType() === 'comment') {
|
||||||
const excerpt = highlight(post.contentPlain(), this.highlightRegExp, 175);
|
const excerpt = highlight(post.contentPlain(), this.highlightRegExp, 175);
|
||||||
@ -202,7 +202,7 @@ export default class DiscussionListItem extends Component {
|
|||||||
items.add('terminalPost',
|
items.add('terminalPost',
|
||||||
TerminalPost.component({
|
TerminalPost.component({
|
||||||
discussion: this.props.discussion,
|
discussion: this.props.discussion,
|
||||||
lastPost: !this.showStartPost()
|
lastPost: !this.showFirstPost()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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
|
// If the user hasn't read past here before, then we'll update their read
|
||||||
// state and redraw.
|
// state and redraw.
|
||||||
if (app.session.user && endNumber > (discussion.readNumber() || 0)) {
|
if (app.session.user && endNumber > (discussion.lastReadPostNumber() || 0)) {
|
||||||
discussion.save({readNumber: endNumber});
|
discussion.save({lastReadPostNumber: endNumber});
|
||||||
m.redraw();
|
m.redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,6 @@ export default class DiscussionRenamedNotification extends Notification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
content() {
|
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()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ export default class EditUserModal extends Modal {
|
|||||||
|
|
||||||
this.username = m.prop(user.username() || '');
|
this.username = m.prop(user.username() || '');
|
||||||
this.email = m.prop(user.email() || '');
|
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.setPassword = m.prop(false);
|
||||||
this.password = m.prop(user.password() || '');
|
this.password = m.prop(user.password() || '');
|
||||||
this.groups = {};
|
this.groups = {};
|
||||||
@ -50,7 +50,7 @@ export default class EditUserModal extends Modal {
|
|||||||
<input className="FormControl" placeholder={extractText(app.translator.trans('core.forum.edit_user.email_label'))}
|
<input className="FormControl" placeholder={extractText(app.translator.trans('core.forum.edit_user.email_label'))}
|
||||||
bidi={this.email} />
|
bidi={this.email} />
|
||||||
</div>
|
</div>
|
||||||
{!this.isActivated() ? (
|
{!this.isEmailConfirmed() ? (
|
||||||
<div>
|
<div>
|
||||||
{Button.component({
|
{Button.component({
|
||||||
className: 'Button Button--block',
|
className: 'Button Button--block',
|
||||||
@ -115,11 +115,11 @@ export default class EditUserModal extends Modal {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
const data = {
|
const data = {
|
||||||
username: this.username(),
|
username: this.username(),
|
||||||
isActivated: true,
|
isEmailConfirmed: true,
|
||||||
};
|
};
|
||||||
this.props.user.save(data, {errorHandler: this.onerror.bind(this)})
|
this.props.user.save(data, {errorHandler: this.onerror.bind(this)})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.isActivated(true);
|
this.isEmailConfirmed(true);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
m.redraw();
|
m.redraw();
|
||||||
})
|
})
|
||||||
|
@ -385,7 +385,7 @@ export default class IndexPage extends Page {
|
|||||||
const confirmation = confirm(app.translator.trans('core.forum.index.mark_all_as_read_confirmation'));
|
const confirmation = confirm(app.translator.trans('core.forum.index.mark_all_as_read_confirmation'));
|
||||||
|
|
||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
app.session.user.save({readTime: new Date()});
|
app.session.user.save({markedAllAsReadAt: new Date()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,10 @@ export default class Notification extends Component {
|
|||||||
|
|
||||||
if (!isInitialized) $(element).click(this.markAsRead.bind(this));
|
if (!isInitialized) $(element).click(this.markAsRead.bind(this));
|
||||||
}}>
|
}}>
|
||||||
{avatar(notification.sender())}
|
{avatar(notification.fromUser())}
|
||||||
{icon(this.icon(), {className: 'Notification-icon'})}
|
{icon(this.icon(), {className: 'Notification-icon'})}
|
||||||
<span className="Notification-content">{this.content()}</span>
|
<span className="Notification-content">{this.content()}</span>
|
||||||
{humanTime(notification.time())}
|
{humanTime(notification.createdAt())}
|
||||||
<div className="Notification-excerpt">
|
<div className="Notification-excerpt">
|
||||||
{this.excerpt()}
|
{this.excerpt()}
|
||||||
</div>
|
</div>
|
||||||
@ -79,7 +79,7 @@ export default class Notification extends Component {
|
|||||||
markAsRead() {
|
markAsRead() {
|
||||||
if (this.props.notification.isRead()) return;
|
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});
|
this.props.notification.save({isRead: true});
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ export default class NotificationList extends Component {
|
|||||||
* been loaded.
|
* been loaded.
|
||||||
*/
|
*/
|
||||||
load() {
|
load() {
|
||||||
if (app.session.user.newNotificationsCount()) {
|
if (app.session.user.newNotificationCount()) {
|
||||||
delete app.cache.notifications;
|
delete app.cache.notifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ export default class NotificationList extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
app.session.user.pushAttributes({newNotificationsCount: 0});
|
app.session.user.pushAttributes({newNotificationCount: 0});
|
||||||
|
|
||||||
this.loadMore();
|
this.loadMore();
|
||||||
}
|
}
|
||||||
@ -191,7 +191,7 @@ export default class NotificationList extends Component {
|
|||||||
markAllAsRead() {
|
markAllAsRead() {
|
||||||
if (!app.cache.notifications) return;
|
if (!app.cache.notifications) return;
|
||||||
|
|
||||||
app.session.user.pushAttributes({unreadNotificationsCount: 0});
|
app.session.user.pushAttributes({unreadNotificationCount: 0});
|
||||||
|
|
||||||
app.cache.notifications.forEach(notifications => {
|
app.cache.notifications.forEach(notifications => {
|
||||||
notifications.forEach(notification => notification.pushAttributes({isRead: true}))
|
notifications.forEach(notification => notification.pushAttributes({isRead: true}))
|
||||||
|
@ -62,11 +62,11 @@ export default class NotificationsDropdown extends Dropdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getUnreadCount() {
|
getUnreadCount() {
|
||||||
return app.session.user.unreadNotificationsCount();
|
return app.session.user.unreadNotificationCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
getNewCount() {
|
getNewCount() {
|
||||||
return app.session.user.newNotificationsCount();
|
return app.session.user.newNotificationCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
menuClick(e) {
|
menuClick(e) {
|
||||||
|
@ -18,10 +18,10 @@ export default class PostEdited extends Component {
|
|||||||
|
|
||||||
view() {
|
view() {
|
||||||
const post = this.props.post;
|
const post = this.props.post;
|
||||||
const editUser = post.editUser();
|
const editedUser = post.editedUser();
|
||||||
const editedInfo = extractText(app.translator.trans(
|
const editedInfo = extractText(app.translator.trans(
|
||||||
'core.forum.post.edited_tooltip',
|
'core.forum.post.edited_tooltip',
|
||||||
{user: editUser, ago: humanTime(post.editTime())}
|
{user: editedUser, ago: humanTime(post.editedAt())}
|
||||||
));
|
));
|
||||||
if (editedInfo !== this.oldEditedInfo) {
|
if (editedInfo !== this.oldEditedInfo) {
|
||||||
this.shouldUpdateTooltip = true;
|
this.shouldUpdateTooltip = true;
|
||||||
|
@ -14,7 +14,7 @@ import fullTime from '../../common/helpers/fullTime';
|
|||||||
export default class PostMeta extends Component {
|
export default class PostMeta extends Component {
|
||||||
view() {
|
view() {
|
||||||
const post = this.props.post;
|
const post = this.props.post;
|
||||||
const time = post.time();
|
const time = post.createdAt();
|
||||||
const permalink = this.getPermalink(post);
|
const permalink = this.getPermalink(post);
|
||||||
const touch = 'ontouchstart' in document.documentElement;
|
const touch = 'ontouchstart' in document.documentElement;
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ class PostStream extends Component {
|
|||||||
const attrs = {'data-index': this.visibleStart + i};
|
const attrs = {'data-index': this.visibleStart + i};
|
||||||
|
|
||||||
if (post) {
|
if (post) {
|
||||||
const time = post.time();
|
const time = post.createdAt();
|
||||||
const PostComponent = app.postComponents[post.contentType()];
|
const PostComponent = app.postComponents[post.contentType()];
|
||||||
content = PostComponent ? PostComponent.component({post}) : '';
|
content = PostComponent ? PostComponent.component({post}) : '';
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ export default class PostsUserPage extends UserPage {
|
|||||||
type: 'comment'
|
type: 'comment'
|
||||||
},
|
},
|
||||||
page: {offset, limit: this.loadLimit},
|
page: {offset, limit: this.loadLimit},
|
||||||
sort: '-time'
|
sort: '-createdAt'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ export default class SettingsPage extends UserPage {
|
|||||||
children: app.translator.trans('core.forum.settings.privacy_disclose_online_label'),
|
children: app.translator.trans('core.forum.settings.privacy_disclose_online_label'),
|
||||||
state: this.user.preferences().discloseOnline,
|
state: this.user.preferences().discloseOnline,
|
||||||
onchange: (value, component) => {
|
onchange: (value, component) => {
|
||||||
this.user.pushAttributes({lastSeenTime: null});
|
this.user.pushAttributes({lastSeenAt: null});
|
||||||
this.preferenceSaver('discloseOnline')(value, component);
|
this.preferenceSaver('discloseOnline')(value, component);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -13,10 +13,10 @@ import icon from '../../common/helpers/icon';
|
|||||||
export default class TerminalPost extends Component {
|
export default class TerminalPost extends Component {
|
||||||
view() {
|
view() {
|
||||||
const discussion = this.props.discussion;
|
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 user = discussion[lastPost ? 'lastPostedUser' : 'user']();
|
||||||
const time = discussion[lastPost ? 'lastTime' : 'startTime']();
|
const time = discussion[lastPost ? 'lastPostedAt' : 'createdAt']();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
|
@ -79,16 +79,16 @@ export default class UserCard extends Component {
|
|||||||
infoItems() {
|
infoItems() {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
const user = this.props.user;
|
const user = this.props.user;
|
||||||
const lastSeenTime = user.lastSeenTime();
|
const lastSeenAt = user.lastSeenAt();
|
||||||
|
|
||||||
if (lastSeenTime) {
|
if (lastSeenAt) {
|
||||||
const online = user.isOnline();
|
const online = user.isOnline();
|
||||||
|
|
||||||
items.add('lastSeen', (
|
items.add('lastSeen', (
|
||||||
<span className={'UserCard-lastSeen' + (online ? ' online' : '')}>
|
<span className={'UserCard-lastSeen' + (online ? ' online' : '')}>
|
||||||
{online
|
{online
|
||||||
? [icon('fas fa-circle'), ' ', app.translator.trans('core.forum.user.online_text')]
|
? [icon('fas fa-circle'), ' ', app.translator.trans('core.forum.user.online_text')]
|
||||||
: [icon('far fa-clock'), ' ', humanTime(lastSeenTime)]}
|
: [icon('far fa-clock'), ' ', humanTime(lastSeenAt)]}
|
||||||
</span>
|
</span>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ export default class UserPage extends Page {
|
|||||||
items.add('posts',
|
items.add('posts',
|
||||||
LinkButton.component({
|
LinkButton.component({
|
||||||
href: app.route('user.posts', {username: user.username()}),
|
href: app.route('user.posts', {username: user.username()}),
|
||||||
children: [app.translator.trans('core.forum.user.posts_link'), <span className="Button-badge">{user.commentsCount()}</span>],
|
children: [app.translator.trans('core.forum.user.posts_link'), <span className="Button-badge">{user.commentCount()}</span>],
|
||||||
icon: 'far fa-comment'
|
icon: 'far fa-comment'
|
||||||
}),
|
}),
|
||||||
100
|
100
|
||||||
@ -140,7 +140,7 @@ export default class UserPage extends Page {
|
|||||||
items.add('discussions',
|
items.add('discussions',
|
||||||
LinkButton.component({
|
LinkButton.component({
|
||||||
href: app.route('user.discussions', {username: user.username()}),
|
href: app.route('user.discussions', {username: user.username()}),
|
||||||
children: [app.translator.trans('core.forum.user.discussions_link'), <span className="Button-badge">{user.discussionsCount()}</span>],
|
children: [app.translator.trans('core.forum.user.discussions_link'), <span className="Button-badge">{user.discussionCount()}</span>],
|
||||||
icon: 'fas fa-bars'
|
icon: 'fas fa-bars'
|
||||||
}),
|
}),
|
||||||
90
|
90
|
||||||
|
@ -183,7 +183,7 @@ export default {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
hideAction() {
|
hideAction() {
|
||||||
this.pushAttributes({ hideTime: new Date(), hideUser: app.session.user });
|
this.pushAttributes({ hiddenAt: new Date(), hiddenUser: app.session.user });
|
||||||
|
|
||||||
return this.save({ isHidden: true });
|
return this.save({ isHidden: true });
|
||||||
},
|
},
|
||||||
@ -194,7 +194,7 @@ export default {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
restoreAction() {
|
restoreAction() {
|
||||||
this.pushAttributes({ hideTime: null, hideUser: null });
|
this.pushAttributes({ hiddenAt: null, hiddenUser: null });
|
||||||
|
|
||||||
return this.save({ isHidden: false });
|
return this.save({ isHidden: false });
|
||||||
},
|
},
|
||||||
|
@ -123,7 +123,7 @@ export default {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
hideAction() {
|
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());
|
return this.save({ isHidden: true }).then(() => m.redraw());
|
||||||
},
|
},
|
||||||
@ -134,7 +134,7 @@ export default {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
restoreAction() {
|
restoreAction() {
|
||||||
this.pushAttributes({ hideTime: null, hideUser: null });
|
this.pushAttributes({ hiddenAt: null, hiddenUser: null });
|
||||||
|
|
||||||
return this.save({ isHidden: false }).then(() => m.redraw());
|
return this.save({ isHidden: false }).then(() => m.redraw());
|
||||||
},
|
},
|
||||||
|
@ -10,7 +10,7 @@ import icon from '../../common/helpers/icon';
|
|||||||
export default function alertEmailConfirmation(app) {
|
export default function alertEmailConfirmation(app) {
|
||||||
const user = app.session.user;
|
const user = app.session.user;
|
||||||
|
|
||||||
if (!user || user.isActivated()) return;
|
if (!user || user.isEmailConfirmed()) return;
|
||||||
|
|
||||||
const resendButton = Button.component({
|
const resendButton = Button.component({
|
||||||
className: 'Button Button--link',
|
className: 'Button Button--link',
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Flarum.
|
|
||||||
*
|
|
||||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
|
||||||
*
|
|
||||||
* 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,
|
|
||||||
]);
|
|
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
47
migrations/2018_01_11_095000_change_api_keys_columns.php
Normal file
47
migrations/2018_01_11_095000_change_api_keys_columns.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -10,8 +10,5 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use Flarum\Database\Migration;
|
use Flarum\Database\Migration;
|
||||||
use Flarum\Group\Group;
|
|
||||||
|
|
||||||
return Migration::addPermissions([
|
return Migration::renameTable('auth_tokens', 'registration_tokens');
|
||||||
'discussion.hidePosts' => Group::MODERATOR_ID
|
|
||||||
]);
|
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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');
|
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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");
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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'
|
||||||
|
]);
|
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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');
|
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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'
|
||||||
|
]);
|
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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');
|
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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");
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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');
|
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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');
|
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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');
|
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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");
|
||||||
|
}
|
||||||
|
];
|
20
migrations/2018_01_18_135000_change_posts_rename_columns.php
Normal file
20
migrations/2018_01_18_135000_change_posts_rename_columns.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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'
|
||||||
|
]);
|
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
26
migrations/2018_01_30_220100_create_post_user_table.php
Normal file
26
migrations/2018_01_30_220100_create_post_user_table.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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');
|
||||||
|
}
|
||||||
|
);
|
23
migrations/2018_01_30_222900_change_users_rename_columns.php
Normal file
23
migrations/2018_01_30_222900_change_users_rename_columns.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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'
|
||||||
|
]);
|
38
migrations/2018_07_21_000000_seed_default_groups.php
Normal file
38
migrations/2018_07_21_000000_seed_default_groups.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
];
|
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||||
|
*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
@ -14,22 +14,16 @@ namespace Flarum\Api;
|
|||||||
use Flarum\Database\AbstractModel;
|
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
|
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.
|
* Generate an API key.
|
||||||
*
|
*
|
||||||
@ -37,8 +31,10 @@ class ApiKey extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public static function generate()
|
public static function generate()
|
||||||
{
|
{
|
||||||
return new static([
|
$key = new static;
|
||||||
'id' => str_random(40)
|
|
||||||
]);
|
$key->key = str_random(40);
|
||||||
|
|
||||||
|
return $key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,9 +31,9 @@ class CreateDiscussionController extends AbstractCreateController
|
|||||||
*/
|
*/
|
||||||
public $include = [
|
public $include = [
|
||||||
'posts',
|
'posts',
|
||||||
'startUser',
|
'user',
|
||||||
'lastUser',
|
'lastPostedUser',
|
||||||
'startPost',
|
'firstPost',
|
||||||
'lastPost'
|
'lastPost'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ class CreatePostController extends AbstractCreateController
|
|||||||
'user',
|
'user',
|
||||||
'discussion',
|
'discussion',
|
||||||
'discussion.posts',
|
'discussion.posts',
|
||||||
'discussion.lastUser'
|
'discussion.lastPostedUser'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,7 +84,7 @@ class CreatePostController extends AbstractCreateController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$discussion = $post->discussion;
|
$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;
|
return $post;
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ class CreateTokenController implements RequestHandlerInterface
|
|||||||
$token->save();
|
$token->save();
|
||||||
|
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
'token' => $token->id,
|
'token' => $token->token,
|
||||||
'userId' => $user->id
|
'userId' => $user->id
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,8 @@ class ListDiscussionsController extends AbstractListController
|
|||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public $include = [
|
public $include = [
|
||||||
'startUser',
|
'user',
|
||||||
'lastUser',
|
'lastPostedUser',
|
||||||
'mostRelevantPost',
|
'mostRelevantPost',
|
||||||
'mostRelevantPost.user'
|
'mostRelevantPost.user'
|
||||||
];
|
];
|
||||||
@ -40,14 +40,14 @@ class ListDiscussionsController extends AbstractListController
|
|||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public $optionalInclude = [
|
public $optionalInclude = [
|
||||||
'startPost',
|
'firstPost',
|
||||||
'lastPost'
|
'lastPost'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public $sortFields = ['lastTime', 'commentsCount', 'startTime'];
|
public $sortFields = ['lastPostedAt', 'commentCount', 'createdAt'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var DiscussionSearcher
|
* @var DiscussionSearcher
|
||||||
@ -98,7 +98,7 @@ class ListDiscussionsController extends AbstractListController
|
|||||||
|
|
||||||
$results = $results->getResults()->load($load);
|
$results = $results->getResults()->load($load);
|
||||||
|
|
||||||
if ($relations = array_intersect($load, ['startPost', 'lastPost'])) {
|
if ($relations = array_intersect($load, ['firstPost', 'lastPost'])) {
|
||||||
foreach ($results as $discussion) {
|
foreach ($results as $discussion) {
|
||||||
foreach ($relations as $relation) {
|
foreach ($relations as $relation) {
|
||||||
if ($discussion->$relation) {
|
if ($discussion->$relation) {
|
||||||
|
@ -30,7 +30,7 @@ class ListNotificationsController extends AbstractListController
|
|||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public $include = [
|
public $include = [
|
||||||
'sender',
|
'fromUser',
|
||||||
'subject',
|
'subject',
|
||||||
'subject.discussion'
|
'subject.discussion'
|
||||||
];
|
];
|
||||||
|
@ -32,15 +32,15 @@ class ListPostsController extends AbstractListController
|
|||||||
public $include = [
|
public $include = [
|
||||||
'user',
|
'user',
|
||||||
'user.groups',
|
'user.groups',
|
||||||
'editUser',
|
'editedUser',
|
||||||
'hideUser',
|
'hiddenUser',
|
||||||
'discussion'
|
'discussion'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public $sortFields = ['time'];
|
public $sortFields = ['createdAt'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Flarum\Post\PostRepository
|
* @var \Flarum\Post\PostRepository
|
||||||
@ -120,7 +120,7 @@ class ListPostsController extends AbstractListController
|
|||||||
$query->skip($offset)->take($limit);
|
$query->skip($offset)->take($limit);
|
||||||
|
|
||||||
foreach ((array) $sort as $field => $order) {
|
foreach ((array) $sort as $field => $order) {
|
||||||
$query->orderBy($field, $order);
|
$query->orderBy(snake_case($field), $order);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $query->pluck('id')->all();
|
return $query->pluck('id')->all();
|
||||||
|
@ -36,10 +36,10 @@ class ListUsersController extends AbstractListController
|
|||||||
*/
|
*/
|
||||||
public $sortFields = [
|
public $sortFields = [
|
||||||
'username',
|
'username',
|
||||||
'commentsCount',
|
'commentCount',
|
||||||
'discussionsCount',
|
'discussionCount',
|
||||||
'lastSeenTime',
|
'lastSeenAt',
|
||||||
'joinTime'
|
'joinedAt'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,7 +81,7 @@ class SendConfirmationEmailController implements RequestHandlerInterface
|
|||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'{username}' => $actor->username,
|
'{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')
|
'{forum}' => $this->settings->get('forum_title')
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -44,17 +44,17 @@ class ShowDiscussionController extends AbstractShowController
|
|||||||
'posts.discussion',
|
'posts.discussion',
|
||||||
'posts.user',
|
'posts.user',
|
||||||
'posts.user.groups',
|
'posts.user.groups',
|
||||||
'posts.editUser',
|
'posts.editedUser',
|
||||||
'posts.hideUser'
|
'posts.hiddenUser'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public $optionalInclude = [
|
public $optionalInclude = [
|
||||||
'startUser',
|
'user',
|
||||||
'lastUser',
|
'lastPostedUser',
|
||||||
'startPost',
|
'firstPost',
|
||||||
'lastPost'
|
'lastPost'
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -118,7 +118,7 @@ class ShowDiscussionController extends AbstractShowController
|
|||||||
*/
|
*/
|
||||||
private function loadPostIds(Discussion $discussion, User $actor)
|
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 = $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();
|
$posts = $query->get()->all();
|
||||||
|
|
||||||
|
@ -29,8 +29,8 @@ class ShowPostController extends AbstractShowController
|
|||||||
public $include = [
|
public $include = [
|
||||||
'user',
|
'user',
|
||||||
'user.groups',
|
'user.groups',
|
||||||
'editUser',
|
'editedUser',
|
||||||
'hideUser',
|
'hiddenUser',
|
||||||
'discussion'
|
'discussion'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ class UpdateDiscussionController extends AbstractShowController
|
|||||||
|
|
||||||
// TODO: Refactor the ReadDiscussion (state) command into EditDiscussion?
|
// TODO: Refactor the ReadDiscussion (state) command into EditDiscussion?
|
||||||
// That's what extensions will do anyway.
|
// 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(
|
$state = $this->bus->dispatch(
|
||||||
new ReadDiscussion($discussionId, $actor, $readNumber)
|
new ReadDiscussion($discussionId, $actor, $readNumber)
|
||||||
);
|
);
|
||||||
@ -64,7 +64,7 @@ class UpdateDiscussionController extends AbstractShowController
|
|||||||
|
|
||||||
if ($posts = $discussion->getModifiedPosts()) {
|
if ($posts = $discussion->getModifiedPosts()) {
|
||||||
$posts = (new Collection($posts))->load('discussion', 'user');
|
$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 ($discussionPosts as &$id) {
|
||||||
foreach ($posts as $post) {
|
foreach ($posts as $post) {
|
||||||
|
@ -28,7 +28,7 @@ class UpdatePostController extends AbstractShowController
|
|||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public $include = [
|
public $include = [
|
||||||
'editUser',
|
'editedUser',
|
||||||
'discussion'
|
'discussion'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ class BasicDiscussionSerializer extends AbstractSerializer
|
|||||||
/**
|
/**
|
||||||
* @return \Tobscure\JsonApi\Relationship
|
* @return \Tobscure\JsonApi\Relationship
|
||||||
*/
|
*/
|
||||||
protected function startUser($discussion)
|
protected function user($discussion)
|
||||||
{
|
{
|
||||||
return $this->hasOne($discussion, BasicUserSerializer::class);
|
return $this->hasOne($discussion, BasicUserSerializer::class);
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ class BasicDiscussionSerializer extends AbstractSerializer
|
|||||||
/**
|
/**
|
||||||
* @return \Tobscure\JsonApi\Relationship
|
* @return \Tobscure\JsonApi\Relationship
|
||||||
*/
|
*/
|
||||||
protected function startPost($discussion)
|
protected function firstPost($discussion)
|
||||||
{
|
{
|
||||||
return $this->hasOne($discussion, BasicPostSerializer::class);
|
return $this->hasOne($discussion, BasicPostSerializer::class);
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ class BasicDiscussionSerializer extends AbstractSerializer
|
|||||||
/**
|
/**
|
||||||
* @return \Tobscure\JsonApi\Relationship
|
* @return \Tobscure\JsonApi\Relationship
|
||||||
*/
|
*/
|
||||||
protected function lastUser($discussion)
|
protected function lastPostedUser($discussion)
|
||||||
{
|
{
|
||||||
return $this->hasOne($discussion, BasicUserSerializer::class);
|
return $this->hasOne($discussion, BasicUserSerializer::class);
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ class BasicDiscussionSerializer extends AbstractSerializer
|
|||||||
/**
|
/**
|
||||||
* @return \Tobscure\JsonApi\Relationship
|
* @return \Tobscure\JsonApi\Relationship
|
||||||
*/
|
*/
|
||||||
protected function hideUser($discussion)
|
protected function hiddenUser($discussion)
|
||||||
{
|
{
|
||||||
return $this->hasOne($discussion, BasicUserSerializer::class);
|
return $this->hasOne($discussion, BasicUserSerializer::class);
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ class BasicPostSerializer extends AbstractSerializer
|
|||||||
$attributes = [
|
$attributes = [
|
||||||
'id' => (int) $post->id,
|
'id' => (int) $post->id,
|
||||||
'number' => (int) $post->number,
|
'number' => (int) $post->number,
|
||||||
'time' => $this->formatDate($post->time),
|
'createdAt' => $this->formatDate($post->created_at),
|
||||||
'contentType' => $post->type
|
'contentType' => $post->type
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -14,18 +14,19 @@ namespace Flarum\Api\Serializer;
|
|||||||
class CurrentUserSerializer extends UserSerializer
|
class CurrentUserSerializer extends UserSerializer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* @param \Flarum\User\User $user
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function getDefaultAttributes($user)
|
protected function getDefaultAttributes($user)
|
||||||
{
|
{
|
||||||
$attributes = parent::getDefaultAttributes($user);
|
$attributes = parent::getDefaultAttributes($user);
|
||||||
|
|
||||||
$attributes += [
|
$attributes += [
|
||||||
'isActivated' => (bool) $user->is_activated,
|
'isEmailConfirmed' => (bool) $user->is_email_confirmed,
|
||||||
'email' => $user->email,
|
'email' => $user->email,
|
||||||
'readTime' => $this->formatDate($user->read_time),
|
'markedAllAsReadAt' => $this->formatDate($user->marked_all_as_read_at),
|
||||||
'unreadNotificationsCount' => (int) $user->getUnreadNotificationsCount(),
|
'unreadNotificationCount' => (int) $user->getUnreadNotificationCount(),
|
||||||
'newNotificationsCount' => (int) $user->getNewNotificationsCount(),
|
'newNotificationCount' => (int) $user->getNewNotificationCount(),
|
||||||
'preferences' => (array) $user->preferences
|
'preferences' => (array) $user->preferences
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -37,10 +37,10 @@ class DiscussionSerializer extends BasicDiscussionSerializer
|
|||||||
$gate = $this->gate->forUser($this->actor);
|
$gate = $this->gate->forUser($this->actor);
|
||||||
|
|
||||||
$attributes = parent::getDefaultAttributes($discussion) + [
|
$attributes = parent::getDefaultAttributes($discussion) + [
|
||||||
'commentsCount' => (int) $discussion->comments_count,
|
'commentCount' => (int) $discussion->comment_count,
|
||||||
'participantsCount' => (int) $discussion->participants_count,
|
'participantCount' => (int) $discussion->participant_count,
|
||||||
'startTime' => $this->formatDate($discussion->start_time),
|
'createdAt' => $this->formatDate($discussion->created_at),
|
||||||
'lastTime' => $this->formatDate($discussion->last_time),
|
'lastPostedAt' => $this->formatDate($discussion->last_posted_at),
|
||||||
'lastPostNumber' => (int) $discussion->last_post_number,
|
'lastPostNumber' => (int) $discussion->last_post_number,
|
||||||
'canReply' => $gate->allows('reply', $discussion),
|
'canReply' => $gate->allows('reply', $discussion),
|
||||||
'canRename' => $gate->allows('rename', $discussion),
|
'canRename' => $gate->allows('rename', $discussion),
|
||||||
@ -48,17 +48,17 @@ class DiscussionSerializer extends BasicDiscussionSerializer
|
|||||||
'canHide' => $gate->allows('hide', $discussion)
|
'canHide' => $gate->allows('hide', $discussion)
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($discussion->hide_time) {
|
if ($discussion->hidden_at) {
|
||||||
$attributes['isHidden'] = true;
|
$attributes['isHidden'] = true;
|
||||||
$attributes['hideTime'] = $this->formatDate($discussion->hide_time);
|
$attributes['hiddenAt'] = $this->formatDate($discussion->hidden_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
Discussion::setStateUser($this->actor);
|
Discussion::setStateUser($this->actor);
|
||||||
|
|
||||||
if ($state = $discussion->state) {
|
if ($state = $discussion->state) {
|
||||||
$attributes += [
|
$attributes += [
|
||||||
'readTime' => $this->formatDate($state->read_time),
|
'lastReadAt' => $this->formatDate($state->last_read_at),
|
||||||
'readNumber' => (int) $state->read_number
|
'lastReadPostNumber' => (int) $state->last_read_post_number
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +47,13 @@ class NotificationSerializer extends AbstractSerializer
|
|||||||
'id' => (int) $notification->id,
|
'id' => (int) $notification->id,
|
||||||
'contentType' => $notification->type,
|
'contentType' => $notification->type,
|
||||||
'content' => $notification->data,
|
'content' => $notification->data,
|
||||||
'time' => $this->formatDate($notification->time),
|
'createdAt' => $this->formatDate($notification->created_at),
|
||||||
'isRead' => (bool) $notification->is_read
|
'isRead' => (bool) $notification->read_at
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Notification $notification
|
||||||
* @return \Tobscure\JsonApi\Relationship
|
* @return \Tobscure\JsonApi\Relationship
|
||||||
*/
|
*/
|
||||||
protected function user($notification)
|
protected function user($notification)
|
||||||
@ -61,14 +62,16 @@ class NotificationSerializer extends AbstractSerializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Notification $notification
|
||||||
* @return \Tobscure\JsonApi\Relationship
|
* @return \Tobscure\JsonApi\Relationship
|
||||||
*/
|
*/
|
||||||
protected function sender($notification)
|
protected function fromUser($notification)
|
||||||
{
|
{
|
||||||
return $this->hasOne($notification, BasicUserSerializer::class);
|
return $this->hasOne($notification, BasicUserSerializer::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Notification $notification
|
||||||
* @return \Tobscure\JsonApi\Relationship
|
* @return \Tobscure\JsonApi\Relationship
|
||||||
*/
|
*/
|
||||||
protected function subject($notification)
|
protected function subject($notification)
|
||||||
|
@ -55,13 +55,13 @@ class PostSerializer extends BasicPostSerializer
|
|||||||
$attributes['content'] = $post->content;
|
$attributes['content'] = $post->content;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($post->edit_time) {
|
if ($post->edited_at) {
|
||||||
$attributes['editTime'] = $this->formatDate($post->edit_time);
|
$attributes['editedAt'] = $this->formatDate($post->edited_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($post->hide_time) {
|
if ($post->hidden_at) {
|
||||||
$attributes['isHidden'] = true;
|
$attributes['isHidden'] = true;
|
||||||
$attributes['hideTime'] = $this->formatDate($post->hide_time);
|
$attributes['hiddenAt'] = $this->formatDate($post->hidden_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
$attributes += [
|
$attributes += [
|
||||||
@ -92,7 +92,7 @@ class PostSerializer extends BasicPostSerializer
|
|||||||
/**
|
/**
|
||||||
* @return \Tobscure\JsonApi\Relationship
|
* @return \Tobscure\JsonApi\Relationship
|
||||||
*/
|
*/
|
||||||
protected function editUser($post)
|
protected function editedUser($post)
|
||||||
{
|
{
|
||||||
return $this->hasOne($post, BasicUserSerializer::class);
|
return $this->hasOne($post, BasicUserSerializer::class);
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ class PostSerializer extends BasicPostSerializer
|
|||||||
/**
|
/**
|
||||||
* @return \Tobscure\JsonApi\Relationship
|
* @return \Tobscure\JsonApi\Relationship
|
||||||
*/
|
*/
|
||||||
protected function hideUser($post)
|
protected function hiddenUser($post)
|
||||||
{
|
{
|
||||||
return $this->hasOne($post, BasicUserSerializer::class);
|
return $this->hasOne($post, BasicUserSerializer::class);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,8 @@ class UserSerializer extends BasicUserSerializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* @param \Flarum\User\User $user
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function getDefaultAttributes($user)
|
protected function getDefaultAttributes($user)
|
||||||
{
|
{
|
||||||
@ -40,23 +41,23 @@ class UserSerializer extends BasicUserSerializer
|
|||||||
$canEdit = $gate->allows('edit', $user);
|
$canEdit = $gate->allows('edit', $user);
|
||||||
|
|
||||||
$attributes += [
|
$attributes += [
|
||||||
'joinTime' => $this->formatDate($user->join_time),
|
'joinTime' => $this->formatDate($user->joined_at),
|
||||||
'discussionsCount' => (int) $user->discussions_count,
|
'discussionCount' => (int) $user->discussion_count,
|
||||||
'commentsCount' => (int) $user->comments_count,
|
'commentCount' => (int) $user->comment_count,
|
||||||
'canEdit' => $canEdit,
|
'canEdit' => $canEdit,
|
||||||
'canDelete' => $gate->allows('delete', $user),
|
'canDelete' => $gate->allows('delete', $user),
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($user->getPreference('discloseOnline')) {
|
if ($user->getPreference('discloseOnline')) {
|
||||||
$attributes += [
|
$attributes += [
|
||||||
'lastSeenTime' => $this->formatDate($user->last_seen_time)
|
'lastSeenAt' => $this->formatDate($user->last_seen_at)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($canEdit || $this->actor->id === $user->id) {
|
if ($canEdit || $this->actor->id === $user->id) {
|
||||||
$attributes += [
|
$attributes += [
|
||||||
'isActivated' => (bool) $user->is_activated,
|
'isEmailConfirmed' => (bool) $user->is_email_confirmed,
|
||||||
'email' => $user->email
|
'email' => $user->email
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,16 +78,28 @@ abstract class Migration
|
|||||||
* Rename a column.
|
* Rename a column.
|
||||||
*/
|
*/
|
||||||
public static function renameColumn($tableName, $from, $to)
|
public static function renameColumn($tableName, $from, $to)
|
||||||
|
{
|
||||||
|
return static::renameColumns($tableName, [$from => $to]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename multiple columns.
|
||||||
|
*/
|
||||||
|
public static function renameColumns($tableName, array $columnNames)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'up' => function (Builder $schema) use ($tableName, $from, $to) {
|
'up' => function (Builder $schema) use ($tableName, $columnNames) {
|
||||||
$schema->table($tableName, function (Blueprint $table) use ($from, $to) {
|
$schema->table($tableName, function (Blueprint $table) use ($columnNames) {
|
||||||
$table->renameColumn($from, $to);
|
foreach ($columnNames as $from => $to) {
|
||||||
|
$table->renameColumn($from, $to);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'down' => function (Builder $schema) use ($tableName, $from, $to) {
|
'down' => function (Builder $schema) use ($tableName, $columnNames) {
|
||||||
$schema->table($tableName, function (Blueprint $table) use ($from, $to) {
|
$schema->table($tableName, function (Blueprint $table) use ($columnNames) {
|
||||||
$table->renameColumn($to, $from);
|
foreach ($columnNames as $to => $from) {
|
||||||
|
$table->renameColumn($from, $to);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@ -125,11 +137,11 @@ abstract class Migration
|
|||||||
*/
|
*/
|
||||||
public static function addPermissions(array $permissions)
|
public static function addPermissions(array $permissions)
|
||||||
{
|
{
|
||||||
$keys = [];
|
$rows = [];
|
||||||
|
|
||||||
foreach ($permissions as $permission => $groups) {
|
foreach ($permissions as $permission => $groups) {
|
||||||
foreach ((array) $groups as $group) {
|
foreach ((array) $groups as $group) {
|
||||||
$keys[] = [
|
$rows[] = [
|
||||||
'group_id' => $group,
|
'group_id' => $group,
|
||||||
'permission' => $permission,
|
'permission' => $permission,
|
||||||
];
|
];
|
||||||
@ -137,23 +149,27 @@ abstract class Migration
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'up' => function (Builder $schema) use ($keys) {
|
'up' => function (Builder $schema) use ($rows) {
|
||||||
$db = $schema->getConnection();
|
$db = $schema->getConnection();
|
||||||
|
|
||||||
foreach ($keys as $key) {
|
foreach ($rows as $row) {
|
||||||
$instance = $db->table('permissions')->where($key)->first();
|
if ($db->table('group_permission')->where($row)->exists()) {
|
||||||
|
continue;
|
||||||
if (is_null($instance)) {
|
|
||||||
$db->table('permissions')->insert($key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
$db = $schema->getConnection();
|
||||||
|
|
||||||
foreach ($keys as $key) {
|
foreach ($rows as $row) {
|
||||||
$db->table('permissions')->where($key)->delete();
|
$db->table('group_permission')->where($row)->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -63,6 +63,9 @@ class Migrator
|
|||||||
$this->repository = $repository;
|
$this->repository = $repository;
|
||||||
|
|
||||||
$this->schemaBuilder = $connection->getSchemaBuilder();
|
$this->schemaBuilder = $connection->getSchemaBuilder();
|
||||||
|
|
||||||
|
// Workaround for https://github.com/laravel/framework/issues/1186
|
||||||
|
$connection->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,17 +34,17 @@ class ReadDiscussion
|
|||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
public $readNumber;
|
public $lastReadPostNumber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $discussionId The ID of the discussion to mark as read.
|
* @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 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->discussionId = $discussionId;
|
||||||
$this->actor = $actor;
|
$this->actor = $actor;
|
||||||
$this->readNumber = $readNumber;
|
$this->lastReadPostNumber = $lastReadPostNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ class ReadDiscussionHandler
|
|||||||
$discussion = $this->discussions->findOrFail($command->discussionId, $actor);
|
$discussion = $this->discussions->findOrFail($command->discussionId, $actor);
|
||||||
|
|
||||||
$state = $discussion->stateFor($actor);
|
$state = $discussion->stateFor($actor);
|
||||||
$state->read($command->readNumber);
|
$state->read($command->lastReadPostNumber);
|
||||||
|
|
||||||
$this->events->dispatch(
|
$this->events->dispatch(
|
||||||
new UserDataSaving($state)
|
new UserDataSaving($state)
|
||||||
|
@ -30,10 +30,18 @@ class StartDiscussion
|
|||||||
public $data;
|
public $data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param User $actor The user authoring the discussion.
|
* The current ip address of the actor.
|
||||||
* @param array $data The discussion attributes.
|
*
|
||||||
|
* @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->actor = $actor;
|
||||||
$this->data = $data;
|
$this->data = $data;
|
||||||
|
@ -94,7 +94,7 @@ class StartDiscussionHandler
|
|||||||
// attributes as posting the reply will have changed some of them (e.g.
|
// attributes as posting the reply will have changed some of them (e.g.
|
||||||
// last_time.)
|
// last_time.)
|
||||||
$discussion->setRawAttributes($post->discussion->getAttributes(), true);
|
$discussion->setRawAttributes($post->discussion->getAttributes(), true);
|
||||||
$discussion->setStartPost($post);
|
$discussion->setFirstPost($post);
|
||||||
$discussion->setLastPost($post);
|
$discussion->setLastPost($post);
|
||||||
|
|
||||||
$this->dispatchEventsFor($discussion, $actor);
|
$this->dispatchEventsFor($discussion, $actor);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Flarum\Discussion;
|
namespace Flarum\Discussion;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Flarum\Database\AbstractModel;
|
use Flarum\Database\AbstractModel;
|
||||||
use Flarum\Database\ScopeVisibilityTrait;
|
use Flarum\Database\ScopeVisibilityTrait;
|
||||||
use Flarum\Discussion\Event\Deleted;
|
use Flarum\Discussion\Event\Deleted;
|
||||||
@ -20,7 +21,6 @@ use Flarum\Discussion\Event\Restored;
|
|||||||
use Flarum\Discussion\Event\Started;
|
use Flarum\Discussion\Event\Started;
|
||||||
use Flarum\Event\GetModelIsPrivate;
|
use Flarum\Event\GetModelIsPrivate;
|
||||||
use Flarum\Foundation\EventGeneratorTrait;
|
use Flarum\Foundation\EventGeneratorTrait;
|
||||||
use Flarum\Post\Event\Deleted as PostDeleted;
|
|
||||||
use Flarum\Post\MergeableInterface;
|
use Flarum\Post\MergeableInterface;
|
||||||
use Flarum\Post\Post;
|
use Flarum\Post\Post;
|
||||||
use Flarum\User\User;
|
use Flarum\User\User;
|
||||||
@ -30,26 +30,26 @@ use Flarum\Util\Str;
|
|||||||
* @property int $id
|
* @property int $id
|
||||||
* @property string $title
|
* @property string $title
|
||||||
* @property string $slug
|
* @property string $slug
|
||||||
* @property int $comments_count
|
* @property int $comment_count
|
||||||
* @property int $participants_count
|
* @property int $participant_count
|
||||||
* @property int $number_index
|
* @property int $post_number_index
|
||||||
* @property \Carbon\Carbon $start_time
|
* @property \Carbon\Carbon $created_at
|
||||||
* @property int|null $start_user_id
|
* @property int|null $user_id
|
||||||
* @property int|null $start_post_id
|
* @property int|null $first_post_id
|
||||||
* @property \Carbon\Carbon|null $last_time
|
* @property \Carbon\Carbon|null $last_posted_at
|
||||||
* @property int|null $last_user_id
|
* @property int|null $last_posted_user_id
|
||||||
* @property int|null $last_post_id
|
* @property int|null $last_post_id
|
||||||
* @property int|null $last_post_number
|
* @property int|null $last_post_number
|
||||||
* @property \Carbon\Carbon|null $hide_time
|
* @property \Carbon\Carbon|null $hidden_at
|
||||||
* @property int|null $hide_user_id
|
* @property int|null $hidden_user_id
|
||||||
* @property UserState|null $state
|
* @property UserState|null $state
|
||||||
* @property \Illuminate\Database\Eloquent\Collection $posts
|
* @property \Illuminate\Database\Eloquent\Collection $posts
|
||||||
* @property \Illuminate\Database\Eloquent\Collection $comments
|
* @property \Illuminate\Database\Eloquent\Collection $comments
|
||||||
* @property \Illuminate\Database\Eloquent\Collection $participants
|
* @property \Illuminate\Database\Eloquent\Collection $participants
|
||||||
* @property Post|null $startPost
|
* @property Post|null $firstPost
|
||||||
* @property User|null $startUser
|
* @property User|null $user
|
||||||
* @property Post|null $lastPost
|
* @property Post|null $lastPost
|
||||||
* @property User|null $lastUser
|
* @property User|null $lastPostedUser
|
||||||
* @property \Illuminate\Database\Eloquent\Collection $readers
|
* @property \Illuminate\Database\Eloquent\Collection $readers
|
||||||
* @property bool $is_private
|
* @property bool $is_private
|
||||||
*/
|
*/
|
||||||
@ -58,11 +58,6 @@ class Discussion extends AbstractModel
|
|||||||
use EventGeneratorTrait;
|
use EventGeneratorTrait;
|
||||||
use ScopeVisibilityTrait;
|
use ScopeVisibilityTrait;
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
protected $table = 'discussions';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of posts that have been modified during this request.
|
* An array of posts that have been modified during this request.
|
||||||
*
|
*
|
||||||
@ -71,12 +66,14 @@ class Discussion extends AbstractModel
|
|||||||
protected $modifiedPosts = [];
|
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
|
* @var array
|
||||||
*/
|
*/
|
||||||
@ -102,21 +99,6 @@ class Discussion extends AbstractModel
|
|||||||
|
|
||||||
static::deleted(function (Discussion $discussion) {
|
static::deleted(function (Discussion $discussion) {
|
||||||
$discussion->raise(new Deleted($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) {
|
static::saving(function (Discussion $discussion) {
|
||||||
@ -138,10 +120,10 @@ class Discussion extends AbstractModel
|
|||||||
$discussion = new static;
|
$discussion = new static;
|
||||||
|
|
||||||
$discussion->title = $title;
|
$discussion->title = $title;
|
||||||
$discussion->start_time = time();
|
$discussion->created_at = Carbon::now();
|
||||||
$discussion->start_user_id = $user->id;
|
$discussion->user_id = $user->id;
|
||||||
|
|
||||||
$discussion->setRelation('startUser', $user);
|
$discussion->setRelation('user', $user);
|
||||||
|
|
||||||
$discussion->raise(new Started($discussion));
|
$discussion->raise(new Started($discussion));
|
||||||
|
|
||||||
@ -174,9 +156,9 @@ class Discussion extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public function hide(User $actor = null)
|
public function hide(User $actor = null)
|
||||||
{
|
{
|
||||||
if (! $this->hide_time) {
|
if (! $this->hidden_at) {
|
||||||
$this->hide_time = time();
|
$this->hidden_at = Carbon::now();
|
||||||
$this->hide_user_id = $actor ? $actor->id : null;
|
$this->hidden_user_id = $actor ? $actor->id : null;
|
||||||
|
|
||||||
$this->raise(new Hidden($this));
|
$this->raise(new Hidden($this));
|
||||||
}
|
}
|
||||||
@ -191,9 +173,9 @@ class Discussion extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public function restore()
|
public function restore()
|
||||||
{
|
{
|
||||||
if ($this->hide_time !== null) {
|
if ($this->hidden_at !== null) {
|
||||||
$this->hide_time = null;
|
$this->hidden_at = null;
|
||||||
$this->hide_user_id = null;
|
$this->hidden_user_id = null;
|
||||||
|
|
||||||
$this->raise(new Restored($this));
|
$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
|
* @param Post $post
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setStartPost(Post $post)
|
public function setFirstPost(Post $post)
|
||||||
{
|
{
|
||||||
$this->start_time = $post->time;
|
$this->created_at = $post->created_at;
|
||||||
$this->start_user_id = $post->user_id;
|
$this->user_id = $post->user_id;
|
||||||
$this->start_post_id = $post->id;
|
$this->first_post_id = $post->id;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -224,8 +206,8 @@ class Discussion extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public function setLastPost(Post $post)
|
public function setLastPost(Post $post)
|
||||||
{
|
{
|
||||||
$this->last_time = $post->time;
|
$this->last_posted_at = $post->created_at;
|
||||||
$this->last_user_id = $post->user_id;
|
$this->last_posted_user_id = $post->user_id;
|
||||||
$this->last_post_id = $post->id;
|
$this->last_post_id = $post->id;
|
||||||
$this->last_post_number = $post->number;
|
$this->last_post_number = $post->number;
|
||||||
|
|
||||||
@ -240,7 +222,7 @@ class Discussion extends AbstractModel
|
|||||||
public function refreshLastPost()
|
public function refreshLastPost()
|
||||||
{
|
{
|
||||||
/** @var Post $lastPost */
|
/** @var Post $lastPost */
|
||||||
if ($lastPost = $this->comments()->latest('time')->first()) {
|
if ($lastPost = $this->comments()->latest()->first()) {
|
||||||
$this->setLastPost($lastPost);
|
$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
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function refreshCommentsCount()
|
public function refreshCommentCount()
|
||||||
{
|
{
|
||||||
$this->comments_count = $this->comments()->count();
|
$this->comment_count = $this->comments()->count();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh the discussion's participants count.
|
* Refresh the discussion's participant count.
|
||||||
*
|
*
|
||||||
* @return $this
|
* @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;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -286,7 +268,7 @@ class Discussion extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public function mergePost(MergeableInterface $post)
|
public function mergePost(MergeableInterface $post)
|
||||||
{
|
{
|
||||||
$lastPost = $this->posts()->latest('time')->first();
|
$lastPost = $this->posts()->latest()->first();
|
||||||
|
|
||||||
$post = $post->saveAfter($lastPost);
|
$post = $post->saveAfter($lastPost);
|
||||||
|
|
||||||
@ -322,7 +304,7 @@ class Discussion extends AbstractModel
|
|||||||
{
|
{
|
||||||
return $this->posts()
|
return $this->posts()
|
||||||
->where('is_private', false)
|
->where('is_private', false)
|
||||||
->whereNull('hide_time')
|
->whereNull('hidden_at')
|
||||||
->where('type', 'comment');
|
->where('type', 'comment');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,9 +329,9 @@ class Discussion extends AbstractModel
|
|||||||
*
|
*
|
||||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
* @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
|
* @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
|
* @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()
|
public function readers()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(User::class, 'users_discussions');
|
return $this->belongsToMany(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,9 +39,9 @@ class DiscussionMetadataUpdater
|
|||||||
$discussion = $event->post->discussion;
|
$discussion = $event->post->discussion;
|
||||||
|
|
||||||
if ($discussion && $discussion->exists) {
|
if ($discussion && $discussion->exists) {
|
||||||
$discussion->refreshCommentsCount();
|
$discussion->refreshCommentCount();
|
||||||
$discussion->refreshLastPost();
|
$discussion->refreshLastPost();
|
||||||
$discussion->refreshParticipantsCount();
|
$discussion->refreshParticipantCount();
|
||||||
$discussion->save();
|
$discussion->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,8 +76,8 @@ class DiscussionMetadataUpdater
|
|||||||
$discussion = $event->post->discussion;
|
$discussion = $event->post->discussion;
|
||||||
|
|
||||||
if ($discussion && $discussion->exists) {
|
if ($discussion && $discussion->exists) {
|
||||||
$discussion->refreshCommentsCount();
|
$discussion->refreshCommentCount();
|
||||||
$discussion->refreshParticipantsCount();
|
$discussion->refreshParticipantCount();
|
||||||
$discussion->refreshLastPost();
|
$discussion->refreshLastPost();
|
||||||
$discussion->save();
|
$discussion->save();
|
||||||
}
|
}
|
||||||
@ -91,8 +91,8 @@ class DiscussionMetadataUpdater
|
|||||||
$discussion = $post->discussion;
|
$discussion = $post->discussion;
|
||||||
|
|
||||||
if ($discussion && $discussion->exists) {
|
if ($discussion && $discussion->exists) {
|
||||||
$discussion->refreshCommentsCount();
|
$discussion->refreshCommentCount();
|
||||||
$discussion->refreshParticipantsCount();
|
$discussion->refreshParticipantCount();
|
||||||
|
|
||||||
if ($discussion->last_post_id == $post->id) {
|
if ($discussion->last_post_id == $post->id) {
|
||||||
$discussion->refreshLastPost();
|
$discussion->refreshLastPost();
|
||||||
|
@ -91,8 +91,8 @@ class DiscussionPolicy extends AbstractPolicy
|
|||||||
// user, or the current user has permission to view hidden discussions.
|
// user, or the current user has permission to view hidden discussions.
|
||||||
if (! $actor->hasPermission('discussion.hide')) {
|
if (! $actor->hasPermission('discussion.hide')) {
|
||||||
$query->where(function ($query) use ($actor) {
|
$query->where(function ($query) use ($actor) {
|
||||||
$query->whereNull('discussions.hide_time')
|
$query->whereNull('discussions.hidden_at')
|
||||||
->orWhere('start_user_id', $actor->id)
|
->orWhere('discussions.user_id', $actor->id)
|
||||||
->orWhere(function ($query) use ($actor) {
|
->orWhere(function ($query) use ($actor) {
|
||||||
$this->events->dispatch(
|
$this->events->dispatch(
|
||||||
new ScopeModelVisibility($query, $actor, 'hide')
|
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.
|
// current user, or the user is allowed to edit the discussion's posts.
|
||||||
if (! $actor->hasPermission('discussion.editPosts')) {
|
if (! $actor->hasPermission('discussion.editPosts')) {
|
||||||
$query->where(function ($query) use ($actor) {
|
$query->where(function ($query) use ($actor) {
|
||||||
$query->where('comments_count', '>', 0)
|
$query->where('discussions.comment_count', '>', 0)
|
||||||
->orWhere('start_user_id', $actor->id)
|
->orWhere('discussions.user_id', $actor->id)
|
||||||
->orWhere(function ($query) use ($actor) {
|
->orWhere(function ($query) use ($actor) {
|
||||||
$this->events->dispatch(
|
$this->events->dispatch(
|
||||||
new ScopeModelVisibility($query, $actor, 'editPosts')
|
new ScopeModelVisibility($query, $actor, 'editPosts')
|
||||||
@ -123,12 +123,12 @@ class DiscussionPolicy extends AbstractPolicy
|
|||||||
*/
|
*/
|
||||||
public function rename(User $actor, Discussion $discussion)
|
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');
|
$allowRenaming = $this->settings->get('allow_renaming');
|
||||||
|
|
||||||
if ($allowRenaming === '-1'
|
if ($allowRenaming === '-1'
|
||||||
|| ($allowRenaming === 'reply' && $discussion->participants_count <= 1)
|
|| ($allowRenaming === 'reply' && $discussion->participant_count <= 1)
|
||||||
|| ($discussion->start_time->diffInMinutes() < $allowRenaming)) {
|
|| ($discussion->created_at->diffInMinutes() < $allowRenaming)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ class DiscussionPolicy extends AbstractPolicy
|
|||||||
*/
|
*/
|
||||||
public function hide(User $actor, Discussion $discussion)
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,11 +54,11 @@ class DiscussionRenamedLogger
|
|||||||
|
|
||||||
$post = $event->discussion->mergePost($post);
|
$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);
|
$blueprint = new DiscussionRenamedBlueprint($post);
|
||||||
|
|
||||||
if ($post->exists) {
|
if ($post->exists) {
|
||||||
$this->notifications->sync($blueprint, [$event->discussion->startUser]);
|
$this->notifications->sync($blueprint, [$event->discussion->user]);
|
||||||
} else {
|
} else {
|
||||||
$this->notifications->delete($blueprint);
|
$this->notifications->delete($blueprint);
|
||||||
}
|
}
|
||||||
|
@ -49,9 +49,9 @@ class DiscussionRepository
|
|||||||
*/
|
*/
|
||||||
public function getReadIds(User $user)
|
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)
|
->where('user_id', $user->id)
|
||||||
->whereRaw('read_number >= last_post_number')
|
->whereColumn('last_read_post_number', '>=', 'last_post_number')
|
||||||
->pluck('id')
|
->pluck('id')
|
||||||
->all();
|
->all();
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class DiscussionSearch extends AbstractSearch
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
protected $defaultSort = ['lastTime' => 'desc'];
|
protected $defaultSort = ['lastPostedAt' => 'desc'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
|
@ -54,6 +54,6 @@ class AuthorGambit extends AbstractRegexGambit
|
|||||||
$ids[] = $this->users->getIdForUsername($username);
|
$ids[] = $this->users->getIdForUsername($username);
|
||||||
}
|
}
|
||||||
|
|
||||||
$search->getQuery()->whereIn('start_user_id', $ids, 'and', $negate);
|
$search->getQuery()->whereIn('user_id', $ids, 'and', $negate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,9 @@ class CreatedGambit extends AbstractRegexGambit
|
|||||||
// provided with a YYYY-MM-DD..YYYY-MM-DD range, then find discussions
|
// provided with a YYYY-MM-DD..YYYY-MM-DD range, then find discussions
|
||||||
// that were started during that period.
|
// that were started during that period.
|
||||||
if (empty($matches[3])) {
|
if (empty($matches[3])) {
|
||||||
$search->getQuery()->whereDate('start_time', $negate ? '!=' : '=', $matches[1]);
|
$search->getQuery()->whereDate('created_at', $negate ? '!=' : '=', $matches[1]);
|
||||||
} else {
|
} else {
|
||||||
$search->getQuery()->whereBetween('start_time', [$matches[1], $matches[3]], 'and', $negate);
|
$search->getQuery()->whereBetween('created_at', [$matches[1], $matches[3]], 'and', $negate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,22 +33,29 @@ class FulltextGambit implements GambitInterface
|
|||||||
// See https://bugs.mysql.com/bug.php?id=74042
|
// See https://bugs.mysql.com/bug.php?id=74042
|
||||||
$bit = str_replace('@', '*', $bit);
|
$bit = str_replace('@', '*', $bit);
|
||||||
|
|
||||||
$search->getQuery()
|
$query = $search->getQuery();
|
||||||
->selectRaw('SUBSTRING_INDEX(GROUP_CONCAT(posts.id ORDER BY MATCH(posts.content) AGAINST (?) DESC), \',\', 1) as most_relevant_post_id', [$bit])
|
$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')
|
->leftJoin('posts', 'posts.discussion_id', '=', 'discussions.id')
|
||||||
->where('posts.type', 'comment')
|
->where('posts.type', 'comment')
|
||||||
->where(function ($query) use ($search) {
|
->where(function ($query) use ($search) {
|
||||||
event(new ScopeModelVisibility(Post::query()->setQuery($query), $search->getActor(), 'view'));
|
event(new ScopeModelVisibility(Post::query()->setQuery($query), $search->getActor(), 'view'));
|
||||||
})
|
})
|
||||||
->where(function ($query) use ($bit) {
|
->where(function ($query) use ($bit) {
|
||||||
$query->whereRaw('MATCH(discussions.title) AGAINST (? IN BOOLEAN MODE)', [$bit])
|
$grammar = $query->getGrammar();
|
||||||
->orWhereRaw('MATCH(posts.content) AGAINST (? IN BOOLEAN MODE)', [$bit]);
|
|
||||||
|
$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');
|
->groupBy('posts.discussion_id');
|
||||||
|
|
||||||
$search->setDefaultSort(function ($query) use ($bit) {
|
$search->setDefaultSort(function ($query) use ($bit) {
|
||||||
$query->orderByRaw('MATCH(discussions.title) AGAINST (?) desc', [$bit]);
|
$grammar = $query->getGrammar();
|
||||||
$query->orderByRaw('MATCH(posts.content) AGAINST (?) desc', [$bit]);
|
|
||||||
|
$query->orderByRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (?) desc', [$bit]);
|
||||||
|
$query->orderByRaw('MATCH('.$grammar->wrap('posts.content').') AGAINST (?) desc', [$bit]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,9 @@ class HiddenGambit extends AbstractRegexGambit
|
|||||||
|
|
||||||
$search->getQuery()->where(function ($query) use ($negate) {
|
$search->getQuery()->where(function ($query) use ($negate) {
|
||||||
if ($negate) {
|
if ($negate) {
|
||||||
$query->whereNull('hide_time')->where('comments_count', '>', 0);
|
$query->whereNull('hidden_at')->where('comment_count', '>', 0);
|
||||||
} else {
|
} else {
|
||||||
$query->whereNotNull('hide_time')->orWhere('comments_count', 0);
|
$query->whereNotNull('hidden_at')->orWhere('comment_count', 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -53,9 +53,9 @@ class UnreadGambit extends AbstractRegexGambit
|
|||||||
|
|
||||||
$search->getQuery()->where(function ($query) use ($readIds, $negate, $actor) {
|
$search->getQuery()->where(function ($query) use ($readIds, $negate, $actor) {
|
||||||
if (! $negate) {
|
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 {
|
} 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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Flarum\Discussion;
|
namespace Flarum\Discussion;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Flarum\Database\AbstractModel;
|
use Flarum\Database\AbstractModel;
|
||||||
use Flarum\Discussion\Event\UserRead;
|
use Flarum\Discussion\Event\UserRead;
|
||||||
use Flarum\Foundation\EventGeneratorTrait;
|
use Flarum\Foundation\EventGeneratorTrait;
|
||||||
@ -26,8 +27,8 @@ use Illuminate\Database\Eloquent\Builder;
|
|||||||
*
|
*
|
||||||
* @property int $user_id
|
* @property int $user_id
|
||||||
* @property int $discussion_id
|
* @property int $discussion_id
|
||||||
* @property \Carbon\Carbon|null $read_time
|
* @property \Carbon\Carbon|null $last_read_at
|
||||||
* @property int|null $read_number
|
* @property int|null $last_read_post_number
|
||||||
* @property Discussion $discussion
|
* @property Discussion $discussion
|
||||||
* @property \Flarum\User\User $user
|
* @property \Flarum\User\User $user
|
||||||
*/
|
*/
|
||||||
@ -38,12 +39,14 @@ class UserState extends AbstractModel
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@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
|
* 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)
|
public function read($number)
|
||||||
{
|
{
|
||||||
if ($number > $this->read_number) {
|
if ($number > $this->last_read_post_number) {
|
||||||
$this->read_number = $number;
|
$this->last_read_post_number = $number;
|
||||||
$this->read_time = time();
|
$this->last_read_at = Carbon::now();
|
||||||
|
|
||||||
$this->raise(new UserRead($this));
|
$this->raise(new UserRead($this));
|
||||||
}
|
}
|
||||||
@ -71,7 +74,7 @@ class UserState extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public function discussion()
|
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()
|
public function user()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(User::class, 'user_id');
|
return $this->belongsTo(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,10 +76,10 @@ class Index implements ContentInterface
|
|||||||
private function getSortMap()
|
private function getSortMap()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'latest' => '-lastTime',
|
'latest' => '-lastPostedAt',
|
||||||
'top' => '-commentsCount',
|
'top' => '-commentCount',
|
||||||
'newest' => '-startTime',
|
'newest' => '-createdAt',
|
||||||
'oldest' => 'startTime'
|
'oldest' => 'createdAt'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ class ResetPasswordController extends AbstractHtmlController
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $this->view->make('flarum.forum::reset-password')
|
return $this->view->make('flarum.forum::reset-password')
|
||||||
->with('passwordToken', $token->id)
|
->with('passwordToken', $token->token)
|
||||||
->with('csrfToken', $request->getAttribute('session')->token());
|
->with('csrfToken', $request->getAttribute('session')->token());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,11 +33,6 @@ class Group extends AbstractModel
|
|||||||
use EventGeneratorTrait;
|
use EventGeneratorTrait;
|
||||||
use ScopeVisibilityTrait;
|
use ScopeVisibilityTrait;
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
protected $table = 'groups';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ID of the administrator group.
|
* The ID of the administrator group.
|
||||||
*/
|
*/
|
||||||
@ -69,8 +64,6 @@ class Group extends AbstractModel
|
|||||||
|
|
||||||
static::deleted(function (Group $group) {
|
static::deleted(function (Group $group) {
|
||||||
$group->raise(new Deleted($group));
|
$group->raise(new Deleted($group));
|
||||||
|
|
||||||
$group->permissions()->delete();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +114,7 @@ class Group extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public function users()
|
public function users()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(User::class, 'users_groups');
|
return $this->belongsToMany(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,14 +15,15 @@ use Flarum\Database\AbstractModel;
|
|||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo document database columns with @property
|
* @property int $group_id
|
||||||
|
* @property string $permission
|
||||||
*/
|
*/
|
||||||
class Permission extends AbstractModel
|
class Permission extends AbstractModel
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
protected $table = 'permissions';
|
protected $table = 'group_permission';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the relationship with the group that this permission is for.
|
* Define the relationship with the group that this permission is for.
|
||||||
@ -31,7 +32,7 @@ class Permission extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public function group()
|
public function group()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Group::class, 'group_id');
|
return $this->belongsTo(Group::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,22 +11,19 @@
|
|||||||
|
|
||||||
namespace Flarum\Http;
|
namespace Flarum\Http;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Flarum\Database\AbstractModel;
|
use Flarum\Database\AbstractModel;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property string $id
|
* @property string $token
|
||||||
* @property int $user_id
|
* @property int $user_id
|
||||||
* @property int $last_activity
|
* @property int $last_activity_at
|
||||||
* @property int $lifetime
|
* @property int $lifetime_seconds
|
||||||
* @property \Flarum\User\User|null $user
|
* @property \Flarum\User\User|null $user
|
||||||
*/
|
*/
|
||||||
class AccessToken extends AbstractModel
|
class AccessToken extends AbstractModel
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
protected $table = 'access_tokens';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use a custom primary key for this model.
|
* Use a custom primary key for this model.
|
||||||
*
|
*
|
||||||
@ -34,6 +31,10 @@ class AccessToken extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public $incrementing = false;
|
public $incrementing = false;
|
||||||
|
|
||||||
|
protected $primaryKey = 'token';
|
||||||
|
|
||||||
|
protected $dates = ['last_activity_at'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate an access token for the specified user.
|
* Generate an access token for the specified user.
|
||||||
*
|
*
|
||||||
@ -45,17 +46,17 @@ class AccessToken extends AbstractModel
|
|||||||
{
|
{
|
||||||
$token = new static;
|
$token = new static;
|
||||||
|
|
||||||
$token->id = str_random(40);
|
$token->token = str_random(40);
|
||||||
$token->user_id = $userId;
|
$token->user_id = $userId;
|
||||||
$token->last_activity = time();
|
$token->last_activity_at = Carbon::now();
|
||||||
$token->lifetime = $lifetime;
|
$token->lifetime_seconds = $lifetime;
|
||||||
|
|
||||||
return $token;
|
return $token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function touch()
|
public function touch()
|
||||||
{
|
{
|
||||||
$this->last_activity = time();
|
$this->last_activity_at = Carbon::now();
|
||||||
|
|
||||||
return $this->save();
|
return $this->save();
|
||||||
}
|
}
|
||||||
@ -67,6 +68,6 @@ class AccessToken extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public function user()
|
public function user()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('Flarum\User\User');
|
return $this->belongsTo(User::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Flarum\Http\Middleware;
|
namespace Flarum\Http\Middleware;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Flarum\Http\AccessToken;
|
use Flarum\Http\AccessToken;
|
||||||
use Flarum\User\AuthToken;
|
use Flarum\User\AuthToken;
|
||||||
use Flarum\User\EmailToken;
|
use Flarum\User\EmailToken;
|
||||||
@ -55,9 +56,11 @@ class CollectGarbage implements Middleware
|
|||||||
return;
|
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();
|
EmailToken::where('created_at', '<=', $earliestToKeep)->delete();
|
||||||
PasswordToken::where('created_at', '<=', $earliestToKeep)->delete();
|
PasswordToken::where('created_at', '<=', $earliestToKeep)->delete();
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user