mirror of
https://github.com/AnimeThemes/animethemes-server.git
synced 2026-11-13 17:48:49 +01:00
feat: external lists (#710)
This commit is contained in:
1 parent
294ab4fb75
commit
9238fd3d57
179 files changed
+5481
-414
No files matched your search
@@ -94,6 +94,10 @@ ELASTIC_SCOUT_DRIVER_REFRESH_DOCUMENTS=false
|
||||
# elastic migrations
|
||||
ELASTIC_MIGRATIONS_TABLE=elastic_migrations
|
||||
|
||||
# external profile
|
||||
PROFILE_MAX_ENTRIES=1000
|
||||
USER_MAX_PROFILES=5
|
||||
|
||||
# ffmpeg
|
||||
FFMPEG_BINARIES=
|
||||
FFPROBE_BINARIES=
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\List\ExternalProfile;
|
||||
|
||||
use App\Actions\Http\Api\StoreAction;
|
||||
use App\Enums\Models\List\AnimeWatchStatus;
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class StoreExternalProfileAction.
|
||||
*/
|
||||
class StoreExternalProfileAction
|
||||
{
|
||||
/**
|
||||
* Store external profile and its entries.
|
||||
*
|
||||
* @param Builder $builder
|
||||
* @param array $profileParameters
|
||||
* @return Model
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function store(Builder $builder, array $profileParameters): Model
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
// $getEntriesAction = new GetEntries();
|
||||
|
||||
// $entries = $getEntriesAction->get($profileParameters);
|
||||
|
||||
$entries = [
|
||||
'name' => 'Kyrch Profile',
|
||||
'entries' => [
|
||||
[
|
||||
ExternalResource::ATTRIBUTE_EXTERNAL_ID => 101573,
|
||||
ExternalEntry::ATTRIBUTE_SCORE => 10,
|
||||
ExternalEntry::ATTRIBUTE_IS_FAVORITE => true,
|
||||
ExternalEntry::ATTRIBUTE_WATCH_STATUS => AnimeWatchStatus::COMPLETED->value,
|
||||
],
|
||||
[
|
||||
ExternalResource::ATTRIBUTE_EXTERNAL_ID => 477,
|
||||
ExternalEntry::ATTRIBUTE_SCORE => 9.5,
|
||||
ExternalEntry::ATTRIBUTE_IS_FAVORITE => false,
|
||||
ExternalEntry::ATTRIBUTE_WATCH_STATUS => AnimeWatchStatus::WATCHING->value,
|
||||
],
|
||||
[
|
||||
ExternalResource::ATTRIBUTE_EXTERNAL_ID => 934,
|
||||
ExternalEntry::ATTRIBUTE_SCORE => 8,
|
||||
ExternalEntry::ATTRIBUTE_IS_FAVORITE => false,
|
||||
ExternalEntry::ATTRIBUTE_WATCH_STATUS => AnimeWatchStatus::PAUSED->value,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$storeProfileAction = new StoreAction();
|
||||
|
||||
$profile = $storeProfileAction->store($builder, $profileParameters);
|
||||
|
||||
foreach (Arr::get($entries, 'entries') as $entryParameters) {
|
||||
$storeEntryAction = new StoreAction();
|
||||
|
||||
$externalSite = Arr::get($profileParameters, 'site');
|
||||
$external_id = Arr::get($entryParameters, 'external_id');
|
||||
|
||||
$animes = Anime::query()->whereHas(ExternalResource::TABLE, function (Builder $query) use ($externalSite, $external_id) {
|
||||
$query->where(ExternalResource::ATTRIBUTE_SITE, ExternalProfileSite::getResourceSite($externalSite)->value)
|
||||
->where(ExternalResource::ATTRIBUTE_EXTERNAL_ID, $external_id);
|
||||
})->get();
|
||||
|
||||
foreach ($animes as $anime) {
|
||||
if ($anime instanceof Anime) {
|
||||
$storeEntryAction->store(ExternalEntry::query(), array_merge(
|
||||
$entryParameters,
|
||||
[
|
||||
ExternalEntry::ATTRIBUTE_ANIME => $anime->getKey(),
|
||||
ExternalEntry::ATTRIBUTE_EXTERNAL_PROFILE => $profile->getKey(),
|
||||
]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $storeProfileAction->cleanup($profile);
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
|
||||
DB::rollBack();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Constants\Config;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileConstants.
|
||||
*/
|
||||
class ExternalProfileConstants
|
||||
{
|
||||
final public const MAX_PROFILES_QUALIFIED = 'externalprofile.user_max_profiles';
|
||||
|
||||
final public const MAX_ENTRIES_QUALIFIED = 'externalprofile.profile_max_entries';
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Enums\Models\List;
|
||||
|
||||
use App\Concerns\Enums\LocalizesName;
|
||||
|
||||
/**
|
||||
* Enum AnimeWatchStatus.
|
||||
*/
|
||||
enum AnimeWatchStatus: int
|
||||
{
|
||||
use LocalizesName;
|
||||
|
||||
case WATCHING = 0;
|
||||
case COMPLETED = 1;
|
||||
case PAUSED = 2;
|
||||
case DROPPED = 3;
|
||||
case PLAN_TO_WATCH = 4;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Enums\Models\List;
|
||||
|
||||
use App\Concerns\Enums\LocalizesName;
|
||||
use App\Enums\Models\Wiki\ResourceSite;
|
||||
|
||||
/**
|
||||
* Enum ExternalProfileSite.
|
||||
*/
|
||||
enum ExternalProfileSite: int
|
||||
{
|
||||
use LocalizesName;
|
||||
|
||||
case MAL = 0;
|
||||
case ANILIST = 1;
|
||||
case KITSU = 2;
|
||||
|
||||
/**
|
||||
* Get the ResourceSite by the ExternalProfileSite value.
|
||||
*
|
||||
* @param int|null $value
|
||||
* @return ResourceSite|null
|
||||
*/
|
||||
public static function getResourceSite(?int $value): ?ResourceSite
|
||||
{
|
||||
return match ($value) {
|
||||
static::MAL->value => ResourceSite::MAL,
|
||||
static::ANILIST->value => ResourceSite::ANILIST,
|
||||
static::KITSU->value => ResourceSite::KITSU,
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Enums\Models\List;
|
||||
|
||||
use App\Concerns\Enums\LocalizesName;
|
||||
|
||||
/**
|
||||
* Enum ExternalProfileVisibility.
|
||||
*/
|
||||
enum ExternalProfileVisibility: int
|
||||
{
|
||||
use LocalizesName;
|
||||
|
||||
case PUBLIC = 0;
|
||||
case PRIVATE = 1;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\Base\List;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Events\Base\BaseCreatedEvent;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class ListCreatedEvent.
|
||||
*
|
||||
* @template TModel of \App\Models\BaseModel
|
||||
*
|
||||
* @extends BaseCreatedEvent<TModel>
|
||||
*/
|
||||
abstract class ListCreatedEvent extends BaseCreatedEvent
|
||||
{
|
||||
/**
|
||||
* Get Discord channel the message will be sent to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::ADMIN_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\Base\List;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Events\Base\BaseDeletedEvent;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class ListDeletedEvent.
|
||||
*
|
||||
* @template TModel of \App\Models\BaseModel
|
||||
*
|
||||
* @extends BaseDeletedEvent<TModel>
|
||||
*/
|
||||
abstract class ListDeletedEvent extends BaseDeletedEvent
|
||||
{
|
||||
/**
|
||||
* Get Discord channel the message will be sent to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::ADMIN_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\Base\List;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Events\Base\BaseRestoredEvent;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class ListRestoredEvent.
|
||||
*
|
||||
* @template TModel of \App\Models\BaseModel
|
||||
*
|
||||
* @extends BaseRestoredEvent<TModel>
|
||||
*/
|
||||
abstract class ListRestoredEvent extends BaseRestoredEvent
|
||||
{
|
||||
/**
|
||||
* Get Discord channel the message will be sent to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::ADMIN_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\Base\List;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Events\Base\BaseUpdatedEvent;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class ListUpdatedEvent.
|
||||
*
|
||||
* @template TModel of \App\Models\BaseModel
|
||||
*
|
||||
* @extends BaseUpdatedEvent<TModel>
|
||||
*/
|
||||
abstract class ListUpdatedEvent extends BaseUpdatedEvent
|
||||
{
|
||||
/**
|
||||
* Get Discord channel the message will be sent to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::ADMIN_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\ExternalProfile\ExternalEntry;
|
||||
|
||||
use App\Events\Base\List\ListCreatedEvent;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\List\ExternalProfile;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryCreated.
|
||||
*
|
||||
* @extends ListCreatedEvent<ExternalEntry>
|
||||
*/
|
||||
class ExternalEntryCreated extends ListCreatedEvent
|
||||
{
|
||||
/**
|
||||
* The profile the entry belongs to.
|
||||
*
|
||||
* @var ExternalProfile
|
||||
*/
|
||||
protected ExternalProfile $profile;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param ExternalEntry $entry
|
||||
*/
|
||||
public function __construct(ExternalEntry $entry)
|
||||
{
|
||||
parent::__construct($entry);
|
||||
$this->profile = $entry->externalprofile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return ExternalEntry
|
||||
*/
|
||||
public function getModel(): ExternalEntry
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "Entry '**{$this->getModel()->getName()}**' has been created for External Profile '**{$this->profile->getName()}**'.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\ExternalProfile\ExternalEntry;
|
||||
|
||||
use App\Events\Base\List\ListDeletedEvent;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryDeleted.
|
||||
*
|
||||
* @extends ListDeletedEvent<ExternalEntry>
|
||||
*/
|
||||
class ExternalEntryDeleted extends ListDeletedEvent
|
||||
{
|
||||
/**
|
||||
* The profile the entry belongs to.
|
||||
*
|
||||
* @var ExternalProfile
|
||||
*/
|
||||
protected ExternalProfile $profile;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param ExternalEntry $entry
|
||||
*/
|
||||
public function __construct(ExternalEntry $entry)
|
||||
{
|
||||
parent::__construct($entry);
|
||||
$this->profile = $entry->externalprofile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return ExternalEntry
|
||||
*/
|
||||
public function getModel(): ExternalEntry
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "Entry '**{$this->getModel()->getName()}**' has been deleted for External Profile '**{$this->profile->getName()}**'.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\ExternalProfile\ExternalEntry;
|
||||
|
||||
use App\Events\Base\List\ListRestoredEvent;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryRestored.
|
||||
*
|
||||
* @extends ListRestoredEvent<ExternalEntry>
|
||||
*/
|
||||
class ExternalEntryRestored extends ListRestoredEvent
|
||||
{
|
||||
/**
|
||||
* The profile the entry belongs to.
|
||||
*
|
||||
* @var ExternalProfile
|
||||
*/
|
||||
protected ExternalProfile $profile;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param ExternalEntry $entry
|
||||
*/
|
||||
public function __construct(ExternalEntry $entry)
|
||||
{
|
||||
parent::__construct($entry);
|
||||
$this->profile = $entry->externalprofile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return ExternalEntry
|
||||
*/
|
||||
public function getModel(): ExternalEntry
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "Entry '**{$this->getModel()->getName()}**' has been restored for External Profile '**{$this->profile->getName()}**'.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\ExternalProfile\ExternalEntry;
|
||||
|
||||
use App\Events\Base\List\ListUpdatedEvent;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryUpdated.
|
||||
*
|
||||
* @extends ListUpdatedEvent<ExternalEntry>
|
||||
*/
|
||||
class ExternalEntryUpdated extends ListUpdatedEvent
|
||||
{
|
||||
/**
|
||||
* The profile the entry belongs to.
|
||||
*
|
||||
* @var ExternalProfile
|
||||
*/
|
||||
protected ExternalProfile $profile;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param ExternalEntry $entry
|
||||
*/
|
||||
public function __construct(ExternalEntry $entry)
|
||||
{
|
||||
parent::__construct($entry);
|
||||
$this->profile = $entry->externalprofile;
|
||||
$this->initializeEmbedFields($entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return ExternalEntry
|
||||
*/
|
||||
public function getModel(): ExternalEntry
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "Entry '**{$this->getModel()->getName()}**' has been updated for External Profile '**{$this->profile->getName()}**'.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\ExternalProfile;
|
||||
|
||||
use App\Events\Base\List\ListCreatedEvent;
|
||||
use App\Models\List\ExternalProfile;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileCreated.
|
||||
*
|
||||
* @extends ListCreatedEvent<ExternalProfile>
|
||||
*/
|
||||
class ExternalProfileCreated extends ListCreatedEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param ExternalProfile $profile
|
||||
*/
|
||||
public function __construct(ExternalProfile $profile)
|
||||
{
|
||||
parent::__construct($profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return ExternalProfile
|
||||
*/
|
||||
public function getModel(): ExternalProfile
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "External Profile '**{$this->getModel()->getName()}**' has been created.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\ExternalProfile;
|
||||
|
||||
use App\Events\Base\List\ListDeletedEvent;
|
||||
use App\Models\List\ExternalProfile;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileDeleted.
|
||||
*
|
||||
* @extends ListDeletedEvent<ExternalProfile>
|
||||
*/
|
||||
class ExternalProfileDeleted extends ListDeletedEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param ExternalProfile $profile
|
||||
*/
|
||||
public function __construct(ExternalProfile $profile)
|
||||
{
|
||||
parent::__construct($profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return ExternalProfile
|
||||
*/
|
||||
public function getModel(): ExternalProfile
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "External Profile '**{$this->getModel()->getName()}**' has been deleted.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\ExternalProfile;
|
||||
|
||||
use App\Events\Base\List\ListRestoredEvent;
|
||||
use App\Models\List\ExternalProfile;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileRestored.
|
||||
*
|
||||
* @extends ListRestoredEvent<ExternalProfile>
|
||||
*/
|
||||
class ExternalProfileRestored extends ListRestoredEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param ExternalProfile $profile
|
||||
*/
|
||||
public function __construct(ExternalProfile $profile)
|
||||
{
|
||||
parent::__construct($profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return ExternalProfile
|
||||
*/
|
||||
public function getModel(): ExternalProfile
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "External Profile '**{$this->getModel()->getName()}**' has been restored.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\ExternalProfile;
|
||||
|
||||
use App\Events\Base\List\ListUpdatedEvent;
|
||||
use App\Models\List\ExternalProfile;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileUpdated.
|
||||
*
|
||||
* @extends ListUpdatedEvent<ExternalProfile>
|
||||
*/
|
||||
class ExternalProfileUpdated extends ListUpdatedEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param ExternalProfile $profile
|
||||
*/
|
||||
public function __construct(ExternalProfile $profile)
|
||||
{
|
||||
parent::__construct($profile);
|
||||
$this->initializeEmbedFields($profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return ExternalProfile
|
||||
*/
|
||||
public function getModel(): ExternalProfile
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "External Profile '**{$this->getModel()->getName()}**' has been updated.";
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\Playlist;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Contracts\Events\AssignHashidsEvent;
|
||||
use App\Events\Base\BaseCreatedEvent;
|
||||
use App\Events\Base\List\ListCreatedEvent;
|
||||
use App\Models\List\Playlist;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class PlaylistCreated.
|
||||
*
|
||||
* @extends BaseCreatedEvent<Playlist>
|
||||
* @extends ListCreatedEvent<Playlist>
|
||||
*/
|
||||
class PlaylistCreated extends BaseCreatedEvent implements AssignHashidsEvent
|
||||
class PlaylistCreated extends ListCreatedEvent implements AssignHashidsEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
@@ -27,28 +25,6 @@ class PlaylistCreated extends BaseCreatedEvent implements AssignHashidsEvent
|
||||
parent::__construct($playlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Discord channel the message will be sent to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::ADMIN_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
|
||||
@@ -4,17 +4,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\Playlist;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Events\Base\BaseDeletedEvent;
|
||||
use App\Events\Base\List\ListDeletedEvent;
|
||||
use App\Models\List\Playlist;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class PlaylistDeleted.
|
||||
*
|
||||
* @extends BaseDeletedEvent<Playlist>
|
||||
* @extends ListDeletedEvent<Playlist>
|
||||
*/
|
||||
class PlaylistDeleted extends BaseDeletedEvent
|
||||
class PlaylistDeleted extends ListDeletedEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
@@ -26,16 +24,6 @@ class PlaylistDeleted extends BaseDeletedEvent
|
||||
parent::__construct($playlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Discord channel the message will be sent to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::ADMIN_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
|
||||
@@ -4,17 +4,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\Playlist;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Events\Base\BaseRestoredEvent;
|
||||
use App\Events\Base\List\ListRestoredEvent;
|
||||
use App\Models\List\Playlist;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class PlaylistRestored.
|
||||
*
|
||||
* @extends BaseRestoredEvent<Playlist>
|
||||
* @extends ListRestoredEvent<Playlist>
|
||||
*/
|
||||
class PlaylistRestored extends BaseRestoredEvent
|
||||
class PlaylistRestored extends ListRestoredEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
@@ -26,16 +24,6 @@ class PlaylistRestored extends BaseRestoredEvent
|
||||
parent::__construct($playlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Discord channel the message will be sent to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::ADMIN_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
|
||||
@@ -4,17 +4,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\Playlist;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Events\Base\BaseUpdatedEvent;
|
||||
use App\Events\Base\List\ListUpdatedEvent;
|
||||
use App\Models\List\Playlist;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class PlaylistUpdated.
|
||||
*
|
||||
* @extends BaseUpdatedEvent<Playlist>
|
||||
* @extends ListUpdatedEvent<Playlist>
|
||||
*/
|
||||
class PlaylistUpdated extends BaseUpdatedEvent
|
||||
class PlaylistUpdated extends ListUpdatedEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
@@ -27,28 +25,6 @@ class PlaylistUpdated extends BaseUpdatedEvent
|
||||
$this->initializeEmbedFields($playlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Discord channel the message will be sent to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::ADMIN_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldSendDiscordMessage(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
|
||||
@@ -4,19 +4,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\Playlist\Track;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Contracts\Events\AssignHashidsEvent;
|
||||
use App\Events\Base\BaseCreatedEvent;
|
||||
use App\Events\Base\List\ListCreatedEvent;
|
||||
use App\Models\List\Playlist;
|
||||
use App\Models\List\Playlist\PlaylistTrack;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class TrackCreated.
|
||||
*
|
||||
* @extends BaseCreatedEvent<PlaylistTrack>
|
||||
* @extends ListCreatedEvent<PlaylistTrack>
|
||||
*/
|
||||
class TrackCreated extends BaseCreatedEvent implements AssignHashidsEvent
|
||||
class TrackCreated extends ListCreatedEvent implements AssignHashidsEvent
|
||||
{
|
||||
/**
|
||||
* The playlist the track belongs to.
|
||||
@@ -36,16 +34,6 @@ class TrackCreated extends BaseCreatedEvent implements AssignHashidsEvent
|
||||
$this->playlist = $track->playlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Discord channel the message will be sent to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::ADMIN_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
|
||||
@@ -4,18 +4,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\Playlist\Track;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Events\Base\BaseDeletedEvent;
|
||||
use App\Events\Base\List\ListDeletedEvent;
|
||||
use App\Models\List\Playlist;
|
||||
use App\Models\List\Playlist\PlaylistTrack;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class TrackDeleted.
|
||||
*
|
||||
* @extends BaseDeletedEvent<PlaylistTrack>
|
||||
* @extends ListDeletedEvent<PlaylistTrack>
|
||||
*/
|
||||
class TrackDeleted extends BaseDeletedEvent
|
||||
class TrackDeleted extends ListDeletedEvent
|
||||
{
|
||||
/**
|
||||
* The playlist the track belongs to.
|
||||
@@ -35,16 +33,6 @@ class TrackDeleted extends BaseDeletedEvent
|
||||
$this->playlist = $track->playlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Discord channel the message will be sent to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::ADMIN_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
|
||||
@@ -4,18 +4,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\Playlist\Track;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Events\Base\BaseRestoredEvent;
|
||||
use App\Events\Base\List\ListRestoredEvent;
|
||||
use App\Models\List\Playlist;
|
||||
use App\Models\List\Playlist\PlaylistTrack;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class TrackRestored.
|
||||
*
|
||||
* @extends BaseRestoredEvent<PlaylistTrack>
|
||||
* @extends ListRestoredEvent<PlaylistTrack>
|
||||
*/
|
||||
class TrackRestored extends BaseRestoredEvent
|
||||
class TrackRestored extends ListRestoredEvent
|
||||
{
|
||||
/**
|
||||
* The playlist the track belongs to.
|
||||
@@ -35,16 +33,6 @@ class TrackRestored extends BaseRestoredEvent
|
||||
$this->playlist = $track->playlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Discord channel the message will be sent to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::ADMIN_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
|
||||
@@ -4,18 +4,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\Playlist\Track;
|
||||
|
||||
use App\Constants\Config\ServiceConstants;
|
||||
use App\Events\Base\BaseUpdatedEvent;
|
||||
use App\Events\Base\List\ListUpdatedEvent;
|
||||
use App\Models\List\Playlist;
|
||||
use App\Models\List\Playlist\PlaylistTrack;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class TrackUpdated.
|
||||
*
|
||||
* @extends BaseUpdatedEvent<PlaylistTrack>
|
||||
* @extends ListUpdatedEvent<PlaylistTrack>
|
||||
*/
|
||||
class TrackUpdated extends BaseUpdatedEvent
|
||||
class TrackUpdated extends ListUpdatedEvent
|
||||
{
|
||||
/**
|
||||
* The playlist the track belongs to.
|
||||
@@ -36,16 +34,6 @@ class TrackUpdated extends BaseUpdatedEvent
|
||||
$this->initializeEmbedFields($track);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Discord channel the message will be sent to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiscordChannel(): string
|
||||
{
|
||||
return Config::get(ServiceConstants::ADMIN_DISCORD_CHANNEL_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the message should be sent.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Features;
|
||||
|
||||
use App\Enums\Auth\SpecialPermission;
|
||||
use App\Models\Auth\User;
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
/**
|
||||
* Class AllowExternalProfileManagement.
|
||||
*/
|
||||
class AllowExternalProfileManagement
|
||||
{
|
||||
/**
|
||||
* Resolve feature for scope.
|
||||
*
|
||||
* @param User|null $user
|
||||
* @return bool
|
||||
*/
|
||||
public function resolve(?User $user): bool
|
||||
{
|
||||
if (! empty($user?->can(SpecialPermission::BYPASS_FEATURE_FLAGS->value))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Feature::for(null)->value(static::class);
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
<?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 $fromLabel = '';
|
||||
protected string $toLabel = '';
|
||||
|
||||
/**
|
||||
* 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->getName().'_'.'from')
|
||||
->label($this->fromLabel),
|
||||
|
||||
DatePicker::make($this->getName().'_'.'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->getName().'_'.'from'),
|
||||
fn (Builder $query, $date): Builder => $query->whereDate($this->getName(), ComparisonOperator::GTE->value, $date),
|
||||
)
|
||||
->when(
|
||||
Arr::get($data, $this->getName().'_'.'to'),
|
||||
fn (Builder $query, $date): Builder => $query->whereDate($this->getName(), ComparisonOperator::LTE->value, $date),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,9 @@ declare(strict_types=1);
|
||||
namespace App\Filament\Components\Filters;
|
||||
|
||||
use App\Enums\Http\Api\Filter\ComparisonOperator;
|
||||
use Filament\Forms\Components\Fieldset;
|
||||
use Filament\Forms\Components\Grid;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Tables\Filters\Filter;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
@@ -15,25 +18,6 @@ use Illuminate\Support\Arr;
|
||||
*/
|
||||
class NumberFilter extends Filter
|
||||
{
|
||||
protected string $fromLabel = '';
|
||||
protected string $toLabel = '';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -42,13 +26,22 @@ class NumberFilter extends Filter
|
||||
public function getFormSchema(): array
|
||||
{
|
||||
return [
|
||||
TextInput::make($this->getName().'_'.'from')
|
||||
->label($this->fromLabel)
|
||||
->integer(),
|
||||
|
||||
TextInput::make($this->getName().'_'.'to')
|
||||
->label($this->toLabel)
|
||||
->integer(),
|
||||
Fieldset::make($this->getLabel())
|
||||
->label($this->getLabel())
|
||||
->schema([
|
||||
Grid::make([
|
||||
'sm' => 2,
|
||||
])
|
||||
->schema([
|
||||
TextInput::make('from')
|
||||
->label(__('filament.filters.base.from'))
|
||||
->integer(),
|
||||
|
||||
TextInput::make('to')
|
||||
->label(__('filament.filters.base.to'))
|
||||
->integer(),
|
||||
])
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -63,11 +56,11 @@ class NumberFilter extends Filter
|
||||
{
|
||||
return $query
|
||||
->when(
|
||||
Arr::get($data, $this->getName().'_'.'from'),
|
||||
Arr::get($data, 'from'),
|
||||
fn (Builder $query, $value): Builder => $query->where($this->getName(), ComparisonOperator::GTE->value, $value),
|
||||
)
|
||||
->when(
|
||||
Arr::get($data, $this->getName().'_'.'to'),
|
||||
Arr::get($data, 'to'),
|
||||
fn (Builder $query, $value): Builder => $query->where($this->getName(), ComparisonOperator::LTE->value, $value),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace App\Filament\Dashboards;
|
||||
|
||||
use App\Enums\Auth\Role as RoleEnum;
|
||||
use App\Filament\Widgets\Auth\UserChart;
|
||||
use App\Filament\Widgets\List\ExternalProfileChart;
|
||||
use App\Filament\Widgets\List\PlaylistChart;
|
||||
use App\Filament\Widgets\List\PlaylistTrackChart;
|
||||
use App\Models\Auth\User;
|
||||
@@ -65,6 +66,7 @@ class AdminDashboard extends BaseDashboard
|
||||
{
|
||||
return [
|
||||
UserChart::class,
|
||||
ExternalProfileChart::class,
|
||||
PlaylistChart::class,
|
||||
PlaylistTrackChart::class,
|
||||
];
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\RelationManagers\List\External;
|
||||
|
||||
use App\Filament\RelationManagers\BaseRelationManager;
|
||||
use App\Filament\Resources\List\External\ExternalEntry as ExternalEntryResource;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryRelationManager.
|
||||
*/
|
||||
abstract class ExternalEntryRelationManager extends BaseRelationManager
|
||||
{
|
||||
/**
|
||||
* The form to the actions.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function form(Form $form): Form
|
||||
{
|
||||
return ExternalEntryResource::form($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* The index page of the resource.
|
||||
*
|
||||
* @param Table $table
|
||||
* @return Table
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return parent::table(
|
||||
$table
|
||||
->heading(ExternalEntryResource::getPluralLabel())
|
||||
->modelLabel(ExternalEntryResource::getLabel())
|
||||
->recordTitleAttribute(ExternalEntry::ATTRIBUTE_ID)
|
||||
->columns(ExternalEntryResource::table($table)->getColumns())
|
||||
->defaultSort(ExternalEntry::TABLE . '.' . ExternalEntry::ATTRIBUTE_ID, 'desc')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filters available for the relation.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getFilters(): array
|
||||
{
|
||||
return array_merge(
|
||||
[],
|
||||
ExternalEntryResource::getFilters(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actions available for the relation.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getActions(),
|
||||
ExternalEntryResource::getActions(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bulk actions available for the relation.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getBulkActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getBulkActions(),
|
||||
ExternalEntryResource::getBulkActions(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header actions available for the relation.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
ExternalEntryResource::getHeaderActions(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\RelationManagers\List;
|
||||
|
||||
use App\Filament\RelationManagers\BaseRelationManager;
|
||||
use App\Filament\Resources\List\ExternalProfile as ExternalProfileResource;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileRelationManager.
|
||||
*/
|
||||
abstract class ExternalProfileRelationManager extends BaseRelationManager
|
||||
{
|
||||
/**
|
||||
* The form to the actions.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function form(Form $form): Form
|
||||
{
|
||||
return ExternalProfileResource::form($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* The index page of the resource.
|
||||
*
|
||||
* @param Table $table
|
||||
* @return Table
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return parent::table(
|
||||
$table
|
||||
->heading(ExternalProfileResource::getPluralLabel())
|
||||
->modelLabel(ExternalProfileResource::getLabel())
|
||||
->recordTitleAttribute(ExternalProfile::ATTRIBUTE_NAME)
|
||||
->columns(ExternalProfileResource::table($table)->getColumns())
|
||||
->defaultSort(ExternalProfile::TABLE . '.' . ExternalProfile::ATTRIBUTE_ID, 'desc')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filters available for the relation.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getFilters(): array
|
||||
{
|
||||
return array_merge(
|
||||
[],
|
||||
ExternalProfileResource::getFilters(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actions available for the relation.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getActions(),
|
||||
ExternalProfileResource::getActions(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bulk actions available for the relation.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getBulkActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getBulkActions(),
|
||||
ExternalProfileResource::getBulkActions(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header actions available for the relation.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
ExternalProfileResource::getHeaderActions(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ namespace App\Filament\Resources\Admin;
|
||||
use App\Enums\Actions\ActionLogStatus;
|
||||
use App\Enums\Auth\Role;
|
||||
use App\Filament\Components\Columns\TextColumn;
|
||||
use App\Filament\Components\Filters\DateFilter;
|
||||
use App\Filament\Components\Infolist\TextEntry;
|
||||
use App\Filament\Resources\BaseResource;
|
||||
use App\Filament\Resources\Admin\ActionLog\Pages\ListActionLogs;
|
||||
@@ -25,6 +24,7 @@ use Filament\Resources\RelationManagers\RelationGroup;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Malzariey\FilamentDaterangepickerFilter\Filters\DateRangeFilter;
|
||||
|
||||
/**
|
||||
* Class ActionLog.
|
||||
@@ -269,11 +269,11 @@ class ActionLog extends BaseResource
|
||||
->label(__('filament.fields.action_log.status'))
|
||||
->options(ActionLogStatus::asSelectArray()),
|
||||
|
||||
DateFilter::make(BaseModel::ATTRIBUTE_CREATED_AT)
|
||||
->labels(__('filament.filters.action_log.happened_at_from'), __('filament.filters.action_log.happened_at_to')),
|
||||
DateRangeFilter::make(BaseModel::ATTRIBUTE_CREATED_AT)
|
||||
->label(__('filament.fields.action_log.happened_at')),
|
||||
|
||||
DateFilter::make(ActionLogModel::ATTRIBUTE_FINISHED_AT)
|
||||
->labels(__('filament.filters.action_log.finished_at_from'), __('filament.filters.action_log.finished_at_to')),
|
||||
DateRangeFilter::make(ActionLogModel::ATTRIBUTE_FINISHED_AT)
|
||||
->label(__('filament.fields.action_log.finished_at')),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -22,15 +22,16 @@ use App\Models\Auth\User;
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry as EntryModel;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Pivots\Wiki\AnimeThemeEntryVideo;
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use App\Rules\Admin\StartDateBeforeEndDateRule;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Infolists\Components\Section;
|
||||
use Filament\Infolists\Infolist;
|
||||
use Filament\Resources\RelationManagers\RelationGroup;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Malzariey\FilamentDaterangepickerFilter\Fields\DateRangePicker;
|
||||
|
||||
/**
|
||||
* Class FeaturedTheme.
|
||||
@@ -130,33 +131,26 @@ class FeaturedTheme extends BaseResource
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
DatePicker::make(FeaturedThemeModel::ATTRIBUTE_START_AT)
|
||||
->label(__('filament.fields.featured_theme.start_at.name'))
|
||||
->helperText(__('filament.fields.featured_theme.start_at.help'))
|
||||
DateRangePicker::make('date')
|
||||
->label(__('filament.fields.featured_theme.date.name'))
|
||||
->helperText(__('filament.fields.featured_theme.date.help'))
|
||||
->timezone('UTC')
|
||||
->displayFormat('MM/DD/YYYY')
|
||||
->format(AllowedDateFormat::YMD->value)
|
||||
->required()
|
||||
->rules([
|
||||
'required',
|
||||
Str::of('date_format:')
|
||||
->append(implode(',', $allowedDateFormats))
|
||||
->__toString(),
|
||||
Str::of('before:')
|
||||
->append(FeaturedThemeModel::ATTRIBUTE_END_AT)
|
||||
->__toString(),
|
||||
]),
|
||||
|
||||
DatePicker::make(FeaturedThemeModel::ATTRIBUTE_END_AT)
|
||||
->label(__('filament.fields.featured_theme.end_at.name'))
|
||||
->helperText(__('filament.fields.featured_theme.end_at.help'))
|
||||
->required()
|
||||
->rules([
|
||||
'required',
|
||||
Str::of('date_format:')
|
||||
->append(implode(',', $allowedDateFormats))
|
||||
->__toString(),
|
||||
Str::of('after:')
|
||||
->append(FeaturedThemeModel::ATTRIBUTE_START_AT)
|
||||
->__toString(),
|
||||
]),
|
||||
'string',
|
||||
'regex:/^\d{2}\/\d{2}\/\d{4} - \d{2}\/\d{2}\/\d{4}$/',
|
||||
new StartDateBeforeEndDateRule(),
|
||||
])
|
||||
->saveRelationshipsUsing(function (FeaturedThemeModel $record, string $state) {
|
||||
$dates = explode(' - ', $state);
|
||||
|
||||
$record->start_at = Carbon::createFromFormat('m/d/Y', $dates[0]);
|
||||
$record->end_at = Carbon::createFromFormat('m/d/Y', $dates[1]);
|
||||
$record->save();
|
||||
}),
|
||||
|
||||
Select::make(FeaturedThemeModel::ATTRIBUTE_ENTRY)
|
||||
->label(__('filament.resources.singularLabel.anime_theme_entry'))
|
||||
@@ -234,13 +228,13 @@ class FeaturedTheme extends BaseResource
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make(FeaturedThemeModel::ATTRIBUTE_START_AT)
|
||||
->label(__('filament.fields.featured_theme.start_at.name'))
|
||||
->label(__('filament.fields.featured_theme.start_at'))
|
||||
->sortable()
|
||||
->date()
|
||||
->toggleable(),
|
||||
|
||||
TextColumn::make(FeaturedThemeModel::ATTRIBUTE_END_AT)
|
||||
->label(__('filament.fields.featured_theme.end_at.name'))
|
||||
->label(__('filament.fields.featured_theme.end_at'))
|
||||
->sortable()
|
||||
->date()
|
||||
->toggleable(),
|
||||
@@ -284,11 +278,11 @@ class FeaturedTheme extends BaseResource
|
||||
->label(__('filament.fields.base.id')),
|
||||
|
||||
TextEntry::make(FeaturedThemeModel::ATTRIBUTE_START_AT)
|
||||
->label(__('filament.fields.featured_theme.start_at.name'))
|
||||
->label(__('filament.fields.featured_theme.start_at'))
|
||||
->date(),
|
||||
|
||||
TextEntry::make(FeaturedThemeModel::ATTRIBUTE_END_AT)
|
||||
->label(__('filament.fields.featured_theme.end_at.name'))
|
||||
->label(__('filament.fields.featured_theme.end_at'))
|
||||
->date(),
|
||||
|
||||
TextEntry::make(FeaturedThemeModel::RELATION_VIDEO.'.'.Video::ATTRIBUTE_FILENAME)
|
||||
|
||||
@@ -272,7 +272,7 @@ class Role extends BaseResource
|
||||
->checkbox(),
|
||||
|
||||
NumberFilter::make(RoleModel::ATTRIBUTE_PRIORITY)
|
||||
->labels(__('filament.filters.role.priority_from'), __('filament.filters.role.priority_to')),
|
||||
->label(__('filament.fields.role.priority.name')),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ use App\Filament\Actions\Base\ViewAction;
|
||||
use App\Filament\BulkActions\Base\DeleteBulkAction;
|
||||
use App\Filament\BulkActions\Base\ForceDeleteBulkAction;
|
||||
use App\Filament\BulkActions\Base\RestoreBulkAction;
|
||||
use App\Filament\Components\Filters\DateFilter;
|
||||
use App\Filament\RelationManagers\Base\ActionLogRelationManager;
|
||||
use App\Models\BaseModel;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
@@ -25,6 +24,7 @@ use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
use Malzariey\FilamentDaterangepickerFilter\Filters\DateRangeFilter;
|
||||
|
||||
/**
|
||||
* Class BaseResource.
|
||||
@@ -100,14 +100,14 @@ abstract class BaseResource extends Resource
|
||||
return [
|
||||
TrashedFilter::make(),
|
||||
|
||||
DateFilter::make(BaseModel::ATTRIBUTE_CREATED_AT)
|
||||
->labels(__('filament.filters.base.created_at_from'), __('filament.filters.base.created_at_to')),
|
||||
DateRangeFilter::make(BaseModel::ATTRIBUTE_CREATED_AT)
|
||||
->label(__('filament.fields.base.created_at')),
|
||||
|
||||
DateFilter::make(BaseModel::ATTRIBUTE_UPDATED_AT)
|
||||
->labels(__('filament.filters.base.updated_at_from'), __('filament.filters.base.updated_at_to')),
|
||||
DateRangeFilter::make(BaseModel::ATTRIBUTE_UPDATED_AT)
|
||||
->label(__('filament.fields.base.updated_at')),
|
||||
|
||||
DateFilter::make(BaseModel::ATTRIBUTE_DELETED_AT)
|
||||
->labels(__('filament.filters.base.deleted_at_from'), __('filament.filters.base.deleted_at_to')),
|
||||
DateRangeFilter::make(BaseModel::ATTRIBUTE_DELETED_AT)
|
||||
->label(__('filament.fields.base.deleted_at')),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List\External;
|
||||
|
||||
use App\Enums\Models\List\AnimeWatchStatus;
|
||||
use App\Filament\Components\Columns\TextColumn;
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Filament\Components\Infolist\TextEntry;
|
||||
use App\Filament\Resources\BaseResource;
|
||||
use App\Filament\Resources\List\External\ExternalEntry\Pages\CreateExternalEntry;
|
||||
use App\Filament\Resources\List\External\ExternalEntry\Pages\EditExternalEntry;
|
||||
use App\Filament\Resources\List\External\ExternalEntry\Pages\ListExternalEntries;
|
||||
use App\Filament\Resources\List\External\ExternalEntry\Pages\ViewExternalEntry;
|
||||
use App\Filament\Resources\List\External\RelationManagers\ExternalEntryExternalProfileRelationManager;
|
||||
use App\Filament\Resources\List\ExternalProfile as ExternalProfileResource;
|
||||
use App\Filament\Resources\Wiki\Anime;
|
||||
use App\Models\List\External\ExternalEntry as ExternalEntryModel;
|
||||
use App\Models\List\ExternalProfile as ExternalProfileModel;
|
||||
use App\Models\Wiki\Anime as AnimeModel;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Infolists\Components\IconEntry;
|
||||
use Filament\Infolists\Components\Section;
|
||||
use Filament\Infolists\Infolist;
|
||||
use Filament\Resources\RelationManagers\RelationGroup;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
/**
|
||||
* Class ExternalEntry.
|
||||
*/
|
||||
class ExternalEntry extends BaseResource
|
||||
{
|
||||
/**
|
||||
* The model the resource corresponds to.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected static ?string $model = ExternalEntryModel::class;
|
||||
|
||||
/**
|
||||
* Get the displayable singular label of the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getLabel(): string
|
||||
{
|
||||
return __('filament.resources.singularLabel.external_entry');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displayable label of the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getPluralLabel(): string
|
||||
{
|
||||
return __('filament.resources.label.external_entries');
|
||||
}
|
||||
|
||||
/**
|
||||
* The logical group associated with the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getNavigationGroup(): string
|
||||
{
|
||||
return __('filament.resources.group.list');
|
||||
}
|
||||
|
||||
/**
|
||||
* The icon displayed to the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getNavigationIcon(): string
|
||||
{
|
||||
return __('filament.resources.icon.external_entries');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the slug (URI key) for the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getRecordSlug(): string
|
||||
{
|
||||
return 'external-entries';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route key for the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getRecordRouteKeyName(): string
|
||||
{
|
||||
return ExternalEntryModel::ATTRIBUTE_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* The form to the actions.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(ExternalEntryModel::ATTRIBUTE_EXTERNAL_PROFILE)
|
||||
->label(__('filament.resources.singularLabel.external_profile'))
|
||||
->useScout(ExternalProfileModel::class)
|
||||
->required()
|
||||
->rules(['required'])
|
||||
->hiddenOn([ExternalEntryExternalProfileRelationManager::class])
|
||||
->createOptionForm(ExternalProfileResource::form($form)->getComponents()),
|
||||
|
||||
Select::make(ExternalEntryModel::ATTRIBUTE_ANIME)
|
||||
->label(__('filament.resources.singularLabel.anime'))
|
||||
->useScout(AnimeModel::class)
|
||||
->required()
|
||||
->rules(['required']),
|
||||
|
||||
TextInput::make(ExternalEntryModel::ATTRIBUTE_SCORE)
|
||||
->label(__('filament.fields.external_entry.score.name'))
|
||||
->helperText(__('filament.fields.external_entry.score.help'))
|
||||
->numeric(),
|
||||
|
||||
Select::make(ExternalEntryModel::ATTRIBUTE_WATCH_STATUS)
|
||||
->label(__('filament.fields.external_entry.watch_status.name'))
|
||||
->helperText(__('filament.fields.external_entry.watch_status.help'))
|
||||
->options(AnimeWatchStatus::asSelectArray())
|
||||
->rules([new Enum(AnimeWatchStatus::class)]),
|
||||
|
||||
Checkbox::make(ExternalEntryModel::ATTRIBUTE_IS_FAVORITE)
|
||||
->label(__('filament.fields.external_entry.is_favorite.name'))
|
||||
->helperText(__('filament.fields.external_entry.is_favorite.help'))
|
||||
->rules(['boolean']),
|
||||
])
|
||||
->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(ExternalEntryModel::ATTRIBUTE_EXTERNAL_PROFILE)
|
||||
->label(__('filament.resources.singularLabel.external_profile'))
|
||||
->urlToRelated(ExternalProfileResource::class, ExternalEntryModel::RELATION_EXTERNAL_PROFILE, true)
|
||||
->sortable()
|
||||
->toggleable(),
|
||||
|
||||
TextColumn::make(ExternalEntryModel::ATTRIBUTE_ANIME)
|
||||
->label(__('filament.resources.singularLabel.anime'))
|
||||
->urlToRelated(Anime::class, ExternalEntryModel::RELATION_ANIME, true)
|
||||
->sortable()
|
||||
->toggleable(),
|
||||
|
||||
IconColumn::make(ExternalEntryModel::ATTRIBUTE_IS_FAVORITE)
|
||||
->label(__('filament.fields.external_entry.is_favorite.name'))
|
||||
->boolean()
|
||||
->sortable()
|
||||
->toggleable(),
|
||||
|
||||
TextColumn::make(ExternalEntryModel::ATTRIBUTE_SCORE)
|
||||
->label(__('filament.fields.external_entry.score.name'))
|
||||
->sortable()
|
||||
->toggleable(),
|
||||
|
||||
TextColumn::make(ExternalEntryModel::ATTRIBUTE_WATCH_STATUS)
|
||||
->label(__('filament.fields.external_entry.watch_status.name'))
|
||||
->formatStateUsing(fn (AnimeWatchStatus $state) => $state->localize())
|
||||
->sortable()
|
||||
->toggleable(),
|
||||
|
||||
TextColumn::make(ExternalEntryModel::ATTRIBUTE_ID)
|
||||
->label(__('filament.fields.base.id'))
|
||||
->sortable()
|
||||
->toggleable(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ExternalEntryModel::ATTRIBUTE_EXTERNAL_PROFILE)
|
||||
->label(__('filament.resources.singularLabel.external_profile'))
|
||||
->urlToRelated(ExternalProfileResource::class, ExternalEntryModel::RELATION_EXTERNAL_PROFILE, true),
|
||||
|
||||
TextEntry::make(ExternalEntryModel::ATTRIBUTE_ANIME)
|
||||
->label(__('filament.resources.singularLabel.anime'))
|
||||
->urlToRelated(Anime::class, ExternalEntryModel::RELATION_ANIME, true),
|
||||
|
||||
IconEntry::make(ExternalEntryModel::ATTRIBUTE_IS_FAVORITE)
|
||||
->label(__('filament.fields.external_entry.is_favorite.name')),
|
||||
|
||||
TextEntry::make(ExternalEntryModel::ATTRIBUTE_SCORE)
|
||||
->label(__('filament.fields.external_entry.score.name')),
|
||||
|
||||
TextEntry::make(ExternalEntryModel::ATTRIBUTE_WATCH_STATUS)
|
||||
->label(__('filament.fields.external_entry.watch_status.name'))
|
||||
->formatStateUsing(fn (AnimeWatchStatus $state) => $state->localize()),
|
||||
|
||||
TextEntry::make(ExternalEntryModel::ATTRIBUTE_ID)
|
||||
->label(__('filament.fields.base.id')),
|
||||
])
|
||||
->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 [
|
||||
RelationGroup::make(
|
||||
static::getLabel(),
|
||||
array_merge(
|
||||
[],
|
||||
parent::getBaseRelations(),
|
||||
)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 header actions available for the resource.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pages available for the resource.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListExternalEntries::route('/'),
|
||||
'create' => CreateExternalEntry::route('/create'),
|
||||
'view' => ViewExternalEntry::route('/{record:entry_id}'),
|
||||
'edit' => EditExternalEntry::route('/{record:entry_id}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List\External\ExternalEntry\Pages;
|
||||
|
||||
use App\Filament\Resources\Base\BaseCreateResource;
|
||||
use App\Filament\Resources\List\External\ExternalEntry;
|
||||
|
||||
/**
|
||||
* Class CreateExternalEntry.
|
||||
*/
|
||||
class CreateExternalEntry extends BaseCreateResource
|
||||
{
|
||||
protected static string $resource = ExternalEntry::class;
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List\External\ExternalEntry\Pages;
|
||||
|
||||
use App\Filament\Resources\List\External\ExternalEntry;
|
||||
use App\Filament\Resources\Base\BaseEditResource;
|
||||
|
||||
/**
|
||||
* Class EditExternalEntry.
|
||||
*/
|
||||
class EditExternalEntry extends BaseEditResource
|
||||
{
|
||||
protected static string $resource = ExternalEntry::class;
|
||||
|
||||
/**
|
||||
* Get the header actions available.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List\External\ExternalEntry\Pages;
|
||||
|
||||
use App\Filament\Resources\Base\BaseListResources;
|
||||
use App\Filament\Resources\List\External\ExternalEntry;
|
||||
|
||||
/**
|
||||
* Class ListExternalEntries.
|
||||
*/
|
||||
class ListExternalEntries extends BaseListResources
|
||||
{
|
||||
protected static string $resource = ExternalEntry::class;
|
||||
|
||||
/**
|
||||
* Get the header actions available.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List\External\ExternalEntry\Pages;
|
||||
|
||||
use App\Filament\Resources\Base\BaseViewResource;
|
||||
use App\Filament\Resources\List\External\ExternalEntry;
|
||||
|
||||
/**
|
||||
* Class ViewExternalEntry.
|
||||
*/
|
||||
class ViewExternalEntry extends BaseViewResource
|
||||
{
|
||||
protected static string $resource = ExternalEntry::class;
|
||||
|
||||
/**
|
||||
* Get the header actions available.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List\External\Pages;
|
||||
|
||||
use App\Filament\Resources\Base\BaseCreateResource;
|
||||
use App\Filament\Resources\List\ExternalProfile;
|
||||
|
||||
/**
|
||||
* Class CreateExternalProfile.
|
||||
*/
|
||||
class CreateExternalProfile extends BaseCreateResource
|
||||
{
|
||||
protected static string $resource = ExternalProfile::class;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List\External\Pages;
|
||||
|
||||
use App\Filament\Resources\List\ExternalProfile;
|
||||
use App\Filament\Resources\Base\BaseEditResource;
|
||||
|
||||
/**
|
||||
* Class EditExternalProfile.
|
||||
*/
|
||||
class EditExternalProfile extends BaseEditResource
|
||||
{
|
||||
protected static string $resource = ExternalProfile::class;
|
||||
|
||||
/**
|
||||
* Get the header actions available.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List\External\Pages;
|
||||
|
||||
use App\Filament\Resources\Base\BaseListResources;
|
||||
use App\Filament\Resources\List\ExternalProfile;
|
||||
use App\Models\List\ExternalProfile as ExternalProfileModel;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* Class ListExternalProfiles.
|
||||
*/
|
||||
class ListExternalProfiles extends BaseListResources
|
||||
{
|
||||
protected static string $resource = ExternalProfile::class;
|
||||
|
||||
/**
|
||||
* Get the header actions available.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Using Laravel Scout to search.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
protected function applySearchToTableQuery(Builder $query): Builder
|
||||
{
|
||||
$this->applyColumnSearchesToTableQuery($query);
|
||||
|
||||
if (filled($search = $this->getTableSearch())) {
|
||||
$query->whereIn(ExternalProfileModel::ATTRIBUTE_ID, ExternalProfileModel::search($search)->take(25)->keys());
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List\External\Pages;
|
||||
|
||||
use App\Filament\Resources\Base\BaseViewResource;
|
||||
use App\Filament\Resources\List\ExternalProfile;
|
||||
|
||||
/**
|
||||
* Class ViewExternalProfile.
|
||||
*/
|
||||
class ViewExternalProfile extends BaseViewResource
|
||||
{
|
||||
protected static string $resource = ExternalProfile::class;
|
||||
|
||||
/**
|
||||
* Get the header actions available.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List\External\RelationManagers;
|
||||
|
||||
use App\Filament\RelationManagers\List\External\ExternalEntryRelationManager;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryExternalProfileRelationManager.
|
||||
*/
|
||||
class ExternalEntryExternalProfileRelationManager extends ExternalEntryRelationManager
|
||||
{
|
||||
/**
|
||||
* The relationship the relation manager corresponds to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static string $relationship = ExternalProfile::RELATION_EXTERNAL_ENTRIES;
|
||||
|
||||
/**
|
||||
* The index page of the resource.
|
||||
*
|
||||
* @param Table $table
|
||||
* @return Table
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return parent::table(
|
||||
$table
|
||||
->inverseRelationship(ExternalEntry::RELATION_EXTERNAL_PROFILE)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filters available for the relation.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getFilters(): array
|
||||
{
|
||||
return array_merge(
|
||||
[],
|
||||
parent::getFilters(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actions available for the relation.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bulk actions available for the relation.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getBulkActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getBulkActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header actions available for the relation.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List;
|
||||
|
||||
use App\Enums\Http\Api\Filter\ComparisonOperator;
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Filament\Components\Columns\TextColumn;
|
||||
use App\Filament\Components\Fields\Select;
|
||||
use App\Filament\Components\Infolist\TextEntry;
|
||||
use App\Filament\Resources\Auth\User;
|
||||
use App\Filament\Resources\BaseResource;
|
||||
use App\Filament\Resources\List\External\Pages\CreateExternalProfile;
|
||||
use App\Filament\Resources\List\External\Pages\EditExternalProfile;
|
||||
use App\Filament\Resources\List\External\Pages\ListExternalProfiles;
|
||||
use App\Filament\Resources\List\External\Pages\ViewExternalProfile;
|
||||
use App\Filament\Resources\List\External\RelationManagers\ExternalEntryExternalProfileRelationManager;
|
||||
use App\Models\Auth\User as UserModel;
|
||||
use App\Models\List\ExternalProfile as ExternalProfileModel;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Infolists\Components\Section;
|
||||
use Filament\Infolists\Infolist;
|
||||
use Filament\Resources\RelationManagers\RelationGroup;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
/**
|
||||
* Class ExternalProfile.
|
||||
*/
|
||||
class ExternalProfile extends BaseResource
|
||||
{
|
||||
/**
|
||||
* The model the resource corresponds to.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected static ?string $model = ExternalProfileModel::class;
|
||||
|
||||
/**
|
||||
* Get the displayable singular label of the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getLabel(): string
|
||||
{
|
||||
return __('filament.resources.singularLabel.external_profile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displayable label of the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getPluralLabel(): string
|
||||
{
|
||||
return __('filament.resources.label.external_profiles');
|
||||
}
|
||||
|
||||
/**
|
||||
* The logical group associated with the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getNavigationGroup(): string
|
||||
{
|
||||
return __('filament.resources.group.list');
|
||||
}
|
||||
|
||||
/**
|
||||
* The icon displayed to the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getNavigationIcon(): string
|
||||
{
|
||||
return __('filament.resources.icon.external_profiles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the slug (URI key) for the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getRecordSlug(): string
|
||||
{
|
||||
return 'external-profiles';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route key for the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getRecordRouteKeyName(): string
|
||||
{
|
||||
return ExternalProfileModel::ATTRIBUTE_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* The form to the actions.
|
||||
*
|
||||
* @param Form $form
|
||||
* @return Form
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->schema([
|
||||
Select::make(ExternalProfileModel::ATTRIBUTE_USER)
|
||||
->label(__('filament.resources.singularLabel.user'))
|
||||
->relationship(ExternalProfileModel::RELATION_USER, UserModel::ATTRIBUTE_NAME)
|
||||
->allowHtml()
|
||||
->searchable()
|
||||
->getSearchResultsUsing(function (string $search) {
|
||||
return UserModel::query()
|
||||
->where(UserModel::ATTRIBUTE_NAME, ComparisonOperator::LIKE->value, "%$search%")
|
||||
->get()
|
||||
->mapWithKeys(fn (UserModel $model) => [$model->getKey() => Select::getSearchLabelWithBlade($model)])
|
||||
->toArray();
|
||||
}),
|
||||
|
||||
TextInput::make(ExternalProfileModel::ATTRIBUTE_NAME)
|
||||
->label(__('filament.fields.external_profile.name.name'))
|
||||
->helperText(__('filament.fields.external_profile.name.help'))
|
||||
->required()
|
||||
->maxLength(192)
|
||||
->rules(['required', 'max:192']),
|
||||
|
||||
Select::make(ExternalProfileModel::ATTRIBUTE_SITE)
|
||||
->label(__('filament.fields.external_profile.site.name'))
|
||||
->helperText(__('filament.fields.external_profile.site.help'))
|
||||
->options(ExternalProfileSite::asSelectArray())
|
||||
->required()
|
||||
->rules(['required', new Enum(ExternalProfileSite::class)]),
|
||||
|
||||
Select::make(ExternalProfileModel::ATTRIBUTE_VISIBILITY)
|
||||
->label(__('filament.fields.external_profile.visibility.name'))
|
||||
->helperText(__('filament.fields.external_profile.visibility.help'))
|
||||
->options(ExternalProfileVisibility::asSelectArray())
|
||||
->required()
|
||||
->rules(['required', new Enum(ExternalProfileVisibility::class)]),
|
||||
])
|
||||
->columns(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ExternalProfileModel::ATTRIBUTE_USER)
|
||||
->label(__('filament.resources.singularLabel.user'))
|
||||
->urlToRelated(User::class, ExternalProfileModel::RELATION_USER, true)
|
||||
->sortable()
|
||||
->toggleable()
|
||||
->placeholder('-'),
|
||||
|
||||
TextColumn::make(ExternalProfileModel::ATTRIBUTE_NAME)
|
||||
->label(__('filament.fields.external_profile.name.name'))
|
||||
->sortable()
|
||||
->toggleable(),
|
||||
|
||||
TextColumn::make(ExternalProfileModel::ATTRIBUTE_SITE)
|
||||
->label(__('filament.fields.external_profile.site.name'))
|
||||
->formatStateUsing(fn (ExternalProfileSite $state) => $state->localize())
|
||||
->sortable()
|
||||
->toggleable(),
|
||||
|
||||
TextColumn::make(ExternalProfileModel::ATTRIBUTE_VISIBILITY)
|
||||
->label(__('filament.fields.external_profile.visibility.name'))
|
||||
->formatStateUsing(fn (ExternalProfileVisibility $state) => $state->localize())
|
||||
->sortable()
|
||||
->toggleable(),
|
||||
|
||||
TextColumn::make(ExternalProfileModel::ATTRIBUTE_ID)
|
||||
->label(__('filament.fields.base.id'))
|
||||
->sortable()
|
||||
->toggleable(),
|
||||
])
|
||||
->searchable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ExternalProfileModel::ATTRIBUTE_USER)
|
||||
->label(__('filament.resources.singularLabel.user'))
|
||||
->urlToRelated(User::class, ExternalProfileModel::RELATION_USER, true)
|
||||
->placeholder('-'),
|
||||
|
||||
TextEntry::make(ExternalProfileModel::ATTRIBUTE_NAME)
|
||||
->label(__('filament.fields.external_profile.name.name')),
|
||||
|
||||
TextEntry::make(ExternalProfileModel::ATTRIBUTE_SITE)
|
||||
->label(__('filament.fields.external_profile.site.name'))
|
||||
->formatStateUsing(fn (ExternalProfileSite $state) => $state->localize()),
|
||||
|
||||
TextEntry::make(ExternalProfileModel::ATTRIBUTE_VISIBILITY)
|
||||
->label(__('filament.fields.external_profile.visibility.name'))
|
||||
->formatStateUsing(fn (ExternalProfileVisibility $state) => $state->localize()),
|
||||
|
||||
TextEntry::make(ExternalProfileModel::ATTRIBUTE_ID)
|
||||
->label(__('filament.fields.base.id')),
|
||||
])
|
||||
->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 [
|
||||
RelationGroup::make(
|
||||
static::getLabel(),
|
||||
array_merge(
|
||||
[
|
||||
ExternalEntryExternalProfileRelationManager::class,
|
||||
],
|
||||
parent::getBaseRelations(),
|
||||
)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 header actions available for the resource.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getHeaderActions(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getHeaderActions(),
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pages available for the resource.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListExternalProfiles::route('/'),
|
||||
'create' => CreateExternalProfile::route('/create'),
|
||||
'view' => ViewExternalProfile::route('/{record:profile_id}'),
|
||||
'edit' => EditExternalProfile::route('/{record:profile_id}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\List;
|
||||
|
||||
use App\Enums\Http\Api\Filter\ComparisonOperator;
|
||||
use App\Enums\Models\List\PlaylistVisibility;
|
||||
use App\Filament\Actions\Models\AssignHashidsAction;
|
||||
use App\Filament\Components\Columns\TextColumn;
|
||||
@@ -129,7 +130,15 @@ class Playlist extends BaseResource
|
||||
Select::make(PlaylistModel::ATTRIBUTE_USER)
|
||||
->label(__('filament.resources.singularLabel.user'))
|
||||
->relationship(PlaylistModel::RELATION_USER, UserModel::ATTRIBUTE_NAME)
|
||||
->searchable(),
|
||||
->allowHtml()
|
||||
->searchable()
|
||||
->getSearchResultsUsing(function (string $search) {
|
||||
return UserModel::query()
|
||||
->where(UserModel::ATTRIBUTE_NAME, ComparisonOperator::LIKE->value, "%$search%")
|
||||
->get()
|
||||
->mapWithKeys(fn (UserModel $model) => [$model->getKey() => Select::getSearchLabelWithBlade($model)])
|
||||
->toArray();
|
||||
}),
|
||||
|
||||
TextInput::make(PlaylistModel::ATTRIBUTE_NAME)
|
||||
->label(__('filament.fields.playlist.name.name'))
|
||||
|
||||
@@ -381,7 +381,7 @@ class Anime extends BaseResource
|
||||
return array_merge(
|
||||
[
|
||||
NumberFilter::make(AnimeModel::ATTRIBUTE_YEAR)
|
||||
->labels(__('filament.filters.anime.year_from'), __('filament.filters.anime.year_to')),
|
||||
->label(__('filament.fields.anime.year.name')),
|
||||
|
||||
SelectFilter::make(AnimeModel::ATTRIBUTE_SEASON)
|
||||
->label(__('filament.fields.anime.season.name'))
|
||||
|
||||
@@ -502,7 +502,7 @@ class Theme extends BaseResource
|
||||
->options(ThemeType::asSelectArray()),
|
||||
|
||||
NumberFilter::make(ThemeModel::ATTRIBUTE_SEQUENCE)
|
||||
->labels(__('filament.filters.anime_theme.sequence_from'), __('filament.filters.anime_theme.sequence_to')),
|
||||
->label(__('filament.fields.anime_theme.sequence.name')),
|
||||
],
|
||||
parent::getFilters(),
|
||||
);
|
||||
|
||||
@@ -364,7 +364,7 @@ class Entry extends BaseResource
|
||||
return array_merge(
|
||||
[
|
||||
NumberFilter::make(EntryModel::ATTRIBUTE_VERSION)
|
||||
->labels(__('filament.filters.anime_theme_entry.version_from'), __('filament.filters.anime_theme_entry.version_to')),
|
||||
->label(__('filament.fields.anime_theme_entry.version.name')),
|
||||
|
||||
TextFilter::make(EntryModel::ATTRIBUTE_EPISODES)
|
||||
->label(__('filament.fields.anime_theme_entry.episodes.name')),
|
||||
|
||||
@@ -241,7 +241,7 @@ class Audio extends BaseResource
|
||||
return array_merge(
|
||||
[
|
||||
NumberFilter::make(AudioModel::ATTRIBUTE_SIZE)
|
||||
->labels(__('filament.filters.audio.size_from'), __('filament.filters.audio.size_to')),
|
||||
->label(__('filament.fields.audio.size.name')),
|
||||
],
|
||||
parent::getFilters(),
|
||||
);
|
||||
|
||||
@@ -276,7 +276,7 @@ class ExternalResource extends BaseResource
|
||||
->options(ResourceSite::asSelectArray()),
|
||||
|
||||
NumberFilter::make(ExternalResourceModel::ATTRIBUTE_EXTERNAL_ID)
|
||||
->labels(__('filament.filters.external_resource.external_id_from'), __('filament.filters.external_resource.external_id_to')),
|
||||
->label(__('filament.fields.external_resource.external_id.name')),
|
||||
],
|
||||
parent::getFilters(),
|
||||
);
|
||||
|
||||
@@ -326,7 +326,7 @@ class Video extends BaseResource
|
||||
return array_merge(
|
||||
[
|
||||
NumberFilter::make(VideoModel::ATTRIBUTE_RESOLUTION)
|
||||
->labels(__('filament.filters.video.resolution_from'), __('filament.filters.video.resolution_to')),
|
||||
->label(__('filament.fields.video.resolution.name')),
|
||||
|
||||
Filter::make(VideoModel::ATTRIBUTE_NC)
|
||||
->label(__('filament.fields.video.nc.name'))
|
||||
@@ -353,7 +353,7 @@ class Video extends BaseResource
|
||||
->options(VideoSource::asSelectArray()),
|
||||
|
||||
NumberFilter::make(VideoModel::ATTRIBUTE_SIZE)
|
||||
->labels(__('filament.filters.video.size_from'), __('filament.filters.video.size_to')),
|
||||
->label(__('filament.fields.video.size.name')),
|
||||
],
|
||||
parent::getFilters(),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets\List;
|
||||
|
||||
use App\Filament\Widgets\BaseChartWidget;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Flowframe\Trend\TrendValue;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileChart.
|
||||
*/
|
||||
class ExternalProfileChart extends BaseChartWidget
|
||||
{
|
||||
/**
|
||||
* Chart Id.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static string $chartId = 'externalProfileChart';
|
||||
|
||||
/**
|
||||
* Get the displayed label of the widget.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getHeading(): string
|
||||
{
|
||||
return __('filament.resources.label.external_profiles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart options (series, labels, types, size, animations...)
|
||||
* https://apexcharts.com/docs/options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions(): array
|
||||
{
|
||||
$data = $this->perMonth(ExternalProfile::class);
|
||||
|
||||
return [
|
||||
'chart' => [
|
||||
'type' => 'area',
|
||||
'height' => 300,
|
||||
],
|
||||
'series' => [
|
||||
[
|
||||
'name' => $this->getHeading(),
|
||||
'data' => $data->map(fn (TrendValue $value) => $value->aggregate),
|
||||
],
|
||||
],
|
||||
'xaxis' => [
|
||||
'categories' => $data->map(fn (TrendValue $value) => $this->translateDate($value->date)),
|
||||
'labels' => [
|
||||
'style' => [
|
||||
'fontFamily' => 'inherit',
|
||||
],
|
||||
],
|
||||
],
|
||||
'yaxis' => [
|
||||
'labels' => [
|
||||
'style' => [
|
||||
'fontFamily' => 'inherit',
|
||||
],
|
||||
],
|
||||
],
|
||||
'colors' => ['#71E9C5'],
|
||||
'stroke' => [
|
||||
'curve' => 'smooth',
|
||||
],
|
||||
'dataLabels' => [
|
||||
'enabled' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\List\ExternalProfile\ExternalEntry;
|
||||
|
||||
use App\Contracts\Http\Api\Field\CreatableField;
|
||||
use App\Contracts\Http\Api\Field\SelectableField;
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\Wiki\Anime;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryAnimeIdField.
|
||||
*/
|
||||
class ExternalEntryAnimeIdField extends Field implements CreatableField, SelectableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, ExternalEntry::ATTRIBUTE_ANIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the field should be included in the select clause of our query.
|
||||
*
|
||||
* @param Query $query
|
||||
* @param Schema $schema
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldSelect(Query $query, Schema $schema): bool
|
||||
{
|
||||
// Needed to match playlist relation.
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getCreationRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'required',
|
||||
'integer',
|
||||
Rule::exists(Anime::class, Anime::ATTRIBUTE_ID),
|
||||
];
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\List\ExternalProfile\ExternalEntry;
|
||||
|
||||
use App\Contracts\Http\Api\Field\SelectableField;
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryExternalProfileIdField.
|
||||
*/
|
||||
class ExternalEntryExternalProfileIdField extends Field implements SelectableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, ExternalEntry::ATTRIBUTE_EXTERNAL_PROFILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the field should be included in the select clause of our query.
|
||||
*
|
||||
* @param Query $query
|
||||
* @param Schema $schema
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldSelect(Query $query, Schema $schema): bool
|
||||
{
|
||||
// Needed to match playlist relation.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\List\ExternalProfile\ExternalEntry;
|
||||
|
||||
use App\Contracts\Http\Api\Field\SelectableField;
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryField.
|
||||
*/
|
||||
class ExternalEntryIdField extends Field implements SelectableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, ExternalEntry::ATTRIBUTE_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the field should be included in the select clause of our query.
|
||||
*
|
||||
* @param Query $query
|
||||
* @param Schema $schema
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldSelect(Query $query, Schema $schema): bool
|
||||
{
|
||||
// We can only exclude ID fields for top-level models that are not including related resources.
|
||||
$includeCriteria = $query->getIncludeCriteria($this->schema->type());
|
||||
if (
|
||||
$this->schema->type() === $schema->type()
|
||||
&& ($includeCriteria === null || $includeCriteria->getPaths()->isEmpty())
|
||||
) {
|
||||
$criteria = $query->getFieldCriteria($this->schema->type());
|
||||
|
||||
return $criteria === null || $criteria->isAllowedField($this->getKey());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\List\ExternalProfile\ExternalEntry;
|
||||
|
||||
use App\Contracts\Http\Api\Field\CreatableField;
|
||||
use App\Contracts\Http\Api\Field\UpdatableField;
|
||||
use App\Http\Api\Field\BooleanField;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryIsFavoriteField.
|
||||
*/
|
||||
class ExternalEntryIsFavoriteField extends BooleanField implements CreatableField, UpdatableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, ExternalEntry::ATTRIBUTE_IS_FAVORITE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the creation validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getCreationRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'required',
|
||||
'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the update validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getUpdateRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'sometimes',
|
||||
'required',
|
||||
'boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\List\ExternalProfile\ExternalEntry;
|
||||
|
||||
use App\Contracts\Http\Api\Field\CreatableField;
|
||||
use App\Contracts\Http\Api\Field\UpdatableField;
|
||||
use App\Http\Api\Field\FloatField;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Rules\ModerationRule;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryScoreField.
|
||||
*/
|
||||
class ExternalEntryScoreField extends FloatField implements CreatableField, UpdatableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, ExternalEntry::ATTRIBUTE_SCORE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the creation validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getCreationRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'required',
|
||||
'decimal:0,2',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the update validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getUpdateRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'sometimes',
|
||||
'decimal:0,2',
|
||||
];
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\List\ExternalProfile\ExternalEntry;
|
||||
|
||||
use App\Contracts\Http\Api\Field\CreatableField;
|
||||
use App\Contracts\Http\Api\Field\UpdatableField;
|
||||
use App\Enums\Models\List\AnimeWatchStatus;
|
||||
use App\Http\Api\Field\EnumField;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryWatchStatusField.
|
||||
*/
|
||||
class ExternalEntryWatchStatusField extends EnumField implements CreatableField, UpdatableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, ExternalEntry::ATTRIBUTE_WATCH_STATUS, AnimeWatchStatus::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the creation validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getCreationRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'required',
|
||||
new Enum(AnimeWatchStatus::class),
|
||||
];
|
||||
}
|
||||
|
||||
public function getUpdateRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'sometimes',
|
||||
new Enum(AnimeWatchStatus::class),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\List\ExternalProfile;
|
||||
|
||||
use App\Contracts\Http\Api\Field\SelectableField;
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\List\ExternalProfile;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileIdField.
|
||||
*/
|
||||
class ExternalProfileIdField extends Field implements SelectableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, ExternalProfile::ATTRIBUTE_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the field should be included in the select clause of our query.
|
||||
*
|
||||
* @param Query $query
|
||||
* @param Schema $schema
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldSelect(Query $query, Schema $schema): bool
|
||||
{
|
||||
// We can only exclude ID fields for top-level models that are not including related resources.
|
||||
$includeCriteria = $query->getIncludeCriteria($this->schema->type());
|
||||
if (
|
||||
$this->schema->type() === $schema->type()
|
||||
&& ($includeCriteria === null || $includeCriteria->getPaths()->isEmpty())
|
||||
) {
|
||||
$criteria = $query->getFieldCriteria($this->schema->type());
|
||||
|
||||
return $criteria === null || $criteria->isAllowedField($this->getKey());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\List\ExternalProfile;
|
||||
|
||||
use App\Contracts\Http\Api\Field\CreatableField;
|
||||
use App\Contracts\Http\Api\Field\UpdatableField;
|
||||
use App\Http\Api\Field\StringField;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Rules\ModerationRule;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileNameField.
|
||||
*/
|
||||
class ExternalProfileNameField extends StringField implements CreatableField, UpdatableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, ExternalProfile::ATTRIBUTE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the creation validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getCreationRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'required',
|
||||
'string',
|
||||
'max:192',
|
||||
new ModerationRule(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the update validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getUpdateRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'sometimes',
|
||||
'required',
|
||||
'string',
|
||||
'max:192',
|
||||
new ModerationRule(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\List\ExternalProfile;
|
||||
|
||||
use App\Contracts\Http\Api\Field\CreatableField;
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Http\Api\Field\EnumField;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileSiteField.
|
||||
*/
|
||||
class ExternalProfileSiteField extends EnumField implements CreatableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, ExternalProfile::ATTRIBUTE_SITE, ExternalProfileSite::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the creation validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getCreationRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'required',
|
||||
new Enum(ExternalProfileSite::class),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\List\ExternalProfile;
|
||||
|
||||
use App\Contracts\Http\Api\Field\SelectableField;
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\List\ExternalProfile;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileUserIdField.
|
||||
*/
|
||||
class ExternalProfileUserIdField extends Field implements SelectableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, ExternalProfile::ATTRIBUTE_USER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the field should be included in the select clause of our query.
|
||||
*
|
||||
* @param Query $query
|
||||
* @param Schema $schema
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldSelect(Query $query, Schema $schema): bool
|
||||
{
|
||||
// Needed to match user relation.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\List\ExternalProfile;
|
||||
|
||||
use App\Contracts\Http\Api\Field\CreatableField;
|
||||
use App\Contracts\Http\Api\Field\UpdatableField;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Http\Api\Field\EnumField;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileVisibilityField.
|
||||
*/
|
||||
class ExternalProfileVisibilityField extends EnumField implements CreatableField, UpdatableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, ExternalProfile::ATTRIBUTE_VISIBILITY, ExternalProfileVisibility::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the creation validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getCreationRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'required',
|
||||
new Enum(ExternalProfileVisibility::class),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the update validation rules for the field.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getUpdateRules(Request $request): array
|
||||
{
|
||||
return [
|
||||
'sometimes',
|
||||
'required',
|
||||
new Enum(ExternalProfileVisibility::class),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ use App\Http\Api\Include\AllowedInclude;
|
||||
use App\Http\Api\Schema\Auth\PermissionSchema;
|
||||
use App\Http\Api\Schema\Auth\RoleSchema;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\List\ExternalProfileSchema;
|
||||
use App\Http\Api\Schema\List\PlaylistSchema;
|
||||
use App\Http\Resources\Auth\User\Resource\MyResource;
|
||||
use App\Models\Auth\User;
|
||||
@@ -41,6 +42,7 @@ class MySchema extends EloquentSchema
|
||||
public function allowedIncludes(): array
|
||||
{
|
||||
return [
|
||||
new AllowedInclude(new ExternalProfileSchema(), User::RELATION_EXTERNAL_PROFILES),
|
||||
new AllowedInclude(new PermissionSchema(), User::RELATION_PERMISSIONS),
|
||||
new AllowedInclude(new PlaylistSchema(), User::RELATION_PLAYLISTS),
|
||||
new AllowedInclude(new RoleSchema(), User::RELATION_ROLES),
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Schema\List\External;
|
||||
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Api\Field\List\ExternalProfile\ExternalEntry\ExternalEntryAnimeIdField;
|
||||
use App\Http\Api\Field\List\ExternalProfile\ExternalEntry\ExternalEntryExternalProfileIdField;
|
||||
use App\Http\Api\Field\List\ExternalProfile\ExternalEntry\ExternalEntryWatchStatusField;
|
||||
use App\Http\Api\Field\List\ExternalProfile\ExternalEntry\ExternalEntryIdField;
|
||||
use App\Http\Api\Field\List\ExternalProfile\ExternalEntry\ExternalEntryIsFavoriteField;
|
||||
use App\Http\Api\Field\List\ExternalProfile\ExternalEntry\ExternalEntryScoreField;
|
||||
use App\Http\Api\Include\AllowedInclude;
|
||||
use App\Http\Api\Schema\Auth\UserSchema;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\List\ExternalProfileSchema;
|
||||
use App\Http\Api\Schema\Wiki\AnimeSchema;
|
||||
use App\Http\Resources\List\External\Resource\ExternalEntryResource;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
|
||||
/**
|
||||
* Class ExternalEntrySchema.
|
||||
*/
|
||||
class ExternalEntrySchema extends EloquentSchema
|
||||
{
|
||||
/**
|
||||
* Get the type of the resource.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return ExternalEntryResource::$wrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the allowed includes.
|
||||
*
|
||||
* @return AllowedInclude[]
|
||||
*/
|
||||
public function allowedIncludes(): array
|
||||
{
|
||||
return [
|
||||
new AllowedInclude(new AnimeSchema(), ExternalEntry::RELATION_ANIME),
|
||||
new AllowedInclude(new ExternalProfileSchema(), ExternalEntry::RELATION_EXTERNAL_PROFILE),
|
||||
new AllowedInclude(new UserSchema(), ExternalEntry::RELATION_USER),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the direct fields of the resource.
|
||||
*
|
||||
* @return Field[]
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::fields(),
|
||||
[
|
||||
new ExternalEntryIdField($this),
|
||||
new ExternalEntryScoreField($this),
|
||||
new ExternalEntryIsFavoriteField($this),
|
||||
new ExternalEntryWatchStatusField($this),
|
||||
new ExternalEntryAnimeIdField($this),
|
||||
new ExternalEntryExternalProfileIdField($this),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Schema\List;
|
||||
|
||||
use App\Contracts\Http\Api\Schema\SearchableSchema;
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Api\Field\List\ExternalProfile\ExternalProfileNameField;
|
||||
use App\Http\Api\Field\List\ExternalProfile\ExternalProfileIdField;
|
||||
use App\Http\Api\Field\List\ExternalProfile\ExternalProfileVisibilityField;
|
||||
use App\Http\Api\Field\List\ExternalProfile\ExternalProfileSiteField;
|
||||
use App\Http\Api\Field\List\ExternalProfile\ExternalProfileUserIdField;
|
||||
use App\Http\Api\Include\AllowedInclude;
|
||||
use App\Http\Api\Schema\Auth\UserSchema;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Api\Schema\List\External\ExternalEntrySchema;
|
||||
use App\Http\Api\Schema\Wiki\AnimeSchema;
|
||||
use App\Http\Resources\List\Resource\ExternalProfileResource;
|
||||
use App\Models\List\ExternalProfile;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileSchema.
|
||||
*/
|
||||
class ExternalProfileSchema extends EloquentSchema implements SearchableSchema
|
||||
{
|
||||
/**
|
||||
* Get the type of the resource.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return ExternalProfileResource::$wrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the allowed includes.
|
||||
*
|
||||
* @return AllowedInclude[]
|
||||
*/
|
||||
public function allowedIncludes(): array
|
||||
{
|
||||
return [
|
||||
new AllowedInclude(new AnimeSchema(), ExternalProfile::RELATION_ANIMES),
|
||||
new AllowedInclude(new ExternalEntrySchema(), ExternalProfile::RELATION_EXTERNAL_ENTRIES),
|
||||
new AllowedInclude(new UserSchema(), ExternalProfile::RELATION_USER),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the direct fields of the resource.
|
||||
*
|
||||
* @return Field[]
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::fields(),
|
||||
[
|
||||
new ExternalProfileIdField($this),
|
||||
new ExternalProfileNameField($this),
|
||||
new ExternalProfileSiteField($this),
|
||||
new ExternalProfileVisibilityField($this),
|
||||
new ExternalProfileUserIdField($this),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\Auth\User\Me\List;
|
||||
|
||||
use App\Actions\Http\Api\IndexAction;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Api\Schema\List\ExternalProfileSchema;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Http\Controllers\Api\BaseController;
|
||||
use App\Http\Middleware\Auth\Authenticate;
|
||||
use App\Http\Requests\Api\IndexRequest;
|
||||
use App\Http\Resources\List\Collection\ExternalProfileCollection;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
/**
|
||||
* Class MyExternalProfileController.
|
||||
*/
|
||||
class MyExternalProfileController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware(Authenticate::using('sanctum'));
|
||||
parent::__construct(ExternalProfile::class, 'externalprofile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param IndexRequest $request
|
||||
* @param IndexAction $action
|
||||
* @return ExternalProfileCollection
|
||||
*/
|
||||
public function index(IndexRequest $request, IndexAction $action): ExternalProfileCollection
|
||||
{
|
||||
$query = new Query($request->validated());
|
||||
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
$builder = $user->externalprofiles()->getQuery();
|
||||
|
||||
$profiles = $action->index($builder, $query, $request->schema());
|
||||
|
||||
return new ExternalProfileCollection($profiles, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying schema.
|
||||
*
|
||||
* @return Schema
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function schema(): Schema
|
||||
{
|
||||
return new ExternalProfileSchema();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\List\External;
|
||||
|
||||
use App\Actions\Http\Api\DestroyAction;
|
||||
use App\Actions\Http\Api\ForceDeleteAction;
|
||||
use App\Actions\Http\Api\IndexAction;
|
||||
use App\Actions\Http\Api\RestoreAction;
|
||||
use App\Actions\Http\Api\ShowAction;
|
||||
use App\Actions\Http\Api\StoreAction;
|
||||
use App\Actions\Http\Api\UpdateAction;
|
||||
use App\Features\AllowExternalProfileManagement;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Controllers\Api\BaseController;
|
||||
use App\Http\Middleware\Api\EnabledOnlyOnLocalhost;
|
||||
use App\Http\Middleware\Models\List\ExternalProfileExceedsEntryLimit;
|
||||
use App\Http\Requests\Api\IndexRequest;
|
||||
use App\Http\Requests\Api\ShowRequest;
|
||||
use App\Http\Requests\Api\StoreRequest;
|
||||
use App\Http\Requests\Api\UpdateRequest;
|
||||
use App\Http\Resources\List\External\Collection\ExternalEntryCollection;
|
||||
use App\Http\Resources\List\External\Resource\ExternalEntryResource;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryController.
|
||||
*/
|
||||
class ExternalEntryController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(ExternalEntry::class, 'externalentry,externalprofile');
|
||||
|
||||
$isExternalProfileManagementAllowed = Str::of(EnsureFeaturesAreActive::class)
|
||||
->append(':')
|
||||
->append(AllowExternalProfileManagement::class)
|
||||
->__toString();
|
||||
|
||||
$this->middleware(EnabledOnlyOnLocalhost::class);
|
||||
$this->middleware($isExternalProfileManagementAllowed)->except(['index', 'show']);
|
||||
$this->middleware(ExternalProfileExceedsEntryLimit::class)->only(['store', 'restore']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param IndexRequest $request
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param IndexAction $action
|
||||
* @return ExternalEntryCollection
|
||||
*/
|
||||
public function index(IndexRequest $request, ExternalProfile $externalprofile, IndexAction $action): ExternalEntryCollection
|
||||
{
|
||||
$query = new Query($request->validated());
|
||||
|
||||
$builder = ExternalEntry::query()->where(ExternalEntry::ATTRIBUTE_EXTERNAL_PROFILE, $externalprofile->getKey());
|
||||
|
||||
$resources = $action->index($builder, $query, $request->schema());
|
||||
|
||||
return new ExternalEntryCollection($resources, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource.
|
||||
*
|
||||
* @param StoreRequest $request
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param StoreAction $action
|
||||
* @return ExternalEntryResource
|
||||
*/
|
||||
public function store(StoreRequest $request, ExternalProfile $externalprofile, StoreAction $action): ExternalEntryResource
|
||||
{
|
||||
$validated = array_merge(
|
||||
$request->validated(),
|
||||
[ExternalEntry::ATTRIBUTE_EXTERNAL_PROFILE => $externalprofile->getKey()]
|
||||
);
|
||||
|
||||
$externalentry = $action->store(ExternalEntry::query(), $validated);
|
||||
|
||||
return new ExternalEntryResource($externalentry, new Query());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param ShowRequest $request
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param ExternalEntry $externalentry
|
||||
* @param ShowAction $action
|
||||
* @return ExternalEntryResource
|
||||
*/
|
||||
public function show(ShowRequest $request, ExternalProfile $externalprofile, ExternalEntry $externalentry, ShowAction $action): ExternalEntryResource
|
||||
{
|
||||
$query = new Query($request->validated());
|
||||
|
||||
$show = $action->show($externalentry, $query, $request->schema());
|
||||
|
||||
return new ExternalEntryResource($show, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource.
|
||||
*
|
||||
* @param UpdateRequest $request
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param ExternalEntry $externalentry
|
||||
* @param UpdateAction $action
|
||||
* @return ExternalEntryResource
|
||||
*/
|
||||
public function update(UpdateRequest $request, ExternalProfile $externalprofile, ExternalEntry $externalentry, UpdateAction $action): ExternalEntryResource
|
||||
{
|
||||
$updated = $action->update($externalentry, $request->validated());
|
||||
|
||||
return new ExternalEntryResource($updated, new Query());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource.
|
||||
*
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param ExternalEntry $externalentry
|
||||
* @param DestroyAction $action
|
||||
* @return ExternalEntryResource
|
||||
*/
|
||||
public function destroy(ExternalProfile $externalprofile, ExternalEntry $externalentry, DestroyAction $action): ExternalEntryResource
|
||||
{
|
||||
$deleted = $action->destroy($externalentry);
|
||||
|
||||
return new ExternalEntryResource($deleted, new Query());
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the specified resource.
|
||||
*
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param ExternalEntry $externalentry
|
||||
* @param RestoreAction $action
|
||||
* @return ExternalEntryResource
|
||||
*/
|
||||
public function restore(ExternalProfile $externalprofile, ExternalEntry $externalentry, RestoreAction $action): ExternalEntryResource
|
||||
{
|
||||
$restored = $action->restore($externalentry);
|
||||
|
||||
return new ExternalEntryResource($restored, new Query());
|
||||
}
|
||||
|
||||
/**
|
||||
* Hard-delete the specified resource.
|
||||
*
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param ExternalEntry $externalentry
|
||||
* @param ForceDeleteAction $action
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function forceDelete(ExternalProfile $externalprofile, ExternalEntry $externalentry, ForceDeleteAction $action): JsonResponse
|
||||
{
|
||||
$message = $action->forceDelete($externalentry);
|
||||
|
||||
return new JsonResponse([
|
||||
'message' => $message,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\List;
|
||||
|
||||
use App\Actions\Http\Api\DestroyAction;
|
||||
use App\Actions\Http\Api\ForceDeleteAction;
|
||||
use App\Actions\Http\Api\IndexAction;
|
||||
use App\Actions\Http\Api\RestoreAction;
|
||||
use App\Actions\Http\Api\ShowAction;
|
||||
use App\Actions\Http\Api\UpdateAction;
|
||||
use App\Actions\Models\List\ExternalProfile\StoreExternalProfileAction;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Features\AllowExternalProfileManagement;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Controllers\Api\BaseController;
|
||||
use App\Http\Middleware\Api\EnabledOnlyOnLocalhost;
|
||||
use App\Http\Middleware\Models\List\UserExceedsExternalProfileLimit;
|
||||
use App\Http\Requests\Api\IndexRequest;
|
||||
use App\Http\Requests\Api\ShowRequest;
|
||||
use App\Http\Requests\Api\StoreRequest;
|
||||
use App\Http\Requests\Api\UpdateRequest;
|
||||
use App\Http\Resources\List\Collection\ExternalProfileCollection;
|
||||
use App\Http\Resources\List\Resource\ExternalProfileResource;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileController.
|
||||
*/
|
||||
class ExternalProfileController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(ExternalProfile::class, 'externalprofile');
|
||||
|
||||
$isExternalProfileManagementAllowed = Str::of(EnsureFeaturesAreActive::class)
|
||||
->append(':')
|
||||
->append(AllowExternalProfileManagement::class)
|
||||
->__toString();
|
||||
|
||||
$this->middleware(EnabledOnlyOnLocalhost::class);
|
||||
$this->middleware($isExternalProfileManagementAllowed)->except(['index', 'show']);
|
||||
$this->middleware(UserExceedsExternalProfileLimit::class)->only(['store', 'restore']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param IndexRequest $request
|
||||
* @param IndexAction $action
|
||||
* @return ExternalProfileCollection
|
||||
*/
|
||||
public function index(IndexRequest $request, IndexAction $action): ExternalProfileCollection
|
||||
{
|
||||
$query = new Query($request->validated());
|
||||
|
||||
$builder = ExternalProfile::query()->where(ExternalProfile::ATTRIBUTE_VISIBILITY, ExternalProfileVisibility::PUBLIC->value);
|
||||
|
||||
$externalprofiles = $query->hasSearchCriteria()
|
||||
? $action->search($query, $request->schema())
|
||||
: $action->index($builder, $query, $request->schema());
|
||||
|
||||
return new ExternalProfileCollection($externalprofiles, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource.
|
||||
*
|
||||
* @param StoreRequest $request
|
||||
* @param StoreExternalProfileAction $action
|
||||
* @return ExternalProfileResource
|
||||
*/
|
||||
public function store(StoreRequest $request, StoreExternalProfileAction $action): ExternalProfileResource
|
||||
{
|
||||
$validated = array_merge(
|
||||
$request->validated(),
|
||||
[ExternalProfile::ATTRIBUTE_USER => Auth::id()]
|
||||
);
|
||||
|
||||
$externalprofile = $action->store(ExternalProfile::query(), $validated);
|
||||
|
||||
return new ExternalProfileResource($externalprofile, new Query());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param ShowRequest $request
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param ShowAction $action
|
||||
* @return ExternalProfileResource
|
||||
*/
|
||||
public function show(ShowRequest $request, ExternalProfile $externalprofile, ShowAction $action): ExternalProfileResource
|
||||
{
|
||||
$query = new Query($request->validated());
|
||||
|
||||
$show = $action->show($externalprofile, $query, $request->schema());
|
||||
|
||||
return new ExternalProfileResource($show, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource.
|
||||
*
|
||||
* @param UpdateRequest $request
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param UpdateAction $action
|
||||
* @return ExternalProfileResource
|
||||
*/
|
||||
public function update(UpdateRequest $request, ExternalProfile $externalprofile, UpdateAction $action): ExternalProfileResource
|
||||
{
|
||||
$updated = $action->update($externalprofile, $request->validated());
|
||||
|
||||
return new ExternalProfileResource($updated, new Query());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource.
|
||||
*
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param DestroyAction $action
|
||||
* @return ExternalProfileResource
|
||||
*/
|
||||
public function destroy(ExternalProfile $externalprofile, DestroyAction $action): ExternalProfileResource
|
||||
{
|
||||
$deleted = $action->destroy($externalprofile);
|
||||
|
||||
return new ExternalProfileResource($deleted, new Query());
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the specified resource.
|
||||
*
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param RestoreAction $action
|
||||
* @return ExternalProfileResource
|
||||
*/
|
||||
public function restore(ExternalProfile $externalprofile, RestoreAction $action): ExternalProfileResource
|
||||
{
|
||||
$restored = $action->restore($externalprofile);
|
||||
|
||||
return new ExternalProfileResource($restored, new Query());
|
||||
}
|
||||
|
||||
/**
|
||||
* Hard-delete the specified resource.
|
||||
*
|
||||
* @param ExternalProfile $externalprofile
|
||||
* @param ForceDeleteAction $action
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function forceDelete(ExternalProfile $externalprofile, ForceDeleteAction $action): JsonResponse
|
||||
{
|
||||
$message = $action->forceDelete($externalprofile);
|
||||
|
||||
return new JsonResponse([
|
||||
'message' => $message,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware\Api;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class EnabledOnlyOnLocalhost.
|
||||
*/
|
||||
class EnabledOnlyOnLocalhost
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure(Request): mixed $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): mixed
|
||||
{
|
||||
$ip = $request->ip();
|
||||
|
||||
if ($ip !== '127.0.0.1') {
|
||||
abort(403, "Route only available for localhost");
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware\Models\List;
|
||||
|
||||
use App\Constants\Config\ExternalProfileConstants;
|
||||
use App\Enums\Auth\SpecialPermission;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileExceedsEntryLimit.
|
||||
*/
|
||||
class ExternalProfileExceedsEntryLimit
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure(Request): mixed $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): mixed
|
||||
{
|
||||
$entryLimit = intval(Config::get(ExternalProfileConstants::MAX_ENTRIES_QUALIFIED));
|
||||
|
||||
/** @var ExternalProfile|null $profile */
|
||||
$profile = $request->route('externalprofile');
|
||||
|
||||
/** @var User|null $user */
|
||||
$user = $request->user('sanctum');
|
||||
|
||||
if (
|
||||
intval($profile?->externalentries()?->count()) >= $entryLimit
|
||||
&& empty($user?->can(SpecialPermission::BYPASS_FEATURE_FLAGS->value))
|
||||
) {
|
||||
abort(403, "External profiles cannot contain more than '$entryLimit' entries.");
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Middleware\Models\List;
|
||||
|
||||
use App\Constants\Config\ExternalProfileConstants;
|
||||
use App\Enums\Auth\SpecialPermission;
|
||||
use App\Models\Auth\User;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class UserExceedsExternalProfileLimit.
|
||||
*/
|
||||
class UserExceedsExternalProfileLimit
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure(Request): mixed $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): mixed
|
||||
{
|
||||
$profileLimit = intval(Config::get(ExternalProfileConstants::MAX_PROFILES_QUALIFIED));
|
||||
|
||||
/** @var User|null $user */
|
||||
$user = $request->user('sanctum');
|
||||
|
||||
if (
|
||||
intval($user?->externalprofiles()?->count()) >= $profileLimit
|
||||
&& empty($user?->can(SpecialPermission::BYPASS_FEATURE_FLAGS->value))
|
||||
) {
|
||||
abort(403, "User cannot have more than '$profileLimit' external profiles.");
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\List\Collection;
|
||||
|
||||
use App\Http\Resources\BaseCollection;
|
||||
use App\Http\Resources\List\Resource\ExternalProfileResource;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileCollection.
|
||||
*/
|
||||
class ExternalProfileCollection extends BaseCollection
|
||||
{
|
||||
/**
|
||||
* The "data" wrapper that should be applied.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public static $wrap = 'externalprofiles';
|
||||
|
||||
/**
|
||||
* Transform the resource into a JSON array.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return $this->collection->map(fn (ExternalProfile $profile) => new ExternalProfileResource($profile, $this->query))->all();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\List\External\Collection;
|
||||
|
||||
use App\Http\Resources\BaseCollection;
|
||||
use App\Http\Resources\List\External\Resource\ExternalEntryResource;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryCollection.
|
||||
*/
|
||||
class ExternalEntryCollection extends BaseCollection
|
||||
{
|
||||
/**
|
||||
* The "data" wrapper that should be applied.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public static $wrap = 'externalentries';
|
||||
|
||||
/**
|
||||
* Transform the resource into a JSON array.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return $this->collection->map(fn (ExternalEntry $entry) => new ExternalEntryResource($entry, $this->query))->all();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\List\External\Resource;
|
||||
|
||||
use App\Http\Api\Schema\List\External\ExternalEntrySchema;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Http\Resources\BaseResource;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryResource.
|
||||
*/
|
||||
class ExternalEntryResource extends BaseResource
|
||||
{
|
||||
/**
|
||||
* The "data" wrapper that should be applied.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public static $wrap = 'externalentry';
|
||||
|
||||
/**
|
||||
* Get the resource schema.
|
||||
*
|
||||
* @return Schema
|
||||
*/
|
||||
protected function schema(): Schema
|
||||
{
|
||||
return new ExternalEntrySchema();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\List\Resource;
|
||||
|
||||
use App\Http\Api\Schema\List\ExternalProfileSchema;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Http\Resources\BaseResource;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileResource.
|
||||
*/
|
||||
class ExternalProfileResource extends BaseResource
|
||||
{
|
||||
/**
|
||||
* The "data" wrapper that should be applied.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public static $wrap = 'externalprofile';
|
||||
|
||||
/**
|
||||
* Get the resource schema.
|
||||
*
|
||||
* @return Schema
|
||||
*/
|
||||
protected function schema(): Schema
|
||||
{
|
||||
return new ExternalProfileSchema();
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ use App\Events\Auth\User\UserRestored;
|
||||
use App\Events\Auth\User\UserUpdated;
|
||||
use App\Models\Admin\ActionLog;
|
||||
use App\Models\List\Playlist;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Database\Factories\Auth\UserFactory;
|
||||
use Filament\Models\Contracts\FilamentUser;
|
||||
use Filament\Models\Contracts\HasAvatar;
|
||||
@@ -39,6 +40,7 @@ use Spatie\Permission\Traits\HasRoles;
|
||||
* @property Carbon $deleted_at
|
||||
* @property string $email
|
||||
* @property Carbon|null $email_verified_at
|
||||
* @property Collection<int, ExternalProfile> $externalprofiles
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property string $password
|
||||
@@ -76,6 +78,7 @@ class User extends Authenticatable implements MustVerifyEmail, Nameable, HasSubt
|
||||
final public const ATTRIBUTE_TWO_FACTOR_RECOVERY_CODES = 'two_factor_recovery_codes';
|
||||
final public const ATTRIBUTE_TWO_FACTOR_SECRET = 'two_factor_secret';
|
||||
|
||||
final public const RELATION_EXTERNAL_PROFILES = 'externalprofiles';
|
||||
final public const RELATION_PERMISSIONS = 'permissions';
|
||||
final public const RELATION_PLAYLISTS = 'playlists';
|
||||
final public const RELATION_ROLES = 'roles';
|
||||
@@ -234,4 +237,14 @@ class User extends Authenticatable implements MustVerifyEmail, Nameable, HasSubt
|
||||
{
|
||||
return $this->hasMany(ActionLog::class, ActionLog::ATTRIBUTE_USER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the playlists that belong to the user.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function externalprofiles(): HasMany
|
||||
{
|
||||
return $this->hasMany(ExternalProfile::class, ExternalProfile::ATTRIBUTE_USER);
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\List\External;
|
||||
|
||||
use App\Enums\Models\List\AnimeWatchStatus;
|
||||
use App\Events\List\ExternalProfile\ExternalEntry\ExternalEntryCreated;
|
||||
use App\Events\List\ExternalProfile\ExternalEntry\ExternalEntryDeleted;
|
||||
use App\Events\List\ExternalProfile\ExternalEntry\ExternalEntryRestored;
|
||||
use App\Events\List\ExternalProfile\ExternalEntry\ExternalEntryUpdated;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\Wiki\Anime;
|
||||
use Database\Factories\List\External\ExternalEntryFactory;
|
||||
use Elastic\ScoutDriverPlus\Searchable;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* Class ExternalEntry.
|
||||
*
|
||||
* @property int $entry_id
|
||||
* @property int $anime_id
|
||||
* @property Anime $anime
|
||||
* @property int $external_profile_id
|
||||
* @property ExternalProfile $externalprofile
|
||||
* @property bool $is_favorite
|
||||
* @property float|null $score
|
||||
* @property AnimeWatchStatus|null $watch_status
|
||||
*
|
||||
* @method static ExternalEntryFactory factory(...$parameters)
|
||||
*/
|
||||
class ExternalEntry extends BaseModel
|
||||
{
|
||||
use Searchable;
|
||||
|
||||
final public const TABLE = 'external_entries';
|
||||
|
||||
final public const ATTRIBUTE_ID = 'entry_id';
|
||||
final public const ATTRIBUTE_ANIME = 'anime_id';
|
||||
final public const ATTRIBUTE_EXTERNAL_PROFILE = 'external_profile_id';
|
||||
final public const ATTRIBUTE_IS_FAVORITE = 'is_favorite';
|
||||
final public const ATTRIBUTE_SCORE = 'score';
|
||||
final public const ATTRIBUTE_WATCH_STATUS = 'watch_status';
|
||||
|
||||
final public const RELATION_ANIME = 'anime';
|
||||
final public const RELATION_EXTERNAL_PROFILE = 'externalprofile';
|
||||
final public const RELATION_USER = 'externalprofile.user';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
ExternalEntry::ATTRIBUTE_ANIME,
|
||||
ExternalEntry::ATTRIBUTE_EXTERNAL_PROFILE,
|
||||
ExternalEntry::ATTRIBUTE_IS_FAVORITE,
|
||||
ExternalEntry::ATTRIBUTE_SCORE,
|
||||
ExternalEntry::ATTRIBUTE_WATCH_STATUS,
|
||||
];
|
||||
|
||||
/**
|
||||
* The event map for the model.
|
||||
*
|
||||
* Allows for object-based events for native Eloquent events.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dispatchesEvents = [
|
||||
'created' => ExternalEntryCreated::class,
|
||||
'deleted' => ExternalEntryDeleted::class,
|
||||
'restored' => ExternalEntryRestored::class,
|
||||
'updated' => ExternalEntryUpdated::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = ExternalEntry::TABLE;
|
||||
|
||||
/**
|
||||
* The primary key associated with the table.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = ExternalEntry::ATTRIBUTE_ID;
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
ExternalEntry::ATTRIBUTE_IS_FAVORITE => 'bool',
|
||||
ExternalEntry::ATTRIBUTE_WATCH_STATUS => AnimeWatchStatus::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return strval($this->getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subtitle.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubtitle(): string
|
||||
{
|
||||
return $this->anime->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route key for the model.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getRouteKeyName(): string
|
||||
{
|
||||
return ExternalEntry::ATTRIBUTE_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the anime that owns the user external entry.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function anime(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Anime::class, ExternalEntry::ATTRIBUTE_ANIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user profile that owns the user profile.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function externalprofile(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ExternalProfile::class, ExternalEntry::ATTRIBUTE_EXTERNAL_PROFILE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\List;
|
||||
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Events\List\ExternalProfile\ExternalProfileCreated;
|
||||
use App\Events\List\ExternalProfile\ExternalProfileDeleted;
|
||||
use App\Events\List\ExternalProfile\ExternalProfileRestored;
|
||||
use App\Events\List\ExternalProfile\ExternalProfileUpdated;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use Database\Factories\List\ExternalProfileFactory;
|
||||
use Elastic\ScoutDriverPlus\Searchable;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class ExternalProfile.
|
||||
*
|
||||
* @property int $profile_id
|
||||
* @property Collection<int, ExternalEntry> $externalentries
|
||||
* @property string $name
|
||||
* @property ExternalProfileSite $site
|
||||
* @property int|null $user_id
|
||||
* @property User|null $user
|
||||
* @property ExternalProfileVisibility $visibility
|
||||
*
|
||||
* @method static ExternalProfileFactory factory(...$parameters)
|
||||
*/
|
||||
class ExternalProfile extends BaseModel
|
||||
{
|
||||
use Searchable;
|
||||
|
||||
final public const TABLE = 'external_profiles';
|
||||
|
||||
final public const ATTRIBUTE_ID = 'profile_id';
|
||||
final public const ATTRIBUTE_NAME = 'name';
|
||||
final public const ATTRIBUTE_SITE = 'site';
|
||||
final public const ATTRIBUTE_VISIBILITY = 'visibility';
|
||||
final public const ATTRIBUTE_USER = 'user_id';
|
||||
|
||||
final public const RELATION_ANIMES = 'externalentries.anime';
|
||||
final public const RELATION_EXTERNAL_ENTRIES = 'externalentries';
|
||||
final public const RELATION_USER = 'user';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
ExternalProfile::ATTRIBUTE_NAME,
|
||||
ExternalProfile::ATTRIBUTE_SITE,
|
||||
ExternalProfile::ATTRIBUTE_VISIBILITY,
|
||||
ExternalProfile::ATTRIBUTE_USER,
|
||||
];
|
||||
|
||||
/**
|
||||
* The event map for the model.
|
||||
*
|
||||
* Allows for object-based events for native Eloquent events.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dispatchesEvents = [
|
||||
'created' => ExternalProfileCreated::class,
|
||||
'deleted' => ExternalProfileDeleted::class,
|
||||
'restored' => ExternalProfileRestored::class,
|
||||
'updated' => ExternalProfileUpdated::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = ExternalProfile::TABLE;
|
||||
|
||||
/**
|
||||
* The primary key associated with the table.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = ExternalProfile::ATTRIBUTE_ID;
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
ExternalProfile::ATTRIBUTE_SITE => ExternalProfileSite::class,
|
||||
ExternalProfile::ATTRIBUTE_VISIBILITY => ExternalProfileVisibility::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Get name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subtitle.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubtitle(): string
|
||||
{
|
||||
return $this->user === null ? $this->getName() : $this->user->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route key for the model.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function getRouteKeyName(): string
|
||||
{
|
||||
return ExternalProfile::ATTRIBUTE_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the model should be searchable.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldBeSearchable(): bool
|
||||
{
|
||||
return ExternalProfileVisibility::PUBLIC === $this->visibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entries for the profile.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function externalentries(): HasMany
|
||||
{
|
||||
return $this->hasMany(ExternalEntry::class, ExternalEntry::ATTRIBUTE_EXTERNAL_PROFILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user that owns the profile.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, ExternalProfile::ATTRIBUTE_USER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only get the attributes as an array to prevent recursive toArray() calls.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toSearchableArray(): array
|
||||
{
|
||||
return $this->attributesToArray();
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ use App\Http\Resources\Pivot\Wiki\Resource\AnimeSeriesResource;
|
||||
use App\Http\Resources\Pivot\Wiki\Resource\AnimeStudioResource;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\Discord\DiscordThread;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\Wiki\Anime\AnimeSynonym;
|
||||
use App\Models\Wiki\Anime\AnimeTheme;
|
||||
use App\Pivots\Wiki\AnimeImage;
|
||||
@@ -39,6 +40,7 @@ use Laravel\Nova\Actions\Actionable;
|
||||
* @property Collection<int, AnimeSynonym> $animesynonyms
|
||||
* @property Collection<int, AnimeTheme> $animethemes
|
||||
* @property DiscordThread|null $discordthread
|
||||
* @property Collection<int, ExternalEntry> $externalentries
|
||||
* @property Collection<int, Image> $images
|
||||
* @property AnimeMediaFormat $media_format
|
||||
* @property string $name
|
||||
@@ -70,6 +72,8 @@ class Anime extends BaseModel
|
||||
final public const RELATION_ARTISTS = 'animethemes.song.artists';
|
||||
final public const RELATION_AUDIO = 'animethemes.animethemeentries.videos.audio';
|
||||
final public const RELATION_ENTRIES = 'animethemes.animethemeentries';
|
||||
final public const RELATION_EXTERNAL_ENTRIES = 'externalentries';
|
||||
final public const RELATION_EXTERNAL_PROFILE = 'externalentries.externalprofile';
|
||||
final public const RELATION_GROUPS = 'animethemes.group';
|
||||
final public const RELATION_IMAGES = 'images';
|
||||
final public const RELATION_RESOURCES = 'resources';
|
||||
@@ -273,4 +277,14 @@ class Anime extends BaseModel
|
||||
->as(AnimeStudioResource::$wrap)
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entries for the anime.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function externalentries(): HasMany
|
||||
{
|
||||
return $this->hasMany(ExternalEntry::class, ExternalEntry::ATTRIBUTE_ANIME);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use App\Nova\Actions\Models\Auth\User\RevokePermissionAction;
|
||||
use App\Nova\Actions\Models\Auth\User\RevokeRoleAction;
|
||||
use App\Nova\Resources\BaseResource;
|
||||
use App\Nova\Resources\List\Playlist;
|
||||
use App\Nova\Resources\List\ExternalProfile;
|
||||
use Exception;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Laravel\Nova\Fields\BelongsToMany;
|
||||
@@ -174,6 +175,8 @@ class User extends BaseResource
|
||||
|
||||
HasMany::make(__('nova.resources.label.playlists'), UserModel::RELATION_PLAYLISTS, Playlist::class),
|
||||
|
||||
HasMany::make(__('nova.resources.label.externalprofiles'), UserModel::RELATION_EXTERNAL_PROFILES, ExternalProfile::class),
|
||||
|
||||
Panel::make(__('nova.fields.base.timestamps'), $this->timestamps())
|
||||
->collapsable(),
|
||||
];
|
||||
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Nova\Resources\List\External;
|
||||
|
||||
use App\Enums\Models\List\AnimeWatchStatus;
|
||||
use App\Models\List\External\ExternalEntry as ExternalEntryModel;
|
||||
use App\Nova\Resources\BaseResource;
|
||||
use App\Nova\Resources\List\ExternalProfile;
|
||||
use App\Nova\Resources\Wiki\Anime;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
use Laravel\Nova\Fields\BelongsTo;
|
||||
use Laravel\Nova\Fields\Boolean;
|
||||
use Laravel\Nova\Fields\ID;
|
||||
use Laravel\Nova\Fields\Number;
|
||||
use Laravel\Nova\Fields\Select;
|
||||
use Laravel\Nova\Http\Requests\NovaRequest;
|
||||
use Laravel\Nova\Panel;
|
||||
|
||||
/**
|
||||
* Class ExternalEntry.
|
||||
*/
|
||||
class ExternalEntry extends BaseResource
|
||||
{
|
||||
/**
|
||||
* The model the resource corresponds to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static string $model = ExternalEntryModel::class;
|
||||
|
||||
/**
|
||||
* The single value that should be used to represent the resource when being displayed.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $title = ExternalEntryModel::ATTRIBUTE_ANIME;
|
||||
|
||||
/**
|
||||
* Get the value that should be displayed to represent the resource.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function title(): string
|
||||
{
|
||||
$entry = $this->model();
|
||||
if ($entry instanceof ExternalEntryModel) {
|
||||
return $entry->getName();
|
||||
}
|
||||
|
||||
return parent::title();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the search result subtitle for the resource.
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function subtitle(): ?string
|
||||
{
|
||||
$entry = $this->model();
|
||||
if ($entry instanceof ExternalEntryModel) {
|
||||
return $entry->getName();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displayable label of the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function label(): string
|
||||
{
|
||||
return __('nova.resources.label.externalentries');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displayable singular label of the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function singularLabel(): string
|
||||
{
|
||||
return __('nova.resources.singularLabel.externalentries');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URI key for the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function uriKey(): string
|
||||
{
|
||||
return 'user-external-list-entry';
|
||||
}
|
||||
|
||||
/**
|
||||
* The logical group associated with the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function group(): string
|
||||
{
|
||||
return __('nova.resources.group.list');
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the resource should be globally searchable.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $globallySearchable = false;
|
||||
|
||||
/**
|
||||
* Get the fields displayed by the resource.
|
||||
*
|
||||
* @param NovaRequest $request
|
||||
* @return array
|
||||
*/
|
||||
public function fields(NovaRequest $request): array
|
||||
{
|
||||
return [
|
||||
ID::make(__('nova.fields.base.id'), ExternalEntryModel::ATTRIBUTE_ID)
|
||||
->sortable()
|
||||
->showOnPreview()
|
||||
->showWhenPeeking(),
|
||||
|
||||
BelongsTo::make(__('nova.resources.singularLabel.anime'), ExternalEntryModel::RELATION_ANIME, Anime::class)
|
||||
->sortable()
|
||||
->filterable()
|
||||
->searchable()
|
||||
->required()
|
||||
->rules(['required'])
|
||||
->withSubtitles()
|
||||
->showOnPreview(),
|
||||
|
||||
BelongsTo::make(__('nova.resources.singularLabel.externalprofiles'), ExternalEntryModel::RELATION_EXTERNAL_PROFILE, ExternalProfile::class)
|
||||
->sortable()
|
||||
->filterable()
|
||||
->searchable()
|
||||
->required()
|
||||
->rules(['required'])
|
||||
->withSubtitles()
|
||||
->showCreateRelationButton()
|
||||
->showOnPreview(),
|
||||
|
||||
Boolean::make(__('nova.fields.externalentry.is_favorite.name'), ExternalEntryModel::ATTRIBUTE_IS_FAVORITE)
|
||||
->sortable()
|
||||
->filterable()
|
||||
->rules(['required'])
|
||||
->help(__('nova.fields.externalentry.is_favorite.help'))
|
||||
->showWhenPeeking()
|
||||
->showOnPreview(),
|
||||
|
||||
Number::make(__('nova.fields.externalentry.score.name'), ExternalEntryModel::ATTRIBUTE_SCORE)
|
||||
->sortable()
|
||||
->nullable()
|
||||
->rules(['nullable'])
|
||||
->step(0.01)
|
||||
->help(__('nova.fields.externalentry.score.help'))
|
||||
->showOnPreview()
|
||||
->filterable()
|
||||
->showWhenPeeking(),
|
||||
|
||||
Select::make(__('nova.fields.externalentry.watch_status.name'), ExternalEntryModel::ATTRIBUTE_WATCH_STATUS)
|
||||
->options(AnimeWatchStatus::asSelectArray())
|
||||
->displayUsing(fn (?int $enumValue) => AnimeWatchStatus::tryFrom($enumValue)?->localize())
|
||||
->sortable()
|
||||
->rules(['required', new Enum(AnimeWatchStatus::class)])
|
||||
->help(__('nova.fields.externalentry.watch_status.help'))
|
||||
->showOnPreview()
|
||||
->filterable()
|
||||
->showWhenPeeking(),
|
||||
|
||||
Panel::make(__('nova.fields.base.timestamps'), $this->timestamps())
|
||||
->collapsable(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Nova\Resources\List;
|
||||
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Models\List\ExternalProfile as ExternalProfileModel;
|
||||
use App\Nova\Resources\Auth\User;
|
||||
use App\Nova\Resources\BaseResource;
|
||||
use App\Nova\Resources\List\External\ExternalEntry;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
use Laravel\Nova\Fields\BelongsTo;
|
||||
use Laravel\Nova\Fields\HasMany;
|
||||
use Laravel\Nova\Fields\ID;
|
||||
use Laravel\Nova\Fields\Select;
|
||||
use Laravel\Nova\Fields\Text;
|
||||
use Laravel\Nova\Http\Requests\NovaRequest;
|
||||
use Laravel\Nova\Panel;
|
||||
use Laravel\Nova\Query\Search\Column;
|
||||
|
||||
/**
|
||||
* Class ExternalProfile.
|
||||
*/
|
||||
class ExternalProfile extends BaseResource
|
||||
{
|
||||
/**
|
||||
* The model the resource corresponds to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static string $model = ExternalProfileModel::class;
|
||||
|
||||
/**
|
||||
* The single value that should be used to represent the resource when being displayed.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $title = ExternalProfileModel::ATTRIBUTE_NAME;
|
||||
|
||||
/**
|
||||
* Get the value that should be displayed to represent the resource.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function title(): string
|
||||
{
|
||||
$profile = $this->model();
|
||||
if ($profile instanceof ExternalProfileModel) {
|
||||
return $profile->getName();
|
||||
}
|
||||
|
||||
return parent::title();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the search result subtitle for the resource.
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function subtitle(): ?string
|
||||
{
|
||||
$profile = $this->model();
|
||||
if ($profile instanceof ExternalProfileModel) {
|
||||
return $profile->getName();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displayable label of the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function label(): string
|
||||
{
|
||||
return __('nova.resources.label.externalprofiles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the displayable singular label of the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function singularLabel(): string
|
||||
{
|
||||
return __('nova.resources.singularLabel.externalprofiles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URI key for the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function uriKey(): string
|
||||
{
|
||||
return 'user-external-profile';
|
||||
}
|
||||
|
||||
/**
|
||||
* The logical group associated with the resource.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function group(): string
|
||||
{
|
||||
return __('nova.resources.group.list');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the searchable columns for the resource.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public static function searchableColumns(): array
|
||||
{
|
||||
return [
|
||||
new Column(ExternalProfileModel::ATTRIBUTE_NAME),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the resource should be globally searchable.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $globallySearchable = false;
|
||||
|
||||
/**
|
||||
* Get the fields displayed by the resource.
|
||||
*
|
||||
* @param NovaRequest $request
|
||||
* @return array
|
||||
*/
|
||||
public function fields(NovaRequest $request): array
|
||||
{
|
||||
return [
|
||||
ID::make(__('nova.fields.base.id'), ExternalProfileModel::ATTRIBUTE_ID)
|
||||
->sortable()
|
||||
->showOnPreview()
|
||||
->showWhenPeeking(),
|
||||
|
||||
Text::make(__('nova.fields.externalprofile.name.name'), ExternalProfileModel::ATTRIBUTE_NAME)
|
||||
->sortable()
|
||||
->copyable()
|
||||
->rules(['required', 'max:192'])
|
||||
->help(__('nova.fields.externalprofile.name.help'))
|
||||
->showOnPreview()
|
||||
->filterable()
|
||||
->maxlength(192)
|
||||
->enforceMaxlength()
|
||||
->showWhenPeeking(),
|
||||
|
||||
Select::make(__('nova.fields.externalprofile.site.name'), ExternalProfileModel::ATTRIBUTE_SITE)
|
||||
->options(ExternalProfileSite::asSelectArray())
|
||||
->displayUsing(fn (?int $enumValue) => ExternalProfileSite::tryFrom($enumValue)?->localize())
|
||||
->sortable()
|
||||
->rules(['required', new Enum(ExternalProfileSite::class)])
|
||||
->help(__('nova.fields.externalprofile.site.help'))
|
||||
->showOnPreview()
|
||||
->filterable()
|
||||
->showWhenPeeking(),
|
||||
|
||||
Select::make(__('nova.fields.externalprofile.visibility.name'), ExternalProfileModel::ATTRIBUTE_VISIBILITY)
|
||||
->options(ExternalProfileVisibility::asSelectArray())
|
||||
->displayUsing(fn (?int $enumValue) => ExternalProfileVisibility::tryFrom($enumValue)?->localize())
|
||||
->sortable()
|
||||
->rules(['required', new Enum(ExternalProfileVisibility::class)])
|
||||
->help(__('nova.fields.externalprofile.visibility.help'))
|
||||
->showOnPreview()
|
||||
->filterable()
|
||||
->showWhenPeeking(),
|
||||
|
||||
BelongsTo::make(__('nova.resources.singularLabel.user'), ExternalProfileModel::RELATION_USER, User::class)
|
||||
->sortable()
|
||||
->filterable()
|
||||
->searchable()
|
||||
->nullable()
|
||||
->withSubtitles()
|
||||
->showOnPreview(),
|
||||
|
||||
HasMany::make(__('nova.resources.label.externalentries'), ExternalProfileModel::RELATION_EXTERNAL_ENTRIES, ExternalEntry::class),
|
||||
|
||||
Panel::make(__('nova.fields.base.timestamps'), $this->timestamps())
|
||||
->collapsable(),
|
||||
];
|
||||
}
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Policies\List\External;
|
||||
|
||||
use App\Enums\Auth\CrudPermission;
|
||||
use App\Enums\Auth\ExtendedCrudPermission;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use Filament\Facades\Filament;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
use Illuminate\Http\Request;
|
||||
use Laravel\Nova\Nova;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryPolicy.
|
||||
*/
|
||||
class ExternalEntryPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*
|
||||
* @param User|null $user
|
||||
* @return bool
|
||||
*/
|
||||
public function viewAny(?User $user): bool
|
||||
{
|
||||
if (Filament::isServing()) {
|
||||
return $user !== null && $user->hasRole('Admin');
|
||||
}
|
||||
|
||||
return Nova::whenServing(
|
||||
fn (): bool => $user !== null && $user->hasRole('Admin'),
|
||||
function (Request $request) use ($user): bool {
|
||||
/** @var ExternalProfile|null $profile */
|
||||
$profile = $request->route('externalprofile');
|
||||
|
||||
return $user !== null
|
||||
? ($user->getKey() === $profile?->user_id || ExternalProfileVisibility::PRIVATE !== $profile?->visibility) && $user->can(CrudPermission::VIEW->format(ExternalEntry::class))
|
||||
: ExternalProfileVisibility::PRIVATE !== $profile?->visibility;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*
|
||||
* @param User|null $user
|
||||
* @param ExternalEntry $entry
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function view(?User $user, ExternalEntry $entry): bool
|
||||
{
|
||||
if (Filament::isServing()) {
|
||||
return $user !== null && $user->hasRole('Admin');
|
||||
}
|
||||
|
||||
return Nova::whenServing(
|
||||
fn (): bool => $user !== null && $user->hasRole('Admin'),
|
||||
function (Request $request) use ($user): bool {
|
||||
/** @var ExternalProfile|null $profile */
|
||||
$profile = $request->route('externalprofile');
|
||||
|
||||
return $user !== null
|
||||
? ($user->getKey() === $profile?->user_id || ExternalProfileVisibility::PRIVATE !== $profile?->visibility) && $user->can(CrudPermission::VIEW->format(ExternalEntry::class))
|
||||
: ExternalProfileVisibility::PRIVATE !== $profile?->visibility;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
if (Filament::isServing()) {
|
||||
return $user !== null && $user->hasRole('Admin');
|
||||
}
|
||||
|
||||
return Nova::whenServing(
|
||||
fn (): bool => $user->hasRole('Admin'),
|
||||
function (Request $request) use ($user): bool {
|
||||
/** @var ExternalProfile|null $profile */
|
||||
$profile = $request->route('externalprofile');
|
||||
|
||||
return $user->getKey() === $profile?->user_id;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @param ExternalEntry $entry
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function update(User $user, ExternalEntry $entry): bool
|
||||
{
|
||||
if (Filament::isServing()) {
|
||||
return $user !== null && $user->hasRole('Admin');
|
||||
}
|
||||
|
||||
return Nova::whenServing(
|
||||
fn (): bool => $user->hasRole('Admin'),
|
||||
function (Request $request) use ($user, $entry): bool {
|
||||
/** @var ExternalProfile|null $profile */
|
||||
$profile = $request->route('externalprofile');
|
||||
|
||||
return ! $entry->trashed() && $user->getKey() === $profile?->user_id && $user->can(CrudPermission::UPDATE->format(ExternalEntry::class));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @param ExternalEntry $entry
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function delete(User $user, ExternalEntry $entry): bool
|
||||
{
|
||||
if (Filament::isServing()) {
|
||||
return $user !== null && $user->hasRole('Admin');
|
||||
}
|
||||
|
||||
return Nova::whenServing(
|
||||
fn (): bool => $user->hasRole('Admin'),
|
||||
function (Request $request) use ($user, $entry): bool {
|
||||
/** @var ExternalProfile|null $profile */
|
||||
$profile = $request->route('externalprofile');
|
||||
|
||||
return ! $entry->trashed() && $user->getKey() === $profile?->user_id && $user->can(CrudPermission::DELETE->format(ExternalEntry::class));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @param ExternalEntry $entry
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
public function restore(User $user, ExternalEntry $entry): bool
|
||||
{
|
||||
if (Filament::isServing()) {
|
||||
return $user !== null && $user->hasRole('Admin');
|
||||
}
|
||||
|
||||
return Nova::whenServing(
|
||||
fn (): bool => $user->hasRole('Admin'),
|
||||
function (Request $request) use ($user, $entry): bool {
|
||||
/** @var ExternalProfile|null $profile */
|
||||
$profile = $request->route('externalprofile');
|
||||
|
||||
return $entry->trashed() && $user->getKey() === $profile?->user_id && $user->can(ExtendedCrudPermission::RESTORE->format(ExternalEntry::class));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function forceDelete(User $user): bool
|
||||
{
|
||||
return $user->can(ExtendedCrudPermission::FORCE_DELETE->format(ExternalEntry::class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Policies\List;
|
||||
|
||||
use App\Enums\Auth\CrudPermission;
|
||||
use App\Enums\Auth\ExtendedCrudPermission;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Filament\Facades\Filament;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
use Laravel\Nova\Nova;
|
||||
|
||||
/**
|
||||
* Class ExternalProfilePolicy.
|
||||
*/
|
||||
class ExternalProfilePolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*
|
||||
* @param User|null $user
|
||||
* @return bool
|
||||
*/
|
||||
public function viewAny(?User $user): bool
|
||||
{
|
||||
if (Filament::isServing()) {
|
||||
return $user !== null && $user->hasRole('Admin');
|
||||
}
|
||||
|
||||
return Nova::whenServing(
|
||||
fn (): bool => $user !== null && $user->hasRole('Admin'),
|
||||
fn (): bool => $user === null || $user->can(CrudPermission::VIEW->format(ExternalProfile::class))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*
|
||||
* @param User|null $user
|
||||
* @param ExternalProfile $profile
|
||||
* @return bool
|
||||
*/
|
||||
public function view(?User $user, ExternalProfile $profile): bool
|
||||
{
|
||||
if (Filament::isServing()) {
|
||||
return $user !== null && $user->hasRole('Admin');
|
||||
}
|
||||
|
||||
return Nova::whenServing(
|
||||
fn (): bool => $user !== null && $user->hasRole('Admin'),
|
||||
fn (): bool => $user !== null
|
||||
? ($user->getKey() === $profile->user_id || ExternalProfileVisibility::PRIVATE !== $profile->visibility) && $user->can(CrudPermission::VIEW->format(ExternalProfile::class))
|
||||
: ExternalProfileVisibility::PRIVATE !== $profile->visibility
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
if (Filament::isServing()) {
|
||||
return $user !== null && $user->hasRole('Admin');
|
||||
}
|
||||
|
||||
return Nova::whenServing(
|
||||
fn (): bool => $user->hasRole('Admin'),
|
||||
fn (): bool => $user->can(CrudPermission::CREATE->format(ExternalProfile::class))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @param ExternalProfile $profile
|
||||
* @return bool
|
||||
*/
|
||||
public function update(User $user, ExternalProfile $profile): bool
|
||||
{
|
||||
if (Filament::isServing()) {
|
||||
return $user !== null && $user->hasRole('Admin');
|
||||
}
|
||||
|
||||
return Nova::whenServing(
|
||||
fn (): bool => $user->hasRole('Admin'),
|
||||
fn (): bool => ! $profile->trashed() && $user->getKey() === $profile->user_id && $user->can(CrudPermission::UPDATE->format(ExternalProfile::class))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @param ExternalProfile $profile
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(User $user, ExternalProfile $profile): bool
|
||||
{
|
||||
if (Filament::isServing()) {
|
||||
return $user !== null && $user->hasRole('Admin');
|
||||
}
|
||||
|
||||
return Nova::whenServing(
|
||||
fn (): bool => $user->hasRole('Admin'),
|
||||
fn (): bool => ! $profile->trashed() && $user->getKey() === $profile->user_id && $user->can(CrudPermission::DELETE->format(ExternalProfile::class))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @param ExternalProfile $profile
|
||||
* @return bool
|
||||
*/
|
||||
public function restore(User $user, ExternalProfile $profile): bool
|
||||
{
|
||||
if (Filament::isServing()) {
|
||||
return $user !== null && $user->hasRole('Admin');
|
||||
}
|
||||
|
||||
return Nova::whenServing(
|
||||
fn (): bool => $user->hasRole('Admin'),
|
||||
fn (): bool => $profile->trashed() && $user->getKey() === $profile->user_id && $user->can(ExtendedCrudPermission::RESTORE->format(ExternalProfile::class))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function forceDelete(User $user): bool
|
||||
{
|
||||
return $user->can(ExtendedCrudPermission::FORCE_DELETE->format(ExternalProfile::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can add a entry to the profile.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function addEntry(User $user): bool
|
||||
{
|
||||
return $user->hasRole('Admin');
|
||||
}
|
||||
}
|
||||
@@ -273,4 +273,15 @@ class AnimePolicy
|
||||
{
|
||||
return $user->can(CrudPermission::UPDATE->format(Anime::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can add a entry to the anime.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function addEntry(User $user): bool
|
||||
{
|
||||
return $user->hasRole('Admin');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Rules\Admin;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Translation\PotentiallyTranslatedString;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class StartDateBeforeEndDateRule.
|
||||
*/
|
||||
class StartDateBeforeEndDateRule implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param Closure(string): PotentiallyTranslatedString $fail
|
||||
* @return void
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
$dates = explode(' - ', $value);
|
||||
|
||||
$startAt = Carbon::createFromFormat('m/d/Y', $dates[0]);
|
||||
$endAt = Carbon::createFromFormat('m/d/Y', $dates[1]);
|
||||
|
||||
if ($endAt->lessThan($startAt)) {
|
||||
$fail(__('validation.date', ['attribute' => $attribute]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@
|
||||
"laravel/tinker": "^2.8",
|
||||
"league/flysystem-aws-s3-v3": "^3.0",
|
||||
"leandrocfe/filament-apex-charts": "3.1",
|
||||
"malzariey/filament-daterangepicker-filter": "^2.7",
|
||||
"propaganistas/laravel-disposable-email": "^2.2",
|
||||
"spatie/db-dumper": "^3.1.1",
|
||||
"spatie/laravel-permission": "^5.8",
|
||||
|
||||
Generated
+68
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "ceb52a17efa10f4ca74b636ab69ea8ec",
|
||||
"content-hash": "f7ceaa1076b7ab1d72ea6d6aa211ea76",
|
||||
"packages": [
|
||||
{
|
||||
"name": "anourvalar/eloquent-serialize",
|
||||
@@ -5474,6 +5474,73 @@
|
||||
],
|
||||
"time": "2024-04-02T14:22:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "malzariey/filament-daterangepicker-filter",
|
||||
"version": "2.7.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/malzariey/filament-daterangepicker-filter.git",
|
||||
"reference": "37dca49a04593be4df207107fe62131a02954d8a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/malzariey/filament-daterangepicker-filter/zipball/37dca49a04593be4df207107fe62131a02954d8a",
|
||||
"reference": "37dca49a04593be4df207107fe62131a02954d8a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"filament/filament": "^2.0|^3.0",
|
||||
"illuminate/contracts": "^8.0|^9.0|^10.0|^11.0",
|
||||
"php": "^8.0",
|
||||
"spatie/laravel-package-tools": "^1.16.4"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Malzariey\\FilamentDaterangepickerFilter\\FilamentDaterangepickerFilterServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"FilamentDaterangepickerFilter": "Malzariey\\FilamentDaterangepickerFilter\\Facades\\FilamentDaterangepickerFilter"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Malzariey\\FilamentDaterangepickerFilter\\": "src",
|
||||
"Malzariey\\FilamentDaterangepickerFilter\\Database\\Factories\\": "database/factories"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Majid Al-Zariey",
|
||||
"email": "malzariey@gmail.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "This package uses daterangepciker library to filter date by a range or predefined date ranges (Today , Yesterday ...etc)",
|
||||
"homepage": "https://github.com/malzariey/filament-daterangepicker-filter",
|
||||
"keywords": [
|
||||
"Malzariey",
|
||||
"filament-daterangepicker-filter",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/malzariey/filament-daterangepicker-filter/issues",
|
||||
"source": "https://github.com/malzariey/filament-daterangepicker-filter/tree/2.7.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/Malzariey",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-06-07T06:30:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "masterminds/html5",
|
||||
"version": "2.9.0",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Limits
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These values represent caps on external profiles to prevent spam. By default,
|
||||
| an individual external profile is permitted 1000 entries, and a user
|
||||
| is permitted 5 external profiles.
|
||||
|
|
||||
*/
|
||||
|
||||
'profile_max_entries' => (int) env('PROFILE_MAX_ENTRIES', 1000),
|
||||
|
||||
'user_max_profiles' => (int) env('USER_MAX_PROFILES', 5),
|
||||
];
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories\List\External;
|
||||
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* Class ExternalEntryFactory.
|
||||
*
|
||||
* @method ExternalEntry createOne($attributes = [])
|
||||
* @method ExternalEntry makeOne($attributes = [])
|
||||
*
|
||||
* @extends Factory<ExternalEntry>
|
||||
*/
|
||||
class ExternalEntryFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var class-string<ExternalEntry>
|
||||
*/
|
||||
protected $model = ExternalEntry::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories\List;
|
||||
|
||||
use App\Enums\Models\List\AnimeWatchStatus;
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\Wiki\Anime;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileFactory.
|
||||
*
|
||||
* @method ExternalProfile createOne($attributes = [])
|
||||
* @method ExternalProfile makeOne($attributes = [])
|
||||
*
|
||||
* @extends Factory<ExternalProfile>
|
||||
*/
|
||||
class ExternalProfileFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var class-string<ExternalProfile>
|
||||
*/
|
||||
protected $model = ExternalProfile::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$site = Arr::random(ExternalProfileSite::cases());
|
||||
$visibility = Arr::random(ExternalProfileVisibility::cases());
|
||||
|
||||
return [
|
||||
ExternalProfile::ATTRIBUTE_NAME => fake()->words(3, true),
|
||||
ExternalProfile::ATTRIBUTE_SITE => $site->value,
|
||||
ExternalProfile::ATTRIBUTE_VISIBILITY => $visibility->value,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the model's entry listing.
|
||||
*
|
||||
* @param int $count
|
||||
* @return static
|
||||
*/
|
||||
public function entries(int $count): static
|
||||
{
|
||||
return $this->afterCreating(
|
||||
function (ExternalProfile $profile) use ($count) {
|
||||
foreach (range(1, $count) as $index) {
|
||||
$entry = ExternalEntry::factory()
|
||||
->for($profile)
|
||||
->for(Anime::factory())
|
||||
->createOne();
|
||||
|
||||
$entry->is_favorite = fake()->boolean();
|
||||
$entry->score = fake()->numberBetween(1, 10);
|
||||
$entry->watch_status = Arr::random(AnimeWatchStatus::cases())->value;
|
||||
$entry->save();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the model's entry listing.
|
||||
*
|
||||
* @param array $animeIds
|
||||
* @return static
|
||||
*/
|
||||
public function entriesForIds(array $animeIds): static
|
||||
{
|
||||
return $this->afterCreating(
|
||||
function (ExternalProfile $profile) use ($animeIds) {
|
||||
foreach ($animeIds as $animeId) {
|
||||
$entry = ExternalEntry::factory()
|
||||
->for($profile)
|
||||
->for(Anime::query()->find($animeId))
|
||||
->createOne();
|
||||
|
||||
$entry->is_favorite = fake()->boolean();
|
||||
$entry->score = fake()->numberBetween(1, 10);
|
||||
$entry->watch_status = Arr::random(AnimeWatchStatus::cases())->value;
|
||||
$entry->save();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if (! Schema::hasTable(ExternalProfile::TABLE)) {
|
||||
Schema::create(ExternalProfile::TABLE, function (Blueprint $table) {
|
||||
$table->timestamps(6);
|
||||
$table->softDeletes(BaseModel::ATTRIBUTE_DELETED_AT, 6);
|
||||
$table->id(ExternalProfile::ATTRIBUTE_ID);
|
||||
$table->string(ExternalProfile::ATTRIBUTE_NAME);
|
||||
$table->integer(ExternalProfile::ATTRIBUTE_SITE)->nullable();
|
||||
$table->integer(ExternalProfile::ATTRIBUTE_VISIBILITY)->default(ExternalProfileVisibility::PRIVATE->value);
|
||||
|
||||
$table->unsignedBigInteger(ExternalProfile::ATTRIBUTE_USER)->nullable();
|
||||
$table->foreign(ExternalProfile::ATTRIBUTE_USER)->references(User::ATTRIBUTE_ID)->on(User::TABLE)->cascadeOnDelete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists(ExternalProfile::TABLE);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\Wiki\Anime;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if (! Schema::hasTable(ExternalEntry::TABLE)) {
|
||||
Schema::create(ExternalEntry::TABLE, function (Blueprint $table) {
|
||||
$table->timestamps(6);
|
||||
$table->softDeletes(BaseModel::ATTRIBUTE_DELETED_AT, 6);
|
||||
$table->id(ExternalEntry::ATTRIBUTE_ID);
|
||||
$table->float(ExternalEntry::ATTRIBUTE_SCORE)->nullable();
|
||||
$table->integer(ExternalEntry::ATTRIBUTE_WATCH_STATUS)->nullable();
|
||||
$table->boolean(ExternalEntry::ATTRIBUTE_IS_FAVORITE)->default(false);
|
||||
|
||||
$table->unsignedBigInteger(ExternalEntry::ATTRIBUTE_ANIME)->nullable();
|
||||
$table->foreign(ExternalEntry::ATTRIBUTE_ANIME)->references(Anime::ATTRIBUTE_ID)->on(Anime::TABLE)->nullOnDelete();
|
||||
|
||||
$table->unsignedBigInteger(ExternalEntry::ATTRIBUTE_EXTERNAL_PROFILE)->nullable();
|
||||
$table->foreign(ExternalEntry::ATTRIBUTE_EXTERNAL_PROFILE)->references(ExternalProfile::ATTRIBUTE_ID)->on(ExternalProfile::TABLE)->cascadeOnDelete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists(ExternalEntry::TABLE);
|
||||
}
|
||||
};
|
||||
@@ -7,6 +7,7 @@ namespace Database\Seeders\Admin\Feature;
|
||||
use App\Constants\FeatureConstants;
|
||||
use App\Features\AllowAudioStreams;
|
||||
use App\Features\AllowDumpDownloading;
|
||||
use App\Features\AllowExternalProfileManagement;
|
||||
use App\Features\AllowPlaylistManagement;
|
||||
use App\Features\AllowScriptDownloading;
|
||||
use App\Features\AllowVideoStreams;
|
||||
@@ -27,6 +28,7 @@ class FeatureSeeder extends Seeder
|
||||
{
|
||||
Feature::deactivate(AllowAudioStreams::class);
|
||||
Feature::deactivate(AllowDumpDownloading::class);
|
||||
Feature::deactivate(AllowExternalProfileManagement::class);
|
||||
Feature::deactivate(AllowPlaylistManagement::class);
|
||||
Feature::deactivate(AllowScriptDownloading::class);
|
||||
Feature::deactivate(AllowVideoStreams::class);
|
||||
|
||||
@@ -16,6 +16,8 @@ use App\Models\Auth\Role;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\Discord\DiscordThread;
|
||||
use App\Models\Document\Page;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\List\Playlist;
|
||||
use App\Models\List\Playlist\PlaylistTrack;
|
||||
use App\Models\Wiki\Anime;
|
||||
@@ -66,6 +68,8 @@ class PermissionSeeder extends Seeder
|
||||
$this->registerResource(DiscordThread::class, CrudPermission::cases());
|
||||
|
||||
// List Resources
|
||||
$this->registerResource(ExternalEntry::class, $extendedCrudPermissions);
|
||||
$this->registerResource(ExternalProfile::class, $extendedCrudPermissions);
|
||||
$this->registerResource(Playlist::class, $extendedCrudPermissions);
|
||||
$this->registerResource(PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ use App\Models\Auth\Role;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\Discord\DiscordThread;
|
||||
use App\Models\Document\Page;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\List\Playlist;
|
||||
use App\Models\List\Playlist\PlaylistTrack;
|
||||
use App\Models\Wiki\Anime;
|
||||
@@ -73,6 +75,8 @@ class AdminSeeder extends RoleSeeder
|
||||
$this->configureResource($role, DiscordThread::class, CrudPermission::cases());
|
||||
|
||||
// List Resources
|
||||
$this->configureResource($role, ExternalEntry::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, ExternalProfile::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
|
||||
Loaded 100 of 179 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user