Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions app/Http/Controllers/BlockUserController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace App\Http\Controllers;

use App\Jobs\BlockUser;
use App\Models\User;
use App\Policies\UserPolicy;
use Illuminate\Auth\Middleware\Authenticate;
use Illuminate\Http\Request;

class BlockUserController extends Controller
{
public function __construct()
{
$this->middleware(Authenticate::class);
}

public function __invoke(Request $request, User $user)
{
$this->authorize(UserPolicy::BLOCK, $user);

$this->dispatchSync(new BlockUser($request->user(), $user));

$this->success('settings.user.blocked');

return redirect()->route('profile', $user->username());
}
}
5 changes: 2 additions & 3 deletions app/Http/Controllers/Settings/ApiTokenController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use App\Jobs\CreateApiToken;
use App\Jobs\DeleteApiToken;
use Illuminate\Auth\Middleware\Authenticate;
use Illuminate\Support\Facades\Auth;

class ApiTokenController extends Controller
{
Expand All @@ -19,7 +18,7 @@ public function __construct()

public function store(CreateApiTokenRequest $request)
{
$this->dispatchSync(new CreateApiToken($user = Auth::user(), $request->name()));
$this->dispatchSync(new CreateApiToken($user = $request->user(), $request->name()));

$token = $user->tokens()->where('name', $request->name())->first();

Expand All @@ -30,7 +29,7 @@ public function store(CreateApiTokenRequest $request)

public function destroy(DeleteApiTokenRequest $request)
{
$this->dispatchSync(new DeleteApiToken(Auth::user(), $request->id()));
$this->dispatchSync(new DeleteApiToken($request->user(), $request->id()));

$this->success('settings.api_token.deleted');

Expand Down
3 changes: 1 addition & 2 deletions app/Http/Controllers/Settings/PasswordController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use App\Http\Requests\UpdatePasswordRequest;
use App\Jobs\UpdatePassword;
use Illuminate\Auth\Middleware\Authenticate;
use Illuminate\Support\Facades\Auth;

class PasswordController extends Controller
{
Expand All @@ -17,7 +16,7 @@ public function __construct()

public function update(UpdatePasswordRequest $request)
{
$this->dispatchSync(new UpdatePassword(Auth::user(), $request->newPassword()));
$this->dispatchSync(new UpdatePassword($request->user(), $request->newPassword()));

$this->success('settings.password.updated');

Expand Down
3 changes: 1 addition & 2 deletions app/Http/Controllers/Settings/ProfileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use App\Policies\UserPolicy;
use Illuminate\Auth\Middleware\Authenticate;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class ProfileController extends Controller
{
Expand All @@ -25,7 +24,7 @@ public function edit()

public function update(UpdateProfileRequest $request)
{
$this->dispatchSync(UpdateProfile::fromRequest(Auth::user(), $request));
$this->dispatchSync(UpdateProfile::fromRequest($request->user(), $request));

$this->success('settings.updated');

Expand Down
29 changes: 29 additions & 0 deletions app/Http/Controllers/Settings/UnblockUserController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace App\Http\Controllers\Settings;

use App\Http\Controllers\Controller;
use App\Jobs\UnblockUser;
use App\Models\User;
use App\Policies\UserPolicy;
use Illuminate\Auth\Middleware\Authenticate;
use Illuminate\Http\Request;

class UnblockUserController extends Controller
{
public function __construct()
{
$this->middleware(Authenticate::class);
}

public function __invoke(Request $request, User $user)
{
$this->authorize(UserPolicy::BLOCK, $user);

$this->dispatchSync(new UnblockUser($request->user(), $user));

$this->success('settings.user.unblocked');

return redirect()->route('settings.profile');
}
}
28 changes: 28 additions & 0 deletions app/Http/Controllers/UnblockUserController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace App\Http\Controllers;

use App\Jobs\UnblockUser;
use App\Models\User;
use App\Policies\UserPolicy;
use Illuminate\Auth\Middleware\Authenticate;
use Illuminate\Http\Request;

class UnblockUserController extends Controller
{
public function __construct()
{
$this->middleware(Authenticate::class);
}

public function __invoke(Request $request, User $user)
{
$this->authorize(UserPolicy::BLOCK, $user);

$this->dispatchSync(new UnblockUser($request->user(), $user));

$this->success('settings.user.unblocked');

return redirect()->route('profile', $user->username());
}
}
17 changes: 17 additions & 0 deletions app/Jobs/BlockUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace App\Jobs;

use App\Models\User;

final class BlockUser
{
public function __construct(private User $user, private User $blockedUser)
{
}

public function handle(): void
{
$this->user->blockedUsers()->attach($this->blockedUser);
}
}
17 changes: 17 additions & 0 deletions app/Jobs/UnblockUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace App\Jobs;

use App\Models\User;

final class UnblockUser
{
public function __construct(private User $user, private User $blockedUser)
{
}

public function handle(): void
{
$this->user->blockedUsers()->detach($this->blockedUser);
}
}
4 changes: 3 additions & 1 deletion app/Listeners/NotifyUsersMentionedInReply.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ final class NotifyUsersMentionedInReply
public function handle(ReplyWasCreated $event): void
{
$event->reply->mentionedUsers()->each(function ($user) use ($event) {
$user->notify(new MentionNotification($event->reply));
if (! $user->hasBlocked($event->reply->author())) {
$user->notify(new MentionNotification($event->reply));
}
});
}
}
4 changes: 3 additions & 1 deletion app/Listeners/NotifyUsersMentionedInThread.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ final class NotifyUsersMentionedInThread
public function handle(ThreadWasCreated $event): void
{
$event->thread->mentionedUsers()->each(function ($user) use ($event) {
$user->notify(new MentionNotification($event->thread));
if (! $user->hasBlocked($event->thread->author())) {
$user->notify(new MentionNotification($event->thread));
}
});
}
}
24 changes: 24 additions & 0 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ public function replyAble(): HasMany
return $this->hasMany(Reply::class, 'author_id');
}

public function blockedUsers()
{
return $this->belongsToMany(User::class, 'blocked_users', 'user_id', 'blocked_user_id');
}

public function articles(): HasMany
{
return $this->hasMany(Article::class, 'author_id');
Expand Down Expand Up @@ -301,4 +306,23 @@ public function scopeModerators(Builder $query)
self::MODERATOR,
]);
}

public function hasBlocked(User $user): bool
{
return $this->blockedUsers()->where('blocked_user_id', $user->getKey())->exists();
}

public function scopeWithUsersWhoDoesntBlock(Builder $query, User $user)
{
return $query->whereDoesntHave('blockedUsers', function ($query) use ($user) {
$query->where('blocked_user_id', $user->getKey());
});
}

public function scopeWithUsersWhoArentBlockedBy(Builder $query, User $user)
{
return $query->whereDoesntHave('blockedUsers', function ($query) use ($user) {
$query->where('user_id', $user->getKey());
});
}
}
7 changes: 7 additions & 0 deletions app/Policies/UserPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ final class UserPolicy

const BAN = 'ban';

const BLOCK = 'block';

const DELETE = 'delete';

public function admin(User $user): bool
Expand All @@ -23,6 +25,11 @@ public function ban(User $user, User $subject): bool
($user->isModerator() && ! $subject->isAdmin() && ! $subject->isModerator());
}

public function block(User $user, User $subject): bool
{
return ! $user->is($subject) && ! $subject->isModerator() && ! $subject->isAdmin();
}

public function delete(User $user, User $subject): bool
{
return ($user->isAdmin() || $user->is($subject)) && ! $subject->isAdmin();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up()
{
Schema::create('blocked_users', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('user_id');
$table->foreign('user_id')->references('id')->on('users')->cascadeOnDelete();
$table->unsignedInteger('blocked_user_id');
$table->foreign('blocked_user_id')->references('id')->on('users')->cascadeOnDelete();
});
}
};
15 changes: 15 additions & 0 deletions database/schema/mysql-schema.dump
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ CREATE TABLE `articles` (
CONSTRAINT `articles_author_id_foreign` FOREIGN KEY (`author_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `blocked_users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `blocked_users` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` int unsigned NOT NULL,
`blocked_user_id` int unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `blocked_users_user_id_foreign` (`user_id`),
KEY `blocked_users_blocked_user_id_foreign` (`blocked_user_id`),
CONSTRAINT `blocked_users_blocked_user_id_foreign` FOREIGN KEY (`blocked_user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `blocked_users_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `failed_jobs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
Expand Down Expand Up @@ -355,3 +369,4 @@ INSERT INTO `migrations` VALUES (67,'2021_10_12_170118_add_locked_by_column_to_t
INSERT INTO `migrations` VALUES (68,'2019_12_14_000001_create_personal_access_tokens_table',6);
INSERT INTO `migrations` VALUES (69,'2022_04_06_152416_add_uuids_to_tables',7);
INSERT INTO `migrations` VALUES (70,'2022_05_10_180922_make_uuids_non_nullable',7);
INSERT INTO `migrations` VALUES (71,'2022_06_14_072001_create_blocked_users_table',8);
5 changes: 4 additions & 1 deletion database/seeders/UserSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class UserSeeder extends Seeder
{
public function run()
{
User::factory()->createQuietly([
$admin = User::factory()->createQuietly([
'name' => 'Test User',
'email' => 'test@example.com',
'username' => 'testing',
Expand Down Expand Up @@ -59,5 +59,8 @@ public function run()
->inRandomOrder()
->take(4)
->update(['is_pinned' => true]);

// Block some users...
$admin->blockedUsers()->sync(range(20, 24));
}
}
2 changes: 2 additions & 0 deletions lang/en/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
'updated' => 'Settings successfully saved! If you changed your email address you\'ll receive an email address to re-confirm it.',
'deleted' => 'Account was successfully removed.',
'password.updated' => 'Password successfully changed!',
'user.blocked' => 'User successfully blocked.',
'user.unblocked' => 'User successfully unblocked.',
'api_token' => [
'created' => 'API token created! Please copy the following token as it will not be shown again:',
'deleted' => 'API token successfully removed.',
Expand Down
5 changes: 5 additions & 0 deletions resources/views/emails/mention.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@
View Thread
@endcomponent

@component('mail::subcopy')
If you do not want this user to be able to mention you anymore, you may
[block them through their profile]({{ route('profile', $mentionAble->author()->username()) }}).
@endcomponent

@endcomponent
6 changes: 3 additions & 3 deletions resources/views/layouts/_nav.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@
</li>
@else
<li class="relative p-4 lg:p-0">
<div class="flex items-center justify-center gap-3">
<div class="flex items-center justify-center">
<a href="{{ route('notifications') }}" class="hidden shrink-0 rounded-full lg:block">
<span class="block relative">
<x-heroicon-o-bell class="h-5 w-5 hover:fill-current hover:text-lio-500"/>
Expand All @@ -193,9 +193,9 @@
</span>
</a>

<x-avatar :user="Auth::user()" class="h-8 w-8" />
<x-avatar :user="Auth::user()" class="h-8 w-8 ml-5" />

<div @click.outside="settings = false">
<div class="ml-2" @click.outside="settings = false">
<button @click="settings = !settings" class="flex items-center">
{{ Auth::user()->username() }}
<x-heroicon-s-chevron-down x-show="!settings" class="w-4 h-4 ml-1"/>
Expand Down
Loading