Merge remote-tracking branch 'upstream/develop' into impl/mod-card-moderation-options

This commit is contained in:
IsWaffle 2024-10-28 09:45:13 -04:00
commit e2e764fce7
15 changed files with 171 additions and 32 deletions

View File

@ -15,6 +15,8 @@ class ModResource extends JsonResource
*/ */
public function toArray(Request $request): array public function toArray(Request $request): array
{ {
$this->load(['users', 'versions', 'license']);
return [ return [
'type' => 'mod', 'type' => 'mod',
'id' => $this->id, 'id' => $this->id,
@ -51,11 +53,8 @@ class ModResource extends JsonResource
'type' => 'version', 'type' => 'version',
'id' => $version->id, 'id' => $version->id,
], ],
// TODO: The download link to the version can be placed here, but I'd like to track the number of
// downloads that are made, so we'll need a new route/feature for that. #35
'links' => [ 'links' => [
'self' => $version->link, 'self' => $version->downloadUrl(absolute: true),
], ],
])->toArray(), ])->toArray(),

View File

@ -28,10 +28,7 @@ class ModVersionResource extends JsonResource
// $this->description // $this->description
//), //),
// TODO: The download link to the version can be placed here, but I'd like to track the number of 'link' => $this->downloadUrl(absolute: true),
// downloads that are made, so we'll need a new route/feature for that. #35
'link' => $this->link,
'virus_total_link' => $this->virus_total_link, 'virus_total_link' => $this->virus_total_link,
'downloads' => $this->downloads, 'downloads' => $this->downloads,
'created_at' => $this->created_at, 'created_at' => $this->created_at,

View File

@ -15,6 +15,8 @@ class UserResource extends JsonResource
*/ */
public function toArray(Request $request): array public function toArray(Request $request): array
{ {
$this->load('role');
return [ return [
'type' => 'user', 'type' => 'user',
'id' => $this->id, 'id' => $this->id,
@ -34,7 +36,7 @@ class UserResource extends JsonResource
], ],
'includes' => $this->when( 'includes' => $this->when(
ApiController::shouldInclude('user_role'), ApiController::shouldInclude('user_role'),
new UserRoleResource($this->role) new UserRoleResource($this->role),
), ),
'links' => [ 'links' => [
'self' => $this->profileUrl(), 'self' => $this->profileUrl(),

View File

@ -30,6 +30,7 @@ use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use League\HTMLToMarkdown\HtmlConverter; use League\HTMLToMarkdown\HtmlConverter;
use Stevebauman\Purify\Facades\Purify; use Stevebauman\Purify\Facades\Purify;
use Throwable;
class ImportHubDataJob implements ShouldBeUnique, ShouldQueue class ImportHubDataJob implements ShouldBeUnique, ShouldQueue
{ {
@ -40,6 +41,7 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue
// Stream some data locally so that we don't have to keep accessing the Hub's database. Use MySQL temporary // Stream some data locally so that we don't have to keep accessing the Hub's database. Use MySQL temporary
// tables to store the data to save on memory; we don't want this to be a hog. // tables to store the data to save on memory; we don't want this to be a hog.
$this->bringUserAvatarLocal(); $this->bringUserAvatarLocal();
$this->bringUserOptionsLocal();
$this->bringFileAuthorsLocal(); $this->bringFileAuthorsLocal();
$this->bringFileOptionsLocal(); $this->bringFileOptionsLocal();
$this->bringFileContentLocal(); $this->bringFileContentLocal();
@ -98,6 +100,35 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue
}); });
} }
/**
* Bring the user options table from the Hub database to the local database temporary table.
*/
private function bringUserOptionsLocal(): void
{
DB::statement('DROP TEMPORARY TABLE IF EXISTS temp_user_options_values');
DB::statement('CREATE TEMPORARY TABLE temp_user_options_values (
userID INT,
about LONGTEXT
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci');
DB::connection('mysql_hub')
->table('wcf1_user_option_value')
->orderBy('userID')
->chunk(200, function ($options) {
$insertData = [];
foreach ($options as $option) {
$insertData[] = [
'userID' => (int) $option->userID,
'about' => $option->userOption1,
];
}
if ($insertData) {
DB::table('temp_user_options_values')->insert($insertData);
}
});
}
/** /**
* Bring the file authors from the Hub database to the local database temporary table. * Bring the file authors from the Hub database to the local database temporary table.
*/ */
@ -357,6 +388,7 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue
'name' => $hubUser->username, 'name' => $hubUser->username,
'email' => Str::lower($hubUser->email), 'email' => Str::lower($hubUser->email),
'password' => $this->cleanPasswordHash($hubUser->password), 'password' => $this->cleanPasswordHash($hubUser->password),
'about' => $this->fetchUserAbout($hubUser->userID),
'profile_photo_path' => $this->fetchUserAvatar($curl, $hubUser), 'profile_photo_path' => $this->fetchUserAvatar($curl, $hubUser),
'cover_photo_path' => $this->fetchUserCoverPhoto($curl, $hubUser), 'cover_photo_path' => $this->fetchUserCoverPhoto($curl, $hubUser),
'created_at' => $this->cleanRegistrationDate($hubUser->registrationDate), 'created_at' => $this->cleanRegistrationDate($hubUser->registrationDate),
@ -377,6 +409,32 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue
return str_starts_with($clean, '$2') ? $clean : ''; return str_starts_with($clean, '$2') ? $clean : '';
} }
/**
* Fetch the user about text from the temporary table.
*/
private function fetchUserAbout(int $userID): string
{
$about = DB::table('temp_user_options_values')
->where('userID', $userID)
->limit(1)
->value('about');
return $this->cleanHubContent($about ?? '');
}
/**
* Convert the mod description from WoltHub flavoured HTML to Markdown.
*/
protected function cleanHubContent(string $dirty): string
{
// Alright, hear me out... Shut up.
$converter = new HtmlConverter;
$clean = Purify::clean($dirty);
return $converter->convert($clean);
}
/** /**
* Fetch the user avatar from the Hub and store it anew. * Fetch the user avatar from the Hub and store it anew.
*/ */
@ -914,19 +972,6 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue
curl_close($curl); curl_close($curl);
} }
/**
* Convert the mod description from WoltHub flavoured HTML to Markdown.
*/
protected function cleanHubContent(string $dirty): string
{
// Alright, hear me out... Shut up.
$converter = new HtmlConverter;
$clean = Purify::clean($dirty);
return $converter->convert($clean);
}
/** /**
* Fetch the mod thumbnail from the Hub and store it anew. * Fetch the mod thumbnail from the Hub and store it anew.
*/ */
@ -1034,10 +1079,11 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue
/** /**
* The job failed to process. * The job failed to process.
*/ */
public function failed(Exception $exception): void public function failed(Throwable $exception): void
{ {
// Explicitly drop the temporary tables. // Explicitly drop the temporary tables.
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_user_avatar'); DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_user_avatar');
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_user_options_values');
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_author'); DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_author');
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_option_values'); DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_option_values');
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_content'); DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_content');

View File

@ -75,6 +75,11 @@ class FollowCard extends Component
#[Locked] #[Locked]
public Collection $followUsers; public Collection $followUsers;
/**
* The events the component should listen for.
*/
protected $listeners = ['refreshComponent' => '$refresh'];
/** /**
* The number of users being displayed. * The number of users being displayed.
*/ */
@ -148,6 +153,8 @@ class FollowCard extends Component
{ {
// Update the collection of profile user's followers (or following). // Update the collection of profile user's followers (or following).
$this->followUsers = $this->profileUser->{$this->relationship}()->get(); $this->followUsers = $this->profileUser->{$this->relationship}()->get();
$this->dispatch('refreshComponent');
} }
/** /**

View File

@ -23,6 +23,16 @@ class FollowCards extends Component
#[Locked] #[Locked]
public Collection $authFollowIds; public Collection $authFollowIds;
/**
* Render the component.
*/
public function render(): View
{
$this->updateAuthFollowIds();
return view('livewire.user.follow-cards');
}
/** /**
* Called when the user follows or unfollows a user. * Called when the user follows or unfollows a user.
*/ */
@ -38,12 +48,4 @@ class FollowCards extends Component
$this->dispatch('auth-follow-change'); $this->dispatch('auth-follow-change');
} }
/**
* Render the component.
*/
public function render(): View
{
return view('livewire.user.follow-cards');
}
} }

View File

@ -22,4 +22,17 @@ class License extends Model
return $this->hasMany(Mod::class) return $this->hasMany(Mod::class)
->chaperone(); ->chaperone();
} }
/**
* The attributes that should be cast to native types.
*/
protected function casts(): array
{
return [
'hub_id' => 'integer',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
];
}
} }

View File

@ -243,6 +243,7 @@ class Mod extends Model
'created_at' => 'datetime', 'created_at' => 'datetime',
'updated_at' => 'datetime', 'updated_at' => 'datetime',
'deleted_at' => 'datetime', 'deleted_at' => 'datetime',
'published_at' => 'datetime',
]; ];
} }

View File

@ -41,4 +41,15 @@ class ModDependency extends Model
{ {
return $this->belongsTo(Mod::class, 'dependent_mod_id'); return $this->belongsTo(Mod::class, 'dependent_mod_id');
} }
/**
* The attributes that should be cast to native types.
*/
protected function casts(): array
{
return [
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
}
} }

View File

@ -36,4 +36,15 @@ class ModResolvedDependency extends Model
{ {
return $this->belongsTo(ModVersion::class, 'resolved_mod_version_id'); return $this->belongsTo(ModVersion::class, 'resolved_mod_version_id');
} }
/**
* The attributes that should be cast to native types.
*/
protected function casts(): array
{
return [
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
}
} }

View File

@ -152,4 +152,23 @@ class ModVersion extends Model
return $this->downloads; return $this->downloads;
} }
/**
* The attributes that should be cast to native types.
*/
protected function casts(): array
{
return [
'hub_id' => 'integer',
'version_major' => 'integer',
'version_minor' => 'integer',
'version_patch' => 'integer',
'downloads' => 'integer',
'disabled' => 'boolean',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
'published_at' => 'datetime',
];
}
} }

View File

@ -16,4 +16,15 @@ class OAuthConnection extends Model
{ {
return $this->belongsTo(User::class); return $this->belongsTo(User::class);
} }
/**
* The attributes that should be cast to native types.
*/
protected function casts(): array
{
return [
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
}
} }

View File

@ -172,4 +172,21 @@ class SptVersion extends Model
->first(); ->first();
}); });
} }
/**
* The attributes that should be cast to native types.
*/
protected function casts(): array
{
return [
'hub_id' => 'integer',
'version_major' => 'integer',
'version_minor' => 'integer',
'version_patch' => 'integer',
'mod_count' => 'integer',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
];
}
} }

View File

@ -32,7 +32,10 @@ class UserFactory extends Factory
'email' => fake()->unique()->safeEmail(), 'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(), 'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'), 'password' => static::$password ??= Hash::make('password'),
// TODO: Does Faker have a markdown plugin?
'about' => fake()->paragraphs(random_int(1, 10), true), 'about' => fake()->paragraphs(random_int(1, 10), true),
'two_factor_secret' => null, 'two_factor_secret' => null,
'two_factor_recovery_codes' => null, 'two_factor_recovery_codes' => null,
'remember_token' => Str::random(10), 'remember_token' => Str::random(10),

View File

@ -43,7 +43,7 @@
{{-- About --}} {{-- About --}}
@if ($user->about) @if ($user->about)
<div class="p-4 sm:p-6 bg-white dark:bg-gray-950 rounded-xl shadow-md dark:shadow-gray-950 text-gray-800 dark:text-gray-200 drop-shadow-2xl"> <div class="p-4 sm:p-6 bg-white dark:bg-gray-950 rounded-xl shadow-md dark:shadow-gray-950 text-gray-800 dark:text-gray-200 drop-shadow-2xl">
{{ $user->about }} {!! Str::markdown($user->about) !!}
</div> </div>
@endif @endif