mirror of
https://github.com/AnimeThemes/animethemes-server.git
synced 2026-07-11 01:24:46 +02:00
feat: added notifications (#795)
This commit is contained in:
+2
@@ -88,6 +88,8 @@ class AnilistExternalEntryTokenAction extends BaseExternalEntryTokenAction
|
||||
* Make the request to the external api.
|
||||
*
|
||||
* @return static
|
||||
*
|
||||
* @throws RequestException
|
||||
*/
|
||||
protected function makeRequest(): static
|
||||
{
|
||||
|
||||
+5
-4
@@ -55,10 +55,6 @@ class MalExternalTokenAction extends BaseExternalTokenAction
|
||||
throw new Error('Failed to get token');
|
||||
}
|
||||
|
||||
return ExternalToken::query()->create([
|
||||
ExternalToken::ATTRIBUTE_ACCESS_TOKEN => Crypt::encrypt($token),
|
||||
ExternalToken::ATTRIBUTE_REFRESH_TOKEN => Crypt::encrypt($refreshToken),
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
|
||||
@@ -66,5 +62,10 @@ class MalExternalTokenAction extends BaseExternalTokenAction
|
||||
} finally {
|
||||
Cache::forget("mal-external-token-request-{$state}");
|
||||
}
|
||||
|
||||
return ExternalToken::query()->create([
|
||||
ExternalToken::ATTRIBUTE_ACCESS_TOKEN => Crypt::encrypt($token),
|
||||
ExternalToken::ATTRIBUTE_REFRESH_TOKEN => Crypt::encrypt($refreshToken),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,11 @@ namespace App\Actions\Models\List\ExternalProfile;
|
||||
use App\Actions\Models\List\ExternalProfile\ExternalEntry\BaseExternalEntryAction;
|
||||
use App\Actions\Models\List\ExternalProfile\ExternalEntry\BaseExternalEntryTokenAction;
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Enums\NotificationType;
|
||||
use App\Events\List\ExternalProfile\ExternalProfileSynced;
|
||||
use App\Models\List\External\ExternalEntry;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use App\Notifications\UserNotification;
|
||||
use Exception;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -47,7 +46,7 @@ class SyncExternalProfileAction
|
||||
|
||||
$entries = $action->getEntries();
|
||||
|
||||
$this->preloadResources($profile->site, $entries);
|
||||
$this->cacheResources($profile->site);
|
||||
|
||||
$externalEntries = [];
|
||||
foreach ($entries as $entry) {
|
||||
@@ -58,7 +57,7 @@ class SyncExternalProfileAction
|
||||
ExternalEntry::ATTRIBUTE_SCORE => Arr::get($entry, ExternalEntry::ATTRIBUTE_SCORE),
|
||||
ExternalEntry::ATTRIBUTE_IS_FAVORITE => Arr::get($entry, ExternalEntry::ATTRIBUTE_IS_FAVORITE),
|
||||
ExternalEntry::ATTRIBUTE_WATCH_STATUS => Arr::get($entry, ExternalEntry::ATTRIBUTE_WATCH_STATUS),
|
||||
ExternalEntry::ATTRIBUTE_ANIME => $anime->getKey(),
|
||||
ExternalEntry::ATTRIBUTE_ANIME => $anime,
|
||||
ExternalEntry::ATTRIBUTE_PROFILE => $profile->getKey(),
|
||||
];
|
||||
}
|
||||
@@ -70,11 +69,7 @@ class SyncExternalProfileAction
|
||||
|
||||
DB::commit();
|
||||
|
||||
$profile->user?->notifyNow(new UserNotification(
|
||||
'External Profile Synced',
|
||||
"Your external profile [{$profile->getName()}]({$profile->getClientUrl()}) has been synced.",
|
||||
NotificationType::SYNCED_PROFILE,
|
||||
));
|
||||
ExternalProfileSynced::dispatch($profile);
|
||||
|
||||
return $profile;
|
||||
} catch (Exception $e) {
|
||||
@@ -113,21 +108,20 @@ class SyncExternalProfileAction
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload the resources for performance proposals.
|
||||
* Cache the resources for performance proposals.
|
||||
*
|
||||
* @param ExternalProfileSite $profileSite
|
||||
* @param array $entries
|
||||
* @return void
|
||||
*/
|
||||
protected function preloadResources(ExternalProfileSite $profileSite, array $entries): void
|
||||
protected function cacheResources(ExternalProfileSite $profileSite): void
|
||||
{
|
||||
$this->resources = Cache::flexible("externalprofile_resources", [60, 300], function () use ($profileSite, $entries) {
|
||||
$this->resources = Cache::flexible("resources_{$profileSite->getResourceSite()->localize()}", [60, 300], function () use ($profileSite) {
|
||||
return ExternalResource::query()
|
||||
->where(ExternalResource::ATTRIBUTE_SITE, $profileSite->getResourceSite()->value)
|
||||
->whereIn(ExternalResource::ATTRIBUTE_EXTERNAL_ID, Arr::pluck($entries, 'external_id'))
|
||||
->with(ExternalResource::RELATION_ANIME)
|
||||
->with([ExternalResource::RELATION_ANIME => fn ($query) => $query->select([Anime::TABLE.'.'.Anime::ATTRIBUTE_ID])])
|
||||
->whereHas(ExternalResource::RELATION_ANIME)
|
||||
->get()
|
||||
->mapWithKeys(fn (ExternalResource $resource) => [$resource->external_id => $resource->anime]);
|
||||
->mapWithKeys(fn (ExternalResource $resource) => [$resource->external_id => $resource->anime->map(fn (Anime $anime) => $anime->getKey())]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -135,10 +129,10 @@ class SyncExternalProfileAction
|
||||
* Get the animes by the external id.
|
||||
*
|
||||
* @param int $externalId
|
||||
* @return Collection<int, Anime>
|
||||
* @return Collection<int, int>
|
||||
*/
|
||||
protected function getAnimesByExternalId(int $externalId): Collection
|
||||
{
|
||||
return $this->resources[$externalId] ?? new Collection();
|
||||
return $this->resources->get($externalId) ?? collect();
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -2,11 +2,11 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\Admin\Report;
|
||||
namespace App\Actions\Models\User\Report;
|
||||
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Enums\Models\Admin\ReportActionType;
|
||||
use App\Models\Admin\Report\ReportStep;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Enums\Models\User\ReportActionType;
|
||||
use App\Models\User\Report\ReportStep;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
+5
-5
@@ -2,12 +2,12 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Models\Admin\Report;
|
||||
namespace App\Actions\Models\User\Report;
|
||||
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Enums\Models\Admin\ReportActionType;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Models\Admin\Report\ReportStep;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Enums\Models\User\ReportActionType;
|
||||
use App\Models\User\Report;
|
||||
use App\Models\User\Report\ReportStep;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
use Illuminate\Support\Arr;
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Models;
|
||||
|
||||
use App\Actions\Models\Admin\Report\ReportAction;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Models\Admin\Report\ReportStep;
|
||||
use App\Actions\Models\User\Report\ReportAction;
|
||||
use App\Models\User\Report;
|
||||
use App\Models\User\Report\ReportStep;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts\Events;
|
||||
|
||||
use Filament\Notifications\Notification;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface NotifiesUsersEvent.
|
||||
*/
|
||||
interface NotifiesUsersEvent
|
||||
{
|
||||
/**
|
||||
* Notify the users.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function notify(): void;
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Enums\Models\Admin;
|
||||
namespace App\Enums\Models\User;
|
||||
|
||||
use App\Concerns\Enums\LocalizesName;
|
||||
use Filament\Support\Contracts\HasColor;
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
namespace App\Enums\Models\User;
|
||||
|
||||
/**
|
||||
* Enum NotificationType.
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Enums\Models\Admin;
|
||||
namespace App\Enums\Models\User;
|
||||
|
||||
use App\Concerns\Enums\LocalizesName;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\List\ExternalProfile;
|
||||
|
||||
use App\Contracts\Events\NotifiesUsersEvent;
|
||||
use App\Enums\Models\User\NotificationType;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Notifications\UserNotification;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
|
||||
/**
|
||||
* Class ExternalProfileSynced.
|
||||
*/
|
||||
class ExternalProfileSynced implements NotifiesUsersEvent
|
||||
{
|
||||
use Dispatchable;
|
||||
|
||||
/**
|
||||
* Create new event instance.
|
||||
*
|
||||
* @param ExternalProfile $profile
|
||||
*/
|
||||
public function __construct(protected ExternalProfile $profile)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the users.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function notify(): void
|
||||
{
|
||||
$profile = $this->profile;
|
||||
|
||||
$notification = new UserNotification(
|
||||
'External Profile Synced',
|
||||
"Your external profile [{$profile->getName()}]({$profile->getClientUrl()}) has been synced.",
|
||||
NotificationType::SYNCED_PROFILE,
|
||||
);
|
||||
|
||||
$profile->user?->notifyNow($notification);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -2,11 +2,11 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\RelationManagers\Admin;
|
||||
namespace App\Filament\RelationManagers\User;
|
||||
|
||||
use App\Filament\RelationManagers\BaseRelationManager;
|
||||
use App\Filament\Resources\Admin\Report\ReportStep as ReportStepResource;
|
||||
use App\Models\Admin\Report\ReportStep;
|
||||
use App\Filament\Resources\User\Report\ReportStep as ReportStepResource;
|
||||
use App\Models\User\Report\ReportStep;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
@@ -2,20 +2,20 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Admin;
|
||||
namespace App\Filament\Resources\User;
|
||||
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Filament\Components\Columns\BelongsToColumn;
|
||||
use App\Filament\Components\Columns\TextColumn;
|
||||
use App\Filament\Components\Infolist\BelongsToEntry;
|
||||
use App\Filament\Components\Infolist\TextEntry;
|
||||
use App\Filament\Components\Infolist\TimestampSection;
|
||||
use App\Filament\Resources\BaseResource;
|
||||
use App\Filament\Resources\Admin\Report\Pages\ListReports;
|
||||
use App\Filament\Resources\Admin\Report\Pages\ViewReport;
|
||||
use App\Filament\Resources\Admin\Report\RelationManagers\StepReportRelationManager;
|
||||
use App\Filament\Resources\User\Report\Pages\ListReports;
|
||||
use App\Filament\Resources\User\Report\Pages\ViewReport;
|
||||
use App\Filament\Resources\User\Report\RelationManagers\StepReportRelationManager;
|
||||
use App\Filament\Resources\Auth\User as UserResource;
|
||||
use App\Models\Admin\Report as ReportModel;
|
||||
use App\Models\User\Report as ReportModel;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Infolists\Components\Section;
|
||||
use Filament\Infolists\Infolist;
|
||||
@@ -68,7 +68,7 @@ class Report extends BaseResource
|
||||
*/
|
||||
public static function getNavigationGroup(): string
|
||||
{
|
||||
return __('filament.resources.group.admin');
|
||||
return __('filament.resources.group.user');
|
||||
}
|
||||
|
||||
/**
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Admin\Report\Pages;
|
||||
namespace App\Filament\Resources\User\Report\Pages;
|
||||
|
||||
use App\Filament\Resources\Admin\Report;
|
||||
use App\Filament\Resources\User\Report;
|
||||
use App\Filament\Resources\Base\BaseListResources;
|
||||
|
||||
/**
|
||||
+2
-2
@@ -2,10 +2,10 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Admin\Report\Pages;
|
||||
namespace App\Filament\Resources\User\Report\Pages;
|
||||
|
||||
use App\Filament\Resources\Base\BaseViewResource;
|
||||
use App\Filament\Resources\Admin\Report;
|
||||
use App\Filament\Resources\User\Report;
|
||||
|
||||
/**
|
||||
* Class ViewReport.
|
||||
+4
-4
@@ -2,11 +2,11 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Admin\Report\RelationManagers;
|
||||
namespace App\Filament\Resources\User\Report\RelationManagers;
|
||||
|
||||
use App\Filament\RelationManagers\Admin\ReportStepRelationManager;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Models\Admin\Report\ReportStep;
|
||||
use App\Filament\RelationManagers\User\ReportStepRelationManager;
|
||||
use App\Models\User\Report;
|
||||
use App\Models\User\Report\ReportStep;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
/**
|
||||
+8
-8
@@ -2,22 +2,22 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Admin\Report;
|
||||
namespace App\Filament\Resources\User\Report;
|
||||
|
||||
use App\Contracts\Models\Nameable;
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Enums\Models\Admin\ReportActionType;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Enums\Models\User\ReportActionType;
|
||||
use App\Filament\Components\Columns\BelongsToColumn;
|
||||
use App\Filament\Components\Columns\TextColumn;
|
||||
use App\Filament\Components\Infolist\BelongsToEntry;
|
||||
use App\Filament\Components\Infolist\KeyValueThreeEntry;
|
||||
use App\Filament\Components\Infolist\TextEntry;
|
||||
use App\Filament\Components\Infolist\TimestampSection;
|
||||
use App\Filament\Resources\Admin\Report as ReportResource;
|
||||
use App\Filament\Resources\User\Report as ReportResource;
|
||||
use App\Filament\Resources\BaseResource;
|
||||
use App\Filament\Resources\Admin\Report\ReportStep\Pages\ListReportSteps;
|
||||
use App\Filament\Resources\Admin\Report\ReportStep\Pages\ViewReportStep;
|
||||
use App\Models\Admin\Report\ReportStep as ReportStepModel;
|
||||
use App\Filament\Resources\User\Report\ReportStep\Pages\ListReportSteps;
|
||||
use App\Filament\Resources\User\Report\ReportStep\Pages\ViewReportStep;
|
||||
use App\Models\User\Report\ReportStep as ReportStepModel;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Infolists\Components\KeyValueEntry;
|
||||
@@ -71,7 +71,7 @@ class ReportStep extends BaseResource
|
||||
*/
|
||||
public static function getNavigationGroup(): string
|
||||
{
|
||||
return __('filament.resources.group.admin');
|
||||
return __('filament.resources.group.user');
|
||||
}
|
||||
|
||||
/**
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Admin\Report\ReportStep\Pages;
|
||||
namespace App\Filament\Resources\User\Report\ReportStep\Pages;
|
||||
|
||||
use App\Filament\Resources\Admin\Report\ReportStep;
|
||||
use App\Filament\Resources\User\Report\ReportStep;
|
||||
use App\Filament\Resources\Base\BaseListResources;
|
||||
|
||||
/**
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\Admin\Report\ReportStep\Pages;
|
||||
namespace App\Filament\Resources\User\Report\ReportStep\Pages;
|
||||
|
||||
use App\Filament\Resources\Admin\Report\ReportStep;
|
||||
use App\Filament\Resources\User\Report\ReportStep;
|
||||
use App\Filament\Resources\Base\BaseViewResource;
|
||||
|
||||
/**
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\Base;
|
||||
|
||||
use App\Contracts\Http\Api\Field\FilterableField;
|
||||
use App\Contracts\Http\Api\Field\RenderableField;
|
||||
use App\Contracts\Http\Api\Field\SelectableField;
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Api\Filter\Filter;
|
||||
use App\Http\Api\Filter\StringFilter;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Http\Resources\BaseResource;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* Class UuidField.
|
||||
*/
|
||||
class UuidField extends Field implements FilterableField, RenderableField, SelectableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
* @param string $column
|
||||
*/
|
||||
public function __construct(Schema $schema, string $column)
|
||||
{
|
||||
parent::__construct($schema, BaseResource::ATTRIBUTE_ID, $column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filter that can be applied to the field.
|
||||
*
|
||||
* @return Filter
|
||||
*/
|
||||
public function getFilter(): Filter
|
||||
{
|
||||
return new StringFilter($this->getKey(), $this->getColumn());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the field should be displayed to the user.
|
||||
*
|
||||
* @param Query $query
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldRender(Query $query): bool
|
||||
{
|
||||
$criteria = $query->getFieldCriteria($this->schema->type());
|
||||
|
||||
return $criteria === null || $criteria->isAllowedField($this->getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value to display to the user.
|
||||
*
|
||||
* @param Model $model
|
||||
* @return mixed
|
||||
*/
|
||||
public function render(Model $model): mixed
|
||||
{
|
||||
return $model->getAttribute($this->getColumn());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field;
|
||||
|
||||
use App\Contracts\Http\Api\Field\RenderableField;
|
||||
use App\Contracts\Http\Api\Field\SelectableField;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* Class JsonField.
|
||||
*/
|
||||
abstract class JsonField extends Field implements RenderableField, SelectableField
|
||||
{
|
||||
/**
|
||||
* Determine if the field should be displayed to the user.
|
||||
*
|
||||
* @param Query $query
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldRender(Query $query): bool
|
||||
{
|
||||
$criteria = $query->getFieldCriteria($this->schema->type());
|
||||
|
||||
return $criteria === null || $criteria->isAllowedField($this->getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value to display to the user.
|
||||
*
|
||||
* @param Model $model
|
||||
* @return mixed
|
||||
*/
|
||||
public function render(Model $model): mixed
|
||||
{
|
||||
return $model->getAttribute($this->getColumn());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
$criteria = $query->getFieldCriteria($this->schema->type());
|
||||
|
||||
return $criteria === null || $criteria->isAllowedField($this->getKey());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\User\Notification;
|
||||
|
||||
use App\Http\Api\Field\JsonField;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\User\Notification;
|
||||
|
||||
/**
|
||||
* Class NotificationDataField.
|
||||
*/
|
||||
class NotificationDataField extends JsonField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, Notification::ATTRIBUTE_DATA);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\User\Notification;
|
||||
|
||||
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\User\Notification;
|
||||
|
||||
/**
|
||||
* Class NotificationNotifiableIdField.
|
||||
*/
|
||||
class NotificationNotifiableIdField extends Field implements SelectableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, Notification::ATTRIBUTE_NOTIFIABLE_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
|
||||
{
|
||||
// Needed to filter the user notifications on query.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\User\Notification;
|
||||
|
||||
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\User\Notification;
|
||||
|
||||
/**
|
||||
* Class NotificationNotifiableTypeField.
|
||||
*/
|
||||
class NotificationNotifiableTypeField extends Field implements SelectableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, Notification::ATTRIBUTE_NOTIFIABLE_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 filter the user notifications on query.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\User\Notification;
|
||||
|
||||
use App\Http\Api\Field\DateField;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Models\User\Notification;
|
||||
|
||||
/**
|
||||
* Class NotificationReadAtField.
|
||||
*/
|
||||
class NotificationReadAtField extends DateField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, Notification::ATTRIBUTE_READ_AT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the field should be displayed to the user.
|
||||
*
|
||||
* @param Query $query
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function shouldRender(Query $query): bool
|
||||
{
|
||||
$criteria = $query->getFieldCriteria($this->schema->type());
|
||||
|
||||
return $criteria === null || $criteria->isAllowedField($this->getKey());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Field\User\Notification;
|
||||
|
||||
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\User\Notification;
|
||||
|
||||
/**
|
||||
* Class NotificationTypeField.
|
||||
*/
|
||||
class NotificationTypeField extends Field implements SelectableField
|
||||
{
|
||||
/**
|
||||
* Create a new field instance.
|
||||
*
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function __construct(Schema $schema)
|
||||
{
|
||||
parent::__construct($schema, Notification::ATTRIBUTE_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 notifications on query.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ 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\Api\Schema\User\NotificationSchema;
|
||||
use App\Http\Resources\Auth\User\Resource\MyResource;
|
||||
use App\Models\Auth\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -44,6 +45,7 @@ class MySchema extends EloquentSchema
|
||||
{
|
||||
return $this->withIntermediatePaths([
|
||||
new AllowedInclude(new ExternalProfileSchema(), User::RELATION_EXTERNAL_PROFILES),
|
||||
new AllowedInclude(new NotificationSchema(), User::RELATION_NOTIFICATIONS),
|
||||
new AllowedInclude(new PermissionSchema(), User::RELATION_PERMISSIONS),
|
||||
new AllowedInclude(new PlaylistSchema(), User::RELATION_PLAYLISTS),
|
||||
new AllowedInclude(new RoleSchema(), User::RELATION_ROLES),
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Api\Schema\User;
|
||||
|
||||
use App\Http\Api\Field\User\Notification\NotificationDataField;
|
||||
use App\Http\Api\Field\User\Notification\NotificationNotifiableIdField;
|
||||
use App\Http\Api\Field\User\Notification\NotificationNotifiableTypeField;
|
||||
use App\Http\Api\Field\User\Notification\NotificationReadAtField;
|
||||
use App\Http\Api\Field\User\Notification\NotificationTypeField;
|
||||
use App\Http\Api\Field\Base\CreatedAtField;
|
||||
use App\Http\Api\Field\Base\UpdatedAtField;
|
||||
use App\Http\Api\Field\Base\UuidField;
|
||||
use App\Http\Api\Field\Field;
|
||||
use App\Http\Api\Include\AllowedInclude;
|
||||
use App\Http\Api\Schema\EloquentSchema;
|
||||
use App\Http\Resources\User\Resource\NotificationResource;
|
||||
|
||||
/**
|
||||
* Class NotificationSchema.
|
||||
*/
|
||||
class NotificationSchema extends EloquentSchema
|
||||
{
|
||||
/**
|
||||
* Get the type of the resource.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return NotificationResource::$wrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the allowed includes.
|
||||
*
|
||||
* @return AllowedInclude[]
|
||||
*/
|
||||
public function allowedIncludes(): array
|
||||
{
|
||||
return $this->withIntermediatePaths([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the direct fields of the resource.
|
||||
*
|
||||
* @return Field[]
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function fields(): array
|
||||
{
|
||||
return [
|
||||
new CreatedAtField($this),
|
||||
new UpdatedAtField($this),
|
||||
new UuidField($this, 'id'),
|
||||
new NotificationTypeField($this),
|
||||
new NotificationNotifiableTypeField($this),
|
||||
new NotificationNotifiableIdField($this),
|
||||
new NotificationDataField($this),
|
||||
new NotificationReadAtField($this),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?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\User\NotificationSchema;
|
||||
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\User\Collection\NotificationCollection;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\User\Notification;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
/**
|
||||
* Class MyNotificationController.
|
||||
*/
|
||||
class MyNotificationController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware(Authenticate::using('sanctum'));
|
||||
parent::__construct(Notification::class, 'notification');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param IndexRequest $request
|
||||
* @param IndexAction $action
|
||||
* @return NotificationCollection
|
||||
*/
|
||||
public function index(IndexRequest $request, IndexAction $action): NotificationCollection
|
||||
{
|
||||
$query = new Query($request->validated());
|
||||
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
$builder = $user->notifications()->getQuery();
|
||||
|
||||
$notifications = $action->index($builder, $query, $request->schema());
|
||||
|
||||
return new NotificationCollection($notifications, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark an unread notification as read.
|
||||
*
|
||||
* @param Notification $notification
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function read(Notification $notification): JsonResponse
|
||||
{
|
||||
$this->authorize('read', $notification);
|
||||
|
||||
$notification->markAsRead();
|
||||
|
||||
return new JsonResponse([
|
||||
'message' => __('notifications.read'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a read notification as unread.
|
||||
*
|
||||
* @param Notification $notification
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function unread(Notification $notification): JsonResponse
|
||||
{
|
||||
$this->authorize('unread', $notification);
|
||||
|
||||
$notification->markAsUnread();
|
||||
|
||||
return new JsonResponse([
|
||||
'message' => __('notifications.unread'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark unread notifications as read.
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function readall(): JsonResponse
|
||||
{
|
||||
$this->authorize('readall', Notification::class);
|
||||
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
$user->unreadNotifications()->update([Notification::ATTRIBUTE_READ_AT => now()]);
|
||||
|
||||
return new JsonResponse([
|
||||
'message' => __('notifications.read_all'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying schema.
|
||||
*
|
||||
* @return Schema
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function schema(): Schema
|
||||
{
|
||||
return new NotificationSchema();
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ namespace App\Http\Middleware\Models\Admin;
|
||||
|
||||
use App\Constants\Config\ReportConstants;
|
||||
use App\Enums\Auth\SpecialPermission;
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Models\User\Report;
|
||||
use App\Models\Auth\User;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\User\Collection;
|
||||
|
||||
use App\Http\Resources\User\Resource\NotificationResource;
|
||||
use App\Http\Resources\BaseCollection;
|
||||
use App\Models\User\Notification;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class NotificationCollection.
|
||||
*/
|
||||
class NotificationCollection extends BaseCollection
|
||||
{
|
||||
/**
|
||||
* The "data" wrapper that should be applied.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public static $wrap = 'notifications';
|
||||
|
||||
/**
|
||||
* 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 (Notification $notification) => new NotificationResource($notification, $this->query))->all();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\User\Resource;
|
||||
|
||||
use App\Http\Api\Schema\User\NotificationSchema;
|
||||
use App\Http\Api\Schema\Schema;
|
||||
use App\Http\Resources\BaseResource;
|
||||
|
||||
/**
|
||||
* Class NotificationResource.
|
||||
*/
|
||||
class NotificationResource extends BaseResource
|
||||
{
|
||||
/**
|
||||
* The "data" wrapper that should be applied.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public static $wrap = 'notification';
|
||||
|
||||
/**
|
||||
* Get the resource schema.
|
||||
*
|
||||
* @return Schema
|
||||
*/
|
||||
protected function schema(): Schema
|
||||
{
|
||||
return new NotificationSchema();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Contracts\Events\NotifiesUsersEvent;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
|
||||
/**
|
||||
* Class NotifiesUsers.
|
||||
*/
|
||||
class NotifiesUsers implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue;
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param NotifiesUsersEvent $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(NotifiesUsersEvent $event): void
|
||||
{
|
||||
$event->notify();
|
||||
}
|
||||
}
|
||||
@@ -12,16 +12,21 @@ use App\Events\Auth\User\UserDeleted;
|
||||
use App\Events\Auth\User\UserRestored;
|
||||
use App\Events\Auth\User\UserUpdated;
|
||||
use App\Models\Admin\ActionLog;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Models\User\Report;
|
||||
use App\Models\List\Playlist;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use App\Models\User\Notification;
|
||||
use App\Notifications\UserNotification;
|
||||
use Database\Factories\Auth\UserFactory;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Models\Contracts\FilamentUser;
|
||||
use Filament\Models\Contracts\HasAvatar;
|
||||
use Filament\Notifications\DatabaseNotification as FilamentNotification;
|
||||
use Filament\Panel;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
@@ -80,6 +85,7 @@ class User extends Authenticatable implements MustVerifyEmail, Nameable, HasSubt
|
||||
|
||||
final public const RELATION_EXTERNAL_PROFILES = 'externalprofiles';
|
||||
final public const RELATION_MANAGED_REPORTS = 'managedreports';
|
||||
final public const RELATION_NOTIFICATIONS = 'notifications';
|
||||
final public const RELATION_PERMISSIONS = 'permissions';
|
||||
final public const RELATION_PLAYLISTS = 'playlists';
|
||||
final public const RELATION_REPORTS = 'reports';
|
||||
@@ -272,4 +278,20 @@ class User extends Authenticatable implements MustVerifyEmail, Nameable, HasSubt
|
||||
{
|
||||
return $this->hasMany(ActionLog::class, ActionLog::ATTRIBUTE_USER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entity's notifications.
|
||||
*
|
||||
* @return MorphMany<Notification, $this>
|
||||
*/
|
||||
public function notifications(): MorphMany
|
||||
{
|
||||
$notifications = $this->morphMany(Notification::class, 'notifiable')->latest();
|
||||
|
||||
if (Filament::isServing()) {
|
||||
return $notifications->where(Notification::ATTRIBUTE_TYPE, FilamentNotification::class);
|
||||
}
|
||||
|
||||
return $notifications->where(Notification::ATTRIBUTE_TYPE, UserNotification::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\User;
|
||||
|
||||
use Database\Factories\User\NotificationFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Notifications\DatabaseNotification;
|
||||
|
||||
/**
|
||||
* Class Notification.
|
||||
*
|
||||
* @method static NotificationFactory factory(...$parameters)
|
||||
*/
|
||||
class Notification extends DatabaseNotification
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
final public const ATTRIBUTE_ID = 'id';
|
||||
final public const ATTRIBUTE_TYPE = 'type';
|
||||
final public const ATTRIBUTE_NOTIFIABLE_TYPE = 'notifiable_type';
|
||||
final public const ATTRIBUTE_NOTIFIABLE_ID = 'notifiable_id';
|
||||
final public const ATTRIBUTE_DATA = 'data';
|
||||
final public const ATTRIBUTE_READ_AT = 'read_at';
|
||||
|
||||
final public const RELATION_NOTIFIABLE = 'notifiable';
|
||||
}
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Admin;
|
||||
namespace App\Models\User;
|
||||
|
||||
use App\Contracts\Models\HasSubtitle;
|
||||
use App\Contracts\Models\Nameable;
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Models\Admin\Report\ReportStep;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Models\User\Report\ReportStep;
|
||||
use App\Models\Auth\User;
|
||||
use Database\Factories\Admin\ReportFactory;
|
||||
use Database\Factories\User\ReportFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models\Admin\Report;
|
||||
namespace App\Models\User\Report;
|
||||
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Enums\Models\Admin\ReportActionType;
|
||||
use App\Models\Admin\Report;
|
||||
use Database\Factories\Admin\Report\ReportStepFactory;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Enums\Models\User\ReportActionType;
|
||||
use App\Models\User\Report;
|
||||
use Database\Factories\User\Report\ReportStepFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Enums\NotificationType;
|
||||
use App\Enums\Models\User\NotificationType;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
@@ -24,14 +24,12 @@ class UserNotification extends Notification implements Arrayable, ShouldQueue
|
||||
* @param string $body
|
||||
* @param NotificationType $type
|
||||
* @param string|null $image
|
||||
* @param array<string, mixed>|null $data
|
||||
*/
|
||||
public function __construct(
|
||||
public string $title,
|
||||
public string $body,
|
||||
public NotificationType $type,
|
||||
public ?string $image = null,
|
||||
public ?array $data = [],
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -59,7 +57,6 @@ class UserNotification extends Notification implements Arrayable, ShouldQueue
|
||||
'body' => $this->body,
|
||||
'type' => $this->type->value,
|
||||
'image' => $this->image,
|
||||
'data' => $this->data,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Policies\User;
|
||||
|
||||
use App\Enums\Auth\CrudPermission;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\BaseModel;
|
||||
use App\Models\User\Notification;
|
||||
use App\Policies\BasePolicy;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* Class NotificationPolicy.
|
||||
*/
|
||||
class NotificationPolicy extends BasePolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*
|
||||
* @param User|null $user
|
||||
* @return bool
|
||||
*/
|
||||
public function viewAny(?User $user): bool
|
||||
{
|
||||
return $user === null || $user->can(CrudPermission::VIEW->format(Notification::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*
|
||||
* @param User|null $user
|
||||
* @param Notification $notification
|
||||
* @return bool
|
||||
*/
|
||||
public function view(?User $user, BaseModel|Model $notification): bool
|
||||
{
|
||||
return $notification->notifiable()->is($user) && $user->can(CrudPermission::VIEW->format(Notification::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can mark the notification as read.
|
||||
*
|
||||
* @param User $user
|
||||
* @param Notification $notification
|
||||
* @return bool
|
||||
*/
|
||||
public function read(User $user, BaseModel|Model $notification): bool
|
||||
{
|
||||
return $notification->notifiable()->is($user) && $user->can(CrudPermission::UPDATE->format(Notification::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can mark the notification as unread.
|
||||
*
|
||||
* @param User $user
|
||||
* @param Notification $notification
|
||||
* @return bool
|
||||
*/
|
||||
public function unread(User $user, BaseModel|Model $notification): bool
|
||||
{
|
||||
return $notification->notifiable()->is($user) && $user->can(CrudPermission::UPDATE->format(Notification::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can mark the all their notifications as read.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function readall(User $user): bool
|
||||
{
|
||||
return $user->can(CrudPermission::UPDATE->format(Notification::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @param Notification $notification
|
||||
* @return bool
|
||||
*/
|
||||
public function update(User $user, BaseModel|Model $notification): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*
|
||||
* @param User $user
|
||||
* @param Notification $notification
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(User $user, BaseModel|Model $notification): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Policies\Admin;
|
||||
namespace App\Policies\User;
|
||||
|
||||
use App\Enums\Auth\CrudPermission;
|
||||
use App\Enums\Auth\Role;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Models\User\Report;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\BaseModel;
|
||||
use App\Policies\BasePolicy;
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories\User;
|
||||
|
||||
use App\Models\User\Notification;
|
||||
use App\Notifications\UserNotification;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class NotificationFactory.
|
||||
*
|
||||
* @method Notification createOne($attributes = [])
|
||||
* @method Notification makeOne($attributes = [])
|
||||
*
|
||||
* @extends Factory<Notification>
|
||||
*/
|
||||
class NotificationFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var class-string<Notification>
|
||||
*/
|
||||
protected $model = Notification::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
$data = [
|
||||
'title' => fake()->text(),
|
||||
'body' => fake()->text(),
|
||||
'image' => fake()->imageUrl(),
|
||||
];
|
||||
|
||||
return [
|
||||
Notification::ATTRIBUTE_ID => Str::uuid()->__toString(),
|
||||
Notification::ATTRIBUTE_TYPE => UserNotification::class,
|
||||
Notification::ATTRIBUTE_DATA => $data,
|
||||
];
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -2,11 +2,11 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories\Admin\Report;
|
||||
namespace Database\Factories\User\Report;
|
||||
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Enums\Models\Admin\ReportActionType;
|
||||
use App\Models\Admin\Report\ReportStep;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Enums\Models\User\ReportActionType;
|
||||
use App\Models\User\Report\ReportStep;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
+3
-3
@@ -2,10 +2,10 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories\Admin;
|
||||
namespace Database\Factories\User;
|
||||
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Models\User\Report;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Models\Admin\Report\ReportStep;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Models\User\Report;
|
||||
use App\Models\User\Report\ReportStep;
|
||||
use App\Models\Auth\User;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
@@ -11,7 +11,7 @@ use App\Models\Admin\Announcement;
|
||||
use App\Models\Admin\Dump;
|
||||
use App\Models\Admin\Feature;
|
||||
use App\Models\Admin\FeaturedTheme;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Models\User\Report;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Auth\Role;
|
||||
use App\Models\Auth\User;
|
||||
@@ -21,6 +21,7 @@ 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\User\Notification;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Anime\AnimeSynonym;
|
||||
use App\Models\Wiki\Anime\AnimeTheme;
|
||||
@@ -61,7 +62,6 @@ class PermissionSeeder extends Seeder
|
||||
$this->registerResource(Dump::class, $extendedCrudPermissions);
|
||||
$this->registerResource(Feature::class, CrudPermission::cases());
|
||||
$this->registerResource(FeaturedTheme::class, $extendedCrudPermissions);
|
||||
$this->registerResource(Report::class, CrudPermission::cases());
|
||||
|
||||
// Auth Resources
|
||||
$this->registerResource(Permission::class, [CrudPermission::VIEW]);
|
||||
@@ -77,6 +77,10 @@ class PermissionSeeder extends Seeder
|
||||
$this->registerResource(Playlist::class, $extendedCrudPermissions);
|
||||
$this->registerResource(PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
// User Resources
|
||||
$this->registerResource(Notification::class, [CrudPermission::VIEW, CrudPermission::UPDATE]);
|
||||
$this->registerResource(Report::class, CrudPermission::cases());
|
||||
|
||||
// Wiki Resources
|
||||
$this->registerResource(Anime::class, $extendedCrudPermissions);
|
||||
$this->registerResource(AnimeSynonym::class, $extendedCrudPermissions);
|
||||
|
||||
@@ -12,7 +12,7 @@ use App\Models\Admin\Announcement;
|
||||
use App\Models\Admin\Dump;
|
||||
use App\Models\Admin\Feature;
|
||||
use App\Models\Admin\FeaturedTheme;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Models\User\Report;
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Auth\Role;
|
||||
use App\Models\Auth\User;
|
||||
@@ -22,6 +22,7 @@ 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\User\Notification;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Anime\AnimeSynonym;
|
||||
use App\Models\Wiki\Anime\AnimeTheme;
|
||||
@@ -68,7 +69,6 @@ class AdminSeeder extends RoleSeeder
|
||||
$this->configureResource($role, Dump::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, Feature::class, [CrudPermission::VIEW, CrudPermission::UPDATE]);
|
||||
$this->configureResource($role, FeaturedTheme::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, Report::class, CrudPermission::cases());
|
||||
|
||||
// Auth Resources
|
||||
$this->configureResource($role, Permission::class, [CrudPermission::VIEW]);
|
||||
@@ -84,6 +84,10 @@ class AdminSeeder extends RoleSeeder
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
// User Resources
|
||||
$this->configureResource($role, Notification::class, [CrudPermission::VIEW, CrudPermission::UPDATE]);
|
||||
$this->configureResource($role, Report::class, CrudPermission::cases());
|
||||
|
||||
// Wiki Resources
|
||||
$this->configureResource($role, Anime::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, AnimeSynonym::class, $extendedCrudPermissions);
|
||||
|
||||
@@ -15,6 +15,7 @@ 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\User\Notification;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Anime\AnimeSynonym;
|
||||
use App\Models\Wiki\Anime\AnimeTheme;
|
||||
@@ -65,6 +66,9 @@ class DeveloperRoleSeeder extends RoleSeeder
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
// User Resources
|
||||
$this->configureResource($role, Notification::class, [CrudPermission::VIEW, CrudPermission::UPDATE]);
|
||||
|
||||
// Wiki Resources
|
||||
$this->configureResource($role, Anime::class, [CrudPermission::VIEW]);
|
||||
$this->configureResource($role, AnimeSynonym::class, [CrudPermission::VIEW]);
|
||||
|
||||
@@ -15,6 +15,7 @@ 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\User\Notification;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Anime\AnimeSynonym;
|
||||
use App\Models\Wiki\Anime\AnimeTheme;
|
||||
@@ -72,6 +73,9 @@ class EncoderRoleSeeder extends RoleSeeder
|
||||
],
|
||||
);
|
||||
|
||||
// User Resources
|
||||
$this->configureResource($role, Notification::class, [CrudPermission::VIEW, CrudPermission::UPDATE]);
|
||||
|
||||
// Wiki Resources
|
||||
$this->configureResource($role, Anime::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, AnimeSynonym::class, $extendedCrudPermissions);
|
||||
|
||||
@@ -15,6 +15,7 @@ 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\User\Notification;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Anime\AnimeSynonym;
|
||||
use App\Models\Wiki\Anime\AnimeTheme;
|
||||
@@ -65,6 +66,9 @@ class PatronRoleSeeder extends RoleSeeder
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
// User Resources
|
||||
$this->configureResource($role, Notification::class, [CrudPermission::VIEW, CrudPermission::UPDATE]);
|
||||
|
||||
// Wiki Resources
|
||||
$this->configureResource($role, Anime::class, [CrudPermission::VIEW]);
|
||||
$this->configureResource($role, AnimeSynonym::class, [CrudPermission::VIEW]);
|
||||
|
||||
@@ -15,6 +15,7 @@ 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\User\Notification;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Anime\AnimeSynonym;
|
||||
use App\Models\Wiki\Anime\AnimeTheme;
|
||||
@@ -65,6 +66,9 @@ class WikiEditorRoleSeeder extends RoleSeeder
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
// User Resources
|
||||
$this->configureResource($role, Notification::class, [CrudPermission::VIEW, CrudPermission::UPDATE]);
|
||||
|
||||
$extendedCrudPermissions = array_merge(
|
||||
CrudPermission::cases(),
|
||||
[
|
||||
|
||||
@@ -8,7 +8,7 @@ use App\Enums\Auth\CrudPermission;
|
||||
use App\Enums\Auth\ExtendedCrudPermission;
|
||||
use App\Enums\Auth\Role as RoleEnum;
|
||||
use App\Enums\Auth\SpecialPermission;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Models\User\Report;
|
||||
use App\Models\Auth\Role;
|
||||
use App\Models\Discord\DiscordThread;
|
||||
use App\Models\Document\Page;
|
||||
@@ -16,6 +16,7 @@ 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\User\Notification;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Anime\AnimeSynonym;
|
||||
use App\Models\Wiki\Anime\AnimeTheme;
|
||||
@@ -57,9 +58,6 @@ class WikiViewerRoleSeeder extends RoleSeeder
|
||||
ExtendedCrudPermission::cases(),
|
||||
);
|
||||
|
||||
// Admin Resources
|
||||
$this->configureResource($role, Report::class, [CrudPermission::VIEW, CrudPermission::CREATE, CrudPermission::UPDATE]);
|
||||
|
||||
// Discord Resources
|
||||
$this->configureResource($role, DiscordThread::class, [CrudPermission::VIEW]);
|
||||
|
||||
@@ -69,6 +67,10 @@ class WikiViewerRoleSeeder extends RoleSeeder
|
||||
$this->configureResource($role, Playlist::class, $extendedCrudPermissions);
|
||||
$this->configureResource($role, PlaylistTrack::class, $extendedCrudPermissions);
|
||||
|
||||
// User Resources
|
||||
$this->configureResource($role, Notification::class, [CrudPermission::VIEW, CrudPermission::UPDATE]);
|
||||
$this->configureResource($role, Report::class, [CrudPermission::VIEW, CrudPermission::CREATE, CrudPermission::UPDATE]);
|
||||
|
||||
// Wiki Resources
|
||||
$this->configureResource($role, Anime::class, [CrudPermission::VIEW]);
|
||||
$this->configureResource($role, AnimeSynonym::class, [CrudPermission::VIEW]);
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Enums\Models\Admin\ActionLogStatus;
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Enums\Models\Admin\ReportActionType;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Enums\Models\User\ReportActionType;
|
||||
use App\Enums\Models\List\ExternalEntryWatchStatus;
|
||||
use App\Enums\Models\List\ExternalProfileSite;
|
||||
use App\Enums\Models\List\ExternalProfileVisibility;
|
||||
|
||||
@@ -824,6 +824,7 @@ return [
|
||||
'finished_at' => 'Finished At',
|
||||
'moderator' => 'Moderator',
|
||||
'mod_notes' => 'Moderator Notes',
|
||||
'notes' => 'Notes',
|
||||
'status' => 'Status',
|
||||
'target' => 'Target',
|
||||
],
|
||||
@@ -961,6 +962,7 @@ return [
|
||||
'discord' => 'Discord',
|
||||
'document' => 'Document',
|
||||
'list' => 'List',
|
||||
'user' => 'User',
|
||||
'wiki' => 'Wiki',
|
||||
],
|
||||
'label' => [
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Notifications Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are used during notification responses
|
||||
| to send as response to the user.
|
||||
|
|
||||
*/
|
||||
|
||||
'read_all' => 'All unread notifications were marked as read.',
|
||||
'read' => 'Notification was marked as read.',
|
||||
'unread' => 'Notification was marked as unread.',
|
||||
];
|
||||
@@ -8,6 +8,7 @@ use App\Http\Controllers\Api\Admin\DumpController;
|
||||
use App\Http\Controllers\Api\Admin\FeatureController;
|
||||
use App\Http\Controllers\Api\Admin\FeaturedThemeController;
|
||||
use App\Http\Controllers\Api\Auth\User\Me\List\MyExternalProfileController;
|
||||
use App\Http\Controllers\Api\Auth\User\Me\List\MyNotificationController;
|
||||
use App\Http\Controllers\Api\Auth\User\Me\List\MyPlaylistController;
|
||||
use App\Http\Controllers\Api\Auth\User\Me\MyController;
|
||||
use App\Http\Controllers\Api\Document\PageController;
|
||||
@@ -236,6 +237,10 @@ Route::apiResource('feature', FeatureController::class)
|
||||
// Auth Routes
|
||||
Route::get('/me', [MyController::class, 'show'])->name('me.show');
|
||||
Route::get('/me/externalprofile', [MyExternalProfileController::class, 'index'])->name('me.externalprofile.index');
|
||||
Route::get('/me/notification', [MyNotificationController::class, 'index'])->name('me.notification.index');
|
||||
Route::match(['put', 'patch'], '/me/notification/readall', [MyNotificationController::class, 'readall'])->name('me.notification.readall');
|
||||
Route::match(['put', 'patch'], '/me/notification/{notification}/read', [MyNotificationController::class, 'read'])->name('me.notification.read');
|
||||
Route::match(['put', 'patch'], '/me/notification/{notification}/unread', [MyNotificationController::class, 'unread'])->name('me.notification.unread');
|
||||
Route::get('/me/playlist', [MyPlaylistController::class, 'index'])->name('me.playlist.index');
|
||||
|
||||
// Document Routes
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Http\Api\Auth\User\Me\List\ExternalProfile;
|
||||
|
||||
use App\Enums\Auth\CrudPermission;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Resources\List\Collection\ExternalProfileCollection;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\List\ExternalProfile;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class MyExternalProfileIndexTest.
|
||||
*/
|
||||
class MyExternalProfileIndexTest extends TestCase
|
||||
{
|
||||
use WithFaker;
|
||||
|
||||
/**
|
||||
* The My External Profile Index Endpoint shall be protected by sanctum.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testProtected(): void
|
||||
{
|
||||
$response = $this->get(route('api.me.externalprofile.index'));
|
||||
|
||||
$response->assertUnauthorized();
|
||||
}
|
||||
|
||||
/**
|
||||
* The My External Profile Index Endpoint shall forbid users without the view external profile permission.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testForbiddenIfMissingPermission(): void
|
||||
{
|
||||
$user = User::factory()->createOne();
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$response = $this->get(route('api.me.externalprofile.index'));
|
||||
|
||||
$response->assertForbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* The My External Profile Index Endpoint shall return profiles owned by the user.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testOnlySeesOwnedProfiles(): void
|
||||
{
|
||||
ExternalProfile::factory()
|
||||
->for(User::factory())
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->create();
|
||||
|
||||
ExternalProfile::factory()
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->create();
|
||||
|
||||
$user = User::factory()->withPermissions(CrudPermission::VIEW->format(ExternalProfile::class))->createOne();
|
||||
|
||||
$profileCount = $this->faker->randomDigitNotNull();
|
||||
|
||||
$profiles = ExternalProfile::factory()
|
||||
->for($user)
|
||||
->count($profileCount)
|
||||
->create();
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$response = $this->get(route('api.me.externalprofile.index'));
|
||||
|
||||
$response->assertJsonCount($profileCount, ExternalProfileCollection::$wrap);
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
new ExternalProfileCollection($profiles, new Query())
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Http\Api\Auth\User\Me\Notification;
|
||||
|
||||
use App\Enums\Auth\CrudPermission;
|
||||
use App\Http\Api\Query\Query;
|
||||
use App\Http\Resources\User\Collection\NotificationCollection;
|
||||
use App\Models\Auth\User;
|
||||
use App\Models\User\Notification;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class MyNotificationIndexTest.
|
||||
*/
|
||||
class MyNotificationIndexTest extends TestCase
|
||||
{
|
||||
use WithFaker;
|
||||
|
||||
/**
|
||||
* The My Notification Index Endpoint shall be protected by sanctum.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testProtected(): void
|
||||
{
|
||||
$response = $this->get(route('api.me.notification.index'));
|
||||
|
||||
$response->assertUnauthorized();
|
||||
}
|
||||
|
||||
/**
|
||||
* The My Notification Index Endpoint shall forbid users without the view notification permission.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testForbiddenIfMissingPermission(): void
|
||||
{
|
||||
$user = User::factory()->createOne();
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$response = $this->get(route('api.me.notification.index'));
|
||||
|
||||
$response->assertForbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* The My Notification Index Endpoint shall return notifications owned by the user.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testOnlySeesOwnedNotifications(): void
|
||||
{
|
||||
Notification::factory()
|
||||
->for(User::factory()->createOne(), Notification::RELATION_NOTIFIABLE)
|
||||
->count($this->faker->randomDigitNotNull())
|
||||
->create();
|
||||
|
||||
$user = User::factory()->withPermissions(CrudPermission::VIEW->format(Notification::class))->createOne();
|
||||
|
||||
$notificationCount = $this->faker->randomDigitNotNull();
|
||||
|
||||
$notifications = Notification::factory()
|
||||
->for($user, Notification::RELATION_NOTIFIABLE)
|
||||
->count($notificationCount)
|
||||
->create()
|
||||
->sortBy(Notification::ATTRIBUTE_ID);
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$response = $this->get(route('api.me.notification.index'));
|
||||
|
||||
$response->assertJsonCount($notificationCount, NotificationCollection::$wrap);
|
||||
|
||||
$response->assertJson(
|
||||
json_decode(
|
||||
json_encode(
|
||||
new NotificationCollection($notifications, new Query())
|
||||
->response()
|
||||
->getData()
|
||||
),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Events;
|
||||
|
||||
use App\Contracts\Events\NotifiesUsersEvent;
|
||||
use App\Listeners\NotifiesUsers;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class NotifiesUsersTest.
|
||||
*/
|
||||
class NotifiesUsersTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* NotifiesUsers shall listen to NotifiesUsersEvent.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testListening(): void
|
||||
{
|
||||
Event::assertListening(NotifiesUsersEvent::class, NotifiesUsers::class);
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -2,12 +2,12 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Models\Admin\Report;
|
||||
namespace Tests\Unit\Models\User\Report;
|
||||
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Enums\Models\Admin\ReportActionType;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Models\Admin\Report\ReportStep;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Enums\Models\User\ReportActionType;
|
||||
use App\Models\User\Report;
|
||||
use App\Models\User\Report\ReportStep;
|
||||
use App\Models\Wiki\Anime;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Models\Admin;
|
||||
namespace Tests\Unit\Models\User;
|
||||
|
||||
use App\Enums\Models\Admin\ApprovableStatus;
|
||||
use App\Models\Admin\Report;
|
||||
use App\Models\Admin\Report\ReportStep;
|
||||
use App\Enums\Models\User\ApprovableStatus;
|
||||
use App\Models\User\Report;
|
||||
use App\Models\User\Report\ReportStep;
|
||||
use App\Models\Auth\User;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
Reference in New Issue
Block a user