mirror of
https://github.com/AnimeThemes/animethemes-server.git
synced 2026-07-11 01:24:46 +02:00
feat: filament (#679)
This commit is contained in:
+6
-2
@@ -82,7 +82,7 @@ REDIS_CACHE_DB=1
|
||||
DUMP_DISK=dumps
|
||||
DUMP_DISK_ROOT=
|
||||
DUMP_URL=http://localhost
|
||||
DUMP_PATH=
|
||||
DUMP_PATH=/dump
|
||||
|
||||
# elastic client
|
||||
ELASTIC_CONNECTION=default
|
||||
@@ -98,6 +98,10 @@ ELASTIC_MIGRATIONS_TABLE=elastic_migrations
|
||||
FFMPEG_BINARIES=
|
||||
FFPROBE_BINARIES=
|
||||
|
||||
# filament
|
||||
FILAMENT_DOMAIN_NAME=
|
||||
FILAMENT_PATH=/admin
|
||||
|
||||
# filesystems
|
||||
FILESYSTEM_DISK=local
|
||||
|
||||
@@ -211,7 +215,7 @@ NOVA_DOMAIN_NAME=null
|
||||
NOVA_GUARD=null
|
||||
NOVA_LICENSE_KEY=
|
||||
NOVA_PASSWORDS=null
|
||||
NOVA_PATH=/admin
|
||||
NOVA_PATH=/nova
|
||||
|
||||
# pennant
|
||||
PENNANT_STORE=database
|
||||
|
||||
+5
-1
@@ -83,7 +83,7 @@ REDIS_CACHE_DB=1
|
||||
DUMP_DISK=dumps
|
||||
DUMP_DISK_ROOT=
|
||||
DUMP_URL=http://localhost
|
||||
DUMP_PATH=
|
||||
DUMP_PATH=/dump
|
||||
|
||||
# elastic client
|
||||
ELASTIC_CONNECTION=default
|
||||
@@ -99,6 +99,10 @@ ELASTIC_MIGRATIONS_TABLE=elastic_migrations
|
||||
FFMPEG_BINARIES=
|
||||
FFPROBE_BINARIES=
|
||||
|
||||
# filament
|
||||
FILAMENT_DOMAIN_NAME=
|
||||
FILAMENT_PATH=/admin
|
||||
|
||||
# filesystems
|
||||
FILESYSTEM_DISK=local
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Discord;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Enums\Discord\EmbedColor;
|
||||
use App\Enums\Models\Wiki\AnimeSeason;
|
||||
use App\Enums\Models\Wiki\ImageFacet;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Image;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DiscordThreadAction
|
||||
{
|
||||
/**
|
||||
* Handle the action.
|
||||
*
|
||||
* @param Anime $anime
|
||||
* @param array $fields
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Anime $anime, array $fields): void
|
||||
{
|
||||
$name = Arr::get($fields, 'name');
|
||||
|
||||
/** @var \Illuminate\Filesystem\FilesystemAdapter */
|
||||
$imageDisk = Storage::disk(Config::get('image.disk'));
|
||||
|
||||
$imagePath = $anime->images()->where(Image::ATTRIBUTE_FACET, ImageFacet::COVER_LARGE)->first()->path;
|
||||
|
||||
$animepage = json_decode(file_get_contents(base_path('composer.json')), true)['homepage'].'anime/';
|
||||
$description = '**Synopsis:** '.strip_tags($anime->synopsis)."\n\n".'**Link:** '.$animepage.$anime->slug;
|
||||
|
||||
Http::withToken(Config::get('services.discord.token'), 'Bot')
|
||||
->asMultipart()
|
||||
->attach('file', file_get_contents($imageDisk->url($imagePath)), 'image.jpg')
|
||||
->post("https://discord.com/api/v10/channels/{$this->getDiscordChannel()}/threads", [
|
||||
'payload_json' => json_encode([
|
||||
'name' => $name,
|
||||
'applied_tags' => $this->getAppliedTags($anime->season->value),
|
||||
'message' => [
|
||||
'embeds' => [
|
||||
[
|
||||
'color' => EmbedColor::PURPLE->value,
|
||||
'title' => $anime->name,
|
||||
'description' => $description,
|
||||
]
|
||||
],
|
||||
]
|
||||
])
|
||||
])->throw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Discord forum channel the thread will be created to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::SUBMISSIONS_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IDs of the tags applied to the thread.
|
||||
*
|
||||
* @param int $season
|
||||
* @return array
|
||||
*/
|
||||
protected function getAppliedTags(int $season): array
|
||||
{
|
||||
return match ($season) {
|
||||
AnimeSeason::WINTER->value => [Config::get('services.discord.submissions_forum_tags.winter')],
|
||||
AnimeSeason::SPRING->value => [Config::get('services.discord.submissions_forum_tags.spring')],
|
||||
AnimeSeason::SUMMER->value => [Config::get('services.discord.submissions_forum_tags.summer')],
|
||||
AnimeSeason::FALL->value => [Config::get('services.discord.submissions_forum_tags.fall')],
|
||||
default => [],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\Wiki\Anime;
|
||||
|
||||
use App\Models\Wiki\Image;
|
||||
use App\Actions\Models\Wiki\AttachImageAction;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* Class AttachAnimeImageAction.
|
||||
*/
|
||||
class AttachAnimeImageAction extends AttachImageAction
|
||||
{
|
||||
/**
|
||||
* Get the relation to the action models.
|
||||
*
|
||||
* @param Image $image
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
protected function relation(Image $image): BelongsToMany
|
||||
{
|
||||
return $image->anime();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\Wiki\Anime;
|
||||
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use App\Actions\Models\Wiki\AttachResourceAction;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* Class AttachAnimeResourceAction.
|
||||
*/
|
||||
class AttachAnimeResourceAction extends AttachResourceAction
|
||||
{
|
||||
/**
|
||||
* Get the relation to the action models.
|
||||
*
|
||||
* @param ExternalResource $resource
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
protected function relation(ExternalResource $resource): BelongsToMany
|
||||
{
|
||||
return $resource->anime();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\Wiki\Artist;
|
||||
|
||||
use App\Models\Wiki\Image;
|
||||
use App\Actions\Models\Wiki\AttachImageAction;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* Class AttachArtistImageAction.
|
||||
*/
|
||||
class AttachArtistImageAction extends AttachImageAction
|
||||
{
|
||||
/**
|
||||
* Get the relation to the action models.
|
||||
*
|
||||
* @param Image $image
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
protected function relation(Image $image): BelongsToMany
|
||||
{
|
||||
return $image->artists();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\Wiki\Artist;
|
||||
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use App\Actions\Models\Wiki\AttachResourceAction;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* Class AttachArtistResourceAction.
|
||||
*/
|
||||
class AttachArtistResourceAction extends AttachResourceAction
|
||||
{
|
||||
/**
|
||||
* Get the relation to the action models.
|
||||
*
|
||||
* @param ExternalResource $resource
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
protected function relation(ExternalResource $resource): BelongsToMany
|
||||
{
|
||||
return $resource->artists();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\Wiki;
|
||||
|
||||
use App\Enums\Models\Wiki\ImageFacet;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Image;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class AttachImageAction.
|
||||
*/
|
||||
abstract class AttachImageAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param ImageFacet[] $facets
|
||||
*/
|
||||
public function __construct(protected array $facets)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param BaseModel $model
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
public function handle(BaseModel $model, array $fields): void
|
||||
{
|
||||
$images = $this->createImages($fields, $model);
|
||||
|
||||
foreach ($images as $image) {
|
||||
$relation = $this->relation($image);
|
||||
|
||||
$relation->attach($model);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the images.
|
||||
*
|
||||
* @param array $fields
|
||||
* @param BaseModel $model
|
||||
* @return Image[]
|
||||
*/
|
||||
protected function createImages(array $fields, BaseModel $model): array
|
||||
{
|
||||
$images = [];
|
||||
|
||||
foreach ($this->facets as $facet) {
|
||||
$image = Arr::get($fields, $facet->name);
|
||||
|
||||
if (empty($image)) continue;
|
||||
|
||||
/** @var \Illuminate\Filesystem\FilesystemAdapter */
|
||||
$fs = Storage::disk(Config::get('image.disk'));
|
||||
|
||||
$fsFile = $fs->putFile($this->path($facet, $model), $image);
|
||||
|
||||
$image = Image::query()->create([
|
||||
Image::ATTRIBUTE_FACET => $facet->value,
|
||||
Image::ATTRIBUTE_PATH => $fsFile,
|
||||
]);
|
||||
|
||||
$images[] = $image;
|
||||
}
|
||||
|
||||
return $images;
|
||||
}
|
||||
|
||||
/**
|
||||
* Path to storage image in filesystem.
|
||||
*
|
||||
* @param ImageFacet $facet
|
||||
* @param BaseModel $model
|
||||
* @return string
|
||||
*/
|
||||
protected function path(ImageFacet $facet, BaseModel $model): string
|
||||
{
|
||||
return Str::of(Str::kebab(class_basename($model)))
|
||||
->append(DIRECTORY_SEPARATOR)
|
||||
->append(Str::kebab($facet->localize()))
|
||||
->__toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relation to the action models.
|
||||
*
|
||||
* @param Image $image
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
abstract protected function relation(Image $image): BelongsToMany;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\Wiki;
|
||||
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
abstract class AttachResourceAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param ResourceSite[] $sites
|
||||
*/
|
||||
public function __construct(protected array $sites)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the action.
|
||||
*
|
||||
* @param BaseModel $model
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(BaseModel $model, array $data): void
|
||||
{
|
||||
$resources = $this->getOrCreateResource($data);
|
||||
|
||||
foreach ($resources as $resource) {
|
||||
$relation = $this->relation($resource);
|
||||
|
||||
$relation->attach($model);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or Create Resource from link field.
|
||||
*
|
||||
* @param array $data
|
||||
* @return ExternalResource[]
|
||||
*/
|
||||
protected function getOrCreateResource(array $data): array
|
||||
{
|
||||
$resources = [];
|
||||
|
||||
foreach ($this->sites as $resourceSite) {
|
||||
$link = Arr::get($data, $resourceSite->name);
|
||||
|
||||
if (empty($link)) continue;
|
||||
|
||||
$resource = ExternalResource::query()
|
||||
->where(ExternalResource::ATTRIBUTE_LINK, $link)
|
||||
->first();
|
||||
|
||||
if ($resource === null) {
|
||||
$resource = ExternalResource::query()->create([
|
||||
ExternalResource::ATTRIBUTE_EXTERNAL_ID => ResourceSite::parseIdFromLink($link),
|
||||
ExternalResource::ATTRIBUTE_LINK => $link,
|
||||
ExternalResource::ATTRIBUTE_SITE => $resourceSite->value,
|
||||
]);
|
||||
}
|
||||
|
||||
$resources[] = $resource;
|
||||
}
|
||||
|
||||
return $resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relation to the action models.
|
||||
*
|
||||
* @param ExternalResource $resource
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
abstract protected function relation(ExternalResource $resource): BelongsToMany;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\Wiki\Song;
|
||||
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use App\Actions\Models\Wiki\AttachResourceAction;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* Class AttachSongResourceAction.
|
||||
*/
|
||||
class AttachSongResourceAction extends AttachResourceAction
|
||||
{
|
||||
/**
|
||||
* Get the relation to the action models.
|
||||
*
|
||||
* @param ExternalResource $resource
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
protected function relation(ExternalResource $resource): BelongsToMany
|
||||
{
|
||||
return $resource->songs();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\Wiki\Studio;
|
||||
|
||||
use App\Models\Wiki\Image;
|
||||
use App\Actions\Models\Wiki\AttachImageAction;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* Class AttachStudioImageAction.
|
||||
*/
|
||||
class AttachStudioImageAction extends AttachImageAction
|
||||
{
|
||||
/**
|
||||
* Get the relation to the action models.
|
||||
*
|
||||
* @param Image $image
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
protected function relation(Image $image): BelongsToMany
|
||||
{
|
||||
return $image->studios();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\Wiki\Studio;
|
||||
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use App\Actions\Models\Wiki\AttachResourceAction;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* Class AttachStudioResourceAction.
|
||||
*/
|
||||
class AttachStudioResourceAction extends AttachResourceAction
|
||||
{
|
||||
/**
|
||||
* Get the relation to the action models.
|
||||
*
|
||||
* @param ExternalResource $resource
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
protected function relation(ExternalResource $resource): BelongsToMany
|
||||
{
|
||||
return $resource->studios();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\Wiki;
|
||||
|
||||
use App\Enums\Models\Wiki\ImageFacet;
|
||||
use App\Models\Wiki\Image;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class UploadImageAction.
|
||||
*/
|
||||
class UploadImageAction
|
||||
{
|
||||
/**
|
||||
* Create the images.
|
||||
*
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
public function handle(array $fields): void
|
||||
{
|
||||
$facet = ImageFacet::from(intval(Arr::get($fields, Image::ATTRIBUTE_FACET)));
|
||||
$image = Arr::get($fields, Image::ATTRIBUTE_PATH);
|
||||
|
||||
/** @var \Illuminate\Filesystem\FilesystemAdapter */
|
||||
$fs = Storage::disk(Config::get('image.disk'));
|
||||
|
||||
$fsFile = $fs->putFile($this->path($facet), $image);
|
||||
|
||||
Image::query()->create([
|
||||
Image::ATTRIBUTE_FACET => $facet->value,
|
||||
Image::ATTRIBUTE_PATH => $fsFile,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Path to storage image in filesystem.
|
||||
*
|
||||
* @param ImageFacet $facet
|
||||
* @return string
|
||||
*/
|
||||
protected function path(ImageFacet $facet): string
|
||||
{
|
||||
return Str::of(Str::kebab($facet->localize()))
|
||||
->__toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Filament\Enums;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Trait HasColorOrEmoji.
|
||||
*/
|
||||
trait HasColorOrEmoji
|
||||
{
|
||||
/**
|
||||
* Get the enum as an array formatted for a select, but styled.
|
||||
*
|
||||
* @param bool|null $hasColor
|
||||
* @param bool|null $hasEmoji
|
||||
* @param string|null $locale
|
||||
* @return array
|
||||
*/
|
||||
public static function asSelectArrayStyled(?bool $hasColor = true, ?bool $hasEmoji = true, ?string $locale = null): array
|
||||
{
|
||||
$selectArray = [];
|
||||
|
||||
/** @var static $case */
|
||||
foreach (static::cases() as $case) {
|
||||
$selectArray[$case->value] = $case->localizeStyled($hasColor, $hasEmoji, $locale);
|
||||
}
|
||||
|
||||
return $selectArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Localize the enum, but styled.
|
||||
*
|
||||
* @param bool|null $hasColor
|
||||
* @param bool|null $hasEmoji
|
||||
* @param string|null $locale
|
||||
* @return string
|
||||
*/
|
||||
public function localizeStyled(?bool $hasColor = true, ?bool $hasEmoji = true, ?string $locale = null): string
|
||||
{
|
||||
$localizedName = $this->getLocalizedName($locale) ?? $this->getPrettyName();
|
||||
|
||||
$name = Str::of('');
|
||||
|
||||
if ($hasColor) {
|
||||
$color = $this->getColor();
|
||||
$name = $name->append("<p style='color: rgb($color);'>");
|
||||
}
|
||||
|
||||
if ($hasEmoji) {
|
||||
$emoji = $this->getEmoji();
|
||||
$name = $name->append("$emoji ");
|
||||
}
|
||||
|
||||
$name = $name->append($localizedName);
|
||||
|
||||
if ($hasColor) {
|
||||
$name = $name->append('</p>');
|
||||
}
|
||||
|
||||
return $name->__toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Filament;
|
||||
|
||||
use App\Filament\Tabs\BaseTab;
|
||||
|
||||
/**
|
||||
* Trait HasTabs.
|
||||
*/
|
||||
trait HasTabs
|
||||
{
|
||||
/**
|
||||
* Get the tabs for an array key-mapped.
|
||||
*
|
||||
* @param class-string<BaseTab>[] $tabClasses
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(array $tabClasses): array
|
||||
{
|
||||
$tabs = [];
|
||||
|
||||
foreach ($tabClasses as $class) {
|
||||
if ((new $class)->shouldBeHidden()) continue;
|
||||
$tabs[$class::getKey()] = $class::make();
|
||||
}
|
||||
|
||||
return $tabs;
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ trait ReconcilesDumpRepositories
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
protected function action(): ReconcileRepositoriesAction
|
||||
protected function reconcileAction(): ReconcileRepositoriesAction
|
||||
{
|
||||
return new ReconcileDumpRepositoriesAction();
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ trait ReconcilesRepositories
|
||||
|
||||
$this->handleFilters($sourceRepository, $destinationRepository, $data);
|
||||
|
||||
$action = $this->action();
|
||||
$action = $this->reconcileAction();
|
||||
|
||||
return $action->reconcileRepositories($sourceRepository, $destinationRepository);
|
||||
}
|
||||
@@ -53,7 +53,7 @@ trait ReconcilesRepositories
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
abstract protected function action(): ReconcileRepositoriesAction;
|
||||
abstract protected function reconcileAction(): ReconcileRepositoriesAction;
|
||||
|
||||
/**
|
||||
* Get source repository.
|
||||
|
||||
@@ -43,7 +43,7 @@ trait ReconcilesAudioRepositories
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
protected function action(): ReconcileRepositoriesAction
|
||||
protected function reconcileAction(): ReconcileRepositoriesAction
|
||||
{
|
||||
return new ReconcileAudioRepositoriesAction();
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ trait ReconcilesVideoRepositories
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
protected function action(): ReconcileRepositoriesAction
|
||||
protected function reconcileAction(): ReconcileRepositoriesAction
|
||||
{
|
||||
return new ReconcileVideoRepositoriesAction();
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ trait ReconcilesScriptRepositories
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
protected function action(): ReconcileRepositoriesAction
|
||||
protected function reconcileAction(): ReconcileRepositoriesAction
|
||||
{
|
||||
return new ReconcileScriptRepositoriesAction();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts\Models;
|
||||
|
||||
/**
|
||||
* Interface HasSubtitle.
|
||||
*/
|
||||
interface HasSubtitle
|
||||
{
|
||||
/**
|
||||
* Get subtitle.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubtitle(): string;
|
||||
}
|
||||
@@ -13,6 +13,8 @@ enum SpecialPermission: string
|
||||
|
||||
case BYPASS_FEATURE_FLAGS = 'bypass feature flags';
|
||||
|
||||
case VIEW_FILAMENT = 'view filament';
|
||||
|
||||
case VIEW_HORIZON = 'view horizon';
|
||||
|
||||
case VIEW_NOVA = 'view nova';
|
||||
|
||||
@@ -5,16 +5,48 @@ declare(strict_types=1);
|
||||
namespace App\Enums\Models\Wiki;
|
||||
|
||||
use App\Concerns\Enums\LocalizesName;
|
||||
use App\Concerns\Filament\Enums\HasColorOrEmoji;
|
||||
|
||||
/**
|
||||
* Enum AnimeSeason.
|
||||
*/
|
||||
enum AnimeSeason: int
|
||||
{
|
||||
use HasColorOrEmoji;
|
||||
use LocalizesName;
|
||||
|
||||
case WINTER = 0;
|
||||
case SPRING = 1;
|
||||
case SUMMER = 2;
|
||||
case FALL = 3;
|
||||
|
||||
/**
|
||||
* Get the rgb color for the enum.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getColor(): string
|
||||
{
|
||||
return match ($this) {
|
||||
static::WINTER => '153, 204, 255',
|
||||
static::SPRING => '255, 192, 203',
|
||||
static::SUMMER => '255, 153, 51',
|
||||
static::FALL => '204, 102, 0',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unicode emoji for the enum.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEmoji(): string
|
||||
{
|
||||
return match ($this) {
|
||||
static::WINTER => "\u{2744}",
|
||||
static::SPRING => "\u{1F33C}",
|
||||
static::SUMMER => "\u{2600}",
|
||||
static::FALL => "\u{1F342}",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Discord;
|
||||
|
||||
use App\Actions\Discord\DiscordThreadAction as DiscordThreadActionAction;
|
||||
use App\Models\Wiki\Anime;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* Class DiscordThreadAction.
|
||||
*/
|
||||
class DiscordThreadAction extends Action
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->fillForm(fn (Anime $record): array => ['name' => $record->getName()]);
|
||||
|
||||
$this->action(fn (Anime $record, array $data) => (new DiscordThreadActionAction())->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
->label(__('filament.actions.discord.thread.name'))
|
||||
->helperText(__('filament.actions.discord.thread.help'))
|
||||
->required()
|
||||
->maxlength(100)
|
||||
->rules(['required', 'max:100']),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models;
|
||||
|
||||
use App\Actions\Models\AssignHashidsAction as AssignHashids;
|
||||
use App\Contracts\Models\HasHashids;
|
||||
use App\Models\BaseModel;
|
||||
use Exception;
|
||||
use Filament\Tables\Actions\Action;
|
||||
|
||||
/**
|
||||
* Class AssignHashidsAction.
|
||||
*/
|
||||
class AssignHashidsAction extends Action
|
||||
{
|
||||
protected ?string $connection = null;
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (BaseModel $record) => $this->handle($record));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param BaseModel $model
|
||||
* @return void
|
||||
*
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function handle(BaseModel $model): void
|
||||
{
|
||||
$action = new AssignHashids();
|
||||
|
||||
if ($model instanceof HasHashids) {
|
||||
try {
|
||||
$action->assign($model, $this->connection);
|
||||
} catch (Exception $e) {
|
||||
//$this->markAsFailed($model, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection.
|
||||
*
|
||||
* @param string|null $connection
|
||||
* @return static
|
||||
*/
|
||||
public function setConnection(?string $connection): static
|
||||
{
|
||||
$this->connection = $connection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Auth\Permission;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Auth\Role;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class GiveRoleAction.
|
||||
*/
|
||||
class GiveRoleAction extends Action
|
||||
{
|
||||
final public const FIELD_ROLE = 'role';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Permission $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param Permission $permission
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Permission $permission, array $data): void
|
||||
{
|
||||
$role = Role::findById(intval(Arr::get($data, self::FIELD_ROLE)));
|
||||
|
||||
$permission->assignRole($role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$roles = Role::all([Role::ATTRIBUTE_ID, Role::ATTRIBUTE_NAME])
|
||||
->keyBy(Role::ATTRIBUTE_ID)
|
||||
->map(fn (Role $role) => $role->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_ROLE)
|
||||
->label(__('filament.resources.singularLabel.role'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($roles)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Auth\Permission;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Auth\Role;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class RevokeRoleAction.
|
||||
*/
|
||||
class RevokeRoleAction extends Action
|
||||
{
|
||||
final public const FIELD_ROLE = 'role';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Permission $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param Permission $permission
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Permission $permission, array $data): void
|
||||
{
|
||||
$role = Role::findById(intval(Arr::get($data, self::FIELD_ROLE)));
|
||||
|
||||
$permission->removeRole($role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$roles = Role::all([Role::ATTRIBUTE_ID, Role::ATTRIBUTE_NAME])
|
||||
->keyBy(Role::ATTRIBUTE_ID)
|
||||
->map(fn (Role $role) => $role->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_ROLE)
|
||||
->label(__('filament.resources.singularLabel.role'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($roles)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Auth\Role;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Auth\Role;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class GivePermissionAction.
|
||||
*/
|
||||
class GivePermissionAction extends Action
|
||||
{
|
||||
final public const FIELD_PERMISSION = 'permission';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Role $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given model.
|
||||
*
|
||||
* @param Role $role
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Role $role, array $data): void
|
||||
{
|
||||
$permission = Permission::findById(intval(Arr::get($data, self::FIELD_PERMISSION)));
|
||||
|
||||
$role->givePermissionTo($permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$permissions = Permission::all([Permission::ATTRIBUTE_ID, Permission::ATTRIBUTE_NAME])
|
||||
->keyBy(Permission::ATTRIBUTE_ID)
|
||||
->map(fn (Permission $permission) => $permission->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_PERMISSION)
|
||||
->label(__('filament.resources.singularLabel.permission'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($permissions)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Auth\Role;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Auth\Role;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class RevokePermissionAction.
|
||||
*/
|
||||
class RevokePermissionAction extends Action
|
||||
{
|
||||
final public const FIELD_PERMISSION = 'permission';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Role $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given model.
|
||||
*
|
||||
* @param Role $role
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Role $role, array $data): void
|
||||
{
|
||||
$permission = Permission::findById(intval(Arr::get($data, self::FIELD_PERMISSION)));
|
||||
|
||||
$role->revokePermissionTo($permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$permissions = Permission::all([Permission::ATTRIBUTE_ID, Permission::ATTRIBUTE_NAME])
|
||||
->keyBy(Permission::ATTRIBUTE_ID)
|
||||
->map(fn (Permission $permission) => $permission->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_PERMISSION)
|
||||
->label(__('filament.resources.singularLabel.permission'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($permissions)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Auth\User;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Auth\User;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class GivePermissionAction.
|
||||
*/
|
||||
class GivePermissionAction extends Action
|
||||
{
|
||||
final public const FIELD_PERMISSION = 'permission';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (User $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given model.
|
||||
*
|
||||
* @param User $user
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(User $user, array $data): void
|
||||
{
|
||||
$permission = Permission::findById(intval(Arr::get($data, self::FIELD_PERMISSION)));
|
||||
|
||||
$user->givePermissionTo($permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$permissions = Permission::all([Permission::ATTRIBUTE_ID, Permission::ATTRIBUTE_NAME])
|
||||
->keyBy(Permission::ATTRIBUTE_ID)
|
||||
->map(fn (Permission $permission) => $permission->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_PERMISSION)
|
||||
->label(__('filament.resources.singularLabel.permission'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($permissions)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Auth\User;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Role;
|
||||
use App\Models\Auth\User;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class GiveRoleAction.
|
||||
*/
|
||||
class GiveRoleAction extends Action
|
||||
{
|
||||
final public const FIELD_ROLE = 'role';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (User $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param User $user
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(User $user, array $data): void
|
||||
{
|
||||
$role = Role::findById(intval(Arr::get($data, self::FIELD_ROLE)));
|
||||
|
||||
$user->assignRole($role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$roles = Role::all([Role::ATTRIBUTE_ID, Role::ATTRIBUTE_NAME])
|
||||
->keyBy(Role::ATTRIBUTE_ID)
|
||||
->map(fn (Role $role) => $role->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_ROLE)
|
||||
->label(__('filament.resources.singularLabel.role'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($roles)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Auth\User;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Auth\User;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class RevokePermissionAction.
|
||||
*/
|
||||
class RevokePermissionAction extends Action
|
||||
{
|
||||
final public const FIELD_PERMISSION = 'permission';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (User $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given model.
|
||||
*
|
||||
* @param User $user
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(User $user, array $data): void
|
||||
{
|
||||
$permission = Permission::findById(intval(Arr::get($data, self::FIELD_PERMISSION)));
|
||||
|
||||
$user->revokePermissionTo($permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$permissions = Permission::all([Permission::ATTRIBUTE_ID, Permission::ATTRIBUTE_NAME])
|
||||
->keyBy(Permission::ATTRIBUTE_ID)
|
||||
->map(fn (Permission $permission) => $permission->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_PERMISSION)
|
||||
->label(__('filament.resources.singularLabel.permission'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($permissions)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Auth\User;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Role;
|
||||
use App\Models\Auth\User;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class RevokeRoleAction.
|
||||
*/
|
||||
class RevokeRoleAction extends Action
|
||||
{
|
||||
final public const FIELD_ROLE = 'role';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (User $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param User $user
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(User $user, array $data): void
|
||||
{
|
||||
$role = Role::findById(intval(Arr::get($data, self::FIELD_ROLE)));
|
||||
|
||||
$user->removeRole($role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$roles = Role::all([Role::ATTRIBUTE_ID, Role::ATTRIBUTE_NAME])
|
||||
->keyBy(Role::ATTRIBUTE_ID)
|
||||
->map(fn (Role $role) => $role->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_ROLE)
|
||||
->label(__('filament.resources.singularLabel.role'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($roles)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki\Anime;
|
||||
|
||||
use App\Actions\Models\Wiki\Anime\AttachAnimeImageAction as AttachAnimeImageActionAction;
|
||||
use App\Filament\Actions\Models\Wiki\AttachImageAction;
|
||||
use App\Models\Wiki\Anime;
|
||||
|
||||
/**
|
||||
* Class AttachAnimeImageAction.
|
||||
*/
|
||||
class AttachAnimeImageAction extends AttachImageAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Anime $record, array $data) => (new AttachAnimeImageActionAction($this->facets))->handle($record, $data));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki\Anime;
|
||||
|
||||
use App\Actions\Models\Wiki\Anime\AttachAnimeResourceAction as AttachAnimeResourceActionAction;
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Filament\Actions\Models\Wiki\AttachResourceAction;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Rules\Wiki\Resource\AnimeResourceLinkFormatRule;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
/**
|
||||
* Class AttachAnimeResourceAction.
|
||||
*/
|
||||
class AttachAnimeResourceAction extends AttachResourceAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Anime $record, array $data) => (new AttachAnimeResourceActionAction($this->sites))->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the format validation rule.
|
||||
*
|
||||
* @param ResourceSite $site
|
||||
* @return ValidationRule
|
||||
*/
|
||||
protected function getFormatRule(ResourceSite $site): ValidationRule
|
||||
{
|
||||
return new AnimeResourceLinkFormatRule($site);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki\Anime;
|
||||
|
||||
use App\Actions\Models\BackfillAction;
|
||||
use App\Actions\Models\Wiki\Anime\BackfillAnimeOtherResourcesAction;
|
||||
use App\Actions\Models\Wiki\Anime\BackfillAnimeSynonymsAction;
|
||||
use App\Actions\Models\Wiki\Anime\Image\BackfillLargeCoverImageAction;
|
||||
use App\Actions\Models\Wiki\Anime\Image\BackfillSmallCoverImageAction;
|
||||
use App\Actions\Models\Wiki\Anime\Resource\BackfillAnidbResourceAction;
|
||||
use App\Actions\Models\Wiki\Anime\Resource\BackfillAnilistResourceAction;
|
||||
use App\Actions\Models\Wiki\Anime\Resource\BackfillAnnResourceAction;
|
||||
use App\Actions\Models\Wiki\Anime\Resource\BackfillKitsuResourceAction;
|
||||
use App\Actions\Models\Wiki\Anime\Resource\BackfillMalResourceAction;
|
||||
use App\Actions\Models\Wiki\Anime\Studio\BackfillAnimeStudiosAction;
|
||||
use App\Enums\Models\Wiki\ImageFacet;
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use App\Models\Wiki\Image;
|
||||
use Exception;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Notifications\Actions\Action as NotificationAction;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Sleep;
|
||||
|
||||
/**
|
||||
* Class BackfillAnimeAction.
|
||||
*/
|
||||
class BackfillAnimeAction extends Action implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
|
||||
final public const BACKFILL_ANIDB_RESOURCE = 'backfill_anidb_resource';
|
||||
final public const BACKFILL_ANILIST_RESOURCE = 'backfill_anilist_resource';
|
||||
final public const BACKFILL_ANN_RESOURCE = 'backfill_ann_resource';
|
||||
final public const BACKFILL_KITSU_RESOURCE = 'backfill_kitsu_resource';
|
||||
final public const BACKFILL_OTHER_RESOURCES = 'backfill_other_resources';
|
||||
final public const BACKFILL_LARGE_COVER = 'backfill_large_cover';
|
||||
final public const BACKFILL_MAL_RESOURCE = 'backfill_mal_resource';
|
||||
final public const BACKFILL_SMALL_COVER = 'backfill_small_cover';
|
||||
final public const BACKFILL_STUDIOS = 'backfill_studios';
|
||||
final public const BACKFILL_SYNONYMS = 'backfill_synonyms';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Anime $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param Anime $anime
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Anime $anime, array $fields): void
|
||||
{
|
||||
if ($anime->resources()->doesntExist()) {
|
||||
$this->fail(__('filament.actions.anime.backfill.message.resource_required_failure'));
|
||||
return;
|
||||
}
|
||||
|
||||
$actions = $this->getActions($fields, $anime);
|
||||
|
||||
try {
|
||||
foreach ($actions as $action) {
|
||||
$result = $action->handle();
|
||||
if ($result->hasFailed()) {
|
||||
Notification::make()
|
||||
->body($result->getMessage())
|
||||
->warning()
|
||||
->actions([
|
||||
NotificationAction::make('mark-as-read')
|
||||
->button()
|
||||
->markAsRead(),
|
||||
])
|
||||
->sendToDatabase(auth()->user());
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->fail($e);
|
||||
} finally {
|
||||
// Try not to upset third-party APIs
|
||||
Sleep::for(rand(3, 5))->second();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form|null
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): ?Form
|
||||
{
|
||||
$anime = $this->getRecord();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Section::make(__('filament.actions.anime.backfill.fields.resources.name'))
|
||||
->schema([
|
||||
Checkbox::make(self::BACKFILL_KITSU_RESOURCE)
|
||||
->label(__('filament.actions.anime.backfill.fields.resources.kitsu.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.resources.kitsu.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->resources()->where(ExternalResource::ATTRIBUTE_SITE, ResourceSite::KITSU->value)->doesntExist()),
|
||||
|
||||
Checkbox::make(self::BACKFILL_ANILIST_RESOURCE)
|
||||
->label(__('filament.actions.anime.backfill.fields.resources.anilist.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.resources.anilist.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->resources()->where(ExternalResource::ATTRIBUTE_SITE, ResourceSite::ANILIST->value)->doesntExist()),
|
||||
|
||||
Checkbox::make(self::BACKFILL_MAL_RESOURCE)
|
||||
->label(__('filament.actions.anime.backfill.fields.resources.mal.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.resources.mal.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->resources()->where(ExternalResource::ATTRIBUTE_SITE, ResourceSite::MAL->value)->doesntExist()),
|
||||
|
||||
Checkbox::make(self::BACKFILL_ANIDB_RESOURCE)
|
||||
->label(__('filament.actions.anime.backfill.fields.resources.anidb.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.resources.anidb.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->resources()->where(ExternalResource::ATTRIBUTE_SITE, ResourceSite::ANIDB->value)->doesntExist()),
|
||||
|
||||
Checkbox::make(self::BACKFILL_ANN_RESOURCE)
|
||||
->label(__('filament.actions.anime.backfill.fields.resources.ann.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.resources.ann.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->resources()->where(ExternalResource::ATTRIBUTE_SITE, ResourceSite::ANN->value)->doesntExist()),
|
||||
|
||||
Checkbox::make(self::BACKFILL_OTHER_RESOURCES)
|
||||
->label(__('filament.actions.anime.backfill.fields.resources.external_links.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.resources.external_links.help'))
|
||||
->default(fn () => $anime instanceof Anime),
|
||||
]),
|
||||
|
||||
Section::make(__('filament.actions.anime.backfill.fields.images.name'))
|
||||
->schema([
|
||||
Checkbox::make(self::BACKFILL_LARGE_COVER)
|
||||
->label(__('filament.actions.anime.backfill.fields.images.large_cover.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.images.large_cover.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->images()->where(Image::ATTRIBUTE_FACET, ImageFacet::COVER_LARGE->value)->doesntExist()),
|
||||
|
||||
Checkbox::make(self::BACKFILL_SMALL_COVER)
|
||||
->label(__('filament.actions.anime.backfill.fields.images.small_cover.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.images.small_cover.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->images()->where(Image::ATTRIBUTE_FACET, ImageFacet::COVER_SMALL->value)->doesntExist()),
|
||||
]),
|
||||
|
||||
Section::make(__('filament.actions.anime.backfill.fields.studios.name'))
|
||||
->schema([
|
||||
Checkbox::make(self::BACKFILL_STUDIOS)
|
||||
->label(__('filament.actions.anime.backfill.fields.studios.anime.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.studios.anime.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->studios()->doesntExist()),
|
||||
]),
|
||||
|
||||
Section::make(__('filament.actions.anime.backfill.fields.synonyms.name'))
|
||||
->schema([
|
||||
Checkbox::make(self::BACKFILL_STUDIOS)
|
||||
->label(__('filament.actions.anime.backfill.fields.synonyms.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.synonyms.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->animesynonyms()->count() === 0),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected actions for backfilling anime.
|
||||
*
|
||||
* @param array $fields
|
||||
* @param Anime $anime
|
||||
* @return BackfillAction[]
|
||||
*/
|
||||
protected function getActions(array $fields, Anime $anime): array
|
||||
{
|
||||
$actions = [];
|
||||
|
||||
foreach ($this->getActionMapping($anime) as $field => $action) {
|
||||
if (Arr::get($fields, $field) === true) {
|
||||
$actions[] = $action;
|
||||
}
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapping of actions to their form fields.
|
||||
*
|
||||
* @param Anime $anime
|
||||
* @return array<string, BackfillAction>
|
||||
*/
|
||||
protected function getActionMapping(Anime $anime): array
|
||||
{
|
||||
return [
|
||||
self::BACKFILL_KITSU_RESOURCE => new BackfillKitsuResourceAction($anime),
|
||||
self::BACKFILL_ANILIST_RESOURCE => new BackfillAnilistResourceAction($anime),
|
||||
self::BACKFILL_MAL_RESOURCE => new BackfillMalResourceAction($anime),
|
||||
self::BACKFILL_ANIDB_RESOURCE => new BackfillAnidbResourceAction($anime),
|
||||
self::BACKFILL_ANN_RESOURCE => new BackfillAnnResourceAction($anime),
|
||||
self::BACKFILL_OTHER_RESOURCES => new BackfillAnimeOtherResourcesAction($anime),
|
||||
self::BACKFILL_LARGE_COVER => new BackfillLargeCoverImageAction($anime),
|
||||
self::BACKFILL_SMALL_COVER => new BackfillSmallCoverImageAction($anime),
|
||||
self::BACKFILL_STUDIOS => new BackfillAnimeStudiosAction($anime),
|
||||
self::BACKFILL_SYNONYMS => new BackfillAnimeSynonymsAction($anime),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki\Artist;
|
||||
|
||||
use App\Actions\Models\Wiki\Artist\AttachArtistImageAction as AttachArtistImageActionAction;
|
||||
use App\Filament\Actions\Models\Wiki\AttachImageAction;
|
||||
use App\Models\Wiki\Artist;
|
||||
|
||||
/**
|
||||
* Class AttachArtistImageAction.
|
||||
*/
|
||||
class AttachArtistImageAction extends AttachImageAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Artist $record, array $data) => (new AttachArtistImageActionAction($this->facets))->handle($record, $data));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki\Artist;
|
||||
|
||||
use App\Actions\Models\Wiki\Artist\AttachArtistResourceAction as AttachArtistResourceActionAction;
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Filament\Actions\Models\Wiki\AttachResourceAction;
|
||||
use App\Models\Wiki\Artist;
|
||||
use App\Rules\Wiki\Resource\ArtistResourceLinkFormatRule;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
/**
|
||||
* Class AttachArtistResourceAction.
|
||||
*/
|
||||
class AttachArtistResourceAction extends AttachResourceAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Artist $record, array $data) => (new AttachArtistResourceActionAction($this->sites))->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the format validation rule.
|
||||
*
|
||||
* @param ResourceSite $site
|
||||
* @return ValidationRule
|
||||
*/
|
||||
protected function getFormatRule(ResourceSite $site): ValidationRule
|
||||
{
|
||||
return new ArtistResourceLinkFormatRule($site);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki;
|
||||
|
||||
use App\Enums\Models\Wiki\ImageFacet;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Artist;
|
||||
use App\Models\Wiki\Image;
|
||||
use App\Models\Wiki\Studio;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Actions\Action;
|
||||
|
||||
/**
|
||||
* Class AttachImageAction.
|
||||
*/
|
||||
abstract class AttachImageAction extends Action
|
||||
{
|
||||
protected array $facets = [];
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$fields = [];
|
||||
$model = $form->getRecord();
|
||||
|
||||
if ($model instanceof Anime || $model instanceof Artist || $model instanceof Studio) {
|
||||
foreach ($this->facets as $facet) {
|
||||
$images = $model->images();
|
||||
if ($images->where(Image::ATTRIBUTE_FACET, $facet->value)->exists()) continue;
|
||||
|
||||
$fields[] = FileUpload::make($facet->name)
|
||||
->label($facet->localize())
|
||||
->image()
|
||||
->storeFiles(false);
|
||||
}
|
||||
}
|
||||
|
||||
return $form
|
||||
->schema($fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facets available for the action.
|
||||
*
|
||||
* @param ImageFacet[] $facets
|
||||
* @return static
|
||||
*/
|
||||
public function facets(array $facets): static
|
||||
{
|
||||
$this->facets = $facets;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki;
|
||||
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Artist;
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use App\Models\Wiki\Song;
|
||||
use App\Models\Wiki\Studio;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
/**
|
||||
* Class AttachResourceAction.
|
||||
*/
|
||||
abstract class AttachResourceAction extends Action
|
||||
{
|
||||
protected array $sites = [];
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form|null
|
||||
*/
|
||||
public function getForm(Form $form): ?Form
|
||||
{
|
||||
$fields = [];
|
||||
$model = $this->getRecord();
|
||||
|
||||
foreach ($this->sites as $resourceSite) {
|
||||
if ($model instanceof Anime || $model instanceof Artist || $model instanceof Song || $model instanceof Studio) {
|
||||
$resources = $model->resources();
|
||||
if ($resources->where(ExternalResource::ATTRIBUTE_SITE, $resourceSite->value)->exists()) continue;
|
||||
}
|
||||
|
||||
$resourceSiteLower = strtolower($resourceSite->name);
|
||||
|
||||
$fields[] = TextInput::make($resourceSite->name)
|
||||
->label($resourceSite->localize())
|
||||
->helperText(__("filament.actions.models.wiki.attach_resource.fields.{$resourceSiteLower}.help"))
|
||||
->url()
|
||||
->maxLength(192)
|
||||
->rules(['max:192', $this->getFormatRule($resourceSite)]);
|
||||
}
|
||||
|
||||
return $form
|
||||
->schema($fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sites available for the action.
|
||||
*
|
||||
* @param ResourceSite[] $sites
|
||||
* @return static
|
||||
*/
|
||||
public function sites($sites): static
|
||||
{
|
||||
$this->sites = $sites;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the format validation rule.
|
||||
*
|
||||
* @param ResourceSite $site
|
||||
* @return ValidationRule
|
||||
*/
|
||||
abstract protected function getFormatRule(ResourceSite $site): ValidationRule;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki\Audio;
|
||||
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
|
||||
use App\Models\Wiki\Audio;
|
||||
use App\Models\Wiki\Video;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class AttachAudioToRelatedVideosAction.
|
||||
*/
|
||||
class AttachAudioToRelatedVideosAction extends Action implements ShouldQueue
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Audio $record) => $this->handle($record));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param Audio $audio
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Audio $audio): void
|
||||
{
|
||||
$video = $audio->videos()->first();
|
||||
|
||||
$video->animethemeentries()->each(function (AnimeThemeEntry $firstEntry) use ($audio) {
|
||||
$theme = $firstEntry->animetheme()->first();
|
||||
|
||||
$theme->animethemeentries()->each(function (AnimeThemeEntry $entry) use ($audio) {
|
||||
$entry->videos()->each(function (Video $video) use ($audio) {
|
||||
Log::info("Associating Audio '{$audio->filename}' with Video '{$video->filename}'");
|
||||
$video->audio()->associate($audio)->save();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki\Song;
|
||||
|
||||
use App\Actions\Models\Wiki\Song\AttachSongResourceAction as AttachSongResourceActionAction;
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Filament\Actions\Models\Wiki\AttachResourceAction;
|
||||
use App\Models\Wiki\Song;
|
||||
use App\Rules\Wiki\Resource\SongResourceLinkFormatRule;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
/**
|
||||
* Class AttachSongResourceAction.
|
||||
*/
|
||||
class AttachSongResourceAction extends AttachResourceAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Song $record, array $data) => (new AttachSongResourceActionAction($this->sites))->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the format validation rule.
|
||||
*
|
||||
* @param ResourceSite $site
|
||||
* @return ValidationRule
|
||||
*/
|
||||
protected function getFormatRule(ResourceSite $site): ValidationRule
|
||||
{
|
||||
return new SongResourceLinkFormatRule($site);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki\Studio;
|
||||
|
||||
use App\Actions\Models\Wiki\Studio\AttachStudioImageAction as AttachStudioImageActionAction;
|
||||
use App\Filament\Actions\Models\Wiki\AttachImageAction;
|
||||
use App\Models\Wiki\Studio;
|
||||
|
||||
/**
|
||||
* Class AttachStudioImageAction.
|
||||
*/
|
||||
class AttachStudioImageAction extends AttachImageAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Studio $record, array $data) => (new AttachStudioImageActionAction($this->facets))->handle($record, $data));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki\Studio;
|
||||
|
||||
use App\Actions\Models\Wiki\Studio\AttachStudioResourceAction as AttachStudioResourceActionAction;
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Filament\Actions\Models\Wiki\AttachResourceAction;
|
||||
use App\Models\Wiki\Studio;
|
||||
use App\Rules\Wiki\Resource\StudioResourceLinkFormatRule;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
/**
|
||||
* Class AttachStudioResourceAction.
|
||||
*/
|
||||
class AttachStudioResourceAction extends AttachResourceAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Studio $record, array $data) => (new AttachStudioResourceActionAction($this->sites))->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the format validation rule.
|
||||
*
|
||||
* @param ResourceSite $site
|
||||
* @return ValidationRule
|
||||
*/
|
||||
protected function getFormatRule(ResourceSite $site): ValidationRule
|
||||
{
|
||||
return new StudioResourceLinkFormatRule($site);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki\Studio;
|
||||
|
||||
use App\Actions\Models\BackfillAction;
|
||||
use App\Actions\Models\Wiki\Studio\Image\BackfillLargeCoverImageAction;
|
||||
use App\Enums\Models\Wiki\ImageFacet;
|
||||
use App\Models\Wiki\Image;
|
||||
use App\Models\Wiki\Studio;
|
||||
use Exception;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Notifications\Actions\Action as NotificationAction;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Sleep;
|
||||
|
||||
/**
|
||||
* Class BackfillStudioAction.
|
||||
*/
|
||||
class BackfillStudioAction extends Action implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
|
||||
final public const BACKFILL_LARGE_COVER = 'backfill_large_cover';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Studio $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param Studio $studio
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Studio $studio, array $fields): void
|
||||
{
|
||||
if ($studio->resources()->doesntExist()) {
|
||||
//$this->markAsFailed($studio, __('filament.actions.studio.backfill.message.resource_required_failure'));
|
||||
return;
|
||||
}
|
||||
|
||||
$actions = $this->getActions($fields, $studio);
|
||||
|
||||
try {
|
||||
foreach ($actions as $action) {
|
||||
$result = $action->handle();
|
||||
if ($result->hasFailed()) {
|
||||
Notification::make()
|
||||
->body($result->getMessage())
|
||||
->warning()
|
||||
->actions([
|
||||
NotificationAction::make('mark-as-read')
|
||||
->button()
|
||||
->markAsRead(),
|
||||
])
|
||||
->sendToDatabase(auth()->user());
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
//$this->markAsFailed($studio, $e);
|
||||
} finally {
|
||||
// Try not to upset third-party APIs
|
||||
Sleep::for(rand(3, 5))->second();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$studio = $this->getRecord();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Section::make(__('filament.actions.studio.backfill.fields.images.name'))
|
||||
->schema([
|
||||
Checkbox::make(self::BACKFILL_LARGE_COVER)
|
||||
->label(__('filament.actions.studio.backfill.fields.images.large_cover.name'))
|
||||
->helperText(__('filament.actions.studio.backfill.fields.images.large_cover.help'))
|
||||
->default(fn () => $studio instanceof Studio && $studio->images()->where(Image::ATTRIBUTE_FACET, ImageFacet::COVER_LARGE->value)->doesntExist()),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected actions for backfilling studios.
|
||||
*
|
||||
* @param array $fields
|
||||
* @param Studio $studio
|
||||
* @return BackfillAction[]
|
||||
*/
|
||||
protected function getActions(array $fields, Studio $studio): array
|
||||
{
|
||||
$actions = [];
|
||||
|
||||
foreach ($this->getActionMapping($studio) as $field => $action) {
|
||||
if (Arr::get($fields, $field) === true) {
|
||||
$actions[] = $action;
|
||||
}
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapping of actions to their form fields.
|
||||
*
|
||||
* @param Studio $studio
|
||||
* @return array<string, BackfillAction>
|
||||
*/
|
||||
protected function getActionMapping(Studio $studio): array
|
||||
{
|
||||
return [
|
||||
self::BACKFILL_LARGE_COVER => new BackfillLargeCoverImageAction($studio),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Models\Wiki\Video;
|
||||
|
||||
use App\Actions\Models\Wiki\Video\Audio\BackfillAudioAction as BackfillAudio;
|
||||
use App\Enums\Actions\Models\Wiki\Video\DeriveSourceVideo;
|
||||
use App\Enums\Actions\Models\Wiki\Video\OverwriteAudio;
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Wiki\Video;
|
||||
use Exception;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Notifications\Actions\Action as NotificationAction;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
/**
|
||||
* Class BackfillAudioAction.
|
||||
*/
|
||||
class BackfillAudioAction extends Action implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
|
||||
final public const DERIVE_SOURCE_VIDEO = 'derive_source_video';
|
||||
final public const OVERWRITE_AUDIO = 'overwrite_audio';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Video $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param Video $video
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Video $video, array $data): void
|
||||
{
|
||||
$deriveSourceVideo = DeriveSourceVideo::from(intval(Arr::get($data, self::DERIVE_SOURCE_VIDEO)));
|
||||
$overwriteAudio = OverwriteAudio::from(intval(Arr::get($data, self::OVERWRITE_AUDIO)));
|
||||
|
||||
$action = new BackfillAudio($video, $deriveSourceVideo, $overwriteAudio);
|
||||
|
||||
try {
|
||||
$result = $action->handle();
|
||||
if ($result->hasFailed()) {
|
||||
Notification::make()
|
||||
->body($result->getMessage())
|
||||
->warning()
|
||||
->actions([
|
||||
NotificationAction::make('mark-as-read')
|
||||
->button()
|
||||
->markAsRead(),
|
||||
])
|
||||
->sendToDatabase(auth()->user());
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
//$this->markAsFailed($video, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::DERIVE_SOURCE_VIDEO)
|
||||
->label(__('filament.actions.video.backfill.fields.derive_source.name'))
|
||||
->options(DeriveSourceVideo::asSelectArray())
|
||||
->rules(['required', new Enum(DeriveSourceVideo::class)])
|
||||
->default(DeriveSourceVideo::YES->value)
|
||||
->helperText(__('filament.actions.video.backfill.fields.derive_source.help')),
|
||||
|
||||
Select::make(self::OVERWRITE_AUDIO)
|
||||
->label(__('filament.actions.video.backfill.fields.overwrite.name'))
|
||||
->options(OverwriteAudio::asSelectArray())
|
||||
->rules(['required', new Enum(OverwriteAudio::class)])
|
||||
->default(OverwriteAudio::NO->value)
|
||||
->helperText(__('filament.actions.video.backfill.fields.overwrite.help')),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Storage\Base;
|
||||
|
||||
use App\Actions\Storage\Base\DeleteAction as BaseDeleteAction;
|
||||
use App\Filament\Actions\Storage\StorageAction;
|
||||
use App\Models\BaseModel;
|
||||
|
||||
/**
|
||||
* Class DeleteAction.
|
||||
*/
|
||||
abstract class DeleteAction extends StorageAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param BaseModel $model
|
||||
* @param array $fields
|
||||
* @return BaseDeleteAction
|
||||
*/
|
||||
abstract protected function storageAction(BaseModel $model, array $fields): BaseDeleteAction;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Storage\Base;
|
||||
|
||||
use App\Actions\Storage\Base\MoveAction as BaseMoveAction;
|
||||
use App\Contracts\Storage\InteractsWithDisk;
|
||||
use App\Filament\Actions\Storage\StorageAction;
|
||||
use App\Models\BaseModel;
|
||||
use App\Rules\Storage\StorageFileDirectoryExistsRule;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
* Class MoveAction.
|
||||
*/
|
||||
abstract class MoveAction extends StorageAction implements InteractsWithDisk
|
||||
{
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$defaultPath = $this->defaultPath();
|
||||
|
||||
$fs = Storage::disk($this->disk());
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
TextInput::make('path')
|
||||
->label(__('filament.actions.storage.move.fields.path.name'))
|
||||
->helperText(__('filament.actions.storage.move.fields.path.help'))
|
||||
->required()
|
||||
->rules(['required', 'string', 'doesnt_start_with:/', "ends_with:{$this->allowedFileExtension()}", new StorageFileDirectoryExistsRule($fs)])
|
||||
->default(fn () => $defaultPath),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param BaseModel $model
|
||||
* @param array $fields
|
||||
* @return BaseMoveAction
|
||||
*/
|
||||
abstract protected function storageAction(BaseModel $model, array $fields): BaseMoveAction;
|
||||
|
||||
/**
|
||||
* Resolve the default value for the path field.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
abstract protected function defaultPath(): ?string;
|
||||
|
||||
/**
|
||||
* The file extension that the path must end with.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function allowedFileExtension(): string;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Storage;
|
||||
|
||||
use App\Contracts\Actions\Storage\StorageAction as BaseStorageAction;
|
||||
use App\Models\BaseModel;
|
||||
use Filament\Tables\Actions\Action;
|
||||
|
||||
/**
|
||||
* Class StorageAction.
|
||||
*/
|
||||
abstract class StorageAction extends Action
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (BaseModel $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param BaseModel $model
|
||||
* @param array $fields
|
||||
* @return BaseStorageAction
|
||||
*/
|
||||
abstract protected function storageAction(BaseModel $model, array $fields): BaseStorageAction;
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param BaseModel $model
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
public function handle(BaseModel $model, array $fields): void
|
||||
{
|
||||
$action = $this->storageAction($model, $fields);
|
||||
|
||||
$storageResults = $action->handle();
|
||||
|
||||
$storageResults->toLog();
|
||||
|
||||
$action->then($storageResults);
|
||||
|
||||
$actionResult = $storageResults->toActionResult();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Storage\Wiki\Audio;
|
||||
|
||||
use App\Actions\Storage\Wiki\Audio\DeleteAudioAction as DeleteAudio;
|
||||
use App\Filament\Actions\Storage\Base\DeleteAction;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Audio;
|
||||
|
||||
/**
|
||||
* Class DeleteAudioAction.
|
||||
*/
|
||||
class DeleteAudioAction extends DeleteAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param Audio $audio
|
||||
* @param array $fields
|
||||
* @return DeleteAudio
|
||||
*/
|
||||
protected function storageAction(BaseModel $audio, array $fields): DeleteAudio
|
||||
{
|
||||
return new DeleteAudio($audio);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Storage\Wiki\Audio;
|
||||
|
||||
use App\Actions\Storage\Wiki\Audio\MoveAudioAction as MoveAudio;
|
||||
use App\Constants\Config\AudioConstants;
|
||||
use App\Models\Wiki\Audio;
|
||||
use App\Filament\Actions\Storage\Base\MoveAction;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class MoveAudioAction.
|
||||
*/
|
||||
class MoveAudioAction extends MoveAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param Audio $audio
|
||||
* @param array $fields
|
||||
* @return MoveAudio
|
||||
*/
|
||||
protected function storageAction(BaseModel $audio, array $fields): MoveAudio
|
||||
{
|
||||
/** @var string $path */
|
||||
$path = Arr::get($fields, 'path');
|
||||
|
||||
return new MoveAudio($audio, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(AudioConstants::DEFAULT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the default value for the path field.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function defaultPath(): ?string
|
||||
{
|
||||
$audio = $this->getRecord();
|
||||
|
||||
return $audio instanceof Audio
|
||||
? $audio->path
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The file extension that the path must end with.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function allowedFileExtension(): string
|
||||
{
|
||||
return '.ogg';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Storage\Wiki\Video;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\DeleteVideoAction as DeleteVideo;
|
||||
use App\Filament\Actions\Storage\Base\DeleteAction;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Video;
|
||||
|
||||
/**
|
||||
* Class DeleteVideoAction.
|
||||
*/
|
||||
class DeleteVideoAction extends DeleteAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param Video $video
|
||||
* @param array $fields
|
||||
* @return DeleteVideo
|
||||
*/
|
||||
protected function storageAction(BaseModel $video, array $fields): DeleteVideo
|
||||
{
|
||||
return new DeleteVideo($video);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Storage\Wiki\Video;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\MoveVideoAction as MoveVideo;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Filament\Actions\Storage\Base\MoveAction;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class MoveVideoAction.
|
||||
*/
|
||||
class MoveVideoAction extends MoveAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param Video $video
|
||||
* @param array $fields
|
||||
* @return MoveVideo
|
||||
*/
|
||||
protected function storageAction(BaseModel $video, array $fields): MoveVideo
|
||||
{
|
||||
/** @var string $path */
|
||||
$path = Arr::get($fields, 'path');
|
||||
|
||||
return new MoveVideo($video, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the default value for the path field.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function defaultPath(): ?string
|
||||
{
|
||||
$video = $this->getRecord();
|
||||
|
||||
return $video instanceof Video
|
||||
? $video->path
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The file extension that the path must end with.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function allowedFileExtension(): string
|
||||
{
|
||||
return '.webm';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\Script\DeleteScriptAction as DeleteScript;
|
||||
use App\Filament\Actions\Storage\Base\DeleteAction;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
|
||||
/**
|
||||
* Class DeleteScriptAction.
|
||||
*/
|
||||
class DeleteScriptAction extends DeleteAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
* @param array $fields
|
||||
* @return DeleteScript
|
||||
*/
|
||||
protected function storageAction(BaseModel $script, array $fields): DeleteScript
|
||||
{
|
||||
return new DeleteScript($script);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\Script\MoveScriptAction as MoveScript;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Filament\Actions\Storage\Base\MoveAction;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class MoveScriptAction.
|
||||
*/
|
||||
class MoveScriptAction extends MoveAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
* @param array $fields
|
||||
* @return MoveScript
|
||||
*/
|
||||
protected function storageAction(BaseModel $script, array $fields): MoveScript
|
||||
{
|
||||
/** @var string $path */
|
||||
$path = Arr::get($fields, 'path');
|
||||
|
||||
return new MoveScript($script, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the default value for the path field.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function defaultPath(): ?string
|
||||
{
|
||||
$script = $this->getRecord();
|
||||
|
||||
return $script instanceof VideoScript
|
||||
? $script->path
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The file extension that the path must end with.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function allowedFileExtension(): string
|
||||
{
|
||||
return '.txt';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Components\Columns;
|
||||
|
||||
use App\Filament\Resources\BaseRelationManager;
|
||||
use App\Filament\Resources\BaseResource;
|
||||
use App\Models\BaseModel;
|
||||
use Filament\Support\Enums\FontWeight;
|
||||
use Filament\Tables\Columns\TextColumn as ColumnsTextColumn;
|
||||
|
||||
/**
|
||||
* Class TextColumn.
|
||||
*/
|
||||
class TextColumn extends ColumnsTextColumn
|
||||
{
|
||||
/**
|
||||
* Used for column relationships.
|
||||
*
|
||||
* @param class-string<BaseResource> $resourceRelated
|
||||
* @param string $relation
|
||||
* @param bool|null $shouldUseName
|
||||
* @return static
|
||||
*/
|
||||
public function urlToRelated(string $resourceRelated, string $relation, ?bool $shouldUseName = false): static
|
||||
{
|
||||
return $this
|
||||
->weight(FontWeight::SemiBold)
|
||||
->html()
|
||||
->hiddenOn(BaseRelationManager::class)
|
||||
->url(function (BaseModel $record) use ($resourceRelated, $relation, $shouldUseName) {
|
||||
foreach (explode('.', $relation) as $element) {
|
||||
$record = $record->$element;
|
||||
}
|
||||
|
||||
$this->formatStateUsing(function ($state) use ($shouldUseName, $record) {
|
||||
$name = $shouldUseName ? $record->getName() : $state;
|
||||
return "<p style='color: rgb(64, 184, 166);'>{$name}</p>";
|
||||
});
|
||||
|
||||
return $record !== null ? (new $resourceRelated)::getUrl('edit', ['record' => $record]) : null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the column copyable.
|
||||
*
|
||||
* @param bool $condition
|
||||
* @return static
|
||||
*/
|
||||
public function copyableWithMessage(bool $condition = true): static
|
||||
{
|
||||
return $this
|
||||
->copyable($condition)
|
||||
->copyMessage(__('filament.actions.base.copied'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Components\Fields;
|
||||
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\BaseModel;
|
||||
use Filament\Forms\Components\Select as ComponentsSelect;
|
||||
use Laravel\Scout\Searchable;
|
||||
|
||||
/**
|
||||
* Class Select.
|
||||
*/
|
||||
class Select extends ComponentsSelect
|
||||
{
|
||||
/**
|
||||
* Use laravel scout to make fields searchable.
|
||||
*
|
||||
* @param class-string<BaseModel> $model
|
||||
* @param string|null $loadRelation
|
||||
* @return static
|
||||
*/
|
||||
public function useScout(string $model, ?string $loadRelation = null): static
|
||||
{
|
||||
if (in_array(Searchable::class, class_uses_recursive($model))) {
|
||||
return $this
|
||||
->allowHtml()
|
||||
->searchable()
|
||||
->getSearchResultsUsing(function (string $search) use ($model, $loadRelation) {
|
||||
return (new $model)::search($search)
|
||||
->get()
|
||||
->load($loadRelation ?? [])
|
||||
->mapWithKeys(fn (BaseModel $model) => [$model->getKey() => static::getSearchLabelWithBlade($model)])
|
||||
->toArray();
|
||||
});
|
||||
}
|
||||
|
||||
return $this->searchable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the blade to make the results.
|
||||
*
|
||||
* @param BaseModel|User $model
|
||||
* @return string
|
||||
*/
|
||||
public static function getSearchLabelWithBlade(BaseModel|User $model): string
|
||||
{
|
||||
return view('filament.components.select')
|
||||
->with('name', $model->getName())
|
||||
->with('subtitle', $model->getSubtitle())
|
||||
->with('image', $model instanceof User ? $model->getFilamentAvatarUrl() : null)
|
||||
->render();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Components\Filters;
|
||||
|
||||
use App\Enums\Http\Api\Filter\ComparisonOperator;
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Tables\Filters\Filter;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class DateFilter.
|
||||
*/
|
||||
class DateFilter extends Filter
|
||||
{
|
||||
protected string $attribute = '';
|
||||
protected string $fromLabel = '';
|
||||
protected string $toLabel = '';
|
||||
|
||||
/**
|
||||
* Get the attribute used for filter.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return static
|
||||
*/
|
||||
public function attribute(string $attribute): static
|
||||
{
|
||||
$this->attribute = $attribute;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label for the filters.
|
||||
*
|
||||
* @param string $fromLabel
|
||||
* @param string $toLabel
|
||||
* @return static
|
||||
*/
|
||||
public function labels(string $fromLabel, string $toLabel): static
|
||||
{
|
||||
$this->fromLabel = $fromLabel;
|
||||
$this->toLabel = $toLabel;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form for the filter.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFormSchema(): array
|
||||
{
|
||||
return [
|
||||
DatePicker::make($this->attribute.'_'.'from')
|
||||
->label($this->fromLabel),
|
||||
|
||||
DatePicker::make($this->attribute.'_'.'to')
|
||||
->label($this->toLabel),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the query used to the filter.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param array $data
|
||||
* @return Builder
|
||||
*/
|
||||
public function applyToBaseQuery(Builder $query, array $data = []): Builder
|
||||
{
|
||||
return $query
|
||||
->when(
|
||||
Arr::get($data, $this->attribute.'_'.'from'),
|
||||
fn (Builder $query, $date): Builder => $query->whereDate($this->attribute, ComparisonOperator::GTE->value, $date),
|
||||
)
|
||||
->when(
|
||||
Arr::get($data, $this->attribute.'_'.'to'),
|
||||
fn (Builder $query, $date): Builder => $query->whereDate($this->attribute, ComparisonOperator::LTE->value, $date),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Components\Filters;
|
||||
|
||||
use App\Enums\Http\Api\Filter\ComparisonOperator;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Tables\Filters\Filter;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class NumberFilter.
|
||||
*/
|
||||
class NumberFilter extends Filter
|
||||
{
|
||||
protected string $attribute = '';
|
||||
protected string $fromLabel = '';
|
||||
protected string $toLabel = '';
|
||||
|
||||
/**
|
||||
* Get the attribute used for filter.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return static
|
||||
*/
|
||||
public function attribute(string $attribute): static
|
||||
{
|
||||
$this->attribute = $attribute;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label for the filters.
|
||||
*
|
||||
* @param string $fromLabel
|
||||
* @param string $toLabel
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function labels(string $fromLabel, string $toLabel): static
|
||||
{
|
||||
$this->fromLabel = $fromLabel;
|
||||
$this->toLabel = $toLabel;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form for the filter.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFormSchema(): array
|
||||
{
|
||||
return [
|
||||
TextInput::make($this->attribute.'_'.'from')
|
||||
->label($this->fromLabel)
|
||||
->integer(),
|
||||
|
||||
TextInput::make($this->attribute.'_'.'to')
|
||||
->label($this->toLabel)
|
||||
->integer(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the query used to the filter.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param array $data
|
||||
* @return Builder
|
||||
*/
|
||||
public function applyToBaseQuery(Builder $query, array $data = []): Builder
|
||||
{
|
||||
return $query
|
||||
->when(
|
||||
Arr::get($data, $this->attribute.'_'.'from'),
|
||||
fn (Builder $query, $value): Builder => $query->where($this->attribute, ComparisonOperator::GTE->value, $value),
|
||||
)
|
||||
->when(
|
||||
Arr::get($data, $this->attribute.'_'.'to'),
|
||||
fn (Builder $query, $value): Builder => $query->where($this->attribute, ComparisonOperator::LTE->value, $value),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Components\Filters;
|
||||
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Tables\Filters\Filter;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class TextFilter.
|
||||
*/
|
||||
class TextFilter extends Filter
|
||||
{
|
||||
protected string $attribute = '';
|
||||
|
||||
/**
|
||||
* Get the attribute used for filter.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return static
|
||||
*/
|
||||
public function attribute(string $attribute): static
|
||||
{
|
||||
$this->attribute = $attribute;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form for the filter.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFormSchema(): array
|
||||
{
|
||||
return [
|
||||
TextInput::make($this->attribute)
|
||||
->label($this->label),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the query used to the filter.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param array $data
|
||||
* @return Builder
|
||||
*/
|
||||
public function applyToBaseQuery(Builder $query, array $data = []): Builder
|
||||
{
|
||||
return $query->where($this->attribute, Arr::get($data, $this->attribute));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Components\Infolist;
|
||||
|
||||
use App\Filament\Resources\BaseResource;
|
||||
use App\Models\BaseModel;
|
||||
use Filament\Infolists\Components\TextEntry as ComponentsTextEntry;
|
||||
use Filament\Support\Enums\FontWeight;
|
||||
|
||||
/**
|
||||
* Class TextEntry.
|
||||
*/
|
||||
class TextEntry extends ComponentsTextEntry
|
||||
{
|
||||
/**
|
||||
* Used for entry relationships.
|
||||
*
|
||||
* @param class-string<BaseResource>|string $resourceRelated
|
||||
* @param string $relation
|
||||
* @param bool|null $shouldUseName
|
||||
* @return static
|
||||
*/
|
||||
public function urlToRelated(string $resourceRelated, string $relation, ?bool $shouldUseName = false): static
|
||||
{
|
||||
return $this
|
||||
->weight(FontWeight::SemiBold)
|
||||
->html()
|
||||
->url(function (BaseModel $record) use ($resourceRelated, $relation, $shouldUseName) {
|
||||
foreach (explode('.', $relation) as $element) {
|
||||
$record = $record->$element;
|
||||
}
|
||||
|
||||
$this->formatStateUsing(function ($state) use ($shouldUseName, $record) {
|
||||
$name = $shouldUseName ? $record->getName() : $state;
|
||||
return "<p style='color: rgb(64, 184, 166);'>{$name}</p>";
|
||||
});
|
||||
|
||||
return $record !== null ? (new $resourceRelated)::getUrl('edit', ['record' => $record]) : null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the entry copyable.
|
||||
*
|
||||
* @param bool $condition
|
||||
* @return static
|
||||
*/
|
||||
public function copyableWithMessage(bool $condition = true): static
|
||||
{
|
||||
return $this
|
||||
->copyable($condition)
|
||||
->copyMessage(__('filament.actions.base.copied'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Dashboards;
|
||||
|
||||
use App\Filament\Widgets\Auth\UserChart;
|
||||
use App\Filament\Widgets\List\PlaylistChart;
|
||||
use App\Filament\Widgets\List\PlaylistTrackChart;
|
||||
use App\Models\Auth\User;
|
||||
|
||||
/**
|
||||
* Class AdminDashboard.
|
||||
*/
|
||||
class AdminDashboard extends BaseDashboard
|
||||
{
|
||||
/**
|
||||
* Get the slug used to the dashboard route path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getSlug(): string
|
||||
{
|
||||
return 'admin';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user can access the dashboard.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return User::find(auth()->user()->id)->hasRole('Admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displayed label for the dashboard.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return __('filament.dashboards.label.admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon for the dashboard.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getNavigationIcon(): string
|
||||
{
|
||||
return __('filament.dashboards.icon.admin');;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the widgets available for the dashboard.
|
||||
*
|
||||
* @return class-string[]
|
||||
*/
|
||||
public function getWidgets(): array
|
||||
{
|
||||
return [
|
||||
UserChart::class,
|
||||
PlaylistChart::class,
|
||||
PlaylistTrackChart::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Dashboards;
|
||||
|
||||
use Filament\Pages\Dashboard;
|
||||
|
||||
/**
|
||||
* Class BaseDashboard.
|
||||
*/
|
||||
abstract class BaseDashboard extends Dashboard
|
||||
{
|
||||
/**
|
||||
* Get the route path for the dashboard.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getRoutePath(): string
|
||||
{
|
||||
return 'dashboards/'.static::getSlug();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title for the dashboard.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return static::getNavigationLabel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Dashboards;
|
||||
|
||||
use App\Filament\Widgets\Wiki\Anime\AnimeChart;
|
||||
use App\Filament\Widgets\Wiki\Artist\ArtistChart;
|
||||
use App\Filament\Widgets\Wiki\Series\SeriesChart;
|
||||
use App\Filament\Widgets\Wiki\Video\VideoChart;
|
||||
|
||||
/**
|
||||
* Class WikiDashboard.
|
||||
*/
|
||||
class WikiDashboard extends BaseDashboard
|
||||
{
|
||||
/**
|
||||
* Get the slug used to the dashboard route path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getSlug(): string
|
||||
{
|
||||
return 'wiki';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displayed label for the dashboard.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return __('filament.dashboards.label.wiki');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon for the dashboard.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getNavigationIcon(): string
|
||||
{
|
||||
return __('filament.dashboards.icon.wiki');;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the widgets available for the dashboard.
|
||||
*
|
||||
* @return class-string[]
|
||||
*/
|
||||
public function getWidgets(): array
|
||||
{
|
||||
return [
|
||||
AnimeChart::class,
|
||||
ArtistChart::class,
|
||||
SeriesChart::class,
|
||||
VideoChart::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Discord;
|
||||
|
||||
use App\Actions\Discord\DiscordThreadAction as DiscordThreadActionAction;
|
||||
use App\Models\Wiki\Anime;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\Action;
|
||||
|
||||
/**
|
||||
* Class DiscordThreadAction.
|
||||
*/
|
||||
class DiscordThreadHeaderAction extends Action
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->fillForm(fn (Anime $record): array => ['name' => $record->getName()]);
|
||||
|
||||
$this->action(fn (Anime $record, array $data) => (new DiscordThreadActionAction())->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
->label(__('filament.actions.discord.thread.name'))
|
||||
->helperText(__('filament.actions.discord.thread.help'))
|
||||
->required()
|
||||
->maxlength(100)
|
||||
->rules(['required', 'max:100']),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models;
|
||||
|
||||
use App\Actions\Models\AssignHashidsAction as AssignHashids;
|
||||
use App\Contracts\Models\HasHashids;
|
||||
use App\Models\BaseModel;
|
||||
use Exception;
|
||||
use Filament\Actions\Action;
|
||||
|
||||
/**
|
||||
* Class AssignHashidsHeaderAction.
|
||||
*/
|
||||
class AssignHashidsHeaderAction extends Action
|
||||
{
|
||||
protected ?string $connection = null;
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (BaseModel $record) => $this->handle($record));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param BaseModel $model
|
||||
* @return void
|
||||
*
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function handle(BaseModel $model): void
|
||||
{
|
||||
$action = new AssignHashids();
|
||||
|
||||
if ($model instanceof HasHashids) {
|
||||
try {
|
||||
$action->assign($model, $this->connection);
|
||||
} catch (Exception $e) {
|
||||
//$this->markAsFailed($model, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection.
|
||||
*
|
||||
* @param string|null $connection
|
||||
* @return static
|
||||
*/
|
||||
public function setConnection(?string $connection): static
|
||||
{
|
||||
$this->connection = $connection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Auth\Role;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Auth\Role;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class GivePermissionHeaderAction.
|
||||
*/
|
||||
class GivePermissionHeaderAction extends Action
|
||||
{
|
||||
final public const FIELD_PERMISSION = 'permission';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Role $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given model.
|
||||
*
|
||||
* @param Role $role
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Role $role, array $data): void
|
||||
{
|
||||
$permission = Permission::findById(intval(Arr::get($data, self::FIELD_PERMISSION)));
|
||||
|
||||
$role->givePermissionTo($permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$permissions = Permission::all([Permission::ATTRIBUTE_ID, Permission::ATTRIBUTE_NAME])
|
||||
->keyBy(Permission::ATTRIBUTE_ID)
|
||||
->map(fn (Permission $permission) => $permission->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_PERMISSION)
|
||||
->label(__('filament.resources.singularLabel.permission'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($permissions)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Auth\Role;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Auth\Role;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class RevokePermissionHeaderAction.
|
||||
*/
|
||||
class RevokePermissionHeaderAction extends Action
|
||||
{
|
||||
final public const FIELD_PERMISSION = 'permission';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Role $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given model.
|
||||
*
|
||||
* @param Role $role
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Role $role, array $data): void
|
||||
{
|
||||
$permission = Permission::findById(intval(Arr::get($data, self::FIELD_PERMISSION)));
|
||||
|
||||
$role->revokePermissionTo($permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$permissions = Permission::all([Permission::ATTRIBUTE_ID, Permission::ATTRIBUTE_NAME])
|
||||
->keyBy(Permission::ATTRIBUTE_ID)
|
||||
->map(fn (Permission $permission) => $permission->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_PERMISSION)
|
||||
->label(__('filament.resources.singularLabel.permission'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($permissions)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Auth\User;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Auth\User;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class GivePermissionHeaderAction.
|
||||
*/
|
||||
class GivePermissionHeaderAction extends Action
|
||||
{
|
||||
final public const FIELD_PERMISSION = 'permission';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (User $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given model.
|
||||
*
|
||||
* @param User $user
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(User $user, array $data): void
|
||||
{
|
||||
$permission = Permission::findById(intval(Arr::get($data, self::FIELD_PERMISSION)));
|
||||
|
||||
$user->givePermissionTo($permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$permissions = Permission::all([Permission::ATTRIBUTE_ID, Permission::ATTRIBUTE_NAME])
|
||||
->keyBy(Permission::ATTRIBUTE_ID)
|
||||
->map(fn (Permission $permission) => $permission->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_PERMISSION)
|
||||
->label(__('filament.resources.singularLabel.permission'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($permissions)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Auth\User;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Role;
|
||||
use App\Models\Auth\User;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class GiveRoleHeaderAction.
|
||||
*/
|
||||
class GiveRoleHeaderAction extends Action
|
||||
{
|
||||
final public const FIELD_ROLE = 'role';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (User $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param User $user
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(User $user, array $data): void
|
||||
{
|
||||
$role = Role::findById(intval(Arr::get($data, self::FIELD_ROLE)));
|
||||
|
||||
$user->assignRole($role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$roles = Role::all([Role::ATTRIBUTE_ID, Role::ATTRIBUTE_NAME])
|
||||
->keyBy(Role::ATTRIBUTE_ID)
|
||||
->map(fn (Role $role) => $role->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_ROLE)
|
||||
->label(__('filament.resources.singularLabel.role'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($roles)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Auth\User;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Auth\User;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class RevokePermissionHeaderAction.
|
||||
*/
|
||||
class RevokePermissionHeaderAction extends Action
|
||||
{
|
||||
final public const FIELD_PERMISSION = 'permission';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (User $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given model.
|
||||
*
|
||||
* @param User $user
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(User $user, array $data): void
|
||||
{
|
||||
$permission = Permission::findById(intval(Arr::get($data, self::FIELD_PERMISSION)));
|
||||
|
||||
$user->revokePermissionTo($permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$permissions = Permission::all([Permission::ATTRIBUTE_ID, Permission::ATTRIBUTE_NAME])
|
||||
->keyBy(Permission::ATTRIBUTE_ID)
|
||||
->map(fn (Permission $permission) => $permission->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_PERMISSION)
|
||||
->label(__('filament.resources.singularLabel.permission'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($permissions)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Auth\User;
|
||||
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Auth\Role;
|
||||
use App\Models\Auth\User;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class RevokeRoleHeaderAction.
|
||||
*/
|
||||
class RevokeRoleHeaderAction extends Action
|
||||
{
|
||||
final public const FIELD_ROLE = 'role';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (User $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param User $user
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(User $user, array $data): void
|
||||
{
|
||||
$role = Role::findById(intval(Arr::get($data, self::FIELD_ROLE)));
|
||||
|
||||
$user->removeRole($role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$roles = Role::all([Role::ATTRIBUTE_ID, Role::ATTRIBUTE_NAME])
|
||||
->keyBy(Role::ATTRIBUTE_ID)
|
||||
->map(fn (Role $role) => $role->name)
|
||||
->toArray();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::FIELD_ROLE)
|
||||
->label(__('filament.resources.singularLabel.role'))
|
||||
->searchable()
|
||||
->required()
|
||||
->options($roles)
|
||||
->rules('required'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki\Anime;
|
||||
|
||||
use App\Actions\Models\Wiki\Anime\AttachAnimeImageAction as AttachAnimeImageActionAction;
|
||||
use App\Filament\HeaderActions\Models\Wiki\AttachImageHeaderAction;
|
||||
use App\Models\Wiki\Anime;
|
||||
|
||||
/**
|
||||
* Class AttachAnimeImageHeaderAction.
|
||||
*/
|
||||
class AttachAnimeImageHeaderAction extends AttachImageHeaderAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Anime $record, array $data) => (new AttachAnimeImageActionAction($this->facets))->handle($record, $data));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki\Anime;
|
||||
|
||||
use App\Actions\Models\Wiki\Anime\AttachAnimeResourceAction as AttachAnimeResourceActionAction;
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Filament\HeaderActions\Models\Wiki\AttachResourceHeaderAction;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Rules\Wiki\Resource\AnimeResourceLinkFormatRule;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
/**
|
||||
* Class AttachAnimeResourceHeaderAction.
|
||||
*/
|
||||
class AttachAnimeResourceHeaderAction extends AttachResourceHeaderAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Anime $record, array $data) => (new AttachAnimeResourceActionAction($this->sites))->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the format validation rule.
|
||||
*
|
||||
* @param ResourceSite $site
|
||||
* @return ValidationRule
|
||||
*/
|
||||
protected function getFormatRule(ResourceSite $site): ValidationRule
|
||||
{
|
||||
return new AnimeResourceLinkFormatRule($site);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki\Anime;
|
||||
|
||||
use App\Actions\Models\BackfillAction;
|
||||
use App\Actions\Models\Wiki\Anime\BackfillAnimeOtherResourcesAction;
|
||||
use App\Actions\Models\Wiki\Anime\BackfillAnimeSynonymsAction;
|
||||
use App\Actions\Models\Wiki\Anime\Image\BackfillLargeCoverImageAction;
|
||||
use App\Actions\Models\Wiki\Anime\Image\BackfillSmallCoverImageAction;
|
||||
use App\Actions\Models\Wiki\Anime\Resource\BackfillAnidbResourceAction;
|
||||
use App\Actions\Models\Wiki\Anime\Resource\BackfillAnilistResourceAction;
|
||||
use App\Actions\Models\Wiki\Anime\Resource\BackfillAnnResourceAction;
|
||||
use App\Actions\Models\Wiki\Anime\Resource\BackfillKitsuResourceAction;
|
||||
use App\Actions\Models\Wiki\Anime\Resource\BackfillMalResourceAction;
|
||||
use App\Actions\Models\Wiki\Anime\Studio\BackfillAnimeStudiosAction;
|
||||
use App\Enums\Models\Wiki\ImageFacet;
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use App\Models\Wiki\Image;
|
||||
use Exception;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Notifications\Actions\Action as NotificationAction;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Sleep;
|
||||
|
||||
/**
|
||||
* Class BackfillAnimeHeaderAction.
|
||||
*/
|
||||
class BackfillAnimeHeaderAction extends Action implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
|
||||
final public const BACKFILL_ANIDB_RESOURCE = 'backfill_anidb_resource';
|
||||
final public const BACKFILL_ANILIST_RESOURCE = 'backfill_anilist_resource';
|
||||
final public const BACKFILL_ANN_RESOURCE = 'backfill_ann_resource';
|
||||
final public const BACKFILL_KITSU_RESOURCE = 'backfill_kitsu_resource';
|
||||
final public const BACKFILL_OTHER_RESOURCES = 'backfill_other_resources';
|
||||
final public const BACKFILL_LARGE_COVER = 'backfill_large_cover';
|
||||
final public const BACKFILL_MAL_RESOURCE = 'backfill_mal_resource';
|
||||
final public const BACKFILL_SMALL_COVER = 'backfill_small_cover';
|
||||
final public const BACKFILL_STUDIOS = 'backfill_studios';
|
||||
final public const BACKFILL_SYNONYMS = 'backfill_synonyms';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Anime $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param Anime $anime
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Anime $anime, array $fields): void
|
||||
{
|
||||
if ($anime->resources()->doesntExist()) {
|
||||
$this->fail(__('filament.actions.anime.backfill.message.resource_required_failure'));
|
||||
return;
|
||||
}
|
||||
|
||||
$actions = $this->getActions($fields, $anime);
|
||||
|
||||
try {
|
||||
foreach ($actions as $action) {
|
||||
$result = $action->handle();
|
||||
if ($result->hasFailed()) {
|
||||
Notification::make()
|
||||
->body($result->getMessage())
|
||||
->warning()
|
||||
->actions([
|
||||
NotificationAction::make('mark-as-read')
|
||||
->button()
|
||||
->markAsRead(),
|
||||
])
|
||||
->sendToDatabase(auth()->user());
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->fail($e);
|
||||
} finally {
|
||||
// Try not to upset third-party APIs
|
||||
Sleep::for(rand(3, 5))->second();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form|null
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): ?Form
|
||||
{
|
||||
$anime = $this->getRecord();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Section::make(__('filament.actions.anime.backfill.fields.resources.name'))
|
||||
->schema([
|
||||
Checkbox::make(self::BACKFILL_KITSU_RESOURCE)
|
||||
->label(__('filament.actions.anime.backfill.fields.resources.kitsu.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.resources.kitsu.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->resources()->where(ExternalResource::ATTRIBUTE_SITE, ResourceSite::KITSU->value)->doesntExist()),
|
||||
|
||||
Checkbox::make(self::BACKFILL_ANILIST_RESOURCE)
|
||||
->label(__('filament.actions.anime.backfill.fields.resources.anilist.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.resources.anilist.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->resources()->where(ExternalResource::ATTRIBUTE_SITE, ResourceSite::ANILIST->value)->doesntExist()),
|
||||
|
||||
Checkbox::make(self::BACKFILL_MAL_RESOURCE)
|
||||
->label(__('filament.actions.anime.backfill.fields.resources.mal.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.resources.mal.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->resources()->where(ExternalResource::ATTRIBUTE_SITE, ResourceSite::MAL->value)->doesntExist()),
|
||||
|
||||
Checkbox::make(self::BACKFILL_ANIDB_RESOURCE)
|
||||
->label(__('filament.actions.anime.backfill.fields.resources.anidb.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.resources.anidb.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->resources()->where(ExternalResource::ATTRIBUTE_SITE, ResourceSite::ANIDB->value)->doesntExist()),
|
||||
|
||||
Checkbox::make(self::BACKFILL_ANN_RESOURCE)
|
||||
->label(__('filament.actions.anime.backfill.fields.resources.ann.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.resources.ann.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->resources()->where(ExternalResource::ATTRIBUTE_SITE, ResourceSite::ANN->value)->doesntExist()),
|
||||
|
||||
Checkbox::make(self::BACKFILL_OTHER_RESOURCES)
|
||||
->label(__('filament.actions.anime.backfill.fields.resources.external_links.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.resources.external_links.help'))
|
||||
->default(fn () => $anime instanceof Anime),
|
||||
]),
|
||||
|
||||
Section::make(__('filament.actions.anime.backfill.fields.images.name'))
|
||||
->schema([
|
||||
Checkbox::make(self::BACKFILL_LARGE_COVER)
|
||||
->label(__('filament.actions.anime.backfill.fields.images.large_cover.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.images.large_cover.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->images()->where(Image::ATTRIBUTE_FACET, ImageFacet::COVER_LARGE->value)->doesntExist()),
|
||||
|
||||
Checkbox::make(self::BACKFILL_SMALL_COVER)
|
||||
->label(__('filament.actions.anime.backfill.fields.images.small_cover.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.images.small_cover.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->images()->where(Image::ATTRIBUTE_FACET, ImageFacet::COVER_SMALL->value)->doesntExist()),
|
||||
]),
|
||||
|
||||
Section::make(__('filament.actions.anime.backfill.fields.studios.name'))
|
||||
->schema([
|
||||
Checkbox::make(self::BACKFILL_STUDIOS)
|
||||
->label(__('filament.actions.anime.backfill.fields.studios.anime.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.studios.anime.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->studios()->doesntExist()),
|
||||
]),
|
||||
|
||||
Section::make(__('filament.actions.anime.backfill.fields.synonyms.name'))
|
||||
->schema([
|
||||
Checkbox::make(self::BACKFILL_STUDIOS)
|
||||
->label(__('filament.actions.anime.backfill.fields.synonyms.name'))
|
||||
->helperText(__('filament.actions.anime.backfill.fields.synonyms.help'))
|
||||
->default(fn () => $anime instanceof Anime && $anime->animesynonyms()->count() === 0),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected actions for backfilling anime.
|
||||
*
|
||||
* @param array $fields
|
||||
* @param Anime $anime
|
||||
* @return BackfillAction[]
|
||||
*/
|
||||
protected function getActions(array $fields, Anime $anime): array
|
||||
{
|
||||
$actions = [];
|
||||
|
||||
foreach ($this->getActionMapping($anime) as $field => $action) {
|
||||
if (Arr::get($fields, $field) === true) {
|
||||
$actions[] = $action;
|
||||
}
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapping of actions to their form fields.
|
||||
*
|
||||
* @param Anime $anime
|
||||
* @return array<string, BackfillAction>
|
||||
*/
|
||||
protected function getActionMapping(Anime $anime): array
|
||||
{
|
||||
return [
|
||||
self::BACKFILL_KITSU_RESOURCE => new BackfillKitsuResourceAction($anime),
|
||||
self::BACKFILL_ANILIST_RESOURCE => new BackfillAnilistResourceAction($anime),
|
||||
self::BACKFILL_MAL_RESOURCE => new BackfillMalResourceAction($anime),
|
||||
self::BACKFILL_ANIDB_RESOURCE => new BackfillAnidbResourceAction($anime),
|
||||
self::BACKFILL_ANN_RESOURCE => new BackfillAnnResourceAction($anime),
|
||||
self::BACKFILL_OTHER_RESOURCES => new BackfillAnimeOtherResourcesAction($anime),
|
||||
self::BACKFILL_LARGE_COVER => new BackfillLargeCoverImageAction($anime),
|
||||
self::BACKFILL_SMALL_COVER => new BackfillSmallCoverImageAction($anime),
|
||||
self::BACKFILL_STUDIOS => new BackfillAnimeStudiosAction($anime),
|
||||
self::BACKFILL_SYNONYMS => new BackfillAnimeSynonymsAction($anime),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki\Artist;
|
||||
|
||||
use App\Actions\Models\Wiki\Artist\AttachArtistImageAction as AttachArtistImageActionAction;
|
||||
use App\Filament\HeaderActions\Models\Wiki\AttachImageHeaderAction;
|
||||
use App\Models\Wiki\Artist;
|
||||
|
||||
/**
|
||||
* Class AttachArtistImageHeaderAction.
|
||||
*/
|
||||
class AttachArtistImageHeaderAction extends AttachImageHeaderAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Artist $record, array $data) => (new AttachArtistImageActionAction($this->facets))->handle($record, $data));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki\Artist;
|
||||
|
||||
use App\Actions\Models\Wiki\Artist\AttachArtistResourceAction as AttachArtistResourceActionAction;
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Filament\HeaderActions\Models\Wiki\AttachResourceHeaderAction;
|
||||
use App\Models\Wiki\Artist;
|
||||
use App\Rules\Wiki\Resource\ArtistResourceLinkFormatRule;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
/**
|
||||
* Class AttachArtistResourceHeaderAction.
|
||||
*/
|
||||
class AttachArtistResourceHeaderAction extends AttachResourceHeaderAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Artist $record, array $data) => (new AttachArtistResourceActionAction($this->sites))->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the format validation rule.
|
||||
*
|
||||
* @param ResourceSite $site
|
||||
* @return ValidationRule
|
||||
*/
|
||||
protected function getFormatRule(ResourceSite $site): ValidationRule
|
||||
{
|
||||
return new ArtistResourceLinkFormatRule($site);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki;
|
||||
|
||||
use App\Enums\Models\Wiki\ImageFacet;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Artist;
|
||||
use App\Models\Wiki\Image;
|
||||
use App\Models\Wiki\Studio;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\Action;
|
||||
|
||||
/**
|
||||
* Class AttachImageHeaderAction.
|
||||
*/
|
||||
abstract class AttachImageHeaderAction extends Action
|
||||
{
|
||||
protected array $facets = [];
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$fields = [];
|
||||
$model = $form->getRecord();
|
||||
|
||||
if ($model instanceof Anime || $model instanceof Artist || $model instanceof Studio) {
|
||||
foreach ($this->facets as $facet) {
|
||||
$images = $model->images();
|
||||
if ($images->where(Image::ATTRIBUTE_FACET, $facet->value)->exists()) continue;
|
||||
|
||||
$fields[] = FileUpload::make($facet->name)
|
||||
->label($facet->localize())
|
||||
->image()
|
||||
->storeFiles(false);
|
||||
}
|
||||
}
|
||||
|
||||
return $form
|
||||
->schema($fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facets available for the action.
|
||||
*
|
||||
* @param ImageFacet[] $facets
|
||||
* @return static
|
||||
*/
|
||||
public function facets(array $facets): static
|
||||
{
|
||||
$this->facets = $facets;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki;
|
||||
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Artist;
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use App\Models\Wiki\Song;
|
||||
use App\Models\Wiki\Studio;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
/**
|
||||
* Class AttachResourceHeaderAction.
|
||||
*/
|
||||
abstract class AttachResourceHeaderAction extends Action
|
||||
{
|
||||
protected array $sites = [];
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form|null
|
||||
*/
|
||||
public function getForm(Form $form): ?Form
|
||||
{
|
||||
$fields = [];
|
||||
$model = $this->getRecord();
|
||||
|
||||
foreach ($this->sites as $resourceSite) {
|
||||
if ($model instanceof Anime || $model instanceof Artist || $model instanceof Song || $model instanceof Studio) {
|
||||
$resources = $model->resources();
|
||||
if ($resources->where(ExternalResource::ATTRIBUTE_SITE, $resourceSite->value)->exists()) continue;
|
||||
}
|
||||
|
||||
$resourceSiteLower = strtolower($resourceSite->name);
|
||||
|
||||
$fields[] = TextInput::make($resourceSite->name)
|
||||
->label($resourceSite->localize())
|
||||
->helperText(__("filament.actions.models.wiki.attach_resource.fields.{$resourceSiteLower}.help"))
|
||||
->url()
|
||||
->maxLength(192)
|
||||
->rules(['max:192', $this->getFormatRule($resourceSite)]);
|
||||
}
|
||||
|
||||
return $form
|
||||
->schema($fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sites available for the action.
|
||||
*
|
||||
* @param ResourceSite[] $sites
|
||||
* @return static
|
||||
*/
|
||||
public function sites($sites): static
|
||||
{
|
||||
$this->sites = $sites;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the format validation rule.
|
||||
*
|
||||
* @param ResourceSite $site
|
||||
* @return ValidationRule
|
||||
*/
|
||||
abstract protected function getFormatRule(ResourceSite $site): ValidationRule;
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki\Audio;
|
||||
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
|
||||
use App\Models\Wiki\Audio;
|
||||
use App\Models\Wiki\Video;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class AttachAudioToRelatedVideosHeaderAction.
|
||||
*/
|
||||
class AttachAudioToRelatedVideosHeaderAction extends Action implements ShouldQueue
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Audio $record) => $this->handle($record));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param Audio $audio
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Audio $audio): void
|
||||
{
|
||||
$video = $audio->videos()->first();
|
||||
|
||||
$video->animethemeentries()->each(function (AnimeThemeEntry $firstEntry) use ($audio) {
|
||||
$theme = $firstEntry->animetheme()->first();
|
||||
|
||||
$theme->animethemeentries()->each(function (AnimeThemeEntry $entry) use ($audio) {
|
||||
$entry->videos()->each(function (Video $video) use ($audio) {
|
||||
Log::info("Associating Audio '{$audio->filename}' with Video '{$video->filename}'");
|
||||
$video->audio()->associate($audio)->save();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki\Song;
|
||||
|
||||
use App\Actions\Models\Wiki\Song\AttachSongResourceAction as AttachSongResourceActionAction;
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Filament\HeaderActions\Models\Wiki\AttachResourceHeaderAction;
|
||||
use App\Models\Wiki\Song;
|
||||
use App\Rules\Wiki\Resource\SongResourceLinkFormatRule;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
/**
|
||||
* Class AttachSongResourceHeaderAction.
|
||||
*/
|
||||
class AttachSongResourceHeaderAction extends AttachResourceHeaderAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Song $record, array $data) => (new AttachSongResourceActionAction($this->sites))->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the format validation rule.
|
||||
*
|
||||
* @param ResourceSite $site
|
||||
* @return ValidationRule
|
||||
*/
|
||||
protected function getFormatRule(ResourceSite $site): ValidationRule
|
||||
{
|
||||
return new SongResourceLinkFormatRule($site);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki\Studio;
|
||||
|
||||
use App\Actions\Models\Wiki\Studio\AttachStudioImageAction as AttachStudioImageActionAction;
|
||||
use App\Filament\HeaderActions\Models\Wiki\AttachImageHeaderAction;
|
||||
use App\Models\Wiki\Studio;
|
||||
|
||||
/**
|
||||
* Class AttachStudioImageHeaderAction.
|
||||
*/
|
||||
class AttachStudioImageHeaderAction extends AttachImageHeaderAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Studio $record, array $data) => (new AttachStudioImageActionAction($this->facets))->handle($record, $data));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki\Studio;
|
||||
|
||||
use App\Actions\Models\Wiki\Studio\AttachStudioResourceAction as AttachStudioResourceActionAction;
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
use App\Filament\HeaderActions\Models\Wiki\AttachResourceHeaderAction;
|
||||
use App\Models\Wiki\Studio;
|
||||
use App\Rules\Wiki\Resource\StudioResourceLinkFormatRule;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
/**
|
||||
* Class AttachStudioResourceHeaderAction.
|
||||
*/
|
||||
class AttachStudioResourceHeaderAction extends AttachResourceHeaderAction
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Studio $record, array $data) => (new AttachStudioResourceActionAction($this->sites))->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the format validation rule.
|
||||
*
|
||||
* @param ResourceSite $site
|
||||
* @return ValidationRule
|
||||
*/
|
||||
protected function getFormatRule(ResourceSite $site): ValidationRule
|
||||
{
|
||||
return new StudioResourceLinkFormatRule($site);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki\Studio;
|
||||
|
||||
use App\Actions\Models\BackfillAction;
|
||||
use App\Actions\Models\Wiki\Studio\Image\BackfillLargeCoverImageAction;
|
||||
use App\Enums\Models\Wiki\ImageFacet;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Image;
|
||||
use App\Models\Wiki\Studio;
|
||||
use Exception;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Notifications\Actions\Action as NotificationAction;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Sleep;
|
||||
|
||||
/**
|
||||
* Class BackfillStudioHeaderAction.
|
||||
*/
|
||||
class BackfillStudioHeaderAction extends Action implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
|
||||
final public const BACKFILL_LARGE_COVER = 'backfill_large_cover';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Studio $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param Studio $studio
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Studio $studio, array $fields): void
|
||||
{
|
||||
if ($studio->resources()->doesntExist()) {
|
||||
//$this->markAsFailed($studio, __('filament.actions.studio.backfill.message.resource_required_failure'));
|
||||
return;
|
||||
}
|
||||
|
||||
$actions = $this->getActions($fields, $studio);
|
||||
|
||||
try {
|
||||
foreach ($actions as $action) {
|
||||
$result = $action->handle();
|
||||
if ($result->hasFailed()) {
|
||||
Notification::make()
|
||||
->body($result->getMessage())
|
||||
->warning()
|
||||
->actions([
|
||||
NotificationAction::make('mark-as-read')
|
||||
->button()
|
||||
->markAsRead(),
|
||||
])
|
||||
->sendToDatabase(auth()->user());
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
//$this->markAsFailed($studio, $e);
|
||||
} finally {
|
||||
// Try not to upset third-party APIs
|
||||
Sleep::for(rand(3, 5))->second();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$studio = $this->getRecord();
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Section::make(__('filament.actions.studio.backfill.fields.images.name'))
|
||||
->schema([
|
||||
Checkbox::make(self::BACKFILL_LARGE_COVER)
|
||||
->label(__('filament.actions.studio.backfill.fields.images.large_cover.name'))
|
||||
->helperText(__('filament.actions.studio.backfill.fields.images.large_cover.help'))
|
||||
->default(fn () => $studio instanceof Studio && $studio->images()->where(Image::ATTRIBUTE_FACET, ImageFacet::COVER_LARGE->value)->doesntExist()),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected actions for backfilling studios.
|
||||
*
|
||||
* @param array $fields
|
||||
* @param Studio $studio
|
||||
* @return BackfillAction[]
|
||||
*/
|
||||
protected function getActions(array $fields, Studio $studio): array
|
||||
{
|
||||
$actions = [];
|
||||
|
||||
foreach ($this->getActionMapping($studio) as $field => $action) {
|
||||
if (Arr::get($fields, $field) === true) {
|
||||
$actions[] = $action;
|
||||
}
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapping of actions to their form fields.
|
||||
*
|
||||
* @param Studio $studio
|
||||
* @return array<string, BackfillAction>
|
||||
*/
|
||||
protected function getActionMapping(Studio $studio): array
|
||||
{
|
||||
return [
|
||||
self::BACKFILL_LARGE_COVER => new BackfillLargeCoverImageAction($studio),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Models\Wiki\Video;
|
||||
|
||||
use App\Actions\Models\Wiki\Video\Audio\BackfillAudioAction as BackfillAudio;
|
||||
use App\Enums\Actions\Models\Wiki\Video\DeriveSourceVideo;
|
||||
use App\Enums\Actions\Models\Wiki\Video\OverwriteAudio;
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Models\Wiki\Video;
|
||||
use Exception;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Notifications\Actions\Action as NotificationAction;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
/**
|
||||
* Class BackfillAudioHeaderAction.
|
||||
*/
|
||||
class BackfillAudioHeaderAction extends Action implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
|
||||
final public const DERIVE_SOURCE_VIDEO = 'derive_source_video';
|
||||
final public const OVERWRITE_AUDIO = 'overwrite_audio';
|
||||
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (Video $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param Video $video
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Video $video, array $data): void
|
||||
{
|
||||
$deriveSourceVideo = DeriveSourceVideo::from(intval(Arr::get($data, self::DERIVE_SOURCE_VIDEO)));
|
||||
$overwriteAudio = OverwriteAudio::from(intval(Arr::get($data, self::OVERWRITE_AUDIO)));
|
||||
|
||||
$action = new BackfillAudio($video, $deriveSourceVideo, $overwriteAudio);
|
||||
|
||||
try {
|
||||
$result = $action->handle();
|
||||
if ($result->hasFailed()) {
|
||||
Notification::make()
|
||||
->body($result->getMessage())
|
||||
->warning()
|
||||
->actions([
|
||||
NotificationAction::make('mark-as-read')
|
||||
->button()
|
||||
->markAsRead(),
|
||||
])
|
||||
->sendToDatabase(auth()->user());
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
//$this->markAsFailed($video, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(self::DERIVE_SOURCE_VIDEO)
|
||||
->label(__('filament.actions.video.backfill.fields.derive_source.name'))
|
||||
->options(DeriveSourceVideo::asSelectArray())
|
||||
->rules(['required', new Enum(DeriveSourceVideo::class)])
|
||||
->default(DeriveSourceVideo::YES->value)
|
||||
->helperText(__('filament.actions.video.backfill.fields.derive_source.help')),
|
||||
|
||||
Select::make(self::OVERWRITE_AUDIO)
|
||||
->label(__('filament.actions.video.backfill.fields.overwrite.name'))
|
||||
->options(OverwriteAudio::asSelectArray())
|
||||
->rules(['required', new Enum(OverwriteAudio::class)])
|
||||
->default(OverwriteAudio::NO->value)
|
||||
->helperText(__('filament.actions.video.backfill.fields.overwrite.help')),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Storage\Base;
|
||||
|
||||
use App\Actions\Storage\Base\DeleteAction as BaseDeleteAction;
|
||||
use App\Filament\HeaderActions\Storage\StorageHeaderAction;
|
||||
use App\Models\BaseModel;
|
||||
|
||||
/**
|
||||
* Class DeleteHeaderAction.
|
||||
*/
|
||||
abstract class DeleteHeaderAction extends StorageHeaderAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param BaseModel $model
|
||||
* @param array $fields
|
||||
* @return BaseDeleteAction
|
||||
*/
|
||||
abstract protected function storageAction(BaseModel $model, array $fields): BaseDeleteAction;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Storage\Base;
|
||||
|
||||
use App\Actions\Storage\Base\MoveAction as BaseMoveAction;
|
||||
use App\Contracts\Storage\InteractsWithDisk;
|
||||
use App\Filament\HeaderActions\Storage\StorageHeaderAction;
|
||||
use App\Models\BaseModel;
|
||||
use App\Rules\Storage\StorageFileDirectoryExistsRule;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
* Class MoveHeaderAction.
|
||||
*/
|
||||
abstract class MoveHeaderAction extends StorageHeaderAction implements InteractsWithDisk
|
||||
{
|
||||
/**
|
||||
* Get the fields available on the action.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getForm(Form $form): Form
|
||||
{
|
||||
$defaultPath = $this->defaultPath();
|
||||
|
||||
$fs = Storage::disk($this->disk());
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
TextInput::make('path')
|
||||
->label(__('filament.actions.storage.move.fields.path.name'))
|
||||
->helperText(__('filament.actions.storage.move.fields.path.help'))
|
||||
->required()
|
||||
->rules(['required', 'string', 'doesnt_start_with:/', "ends_with:{$this->allowedFileExtension()}", new StorageFileDirectoryExistsRule($fs)])
|
||||
->default(fn () => $defaultPath),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param BaseModel $model
|
||||
* @param array $fields
|
||||
* @return BaseMoveAction
|
||||
*/
|
||||
abstract protected function storageAction(BaseModel $model, array $fields): BaseMoveAction;
|
||||
|
||||
/**
|
||||
* Resolve the default value for the path field.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
abstract protected function defaultPath(): ?string;
|
||||
|
||||
/**
|
||||
* The file extension that the path must end with.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function allowedFileExtension(): string;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Storage;
|
||||
|
||||
use App\Contracts\Actions\Storage\StorageAction as BaseStorageAction;
|
||||
use App\Models\BaseModel;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* Class StorageHeaderAction.
|
||||
*/
|
||||
abstract class StorageHeaderAction extends Action
|
||||
{
|
||||
/**
|
||||
* Initial setup for the action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->action(fn (BaseModel $record, array $data) => $this->handle($record, $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param BaseModel $model
|
||||
* @param array $fields
|
||||
* @return BaseStorageAction
|
||||
*/
|
||||
abstract protected function storageAction(BaseModel $model, array $fields): BaseStorageAction;
|
||||
|
||||
/**
|
||||
* Perform the action on the given models.
|
||||
*
|
||||
* @param BaseModel $model
|
||||
* @param array $fields
|
||||
* @return void
|
||||
*/
|
||||
public function handle(BaseModel $model, array $fields): void
|
||||
{
|
||||
$action = $this->storageAction($model, $fields);
|
||||
|
||||
$storageResults = $action->handle();
|
||||
|
||||
$storageResults->toLog();
|
||||
|
||||
$action->then($storageResults);
|
||||
|
||||
$actionResult = $storageResults->toActionResult();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Storage\Wiki\Audio;
|
||||
|
||||
use App\Actions\Storage\Wiki\Audio\DeleteAudioAction as DeleteAudio;
|
||||
use App\Filament\HeaderActions\Storage\Base\DeleteHeaderAction;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Audio;
|
||||
|
||||
/**
|
||||
* Class DeleteAudioHeaderAction.
|
||||
*/
|
||||
class DeleteAudioHeaderAction extends DeleteHeaderAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param Audio $audio
|
||||
* @param array $fields
|
||||
* @return DeleteAudio
|
||||
*/
|
||||
protected function storageAction(BaseModel $audio, array $fields): DeleteAudio
|
||||
{
|
||||
return new DeleteAudio($audio);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Storage\Wiki\Audio;
|
||||
|
||||
use App\Actions\Storage\Wiki\Audio\MoveAudioAction as MoveAudio;
|
||||
use App\Constants\Config\AudioConstants;
|
||||
use App\Models\Wiki\Audio;
|
||||
use App\Filament\HeaderActions\Storage\Base\MoveHeaderAction;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class MoveAudioHeaderAction.
|
||||
*/
|
||||
class MoveAudioHeaderAction extends MoveHeaderAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param Audio $audio
|
||||
* @param array $fields
|
||||
* @return MoveAudio
|
||||
*/
|
||||
protected function storageAction(BaseModel $audio, array $fields): MoveAudio
|
||||
{
|
||||
/** @var string $path */
|
||||
$path = Arr::get($fields, 'path');
|
||||
|
||||
return new MoveAudio($audio, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(AudioConstants::DEFAULT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the default value for the path field.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function defaultPath(): ?string
|
||||
{
|
||||
$audio = $this->getRecord();
|
||||
|
||||
return $audio instanceof Audio
|
||||
? $audio->path
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The file extension that the path must end with.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function allowedFileExtension(): string
|
||||
{
|
||||
return '.ogg';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Storage\Wiki\Video;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\DeleteVideoAction as DeleteVideo;
|
||||
use App\Filament\HeaderActions\Storage\Base\DeleteHeaderAction;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Video;
|
||||
|
||||
/**
|
||||
* Class DeleteVideoHeaderAction.
|
||||
*/
|
||||
class DeleteVideoHeaderAction extends DeleteHeaderAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param Video $video
|
||||
* @param array $fields
|
||||
* @return DeleteVideo
|
||||
*/
|
||||
protected function storageAction(BaseModel $video, array $fields): DeleteVideo
|
||||
{
|
||||
return new DeleteVideo($video);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Storage\Wiki\Video;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\MoveVideoAction as MoveVideo;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Filament\HeaderActions\Storage\Base\MoveHeaderAction;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class MoveVideoHeaderAction.
|
||||
*/
|
||||
class MoveVideoHeaderAction extends MoveHeaderAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param Video $video
|
||||
* @param array $fields
|
||||
* @return MoveVideo
|
||||
*/
|
||||
protected function storageAction(BaseModel $video, array $fields): MoveVideo
|
||||
{
|
||||
/** @var string $path */
|
||||
$path = Arr::get($fields, 'path');
|
||||
|
||||
return new MoveVideo($video, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the default value for the path field.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function defaultPath(): ?string
|
||||
{
|
||||
$video = $this->getRecord();
|
||||
|
||||
return $video instanceof Video
|
||||
? $video->path
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The file extension that the path must end with.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function allowedFileExtension(): string
|
||||
{
|
||||
return '.webm';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\Script\DeleteScriptAction as DeleteScript;
|
||||
use App\Filament\HeaderActions\Storage\Base\DeleteHeaderAction;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
|
||||
/**
|
||||
* Class DeleteScriptHeaderAction.
|
||||
*/
|
||||
class DeleteScriptHeaderAction extends DeleteHeaderAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
* @param array $fields
|
||||
* @return DeleteScript
|
||||
*/
|
||||
protected function storageAction(BaseModel $script, array $fields): DeleteScript
|
||||
{
|
||||
return new DeleteScript($script);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\HeaderActions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Wiki\Video\Script\MoveScriptAction as MoveScript;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Filament\HeaderActions\Storage\Base\MoveHeaderAction;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class MoveScriptHeaderAction.
|
||||
*/
|
||||
class MoveScriptHeaderAction extends MoveHeaderAction
|
||||
{
|
||||
/**
|
||||
* Get the underlying storage action.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
* @param array $fields
|
||||
* @return MoveScript
|
||||
*/
|
||||
protected function storageAction(BaseModel $script, array $fields): MoveScript
|
||||
{
|
||||
/** @var string $path */
|
||||
$path = Arr::get($fields, 'path');
|
||||
|
||||
return new MoveScript($script, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the default value for the path field.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function defaultPath(): ?string
|
||||
{
|
||||
$script = $this->getRecord();
|
||||
|
||||
return $script instanceof VideoScript
|
||||
? $script->path
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The file extension that the path must end with.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function allowedFileExtension(): string
|
||||
{
|
||||
return '.txt';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Providers;
|
||||
|
||||
use App\Models\BaseModel;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\GlobalSearch\Contracts\GlobalSearchProvider;
|
||||
use Filament\GlobalSearch\GlobalSearchResult;
|
||||
use Filament\GlobalSearch\GlobalSearchResults;
|
||||
use Elastic\ScoutDriverPlus\Searchable;
|
||||
|
||||
/**
|
||||
* Class GlobalSearchScoutProvider.
|
||||
*/
|
||||
class GlobalSearchScoutProvider implements GlobalSearchProvider
|
||||
{
|
||||
/**
|
||||
* Get the results for the global search.
|
||||
*
|
||||
* @param string $query
|
||||
* @return GlobalSearchResults|null
|
||||
*/
|
||||
public function getResults(string $query): ?GlobalSearchResults
|
||||
{
|
||||
$builder = GlobalSearchResults::make();
|
||||
|
||||
foreach (Filament::getResources() as $resource) {
|
||||
if (!$resource::canGloballySearch() || !in_array(Searchable::class, class_uses_recursive($resource::getModel()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$search = $resource::getModel()::search($query);
|
||||
|
||||
$resourceResults = $search
|
||||
->get()
|
||||
->map(function (BaseModel $record) use ($resource): ?GlobalSearchResult {
|
||||
$url = $resource::getGlobalSearchResultUrl($record);
|
||||
|
||||
if (blank($url)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new GlobalSearchResult(
|
||||
title: $resource::getGlobalSearchResultTitle($record),
|
||||
url: $url,
|
||||
details: $resource::getGlobalSearchResultDetails($record),
|
||||
actions: $resource::getGlobalSearchResultActions($record),
|
||||
);
|
||||
})
|
||||
->filter();
|
||||
|
||||
if (!$resourceResults->count()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$builder->category($resource::getPluralModelLabel(), $resourceResults);
|
||||
}
|
||||
|
||||
return $builder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Admin;
|
||||
|
||||
use App\Filament\Components\Columns\TextColumn;
|
||||
use App\Filament\Components\Infolist\TextEntry;
|
||||
use App\Filament\Resources\BaseResource;
|
||||
use App\Filament\Resources\Admin\Announcement\Pages\CreateAnnouncement;
|
||||
use App\Filament\Resources\Admin\Announcement\Pages\EditAnnouncement;
|
||||
use App\Filament\Resources\Admin\Announcement\Pages\ListAnnouncements;
|
||||
use App\Filament\Resources\Admin\Announcement\Pages\ViewAnnouncement;
|
||||
use App\Models\Admin\Announcement as AnnouncementModel;
|
||||
use Filament\Forms\Components\MarkdownEditor;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Infolists\Components\Section;
|
||||
use Filament\Infolists\Infolist;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
/**
|
||||
* Class Announcement.
|
||||
*/
|
||||
class Announcement extends BaseResource
|
||||
{
|
||||
/**
|
||||
* The model the resource corresponds to.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected static ?string $model = AnnouncementModel::class;
|
||||
|
||||
/**
|
||||
* Get the displayable singular label of the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getLabel(): string
|
||||
{
|
||||
return __('filament.resources.singularLabel.announcement');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displayable label of the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getPluralLabel(): string
|
||||
{
|
||||
return __('filament.resources.label.announcements');
|
||||
}
|
||||
|
||||
/**
|
||||
* The logical group associated with the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getNavigationGroup(): string
|
||||
{
|
||||
return __('filament.resources.group.admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* The icon displayed to the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getNavigationIcon(): string
|
||||
{
|
||||
return __('filament.resources.icon.announcements');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the slug (URI key) for the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getSlug(): string
|
||||
{
|
||||
return static::getDefaultSlug().'announcements';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route key for the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getRecordRouteKeyName(): string
|
||||
{
|
||||
return AnnouncementModel::ATTRIBUTE_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* The form to the actions.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
MarkdownEditor::make(AnnouncementModel::ATTRIBUTE_CONTENT)
|
||||
->label(__('filament.fields.announcement.content'))
|
||||
->required()
|
||||
->rules(['required', 'max:65535'])
|
||||
->maxLength(65535),
|
||||
])
|
||||
->columns(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The index page of the resource.
|
||||
*
|
||||
* @param Table $table
|
||||
* @return Table
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return parent::table($table)
|
||||
->columns([
|
||||
TextColumn::make(AnnouncementModel::ATTRIBUTE_ID)
|
||||
->label(__('filament.fields.base.id'))
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make(AnnouncementModel::ATTRIBUTE_CONTENT)
|
||||
->label(__('filament.fields.announcement.content'))
|
||||
->sortable()
|
||||
->searchable()
|
||||
->copyableWithMessage(),
|
||||
])
|
||||
->defaultSort(AnnouncementModel::ATTRIBUTE_ID, 'desc')
|
||||
->filters(static::getFilters())
|
||||
->filtersFormMaxHeight('400px')
|
||||
->actions(static::getActions())
|
||||
->bulkActions(static::getBulkActions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the infolist available for the resource.
|
||||
*
|
||||
* @param Infolist $infolist
|
||||
* @return Infolist
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function infolist(Infolist $infolist): Infolist
|
||||
{
|
||||
return $infolist
|
||||
->schema([
|
||||
Section::make(static::getRecordTitle($infolist->getRecord()))
|
||||
->schema([
|
||||
TextEntry::make(AnnouncementModel::ATTRIBUTE_ID)
|
||||
->label(__('filament.fields.base.id')),
|
||||
|
||||
TextEntry::make(AnnouncementModel::ATTRIBUTE_CONTENT)
|
||||
->label(__('filament.fields.announcement.content'))
|
||||
->markdown()
|
||||
->columnSpanFull()
|
||||
->copyableWithMessage(),
|
||||
])
|
||||
->columns(3),
|
||||
|
||||
Section::make(__('filament.fields.base.timestamps'))
|
||||
->schema(parent::timestamps())
|
||||
->columns(3),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relationships available for the resource.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filters available for the resource.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getFilters(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getFilters(),
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actions available for the resource.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bulk actions available for the resource.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getBulkActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getBulkActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pages available for the resource.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListAnnouncements::route('/'),
|
||||
'create' => CreateAnnouncement::route('/create'),
|
||||
'view' => ViewAnnouncement::route('/{record:announcement_id}'),
|
||||
'edit' => EditAnnouncement::route('/{record:announcement_id}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Admin\Announcement\Pages;
|
||||
|
||||
use App\Filament\Resources\Base\BaseCreateResource;
|
||||
use App\Filament\Resources\Admin\Announcement;
|
||||
|
||||
/**
|
||||
* Class CreateAnnouncement.
|
||||
*/
|
||||
class CreateAnnouncement extends BaseCreateResource
|
||||
{
|
||||
protected static string $resource = Announcement::class;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Admin\Announcement\Pages;
|
||||
|
||||
use App\Filament\Resources\Admin\Announcement;
|
||||
use App\Filament\Resources\Base\BaseEditResource;
|
||||
|
||||
/**
|
||||
* Class EditAnnouncement.
|
||||
*/
|
||||
class EditAnnouncement extends BaseEditResource
|
||||
{
|
||||
protected static string $resource = Announcement::class;
|
||||
|
||||
/**
|
||||
* Get the header actions available.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Admin\Announcement\Pages;
|
||||
|
||||
use App\Filament\Resources\Base\BaseListResources;
|
||||
use App\Filament\Resources\Admin\Announcement;
|
||||
|
||||
/**
|
||||
* Class ListAnnouncements.
|
||||
*/
|
||||
class ListAnnouncements extends BaseListResources
|
||||
{
|
||||
protected static string $resource = Announcement::class;
|
||||
|
||||
/**
|
||||
* Get the header actions available.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Admin\Announcement\Pages;
|
||||
|
||||
use App\Filament\Resources\Base\BaseViewResource;
|
||||
use App\Filament\Resources\Admin\Announcement;
|
||||
|
||||
/**
|
||||
* Class ViewAnnouncement.
|
||||
*/
|
||||
class ViewAnnouncement extends BaseViewResource
|
||||
{
|
||||
protected static string $resource = Announcement::class;
|
||||
|
||||
/**
|
||||
* Get the header actions available.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user