mirror of
https://github.com/flarum/core.git
synced 2025-08-03 06:57:54 +02:00
feat: Display suspension to user (#41)
This commit is contained in:
@@ -60,5 +60,8 @@ return [
|
|||||||
->addFilter(SuspendedFilterGambit::class),
|
->addFilter(SuspendedFilterGambit::class),
|
||||||
|
|
||||||
(new Extend\SimpleFlarumSearch(UserSearcher::class))
|
(new Extend\SimpleFlarumSearch(UserSearcher::class))
|
||||||
->addGambit(SuspendedFilterGambit::class)
|
->addGambit(SuspendedFilterGambit::class),
|
||||||
|
|
||||||
|
(new Extend\View())
|
||||||
|
->namespace('flarum-suspend', __DIR__.'/views'),
|
||||||
];
|
];
|
||||||
|
19
extensions/suspend/js/src/forum/checkForSuspension.ts
Normal file
19
extensions/suspend/js/src/forum/checkForSuspension.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import SuspensionInfoModal from './components/SuspensionInfoModal';
|
||||||
|
import { localStorageKey } from './helpers/suspensionHelper';
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return setTimeout(() => {
|
||||||
|
if (app.session.user) {
|
||||||
|
const message = app.session.user.suspendMessage();
|
||||||
|
const until = app.session.user.suspendedUntil();
|
||||||
|
const alreadyDisplayed = localStorage.getItem(localStorageKey()) === until.getTime().toString();
|
||||||
|
|
||||||
|
if (message && !alreadyDisplayed) {
|
||||||
|
app.modal.show(SuspensionInfoModal, { message, until });
|
||||||
|
} else if (!until && localStorage.getItem(localStorageKey())) {
|
||||||
|
localStorage.removeItem(localStorageKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
}
|
13
extensions/suspend/js/src/forum/compat.js
Normal file
13
extensions/suspend/js/src/forum/compat.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import SuspendUserModal from './components/SuspendUserModal';
|
||||||
|
import SuspensionInfoModal from './components/SuspensionInfoModal';
|
||||||
|
import UserSuspendedNotification from './components/UserSuspendedNotification';
|
||||||
|
import UserUnsuspendedNotification from './components/UserUnsuspendedNotification';
|
||||||
|
import checkForSuspension from './checkForSuspension';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'suspend/components/suspendUserModal': SuspendUserModal,
|
||||||
|
'suspend/components/suspensionInfoModal': SuspensionInfoModal,
|
||||||
|
'suspend/components/UserSuspendedNotification': UserSuspendedNotification,
|
||||||
|
'suspend/components/UserUnsuspendedNotification': UserUnsuspendedNotification,
|
||||||
|
'suspend/checkForSuspension': checkForSuspension,
|
||||||
|
};
|
@@ -1,14 +1,19 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
import Modal from 'flarum/components/Modal';
|
import Modal from 'flarum/components/Modal';
|
||||||
import Button from 'flarum/components/Button';
|
import Button from 'flarum/components/Button';
|
||||||
|
|
||||||
import Stream from 'flarum/utils/Stream';
|
import Stream from 'flarum/utils/Stream';
|
||||||
import withAttr from 'flarum/utils/withAttr';
|
import withAttr from 'flarum/utils/withAttr';
|
||||||
|
import ItemList from 'flarum/common/utils/ItemList';
|
||||||
|
import { getPermanentSuspensionDate } from '../helpers/suspensionHelper';
|
||||||
|
|
||||||
export default class SuspendUserModal extends Modal {
|
export default class SuspendUserModal extends Modal {
|
||||||
oninit(vnode) {
|
oninit(vnode) {
|
||||||
super.oninit(vnode);
|
super.oninit(vnode);
|
||||||
|
|
||||||
let until = this.attrs.user.suspendedUntil();
|
let until = this.attrs.user.suspendedUntil();
|
||||||
|
const reason = this.attrs.user.suspendReason();
|
||||||
|
const message = this.attrs.user.suspendMessage();
|
||||||
let status = null;
|
let status = null;
|
||||||
|
|
||||||
if (new Date() > until) until = null;
|
if (new Date() > until) until = null;
|
||||||
@@ -19,11 +24,13 @@ export default class SuspendUserModal extends Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.status = Stream(status);
|
this.status = Stream(status);
|
||||||
|
this.reason = Stream(reason);
|
||||||
|
this.message = Stream(message);
|
||||||
this.daysRemaining = Stream(status === 'limited' && -dayjs().diff(until, 'days') + 1);
|
this.daysRemaining = Stream(status === 'limited' && -dayjs().diff(until, 'days') + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
className() {
|
className() {
|
||||||
return 'SuspendUserModal Modal--small';
|
return 'SuspendUserModal Modal--medium';
|
||||||
}
|
}
|
||||||
|
|
||||||
title() {
|
title() {
|
||||||
@@ -36,53 +43,7 @@ export default class SuspendUserModal extends Modal {
|
|||||||
<div className="Form">
|
<div className="Form">
|
||||||
<div className="Form-group">
|
<div className="Form-group">
|
||||||
<label>{app.translator.trans('flarum-suspend.forum.suspend_user.status_heading')}</label>
|
<label>{app.translator.trans('flarum-suspend.forum.suspend_user.status_heading')}</label>
|
||||||
<div>
|
<div>{this.formItems().toArray()}</div>
|
||||||
<label className="checkbox">
|
|
||||||
<input type="radio" name="status" checked={!this.status()} value="" onclick={withAttr('value', this.status)} />
|
|
||||||
{app.translator.trans('flarum-suspend.forum.suspend_user.not_suspended_label')}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label className="checkbox">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="status"
|
|
||||||
checked={this.status() === 'indefinitely'}
|
|
||||||
value="indefinitely"
|
|
||||||
onclick={withAttr('value', this.status)}
|
|
||||||
/>
|
|
||||||
{app.translator.trans('flarum-suspend.forum.suspend_user.indefinitely_label')}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label className="checkbox SuspendUserModal-days">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="status"
|
|
||||||
checked={this.status() === 'limited'}
|
|
||||||
value="limited"
|
|
||||||
onclick={(e) => {
|
|
||||||
this.status(e.target.value);
|
|
||||||
m.redraw.sync();
|
|
||||||
this.$('.SuspendUserModal-days-input input').select();
|
|
||||||
e.redraw = false;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{app.translator.trans('flarum-suspend.forum.suspend_user.limited_time_label')}
|
|
||||||
{this.status() === 'limited' ? (
|
|
||||||
<div className="SuspendUserModal-days-input">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
value={this.daysRemaining()}
|
|
||||||
oninput={withAttr('value', this.daysRemaining)}
|
|
||||||
className="FormControl"
|
|
||||||
/>
|
|
||||||
{app.translator.trans('flarum-suspend.forum.suspend_user.limited_time_days_text')}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="Form-group">
|
<div className="Form-group">
|
||||||
@@ -95,6 +56,96 @@ export default class SuspendUserModal extends Modal {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
radioItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'not-suspended',
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="radio" name="status" checked={!this.status()} value="" onclick={withAttr('value', this.status)} />
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.not_suspended_label')}
|
||||||
|
</label>,
|
||||||
|
100
|
||||||
|
);
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'indefinitely',
|
||||||
|
<label className="checkbox">
|
||||||
|
<input type="radio" name="status" checked={this.status() === 'indefinitely'} value="indefinitely" onclick={withAttr('value', this.status)} />
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.indefinitely_label')}
|
||||||
|
</label>,
|
||||||
|
90
|
||||||
|
);
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'time-suspension',
|
||||||
|
<label className="checkbox SuspendUserModal-days">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="status"
|
||||||
|
checked={this.status() === 'limited'}
|
||||||
|
value="limited"
|
||||||
|
onclick={(e) => {
|
||||||
|
this.status(e.target.value);
|
||||||
|
m.redraw.sync();
|
||||||
|
this.$('.SuspendUserModal-days-input input').select();
|
||||||
|
e.redraw = false;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.limited_time_label')}
|
||||||
|
{this.status() === 'limited' && (
|
||||||
|
<div className="SuspendUserModal-days-input">
|
||||||
|
<input type="number" min="0" value={this.daysRemaining()} oninput={withAttr('value', this.daysRemaining)} className="FormControl" />
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.limited_time_days_text')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</label>,
|
||||||
|
80
|
||||||
|
);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
formItems() {
|
||||||
|
const items = new ItemList();
|
||||||
|
|
||||||
|
items.add('radioItems', <div className="Form-group">{this.radioItems().toArray()}</div>, 100);
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'reason',
|
||||||
|
<div className="Form-group">
|
||||||
|
<label>
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.reason')}
|
||||||
|
<textarea
|
||||||
|
className="FormControl"
|
||||||
|
bidi={this.reason}
|
||||||
|
placeholder={app.translator.trans('flarum-suspend.forum.suspend_user.placeholder_optional')}
|
||||||
|
rows="2"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>,
|
||||||
|
90
|
||||||
|
);
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
'message',
|
||||||
|
<div className="Form-group">
|
||||||
|
<label>
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspend_user.display_message')}
|
||||||
|
<textarea
|
||||||
|
className="FormControl"
|
||||||
|
bidi={this.message}
|
||||||
|
placeholder={app.translator.trans('flarum-suspend.forum.suspend_user.placeholder_optional')}
|
||||||
|
rows="2"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>,
|
||||||
|
80
|
||||||
|
);
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
onsubmit(e) {
|
onsubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -103,7 +154,7 @@ export default class SuspendUserModal extends Modal {
|
|||||||
let suspendedUntil = null;
|
let suspendedUntil = null;
|
||||||
switch (this.status()) {
|
switch (this.status()) {
|
||||||
case 'indefinitely':
|
case 'indefinitely':
|
||||||
suspendedUntil = new Date('2038-01-01');
|
suspendedUntil = getPermanentSuspensionDate();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'limited':
|
case 'limited':
|
||||||
@@ -114,6 +165,8 @@ export default class SuspendUserModal extends Modal {
|
|||||||
// no default
|
// no default
|
||||||
}
|
}
|
||||||
|
|
||||||
this.attrs.user.save({ suspendedUntil }).then(() => this.hide(), this.loaded.bind(this));
|
this.attrs.user
|
||||||
|
.save({ suspendedUntil, suspendReason: this.reason(), suspendMessage: this.message() })
|
||||||
|
.then(() => this.hide(), this.loaded.bind(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,48 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
|
import Modal from 'flarum/common/components/Modal';
|
||||||
|
import Button from 'flarum/common/components/Button';
|
||||||
|
import fullTime from 'flarum/common/helpers/fullTime';
|
||||||
|
import { isPermanentSuspensionDate, localStorageKey } from '../helpers/suspensionHelper';
|
||||||
|
|
||||||
|
export default class SuspensionInfoModal extends Modal {
|
||||||
|
oninit(vnode) {
|
||||||
|
super.oninit(vnode);
|
||||||
|
|
||||||
|
this.message = this.attrs.message;
|
||||||
|
this.until = this.attrs.until;
|
||||||
|
}
|
||||||
|
|
||||||
|
className() {
|
||||||
|
return 'SuspensionInfoModal Modal';
|
||||||
|
}
|
||||||
|
|
||||||
|
title() {
|
||||||
|
return app.translator.trans('flarum-suspend.forum.suspension_info.title');
|
||||||
|
}
|
||||||
|
|
||||||
|
content() {
|
||||||
|
const timespan = isPermanentSuspensionDate(new Date(this.until))
|
||||||
|
? app.translator.trans('flarum-suspend.forum.suspension_info.indefinite')
|
||||||
|
: app.translator.trans('flarum-suspend.forum.suspension_info.limited', { date: fullTime(this.until) });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="Modal-body">
|
||||||
|
<div className="Form Form--centered">
|
||||||
|
<p className="helpText">{this.message}</p>
|
||||||
|
<p className="helpText">{timespan}</p>
|
||||||
|
|
||||||
|
<div className="Form-group">
|
||||||
|
<Button className="Button Button--primary Button--block" onclick={this.hide.bind(this)}>
|
||||||
|
{app.translator.trans('flarum-suspend.forum.suspension_info.dismiss_button')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
localStorage.setItem(localStorageKey(), this.attrs.until.getTime());
|
||||||
|
this.attrs.state.close();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,6 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
import Notification from 'flarum/components/Notification';
|
import Notification from 'flarum/components/Notification';
|
||||||
|
import { isPermanentSuspensionDate } from '../helpers/suspensionHelper';
|
||||||
|
|
||||||
export default class UserSuspendedNotification extends Notification {
|
export default class UserSuspendedNotification extends Notification {
|
||||||
icon() {
|
icon() {
|
||||||
@@ -14,8 +16,10 @@ export default class UserSuspendedNotification extends Notification {
|
|||||||
const suspendedUntil = notification.content();
|
const suspendedUntil = notification.content();
|
||||||
const timeReadable = dayjs(suspendedUntil).from(notification.createdAt(), true);
|
const timeReadable = dayjs(suspendedUntil).from(notification.createdAt(), true);
|
||||||
|
|
||||||
return app.translator.trans('flarum-suspend.forum.notifications.user_suspended_text', {
|
return isPermanentSuspensionDate(suspendedUntil)
|
||||||
timeReadable,
|
? app.translator.trans('flarum-suspend.forum.notifications.user_suspended_indefinite_text')
|
||||||
});
|
: app.translator.trans('flarum-suspend.forum.notifications.user_suspended_text', {
|
||||||
|
timeReadable,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import app from 'flarum/forum/app';
|
||||||
import Notification from 'flarum/components/Notification';
|
import Notification from 'flarum/components/Notification';
|
||||||
|
|
||||||
export default class UserUnsuspendedNotification extends Notification {
|
export default class UserUnsuspendedNotification extends Notification {
|
||||||
|
16
extensions/suspend/js/src/forum/helpers/suspensionHelper.ts
Normal file
16
extensions/suspend/js/src/forum/helpers/suspensionHelper.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import dayjs from 'dayjs';
|
||||||
|
import utc from 'dayjs/plugin/utc';
|
||||||
|
|
||||||
|
dayjs.extend(utc);
|
||||||
|
|
||||||
|
export function getPermanentSuspensionDate(): Date {
|
||||||
|
return new Date('2038-01-01');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPermanentSuspensionDate(date: Date): boolean {
|
||||||
|
return dayjs.utc(date).isSame(dayjs.utc('2038-01-01'));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function localStorageKey(): string {
|
||||||
|
return 'flarum-suspend.acknowledge-suspension';
|
||||||
|
}
|
@@ -9,6 +9,7 @@ import User from 'flarum/models/User';
|
|||||||
import SuspendUserModal from './components/SuspendUserModal';
|
import SuspendUserModal from './components/SuspendUserModal';
|
||||||
import UserSuspendedNotification from './components/UserSuspendedNotification';
|
import UserSuspendedNotification from './components/UserSuspendedNotification';
|
||||||
import UserUnsuspendedNotification from './components/UserUnsuspendedNotification';
|
import UserUnsuspendedNotification from './components/UserUnsuspendedNotification';
|
||||||
|
import checkForSuspension from './checkForSuspension';
|
||||||
|
|
||||||
app.initializers.add('flarum-suspend', () => {
|
app.initializers.add('flarum-suspend', () => {
|
||||||
app.notificationComponents.userSuspended = UserSuspendedNotification;
|
app.notificationComponents.userSuspended = UserSuspendedNotification;
|
||||||
@@ -16,6 +17,8 @@ app.initializers.add('flarum-suspend', () => {
|
|||||||
|
|
||||||
User.prototype.canSuspend = Model.attribute('canSuspend');
|
User.prototype.canSuspend = Model.attribute('canSuspend');
|
||||||
User.prototype.suspendedUntil = Model.attribute('suspendedUntil', Model.transformDate);
|
User.prototype.suspendedUntil = Model.attribute('suspendedUntil', Model.transformDate);
|
||||||
|
User.prototype.suspendReason = Model.attribute('suspendReason');
|
||||||
|
User.prototype.suspendMessage = Model.attribute('suspendMessage');
|
||||||
|
|
||||||
extend(UserControls, 'moderationControls', (items, user) => {
|
extend(UserControls, 'moderationControls', (items, user) => {
|
||||||
if (user.canSuspend()) {
|
if (user.canSuspend()) {
|
||||||
@@ -46,4 +49,12 @@ app.initializers.add('flarum-suspend', () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
checkForSuspension();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Expose compat API
|
||||||
|
import suspendCompat from './compat';
|
||||||
|
import { compat } from '@flarum/core/forum';
|
||||||
|
|
||||||
|
Object.assign(compat, suspendCompat);
|
||||||
|
@@ -16,14 +16,25 @@ flarum-suspend:
|
|||||||
# These translations are used in the suspension notifications
|
# These translations are used in the suspension notifications
|
||||||
notifications:
|
notifications:
|
||||||
user_suspended_text: "You have been suspended for {timeReadable}"
|
user_suspended_text: "You have been suspended for {timeReadable}"
|
||||||
|
user_suspended_indefinite_text: You have been suspended indefinitely
|
||||||
user_unsuspended_text: You have been unsuspended
|
user_unsuspended_text: You have been unsuspended
|
||||||
|
|
||||||
|
# These translations are used for the suspension reason informational modal to the suspended user.
|
||||||
|
suspension_info:
|
||||||
|
dismiss_button: Dismiss
|
||||||
|
indefinite: This is an indefinite suspension
|
||||||
|
limited: "This suspension will be in force until {date}"
|
||||||
|
title: This account is suspended
|
||||||
|
|
||||||
# These translations are used in the Suspend User modal dialog (admin function).
|
# These translations are used in the Suspend User modal dialog (admin function).
|
||||||
suspend_user:
|
suspend_user:
|
||||||
|
display_message: Display message for user
|
||||||
indefinitely_label: Suspended indefinitely
|
indefinitely_label: Suspended indefinitely
|
||||||
limited_time_days_text: " days"
|
limited_time_days_text: " days"
|
||||||
limited_time_label: Suspended for a limited time...
|
limited_time_label: Suspended for a limited time...
|
||||||
not_suspended_label: Not suspended
|
not_suspended_label: Not suspended
|
||||||
|
placeholder_optional: Optional
|
||||||
|
reason: Reason for suspension
|
||||||
status_heading: Suspension Status
|
status_heading: Suspension Status
|
||||||
submit_button: => core.ref.save_changes
|
submit_button: => core.ref.save_changes
|
||||||
title: "Suspend {username}"
|
title: "Suspend {username}"
|
||||||
@@ -35,3 +46,25 @@ flarum-suspend:
|
|||||||
# These translations are found on the user profile page (admin function).
|
# These translations are found on the user profile page (admin function).
|
||||||
user_controls:
|
user_controls:
|
||||||
suspend_button: Suspend
|
suspend_button: Suspend
|
||||||
|
|
||||||
|
# Translations in this namespace are used by suspension email notifications
|
||||||
|
email:
|
||||||
|
suspended:
|
||||||
|
subject: Your account has been suspended
|
||||||
|
body: |
|
||||||
|
Hey {recipient_display_name},
|
||||||
|
|
||||||
|
You have been suspended for the following reason:
|
||||||
|
|
||||||
|
---
|
||||||
|
{suspension_message}
|
||||||
|
---
|
||||||
|
|
||||||
|
unsuspended:
|
||||||
|
subject: Your account has been unsuspended
|
||||||
|
body: |
|
||||||
|
Hey {recipient_display_name},
|
||||||
|
|
||||||
|
You have been unsuspended. You can head back to the forum by clicking on the following link:
|
||||||
|
|
||||||
|
{forum_url}
|
||||||
|
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Database\Migration;
|
||||||
|
|
||||||
|
return Migration::addColumns('users', [
|
||||||
|
'suspend_reason' => ['text', 'nullable' => true],
|
||||||
|
'suspend_message' => ['text', 'nullable' => true]
|
||||||
|
]);
|
@@ -20,6 +20,11 @@ class AddUserSuspendAttributes
|
|||||||
$canSuspend = $serializer->getActor()->can('suspend', $user);
|
$canSuspend = $serializer->getActor()->can('suspend', $user);
|
||||||
|
|
||||||
if ($canSuspend) {
|
if ($canSuspend) {
|
||||||
|
$attributes['suspendReason'] = $user->suspend_reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($serializer->getActor()->id === $user->id || $canSuspend) {
|
||||||
|
$attributes['suspendMessage'] = $user->suspend_message;
|
||||||
$attributes['suspendedUntil'] = $serializer->formatDate($user->suspended_until);
|
$attributes['suspendedUntil'] = $serializer->formatDate($user->suspended_until);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -53,11 +53,17 @@ class SaveSuspensionToDatabase
|
|||||||
|
|
||||||
$actor->assertCan('suspend', $user);
|
$actor->assertCan('suspend', $user);
|
||||||
|
|
||||||
$user->suspended_until = $attributes['suspendedUntil']
|
if ($attributes['suspendedUntil']) {
|
||||||
? new DateTime($attributes['suspendedUntil'])
|
$user->suspended_until = new DateTime($attributes['suspendedUntil']);
|
||||||
: null;
|
$user->suspend_reason = empty($attributes['suspendReason']) ? null : $attributes['suspendReason'];
|
||||||
|
$user->suspend_message = empty($attributes['suspendMessage']) ? null : $attributes['suspendMessage'];
|
||||||
|
} else {
|
||||||
|
$user->suspended_until = null;
|
||||||
|
$user->suspend_reason = null;
|
||||||
|
$user->suspend_message = null;
|
||||||
|
}
|
||||||
|
|
||||||
if ($user->isDirty('suspended_until')) {
|
if ($user->isDirty(['suspended_until', 'suspend_reason', 'suspend_message'])) {
|
||||||
$this->events->dispatch(
|
$this->events->dispatch(
|
||||||
$user->suspended_until === null ?
|
$user->suspended_until === null ?
|
||||||
new Unsuspended($user, $actor) :
|
new Unsuspended($user, $actor) :
|
||||||
|
@@ -10,9 +10,11 @@
|
|||||||
namespace Flarum\Suspend\Notification;
|
namespace Flarum\Suspend\Notification;
|
||||||
|
|
||||||
use Flarum\Notification\Blueprint\BlueprintInterface;
|
use Flarum\Notification\Blueprint\BlueprintInterface;
|
||||||
|
use Flarum\Notification\MailableInterface;
|
||||||
use Flarum\User\User;
|
use Flarum\User\User;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
class UserSuspendedBlueprint implements BlueprintInterface
|
class UserSuspendedBlueprint implements BlueprintInterface, MailableInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var User
|
* @var User
|
||||||
@@ -66,4 +68,20 @@ class UserSuspendedBlueprint implements BlueprintInterface
|
|||||||
{
|
{
|
||||||
return User::class;
|
return User::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getEmailView()
|
||||||
|
{
|
||||||
|
return ['text' => 'flarum-suspend::emails.suspended'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getEmailSubject(TranslatorInterface $translator)
|
||||||
|
{
|
||||||
|
return $translator->trans('flarum-suspend.email.suspended.subject');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,9 +10,12 @@
|
|||||||
namespace Flarum\Suspend\Notification;
|
namespace Flarum\Suspend\Notification;
|
||||||
|
|
||||||
use Flarum\Notification\Blueprint\BlueprintInterface;
|
use Flarum\Notification\Blueprint\BlueprintInterface;
|
||||||
|
use Flarum\Notification\MailableInterface;
|
||||||
use Flarum\User\User;
|
use Flarum\User\User;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
class UserUnsuspendedBlueprint implements BlueprintInterface
|
class UserUnsuspendedBlueprint implements BlueprintInterface, MailableInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var User
|
* @var User
|
||||||
@@ -48,7 +51,7 @@ class UserUnsuspendedBlueprint implements BlueprintInterface
|
|||||||
*/
|
*/
|
||||||
public function getData()
|
public function getData()
|
||||||
{
|
{
|
||||||
return null;
|
return Carbon::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,4 +69,20 @@ class UserUnsuspendedBlueprint implements BlueprintInterface
|
|||||||
{
|
{
|
||||||
return User::class;
|
return User::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getEmailView()
|
||||||
|
{
|
||||||
|
return ['text' => 'flarum-suspend::emails.unsuspended'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getEmailSubject(TranslatorInterface $translator)
|
||||||
|
{
|
||||||
|
return $translator->trans('flarum-suspend.email.unsuspended.subject');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
4
extensions/suspend/views/emails/suspended.blade.php
Normal file
4
extensions/suspend/views/emails/suspended.blade.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{!! $translator->trans('flarum-suspend.email.suspended.body', [
|
||||||
|
'{recipient_display_name}' => $user->display_name,
|
||||||
|
'{suspension_message}' => $blueprint->user->suspend_message,
|
||||||
|
]) !!}
|
4
extensions/suspend/views/emails/unsuspended.blade.php
Normal file
4
extensions/suspend/views/emails/unsuspended.blade.php
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{!! $translator->trans('flarum-suspend.email.unsuspended.body', [
|
||||||
|
'{recipient_display_name}' => $user->display_name,
|
||||||
|
'{forum_url}' => $url->to('forum')->base(),
|
||||||
|
]) !!}
|
Reference in New Issue
Block a user