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', 'userID',
'reason', 'reason',
'bannedUntil', 'bannedUntil',
'userAgent', 'isPermanent',
'authorUserID', 'authorUserID',
]; ];
public $dates = [ public $dates = [
'bannedUntil', 'suspendedUntil',
]; ];
public function user() public function user()

View File

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

View File

@ -34,48 +34,26 @@ class BanController extends Controller
{ {
$this->authorize('create', [Ban::class, $user]); $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)) { if (is_null($user->bans)) {
$duration = $request->duration;
$reason = $request->reason; $reason = $request->reason;
$duration = strtolower($request->durationOperator); $type = $request->suspensionType; // ON: Temporary | OFF: Permanent
$durationOperand = $request->durationOperand;
$expiryDate = now(); if ($type == "on") {
$expiryDate = now()->addDays($duration);
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);
} }
$ban = Ban::create([ $ban = Ban::create([
'userID' => $user->id, 'userID' => $user->id,
'reason' => $reason, 'reason' => $reason,
'bannedUntil' => $expiryDate->format('Y-m-d H:i:s'), 'bannedUntil' => ($type == "on") ? $expiryDate->format('Y-m-d H:i:s') : null,
'userAgent' => 'Unknown',
'authorUserID' => Auth::user()->id, 'authorUserID' => Auth::user()->id,
'isPermanent' => ($type == "off") ? true : false
]); ]);
event(new UserBannedEvent($user, $ban)); $request->session()->flash('success', __('Account suspended.'));
$request->session()->flash('success', __('Account suspended. Suspension ID #:susId', ['susId', $ban->id]));
} else { } else {
$request->session()->flash('error', __('Account already suspended!')); $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')) { if (Auth::user()->is($user) || Auth::user()->can('profiles.view.others')) {
return view('dashboard.user.profile.displayprofile') return view('dashboard.user.profile.displayprofile')
->with([ ->with([
@ -82,6 +93,7 @@ class ProfileController extends Controller
'since' => $createdDate->englishMonth.' '.$createdDate->year, 'since' => $createdDate->englishMonth.' '.$createdDate->year,
'ipInfo' => IP::lookup($user->originalIP), 'ipInfo' => IP::lookup($user->originalIP),
'roles' => $roleList, 'roles' => $roleList,
'suspensionInfo' => $suspensionInfo
]); ]);
} else { } else {
abort(403, __('You cannot view someone else\'s profile.')); abort(403, __('You cannot view someone else\'s profile.'));

View File

@ -45,8 +45,15 @@ class BanUserRequest extends FormRequest
{ {
return [ return [
'reason' => 'required|string', 'reason' => 'required|string',
'durationOperand' => 'nullable|string', 'suspensionType' => 'required|string',
'durationOperator' => 'nullable|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\Queue\SerializesModels;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class CleanBans implements ShouldQueue class ProcessDueSuspensions implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -52,15 +52,15 @@ class CleanBans implements ShouldQueue
*/ */
public function handle() public function handle()
{ {
Log::debug('Running automatic ban cleaner...'); Log::debug('Running automatic suspension cleaner...');
$bans = Ban::all(); $bans = Ban::all();
if (! is_null($bans)) { if (! is_null($bans)) {
foreach ($this->bans as $ban) { foreach ($this->bans as $ban) {
$bannedUntil = Carbon::parse($ban->bannedUntil); $bannedUntil = Carbon::parse($ban->bannedUntil);
if ($bannedUntil->equalTo(now())) { if ($bannedUntil->isToday()) {
Log::debug('Deleted ban '.$ban->id.' belonging to '.$ban->user->name); Log::debug('Lifted expired suspension ID '.$ban->id.' for '.$ban->user->name);
$ban->delete(); $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', 'title' => ':name\'s profile',
'profile' => 'Profile', 'profile' => 'Profile',
'users' => 'Users', 'users' => 'Users',
'account_banned' => 'Account banned', 'account_banned' => 'Account suspended',
'account_banned_exp' => 'This user has been banned by the moderators.', 'account_banned_exp' => 'This user has been suspended by the admins.',
'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).', 'ban_confirm' => 'Please confirm that you want to suspend this account. You\'ll need to add a reason and expiration date to confirm this.',
'leave_empty' => 'Leave empty for a permanent ban',
'duration' => 'Duration', 'duration' => 'Duration',
'p_duration' => 'Punishment duration', 'p_duration' => 'Suspension duration',
'p_duration_exp' => 'e.g. Spamming', 'p_duration_exp' => 'e.g. Spamming',
'ban' => 'Ban', 'ban' => 'Suspend',
'terminate_notice' => 'You are about to terminate a staff member', '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. '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') @section('content')
@if ($profile->user->isBanned()) @if (is_array($suspensionInfo))
<div class="alert alert-danger"> <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>{{__('messages.profile.account_banned_exp')}}</p>
<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> </p>
</div> </div>
@ -43,32 +43,33 @@
<form id="banAccountForm" name="banAccount" method="POST" action="{{route('banUser', ['user' => $profile->user->id])}}"> <form id="banAccountForm" name="banAccount" method="POST" action="{{route('banUser', ['user' => $profile->user->id])}}">
@csrf @csrf
<label for="reason">{{__('messages.reusable.reason')}}</label> <div class="row">
<input type="text" name="reason" id="reason" class="form-control" placeholder="{{__('messages.profile.p_duration_exp')}}">
<div class="input-group"> <div class="col">
<input type="text" class="form-control" name="durationOperator" aria-label="{{__('messages.profile.p_duration')}}"> <label for="reason">{{__('Public note')}}</label>
<div class="input-group-append"> <input type="text" name="reason" id="reason" class="form-control" placeholder="{{__('messages.profile.p_duration_exp')}}">
<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>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">Days</a> <div class="col">
<a class="dropdown-item" href="#">Weeks</a> <label for="duration">{{ __('Duration') }}</label>
<a class="dropdown-item" href="#">Months</a> <input type="text" name="duration" id="duration" class="form-control" placeholder="{{ __('in days') }}">
<div role="separator" class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Years</a>
</div> </div>
</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> </form>
<x-slot name="modalFooter"> <x-slot name="modalFooter">
<button id="banAccountButton" type="button" class="btn btn-danger"><i class="fa fa-gavel"></i> {{__('Confirm')}}</button>
<button id="banAccountButton" type="button" class="btn btn-danger"><i class="fa fa-ban"></i> {{__('messages.profile.ban')}}</button>
</x-slot> </x-slot>
</x-modal> </x-modal>
@ -334,13 +335,13 @@
<div class="management-btn text-center"> <div class="management-btn text-center">
@if (!$profile->user->isBanned()) @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 @else
<form method="post" action="{{route('unbanUser', ['user' => $profile->user->id])}}"> <form method="post" action="{{route('unbanUser', ['user' => $profile->user->id])}}">
@method('DELETE') @method('DELETE')
@csrf @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> </form>
@endif @endif