Skip to content

Commit

Permalink
feat(user): fully support the Visible Role field (#3087)
Browse files Browse the repository at this point in the history
wescopeland authored Jan 23, 2025
1 parent f0cf8db commit ea7a001
Showing 24 changed files with 401 additions and 56 deletions.
9 changes: 5 additions & 4 deletions app/Community/Components/UserCard.php
Original file line number Diff line number Diff line change
@@ -63,6 +63,7 @@ function () use ($username): ?array {
return $foundUser ? [
...$foundUser->toArray(),
'isMuted' => $foundUser->isMuted(),
'visibleRoleName' => $foundUser->visible_role?->name,
] : null;
}
);
@@ -72,7 +73,7 @@ private function buildAllCardViewValues(string $username, array $rawUserData): a
{
$cardBioData = $this->buildCardBioData($rawUserData);
$cardRankData = $this->buildCardRankData($username, $rawUserData['RAPoints'], $rawUserData['RASoftcorePoints'], $rawUserData['Untracked'] ? true : false);
$cardRoleData = $this->buildCardRoleData($username, $rawUserData['Permissions']);
$cardRoleData = $this->buildCardRoleData($username, $rawUserData['visibleRoleName']);

return array_merge($cardBioData, $cardRankData, $cardRoleData);
}
@@ -147,10 +148,10 @@ private function buildCardRankData(string $username, int $hardcorePoints, int $s
);
}

private function buildCardRoleData(string $username, int $permissions): array
private function buildCardRoleData(string $username, ?string $visibleRoleName): array
{
$canShowUserRole = $permissions >= Permissions::JuniorDeveloper;
$roleLabel = Permissions::toString($permissions);
$canShowUserRole = $visibleRoleName !== null;
$roleLabel = $visibleRoleName ? __('permission.role.' . $visibleRoleName) : null;

$useExtraNamePadding =
$canShowUserRole
39 changes: 39 additions & 0 deletions app/Community/Concerns/ActsAsCommunityMember.php
Original file line number Diff line number Diff line change
@@ -16,9 +16,11 @@
use App\Models\UserComment;
use App\Models\UserGameListEntry;
use App\Models\UserRelation;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\Auth;
use Spatie\Permission\Models\Role as SpatieRole;

// TODO organize accessors, relations, and scopes

@@ -28,6 +30,34 @@ public static function bootActsAsCommunityMember(): void
{
}

public function getVisibleRoleAttribute(): ?SpatieRole
{
// Load the user's displayable roles.
if ($this->relationLoaded('roles')) {
/** @var Collection<int, SpatieRole> $displayableRoles */
$displayableRoles = $this->roles->where('display', '>', 0);
} else {
/** @var Collection<int, SpatieRole> $displayableRoles */
$displayableRoles = $this->displayableRoles()->get();
}

// If user has an explicitly set visible_role_id, we'll try to show it.
// However, we need to verify it's still a valid displayable role for the
// user (it's possible they lost the role at some point).
if ($this->visible_role_id !== null) {
/** @var SpatieRole|null $explicitRole */
$explicitRole = $displayableRoles->find($this->visible_role_id);
if ($explicitRole) {
return $explicitRole;
}
}

// Otherwise, fall back to highest ordered displayable role.
// For most users, this will return null.
/** @var SpatieRole|null */
return $displayableRoles->first();
}

/**
* @return HasMany<UserActivity>
*/
@@ -50,6 +80,15 @@ public function gameListEntries(?string $type = null): HasMany
return $query;
}

/**
* @return BelongsToMany<SpatieRole>
*/
public function displayableRoles(): BelongsToMany
{
/** @var BelongsToMany<SpatieRole> */
return $this->roles()->where('display', '>', 0);
}

/**
* @return BelongsToMany<User>
*/
20 changes: 19 additions & 1 deletion app/Community/Controllers/UserSettingsController.php
Original file line number Diff line number Diff line change
@@ -18,14 +18,17 @@
use App\Community\Requests\UpdatePasswordRequest;
use App\Community\Requests\UpdateProfileRequest;
use App\Community\Requests\UpdateWebsitePrefsRequest;
use App\Data\RoleData;
use App\Data\UserData;
use App\Data\UserPermissionsData;
use App\Enums\Permissions;
use App\Enums\UserPreference;
use App\Http\Controller;
use App\Models\Role;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Inertia\Inertia;
use Inertia\Response as InertiaResponse;
@@ -39,6 +42,10 @@ public function show(): InertiaResponse
/** @var User $user */
$user = Auth::user();

$user->load(['roles' => function ($query) {
$query->where('display', '>', 0);
}]);

$userSettings = UserData::fromUser($user)->include(
'apiKey',
'deleteRequested',
@@ -54,7 +61,18 @@ public function show(): InertiaResponse
'updateMotto'
);

$props = new UserSettingsPagePropsData($userSettings, $can);
/** @var Collection<int, Role> $displayableRoles */
$displayableRoles = $user->roles;

$mappedRoles = $displayableRoles->map(fn ($role) => RoleData::fromRole($role))
->values()
->all();

$props = new UserSettingsPagePropsData(
$userSettings,
$can,
$mappedRoles,
);

return Inertia::render('settings', $props);
}
3 changes: 3 additions & 0 deletions app/Community/Data/UpdateProfileData.php
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ class UpdateProfileData extends Data
public function __construct(
public string $motto,
public bool $userWallActive,
public ?int $visibleRoleId,
) {
}

@@ -20,6 +21,7 @@ public static function fromRequest(UpdateProfileRequest $request): self
return new self(
motto: $request->motto,
userWallActive: $request->userWallActive,
visibleRoleId: $request->visibleRoleId,
);
}

@@ -28,6 +30,7 @@ public function toArray(): array
return [
'Motto' => $this->motto,
'UserWallActive' => $this->userWallActive,
'visible_role_id' => $this->visibleRoleId,
];
}
}
3 changes: 3 additions & 0 deletions app/Community/Data/UserSettingsPagePropsData.php
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@

namespace App\Community\Data;

use App\Data\RoleData;
use App\Data\UserData;
use App\Data\UserPermissionsData;
use Spatie\LaravelData\Data;
@@ -15,6 +16,8 @@ class UserSettingsPagePropsData extends Data
public function __construct(
public UserData $userSettings,
public UserPermissionsData $can,
/** @var RoleData[] */
public array $displayableRoles,
) {
}
}
1 change: 1 addition & 0 deletions app/Community/Requests/UpdateProfileRequest.php
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@ public function rules(): array
return [
'motto' => 'nullable|string|max:50',
'userWallActive' => 'nullable|boolean',
'visibleRoleId' => 'nullable|integer',
];
}

27 changes: 27 additions & 0 deletions app/Data/RoleData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace App\Data;

use Spatie\LaravelData\Data;
use Spatie\Permission\Models\Role;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript('Role')]
class RoleData extends Data
{
public function __construct(
public int $id,
public string $name,
) {
}

public static function fromRole(Role $role): self
{
return new self(
id: $role->id,
name: $role->name,
);
}
}
10 changes: 5 additions & 5 deletions app/Data/UserData.php
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@

namespace App\Data;

use App\Enums\Permissions;
use App\Models\User;
use App\Platform\Enums\PlayerPreferredMode;
use Illuminate\Support\Carbon;
@@ -24,6 +23,8 @@ public function __construct(
public Lazy|string|null $apiKey = null,
public Lazy|string|null $deleteRequested = null,
public Lazy|Carbon|null $deletedAt = null,
/** @var RoleData[] */
public Lazy|array|null $displayableRoles = null,
public Lazy|string|null $emailAddress = null,
public Lazy|int $id = 0,
public Lazy|bool $isMuted = false,
@@ -39,7 +40,7 @@ public function __construct(
public Lazy|int|null $unreadMessageCount = null,
public Lazy|string|null $username = null,
public Lazy|bool|null $userWallActive = null,
public Lazy|string|null $visibleRole = null,
public Lazy|RoleData|null $visibleRole = null,
public Lazy|int|null $websitePrefs = null,

#[TypeScriptType([
@@ -64,8 +65,6 @@ public static function fromRecentForumTopic(array $topic): self

public static function fromUser(User $user): self
{
$legacyPermissions = (int) $user->getAttribute('Permissions');

return new self(
// == eager fields
displayName: $user->display_name,
@@ -75,6 +74,7 @@ public static function fromUser(User $user): self
apiKey: Lazy::create(fn () => $user->APIKey),
deletedAt: Lazy::create(fn () => $user->Deleted ? Carbon::parse($user->Deleted) : null),
deleteRequested: Lazy::create(fn () => $user->DeleteRequested),
displayableRoles: Lazy::create(fn () => $user->displayableRoles),
emailAddress: Lazy::create(fn () => $user->EmailAddress),
mutedUntil: Lazy::create(fn () => $user->muted_until),
id: Lazy::create(fn () => $user->id),
@@ -97,7 +97,7 @@ public static function fromUser(User $user): self
unreadMessageCount: Lazy::create(fn () => $user->UnreadMessageCount),
username: Lazy::create(fn () => $user->username),
userWallActive: Lazy::create(fn () => $user->UserWallActive),
visibleRole: Lazy::create(fn () => $legacyPermissions > 1 ? Permissions::toString($legacyPermissions) : null),
visibleRole: Lazy::create(fn () => $user->visible_role ? RoleData::fromRole($user->visible_role) : null),
websitePrefs: Lazy::create(fn () => $user->websitePrefs),
);
}
3 changes: 2 additions & 1 deletion app/Http/Middleware/HandleInertiaRequests.php
Original file line number Diff line number Diff line change
@@ -48,14 +48,15 @@ public function share(Request $request): array
'isNew',
'legacyPermissions',
'locale',
'mutedUntil',
'playerPreferredMode',
'points',
'pointsSoftcore',
'mutedUntil',
'preferences',
'roles',
'unreadMessageCount',
'username',
'visibleRole',
'websitePrefs',
),
] : null,
1 change: 1 addition & 0 deletions app/Models/User.php
Original file line number Diff line number Diff line change
@@ -164,6 +164,7 @@ class User extends Authenticatable implements CommunityMember, Developer, HasLoc
'Untracked',
'User', // fillable for registration
'UserWallActive',
'visible_role_id',
'websitePrefs',
];

1 change: 1 addition & 0 deletions database/factories/UserFactory.php
Original file line number Diff line number Diff line change
@@ -44,6 +44,7 @@ public function definition(): array
'Untracked' => 0,
'UserWallActive' => 1,
'muted_until' => null,
'visible_role_id' => null,

// nullable
'APIKey' => 'apiKey',
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

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

return new class() extends Migration {
public function up(): void
{
Schema::table('UserAccounts', function (Blueprint $table) {
$table->unsignedBigInteger('visible_role_id')->nullable()->after('display_name');

$table->foreign('visible_role_id')
->references('id')
->on('auth_roles')
->onDelete('set null');

$table->index('visible_role_id');
});
}

public function down(): void
{
Schema::table('UserAccounts', function (Blueprint $table) {
$table->dropForeign(['visible_role_id']);
$table->dropIndex(['visible_role_id']);
$table->dropColumn('visible_role_id');
});
}
};
4 changes: 2 additions & 2 deletions lang/en/permission.php
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
return [
'role' => [
Role::ROOT => 'Root',
Role::ADMINISTRATOR => __('Administrator'),
Role::ADMINISTRATOR => __('Admin'),

// moderation & platform staff roles

@@ -23,7 +23,7 @@
Role::ARTIST => __('Artist'),
Role::WRITER => __('Writer'),
Role::GAME_EDITOR => __('Game Editor'),
Role::PLAY_TESTER => __('Play Tester'),
Role::PLAY_TESTER => __('Playtester'),

// community staff roles

24 changes: 23 additions & 1 deletion lang/en_US.json
Original file line number Diff line number Diff line change
@@ -641,5 +641,27 @@
"news-category.technical": "Technical",
"Don't ask for links to copyrighted ROMs. Don't share links to copyrighted ROMs.": "Don't ask for links to copyrighted ROMs. Don't share links to copyrighted ROMs.",
"Start new topic": "Start new topic",
"enter your new topic's title...": "enter your new topic's title..."
"enter your new topic's title...": "enter your new topic's title...",
"administrator": "Admin",
"game-hash-manager": "Hash Manager",
"release-manager": "Release Manager",
"developer": "Developer",
"developer-junior": "Junior Developer",
"artist": "Artist",
"writer": "Writer",
"play-tester": "Playtester",
"moderator": "Moderator",
"forum-manager": "Forum Manager",
"ticket-manager": "Ticket Manager",
"news-manager": "News Manager",
"event-manager": "Event Manager",
"founder": "Founder",
"architect": "Architect",
"engineer": "Engineer",
"developer-retired": "Developer (retired)",
"game-editor": "Game Editor",
"team-account": "Team Account",
"dev-compliance": "Developer Compliance",
"code-reviewer": "Code Reviewer",
"community-manager": "Community Manager"
}
Loading

0 comments on commit ea7a001

Please sign in to comment.