WIP: Road to 1.0.0 #1

Draft
miguel456 wants to merge 123 commits from develop into master
10 changed files with 118 additions and 69 deletions
Showing only changes of commit cbcc1f025a - Show all commits

View File

@ -30,13 +30,13 @@ class Ban extends Model
'userID',
'reason',
'bannedUntil',
'userAgent',
'isPermanent',
'authorUserID',
];
public $dates = [
'bannedUntil',
'suspendedUntil',
];
public function user()

View File

@ -21,7 +21,7 @@
namespace App\Console;
use App\Jobs\CleanBans;
use App\Jobs\ProcessDueSuspensions;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@ -50,7 +50,7 @@ class Kernel extends ConsoleKernel
->daily();
// Production value: Every day
$schedule->job(new CleanBans)
$schedule->job(new ProcessDueSuspensions)
->daily();
// Production value: Every day
}

View File

@ -34,48 +34,26 @@ class BanController extends Controller
{
$this->authorize('create', [Ban::class, $user]);
// FIXME: Needs refactoring to a simpler format, e.g. parse the user's given date directly.
if (is_null($user->bans)) {
$duration = $request->duration;
$reason = $request->reason;
$duration = strtolower($request->durationOperator);
$durationOperand = $request->durationOperand;
$type = $request->suspensionType; // ON: Temporary | OFF: Permanent
$expiryDate = now();
if (! empty($duration)) {
switch ($duration) {
case 'days':
$expiryDate->addDays($durationOperand);
break;
case 'weeks':
$expiryDate->addWeeks($durationOperand);
break;
case 'months':
$expiryDate->addMonths($durationOperand);
break;
case 'years':
$expiryDate->addYears($durationOperand);
break;
}
} else {
// Essentially permanent
$expiryDate->addYears(40);
if ($type == "on") {
$expiryDate = now()->addDays($duration);
}
$ban = Ban::create([
'userID' => $user->id,
'reason' => $reason,
'bannedUntil' => $expiryDate->format('Y-m-d H:i:s'),
'userAgent' => 'Unknown',
'bannedUntil' => ($type == "on") ? $expiryDate->format('Y-m-d H:i:s') : null,
'authorUserID' => Auth::user()->id,
'isPermanent' => ($type == "off") ? true : false
]);
event(new UserBannedEvent($user, $ban));
$request->session()->flash('success', __('Account suspended. Suspension ID #:susId', ['susId', $ban->id]));
$request->session()->flash('success', __('Account suspended.'));
} else {
$request->session()->flash('error', __('Account already suspended!'));
}

View File

@ -71,6 +71,17 @@ class ProfileController extends Controller
}
}
$suspensionInfo = null;
if ($user->isBanned())
{
$suspensionInfo = [
'isPermanent' => $user->bans->isPermanent,
'reason' => $user->bans->reason,
'bannedUntil' => $user->bans->bannedUntil
];
}
if (Auth::user()->is($user) || Auth::user()->can('profiles.view.others')) {
return view('dashboard.user.profile.displayprofile')
->with([
@ -82,6 +93,7 @@ class ProfileController extends Controller
'since' => $createdDate->englishMonth.' '.$createdDate->year,
'ipInfo' => IP::lookup($user->originalIP),
'roles' => $roleList,
'suspensionInfo' => $suspensionInfo
]);
} else {
abort(403, __('You cannot view someone else\'s profile.'));

View File

@ -45,8 +45,15 @@ class BanUserRequest extends FormRequest
{
return [
'reason' => 'required|string',
'durationOperand' => 'nullable|string',
'durationOperator' => 'nullable|string',
'suspensionType' => 'required|string',
'duration' => 'required_if:suspensionType,on|nullable|integer',
];
}
public function messages()
{
return [
'duration.required_if' => __('You must provide a duration if the suspension is temporary.')
];
}
}

View File

@ -30,7 +30,7 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class CleanBans implements ShouldQueue
class ProcessDueSuspensions implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -52,15 +52,15 @@ class CleanBans implements ShouldQueue
*/
public function handle()
{
Log::debug('Running automatic ban cleaner...');
Log::debug('Running automatic suspension cleaner...');
$bans = Ban::all();
if (! is_null($bans)) {
foreach ($this->bans as $ban) {
$bannedUntil = Carbon::parse($ban->bannedUntil);
if ($bannedUntil->equalTo(now())) {
Log::debug('Deleted ban '.$ban->id.' belonging to '.$ban->user->name);
if ($bannedUntil->isToday()) {
Log::debug('Lifted expired suspension ID '.$ban->id.' for '.$ban->user->name);
$ban->delete();
}
}

View File

@ -611,5 +611,21 @@ return [
],
],
[
'name' => 'BootstrapSwitch',
'active' => true,
'files' => [
[
'type' => 'js',
'asset' => false,
'location' => 'https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/js/bootstrap4-toggle.min.js'
],
[
'type' => 'css',
'asset' => false,
'location' => 'https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css'
]
]
]
],
];

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ChangeBansTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('bans', function (Blueprint $table) {
$table->dropColumn('userAgent');
$table->boolean('isPermanent')->default(false);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('bans', function (Blueprint $table) {
$table->dropColumn('isPermanent');
$table->string('userAgent')->after('bannedUntil');
});
}
}

View File

@ -411,14 +411,13 @@ return [
'title' => ':name\'s profile',
'profile' => 'Profile',
'users' => 'Users',
'account_banned' => 'Account banned',
'account_banned_exp' => 'This user has been banned by the moderators.',
'ban_confirm' => 'Please confirm that you want to ban this user account. You\'ll need to add a reason and expiration date to confirm this. Bans don\'t transfer to connected Minecraft networks (yet).',
'leave_empty' => 'Leave empty for a permanent ban',
'account_banned' => 'Account suspended',
'account_banned_exp' => 'This user has been suspended by the admins.',
'ban_confirm' => 'Please confirm that you want to suspend this account. You\'ll need to add a reason and expiration date to confirm this.',
'duration' => 'Duration',
'p_duration' => 'Punishment duration',
'p_duration' => 'Suspension duration',
'p_duration_exp' => 'e.g. Spamming',
'ban' => 'Ban',
'ban' => 'Suspend',
'terminate_notice' => 'You are about to terminate a staff member',
'terminate_notice_warning' => 'Terminating a staff member will remove their privileges on the team management site and Network.

View File

@ -18,16 +18,16 @@
@section('content')
@if ($profile->user->isBanned())
@if (is_array($suspensionInfo))
<div class="alert alert-danger">
<span><i class="fa fa-ban"></i> <b>{{__('messages.profile.account_banned')}}</b></span>
<span><i class="fa fa-ban"></i> <b>{{__('messages.profile.account_banned')}} {{ ($suspensionInfo['isPermanent']) ? __('permanently.') : __('until :date.', ['date' => $suspensionInfo['bannedUntil']]) }}</b></span>
<p>{{__('messages.profile.account_banned_exp')}}</p>
<p>
<i class="fas fa-chevron-right"></i> <b>{{$profile->user->bans->reason}}</>
<i class="fas fa-chevron-right"></i> <b>{{$suspensionInfo['reason']}}</b>
</p>
</div>
@ -43,32 +43,33 @@
<form id="banAccountForm" name="banAccount" method="POST" action="{{route('banUser', ['user' => $profile->user->id])}}">
@csrf
<label for="reason">{{__('messages.reusable.reason')}}</label>
<div class="row">
<div class="col">
<label for="reason">{{__('Public note')}}</label>
<input type="text" name="reason" id="reason" class="form-control" placeholder="{{__('messages.profile.p_duration_exp')}}">
</div>
<div class="input-group">
<input type="text" class="form-control" name="durationOperator" aria-label="{{__('messages.profile.p_duration')}}">
<div class="input-group-append">
<button id="durationDropdown" class="btn btn-outline-secondary dropdown-toggle duration-btn" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{__('messages.profile.duration')}}</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">Days</a>
<a class="dropdown-item" href="#">Weeks</a>
<a class="dropdown-item" href="#">Months</a>
<div role="separator" class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Years</a>
<div class="col">
<label for="duration">{{ __('Duration') }}</label>
<input type="text" name="duration" id="duration" class="form-control" placeholder="{{ __('in days') }}">
</div>
</div>
</div>
<p class="text-muted text-sm">{{__('messages.profile.leave_empty')}}</p>
<input id="operator" type="hidden" value="" name="durationOperand" class="duration-operator-fld">
<div class="mt-2">
<input type="hidden" name="suspensionType" value="off">
<label for="suspensionType">Suspension type</label><br>
<input type="checkbox" id="suspensionType" name="suspensionType" checked data-toggle="toggle" data-on="Temporary" data-off="Permanent" data-onstyle="success" data-offstyle="danger" data-width="130" data-height="40">
<p class="text-muted text-sm"><i class="fas fa-info-circle"></i> {{ __('Temporary suspensions will be automatically lifted. The suspension note is visible to all users. Suspended users will not be able to login or register.') }}</p>
</div>
</form>
<x-slot name="modalFooter">
<button id="banAccountButton" type="button" class="btn btn-danger"><i class="fa fa-ban"></i> {{__('messages.profile.ban')}}</button>
<button id="banAccountButton" type="button" class="btn btn-danger"><i class="fa fa-gavel"></i> {{__('Confirm')}}</button>
</x-slot>
</x-modal>
@ -334,13 +335,13 @@
<div class="management-btn text-center">
@if (!$profile->user->isBanned())
<button class="btn btn-danger mb-2" id="banAccountTrigger"><i class="fa fa-ban"></i> {{__('messages.profile.ban_acc')}}</button><br>
<button class="btn btn-danger mb-2" id="banAccountTrigger"><i class="fa fa-ban"></i> {{__('Suspend')}}</button><br>
@else
<form method="post" action="{{route('unbanUser', ['user' => $profile->user->id])}}">
@method('DELETE')
@csrf
<button type="submit" class="btn btn-warning mb-2"><i class="fa fa-check"></i> {{__('messages.profile.unban_acc')}}</button>
<button type="submit" class="btn btn-warning mb-2"><i class="fa fa-check"></i> {{__('Lift Suspension')}}</button>
</form>
@endif