diff --git a/.gitignore b/.gitignore index 47018b48..64784fb9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ npm-debug.log vagrant-credentials.rb ubuntu-bionic-18.04-cloudimg-console.log .phpunit.result.cache +code/.laravel diff --git a/code/app/Athenia/Contracts/Repositories/Statistic/StatisticRepositoryContract.php b/code/app/Athenia/Contracts/Repositories/Statistic/StatisticRepositoryContract.php index 455a3ed9..14c27b22 100644 --- a/code/app/Athenia/Contracts/Repositories/Statistic/StatisticRepositoryContract.php +++ b/code/app/Athenia/Contracts/Repositories/Statistic/StatisticRepositoryContract.php @@ -4,10 +4,18 @@ namespace App\Athenia\Contracts\Repositories\Statistic; use App\Athenia\Contracts\Repositories\BaseRepositoryContract; +use Illuminate\Support\Collection; /** * Interface StatisticRepositoryContract */ interface StatisticRepositoryContract extends BaseRepositoryContract { + /** + * Get all statistics for a given model + * + * @param string $model + * @return Collection + */ + public function findAllForModel(string $model): Collection; } \ No newline at end of file diff --git a/code/app/Athenia/Contracts/Repositories/User/ArticleNoteRepositoryContract.php b/code/app/Athenia/Contracts/Repositories/User/ArticleNoteRepositoryContract.php new file mode 100644 index 00000000..5ee419cd --- /dev/null +++ b/code/app/Athenia/Contracts/Repositories/User/ArticleNoteRepositoryContract.php @@ -0,0 +1,13 @@ +user = $user; + $this->invitationToken = $invitationToken; + } + + /** + * @return User + */ + public function getUser(): User + { + return $this->user; + } + + /** + * @return InvitationToken + */ + public function getInvitationToken(): InvitationToken + { + return $this->invitationToken; + } +} diff --git a/code/app/Athenia/Http/Core/Controllers/AuthenticationControllerAbstract.php b/code/app/Athenia/Http/Core/Controllers/AuthenticationControllerAbstract.php index f43e283b..2a50977c 100644 --- a/code/app/Athenia/Http/Core/Controllers/AuthenticationControllerAbstract.php +++ b/code/app/Athenia/Http/Core/Controllers/AuthenticationControllerAbstract.php @@ -3,7 +3,9 @@ namespace App\Athenia\Http\Core\Controllers; +use App\Athenia\Contracts\Repositories\User\InvitationTokenRepositoryContract; use App\Athenia\Contracts\Repositories\User\UserRepositoryContract; +use App\Athenia\Events\User\InvitationAcceptedEvent; use App\Athenia\Events\User\SignUpEvent; use App\Http\Core\Requests; use App\Models\User\User; @@ -51,19 +53,26 @@ abstract class AuthenticationControllerAbstract extends BaseControllerAbstract */ protected $dispatcher; + /** + * @var InvitationTokenRepositoryContract + */ + protected $invitationTokenRepository; + /** * AuthenticationController constructor. * @param UserRepositoryContract $userRepository * @param Hasher $hasher * @param JWTAuth $auth * @param Dispatcher $dispatcher + * @param InvitationTokenRepositoryContract $invitationTokenRepository */ - public function __construct(UserRepositoryContract $userRepository, Hasher $hasher, JWTAuth $auth, Dispatcher $dispatcher) + public function __construct(UserRepositoryContract $userRepository, Hasher $hasher, JWTAuth $auth, Dispatcher $dispatcher, InvitationTokenRepositoryContract $invitationTokenRepository) { $this->userRepository = $userRepository; $this->hasher = $hasher; $this->auth = $auth; $this->dispatcher = $dispatcher; + $this->invitationTokenRepository = $invitationTokenRepository; } /** @@ -243,6 +252,10 @@ public function signUp(Requests\Authentication\SignUpRequest $request) { $data = $request->json()->all(); + // Store invitation token separately and remove it from user data + $invitationTokenValue = $data['invitation_token'] ?? null; + unset($data['invitation_token']); + $forcedData = [ 'password' => $this->hasher->make($data['password']), ]; @@ -252,6 +265,14 @@ public function signUp(Requests\Authentication\SignUpRequest $request) $this->dispatcher->dispatch(new SignUpEvent($model)); + // If an invitation token was provided, dispatch the InvitationAcceptedEvent + if ($invitationTokenValue) { + $invitationToken = $this->invitationTokenRepository->findByToken($invitationTokenValue); + if ($invitationToken) { + $this->dispatcher->dispatch(new InvitationAcceptedEvent($model, $invitationToken)); + } + } + $token = $this->auth->fromUser($model); return new JsonResponse([ 'token' => $token, diff --git a/code/app/Athenia/Http/Core/Controllers/User/ArticleNoteControllerAbstract.php b/code/app/Athenia/Http/Core/Controllers/User/ArticleNoteControllerAbstract.php new file mode 100644 index 00000000..90b65e66 --- /dev/null +++ b/code/app/Athenia/Http/Core/Controllers/User/ArticleNoteControllerAbstract.php @@ -0,0 +1,154 @@ +repository = $repository; + $this->articleRepository = $articleRepository; + } + + /** + * @param Requests\User\ArticleNote\IndexRequest $request + * @param User $user + * @return LengthAwarePaginator + */ + public function index(Requests\User\ArticleNote\IndexRequest $request, User $user): LengthAwarePaginator + { + return $this->repository->findAll( + $this->filter($request), + $this->search($request), + $this->order($request), + $this->expand($request), + $this->limit($request), + [$user], + (int)$request->input('page', 1) + ); + } + + /** + * @param Requests\User\ArticleNote\StoreRequest $request + * @param User $user + * @return JsonResponse + */ + public function store(Requests\User\ArticleNote\StoreRequest $request, User $user): JsonResponse + { + $data = $request->json()->all(); + $data['user_id'] = $user->id; + + /** @var ArticleNote $model */ + $model = $this->repository->create($data); + + return new JsonResponse($model, 201); + } + + /** + * @param Requests\User\ArticleNote\ViewRequest $request + * @param User $user + * @param ArticleNote $articleNote + * @return ArticleNote + */ + public function show(Requests\User\ArticleNote\ViewRequest $request, User $user, ArticleNote $articleNote): ArticleNote + { + return $articleNote; + } + + /** + * @param Requests\User\ArticleNote\UpdateRequest $request + * @param User $user + * @param ArticleNote $articleNote + * @return BaseModelAbstract + */ + public function update(Requests\User\ArticleNote\UpdateRequest $request, User $user, ArticleNote $articleNote): BaseModelAbstract + { + $data = $request->json()->all(); + + return $this->repository->update($articleNote, $data); + } + + /** + * @param Requests\User\ArticleNote\DeleteRequest $request + * @param User $user + * @param ArticleNote $articleNote + * @return JsonResponse + */ + public function destroy(Requests\User\ArticleNote\DeleteRequest $request, User $user, ArticleNote $articleNote): JsonResponse + { + $this->repository->delete($articleNote); + + return new JsonResponse(null, 204); + } + + /** + * Selects a random article for the user and creates or retrieves an article note + * + * @param Requests\User\ArticleNote\RandomArticleRequest $request + * @param User $user + * @return JsonResponse + */ + public function randomArticle(Requests\User\ArticleNote\RandomArticleRequest $request, User $user): JsonResponse + { + $article = $this->articleRepository->selectArticleForUser($user); + + if (!$article) { + return new JsonResponse([ + 'message' => 'No available articles found.' + ], 404); + } + + // Check if a note already exists for this article + $existingNote = ArticleNote::where('user_id', $user->id) + ->where('article_id', $article->id) + ->first(); + + if ($existingNote) { + $existingNote->load('article'); + return new JsonResponse($existingNote, 200); + } + + /** @var ArticleNote $articleNote */ + $articleNote = $this->repository->create([ + 'user_id' => $user->id, + 'article_id' => $article->id, + ]); + + $articleNote->load('article'); + + return new JsonResponse($articleNote, 201); + } +} diff --git a/code/app/Athenia/Http/Core/Controllers/Wiki/ArticleSummaryControllerAbstract.php b/code/app/Athenia/Http/Core/Controllers/Wiki/ArticleSummaryControllerAbstract.php new file mode 100644 index 00000000..6adade97 --- /dev/null +++ b/code/app/Athenia/Http/Core/Controllers/Wiki/ArticleSummaryControllerAbstract.php @@ -0,0 +1,118 @@ +repository = $repository; + } + + /** + * Display the summary for the article + * + * @param Requests\Wiki\ArticleSummary\ViewRequest $request + * @param Article $article + * @return JsonResponse + */ + public function show(Requests\Wiki\ArticleSummary\ViewRequest $request, Article $article): JsonResponse + { + $summary = $article->articleSummary; + + if (!$summary) { + return new JsonResponse([ + 'message' => 'Article summary not found.' + ], 404); + } + + return new JsonResponse($summary, 200); + } + + /** + * Create a new summary for the article + * + * @param Requests\Wiki\ArticleSummary\StoreRequest $request + * @param Article $article + * @return JsonResponse + */ + public function store(Requests\Wiki\ArticleSummary\StoreRequest $request, Article $article): JsonResponse + { + $data = $request->json()->all(); + $data['article_id'] = $article->id; + + /** @var ArticleSummary $model */ + $model = $this->repository->create($data); + + return new JsonResponse($model, 201); + } + + /** + * Update the article summary + * + * @param Requests\Wiki\ArticleSummary\UpdateRequest $request + * @param Article $article + * @return JsonResponse + */ + public function update(Requests\Wiki\ArticleSummary\UpdateRequest $request, Article $article): JsonResponse + { + $summary = $article->articleSummary; + + if (!$summary) { + return new JsonResponse([ + 'message' => 'Article summary not found.' + ], 404); + } + + $data = $request->json()->all(); + + /** @var ArticleSummary $updated */ + $updated = $this->repository->update($summary, $data); + + return new JsonResponse($updated, 200); + } + + /** + * Delete the article summary + * + * @param Requests\Wiki\ArticleSummary\DeleteRequest $request + * @param Article $article + * @return JsonResponse + */ + public function destroy(Requests\Wiki\ArticleSummary\DeleteRequest $request, Article $article): JsonResponse + { + $summary = $article->articleSummary; + + if (!$summary) { + return new JsonResponse([ + 'message' => 'Article summary not found.' + ], 404); + } + + $this->repository->delete($summary); + + return new JsonResponse(null, 204); + } +} diff --git a/code/app/Athenia/Http/Core/Requests/Authentication/SignUpRequest.php b/code/app/Athenia/Http/Core/Requests/Authentication/SignUpRequest.php index b9aa773c..e1faed60 100644 --- a/code/app/Athenia/Http/Core/Requests/Authentication/SignUpRequest.php +++ b/code/app/Athenia/Http/Core/Requests/Authentication/SignUpRequest.php @@ -5,7 +5,9 @@ use App\Athenia\Http\Core\Requests\BaseUnauthenticatedRequest; use App\Athenia\Http\Core\Requests\Traits\HasNoExpands; +use App\Athenia\Validators\InvitationTokenIsValidValidator; use App\Models\User\User; +use Illuminate\Contracts\Config\Repository; /** * Class SignUpRequest @@ -19,15 +21,23 @@ class SignUpRequest extends BaseUnauthenticatedRequest * Gets the rules for the verification * * @param User $user + * @param Repository $config * @return array */ - public function rules(User $user) + public function rules(User $user, Repository $config) { - return [ + $rules = [ 'email' => 'required|string|max:120|email|unique:users,email', 'first_name' => 'required|string|max:120', 'last_name' => 'string|max:120', 'password' => 'required|string|min:6|max:256', ]; + + // Add invitation token validation if invitations are required + if ($config->get('athenia.invitation_required', false)) { + $rules['invitation_token'] = 'required|string|' . InvitationTokenIsValidValidator::KEY; + } + + return $rules; } } \ No newline at end of file diff --git a/code/app/Athenia/Http/Core/Requests/User/ArticleNote/DeleteRequest.php b/code/app/Athenia/Http/Core/Requests/User/ArticleNote/DeleteRequest.php new file mode 100644 index 00000000..cfdfeab8 --- /dev/null +++ b/code/app/Athenia/Http/Core/Requests/User/ArticleNote/DeleteRequest.php @@ -0,0 +1,48 @@ +route('user'), + $this->route('article_note'), + ]; + } +} diff --git a/code/app/Athenia/Http/Core/Requests/User/ArticleNote/IndexRequest.php b/code/app/Athenia/Http/Core/Requests/User/ArticleNote/IndexRequest.php new file mode 100644 index 00000000..1ae7a145 --- /dev/null +++ b/code/app/Athenia/Http/Core/Requests/User/ArticleNote/IndexRequest.php @@ -0,0 +1,63 @@ +route('user'), + ]; + } + + /** + * All expands that are allowed for this request + * + * @return array + */ + public function allowedExpands(): array + { + return [ + 'user', + 'article', + ]; + } +} diff --git a/code/app/Athenia/Http/Core/Requests/User/ArticleNote/RandomArticleRequest.php b/code/app/Athenia/Http/Core/Requests/User/ArticleNote/RandomArticleRequest.php new file mode 100644 index 00000000..44120f3c --- /dev/null +++ b/code/app/Athenia/Http/Core/Requests/User/ArticleNote/RandomArticleRequest.php @@ -0,0 +1,51 @@ +route('user'), + ]; + } +} diff --git a/code/app/Athenia/Http/Core/Requests/User/ArticleNote/StoreRequest.php b/code/app/Athenia/Http/Core/Requests/User/ArticleNote/StoreRequest.php new file mode 100644 index 00000000..794db6b5 --- /dev/null +++ b/code/app/Athenia/Http/Core/Requests/User/ArticleNote/StoreRequest.php @@ -0,0 +1,59 @@ +route('user'), + ]; + } + + /** + * @param ArticleNote $model + * @return array + */ + public function rules(ArticleNote $model) + { + return $model->getValidationRules(ArticleNote::VALIDATION_RULES_CREATE); + } +} diff --git a/code/app/Athenia/Http/Core/Requests/User/ArticleNote/UpdateRequest.php b/code/app/Athenia/Http/Core/Requests/User/ArticleNote/UpdateRequest.php new file mode 100644 index 00000000..28790846 --- /dev/null +++ b/code/app/Athenia/Http/Core/Requests/User/ArticleNote/UpdateRequest.php @@ -0,0 +1,61 @@ +route('user'), + $this->route('article_note'), + ]; + } + + /** + * The rules for this request + * + * @param ArticleNote $model + */ + public function rules(ArticleNote $model) + { + return $model->getValidationRules(ArticleNote::VALIDATION_RULES_UPDATE); + } +} diff --git a/code/app/Athenia/Http/Core/Requests/User/ArticleNote/ViewRequest.php b/code/app/Athenia/Http/Core/Requests/User/ArticleNote/ViewRequest.php new file mode 100644 index 00000000..d0130120 --- /dev/null +++ b/code/app/Athenia/Http/Core/Requests/User/ArticleNote/ViewRequest.php @@ -0,0 +1,48 @@ +route('user'), + $this->route('article_note'), + ]; + } +} diff --git a/code/app/Athenia/Http/Core/Requests/Wiki/ArticleSummary/DeleteRequest.php b/code/app/Athenia/Http/Core/Requests/Wiki/ArticleSummary/DeleteRequest.php new file mode 100644 index 00000000..061934ae --- /dev/null +++ b/code/app/Athenia/Http/Core/Requests/Wiki/ArticleSummary/DeleteRequest.php @@ -0,0 +1,51 @@ +route('article'), + ]; + } +} diff --git a/code/app/Athenia/Http/Core/Requests/Wiki/ArticleSummary/StoreRequest.php b/code/app/Athenia/Http/Core/Requests/Wiki/ArticleSummary/StoreRequest.php new file mode 100644 index 00000000..05efe699 --- /dev/null +++ b/code/app/Athenia/Http/Core/Requests/Wiki/ArticleSummary/StoreRequest.php @@ -0,0 +1,59 @@ +route('article'), + ]; + } + + /** + * @param ArticleSummary $model + * @return array + */ + public function rules(ArticleSummary $model) + { + return $model->getValidationRules(ArticleSummary::VALIDATION_RULES_CREATE); + } +} diff --git a/code/app/Athenia/Http/Core/Requests/Wiki/ArticleSummary/UpdateRequest.php b/code/app/Athenia/Http/Core/Requests/Wiki/ArticleSummary/UpdateRequest.php new file mode 100644 index 00000000..01f34aff --- /dev/null +++ b/code/app/Athenia/Http/Core/Requests/Wiki/ArticleSummary/UpdateRequest.php @@ -0,0 +1,60 @@ +route('article'), + ]; + } + + /** + * The rules for this request + * + * @param ArticleSummary $model + */ + public function rules(ArticleSummary $model) + { + return $model->getValidationRules(ArticleSummary::VALIDATION_RULES_UPDATE); + } +} diff --git a/code/app/Athenia/Http/Core/Requests/Wiki/ArticleSummary/ViewRequest.php b/code/app/Athenia/Http/Core/Requests/Wiki/ArticleSummary/ViewRequest.php new file mode 100644 index 00000000..df216a9a --- /dev/null +++ b/code/app/Athenia/Http/Core/Requests/Wiki/ArticleSummary/ViewRequest.php @@ -0,0 +1,47 @@ +route('article'), + ]; + } +} diff --git a/code/app/Athenia/Listeners/User/InvitationAcceptedListener.php b/code/app/Athenia/Listeners/User/InvitationAcceptedListener.php new file mode 100644 index 00000000..cddf2efe --- /dev/null +++ b/code/app/Athenia/Listeners/User/InvitationAcceptedListener.php @@ -0,0 +1,51 @@ +invitationTokenRepository = $invitationTokenRepository; + } + + /** + * Handles the invitation accepted event by marking the token as used + * and adding the associated role to the user if present + * + * @param InvitationAcceptedEvent $event + */ + public function handle(InvitationAcceptedEvent $event): void + { + $user = $event->getUser(); + $invitationToken = $event->getInvitationToken(); + + // Mark the invitation token as used + $this->invitationTokenRepository->update($invitationToken, [ + 'used_at' => Carbon::now(), + ]); + + // If the invitation token has a role, add it to the user + if ($invitationToken->role_id) { + $user->roles()->attach($invitationToken->role_id); + } + } +} diff --git a/code/app/Athenia/Models/Asset.php b/code/app/Athenia/Models/Asset.php new file mode 100644 index 00000000..f2407bf0 --- /dev/null +++ b/code/app/Athenia/Models/Asset.php @@ -0,0 +1,141 @@ +|Asset getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Asset onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereAlt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereHeight($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereSource($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereWidth($value) + * @method static \Illuminate\Database\Eloquent\Builder|Asset withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Asset withoutTrashed() + * @mixin Eloquent + */ +class Asset extends BaseModelAbstract implements HasValidationRulesContract +{ + use HasValidationRules; + + /** + * @var string Makes sure to override all children + */ + protected $table = 'assets'; + + /** + * The user that created this asset + * + * @return BelongsTo + */ + public function owner() + { + return $this->morphTo(); + } + + /** + * All mime types that can be uploaded to the server for this asset + * + * @return array + */ + protected function getAvailableMimeTypes(): array + { + return [ + 'image/jpeg', + 'image/png', + 'image/gif', + ]; + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + self::VALIDATION_RULES_BASE => [ + 'file_contents' => [ + 'string', + ], + + 'name' => [ + 'nullable', + 'string', + ], + + 'caption' => [ + 'nullable', + 'string', + ], + + // Set in the request object, and not set from the user request + 'mime_type' => [ + Rule::in($this->getAvailableMimeTypes()), + ], + ], + self::VALIDATION_RULES_CREATE => [ + self::VALIDATION_PREPEND_REQUIRED => [ + 'file_contents', + ], + ], + self::VALIDATION_RULES_UPDATE => [ + self::VALIDATION_PREPEND_NOT_PRESENT => [ + 'file_contents', + ], + ] + ]; + } +} diff --git a/code/app/Athenia/Models/Category.php b/code/app/Athenia/Models/Category.php new file mode 100644 index 00000000..ce31c195 --- /dev/null +++ b/code/app/Athenia/Models/Category.php @@ -0,0 +1,94 @@ + $articles + * @property-read int|null $articles_count + * @mixin \Eloquent + */ +class Category extends BaseModelAbstract implements HasValidationRulesContract +{ + use HasValidationRules; + + /** + * All articles associated with this category + * + * @return BelongsToMany + */ + public function articles() : BelongsToMany + { + return $this->belongsToMany(Article::class, 'article_category') + ->withPivot('relevance') + ->withTimestamps(); + } + + /** + * @param mixed ...$params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'name' => [ + 'string', + ], + 'description' => [ + 'nullable', + 'string', + ], + ], + static::VALIDATION_RULES_CREATE => [ + static::VALIDATION_PREPEND_REQUIRED => [ + 'name', + ], + ], + ]; + } +} diff --git a/code/app/Athenia/Models/Collection/Collection.php b/code/app/Athenia/Models/Collection/Collection.php new file mode 100644 index 00000000..abb768c4 --- /dev/null +++ b/code/app/Athenia/Models/Collection/Collection.php @@ -0,0 +1,134 @@ + $collectionItems + * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $owner + * @property-read \Illuminate\Database\Eloquent\Collection $targetStatistics + * @method static \Database\Factories\Collection\CollectionFactory factory(...$parameters) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Collection onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereCollectionItemsCount($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereIsPublic($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereOwnerId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereOwnerType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Collection withTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|Collection withoutTrashed() + * @property-read int|null $target_statistics_count + * @mixin \Eloquent + */ +class Collection extends BaseModelAbstract implements HasValidationRulesContract, CanBeStatisticTargetContract +{ + use HasValidationRules, HasStatisticTargets; + + /** + * All collection items + * + * @return HasMany + */ + public function collectionItems(): HasMany + { + return $this->hasMany(CollectionItem::class) + ->orderBy('order'); + } + + /** + * The owner + * + * @return MorphTo + */ + public function owner(): MorphTo + { + return $this->morphTo(); + } + + /** + * @param ...$params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'name' => [ + 'nullable', + 'string', + ], + 'is_public' => [ + 'boolean', + ], + 'collection_item_order' => [ + 'array', + ], + 'collection_item_order.*' => [ + 'integer', + Rule::exists('collection_items', 'id'), + OwnedByValidator::KEY . ':collection,collectionItems', + ] + ], + static::VALIDATION_RULES_CREATE => [ + static::VALIDATION_PREPEND_REQUIRED => ['is_public'], + static::VALIDATION_PREPEND_NOT_PRESENT => ['collection_item_order'], + ], + ]; + } + + /** + * The name of the morph relation + * + * @return string + */ + public function morphRelationName(): string + { + return 'collection'; + } +} \ No newline at end of file diff --git a/code/app/Athenia/Models/Collection/CollectionItem.php b/code/app/Athenia/Models/Collection/CollectionItem.php new file mode 100644 index 00000000..38e3c645 --- /dev/null +++ b/code/app/Athenia/Models/Collection/CollectionItem.php @@ -0,0 +1,134 @@ + $categories + * @property-read int|null $categories_count + * @property-read \App\Models\Collection\Collection $collection + * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $item + * @method static \Database\Factories\Collection\CollectionItemFactory factory(...$parameters) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|CollectionItem onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereCollectionId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereItemId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereItemType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereOrder($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|CollectionItem withTrashed() + * @method static \Illuminate\Database\Eloquent\Builder|CollectionItem withoutTrashed() + * @mixin \Eloquent + */ +class CollectionItem extends BaseModelAbstract implements HasValidationRulesContract, CanBeAggregatedContract +{ + use HasValidationRules; + + /** + * The item this is related to + * + * @return MorphTo + */ + public function item(): MorphTo + { + return $this->morphTo(); + } + + /** + * All categories for this collection item + * + * @return BelongsToMany + */ + public function categories(): BelongsToMany + { + return $this->belongsToMany(Category::class, 'collection_item_categories'); + } + + /** + * The collection this item is apart of + * + * @return BelongsTo + */ + public function collection(): BelongsTo + { + return $this->belongsTo(Collection::class); + } + + /** + * @param ...$params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + self::VALIDATION_RULES_BASE => [ + 'item_id' => [ + 'integer', + ], + 'item_type' => [ + Rule::in(['article']), + ], + 'order' => [ + 'integer', + ], + ], + self::VALIDATION_RULES_CREATE => [ + self::VALIDATION_PREPEND_REQUIRED => ['item_id', 'item_type', 'order'], + ] + ]; + } + + /** + * Returns the relation paths to the models that can be target statistics + * For example: ["collection"] would mean this model affects statistics on collections + * through the collection relation + * + * @return string[] + */ + public function getStatisticTargetRelationPath(): array + { + return ['collection']; + } +} \ No newline at end of file diff --git a/code/app/Athenia/Models/Feature.php b/code/app/Athenia/Models/Feature.php new file mode 100644 index 00000000..8a6996c6 --- /dev/null +++ b/code/app/Athenia/Models/Feature.php @@ -0,0 +1,60 @@ +|Feature getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Feature onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|Feature withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Feature withoutTrashed() + * @mixin \Eloquent + */ +class Feature extends BaseModelAbstract +{ + /** + * @return BelongsToMany + */ + public function membershipPlans(): BelongsToMany + { + return $this->belongsToMany(MembershipPlan::class); + } +} diff --git a/code/app/Athenia/Models/Messaging/Message.php b/code/app/Athenia/Models/Messaging/Message.php new file mode 100644 index 00000000..4258c4a9 --- /dev/null +++ b/code/app/Athenia/Models/Messaging/Message.php @@ -0,0 +1,179 @@ +|Message getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Message onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereFromType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereToType($value) + * @method static \Illuminate\Database\Eloquent\Builder|Message withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Message withoutTrashed() + * @mixin Eloquent + */ +class Message extends BaseModelAbstract implements HasPolicyContract, HasValidationRulesContract +{ + use HasValidationRules; + + const VIA_EMAIL = 'email'; + const VIA_SLACK = 'slack'; + const VIA_SMS = 'sms'; + const VIA_PUSH_NOTIFICATION = 'push'; + + /** + * @var array + */ + protected $casts = [ + 'data' => 'array', + 'via' => 'array', + 'seen_at' => 'datetime', + 'sent_at' => 'datetime', + 'scheduled_at' => 'datetime', + ]; + + /** + * Array of events that need to be dispatched + * + * @var array + */ + protected $dispatchesEvents = [ + 'created' => MessageCreatedEvent::class + ]; + + /** + * Each message belongs to a user + * + * @return MorphTo + */ + public function from() : MorphTo + { + return $this->morphTo(); + } + + /** + * The thread that this message is in + * + * @return BelongsTo + */ + public function thread() : BelongsTo + { + return $this->belongsTo(Thread::class); + } + + /** + * Each message belongs to a user + * + * @return MorphTo + */ + public function to() : MorphTo + { + return $this->morphTo(); + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'message' => [ + 'string', + ], + 'seen' => [ + 'boolean', + ], + 'template' => [ + Rule::in([ + 'contact', + ]), + ], + 'data' => [ + 'array', + ], + ], + static::VALIDATION_RULES_UPDATE => [ + static::VALIDATION_PREPEND_NOT_PRESENT => [ + 'message', + ], + ], + ]; + } +} \ No newline at end of file diff --git a/code/app/Athenia/Models/Messaging/PushNotificationKey.php b/code/app/Athenia/Models/Messaging/PushNotificationKey.php new file mode 100644 index 00000000..b533a4b7 --- /dev/null +++ b/code/app/Athenia/Models/Messaging/PushNotificationKey.php @@ -0,0 +1,69 @@ +|PushNotificationKey getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|PushNotificationKey onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereOwnerId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereOwnerType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey wherePushNotificationKey($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|PushNotificationKey withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|PushNotificationKey withoutTrashed() + * @mixin \Eloquent + */ +class PushNotificationKey extends BaseModelAbstract +{ + /** + * @var string Table override due to laravel bug + */ + protected $table = 'push_notification_keys'; + + /** + * @return MorphTo + */ + public function owner(): MorphTo + { + return $this->morphTo('owner'); + } +} diff --git a/code/app/Athenia/Models/Messaging/Thread.php b/code/app/Athenia/Models/Messaging/Thread.php new file mode 100644 index 00000000..67728401 --- /dev/null +++ b/code/app/Athenia/Models/Messaging/Thread.php @@ -0,0 +1,147 @@ +|Thread getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Thread onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|Thread withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Thread withoutTrashed() + * @mixin Eloquent + */ +class Thread extends BaseModelAbstract implements HasPolicyContract, HasValidationRulesContract +{ + use HasValidationRules; + + /** + * The url of the profile image + * + * @var array + */ + protected $appends = [ + 'last_message', + ]; + + /** + * All messages in this thread + * + * @return HasMany + */ + public function messages() : HasMany + { + return $this->hasMany(Message::class)->orderBy('created_at', 'desc'); + } + + /** + * All users that are in this thread + * + * @return BelongsToMany + */ + public function users() : BelongsToMany + { + return $this->belongsToMany(User::class); + } + + /** + * Get the URL for the profile image + * + * @return null|string + */ + public function getLastMessageAttribute() + { + return $this->messages ? $this->messages->first() : null; + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'subject_type' => [ + 'bail', + 'string', + ], + 'subject_id' => [ + 'int', + ], + 'users' => [ + 'array', + ], + 'users.*' => [ + 'integer', + Rule::exists('users', 'id'), + ], + ], + static::VALIDATION_RULES_CREATE => [ + static::VALIDATION_PREPEND_REQUIRED => [ + 'subject_type', + ], + ], + static::VALIDATION_RULES_UPDATE => [ + static::VALIDATION_PREPEND_REQUIRED => [ + 'users', + ], + static::VALIDATION_PREPEND_NOT_PRESENT => [ + 'subject_type', + 'subject_id', + ], + ], + ]; + } +} diff --git a/code/app/Athenia/Models/Organization/Organization.php b/code/app/Athenia/Models/Organization/Organization.php new file mode 100644 index 00000000..67eaa4ed --- /dev/null +++ b/code/app/Athenia/Models/Organization/Organization.php @@ -0,0 +1,212 @@ + $collections + * @property-read int|null $collections_count + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Organization onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|Organization withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Organization withoutTrashed() + * @mixin \Eloquent + */ +class Organization extends BaseModelAbstract + implements HasValidationRulesContract, IsAnEntityContract, + HasMessageReceiversContract, CanReceiveMessageContract, CanReceiveSlackNotificationsContract +{ + use HasValidationRules, IsEntity; + + /** + * All assets this user has created + * + * @return MorphMany + */ + public function assets(): MorphMany + { + return $this->morphMany(Asset::class, 'owner'); + } + + /** + * All organization managers in this organization + * + * @return HasMany + */ + public function organizationManagers(): HasMany + { + return $this->hasMany(OrganizationManager::class); + } + + /** + * The asset that contains the profile image for this user + * + * @return BelongsTo + */ + public function profileImage() : BelongsTo + { + return $this->belongsTo(ProfileImage::class); + } + + /** + * Get the URL for the profile image + * + * @return null|string + */ + public function getProfileImageUrlAttribute() + { + return $this->profileImage ? $this->profileImage->url : null; + } + + /** + * @inheritDoc + */ + public function morphRelationName(): string + { + return 'organization'; + } + + /** + * @inheritDoc + */ + public function canUserManageEntity(User $user, int $role = Role::MANAGER): bool + { + return $user->canManageOrganization($this, $role); + } + + /** + * @param mixed ...$params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + self::VALIDATION_RULES_BASE => [ + 'name' => [ + 'string', + 'max:120', + ], + ], + self::VALIDATION_RULES_CREATE => [ + self::VALIDATION_PREPEND_REQUIRED => [ + 'name', + ], + ], + ]; + } + + /** + * This will return if the message can be received by the specific model + * + * @param Message $message + * @return bool + */ + public function canReceiveMessage(Message $message): bool + { + if (in_array(Message::VIA_SLACK, $message->via ?? [])) { + return $this->getSlackChannel($message) && $this->getSlackKey($message); + } + + return false; + } + + /** + * All message receivers contained within this model + * These related models will be used to send messages when the parent does not + * + * @param Message $message The message being sent in case there is only + * logic connected to returning receivers + * @return Collection + */ + public function messageReceivers(Message $message): Collection + { + return $this->organizationManagers + ->map(fn (OrganizationManager $i) => $i->user); + } + + /** + * Gets the key used to validate access to the related slack workspace + * + * @param Message $message + * @return string|null + */ + public function getSlackKey(Message $message): ?string + { + return $this->slack_key ?? null; + } + + /** + * Gets the slack channel name based on the message passed in + * + * @param Message $message + * @return string|null + */ + public function getSlackChannel(Message $message): ?string + { + return $this->slack_channel ?? null; + } +} \ No newline at end of file diff --git a/code/app/Athenia/Models/Organization/OrganizationManager.php b/code/app/Athenia/Models/Organization/OrganizationManager.php new file mode 100644 index 00000000..862b4624 --- /dev/null +++ b/code/app/Athenia/Models/Organization/OrganizationManager.php @@ -0,0 +1,117 @@ +|OrganizationManager getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|OrganizationManager onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|OrganizationManager withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|OrganizationManager withoutTrashed() + * @mixin Eloquent + */ +class OrganizationManager extends BaseModelAbstract implements HasValidationRulesContract, BelongsToOrganizationContract +{ + use HasValidationRules, BelongsToOrganization; + + /** + * The related organization + * + * @return BelongsTo + */ + public function role(): BelongsTo + { + return $this->belongsTo(Role::class); + } + + /** + * The related user + * + * @return BelongsTo + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'role_id' => [ + 'required', + 'integer', + Rule::in(Role::ENTITY_ROLES), + ], + 'email' => [ + 'string', + 'email', + ], + ], + static::VALIDATION_RULES_CREATE => [ + static::VALIDATION_PREPEND_REQUIRED => [ + 'email', + ], + ], + static::VALIDATION_RULES_UPDATE => [ + static::VALIDATION_PREPEND_NOT_PRESENT => [ + 'email', + ], + ], + ]; + } +} \ No newline at end of file diff --git a/code/app/Athenia/Models/Payment/LineItem.php b/code/app/Athenia/Models/Payment/LineItem.php new file mode 100644 index 00000000..f00072b3 --- /dev/null +++ b/code/app/Athenia/Models/Payment/LineItem.php @@ -0,0 +1,79 @@ +|LineItem getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|LineItem onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|LineItem withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|LineItem withoutTrashed() + * @mixin Eloquent + */ +class LineItem extends BaseModelAbstract +{ + /** + * The item that was purchased + * + * @return MorphTo + */ + public function item(): MorphTo + { + return $this->morphTo(); + } + + /** + * The payment this purchased item is related to + * + * @return BelongsTo + */ + public function payment(): BelongsTo + { + return $this->belongsTo(Payment::class); + } +} \ No newline at end of file diff --git a/code/app/Athenia/Models/Payment/Payment.php b/code/app/Athenia/Models/Payment/Payment.php new file mode 100644 index 00000000..bb9fb278 --- /dev/null +++ b/code/app/Athenia/Models/Payment/Payment.php @@ -0,0 +1,105 @@ +|Payment getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Payment onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|Payment withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Payment withoutTrashed() + * @mixin Eloquent + */ +class Payment extends BaseModelAbstract +{ + /** + * @var array All custom dates + */ + protected $casts = [ + 'refunded_at' => 'datetime:c', + 'deleted_at' => 'datetime:c', + ]; + + /** + * The items paid for + * + * @return HasMany + */ + public function lineItems(): HasMany + { + return $this->hasMany(LineItem::class); + } + + /** + * The owner of the payment + * + * @return MorphTo + */ + public function owner(): MorphTo + { + return $this->morphTo(); + } + + /** + * The payment method that this payment was made with + * + * @return BelongsTo + */ + public function paymentMethod(): BelongsTo + { + return $this->belongsTo(PaymentMethod::class); + } +} \ No newline at end of file diff --git a/code/app/Athenia/Models/Payment/PaymentMethod.php b/code/app/Athenia/Models/Payment/PaymentMethod.php new file mode 100644 index 00000000..86b7d548 --- /dev/null +++ b/code/app/Athenia/Models/Payment/PaymentMethod.php @@ -0,0 +1,192 @@ +|PaymentMethod getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|PaymentMethod onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|PaymentMethod withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|PaymentMethod withoutTrashed() + * @mixin Eloquent + */ +class PaymentMethod extends BaseModelAbstract implements HasValidationRulesContract +{ + use HasValidationRules; + + /** + * All payments that have been made with this payment method + * + * @return HasMany + */ + public function payments(): HasMany + { + return $this->hasMany(Payment::class); + } + + /** + * All subscriptions that renew with this payment method + * + * @return HasMany + */ + public function subscriptions(): HasMany + { + return $this->hasMany(Subscription::class); + } + + /** + * A payment method will have a morph to relation to the owner of the payment method + * + * @return MorphTo + */ + public function owner(): MorphTo + { + return $this->morphTo(); + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'token' => [ + 'string', + 'max:120', + ], + 'default' => [ + 'boolean', + ], + ], + static::VALIDATION_RULES_CREATE => [ + static::VALIDATION_PREPEND_REQUIRED => [ + 'token', + ], + ], + static::VALIDATION_RULES_UPDATE => [ + static::VALIDATION_PREPEND_NOT_PRESENT => [ + 'token', + ], + ], + ]; + } + + /** + * Swagger definition below... + * + * @SWG\Definition( + * type="object", + * definition="PaymentMethod", + * @SWG\Property( + * property="id", + * type="integer", + * format="int32", + * description="The primary id of the model", + * readOnly=true + * ), + * @SWG\Property( + * property="created_at", + * type="string", + * format="date-time", + * description="UTC date of the time this was created", + * readOnly=true + * ), + * @SWG\Property( + * property="updated_at", + * type="string", + * format="date-time", + * description="UTC date of the time this was last updated", + * readOnly=true + * ), + * @SWG\Property( + * property="payment_method_key", + * type="string", + * maxLength=120, + * description="The key for the payment method on the remote server", + * ), + * @SWG\Property( + * property="payment_method_type", + * type="string", + * maxLength=120, + * description="The type of payment method this is. This refers to the the payment service.", + * ), + * @SWG\Property( + * property="user_id", + * type="integer", + * format="int32", + * description="The primary id of the user that this payment method is related to", + * readOnly=true + * ), + * @SWG\Property( + * property="user", + * description="The users that this was sent to.", + * type="array", + * @SWG\Items(ref="#/definitions/User") + * ) + * ) + */ +} diff --git a/code/app/Athenia/Models/Resource.php b/code/app/Athenia/Models/Resource.php new file mode 100644 index 00000000..988b89f0 --- /dev/null +++ b/code/app/Athenia/Models/Resource.php @@ -0,0 +1,65 @@ +|Resource getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Resource onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|Resource withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Resource withoutTrashed() + * @mixin Eloquent + */ +class Resource extends BaseModelAbstract implements HasPolicyContract +{ + /** + * The database resource this is related to + * + * @return MorphTo + */ + public function resource() : MorphTo + { + return $this->morphTo(); + } +} diff --git a/code/app/Athenia/Models/Role.php b/code/app/Athenia/Models/Role.php new file mode 100644 index 00000000..d336f92e --- /dev/null +++ b/code/app/Athenia/Models/Role.php @@ -0,0 +1,131 @@ +|Role getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Role onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|Role withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Role withoutTrashed() + * @mixin Eloquent + */ +class Role extends BaseModelAbstract implements HasPolicyContract +{ + const APP_USER = 1; + const SUPER_ADMIN = 2; + const ARTICLE_VIEWER = 3; + const ARTICLE_EDITOR = 4; + const ADMINISTRATOR = 10; + const MANAGER = 11; + const CONTENT_EDITOR = 100; + const SUPPORT_STAFF = 101; + // Add more roles here start with 100 in order to avoid application collision + + /** + * @var array[string] the roles (usefully mainly for testing I suppose) + */ + const ROLES = [ + self::APP_USER, + self::SUPER_ADMIN, + self::ARTICLE_VIEWER, + self::ARTICLE_EDITOR, + self::CONTENT_EDITOR, + self::SUPPORT_STAFF, + // Add application specific roles here too + ]; + + /** + * All roles that are related to an organization + */ + const ENTITY_ROLES = [ + self::ADMINISTRATOR, + self::MANAGER, + // Add more organization roles here + ]; + + /** + * Has many users + * + * @return BelongsToMany + */ + public function users(): BelongsToMany + { + return $this->belongsToMany(User::class)->withTimestamps(); + } + + /** + * Swagger definition below... + * + * @SWG\Definition( + * type="object", + * definition="Role", + * @SWG\Property( + * property="id", + * type="integer", + * format="int32", + * description="The primary id of the model", + * readOnly=true + * ), + * @SWG\Property( + * property="created_at", + * type="string", + * format="date-time", + * description="UTC date of the time this was created", + * readOnly=true + * ), + * @SWG\Property( + * property="updated_at", + * type="string", + * format="date-time", + * description="UTC date of the time this was last updated", + * readOnly=true + * ), + * @SWG\Property( + * property="name", + * type="string", + * maxLength=32, + * description="The name of the role" + * ), + * ) + */ +} diff --git a/code/app/Athenia/Models/Statistic/Statistic.php b/code/app/Athenia/Models/Statistic/Statistic.php new file mode 100644 index 00000000..83cc6d3d --- /dev/null +++ b/code/app/Athenia/Models/Statistic/Statistic.php @@ -0,0 +1,155 @@ + $filters + * @property-read int|null $filters_count + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Statistic onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereModel($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic wherePublic($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereRelation($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Statistic withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Statistic withoutTrashed() + * @mixin Eloquent + */ +class Statistic extends BaseModelAbstract implements HasValidationRulesContract +{ + use HasValidationRules; + + /** + * The filters that we use to determine what to count + * + * @return HasMany + */ + public function filters(): HasMany + { + return $this->hasMany(StatisticFilter::class); + } + + /** + * Alias for backward compatibility + * + * @return HasMany + */ + public function statisticFilters(): HasMany + { + return $this->filters(); + } + + /** + * All instances of the target statistics in the system + * + * @return HasMany + */ + public function targetStatistics(): HasMany + { + return $this->hasMany(TargetStatistic::class); + } + + /** + * @inheritDoc + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'name' => [ + 'string', + ], + 'model' => [ + 'string', + ], + 'relation' => [ + 'string', + ], + 'public' => [ + 'boolean', + ], + 'statistic_filters' => [ + 'array', + ], + 'statistic_filters.*' => [ + 'array', + ], + 'statistic_filters.*.field' => [ + 'required', + 'string', + ], + 'statistic_filters.*.operator' => [ + 'required', + 'string', + ], + 'statistic_filters.*.value' => [ + 'nullable', + 'string', + ], + ], + static::VALIDATION_RULES_CREATE => [ + static::VALIDATION_PREPEND_REQUIRED => [ + 'name', + 'model', + 'relation', + ], + ], + static::VALIDATION_RULES_UPDATE => [ + static::VALIDATION_PREPEND_NOT_PRESENT => [ + 'model', + 'relation', + ], + ], + ]; + } +} \ No newline at end of file diff --git a/code/app/Athenia/Models/Statistic/StatisticFilter.php b/code/app/Athenia/Models/Statistic/StatisticFilter.php new file mode 100644 index 00000000..84a4b31c --- /dev/null +++ b/code/app/Athenia/Models/Statistic/StatisticFilter.php @@ -0,0 +1,66 @@ +|StatisticFilter getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|StatisticFilter onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereField($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereOperator($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereStatisticId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereValue($value) + * @method static \Illuminate\Database\Eloquent\Builder|StatisticFilter withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|StatisticFilter withoutTrashed() + * @mixin \Eloquent + */ +class StatisticFilter extends BaseModelAbstract +{ + /** + * The statistic that this filter belongs to + * + * @return BelongsTo + */ + public function statistic(): BelongsTo + { + return $this->belongsTo(Statistic::class); + } +} \ No newline at end of file diff --git a/code/app/Athenia/Models/Statistic/TargetStatistic.php b/code/app/Athenia/Models/Statistic/TargetStatistic.php new file mode 100644 index 00000000..240f3871 --- /dev/null +++ b/code/app/Athenia/Models/Statistic/TargetStatistic.php @@ -0,0 +1,92 @@ +|null $result + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|TargetStatistic onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereFilters($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereResult($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereStatisticId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereTargetId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereTargetType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereValue($value) + * @method static \Illuminate\Database\Eloquent\Builder|TargetStatistic withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|TargetStatistic withoutTrashed() + * @mixin \Eloquent + */ +class TargetStatistic extends BaseModelAbstract +{ + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'result' => 'array', + 'value' => 'float', + ]; + + /** + * The target model that this statistic belongs to + * + * @return MorphTo + */ + public function target(): MorphTo + { + return $this->morphTo(); + } + + /** + * The statistic that this belongs to + * + * @return BelongsTo + */ + public function statistic(): BelongsTo + { + return $this->belongsTo(Statistic::class); + } +} \ No newline at end of file diff --git a/code/app/Athenia/Models/Subscription/MembershipPlan.php b/code/app/Athenia/Models/Subscription/MembershipPlan.php new file mode 100644 index 00000000..6f00de36 --- /dev/null +++ b/code/app/Athenia/Models/Subscription/MembershipPlan.php @@ -0,0 +1,287 @@ +|MembershipPlan getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|MembershipPlan onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|MembershipPlan withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|MembershipPlan withoutTrashed() + * @mixin \Eloquent + */ +class MembershipPlan extends BaseModelAbstract implements HasPolicyContract, HasValidationRulesContract +{ + use HasValidationRules; + + /** + * @var string the enum value for the duration field when the plan only lasts a year + */ + const DURATION_YEAR = 'year'; + + /** + * @var string the enum value for the duration field when the plan only lasts a month + */ + const DURATION_MONTH = 'month'; + + /** + * @var string the enum value for the duration field when the plan lasts forever + */ + const DURATION_LIFETIME = 'lifetime'; + + /** + * The available duration types for a membership plan + */ + const AvailableDurations = [ + MembershipPlan::DURATION_MONTH, + MembershipPlan::DURATION_YEAR, + MembershipPlan::DURATION_LIFETIME, + ]; + + /** + * Values that are appending on a toArray function call + * + * @var array + */ + protected $appends = [ + 'current_cost', + 'current_rate_id', + ]; + + /** + * The current rate for this membership plan + * + * @return HasOne + */ + public function currentRate(): HasOne + { + return $this->hasOne(MembershipPlanRate::class) + ->where('active', true) + ->orderBy('created_at', 'DESC'); + } + + /** + * @return BelongsToMany + */ + public function features(): BelongsToMany + { + return $this->belongsToMany(Feature::class); + } + + /** + * All membership plan rates that have + * + * @return HasMany + */ + public function membershipPlanRates(): HasMany + { + return $this->hasMany(MembershipPlanRate::class); + } + + /** + * Function that creates the current cost attribute + * + * @return null|float + */ + public function getCurrentCostAttribute() + { + return $this->currentRate ? $this->currentRate->cost : null; + } + + /** + * Function that creates the current cost attribute + * + * @return null|float + */ + public function getCurrentRateIdAttribute() + { + return $this->currentRate ? $this->currentRate->id : null; + } + + /** + * Build the model validation rules + * @param array $params Any additional parameters needed + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + self::VALIDATION_RULES_BASE => [ + + 'name' => [ + 'string', + 'max:120', + ], + + 'entity_type' => [ + 'string', + Rule::in([ + 'user', + 'organization', + ]), + ], + + 'description' => [ + 'string', + ], + + 'current_cost' => [ + 'numeric', + 'min:0.00', + 'max:999999.99', + ], + + 'duration' => [ + 'string', + Rule::in(MembershipPlan::AvailableDurations), + ], + + 'trial_period' => [ + 'nullable', + 'integer', + 'min:0', + ], + + 'default' => [ + 'boolean', + ], + + 'features' => [ + 'array', + ], + + 'features.*' => [ + 'numeric', + Rule::exists('features', 'id'), + ], + ], + self::VALIDATION_RULES_CREATE => [ + self::VALIDATION_PREPEND_REQUIRED => [ + 'name', + 'entity_type', + 'current_cost', + 'duration', + ], + ], + self::VALIDATION_RULES_UPDATE => [ + self::VALIDATION_PREPEND_NOT_PRESENT => [ + 'entity_type', + 'duration', + ], + ], + ]; + } + + /** + * Swagger definition below + * + * @SWG\Definition ( + * type="object", + * definition="MembershipPlan", + * description="The details of a membership plan", + * @SWG\Property( + * property="id", + * type="integer", + * format="int32", + * readOnly=true + * ), + * @SWG\Property( + * property="created_at", + * type="string", + * format="date-time", + * description="UTC date of the time this was created", + * readOnly=true + * ), + * @SWG\Property( + * property="updated_at", + * type="string", + * format="date-time", + * description="UTC date of the time this was updated", + * readOnly=true + * ), + * @SWG\Property( + * property="current_cost", + * type="number", + * description="The current cost of the membership plan" + * ), + * @SWG\Property( + * property="current_rate_id", + * type="number", + * readonly=true, + * description="The current id of the membership plan rate" + * ), + * @SWG\Property( + * property="duration", + * type="string", + * maxLength=128, + * description="The duration for this membership plan" + * ), + * @SWG\Property( + * property="subscriptions", + * description="The subscriptions attatched to this membership plan", + * type="array", + * @SWG\Items(ref="#/definitions/Subscription") + * ), + * ) + */ +} diff --git a/code/app/Athenia/Models/Subscription/MembershipPlanRate.php b/code/app/Athenia/Models/Subscription/MembershipPlanRate.php new file mode 100644 index 00000000..6a530596 --- /dev/null +++ b/code/app/Athenia/Models/Subscription/MembershipPlanRate.php @@ -0,0 +1,79 @@ +|MembershipPlanRate getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|MembershipPlanRate onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|MembershipPlanRate withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|MembershipPlanRate withoutTrashed() + * @mixin Eloquent + */ +class MembershipPlanRate extends BaseModelAbstract +{ + /** + * The membership plan this is related to + * + * @return BelongsTo + */ + public function membershipPlan(): BelongsTo + { + return $this->belongsTo(MembershipPlan::class); + } + + /** + * All subscriptions that have been signed up for this membership plan rate + * + * @return HasMany + */ + public function subscriptions(): HasMany + { + return $this->hasMany(Subscription::class); + } +} \ No newline at end of file diff --git a/code/app/Athenia/Models/Subscription/Subscription.php b/code/app/Athenia/Models/Subscription/Subscription.php new file mode 100644 index 00000000..42cce552 --- /dev/null +++ b/code/app/Athenia/Models/Subscription/Subscription.php @@ -0,0 +1,219 @@ +|Subscription getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Subscription onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|Subscription withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Subscription withoutTrashed() + * @mixin Eloquent + */ +class Subscription extends BaseModelAbstract implements HasValidationRulesContract, HasPaymentsContract +{ + use HasValidationRules, HasPayments; + + /** + * The attributes that should be cast to native types. + * + * @var array + */ + protected $casts = [ + 'last_renewed_at' => 'datetime:c', + 'subscribed_at' => 'datetime:c', + 'expires_at' => 'datetime:c', + 'canceled_at' => 'datetime:c', + ]; + + /** + * The membership plan rate this subscription is signed up for + * + * @return BelongsTo + */ + public function membershipPlanRate(): BelongsTo + { + return $this->belongsTo(MembershipPlanRate::class); + } + + /** + * The payment method that is used to renew this subscription + * + * @return BelongsTo + */ + public function paymentMethod(): BelongsTo + { + return $this->belongsTo(PaymentMethod::class); + } + + /** + * The subscriber this subscription is for + * + * @return MorphTo + */ + public function subscriber(): MorphTo + { + return $this->morphTo(); + } + + /** + * @inheritDoc + */ + public function morphRelationName(): string + { + return 'subscription'; + } + + /** + * Determines whether or not this subscription is good for a lifetime + * + * @return bool + */ + public function isLifetime() : bool + { + return $this->membershipPlanRate->membershipPlan->duration == MembershipPlan::DURATION_LIFETIME; + } + + /** + * Formats the expires at date string + * + * @return null|string + */ + public function getFormattedExpiresAtAttribute() + { + return $this->expires_at ? $this->expires_at->format('F jS Y') : null; + } + + /** + * Formats the cost to be human readable + * + * @return null|string + */ + public function getFormattedCostAttribute() + { + return $this->membershipPlanRate && $this->membershipPlanRate->cost ? + number_format((float)$this->membershipPlanRate->cost, 2) : null; + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + self::VALIDATION_RULES_BASE => [ + 'cancel' => [ + 'boolean', + ], + 'membership_plan_rate_id' => [ + 'integer', + Rule::exists('membership_plan_rates', 'id'), + MembershipPlanRateIsActiveValidator::KEY, + ], + 'payment_method_id' => [ + 'integer', + Rule::exists('payment_methods', 'id'), + PaymentMethodIsOwnedByEntityValidator::KEY, + ], + 'is_trial' => [ + 'boolean', + ], + 'recurring' => [ + 'boolean', + ], + ], + self::VALIDATION_RULES_CREATE => [ + self::VALIDATION_PREPEND_REQUIRED_UNLESS . 'is_trial,true' => [ + 'payment_method_id', + ], + self::VALIDATION_PREPEND_REQUIRED => [ + 'membership_plan_rate_id', + ], + self::VALIDATION_PREPEND_NOT_PRESENT => [ + 'cancel', + ], + ], + self::VALIDATION_RULES_UPDATE => [ + self::VALIDATION_PREPEND_NOT_PRESENT => [ + 'membership_plan_rate_id', + 'is_trial', + ], + ], + ]; + } +} diff --git a/code/app/Athenia/Models/User/ArticleNote.php b/code/app/Athenia/Models/User/ArticleNote.php new file mode 100644 index 00000000..b3e3447a --- /dev/null +++ b/code/app/Athenia/Models/User/ArticleNote.php @@ -0,0 +1,131 @@ +|ArticleNote getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ArticleNote onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereArticleId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereCompletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereResponse($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleNote withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleNote withoutTrashed() + * @mixin Eloquent + */ +class ArticleNote extends BaseModelAbstract implements HasValidationRulesContract, CanBeAggregatedContract +{ + use HasValidationRules, SoftDeletes; + + /** + * The user who created this note + * + * @return BelongsTo + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + /** + * The article this note is for + * + * @return BelongsTo + */ + public function article(): BelongsTo + { + return $this->belongsTo(Article::class); + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'article_id' => [ + 'integer', + Rule::exists('articles', 'id'), + ], + 'completed' => [ + 'boolean', + ], + 'response' => [ + 'nullable', + 'string', + ], + ], + static::VALIDATION_RULES_UPDATE => [ + static::VALIDATION_PREPEND_REQUIRED => [ + ], + ], + static::VALIDATION_RULES_CREATE => [ + static::VALIDATION_PREPEND_REQUIRED => [ + 'article_id', + ], + ], + ]; + } + + /** + * Returns the relation paths to the models that can be target statistics + * For example: ["article"] would mean this model affects statistics on articles + * through the article relation + * + * @return string[] + */ + public function getStatisticTargetRelationPath(): array + { + return ['article']; + } +} diff --git a/code/app/Athenia/Models/User/Contact.php b/code/app/Athenia/Models/User/Contact.php new file mode 100644 index 00000000..7de2ef33 --- /dev/null +++ b/code/app/Athenia/Models/User/Contact.php @@ -0,0 +1,127 @@ +|Contact getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Contact onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|Contact withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Contact withoutTrashed() + * @mixin Eloquent + */ +class Contact extends BaseModelAbstract implements HasValidationRulesContract +{ + use HasValidationRules; + + /** + * @var array + */ + protected $casts = [ + 'confirmed_at' => 'datetime', + 'denied_at' => 'datetime', + ]; + + /** + * @return BelongsTo + */ + public function initiatedBy() : BelongsTo + { + return $this->belongsTo(User::class, 'initiated_by_id'); + } + + /** + * @return BelongsTo + */ + public function requested() : BelongsTo + { + return $this->belongsTo(User::class, 'requested_id'); + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'initiated_by_id' => [ + 'not_present', + ], + 'requested_id' => [ + 'integer', + Rule::exists('users', 'id'), + ], + 'deny' => [ + 'boolean', + ], + 'confirm' => [ + 'boolean', + ], + ], + static::VALIDATION_RULES_CREATE => [ + static::VALIDATION_PREPEND_REQUIRED => [ + 'requested_id', + ], + static::VALIDATION_PREPEND_NOT_PRESENT => [ + 'deny', + 'confirm', + ], + ], + static::VALIDATION_RULES_UPDATE => [ + static::VALIDATION_PREPEND_NOT_PRESENT => [ + 'requested_id', + ], + ], + ]; + } +} \ No newline at end of file diff --git a/code/app/Athenia/Models/User/InvitationToken.php b/code/app/Athenia/Models/User/InvitationToken.php new file mode 100644 index 00000000..f81cc8d1 --- /dev/null +++ b/code/app/Athenia/Models/User/InvitationToken.php @@ -0,0 +1,100 @@ +|InvitationToken getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|InvitationToken onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|InvitationToken withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|InvitationToken withoutTrashed() + * @mixin \Eloquent + */ +class InvitationToken extends BaseModelAbstract +{ + /** + * The role relation for this invitation token + * + * @return BelongsTo + */ + public function role(): BelongsTo + { + return $this->belongsTo(Role::class); + } + + /** + * Check if this invitation token has been used + * + * @return bool + */ + public function isUsed(): bool + { + return $this->used_at !== null; + } + + /** + * Swagger definition below for an invitation token... + * + * @SWG\Definition( + * type="object", + * definition="InvitationToken", + * @SWG\Property( + * property="token", + * type="string", + * maxLength=40, + * description="The invitation token that was generated." + * ), + * @SWG\Property( + * property="role_id", + * type="integer", + * format="int32", + * description="The role ID that will be assigned when this invitation is accepted." + * ), + * @SWG\Property( + * property="used_at", + * type="string", + * format="date-time", + * description="UTC date of when this invitation was used." + * ), + * ) + */ +} diff --git a/code/app/Athenia/Models/User/PasswordToken.php b/code/app/Athenia/Models/User/PasswordToken.php new file mode 100644 index 00000000..6392c039 --- /dev/null +++ b/code/app/Athenia/Models/User/PasswordToken.php @@ -0,0 +1,82 @@ +|PasswordToken getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|PasswordToken onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|PasswordToken withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|PasswordToken withoutTrashed() + * @mixin \Eloquent + */ +class PasswordToken extends BaseModelAbstract +{ + /** + * The user relation to the user that generated this token + * + * @return BelongsTo + */ + public function user() : BelongsTo + { + return $this->belongsTo(User::class); + } + + /** + * Swagger definition below for a password token... + * + * @SWG\Definition( + * type="object", + * definition="PasswordToken", + * @SWG\Property( + * property="token", + * type="string", + * maxLength=120, + * description="The token that was generated." + * ), + * @SWG\Property( + * property="email", + * type="string", + * maxLength=120, + * description="The email address of the user the token is associated with." + * ), + * ) + */ +} \ No newline at end of file diff --git a/code/app/Athenia/Models/User/ProfileImage.php b/code/app/Athenia/Models/User/ProfileImage.php new file mode 100644 index 00000000..788735a7 --- /dev/null +++ b/code/app/Athenia/Models/User/ProfileImage.php @@ -0,0 +1,86 @@ +|ProfileImage getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|ProfileImage onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereAlt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereHeight($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereSource($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereWidth($value) + * @method static \Illuminate\Database\Eloquent\Builder|ProfileImage withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|ProfileImage withoutTrashed() + * @mixin Eloquent + */ +class ProfileImage extends Asset +{ + /** + * @return HasOne + */ + public function organization(): HasOne + { + return $this->hasOne(Organization::class); + } + + /** + * @return HasOne + */ + public function user(): HasOne + { + return $this->hasOne(User::class); + } +} diff --git a/code/app/Athenia/Models/User/User.php b/code/app/Athenia/Models/User/User.php new file mode 100644 index 00000000..961cdc91 --- /dev/null +++ b/code/app/Athenia/Models/User/User.php @@ -0,0 +1,492 @@ + $articleNotes + * @property-read int|null $article_notes_count + * @property-read \Illuminate\Database\Eloquent\Collection $collections + * @property-read int|null $collections_count + * @property-read \Illuminate\Database\Eloquent\Collection $pushNotificationKeys + * @property-read int|null $push_notification_keys_count + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|User onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|User withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|User withoutTrashed() + * @mixin \Eloquent + */ +class User extends BaseModelAbstract + implements AuthenticatableContract, JWTSubject, + HasPolicyContract, HasValidationRulesContract, + CanBeIndexedContract, IsAnEntityContract, CanReceiveMessageContract, + CanReceiveEmailsContract, CanReceivePushNotificationContract, CanReceiveSMSContract +{ + use Authenticatable, HasValidationRules, IsEntity, CanBeIndexed; + + /** + * The attributes excluded from the model's JSON form. + * + * @var array + */ + protected $hidden = [ + 'deleted_at', + 'password', + ]; + + /** + * The url of the profile image + * + * @var array + */ + protected $appends = [ + 'profile_image_url', + ]; + + /** + * All assets this user has created + * + * @return MorphMany + */ + public function assets(): MorphMany + { + return $this->morphMany(Asset::class, 'owner'); + } + + /** + * The ballot completions the user has done + * + * @return HasMany + */ + public function ballotCompletions(): HasMany + { + return $this->hasMany(BallotCompletion::class); + } + + /** + * The article notes this user has created + * + * @return HasMany + */ + public function articleNotes(): HasMany + { + return $this->hasMany(ArticleNote::class); + } + + /** + * The articles that were created by this user + * + * @return HasMany + */ + public function createdArticles(): HasMany + { + return $this->hasMany(Article::class, 'created_by_id'); + } + + /** + * The iterations that were created by this user + * + * @return HasMany + */ + public function createdIterations(): HasMany + { + return $this->hasMany(ArticleIteration::class, 'created_by_id'); + } + + /** + * The messages that were sent to a user + * + * @return HasMany + */ + public function messages(): HasMany + { + return $this->hasMany(Message::class, 'to_id'); + } + + /** + * All organization manager relations this user has + * + * @return HasMany + */ + public function organizationManagers(): HasMany + { + return $this->hasMany(OrganizationManager::class); + } + + /** + * The push notification keys that the push notification should be sent to + * + * @return HasMany + */ + public function pushNotificationKeys(): HasMany + { + return $this->hasMany(PushNotificationKey::class); + } + + /** + * The asset that contains the profile image for this user + * + * @return BelongsTo + */ + public function profileImage() : BelongsTo + { + return $this->belongsTo(ProfileImage::class); + } + + /** + * The resource object for this user + * + * @return MorphOne + */ + public function resource() : MorphOne + { + return $this->morphOne(Resource::class, 'resource'); + } + + /** + * What roles this user has + * + * @return BelongsToMany + */ + public function roles(): BelongsToMany + { + return $this->belongsToMany(Role::class); + } + + /** + * Any threads this user is apart of + * + * @return BelongsToMany + */ + public function threads(): BelongsToMany + { + return $this->belongsToMany(Thread::class); + } + + /** + * Add a Role to this user + * + * @param int $roleId + * @return $this + */ + public function addRole(int $roleId) + { + $this->roles()->attach($roleId); + return $this; + } + + /** + * Does this have the role + * + * @param mixed $roles + * @return bool + */ + public function hasRole($roles) + { + $roles = (array)$roles; + return $this->roles()->whereIn('id', $roles)->exists(); + } + + /** + * Add a Role to this user + * + * @param int $roleId + * @return $this + */ + public function removeRole(int $roleId) + { + $this->roles()->detach($roleId); + return $this; + } + + /** + * Determines whether or not the user can manage the organization. + * + * @param Organization $organization + * @param int|array $role + * If the manager role is passed in then this will return true for both the manager role and admin role. + * The admin role will only check for the admin role. + * @return bool + */ + public function canManageOrganization(Organization $organization, $role = Role::MANAGER): bool + { + $roles = is_array($role) ? $role : [$role]; + if (!in_array(Role::ADMINISTRATOR, $roles)) { + $roles[] = Role::ADMINISTRATOR; + } + return $this->organizationManagers->first(fn (OrganizationManager $organizationManager) => + in_array($organizationManager->role_id, $roles) && $organizationManager->organization_id === $organization->id + ) != null; + } + + /** + * Get the URL for the profile image + * + * @return null|string + */ + public function getProfileImageUrlAttribute(): string|null + { + return $this->profileImage?->url; + } + + /** + * @inheritDoc + */ + public function canUserManageEntity(User $user, int $role = null): bool + { + return $this->id == $user->id; + } + + /** + * This will return if the message can be received by the specific model + * + * @param Message $message + * @return bool + */ + public function canReceiveMessage(Message $message): bool + { + foreach ($message->via ?? [] as $via) { + switch ($via) { + case Message::VIA_EMAIL: + return true; + case Message::VIA_PUSH_NOTIFICATION: + return !!$this->pushNotificationKeys->count(); + case Message::VIA_SMS: + return !!$this->getPhoneNumber(); + } + } + + return false; + } + + /** + * The email address to send the email to + * + * @return string + */ + public function getEmailAddress(): string + { + return $this->email; + } + + /** + * The name of the person to be added as the to field + * + * @return string + */ + public function getEmailToName(): string + { + return $this->first_name . ' ' . $this->last_name; + } + + /** + * Gets the phone number for routing SMS messages + * + * @return string|null + */ + public function getPhoneNumber(): ?string + { + return $this->phone ?? null; + } + + /** + * The name of the morph relation + * + * @return string + */ + public function morphRelationName(): string + { + return 'user'; + } + + /** + * Gets the content string to index + * + * @return string + */ + public function getContentString(): ?string + { + return $this->first_name . ' ' . $this->last_name; + } + + /** + * Get the identifier that will be stored in the subject claim of the JWT. + * + * @return mixed + */ + public function getJWTIdentifier() + { + return $this->id; + } + + /** + * Return a key value array, containing any custom claims to be added to the JWT. + * + * @return array + */ + public function getJWTCustomClaims() + { + return []; + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + $emailUnique = Rule::unique('users', 'email'); + + $userId = count($params) ? $params[0]->id : null; + + if ($userId) { + $emailUnique->ignore($userId); + } + + return [ + static::VALIDATION_RULES_BASE => [ + 'email' => [ + 'string', + 'max:120', + 'email', + $emailUnique, + ], + 'first_name' => [ + 'string', + 'max:120', + ], + 'last_name' => [ + 'string', + 'max:120', + ], + 'password' => [ + 'string', + 'min:6', + ], + 'push_notification_key' => [ + 'string', + 'max:512' + ], + 'about_me' => [ + 'string', + ], + 'allow_users_to_add_me' => [ + 'boolean', + ], + 'receive_push_notifications' => [ + 'boolean', + ], + ], + ]; + } +} diff --git a/code/app/Athenia/Models/Vote/Ballot.php b/code/app/Athenia/Models/Vote/Ballot.php new file mode 100644 index 00000000..363fdff6 --- /dev/null +++ b/code/app/Athenia/Models/Vote/Ballot.php @@ -0,0 +1,90 @@ +|Ballot getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Ballot onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|Ballot withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Ballot withoutTrashed() + * @mixin Eloquent + */ +class Ballot extends BaseModelAbstract +{ + /** + * This ballot type when there is a single subject and the user chooses yes or no + */ + const TYPE_SINGLE_OPTION = 'single_option'; + + /** + * The ballot type for when there will be multiple options within each item + */ + const TYPE_MULTIPLE_OPTIONS = 'multiple_options'; + + /** + * The ballot type for when we allow the user to rank their options + */ + const TYPE_RANKED_CHOICE = 'ranked_choice'; + + /** + * All times someone has completed this ballot + * + * @return HasMany + */ + public function ballotCompletions(): HasMany + { + return $this->hasMany(BallotCompletion::class); + } + + /** + * All subjects contained in this ballot + * + * @return HasMany + */ + public function ballotItems(): HasMany + { + return $this->hasMany(BallotItem::class); + } +} diff --git a/code/app/Athenia/Models/Vote/BallotCompletion.php b/code/app/Athenia/Models/Vote/BallotCompletion.php new file mode 100644 index 00000000..364c6004 --- /dev/null +++ b/code/app/Athenia/Models/Vote/BallotCompletion.php @@ -0,0 +1,125 @@ +|BallotCompletion getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|BallotCompletion onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereCompletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereResponse($value) + * @method static \Illuminate\Database\Eloquent\Builder|BallotCompletion withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|BallotCompletion withoutTrashed() + * @mixin Eloquent + */ +class BallotCompletion extends BaseModelAbstract implements HasValidationRulesContract +{ + use HasValidationRules; + + /** + * The ballot that was completed + * + * @return BelongsTo + */ + public function ballot(): BelongsTo + { + return $this->belongsTo(Ballot::class); + } + + /** + * THe user that completed this ballot + * + * @return BelongsTo + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + /** + * All votes that were cat in this ballot + * + * @return HasMany + */ + public function votes(): HasMany + { + return $this->hasMany(Vote::class); + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'votes' => [ + 'required', + 'array', + ], + 'votes.*' => [ + 'array', + ], + 'votes.*.result' => [ + 'required', + 'integer', + ], + 'votes.*.ballot_item_option_id' => [ + 'required', + 'integer', + Rule::exists('ballot_item_options', 'id'), + ], + ] + ]; + } +} diff --git a/code/app/Athenia/Models/Vote/BallotItem.php b/code/app/Athenia/Models/Vote/BallotItem.php new file mode 100644 index 00000000..78dc7ef5 --- /dev/null +++ b/code/app/Athenia/Models/Vote/BallotItem.php @@ -0,0 +1,80 @@ +|BallotItem getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|BallotItem onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|BallotItem withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|BallotItem withoutTrashed() + * @mixin Eloquent + */ +class BallotItem extends BaseModelAbstract +{ + /** + * The ballot this is apart of + * + * @return BelongsTo + */ + public function ballot(): BelongsTo + { + return $this->belongsTo(Ballot::class); + } + + /** + * The subject that this represents + * + * @return MorphTo + */ + public function ballotItemOptions(): HasMany + { + return $this->hasMany(BallotItemOption::class); + } +} diff --git a/code/app/Athenia/Models/Vote/BallotItemOption.php b/code/app/Athenia/Models/Vote/BallotItemOption.php new file mode 100644 index 00000000..ea2d0581 --- /dev/null +++ b/code/app/Athenia/Models/Vote/BallotItemOption.php @@ -0,0 +1,87 @@ +|BallotItemOption getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|BallotItemOption onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|BallotItemOption withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|BallotItemOption withoutTrashed() + * @mixin \Eloquent + */ +class BallotItemOption extends BaseModelAbstract +{ + /** + * @return BelongsTo + */ + public function ballotItem() + { + return $this->belongsTo(BallotItem::class); + } + + /** + * The subject that this represents + * + * @return MorphTo + */ + public function subject(): MorphTo + { + return $this->morphTo(); + } + + /** + * The votes that have been submitted where this option was selected + * + * @return HasMany + */ + public function votes(): HasMany + { + return $this->hasMany(Vote::class); + } +} diff --git a/code/app/Athenia/Models/Vote/Vote.php b/code/app/Athenia/Models/Vote/Vote.php new file mode 100644 index 00000000..5cd5692e --- /dev/null +++ b/code/app/Athenia/Models/Vote/Vote.php @@ -0,0 +1,81 @@ +|Vote getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Vote onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|Vote withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Vote withoutTrashed() + * @mixin \Eloquent + */ +class Vote extends BaseModelAbstract +{ + /** + * @var array + */ + protected $dispatchesEvents = [ + 'created' => VoteCreatedEvent::class, + ]; + + /** + * The ballot completion that this is part of + * + * @return BelongsTo + */ + public function ballotCompletion(): BelongsTo + { + return $this->belongsTo(BallotCompletion::class); + } + + /** + * The subject that was voted for + * + * @return BelongsTo + */ + public function ballotItemOption(): BelongsTo + { + return $this->belongsTo(BallotItemOption::class); + } +} diff --git a/code/app/Athenia/Models/Wiki/Article.php b/code/app/Athenia/Models/Wiki/Article.php new file mode 100644 index 00000000..2a2649c3 --- /dev/null +++ b/code/app/Athenia/Models/Wiki/Article.php @@ -0,0 +1,271 @@ + $articleNotes + * @property-read int|null $article_notes_count + * @property-read \Illuminate\Database\Eloquent\Collection $categories + * @property-read int|null $categories_count + * @property-read \Illuminate\Database\Eloquent\Collection $modifications + * @property-read int|null $modifications_count + * @property-read \App\Models\Resource|null $resource + * @property-read \Illuminate\Database\Eloquent\Collection $targetStatistics + * @property-read int|null $target_statistics_count + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|Article onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereAuthors($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereHasFullModificationHistory($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|Article withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Article withoutTrashed() + * @mixin Eloquent + */ +class Article extends BaseModelAbstract implements HasPolicyContract, HasValidationRulesContract, CanBeIndexedContract, CanBeStatisticTargetContract +{ + use HasValidationRules, CanBeIndexed, HasStatisticTargets; + + /** + * Values that are appending on a toArray function call + * + * @var array + */ + protected $appends = [ + 'content', + 'last_iteration_content', + ]; + + /** + * The user that originally created this article + * + * @return BelongsTo + */ + public function createdBy() : BelongsTo + { + return $this->belongsTo(User::class, 'created_by_id'); + } + + /** + * All of the iterations + * + * @return HasMany + */ + public function iterations() : HasMany + { + return $this->hasMany(ArticleIteration::class) + ->orderByDesc('created_at')->orderByDesc('id'); + } + + /** + * All modifications for this article + * + * @return HasMany + */ + public function modifications() : HasMany + { + return $this->hasMany(ArticleModification::class) + ->orderByDesc('created_at')->orderByDesc('id'); + } + + /** + * All versions related to this article + * + * @return HasMany + */ + public function versions() : HasMany + { + return $this->hasMany(ArticleVersion::class) + ->orderByDesc('created_at')->orderByDesc('id'); + } + + /** + * All categories associated with this article + * + * @return BelongsToMany + */ + public function categories() : BelongsToMany + { + return $this->belongsToMany(Category::class, 'article_category') + ->withPivot('relevance') + ->withTimestamps(); + } + + /** + * All notes associated with this article + * + * @return HasMany + */ + public function articleNotes() : HasMany + { + return $this->hasMany(\App\Models\User\ArticleNote::class); + } + + /** + * The summary for this article + * + * @return HasOne + */ + public function articleSummary() : HasOne + { + return $this->hasOne(ArticleSummary::class); + } + + /** + * Gets the content of the article + * + * @return null|string + */ + public function getContentAttribute() : ?string + { + return $this->current_version?->articleIteration?->content; + } + + /** + * Gets the content of the article + * + * @return null|ArticleVersion + */ + public function getCurrentVersionAttribute() : ?ArticleVersion + { + return $this->versions()->limit(1)->get()->first(); + } + + /** + * Gets the content of the article + * + * @return null|string + */ + public function getLastIterationContentAttribute() : ?string + { + if (isset($this->attributes['last_iteration_content'])) { + return $this->attributes['last_iteration_content']; + } + /** @var ArticleIteration|null $iteration */ + $iteration = $this->iterations()->limit(1)->get()->first(); + return $iteration ? $iteration->content : null; + } + + /** + * @return string + */ + public function morphRelationName(): string + { + return 'article'; + } + + /** + * Gets the content that will be indexed for this resource + * + * @return string|null + */ + public function getContentString(): ?string + { + return $this->title . ' ' . ($this->content ?? ''); + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'title' => [ + 'string', + 'max:120', + ], + 'url' => [ + 'nullable', + 'string', + 'url', + ], + 'authors' => [ + 'nullable', + 'string', + ], + 'categories' => [ + 'array', + ], + 'categories.*.category_id' => [ + 'integer', + 'exists:categories,id', + ], + 'categories.*.relevance' => [ + 'numeric', + ], + ], + static::VALIDATION_RULES_CREATE => [ + static::VALIDATION_PREPEND_REQUIRED => [ + 'title', + ], + ], + ]; + } +} diff --git a/code/app/Athenia/Models/Wiki/ArticleIteration.php b/code/app/Athenia/Models/Wiki/ArticleIteration.php new file mode 100644 index 00000000..3e399eb1 --- /dev/null +++ b/code/app/Athenia/Models/Wiki/ArticleIteration.php @@ -0,0 +1,181 @@ +|ArticleIteration getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration joinRelations($relations, $leftJoin = null) + * @method static Builder|ArticleIteration onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereArticleModificationId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereNotInJoin($column, $values, $boolean = 'and') + * @method static Builder|ArticleIteration withTrashed(bool $withTrashed = true) + * @method static Builder|ArticleIteration withoutTrashed() + * @mixin Eloquent + */ +class ArticleIteration extends BaseModelAbstract implements HasPolicyContract +{ + /** + * The article that this iteration is for + * + * @return BelongsTo + */ + public function article() : BelongsTo + { + return $this->belongsTo(Article::class); + } + + /** + * The user that originally created this article + * + * @return BelongsTo + */ + public function createdBy() : BelongsTo + { + return $this->belongsTo(User::class, 'created_by_id'); + } + + /** + * Any modification that is tagged that generated this iteration + * + * @return BelongsTo + */ + public function modification(): BelongsTo + { + return $this->belongsTo(ArticleModification::class, 'article_modification_id'); + } + + /** + * The version of this article if there is one + * + * @return HasOne + */ + public function version(): HasOne + { + return $this->hasOne(ArticleVersion::class); + } + + /** + * Makes sure everything is by default ordered by the created at date in reverse + * + * @return Builder + */ + public function newQuery() + { + $query = parent::newQuery(); + + $query->orderBy('created_at', 'desc'); + + return $query; + } + + /** + * Swagger definition below... + * + * @SWG\Definition( + * type="object", + * definition="Iteration", + * @SWG\Property( + * property="id", + * type="integer", + * format="int32", + * description="The primary id of the model", + * readOnly=true + * ), + * @SWG\Property( + * property="created_at", + * type="string", + * format="date-time", + * description="UTC date of the time this was created", + * readOnly=true + * ), + * @SWG\Property( + * property="updated_at", + * type="string", + * format="date-time", + * description="UTC date of the time this was last updated", + * readOnly=true + * ), + * @SWG\Property( + * property="content", + * type="string", + * description="The content of this iteration." + * ), + * @SWG\Property( + * property="article_id", + * type="integer", + * format="int32", + * description="The primary id of the article that created this iteration is for.", + * readOnly=true + * ), + * @SWG\Property( + * property="created_by_id", + * type="integer", + * format="int32", + * description="The primary id of the user that created this article.", + * readOnly=true + * ), + * @SWG\Property( + * property="createdBy", + * description="The users that created this article.", + * type="array", + * @SWG\Items(ref="#/definitions/User") + * ), + * @SWG\Property( + * property="article", + * description="The article that this iteration is for.", + * type="array", + * @SWG\Items(ref="#/definitions/Article") + * ) + * ) + */ +} diff --git a/code/app/Athenia/Models/Wiki/ArticleModification.php b/code/app/Athenia/Models/Wiki/ArticleModification.php new file mode 100644 index 00000000..0d8d6f21 --- /dev/null +++ b/code/app/Athenia/Models/Wiki/ArticleModification.php @@ -0,0 +1,79 @@ +|ArticleModification getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ArticleModification onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereAction($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereArticleId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereContent($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereLength($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereStartPosition($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleModification withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleModification withoutTrashed() + * @mixin \Eloquent + */ +class ArticleModification extends BaseModelAbstract +{ + /** + * The article this modification is from + * + * @return BelongsTo + */ + public function article(): BelongsTo + { + return $this->belongsTo(Article::class); + } + + /** + * @return HasOne + */ + public function iteration(): HasOne + { + return $this->hasOne(ArticleIteration::class); + } +} diff --git a/code/app/Athenia/Models/Wiki/ArticleSummary.php b/code/app/Athenia/Models/Wiki/ArticleSummary.php new file mode 100644 index 00000000..2382459b --- /dev/null +++ b/code/app/Athenia/Models/Wiki/ArticleSummary.php @@ -0,0 +1,88 @@ +|ArticleSummary getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary newQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereArticleId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereContent($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereUpdatedAt($value) + * @mixin Eloquent + */ +class ArticleSummary extends BaseModelAbstract implements HasValidationRulesContract +{ + use HasValidationRules; + + /** + * The article this summary belongs to + * + * @return BelongsTo + */ + public function article(): BelongsTo + { + return $this->belongsTo(Article::class); + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'content' => [ + 'string', + ], + ], + static::VALIDATION_RULES_UPDATE => [ + static::VALIDATION_PREPEND_REQUIRED => [ + ], + ], + static::VALIDATION_RULES_CREATE => [ + static::VALIDATION_PREPEND_REQUIRED => [ + 'content', + ], + ], + ]; + } +} diff --git a/code/app/Athenia/Models/Wiki/ArticleVersion.php b/code/app/Athenia/Models/Wiki/ArticleVersion.php new file mode 100644 index 00000000..46167dea --- /dev/null +++ b/code/app/Athenia/Models/Wiki/ArticleVersion.php @@ -0,0 +1,103 @@ +|ArticleVersion getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion joinRelations($relations, $leftJoin = null) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleVersion onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereArticleIterationId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereNotInJoin($column, $values, $boolean = 'and') + * @method static \Illuminate\Database\Eloquent\Builder|ArticleVersion withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleVersion withoutTrashed() + * @mixin \Eloquent + */ +class ArticleVersion extends BaseModelAbstract implements HasValidationRulesContract +{ + use HasValidationRules; + + /** + * The article this version is for + * + * @return BelongsTo + */ + public function article(): BelongsTo + { + return $this->belongsTo(Article::class); + } + + /** + * The iteration that this version is for + * + * @return BelongsTo + */ + public function articleIteration(): BelongsTo + { + return $this->belongsTo(ArticleIteration::class); + } + + /** + * Build the model validation rules + * @param array $params + * @return array + */ + public function buildModelValidationRules(...$params): array + { + return [ + static::VALIDATION_RULES_BASE => [ + 'article_iteration_id' => [ + 'bail', + 'required', + 'int', + Rule::exists('article_iterations', 'id'), + SelectedIterationBelongsToArticleValidator::KEY, + ], + ], + ]; + } +} diff --git a/code/app/Athenia/Providers/BaseEventServiceProvider.php b/code/app/Athenia/Providers/BaseEventServiceProvider.php index 48bd3e50..26b962d2 100644 --- a/code/app/Athenia/Providers/BaseEventServiceProvider.php +++ b/code/app/Athenia/Providers/BaseEventServiceProvider.php @@ -11,6 +11,7 @@ use App\Athenia\Events\Payment\PaymentReversedEvent; use App\Athenia\Events\User\Contact\ContactCreatedEvent; use App\Athenia\Events\User\ForgotPasswordEvent; +use App\Athenia\Events\User\InvitationAcceptedEvent; use App\Athenia\Events\User\SignUpEvent; use App\Athenia\Events\User\UserMergeEvent; use App\Athenia\Events\Vote\VoteCreatedEvent; @@ -22,6 +23,7 @@ use App\Athenia\Listeners\Messaging\MessageSentListener; use App\Athenia\Listeners\Payment\DefaultPaymentMethodSetListener; use App\Athenia\Listeners\User\ForgotPasswordListener; +use App\Athenia\Listeners\User\InvitationAcceptedListener; use App\Athenia\Listeners\User\UserMerge\UserBallotCompletionsMergeListener; use App\Athenia\Listeners\User\UserMerge\UserCreatedArticlesMergeListener; use App\Athenia\Listeners\User\UserMerge\UserCreatedIterationsMergeListener; @@ -38,6 +40,7 @@ use App\Athenia\Listeners\Statistic\StatisticCreatedListener; use App\Athenia\Listeners\Statistic\StatisticDeletedListener; use App\Models\Payment\PaymentMethod; +use App\Models\User\ArticleNote; use App\Models\User\User; use App\Models\Wiki\Article; use App\Models\Collection\CollectionItem; @@ -70,6 +73,9 @@ public function listens(): array ForgotPasswordEvent::class => [ ForgotPasswordListener::class, ], + InvitationAcceptedEvent::class => [ + InvitationAcceptedListener::class, + ], MessageCreatedEvent::class => [ MessageCreatedListener::class, ], @@ -135,6 +141,7 @@ public function boot() User::observe(IndexableModelObserver::class); PaymentMethod::observe(PaymentMethodObserver::class); CollectionItem::observe(AggregatedModelObserver::class); + ArticleNote::observe(AggregatedModelObserver::class); $this->registerObservers(); } diff --git a/code/app/Athenia/Providers/BaseRepositoryProvider.php b/code/app/Athenia/Providers/BaseRepositoryProvider.php index 05798510..101c7ddf 100644 --- a/code/app/Athenia/Providers/BaseRepositoryProvider.php +++ b/code/app/Athenia/Providers/BaseRepositoryProvider.php @@ -12,6 +12,7 @@ use App\Athenia\Contracts\Repositories\Messaging\ThreadRepositoryContract; use App\Athenia\Contracts\Repositories\Organization\OrganizationManagerRepositoryContract; use App\Athenia\Contracts\Repositories\Organization\OrganizationRepositoryContract; +use App\Athenia\Contracts\Repositories\User\InvitationTokenRepositoryContract; use App\Athenia\Contracts\Repositories\Payment\LineItemRepositoryContract; use App\Athenia\Contracts\Repositories\Payment\PaymentMethodRepositoryContract; use App\Athenia\Contracts\Repositories\Payment\PaymentRepositoryContract; @@ -20,6 +21,7 @@ use App\Athenia\Contracts\Repositories\Subscription\MembershipPlanRateRepositoryContract; use App\Athenia\Contracts\Repositories\Subscription\MembershipPlanRepositoryContract; use App\Athenia\Contracts\Repositories\Subscription\SubscriptionRepositoryContract; +use App\Athenia\Contracts\Repositories\User\ArticleNoteRepositoryContract; use App\Athenia\Contracts\Repositories\User\ContactRepositoryContract; use App\Athenia\Contracts\Repositories\User\PasswordTokenRepositoryContract; use App\Athenia\Contracts\Repositories\User\ProfileImageRepositoryContract; @@ -32,6 +34,7 @@ use App\Athenia\Contracts\Repositories\Wiki\ArticleIterationRepositoryContract; use App\Athenia\Contracts\Repositories\Wiki\ArticleModificationRepositoryContract; use App\Athenia\Contracts\Repositories\Wiki\ArticleRepositoryContract; +use App\Athenia\Contracts\Repositories\Wiki\ArticleSummaryRepositoryContract; use App\Athenia\Contracts\Repositories\Wiki\ArticleVersionRepositoryContract; use App\Athenia\Contracts\Repositories\Statistic\TargetStatisticRepositoryContract; use App\Athenia\Contracts\Repositories\Statistic\StatisticRepositoryContract; @@ -58,7 +61,9 @@ use App\Athenia\Repositories\Subscription\MembershipPlanRateRepository; use App\Athenia\Repositories\Subscription\MembershipPlanRepository; use App\Athenia\Repositories\Subscription\SubscriptionRepository; +use App\Athenia\Repositories\User\ArticleNoteRepository; use App\Athenia\Repositories\User\ContactRepository; +use App\Athenia\Repositories\User\InvitationTokenRepository; use App\Athenia\Repositories\User\PasswordTokenRepository; use App\Athenia\Repositories\User\ProfileImageRepository; use App\Athenia\Repositories\User\UserRepository; @@ -70,6 +75,7 @@ use App\Athenia\Repositories\Wiki\ArticleIterationRepository; use App\Athenia\Repositories\Wiki\ArticleModificationRepository; use App\Athenia\Repositories\Wiki\ArticleRepository; +use App\Athenia\Repositories\Wiki\ArticleSummaryRepository; use App\Athenia\Repositories\Wiki\ArticleVersionRepository; use App\Athenia\Repositories\Statistic\StatisticRepository; use App\Athenia\Repositories\Statistic\StatisticFilterRepository; @@ -91,7 +97,9 @@ use App\Models\Subscription\MembershipPlan; use App\Models\Subscription\MembershipPlanRate; use App\Models\Subscription\Subscription; +use App\Models\User\ArticleNote; use App\Models\User\Contact; +use App\Models\User\InvitationToken; use App\Models\User\PasswordToken; use App\Models\User\ProfileImage; use App\Models\User\User; @@ -103,6 +111,7 @@ use App\Models\Wiki\Article; use App\Models\Wiki\ArticleIteration; use App\Models\Wiki\ArticleModification; +use App\Models\Wiki\ArticleSummary; use App\Models\Wiki\ArticleVersion; use App\Models\Statistic\TargetStatistic; use App\Models\Statistic\Statistic; @@ -128,7 +137,9 @@ public final function provides(): array ArticleRepositoryContract::class, ArticleIterationRepositoryContract::class, ArticleModificationRepositoryContract::class, + ArticleSummaryRepositoryContract::class, ArticleVersionRepositoryContract::class, + ArticleNoteRepositoryContract::class, AssetRepositoryContract::class, BallotRepositoryContract::class, BallotCompletionRepositoryContract::class, @@ -139,6 +150,7 @@ public final function provides(): array CollectionItemRepositoryContract::class, ContactRepositoryContract::class, FeatureRepositoryContract::class, + InvitationTokenRepositoryContract::class, LineItemRepositoryContract::class, MembershipPlanRepositoryContract::class, MembershipPlanRateRepositoryContract::class, @@ -186,6 +198,7 @@ public final function register(): void return new ArticleRepository( new Article(), $this->app->make('log'), + $this->app->make(StatisticRepositoryContract::class), ); }); $this->app->bind(ArticleIterationRepositoryContract::class, function() { @@ -200,6 +213,12 @@ public final function register(): void $this->app->make('log'), ); }); + $this->app->bind(ArticleSummaryRepositoryContract::class, function() { + return new ArticleSummaryRepository( + new ArticleSummary(), + $this->app->make('log'), + ); + }); $this->app->bind(ArticleVersionRepositoryContract::class, function() { return new ArticleVersionRepository( new ArticleVersion(), @@ -207,6 +226,12 @@ public final function register(): void $this->app->make(Dispatcher::class), ); }); + $this->app->bind(ArticleNoteRepositoryContract::class, function() { + return new ArticleNoteRepository( + new ArticleNote(), + $this->app->make('log'), + ); + }); $this->app->bind(AssetRepositoryContract::class, function() { return new AssetRepository( new Asset(), @@ -311,6 +336,13 @@ public final function register(): void $this->app->make('log') ); }); + $this->app->bind(InvitationTokenRepositoryContract::class, function() { + return new InvitationTokenRepository( + new InvitationToken(), + $this->app->make('log'), + $this->app->make(TokenGenerationServiceContract::class) + ); + }); $this->app->bind(PasswordTokenRepositoryContract::class, function() { return new PasswordTokenRepository( new PasswordToken(), diff --git a/code/app/Athenia/Providers/BaseValidatorProvider.php b/code/app/Athenia/Providers/BaseValidatorProvider.php index 87d60cdb..1dfdd80e 100644 --- a/code/app/Athenia/Providers/BaseValidatorProvider.php +++ b/code/app/Athenia/Providers/BaseValidatorProvider.php @@ -6,6 +6,7 @@ use App\Athenia\Validators\ArticleVersion\SelectedIterationBelongsToArticleValidator; use App\Athenia\Validators\ForgotPassword\TokenIsNotExpiredValidator; use App\Athenia\Validators\ForgotPassword\UserOwnsTokenValidator; +use App\Athenia\Validators\InvitationTokenIsValidValidator; use App\Athenia\Validators\NotPresentValidator; use App\Athenia\Validators\OwnedByValidator; use App\Athenia\Validators\Subscription\MembershipPlanRateIsActiveValidator; @@ -30,6 +31,7 @@ public function boot(): void $validator->extend('token_is_not_expired', TokenIsNotExpiredValidator::class); $validator->extend('user_owns_token', UserOwnsTokenValidator::class); $validator->extend('not_present', NotPresentValidator::class); + $validator->extend(InvitationTokenIsValidValidator::KEY, InvitationTokenIsValidValidator::class); $validator->extend(MembershipPlanRateIsActiveValidator::KEY, MembershipPlanRateIsActiveValidator::class); $validator->extend(OwnedByValidator::KEY, OwnedByValidator::class); $validator->extend(PaymentMethodIsOwnedByEntityValidator::KEY, PaymentMethodIsOwnedByEntityValidator::class); diff --git a/code/app/Athenia/Repositories/Statistic/StatisticRepository.php b/code/app/Athenia/Repositories/Statistic/StatisticRepository.php index 341708dd..53b772f4 100644 --- a/code/app/Athenia/Repositories/Statistic/StatisticRepository.php +++ b/code/app/Athenia/Repositories/Statistic/StatisticRepository.php @@ -10,6 +10,7 @@ use App\Athenia\Events\Statistic\StatisticUpdatedEvent; use App\Athenia\Events\Statistic\StatisticCreatedEvent; use Illuminate\Contracts\Events\Dispatcher; +use Illuminate\Support\Collection; use Psr\Log\LoggerInterface as LogContract; use App\Athenia\Repositories\Statistic\StatisticFilterRepository; use App\Athenia\Traits\CanGetAndUnset; @@ -89,4 +90,16 @@ public function delete(BaseModelAbstract $model): void parent::delete($model); $this->dispatcher->dispatch(new StatisticDeletedEvent($model)); } + + /** + * Get all statistics for a given model + * + * @param string $model + * @return Collection + */ + public function findAllForModel(string $model): Collection + { + return $this->model->newQuery() + ->where('model', $model)->get(); + } } \ No newline at end of file diff --git a/code/app/Athenia/Repositories/User/ArticleNoteRepository.php b/code/app/Athenia/Repositories/User/ArticleNoteRepository.php new file mode 100644 index 00000000..a19436cf --- /dev/null +++ b/code/app/Athenia/Repositories/User/ArticleNoteRepository.php @@ -0,0 +1,70 @@ +getAndUnset($data, 'completed'); + + if ($completed === true) { + $data['completed_at'] = now(); + } + + return parent::create($data, $relatedModel, $forcedValues); + } + + /** + * Override update to handle completed boolean + * + * @param BaseModelAbstract $model + * @param array $data + * @param array $forcedValues + * @return BaseModelAbstract + */ + public function update(BaseModelAbstract $model, array $data, array $forcedValues = []): BaseModelAbstract + { + $completed = $this->getAndUnset($data, 'completed'); + + if ($completed === true) { + $data['completed_at'] = now(); + } elseif ($completed === false) { + $data['completed_at'] = null; + } + + return parent::update($model, $data, $forcedValues); + } +} diff --git a/code/app/Athenia/Repositories/User/InvitationTokenRepository.php b/code/app/Athenia/Repositories/User/InvitationTokenRepository.php new file mode 100644 index 00000000..14190046 --- /dev/null +++ b/code/app/Athenia/Repositories/User/InvitationTokenRepository.php @@ -0,0 +1,73 @@ +tokenGenerationService = $tokenGenerationService; + } + + /** + * Finds an invitation token by its token string + * + * @param string $token + * @return Model|InvitationToken|null + */ + public function findByToken(string $token): ?InvitationToken + { + return $this->model->newQuery() + ->where('token', '=', $token) + ->first(); + } + + /** + * Generates a unique token, or throws an exception if it cannot do so. + * + * @throws \OverflowException + * @return string + */ + public function generateUniqueToken(): string + { + $attempts = 0; + do { + $token = $this->tokenGenerationService->generateToken(); + $existingModel = $this->findByToken($token); + $attempts++; + } while ($existingModel != null && $attempts < 5); + + if ($existingModel) { + throw new \OverflowException('Unable to generate unique invitation token.'); + } + + return $token; + } +} diff --git a/code/app/Athenia/Repositories/Wiki/ArticleRepository.php b/code/app/Athenia/Repositories/Wiki/ArticleRepository.php index f8652aae..fb4ae5ab 100644 --- a/code/app/Athenia/Repositories/Wiki/ArticleRepository.php +++ b/code/app/Athenia/Repositories/Wiki/ArticleRepository.php @@ -3,8 +3,13 @@ namespace App\Athenia\Repositories\Wiki; +use App\Athenia\Contracts\Repositories\Statistic\StatisticRepositoryContract; use App\Athenia\Contracts\Repositories\Wiki\ArticleRepositoryContract; +use App\Athenia\Models\BaseModelAbstract; use App\Athenia\Repositories\BaseRepositoryAbstract; +use App\Athenia\Traits\CanGetAndUnset; +use App\Models\Statistic\Statistic; +use App\Models\User\User; use App\Models\Wiki\Article; use App\Repositories\Traits\NotImplemented; use Psr\Log\LoggerInterface as LogContract; @@ -16,14 +21,150 @@ class ArticleRepository extends BaseRepositoryAbstract implements ArticleRepositoryContract { use \App\Athenia\Repositories\Traits\NotImplemented\Delete; + use CanGetAndUnset; /** * ArticleRepository constructor. * @param Article $model * @param LogContract $log + * @param StatisticRepositoryContract $statisticRepository */ - public function __construct(Article $model, LogContract $log) + public function __construct( + Article $model, + LogContract $log, + private readonly StatisticRepositoryContract $statisticRepository + ) { parent::__construct($model, $log); } + + /** + * Override create to handle categories sync + * + * @param array $data + * @param BaseModelAbstract|null $relatedModel + * @param array $forcedValues + * @return BaseModelAbstract + */ + public function create(array $data = [], BaseModelAbstract $relatedModel = null, array $forcedValues = []): BaseModelAbstract + { + $categories = $this->getAndUnset($data, 'categories'); + + /** @var Article $article */ + $article = parent::create($data, $relatedModel, $forcedValues); + + if ($categories !== null) { + $syncData = collect($categories)->mapWithKeys(fn($cat) => [ + $cat['category_id'] => array_filter(['relevance' => $cat['relevance'] ?? null]) + ])->toArray(); + $article->categories()->sync($syncData); + } + + return $article; + } + + /** + * Override update to handle categories sync + * + * @param BaseModelAbstract $model + * @param array $data + * @param array $forcedValues + * @return BaseModelAbstract + */ + public function update(BaseModelAbstract $model, array $data, array $forcedValues = []): BaseModelAbstract + { + $categories = $this->getAndUnset($data, 'categories'); + + /** @var Article $article */ + $article = parent::update($model, $data, $forcedValues); + + if ($categories !== null) { + $syncData = collect($categories)->mapWithKeys(fn($cat) => [ + $cat['category_id'] => array_filter(['relevance' => $cat['relevance'] ?? null]) + ])->toArray(); + $article->categories()->sync($syncData); + } + + return $article; + } + + /** + * Selects an article for a user based on their note completion status and article statistics. + * + * Priority order: + * 1. Articles where user has NO note started (never returns articles with completed notes) + * 2. Articles where user has an incomplete note + * + * Within each priority group, orders by: + * - Lowest total_completed_notes statistic (fewest completions by all users) + * - Lowest total_notes statistic (fewest notes started by all users) + * - Random order for variety when statistics are equal + * + * @param User $user + * @return Article|null + */ + public function selectArticleForUser(User $user): ?Article + { + // Get all statistics for the article model + $statistics = $this->statisticRepository->findAllForModel('article'); + + // Build a map of statistic names to their IDs + $statisticMap = []; + foreach ($statistics as $statistic) { + $statisticMap[$statistic->name] = $statistic->id; + } + + // Start building the query + $query = $this->model->newQuery() + ->select('articles.*') + ->leftJoin('article_notes as user_notes', function($join) use ($user) { + $join->on('articles.id', '=', 'user_notes.article_id') + ->where('user_notes.user_id', '=', $user->id) + ->whereNull('user_notes.deleted_at'); + }); + + // Conditionally add join for total_completed_notes statistic + if (isset($statisticMap['total_completed_notes'])) { + $completedStatId = $statisticMap['total_completed_notes']; + $query->leftJoin('target_statistics as completed_stats', function($join) use ($completedStatId) { + $join->on('articles.id', '=', 'completed_stats.target_id') + ->where('completed_stats.target_type', '=', 'article') + ->where('completed_stats.statistic_id', '=', $completedStatId); + }); + } + + // Conditionally add join for total_notes statistic + if (isset($statisticMap['total_notes'])) { + $totalNotesStatId = $statisticMap['total_notes']; + $query->leftJoin('target_statistics as total_stats', function($join) use ($totalNotesStatId) { + $join->on('articles.id', '=', 'total_stats.target_id') + ->where('total_stats.target_type', '=', 'article') + ->where('total_stats.statistic_id', '=', $totalNotesStatId); + }); + } + + // Exclude articles where user has completed a note + $query->where(function($query) { + $query->whereNull('user_notes.id') + ->orWhereNull('user_notes.completed_at'); + }); + + // Order by priority: no note (1) before incomplete note (2) + $query->orderByRaw('CASE WHEN user_notes.id IS NULL THEN 1 ELSE 2 END ASC'); + + // Conditionally add ordering by completed notes count + if (isset($statisticMap['total_completed_notes'])) { + $query->orderByRaw('COALESCE(CAST(JSON_EXTRACT(completed_stats.result, "$.total") AS UNSIGNED), 0) ASC'); + } + + // Conditionally add ordering by total notes count + if (isset($statisticMap['total_notes'])) { + $query->orderByRaw('COALESCE(CAST(JSON_EXTRACT(total_stats.result, "$.total") AS UNSIGNED), 0) ASC'); + } + + // Finally order randomly for variety + $query->inRandomOrder(); + + return $query->first(); + } } \ No newline at end of file diff --git a/code/app/Athenia/Repositories/Wiki/ArticleSummaryRepository.php b/code/app/Athenia/Repositories/Wiki/ArticleSummaryRepository.php new file mode 100644 index 00000000..c9fc7626 --- /dev/null +++ b/code/app/Athenia/Repositories/Wiki/ArticleSummaryRepository.php @@ -0,0 +1,26 @@ +invitationTokenRepository = $invitationTokenRepository; + } + + /** + * This is invoked by the validator rule 'invitation_token_is_valid' + * + * @param $attribute + * @param $value + * @param array $parameters + * @param Validator|null $validator + * @return bool + */ + public function validate($attribute, $value, $parameters = [], Validator $validator = null): bool + { + if (!is_string($value)) { + return false; + } + + $invitationToken = $this->invitationTokenRepository->findByToken($value); + + if (!$invitationToken) { + return false; + } + + if ($invitationToken->isUsed()) { + return false; + } + + return true; + } +} diff --git a/code/app/Http/Core/Controllers/Wiki/ArticleSummaryController.php b/code/app/Http/Core/Controllers/Wiki/ArticleSummaryController.php new file mode 100644 index 00000000..357633c1 --- /dev/null +++ b/code/app/Http/Core/Controllers/Wiki/ArticleSummaryController.php @@ -0,0 +1,13 @@ +|Asset getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Asset onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereAlt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereCaption($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereHeight($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereOwnerId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereOwnerType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereSource($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereUrl($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Asset whereWidth($value) + * @method static \Illuminate\Database\Eloquent\Builder|Asset withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Asset withoutTrashed() * @mixin \Eloquent */ -class Asset extends BaseModelAbstract implements HasValidationRulesContract +class Asset extends AtheniaAsset { - use HasValidationRules; - - /** - * @var string Makes sure to override all children - */ - protected $table = 'assets'; - - /** - * The user that created this asset - * - * @return BelongsTo - */ - public function owner() - { - return $this->morphTo(); - } - - /** - * All mime types that can be uploaded to the server for this asset - * - * @return array - */ - protected function getAvailableMimeTypes(): array - { - return [ - 'image/jpeg', - 'image/png', - 'image/gif', - ]; - } - - /** - * Build the model validation rules - * @param array $params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - self::VALIDATION_RULES_BASE => [ - 'file_contents' => [ - 'string', - ], - - 'name' => [ - 'nullable', - 'string', - ], - - 'caption' => [ - 'nullable', - 'string', - ], - - // Set in the request object, and not set from the user request - 'mime_type' => [ - Rule::in($this->getAvailableMimeTypes()), - ], - ], - self::VALIDATION_RULES_CREATE => [ - self::VALIDATION_PREPEND_REQUIRED => [ - 'file_contents', - ], - ], - self::VALIDATION_RULES_UPDATE => [ - self::VALIDATION_PREPEND_NOT_PRESENT => [ - 'file_contents', - ], - ] - ]; - } -} +} \ No newline at end of file diff --git a/code/app/Models/Category.php b/code/app/Models/Category.php index f4e2ac38..ca12c434 100644 --- a/code/app/Models/Category.php +++ b/code/app/Models/Category.php @@ -3,76 +3,51 @@ namespace App\Models; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasValidationRules; -use Illuminate\Database\Query\Builder; +use App\Athenia\Models\Category as AtheniaCategory; /** * Class Category * + * @package App\Models * @property int $id * @property string $name * @property string|null $description * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $deleted_at - * @method static \Database\Factories\CategoryFactory factory(...$parameters) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category getAggregateMethod() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category isAppendRelationsCount() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category isLeftJoin() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category isUseTableAlias() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category joinRelations($relations, $leftJoin = null) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category newQuery() - * @method static \Illuminate\Database\Eloquent\Builder|Category onlyTrashed() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category orWhereInJoin($column, $values) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category orWhereJoin($column, $operator, $value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category orWhereNotInJoin($column, $values) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category orderByJoin($column, $direction = 'asc', $aggregateMethod = null) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category query() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category setAggregateMethod(string $aggregateMethod) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category setAppendRelationsCount(bool $appendRelationsCount) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category setLeftJoin(bool $leftJoin) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category setUseTableAlias(bool $useTableAlias) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereCreatedAt($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereDeletedAt($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereDescription($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereId($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereInJoin($column, $values, $boolean = 'and', $not = false) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereJoin($column, $operator, $value, $boolean = 'and') - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereName($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereNotInJoin($column, $values, $boolean = 'and') - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Category withTrashed() - * @method static \Illuminate\Database\Eloquent\Builder|Category withoutTrashed() + * @property-read \Illuminate\Database\Eloquent\Collection $articles + * @property-read int|null $articles_count + * @method static \Database\Factories\CategoryFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Category onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereDescription($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Category whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Category withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Category withoutTrashed() * @mixin \Eloquent */ -class Category extends BaseModelAbstract implements HasValidationRulesContract +class Category extends AtheniaCategory { - use HasValidationRules; - - /** - * @param mixed ...$params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - static::VALIDATION_RULES_BASE => [ - 'name' => [ - 'string', - ], - 'description' => [ - 'nullable', - 'string', - ], - ], - static::VALIDATION_RULES_CREATE => [ - static::VALIDATION_PREPEND_REQUIRED => [ - 'name', - ], - ], - ]; - } -} +} \ No newline at end of file diff --git a/code/app/Models/Collection/Collection.php b/code/app/Models/Collection/Collection.php index 21dbbb89..a2794dc5 100644 --- a/code/app/Models/Collection/Collection.php +++ b/code/app/Models/Collection/Collection.php @@ -3,21 +3,12 @@ namespace App\Models\Collection; -use App\Athenia\Contracts\Models\CanBeStatisticTargetContract; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasStatisticTargets; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Athenia\Validators\OwnedByValidator; -use App\Models\Statistic\TargetStatistic; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\MorphMany; -use Illuminate\Database\Eloquent\Relations\MorphTo; -use Illuminate\Validation\Rule; +use App\Athenia\Models\Collection\Collection as AtheniaCollection; /** - * App\Models\Collection\Collection + * Class Collection * + * @package App\Models\Collection * @property int $id * @property int $owner_id * @property string $owner_type @@ -26,107 +17,44 @@ * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $deleted_at - * @property-read int|null $collection_items_count * @property-read \Illuminate\Database\Eloquent\Collection $collectionItems + * @property-read int|null $collection_items_count * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $owner * @property-read \Illuminate\Database\Eloquent\Collection $targetStatistics - * @method static \Database\Factories\Collection\CollectionFactory factory(...$parameters) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection getAggregateMethod() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection isAppendRelationsCount() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection isLeftJoin() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection isUseTableAlias() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection joinRelations($relations, $leftJoin = null) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection newQuery() - * @method static \Illuminate\Database\Eloquent\Builder|Collection onlyTrashed() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection orWhereInJoin($column, $values) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection orWhereJoin($column, $operator, $value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection orWhereNotInJoin($column, $values) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection orderByJoin($column, $direction = 'asc', $aggregateMethod = null) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection query() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection setAggregateMethod(string $aggregateMethod) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection setAppendRelationsCount(bool $appendRelationsCount) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection setLeftJoin(bool $leftJoin) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection setUseTableAlias(bool $useTableAlias) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereCollectionItemsCount($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereCreatedAt($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereDeletedAt($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereId($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereInJoin($column, $values, $boolean = 'and', $not = false) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereIsPublic($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereJoin($column, $operator, $value, $boolean = 'and') - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereName($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereNotInJoin($column, $values, $boolean = 'and') - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereOwnerId($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereOwnerType($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Collection withTrashed() - * @method static \Illuminate\Database\Eloquent\Builder|Collection withoutTrashed() + * @property-read int|null $target_statistics_count + * @method static \Database\Factories\Collection\CollectionFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Collection onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereIsPublic($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereOwnerId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereOwnerType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Collection whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Collection withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Collection withoutTrashed() * @mixin \Eloquent */ -class Collection extends BaseModelAbstract implements HasValidationRulesContract, CanBeStatisticTargetContract +class Collection extends AtheniaCollection { - use HasValidationRules, HasStatisticTargets; - - /** - * All collection items - * - * @return HasMany - */ - public function collectionItems(): HasMany - { - return $this->hasMany(CollectionItem::class) - ->orderBy('order'); - } - - /** - * The owner - * - * @return MorphTo - */ - public function owner(): MorphTo - { - return $this->morphTo(); - } - - /** - * @param ...$params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - static::VALIDATION_RULES_BASE => [ - 'name' => [ - 'nullable', - 'string', - ], - 'is_public' => [ - 'boolean', - ], - 'collection_item_order' => [ - 'array', - ], - 'collection_item_order.*' => [ - 'integer', - Rule::exists('collection_items', 'id'), - OwnedByValidator::KEY . ':collection,collectionItems', - ] - ], - static::VALIDATION_RULES_CREATE => [ - static::VALIDATION_PREPEND_REQUIRED => ['is_public'], - static::VALIDATION_PREPEND_NOT_PRESENT => ['collection_item_order'], - ], - ]; - } - - /** - * The name of the morph relation - * - * @return string - */ - public function morphRelationName(): string - { - return 'collection'; - } } \ No newline at end of file diff --git a/code/app/Models/Collection/CollectionItem.php b/code/app/Models/Collection/CollectionItem.php index b364ef6c..9469657d 100644 --- a/code/app/Models/Collection/CollectionItem.php +++ b/code/app/Models/Collection/CollectionItem.php @@ -3,19 +3,12 @@ namespace App\Models\Collection; -use App\Athenia\Contracts\Models\CanBeAggregatedContract; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Models\Category; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\BelongsToMany; -use Illuminate\Database\Eloquent\Relations\MorphTo; -use Illuminate\Validation\Rule; +use App\Athenia\Models\Collection\CollectionItem as AtheniaCollectionItem; /** - * App\Models\Collection\CollectionItem + * Class CollectionItem * + * @package App\Models\Collection * @property int $id * @property int $item_id * @property string $item_type @@ -24,110 +17,43 @@ * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $deleted_at - * @property-read \Illuminate\Database\Eloquent\Collection $categories + * @property-read \Illuminate\Database\Eloquent\Collection $categories * @property-read int|null $categories_count * @property-read \App\Models\Collection\Collection $collection * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $item - * @method static \Database\Factories\Collection\CollectionItemFactory factory(...$parameters) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem getAggregateMethod() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem isAppendRelationsCount() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem isLeftJoin() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem isUseTableAlias() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem joinRelations($relations, $leftJoin = null) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem newQuery() - * @method static \Illuminate\Database\Eloquent\Builder|CollectionItem onlyTrashed() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem orWhereInJoin($column, $values) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem orWhereJoin($column, $operator, $value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem orWhereNotInJoin($column, $values) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem orderByJoin($column, $direction = 'asc', $aggregateMethod = null) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem query() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem setAggregateMethod(string $aggregateMethod) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem setAppendRelationsCount(bool $appendRelationsCount) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem setLeftJoin(bool $leftJoin) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem setUseTableAlias(bool $useTableAlias) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereCollectionId($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereCreatedAt($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereDeletedAt($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereId($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereInJoin($column, $values, $boolean = 'and', $not = false) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereItemId($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereItemType($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereJoin($column, $operator, $value, $boolean = 'and') - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereNotInJoin($column, $values, $boolean = 'and') - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereOrder($value) - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|CollectionItem withTrashed() - * @method static \Illuminate\Database\Eloquent\Builder|CollectionItem withoutTrashed() + * @method static \Database\Factories\Collection\CollectionItemFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|CollectionItem onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereCollectionId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereItemId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereItemType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereOrder($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|CollectionItem whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|CollectionItem withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|CollectionItem withoutTrashed() * @mixin \Eloquent */ -class CollectionItem extends BaseModelAbstract implements HasValidationRulesContract, CanBeAggregatedContract +class CollectionItem extends AtheniaCollectionItem { - use HasValidationRules; - - /** - * The item this is related to - * - * @return MorphTo - */ - public function item(): MorphTo - { - return $this->morphTo(); - } - - /** - * All categories for this collection item - * - * @return BelongsToMany - */ - public function categories(): BelongsToMany - { - return $this->belongsToMany(Category::class, 'collection_item_categories'); - } - - /** - * The collection this item is apart of - * - * @return BelongsTo - */ - public function collection(): BelongsTo - { - return $this->belongsTo(Collection::class); - } - - /** - * @param ...$params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - self::VALIDATION_RULES_BASE => [ - 'item_id' => [ - 'integer', - ], - 'item_type' => [ - Rule::in(['article']), - ], - 'order' => [ - 'integer', - ], - ], - self::VALIDATION_RULES_CREATE => [ - self::VALIDATION_PREPEND_REQUIRED => ['item_id', 'item_type', 'order'], - ] - ]; - } - - /** - * Returns the relation paths to the models that can be target statistics - * For example: ["collection"] would mean this model affects statistics on collections - * through the collection relation - * - * @return string[] - */ - public function getStatisticTargetRelationPath(): array - { - return ['collection']; - } } \ No newline at end of file diff --git a/code/app/Models/Feature.php b/code/app/Models/Feature.php index 8d0e37c4..0744fc4b 100644 --- a/code/app/Models/Feature.php +++ b/code/app/Models/Feature.php @@ -3,39 +3,51 @@ namespace App\Models; -use App\Athenia\Models\BaseModelAbstract; -use App\Models\Subscription\MembershipPlan; -use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use App\Athenia\Models\Feature as AtheniaFeature; /** * Class Feature * + * @package App\Models * @property int $id * @property string $name * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property string|null $description - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Subscription\MembershipPlan[] $membershipPlans + * @property-read \Illuminate\Database\Eloquent\Collection $membershipPlans * @property-read int|null $membership_plans_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Feature newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Feature newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Feature query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feature whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feature whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feature whereDescription($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feature whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feature whereName($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Feature whereUpdatedAt($value) + * @method static \Database\Factories\FeatureFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Feature onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature whereDescription($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Feature whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Feature withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Feature withoutTrashed() * @mixin \Eloquent */ -class Feature extends BaseModelAbstract +class Feature extends AtheniaFeature { - /** - * @return BelongsToMany - */ - public function membershipPlans(): BelongsToMany - { - return $this->belongsToMany(MembershipPlan::class); - } -} +} \ No newline at end of file diff --git a/code/app/Models/Messaging/Message.php b/code/app/Models/Messaging/Message.php index 4ae6907c..1cf30122 100644 --- a/code/app/Models/Messaging/Message.php +++ b/code/app/Models/Messaging/Message.php @@ -3,29 +3,21 @@ namespace App\Models\Messaging; -use App\Athenia\Contracts\Models\HasPolicyContract; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Events\Messaging\MessageCreatedEvent; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Models\User\User; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\MorphTo; -use Illuminate\Validation\Rule; +use App\Athenia\Models\Messaging\Message as AtheniaMessage; /** * Class Message * + * @package App\Models\Messaging * @property int $id * @property string|null $email * @property string|null $subject * @property string|null $template - * @property array $data + * @property array $data * @property int|null $to_id * @property int|null $from_id * @property int|null $thread_id - * @property array|null $via + * @property array|null $via * @property string|null $action * @property \Illuminate\Support\Carbon|null $scheduled_at * @property \Illuminate\Support\Carbon|null $sent_at @@ -35,120 +27,56 @@ * @property \Illuminate\Support\Carbon|null $updated_at * @property string|null $reply_to_email * @property string|null $reply_to_name - * @property-read \App\Models\User\User|null $from + * @property string|null $to_type + * @property string|null $from_type + * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent|null $from * @property-read \App\Models\Messaging\Thread|null $thread - * @property-read \App\Models\User\User|null $to - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\Message newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\Message newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\Message query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereAction($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereData($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereEmail($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereFromId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereReplyToEmail($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereReplyToName($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereScheduledAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereSeenAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereSentAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereSubject($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereTemplate($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereThreadId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereToId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Message whereVia($value) + * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent|null $to + * @method static \Database\Factories\Messaging\MessageFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Message onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereAction($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereData($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereEmail($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereFromId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereFromType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereReplyToEmail($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereReplyToName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereScheduledAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereSeenAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereSentAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereSubject($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereTemplate($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereThreadId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereToId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereToType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Message whereVia($value) + * @method static \Illuminate\Database\Eloquent\Builder|Message withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Message withoutTrashed() * @mixin \Eloquent */ -class Message extends BaseModelAbstract implements HasPolicyContract, HasValidationRulesContract +class Message extends AtheniaMessage { - use HasValidationRules; - - const VIA_EMAIL = 'email'; - const VIA_SLACK = 'slack'; - const VIA_SMS = 'sms'; - const VIA_PUSH_NOTIFICATION = 'push'; - - /** - * @var array - */ - protected $casts = [ - 'data' => 'array', - 'via' => 'array', - 'seen_at' => 'datetime', - 'sent_at' => 'datetime', - 'scheduled_at' => 'datetime', - ]; - - /** - * Array of events that need to be dispatched - * - * @var array - */ - protected $dispatchesEvents = [ - 'created' => MessageCreatedEvent::class - ]; - - /** - * Each message belongs to a user - * - * @return MorphTo - */ - public function from() : MorphTo - { - return $this->morphTo(); - } - - /** - * The thread that this message is in - * - * @return BelongsTo - */ - public function thread() : BelongsTo - { - return $this->belongsTo(Thread::class); - } - - /** - * Each message belongs to a user - * - * @return MorphTo - */ - public function to() : MorphTo - { - return $this->morphTo(); - } - - /** - * Build the model validation rules - * @param array $params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - static::VALIDATION_RULES_BASE => [ - 'message' => [ - 'string', - ], - 'seen' => [ - 'boolean', - ], - 'template' => [ - Rule::in([ - 'contact', - ]), - ], - 'data' => [ - 'array', - ], - ], - static::VALIDATION_RULES_UPDATE => [ - static::VALIDATION_PREPEND_NOT_PRESENT => [ - 'message', - ], - ], - ]; - } } \ No newline at end of file diff --git a/code/app/Models/Messaging/PushNotificationKey.php b/code/app/Models/Messaging/PushNotificationKey.php index bc7daeb0..7e96d3dc 100644 --- a/code/app/Models/Messaging/PushNotificationKey.php +++ b/code/app/Models/Messaging/PushNotificationKey.php @@ -3,35 +3,52 @@ namespace App\Models\Messaging; -use App\Athenia\Contracts\Models\Messaging\CanReceivePushNotificationContract; -use App\Athenia\Models\BaseModelAbstract; -use Illuminate\Database\Eloquent\Relations\MorphTo; -use Illuminate\Support\Carbon; +use App\Athenia\Models\Messaging\PushNotificationKey as AtheniaPushNotificationKey; /** - * Class PushNotifications + * Class PushNotificationKey * + * @package App\Models\Messaging * @property int $id - * @property int $user_id + * @property int $owner_id + * @property string $owner_type * @property string $push_notification_key - * @property Carbon|null $created_at - * @property Carbon|null $updated_at - * @property Carbon|null $deleted_at - * @property-read CanReceivePushNotificationContract $owner - * @mixin Eloquent + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Illuminate\Support\Carbon|null $deleted_at + * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $owner + * @method static \Database\Factories\Messaging\PushNotificationKeyFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|PushNotificationKey onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereOwnerId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereOwnerType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey wherePushNotificationKey($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PushNotificationKey whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|PushNotificationKey withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|PushNotificationKey withoutTrashed() + * @mixin \Eloquent */ -class PushNotificationKey extends BaseModelAbstract +class PushNotificationKey extends AtheniaPushNotificationKey { - /** - * @var string Table override due to laravel bug - */ - protected $table = 'push_notification_keys'; - - /** - * @return MorphTo - */ - public function owner(): MorphTo - { - return $this->morphTo('owner'); - } -} +} \ No newline at end of file diff --git a/code/app/Models/Messaging/Thread.php b/code/app/Models/Messaging/Thread.php index b99e75ab..82dac221 100644 --- a/code/app/Models/Messaging/Thread.php +++ b/code/app/Models/Messaging/Thread.php @@ -3,124 +3,56 @@ namespace App\Models\Messaging; -use App\Athenia\Contracts\Models\HasPolicyContract; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Models\User\User; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\BelongsToMany; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Validation\Rule; +use App\Athenia\Models\Messaging\Thread as AtheniaThread; /** * Class Thread * + * @package App\Models\Messaging * @property int $id * @property string|null $topic * @property int|null $subject_id * @property string|null $subject_type * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property-read null|string $last_message - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Messaging\Message[] $messages + * @property-read \Illuminate\Database\Eloquent\Collection $messages * @property-read int|null $messages_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\User\User[] $users + * @property-read \Illuminate\Database\Eloquent\Collection $users * @property-read int|null $users_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\Thread newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\Thread newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\Thread query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Thread whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Thread whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Thread whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Thread whereSubjectId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Thread whereSubjectType($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Thread whereTopic($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Thread whereUpdatedAt($value) + * @method static \Database\Factories\Messaging\ThreadFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Thread onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereSubjectId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereSubjectType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereTopic($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Thread whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Thread withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Thread withoutTrashed() * @mixin \Eloquent */ -class Thread extends BaseModelAbstract implements HasPolicyContract, HasValidationRulesContract +class Thread extends AtheniaThread { - use HasValidationRules; - - /** - * The url of the profile image - * - * @var array - */ - protected $appends = [ - 'last_message', - ]; - - /** - * All messages in this thread - * - * @return HasMany - */ - public function messages() : HasMany - { - return $this->hasMany(Message::class)->orderBy('created_at', 'desc'); - } - - /** - * All users that are in this thread - * - * @return BelongsToMany - */ - public function users() : BelongsToMany - { - return $this->belongsToMany(User::class); - } - - /** - * Get the URL for the profile image - * - * @return null|string - */ - public function getLastMessageAttribute() - { - return $this->messages ? $this->messages->first() : null; - } - - /** - * Build the model validation rules - * @param array $params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - static::VALIDATION_RULES_BASE => [ - 'subject_type' => [ - 'bail', - 'string', - ], - 'subject_id' => [ - 'int', - ], - 'users' => [ - 'array', - ], - 'users.*' => [ - 'integer', - Rule::exists('users', 'id'), - ], - ], - static::VALIDATION_RULES_CREATE => [ - static::VALIDATION_PREPEND_REQUIRED => [ - 'subject_type', - ], - ], - static::VALIDATION_RULES_UPDATE => [ - static::VALIDATION_PREPEND_REQUIRED => [ - 'users', - ], - static::VALIDATION_PREPEND_NOT_PRESENT => [ - 'subject_type', - 'subject_id', - ], - ], - ]; - } -} +} \ No newline at end of file diff --git a/code/app/Models/Organization/Organization.php b/code/app/Models/Organization/Organization.php index eca0cb1a..fd4b6a40 100644 --- a/code/app/Models/Organization/Organization.php +++ b/code/app/Models/Organization/Organization.php @@ -3,189 +3,65 @@ namespace App\Models\Organization; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Contracts\Models\IsAnEntityContract; -use App\Athenia\Contracts\Models\Messaging\CanReceiveMessageContract; -use App\Athenia\Contracts\Models\Messaging\CanReceiveSlackNotificationsContract; -use App\Athenia\Contracts\Models\Messaging\HasMessageReceiversContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Athenia\Models\Traits\IsEntity; -use App\Models\Asset; -use App\Models\Messaging\Message; -use App\Models\Role; -use App\Models\User\ProfileImage; -use App\Models\User\User; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\MorphMany; -use Illuminate\Support\Collection; +use App\Athenia\Models\Organization\Organization as AtheniaOrganization; /** * Class Organization * + * @package App\Models\Organization * @property int $id * @property string $name * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property int|null $profile_image_id * @property string|null $stripe_customer_key - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Asset[] $assets + * @property-read \Illuminate\Database\Eloquent\Collection $assets * @property-read int|null $assets_count + * @property-read \Illuminate\Database\Eloquent\Collection $collections + * @property-read int|null $collections_count * @property-read null|string $profile_image_url - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Organization\OrganizationManager[] $organizationManagers + * @property-read \Illuminate\Database\Eloquent\Collection $organizationManagers * @property-read int|null $organization_managers_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Payment\PaymentMethod[] $paymentMethods + * @property-read \Illuminate\Database\Eloquent\Collection $paymentMethods * @property-read int|null $payment_methods_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Payment\Payment[] $payments + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @property-read int|null $payments_count * @property-read \App\Models\User\ProfileImage|null $profileImage - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Subscription\Subscription[] $subscriptions + * @property-read \Illuminate\Database\Eloquent\Collection $subscriptions * @property-read int|null $subscriptions_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Organization\Organization newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Organization\Organization newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Organization\Organization query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\Organization whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\Organization whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\Organization whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\Organization whereName($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\Organization whereProfileImageId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\Organization whereStripeCustomerKey($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\Organization whereUpdatedAt($value) + * @method static \Database\Factories\Organization\OrganizationFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Organization onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereProfileImageId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereStripeCustomerKey($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Organization whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Organization withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Organization withoutTrashed() * @mixin \Eloquent */ -class Organization extends BaseModelAbstract - implements HasValidationRulesContract, IsAnEntityContract, - HasMessageReceiversContract, CanReceiveMessageContract, CanReceiveSlackNotificationsContract +class Organization extends AtheniaOrganization { - use HasValidationRules, IsEntity; - - /** - * All assets this user has created - * - * @return MorphMany - */ - public function assets(): MorphMany - { - return $this->morphMany(Asset::class, 'owner'); - } - - /** - * All organization managers in this organization - * - * @return HasMany - */ - public function organizationManagers(): HasMany - { - return $this->hasMany(OrganizationManager::class); - } - - /** - * The asset that contains the profile image for this user - * - * @return BelongsTo - */ - public function profileImage() : BelongsTo - { - return $this->belongsTo(ProfileImage::class); - } - - /** - * Get the URL for the profile image - * - * @return null|string - */ - public function getProfileImageUrlAttribute() - { - return $this->profileImage ? $this->profileImage->url : null; - } - - /** - * @inheritDoc - */ - public function morphRelationName(): string - { - return 'organization'; - } - - /** - * @inheritDoc - */ - public function canUserManageEntity(User $user, int $role = Role::MANAGER): bool - { - return $user->canManageOrganization($this, $role); - } - - /** - * @param mixed ...$params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - self::VALIDATION_RULES_BASE => [ - 'name' => [ - 'string', - 'max:120', - ], - ], - self::VALIDATION_RULES_CREATE => [ - self::VALIDATION_PREPEND_REQUIRED => [ - 'name', - ], - ], - ]; - } - - /** - * This will return if the message can be received by the specific model - * - * @param Message $message - * @return bool - */ - public function canReceiveMessage(Message $message): bool - { - if (in_array(Message::VIA_SLACK, $message->via ?? [])) { - return $this->getSlackChannel($message) && $this->getSlackKey($message); - } - - return false; - } - - /** - * All message receivers contained within this model - * These related models will be used to send messages when the parent does not - * - * @param Message $message The message being sent in case there is only - * logic connected to returning receivers - * @return Collection - */ - public function messageReceivers(Message $message): Collection - { - return $this->organizationManagers - ->map(fn (OrganizationManager $i) => $i->user); - } - - /** - * Gets the key used to validate access to the related slack workspace - * - * @param Message $message - * @return string|null - */ - public function getSlackKey(Message $message): ?string - { - return $this->slack_key ?? null; - } - - /** - * Gets the slack channel name based on the message passed in - * - * @param Message $message - * @return string|null - */ - public function getSlackChannel(Message $message): ?string - { - return $this->slack_channel ?? null; - } } \ No newline at end of file diff --git a/code/app/Models/Organization/OrganizationManager.php b/code/app/Models/Organization/OrganizationManager.php index 13d87d1b..b353f103 100644 --- a/code/app/Models/Organization/OrganizationManager.php +++ b/code/app/Models/Organization/OrganizationManager.php @@ -3,95 +3,54 @@ namespace App\Models\Organization; -use App\Athenia\Contracts\Models\BelongsToOrganizationContract; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\BelongsToOrganization; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Models\Role; -use App\Models\User\User; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Validation\Rule; +use App\Athenia\Models\Organization\OrganizationManager as AtheniaOrganizationManager; /** * Class OrganizationManager * + * @package App\Models\Organization * @property int $id * @property int $user_id * @property int $organization_id * @property int $role_id * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property-read \App\Models\Organization\Organization $organization * @property-read \App\Models\Role $role * @property-read \App\Models\User\User $user - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Organization\OrganizationManager newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Organization\OrganizationManager newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Organization\OrganizationManager query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\OrganizationManager whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\OrganizationManager whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\OrganizationManager whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\OrganizationManager whereOrganizationId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\OrganizationManager whereRoleId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\OrganizationManager whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Organization\OrganizationManager whereUserId($value) + * @method static \Database\Factories\Organization\OrganizationManagerFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|OrganizationManager onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereOrganizationId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereRoleId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|OrganizationManager whereUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|OrganizationManager withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|OrganizationManager withoutTrashed() * @mixin \Eloquent */ -class OrganizationManager extends BaseModelAbstract implements HasValidationRulesContract, BelongsToOrganizationContract +class OrganizationManager extends AtheniaOrganizationManager { - use HasValidationRules, BelongsToOrganization; - - /** - * The related organization - * - * @return BelongsTo - */ - public function role(): BelongsTo - { - return $this->belongsTo(Role::class); - } - - /** - * The related user - * - * @return BelongsTo - */ - public function user(): BelongsTo - { - return $this->belongsTo(User::class); - } - - /** - * Build the model validation rules - * @param array $params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - static::VALIDATION_RULES_BASE => [ - 'role_id' => [ - 'required', - 'integer', - Rule::in(Role::ENTITY_ROLES), - ], - 'email' => [ - 'string', - 'email', - ], - ], - static::VALIDATION_RULES_CREATE => [ - static::VALIDATION_PREPEND_REQUIRED => [ - 'email', - ], - ], - static::VALIDATION_RULES_UPDATE => [ - static::VALIDATION_PREPEND_NOT_PRESENT => [ - 'email', - ], - ], - ]; - } } \ No newline at end of file diff --git a/code/app/Models/Payment/LineItem.php b/code/app/Models/Payment/LineItem.php index 44cebae0..0c6fbc64 100644 --- a/code/app/Models/Payment/LineItem.php +++ b/code/app/Models/Payment/LineItem.php @@ -3,56 +3,55 @@ namespace App\Models\Payment; -use App\Athenia\Models\BaseModelAbstract; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\MorphTo; +use App\Athenia\Models\Payment\LineItem as AtheniaLineItem; /** - * Class PurchasedItem + * Class LineItem * + * @package App\Models\Payment * @property int $id * @property int $payment_id * @property int|null $item_id * @property string $item_type * @property float $amount * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at - * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $item + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent|null $item * @property-read \App\Models\Payment\Payment $payment - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Payment\LineItem newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Payment\LineItem newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Payment\LineItem query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\LineItem whereAmount($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\LineItem whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\LineItem whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\LineItem whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\LineItem whereItemId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\LineItem whereItemType($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\LineItem wherePaymentId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\LineItem whereUpdatedAt($value) + * @method static \Database\Factories\Payment\LineItemFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|LineItem onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereAmount($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereItemId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereItemType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem wherePaymentId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|LineItem whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|LineItem withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|LineItem withoutTrashed() * @mixin \Eloquent */ -class LineItem extends BaseModelAbstract +class LineItem extends AtheniaLineItem { - /** - * The item that was purchased - * - * @return MorphTo - */ - public function item(): MorphTo - { - return $this->morphTo(); - } - - /** - * The payment this purchased item is related to - * - * @return BelongsTo - */ - public function payment(): BelongsTo - { - return $this->belongsTo(Payment::class); - } } \ No newline at end of file diff --git a/code/app/Models/Payment/Payment.php b/code/app/Models/Payment/Payment.php index 4d7fe393..98b7e81a 100644 --- a/code/app/Models/Payment/Payment.php +++ b/code/app/Models/Payment/Payment.php @@ -3,81 +3,61 @@ namespace App\Models\Payment; -use App\Athenia\Models\BaseModelAbstract; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\MorphTo; +use App\Athenia\Models\Payment\Payment as AtheniaPayment; /** * Class Payment * + * @package App\Models\Payment * @property int $id * @property int $payment_method_id * @property float $amount * @property string|null $transaction_key * @property \Illuminate\Support\Carbon|null $refunded_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $deleted_at * @property int|null $owner_id * @property string|null $owner_type - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Payment\LineItem[] $lineItems + * @property-read \Illuminate\Database\Eloquent\Collection $lineItems * @property-read int|null $line_items_count - * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $owner + * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent|null $owner * @property-read \App\Models\Payment\PaymentMethod $paymentMethod - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Payment\Payment newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Payment\Payment newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Payment\Payment query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\Payment whereAmount($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\Payment whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\Payment whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\Payment whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\Payment whereOwnerId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\Payment whereOwnerType($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\Payment wherePaymentMethodId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\Payment whereRefundedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\Payment whereTransactionKey($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\Payment whereUpdatedAt($value) + * @method static \Database\Factories\Payment\PaymentFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Payment onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereAmount($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereOwnerId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereOwnerType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment wherePaymentMethodId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereRefundedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereTransactionKey($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Payment whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Payment withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Payment withoutTrashed() * @mixin \Eloquent */ -class Payment extends BaseModelAbstract +class Payment extends AtheniaPayment { - /** - * @var array All custom dates - */ - protected $casts = [ - 'refunded_at' => 'datetime:c', - 'deleted_at' => 'datetime:c', - ]; - - /** - * The items paid for - * - * @return HasMany - */ - public function lineItems(): HasMany - { - return $this->hasMany(LineItem::class); - } - - /** - * The owner of the payment - * - * @return MorphTo - */ - public function owner(): MorphTo - { - return $this->morphTo(); - } - - /** - * The payment method that this payment was made with - * - * @return BelongsTo - */ - public function paymentMethod(): BelongsTo - { - return $this->belongsTo(PaymentMethod::class); - } } \ No newline at end of file diff --git a/code/app/Models/Payment/PaymentMethod.php b/code/app/Models/Payment/PaymentMethod.php index 5c1229ab..b38ce27b 100644 --- a/code/app/Models/Payment/PaymentMethod.php +++ b/code/app/Models/Payment/PaymentMethod.php @@ -3,169 +3,68 @@ namespace App\Models\Payment; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Models\Subscription\Subscription; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\MorphTo; +use App\Athenia\Models\Payment\PaymentMethod as AtheniaPaymentMethod; /** * Class PaymentMethod * + * @package App\Models\Payment * @property int $id * @property int $owner_id * @property string $owner_type * @property string|null $payment_method_key * @property string $payment_method_type * @property string|null $identifier - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $deleted_at - * @property bool $default + * @property int $default * @property string|null $brand * @property string|null $exp_month * @property string|null $exp_year * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $owner - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Payment\Payment[] $payments + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @property-read int|null $payments_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Subscription\Subscription[] $subscriptions + * @property-read \Illuminate\Database\Eloquent\Collection $subscriptions * @property-read int|null $subscriptions_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Payment\PaymentMethod newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Payment\PaymentMethod newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Payment\PaymentMethod query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod whereBrand($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod whereDefault($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod whereExpMonth($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod whereExpYear($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod whereIdentifier($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod whereOwnerId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod whereOwnerType($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod wherePaymentMethodKey($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod wherePaymentMethodType($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Payment\PaymentMethod whereUpdatedAt($value) + * @method static \Database\Factories\Payment\PaymentMethodFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|PaymentMethod onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereBrand($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereDefault($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereExpMonth($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereExpYear($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereIdentifier($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereOwnerId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereOwnerType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod wherePaymentMethodKey($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod wherePaymentMethodType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PaymentMethod whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|PaymentMethod withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|PaymentMethod withoutTrashed() * @mixin \Eloquent */ -class PaymentMethod extends BaseModelAbstract implements HasValidationRulesContract +class PaymentMethod extends AtheniaPaymentMethod { - use HasValidationRules; - - /** - * All payments that have been made with this payment method - * - * @return HasMany - */ - public function payments(): HasMany - { - return $this->hasMany(Payment::class); - } - - /** - * All subscriptions that renew with this payment method - * - * @return HasMany - */ - public function subscriptions(): HasMany - { - return $this->hasMany(Subscription::class); - } - - /** - * A payment method will have a morph to relation to the owner of the payment method - * - * @return MorphTo - */ - public function owner(): MorphTo - { - return $this->morphTo(); - } - - /** - * Build the model validation rules - * @param array $params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - static::VALIDATION_RULES_BASE => [ - 'token' => [ - 'string', - 'max:120', - ], - 'default' => [ - 'boolean', - ], - ], - static::VALIDATION_RULES_CREATE => [ - static::VALIDATION_PREPEND_REQUIRED => [ - 'token', - ], - ], - static::VALIDATION_RULES_UPDATE => [ - static::VALIDATION_PREPEND_NOT_PRESENT => [ - 'token', - ], - ], - ]; - } - - /** - * Swagger definition below... - * - * @SWG\Definition( - * type="object", - * definition="PaymentMethod", - * @SWG\Property( - * property="id", - * type="integer", - * format="int32", - * description="The primary id of the model", - * readOnly=true - * ), - * @SWG\Property( - * property="created_at", - * type="string", - * format="date-time", - * description="UTC date of the time this was created", - * readOnly=true - * ), - * @SWG\Property( - * property="updated_at", - * type="string", - * format="date-time", - * description="UTC date of the time this was last updated", - * readOnly=true - * ), - * @SWG\Property( - * property="payment_method_key", - * type="string", - * maxLength=120, - * description="The key for the payment method on the remote server", - * ), - * @SWG\Property( - * property="payment_method_type", - * type="string", - * maxLength=120, - * description="The type of payment method this is. This refers to the the payment service.", - * ), - * @SWG\Property( - * property="user_id", - * type="integer", - * format="int32", - * description="The primary id of the user that this payment method is related to", - * readOnly=true - * ), - * @SWG\Property( - * property="user", - * description="The users that this was sent to.", - * type="array", - * @SWG\Items(ref="#/definitions/User") - * ) - * ) - */ -} +} \ No newline at end of file diff --git a/code/app/Models/Resource.php b/code/app/Models/Resource.php index 945c579c..1be8e2d1 100644 --- a/code/app/Models/Resource.php +++ b/code/app/Models/Resource.php @@ -3,43 +3,52 @@ namespace App\Models; -use App\Athenia\Contracts\Models\HasPolicyContract; -use App\Athenia\Models\BaseModelAbstract; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\MorphTo; +use App\Athenia\Models\Resource as AtheniaResource; /** - * App\Models\Resource + * Class Resource * + * @package App\Models * @property int $id * @property string $content * @property int $resource_id * @property string $resource_type * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $resource - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Resource newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Resource newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Resource query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Resource whereContent($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Resource whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Resource whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Resource whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Resource whereResourceId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Resource whereResourceType($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Resource whereUpdatedAt($value) + * @method static \Database\Factories\ResourceFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Resource onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereContent($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereResourceId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereResourceType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Resource whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Resource withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Resource withoutTrashed() * @mixin \Eloquent */ -class Resource extends BaseModelAbstract implements HasPolicyContract +class Resource extends AtheniaResource { - /** - * The database resource this is related to - * - * @return MorphTo - */ - public function resource() : MorphTo - { - return $this->morphTo(); - } -} +} \ No newline at end of file diff --git a/code/app/Models/Role.php b/code/app/Models/Role.php index 97df3df8..4d987927 100644 --- a/code/app/Models/Role.php +++ b/code/app/Models/Role.php @@ -3,109 +3,48 @@ namespace App\Models; -use App\Athenia\Contracts\Models\HasPolicyContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Models\User\User; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use App\Athenia\Models\Role as AtheniaRole; /** * Class Role * + * @package App\Models * @property int $id * @property string $name - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $deleted_at - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\User\User[] $users + * @property-read \Illuminate\Database\Eloquent\Collection $users * @property-read int|null $users_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Role newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Role newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Role query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Role whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Role whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Role whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Role whereName($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Role whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Role onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Role whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Role withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Role withoutTrashed() * @mixin \Eloquent */ -class Role extends BaseModelAbstract implements HasPolicyContract +class Role extends AtheniaRole { - const APP_USER = 1; - const SUPER_ADMIN = 2; - const ARTICLE_VIEWER = 3; - const ARTICLE_EDITOR = 4; - const ADMINISTRATOR = 10; - const MANAGER = 11; - const CONTENT_EDITOR = 100; - const SUPPORT_STAFF = 101; - // Add more roles here start with 100 in order to avoid application collision - - /** - * @var array[string] the roles (usefully mainly for testing I suppose) - */ - const ROLES = [ - self::APP_USER, - self::SUPER_ADMIN, - self::ARTICLE_VIEWER, - self::ARTICLE_EDITOR, - self::CONTENT_EDITOR, - self::SUPPORT_STAFF, - // Add application specific roles here too - ]; - - /** - * All roles that are related to an organization - */ - const ENTITY_ROLES = [ - self::ADMINISTRATOR, - self::MANAGER, - // Add more organization roles here - ]; - - /** - * Has many users - * - * @return BelongsToMany - */ - public function users(): BelongsToMany - { - return $this->belongsToMany(User::class)->withTimestamps(); - } - - /** - * Swagger definition below... - * - * @SWG\Definition( - * type="object", - * definition="Role", - * @SWG\Property( - * property="id", - * type="integer", - * format="int32", - * description="The primary id of the model", - * readOnly=true - * ), - * @SWG\Property( - * property="created_at", - * type="string", - * format="date-time", - * description="UTC date of the time this was created", - * readOnly=true - * ), - * @SWG\Property( - * property="updated_at", - * type="string", - * format="date-time", - * description="UTC date of the time this was last updated", - * readOnly=true - * ), - * @SWG\Property( - * property="name", - * type="string", - * maxLength=32, - * description="The name of the role" - * ), - * ) - */ -} +} \ No newline at end of file diff --git a/code/app/Models/Statistic/Statistic.php b/code/app/Models/Statistic/Statistic.php index d9a691b8..7f475a86 100644 --- a/code/app/Models/Statistic/Statistic.php +++ b/code/app/Models/Statistic/Statistic.php @@ -3,108 +3,59 @@ namespace App\Models\Statistic; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Models\Statistic\TargetStatistic; -use Eloquent; -use Illuminate\Database\Eloquent\Collection; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Support\Carbon; -use Illuminate\Validation\Rule; +use App\Athenia\Models\Statistic\Statistic as AtheniaStatistic; /** * Class Statistic * + * @package App\Models\Statistic * @property int $id - * @property string $type - * @property int $total - * @property Carbon|null $deleted_at - * @property \datetime|null $created_at - * @property \datetime|null $updated_at - * @property string|null $name - * @property bool $public - * @property-read Collection|StatisticFilter[] $statisticFilters + * @property string $name + * @property string $model + * @property string $relation + * @property int $public + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Illuminate\Support\Carbon|null $deleted_at + * @property-read \Illuminate\Database\Eloquent\Collection $filters + * @property-read int|null $filters_count + * @property-read \Illuminate\Database\Eloquent\Collection $statisticFilters * @property-read int|null $statistic_filters_count - * @property-read Collection|TargetStatistic[] $targetStatistics + * @property-read \Illuminate\Database\Eloquent\Collection $targetStatistics * @property-read int|null $target_statistics_count - * @mixin Eloquent + * @method static \Database\Factories\Statistic\StatisticFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Statistic onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereModel($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic wherePublic($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereRelation($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Statistic whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Statistic withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Statistic withoutTrashed() + * @mixin \Eloquent */ -class Statistic extends BaseModelAbstract implements HasValidationRulesContract +class Statistic extends AtheniaStatistic { - use HasValidationRules; - - /** - * The filters that we use to determine what to count - * - * @return HasMany - */ - public function statisticFilters(): HasMany - { - return $this->hasMany(StatisticFilter::class); - } - - /** - * All instances of the target statistics in the system - * - * @return HasMany - */ - public function targetStatistics(): HasMany - { - return $this->hasMany(TargetStatistic::class); - } - - /** - * @inheritDoc - */ - public function buildModelValidationRules(...$params): array - { - return [ - static::VALIDATION_RULES_BASE => [ - 'name' => [ - 'string', - ], - 'model' => [ - 'string', - ], - 'relation' => [ - 'string', - ], - 'public' => [ - 'boolean', - ], - 'statistic_filters' => [ - 'array', - ], - 'statistic_filters.*' => [ - 'array', - ], - 'statistic_filters.*.field' => [ - 'required', - 'string', - ], - 'statistic_filters.*.operator' => [ - 'required', - 'string', - ], - 'statistic_filters.*.value' => [ - 'nullable', - 'string', - ], - ], - static::VALIDATION_RULES_CREATE => [ - static::VALIDATION_PREPEND_REQUIRED => [ - 'name', - 'model', - 'relation', - ], - ], - static::VALIDATION_RULES_UPDATE => [ - static::VALIDATION_PREPEND_NOT_PRESENT => [ - 'model', - 'relation', - ], - ], - ]; - } -} \ No newline at end of file +} \ No newline at end of file diff --git a/code/app/Models/Statistic/StatisticFilter.php b/code/app/Models/Statistic/StatisticFilter.php index 6d720130..d213e578 100644 --- a/code/app/Models/Statistic/StatisticFilter.php +++ b/code/app/Models/Statistic/StatisticFilter.php @@ -3,32 +3,54 @@ namespace App\Models\Statistic; -use App\Athenia\Models\BaseModelAbstract; -use Illuminate\Database\Eloquent\Relations\BelongsTo; +use App\Athenia\Models\Statistic\StatisticFilter as AtheniaStatisticFilter; /** * Class StatisticFilter - * @package App\Models\Statistics * + * @package App\Models\Statistic * @property int $id * @property int $statistic_id * @property string $field * @property string $operator * @property string|null $value - * @property \datetime|null $created_at - * @property \datetime|null $updated_at - * @property \datetime|null $deleted_at - * @property-read Statistic $statistic + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Illuminate\Support\Carbon|null $deleted_at + * @property-read \App\Models\Statistic\Statistic $statistic + * @method static \Database\Factories\Statistic\StatisticFilterFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|StatisticFilter onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereField($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereOperator($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereStatisticId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|StatisticFilter whereValue($value) + * @method static \Illuminate\Database\Eloquent\Builder|StatisticFilter withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|StatisticFilter withoutTrashed() + * @mixin \Eloquent */ -class StatisticFilter extends BaseModelAbstract +class StatisticFilter extends AtheniaStatisticFilter { - /** - * The statistic that this filter belongs to - * - * @return BelongsTo - */ - public function statistic(): BelongsTo - { - return $this->belongsTo(Statistic::class); - } -} \ No newline at end of file +} \ No newline at end of file diff --git a/code/app/Models/Statistic/TargetStatistic.php b/code/app/Models/Statistic/TargetStatistic.php index ce6371da..1e8fd27b 100644 --- a/code/app/Models/Statistic/TargetStatistic.php +++ b/code/app/Models/Statistic/TargetStatistic.php @@ -3,54 +3,59 @@ namespace App\Models\Statistic; -use App\Athenia\Models\BaseModelAbstract; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\MorphTo; +use App\Athenia\Models\Statistic\TargetStatistic as AtheniaTargetStatistic; /** * Class TargetStatistic - * @package App\Models\Statistics + * + * @package App\Models\Statistic * @property int $id - * @property int $target_id - * @property string $target_type * @property int $statistic_id + * @property string $target_type + * @property int $target_id + * @property array|null $result + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Illuminate\Support\Carbon|null $deleted_at * @property float $value - * @property array|null $filters - * @property \Carbon\Carbon|null $created_at - * @property \Carbon\Carbon|null $updated_at - * @property \Carbon\Carbon|null $deleted_at + * @property string|null $filters * @property-read \App\Models\Statistic\Statistic $statistic - * @property-read \Illuminate\Database\Eloquent\Model $target + * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $target + * @method static \Database\Factories\Statistic\TargetStatisticFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|TargetStatistic onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereFilters($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereResult($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereStatisticId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereTargetId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereTargetType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|TargetStatistic whereValue($value) + * @method static \Illuminate\Database\Eloquent\Builder|TargetStatistic withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|TargetStatistic withoutTrashed() + * @mixin \Eloquent */ -class TargetStatistic extends BaseModelAbstract +class TargetStatistic extends AtheniaTargetStatistic { - /** - * The attributes that should be cast. - * - * @var array - */ - protected $casts = [ - 'result' => 'array', - 'value' => 'float', - ]; - - /** - * The target model that this statistic belongs to - * - * @return MorphTo - */ - public function target(): MorphTo - { - return $this->morphTo(); - } - - /** - * The statistic that this belongs to - * - * @return BelongsTo - */ - public function statistic(): BelongsTo - { - return $this->belongsTo(Statistic::class); - } -} \ No newline at end of file +} \ No newline at end of file diff --git a/code/app/Models/Subscription/MembershipPlan.php b/code/app/Models/Subscription/MembershipPlan.php index 012acc16..e51aab2e 100644 --- a/code/app/Models/Subscription/MembershipPlan.php +++ b/code/app/Models/Subscription/MembershipPlan.php @@ -3,265 +3,64 @@ namespace App\Models\Subscription; -use App\Athenia\Contracts\Models\HasPolicyContract; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Models\DiscountCode; -use App\Models\Feature; -use App\Models\Questionnaire\Question; -use Illuminate\Database\Eloquent\Relations\BelongsToMany; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\HasOne; -use Illuminate\Validation\Rule; +use App\Athenia\Models\Subscription\MembershipPlan as AtheniaMembershipPlan; /** - * Class Plan + * Class MembershipPlan * + * @package App\Models\Subscription * @property int $id * @property string $name * @property string $duration * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property string|null $description * @property string $entity_type - * @property bool $default + * @property int $default * @property int|null $trial_period * @property-read \App\Models\Subscription\MembershipPlanRate|null $currentRate - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Feature[] $features + * @property-read \Illuminate\Database\Eloquent\Collection $features * @property-read int|null $features_count * @property-read null|float $current_cost * @property-read null|float $current_rate_id - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Subscription\MembershipPlanRate[] $membershipPlanRates + * @property-read \Illuminate\Database\Eloquent\Collection $membershipPlanRates * @property-read int|null $membership_plan_rates_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Subscription\MembershipPlan newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Subscription\MembershipPlan newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Subscription\MembershipPlan query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlan whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlan whereDefault($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlan whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlan whereDescription($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlan whereDuration($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlan whereEntityType($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlan whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlan whereName($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlan whereTrialPeriod($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlan whereUpdatedAt($value) + * @method static \Database\Factories\Subscription\MembershipPlanFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|MembershipPlan onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereDefault($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereDescription($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereDuration($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereEntityType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereTrialPeriod($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlan whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|MembershipPlan withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|MembershipPlan withoutTrashed() * @mixin \Eloquent */ -class MembershipPlan extends BaseModelAbstract implements HasPolicyContract, HasValidationRulesContract +class MembershipPlan extends AtheniaMembershipPlan { - use HasValidationRules; - - /** - * @var string the enum value for the duration field when the plan only lasts a year - */ - const DURATION_YEAR = 'year'; - - /** - * @var string the enum value for the duration field when the plan only lasts a month - */ - const DURATION_MONTH = 'month'; - - /** - * @var string the enum value for the duration field when the plan lasts forever - */ - const DURATION_LIFETIME = 'lifetime'; - - /** - * The available duration types for a membership plan - */ - const AvailableDurations = [ - MembershipPlan::DURATION_MONTH, - MembershipPlan::DURATION_YEAR, - MembershipPlan::DURATION_LIFETIME, - ]; - - /** - * Values that are appending on a toArray function call - * - * @var array - */ - protected $appends = [ - 'current_cost', - 'current_rate_id', - ]; - - /** - * The current rate for this membership plan - * - * @return HasOne - */ - public function currentRate(): HasOne - { - return $this->hasOne(MembershipPlanRate::class) - ->where('active', true) - ->orderBy('created_at', 'DESC'); - } - - /** - * @return BelongsToMany - */ - public function features(): BelongsToMany - { - return $this->belongsToMany(Feature::class); - } - - /** - * All membership plan rates that have - * - * @return HasMany - */ - public function membershipPlanRates(): HasMany - { - return $this->hasMany(MembershipPlanRate::class); - } - - /** - * Function that creates the current cost attribute - * - * @return null|float - */ - public function getCurrentCostAttribute() - { - return $this->currentRate ? $this->currentRate->cost : null; - } - - /** - * Function that creates the current cost attribute - * - * @return null|float - */ - public function getCurrentRateIdAttribute() - { - return $this->currentRate ? $this->currentRate->id : null; - } - - /** - * Build the model validation rules - * @param array $params Any additional parameters needed - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - self::VALIDATION_RULES_BASE => [ - - 'name' => [ - 'string', - 'max:120', - ], - - 'entity_type' => [ - 'string', - Rule::in([ - 'user', - 'organization', - ]), - ], - - 'description' => [ - 'string', - ], - - 'current_cost' => [ - 'numeric', - 'min:0.00', - 'max:999999.99', - ], - - 'duration' => [ - 'string', - Rule::in(MembershipPlan::AvailableDurations), - ], - - 'trial_period' => [ - 'nullable', - 'integer', - 'min:0', - ], - - 'default' => [ - 'boolean', - ], - - 'features' => [ - 'array', - ], - - 'features.*' => [ - 'numeric', - Rule::exists('features', 'id'), - ], - ], - self::VALIDATION_RULES_CREATE => [ - self::VALIDATION_PREPEND_REQUIRED => [ - 'name', - 'entity_type', - 'current_cost', - 'duration', - ], - ], - self::VALIDATION_RULES_UPDATE => [ - self::VALIDATION_PREPEND_NOT_PRESENT => [ - 'entity_type', - 'duration', - ], - ], - ]; - } - - /** - * Swagger definition below - * - * @SWG\Definition ( - * type="object", - * definition="MembershipPlan", - * description="The details of a membership plan", - * @SWG\Property( - * property="id", - * type="integer", - * format="int32", - * readOnly=true - * ), - * @SWG\Property( - * property="created_at", - * type="string", - * format="date-time", - * description="UTC date of the time this was created", - * readOnly=true - * ), - * @SWG\Property( - * property="updated_at", - * type="string", - * format="date-time", - * description="UTC date of the time this was updated", - * readOnly=true - * ), - * @SWG\Property( - * property="current_cost", - * type="number", - * description="The current cost of the membership plan" - * ), - * @SWG\Property( - * property="current_rate_id", - * type="number", - * readonly=true, - * description="The current id of the membership plan rate" - * ), - * @SWG\Property( - * property="duration", - * type="string", - * maxLength=128, - * description="The duration for this membership plan" - * ), - * @SWG\Property( - * property="subscriptions", - * description="The subscriptions attatched to this membership plan", - * type="array", - * @SWG\Items(ref="#/definitions/Subscription") - * ), - * ) - */ -} +} \ No newline at end of file diff --git a/code/app/Models/Subscription/MembershipPlanRate.php b/code/app/Models/Subscription/MembershipPlanRate.php index 0e9a5a44..f727be88 100644 --- a/code/app/Models/Subscription/MembershipPlanRate.php +++ b/code/app/Models/Subscription/MembershipPlanRate.php @@ -3,55 +3,54 @@ namespace App\Models\Subscription; -use App\Athenia\Models\BaseModelAbstract; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasMany; +use App\Athenia\Models\Subscription\MembershipPlanRate as AtheniaMembershipPlanRate; /** * Class MembershipPlanRate * + * @package App\Models\Subscription * @property int $id * @property int $membership_plan_id * @property float $cost - * @property bool $active + * @property int $active * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property-read \App\Models\Subscription\MembershipPlan $membershipPlan - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Subscription\Subscription[] $subscriptions + * @property-read \Illuminate\Database\Eloquent\Collection $subscriptions * @property-read int|null $subscriptions_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Subscription\MembershipPlanRate newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Subscription\MembershipPlanRate newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Subscription\MembershipPlanRate query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlanRate whereActive($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlanRate whereCost($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlanRate whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlanRate whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlanRate whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlanRate whereMembershipPlanId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\MembershipPlanRate whereUpdatedAt($value) + * @method static \Database\Factories\Subscription\MembershipPlanRateFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|MembershipPlanRate onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereActive($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereCost($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereMembershipPlanId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|MembershipPlanRate whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|MembershipPlanRate withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|MembershipPlanRate withoutTrashed() * @mixin \Eloquent */ -class MembershipPlanRate extends BaseModelAbstract +class MembershipPlanRate extends AtheniaMembershipPlanRate { - /** - * The membership plan this is related to - * - * @return BelongsTo - */ - public function membershipPlan(): BelongsTo - { - return $this->belongsTo(MembershipPlan::class); - } - - /** - * All subscriptions that have been signed up for this membership plan rate - * - * @return HasMany - */ - public function subscriptions(): HasMany - { - return $this->hasMany(Subscription::class); - } } \ No newline at end of file diff --git a/code/app/Models/Subscription/Subscription.php b/code/app/Models/Subscription/Subscription.php index 80fa6ca2..f37eab5f 100644 --- a/code/app/Models/Subscription/Subscription.php +++ b/code/app/Models/Subscription/Subscription.php @@ -3,195 +3,74 @@ namespace App\Models\Subscription; -use App\Athenia\Contracts\Models\HasPaymentsContract; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasPayments; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Athenia\Validators\Subscription\MembershipPlanRateIsActiveValidator; -use App\Athenia\Validators\Subscription\PaymentMethodIsOwnedByEntityValidator; -use App\Models\Payment\PaymentMethod; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\MorphTo; -use Illuminate\Validation\Rule; +use App\Athenia\Models\Subscription\Subscription as AtheniaSubscription; /** * Class Subscription * + * @package App\Models\Subscription * @property int $id * @property int $membership_plan_rate_id * @property int $payment_method_id * @property int $subscriber_id * @property string $subscriber_type - * @property mixed|null $last_renewed_at - * @property mixed|null $subscribed_at - * @property mixed|null $expires_at - * @property mixed|null $canceled_at - * @property bool $recurring + * @property \Illuminate\Support\Carbon|null $last_renewed_at + * @property \Illuminate\Support\Carbon|null $subscribed_at + * @property \Illuminate\Support\Carbon|null $expires_at + * @property \Illuminate\Support\Carbon|null $canceled_at + * @property int $recurring * @property \Illuminate\Support\Carbon|null $deleted_at * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at - * @property bool $is_trial + * @property int $is_trial * @property-read null|string $formatted_cost * @property-read null|string $formatted_expires_at - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Payment\LineItem[] $lineItems + * @property-read \Illuminate\Database\Eloquent\Collection $lineItems * @property-read int|null $line_items_count * @property-read \App\Models\Subscription\MembershipPlanRate $membershipPlanRate * @property-read \App\Models\Payment\PaymentMethod $paymentMethod - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Payment\Payment[] $payments + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @property-read int|null $payments_count * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $subscriber - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Subscription\Subscription newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Subscription\Subscription newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Subscription\Subscription query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereCanceledAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereExpiresAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereIsTrial($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereLastRenewedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereMembershipPlanRateId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription wherePaymentMethodId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereRecurring($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereSubscribedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereSubscriberId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereSubscriberType($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Subscription\Subscription whereUpdatedAt($value) + * @method static \Database\Factories\Subscription\SubscriptionFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Subscription onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereCanceledAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereExpiresAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereIsTrial($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereLastRenewedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereMembershipPlanRateId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription wherePaymentMethodId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereRecurring($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereSubscribedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereSubscriberId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereSubscriberType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Subscription whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Subscription withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Subscription withoutTrashed() * @mixin \Eloquent */ -class Subscription extends BaseModelAbstract implements HasValidationRulesContract, HasPaymentsContract +class Subscription extends AtheniaSubscription { - use HasValidationRules, HasPayments; - - /** - * The attributes that should be cast to native types. - * - * @var array - */ - protected $casts = [ - 'last_renewed_at' => 'datetime:c', - 'subscribed_at' => 'datetime:c', - 'expires_at' => 'datetime:c', - 'canceled_at' => 'datetime:c', - ]; - - /** - * The membership plan rate this subscription is signed up for - * - * @return BelongsTo - */ - public function membershipPlanRate(): BelongsTo - { - return $this->belongsTo(MembershipPlanRate::class); - } - - /** - * The payment method that is used to renew this subscription - * - * @return BelongsTo - */ - public function paymentMethod(): BelongsTo - { - return $this->belongsTo(PaymentMethod::class); - } - - /** - * The subscriber this subscription is for - * - * @return MorphTo - */ - public function subscriber(): MorphTo - { - return $this->morphTo(); - } - - /** - * @inheritDoc - */ - public function morphRelationName(): string - { - return 'subscription'; - } - - /** - * Determines whether or not this subscription is good for a lifetime - * - * @return bool - */ - public function isLifetime() : bool - { - return $this->membershipPlanRate->membershipPlan->duration == MembershipPlan::DURATION_LIFETIME; - } - - /** - * Formats the expires at date string - * - * @return null|string - */ - public function getFormattedExpiresAtAttribute() - { - return $this->expires_at ? $this->expires_at->format('F jS Y') : null; - } - - /** - * Formats the cost to be human readable - * - * @return null|string - */ - public function getFormattedCostAttribute() - { - return $this->membershipPlanRate && $this->membershipPlanRate->cost ? - number_format((float)$this->membershipPlanRate->cost, 2) : null; - } - - /** - * Build the model validation rules - * @param array $params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - self::VALIDATION_RULES_BASE => [ - 'cancel' => [ - 'boolean', - ], - 'membership_plan_rate_id' => [ - 'integer', - Rule::exists('membership_plan_rates', 'id'), - MembershipPlanRateIsActiveValidator::KEY, - ], - 'payment_method_id' => [ - 'integer', - Rule::exists('payment_methods', 'id'), - PaymentMethodIsOwnedByEntityValidator::KEY, - ], - 'is_trial' => [ - 'boolean', - ], - 'recurring' => [ - 'boolean', - ], - ], - self::VALIDATION_RULES_CREATE => [ - self::VALIDATION_PREPEND_REQUIRED_UNLESS . 'is_trial,true' => [ - 'payment_method_id', - ], - self::VALIDATION_PREPEND_REQUIRED => [ - 'membership_plan_rate_id', - ], - self::VALIDATION_PREPEND_NOT_PRESENT => [ - 'cancel', - ], - ], - self::VALIDATION_RULES_UPDATE => [ - self::VALIDATION_PREPEND_NOT_PRESENT => [ - 'membership_plan_rate_id', - 'is_trial', - ], - ], - ]; - } -} +} \ No newline at end of file diff --git a/code/app/Models/User/ArticleNote.php b/code/app/Models/User/ArticleNote.php new file mode 100644 index 00000000..1bc7f427 --- /dev/null +++ b/code/app/Models/User/ArticleNote.php @@ -0,0 +1,57 @@ +|ArticleNote getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ArticleNote onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereArticleId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereCompletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereResponse($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleNote whereUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleNote withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleNote withoutTrashed() + * @mixin \Eloquent + */ +class ArticleNote extends AtheniaArticleNote +{ +} diff --git a/code/app/Models/User/Contact.php b/code/app/Models/User/Contact.php index b78935d7..96106185 100644 --- a/code/app/Models/User/Contact.php +++ b/code/app/Models/User/Contact.php @@ -3,104 +3,55 @@ namespace App\Models\User; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasValidationRules; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Validation\Rule; +use App\Athenia\Models\User\Contact as AtheniaContact; /** * Class Contact * + * @package App\Models\User * @property int $id * @property int $initiated_by_id * @property int $requested_id * @property \Illuminate\Support\Carbon|null $confirmed_at * @property \Illuminate\Support\Carbon|null $denied_at * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property-read \App\Models\User\User $initiatedBy * @property-read \App\Models\User\User $requested - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\Contact newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\Contact newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\Contact query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Contact whereConfirmedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Contact whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Contact whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Contact whereDeniedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Contact whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Contact whereInitiatedById($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Contact whereRequestedId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\Contact whereUpdatedAt($value) + * @method static \Database\Factories\User\ContactFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Contact onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereConfirmedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereDeniedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereInitiatedById($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereRequestedId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Contact whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Contact withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Contact withoutTrashed() * @mixin \Eloquent */ -class Contact extends BaseModelAbstract implements HasValidationRulesContract +class Contact extends AtheniaContact { - use HasValidationRules; - - /** - * @var array - */ - protected $casts = [ - 'confirmed_at' => 'datetime', - 'denied_at' => 'datetime', - ]; - - /** - * @return BelongsTo - */ - public function initiatedBy() : BelongsTo - { - return $this->belongsTo(User::class, 'initiated_by_id'); - } - - /** - * @return BelongsTo - */ - public function requested() : BelongsTo - { - return $this->belongsTo(User::class, 'requested_id'); - } - - /** - * Build the model validation rules - * @param array $params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - static::VALIDATION_RULES_BASE => [ - 'initiated_by_id' => [ - 'not_present', - ], - 'requested_id' => [ - 'integer', - Rule::exists('users', 'id'), - ], - 'deny' => [ - 'boolean', - ], - 'confirm' => [ - 'boolean', - ], - ], - static::VALIDATION_RULES_CREATE => [ - static::VALIDATION_PREPEND_REQUIRED => [ - 'requested_id', - ], - static::VALIDATION_PREPEND_NOT_PRESENT => [ - 'deny', - 'confirm', - ], - ], - static::VALIDATION_RULES_UPDATE => [ - static::VALIDATION_PREPEND_NOT_PRESENT => [ - 'requested_id', - ], - ], - ]; - } } \ No newline at end of file diff --git a/code/app/Models/User/InvitationToken.php b/code/app/Models/User/InvitationToken.php new file mode 100644 index 00000000..2bbb6198 --- /dev/null +++ b/code/app/Models/User/InvitationToken.php @@ -0,0 +1,54 @@ +|InvitationToken getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|InvitationToken onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereToken($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereRoleId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereUsedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|InvitationToken whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|InvitationToken withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|InvitationToken withoutTrashed() + * @mixin \Eloquent + */ +class InvitationToken extends AtheniaInvitationToken +{ +} diff --git a/code/app/Models/User/PasswordToken.php b/code/app/Models/User/PasswordToken.php index a42c0a94..750986fc 100644 --- a/code/app/Models/User/PasswordToken.php +++ b/code/app/Models/User/PasswordToken.php @@ -3,60 +3,50 @@ namespace App\Models\User; -use App\Athenia\Models\BaseModelAbstract; -use Illuminate\Database\Eloquent\Relations\BelongsTo; +use App\Athenia\Models\User\PasswordToken as AtheniaPasswordToken; /** * Class PasswordToken * + * @package App\Models\User * @property int $id * @property int $user_id * @property string $token * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property-read \App\Models\User\User $user - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\PasswordToken newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\PasswordToken newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\PasswordToken query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\PasswordToken whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\PasswordToken whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\PasswordToken whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\PasswordToken whereToken($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\PasswordToken whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\PasswordToken whereUserId($value) + * @method static \Database\Factories\User\PasswordTokenFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|PasswordToken onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken whereToken($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|PasswordToken whereUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|PasswordToken withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|PasswordToken withoutTrashed() * @mixin \Eloquent */ -class PasswordToken extends BaseModelAbstract +class PasswordToken extends AtheniaPasswordToken { - /** - * The user relation to the user that generated this token - * - * @return BelongsTo - */ - public function user() : BelongsTo - { - return $this->belongsTo(User::class); - } - - /** - * Swagger definition below for a password token... - * - * @SWG\Definition( - * type="object", - * definition="PasswordToken", - * @SWG\Property( - * property="token", - * type="string", - * maxLength=120, - * description="The token that was generated." - * ), - * @SWG\Property( - * property="email", - * type="string", - * maxLength=120, - * description="The email address of the user the token is associated with." - * ), - * ) - */ } \ No newline at end of file diff --git a/code/app/Models/User/ProfileImage.php b/code/app/Models/User/ProfileImage.php index ff6dfe0b..987baab5 100644 --- a/code/app/Models/User/ProfileImage.php +++ b/code/app/Models/User/ProfileImage.php @@ -3,55 +3,65 @@ namespace App\Models\User; -use App\Models\Asset; -use App\Models\Organization\Organization; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\HasOne; +use App\Athenia\Models\User\ProfileImage as AtheniaProfileImage; /** * Class ProfileImage * + * @package App\Models\User * @property int $id * @property int|null $owner_id * @property string|null $name * @property string|null $caption * @property string $url * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property string|null $owner_type + * @property string|null $source + * @property string|null $alt + * @property int $width + * @property int $height * @property-read \App\Models\Organization\Organization|null $organization - * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $owner + * @property-read ProfileImage|null $owner * @property-read \App\Models\User\User|null $user - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\ProfileImage newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\ProfileImage newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\ProfileImage query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\ProfileImage whereCaption($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\ProfileImage whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\ProfileImage whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\ProfileImage whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\ProfileImage whereName($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\ProfileImage whereOwnerId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\ProfileImage whereOwnerType($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\ProfileImage whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\ProfileImage whereUrl($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ProfileImage onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereAlt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereCaption($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereHeight($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereOwnerId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereOwnerType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereSource($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereUrl($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ProfileImage whereWidth($value) + * @method static \Illuminate\Database\Eloquent\Builder|ProfileImage withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|ProfileImage withoutTrashed() * @mixin \Eloquent */ -class ProfileImage extends Asset +class ProfileImage extends AtheniaProfileImage { - /** - * @return HasOne - */ - public function organization(): HasOne - { - return $this->hasOne(Organization::class); - } - - /** - * @return HasOne - */ - public function user(): HasOne - { - return $this->hasOne(User::class); - } -} +} \ No newline at end of file diff --git a/code/app/Models/User/User.php b/code/app/Models/User/User.php index 8f9bf8ef..0545b9e5 100644 --- a/code/app/Models/User/User.php +++ b/code/app/Models/User/User.php @@ -3,454 +3,98 @@ namespace App\Models\User; -use App\Athenia\Contracts\Models\CanBeIndexedContract; -use App\Athenia\Contracts\Models\HasPolicyContract; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Contracts\Models\IsAnEntityContract; -use App\Athenia\Contracts\Models\Messaging\CanReceiveEmailsContract; -use App\Athenia\Contracts\Models\Messaging\CanReceiveMessageContract; -use App\Athenia\Contracts\Models\Messaging\CanReceivePushNotificationContract; -use App\Athenia\Contracts\Models\Messaging\CanReceiveSMSContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\CanBeIndexed; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Athenia\Models\Traits\IsEntity; -use App\Models\Asset; -use App\Models\Messaging\Message; -use App\Models\Messaging\PushNotificationKey; -use App\Models\Messaging\Thread; -use App\Models\Organization\Organization; -use App\Models\Organization\OrganizationManager; -use App\Models\Resource; -use App\Models\Role; -use App\Models\Vote\BallotCompletion; -use App\Models\Wiki\Article; -use App\Models\Wiki\ArticleIteration; -use Illuminate\Auth\Authenticatable; -use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\BelongsToMany; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\MorphMany; -use Illuminate\Database\Eloquent\Relations\MorphOne; -use Illuminate\Validation\Rule; -use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject; +use App\Athenia\Models\User\User as AtheniaUser; /** * Class User * + * @package App\Models\User * @property int $id * @property int|null $merged_to_id * @property string|null $stripe_customer_key * @property string $email * @property string|null $first_name - * @property string $password - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property string|null $password + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $deleted_at - * @property bool $allow_users_to_add_me - * @property bool $receive_push_notifications + * @property int $allow_users_to_add_me + * @property int $receive_push_notifications * @property string|null $about_me * @property string|null $push_notification_key * @property int|null $profile_image_id * @property string|null $last_name - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Asset[] $assets + * @property-read \Illuminate\Database\Eloquent\Collection $articleNotes + * @property-read int|null $article_notes_count + * @property-read \Illuminate\Database\Eloquent\Collection $assets * @property-read int|null $assets_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Vote\BallotCompletion[] $ballotCompletions + * @property-read \Illuminate\Database\Eloquent\Collection $ballotCompletions * @property-read int|null $ballot_completions_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Wiki\Article[] $createdArticles + * @property-read \Illuminate\Database\Eloquent\Collection $collections + * @property-read int|null $collections_count + * @property-read \Illuminate\Database\Eloquent\Collection $createdArticles * @property-read int|null $created_articles_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Wiki\ArticleIteration[] $createdIterations + * @property-read \Illuminate\Database\Eloquent\Collection $createdIterations * @property-read int|null $created_iterations_count * @property-read null|string $profile_image_url - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Messaging\Message[] $messages + * @property-read \Illuminate\Database\Eloquent\Collection $messages * @property-read int|null $messages_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Organization\OrganizationManager[] $organizationManagers + * @property-read \Illuminate\Database\Eloquent\Collection $organizationManagers * @property-read int|null $organization_managers_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Payment\PaymentMethod[] $paymentMethods + * @property-read \Illuminate\Database\Eloquent\Collection $paymentMethods * @property-read int|null $payment_methods_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Payment\Payment[] $payments + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @property-read int|null $payments_count - * @property-read \App\Models\User\ProfileImage|null $profileImage + * @property-read \App\Athenia\Models\User\ProfileImage|null $profileImage + * @property-read \Illuminate\Database\Eloquent\Collection $pushNotificationKeys + * @property-read int|null $push_notification_keys_count * @property-read \App\Models\Resource|null $resource - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Role[] $roles + * @property-read \Illuminate\Database\Eloquent\Collection $roles * @property-read int|null $roles_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Subscription\Subscription[] $subscriptions + * @property-read \Illuminate\Database\Eloquent\Collection $subscriptions * @property-read int|null $subscriptions_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Messaging\Thread[] $threads + * @property-read \Illuminate\Database\Eloquent\Collection $threads * @property-read int|null $threads_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\User newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\User newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\User\User query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereAboutMe($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereAllowUsersToAddMe($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereEmail($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereFirstName($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereLastName($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereMergedToId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User wherePassword($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereProfileImageId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User wherePushNotificationKey($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereReceivePushNotifications($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereStripeCustomerKey($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\User\User whereUpdatedAt($value) + * @method static \Database\Factories\User\UserFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|User onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereAboutMe($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereAllowUsersToAddMe($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereEmail($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereFirstName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereLastName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereMergedToId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User wherePassword($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereProfileImageId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User wherePushNotificationKey($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereReceivePushNotifications($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereStripeCustomerKey($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|User whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|User withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|User withoutTrashed() * @mixin \Eloquent */ -class User extends BaseModelAbstract - implements AuthenticatableContract, JWTSubject, - HasPolicyContract, HasValidationRulesContract, - CanBeIndexedContract, IsAnEntityContract, CanReceiveMessageContract, - CanReceiveEmailsContract, CanReceivePushNotificationContract, CanReceiveSMSContract +class User extends AtheniaUser { - use Authenticatable, HasValidationRules, IsEntity, CanBeIndexed; - - /** - * The attributes excluded from the model's JSON form. - * - * @var array - */ - protected $hidden = [ - 'deleted_at', - 'password', - ]; - - /** - * The url of the profile image - * - * @var array - */ - protected $appends = [ - 'profile_image_url', - ]; - - /** - * All assets this user has created - * - * @return MorphMany - */ - public function assets(): MorphMany - { - return $this->morphMany(Asset::class, 'owner'); - } - - /** - * The ballot completions the user has done - * - * @return HasMany - */ - public function ballotCompletions(): HasMany - { - return $this->hasMany(BallotCompletion::class); - } - - /** - * The articles that were created by this user - * - * @return HasMany - */ - public function createdArticles(): HasMany - { - return $this->hasMany(Article::class, 'created_by_id'); - } - - /** - * The iterations that were created by this user - * - * @return HasMany - */ - public function createdIterations(): HasMany - { - return $this->hasMany(ArticleIteration::class, 'created_by_id'); - } - - /** - * The messages that were sent to a user - * - * @return HasMany - */ - public function messages(): HasMany - { - return $this->hasMany(Message::class, 'to_id'); - } - - /** - * All organization manager relations this user has - * - * @return HasMany - */ - public function organizationManagers(): HasMany - { - return $this->hasMany(OrganizationManager::class); - } - - /** - * The push notification keys that the push notification should be sent to - * - * @return HasMany - */ - public function pushNotificationKeys(): HasMany - { - return $this->hasMany(PushNotificationKey::class); - } - - /** - * The asset that contains the profile image for this user - * - * @return BelongsTo - */ - public function profileImage() : BelongsTo - { - return $this->belongsTo(ProfileImage::class); - } - - /** - * The resource object for this user - * - * @return MorphOne - */ - public function resource() : MorphOne - { - return $this->morphOne(Resource::class, 'resource'); - } - - /** - * What roles this user has - * - * @return BelongsToMany - */ - public function roles(): BelongsToMany - { - return $this->belongsToMany(Role::class); - } - - /** - * Any threads this user is apart of - * - * @return BelongsToMany - */ - public function threads(): BelongsToMany - { - return $this->belongsToMany(Thread::class); - } - - /** - * Add a Role to this user - * - * @param int $roleId - * @return $this - */ - public function addRole(int $roleId) - { - $this->roles()->attach($roleId); - return $this; - } - - /** - * Does this have the role - * - * @param mixed $roles - * @return bool - */ - public function hasRole($roles) - { - $roles = (array)$roles; - return $this->roles()->whereIn('id', $roles)->exists(); - } - - /** - * Add a Role to this user - * - * @param int $roleId - * @return $this - */ - public function removeRole(int $roleId) - { - $this->roles()->detach($roleId); - return $this; - } - - /** - * Determines whether or not the user can manage the organization. - * - * @param Organization $organization - * @param int|array $role - * If the manager role is passed in then this will return true for both the manager role and admin role. - * The admin role will only check for the admin role. - * @return bool - */ - public function canManageOrganization(Organization $organization, $role = Role::MANAGER): bool - { - $roles = is_array($role) ? $role : [$role]; - if (!in_array(Role::ADMINISTRATOR, $roles)) { - $roles[] = Role::ADMINISTRATOR; - } - return $this->organizationManagers->first(fn (OrganizationManager $organizationManager) => - in_array($organizationManager->role_id, $roles) && $organizationManager->organization_id === $organization->id - ) != null; - } - - /** - * Get the URL for the profile image - * - * @return null|string - */ - public function getProfileImageUrlAttribute(): string|null - { - return $this->profileImage?->url; - } - - /** - * @inheritDoc - */ - public function canUserManageEntity(User $user, int $role = null): bool - { - return $this->id == $user->id; - } - - /** - * This will return if the message can be received by the specific model - * - * @param Message $message - * @return bool - */ - public function canReceiveMessage(Message $message): bool - { - foreach ($message->via ?? [] as $via) { - switch ($via) { - case Message::VIA_EMAIL: - return true; - case Message::VIA_PUSH_NOTIFICATION: - return !!$this->pushNotificationKeys->count(); - case Message::VIA_SMS: - return !!$this->getPhoneNumber(); - } - } - - return false; - } - - /** - * The email address to send the email to - * - * @return string - */ - public function getEmailAddress(): string - { - return $this->email; - } - - /** - * The name of the person to be added as the to field - * - * @return string - */ - public function getEmailToName(): string - { - return $this->first_name . ' ' . $this->last_name; - } - - /** - * Gets the phone number for routing SMS messages - * - * @return string|null - */ - public function getPhoneNumber(): ?string - { - return $this->phone ?? null; - } - - /** - * The name of the morph relation - * - * @return string - */ - public function morphRelationName(): string - { - return 'user'; - } - - /** - * Gets the content string to index - * - * @return string - */ - public function getContentString(): ?string - { - return $this->first_name . ' ' . $this->last_name; - } - - /** - * Get the identifier that will be stored in the subject claim of the JWT. - * - * @return mixed - */ - public function getJWTIdentifier() - { - return $this->id; - } - - /** - * Return a key value array, containing any custom claims to be added to the JWT. - * - * @return array - */ - public function getJWTCustomClaims() - { - return []; - } - - /** - * Build the model validation rules - * @param array $params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - $emailUnique = Rule::unique('users', 'email'); - - $userId = count($params) ? $params[0]->id : null; - - if ($userId) { - $emailUnique->ignore($userId); - } - - return [ - static::VALIDATION_RULES_BASE => [ - 'email' => [ - 'string', - 'max:120', - 'email', - $emailUnique, - ], - 'first_name' => [ - 'string', - 'max:120', - ], - 'last_name' => [ - 'string', - 'max:120', - ], - 'password' => [ - 'string', - 'min:6', - ], - 'push_notification_key' => [ - 'string', - 'max:512' - ], - 'about_me' => [ - 'string', - ], - 'allow_users_to_add_me' => [ - 'boolean', - ], - 'receive_push_notifications' => [ - 'boolean', - ], - ], - ]; - } -} +} \ No newline at end of file diff --git a/code/app/Models/Vote/Ballot.php b/code/app/Models/Vote/Ballot.php index 401f1511..60a70104 100644 --- a/code/app/Models/Vote/Ballot.php +++ b/code/app/Models/Vote/Ballot.php @@ -3,68 +3,53 @@ namespace App\Models\Vote; -use App\Athenia\Models\BaseModelAbstract; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\HasMany; +use App\Athenia\Models\Vote\Ballot as AtheniaBallot; /** * Class Ballot * + * @package App\Models\Vote * @property int $id * @property string|null $name * @property string $type * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Vote\BallotCompletion[] $ballotCompletions + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \Illuminate\Database\Eloquent\Collection $ballotCompletions * @property-read int|null $ballot_completions_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Vote\BallotItem[] $ballotItems + * @property-read \Illuminate\Database\Eloquent\Collection $ballotItems * @property-read int|null $ballot_items_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\Ballot newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\Ballot newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\Ballot query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Ballot whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Ballot whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Ballot whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Ballot whereName($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Ballot whereType($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Ballot whereUpdatedAt($value) + * @method static \Database\Factories\Vote\BallotFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Ballot onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot whereType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Ballot whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Ballot withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Ballot withoutTrashed() * @mixin \Eloquent */ -class Ballot extends BaseModelAbstract +class Ballot extends AtheniaBallot { - /** - * This ballot type when there is a single subject and the user chooses yes or no - */ - const TYPE_SINGLE_OPTION = 'single_option'; - - /** - * The ballot type for when there will be multiple options within each item - */ - const TYPE_MULTIPLE_OPTIONS = 'multiple_options'; - - /** - * The ballot type for when we allow the user to rank their options - */ - const TYPE_RANKED_CHOICE = 'ranked_choice'; - - /** - * All times someone has completed this ballot - * - * @return HasMany - */ - public function ballotCompletions(): HasMany - { - return $this->hasMany(BallotCompletion::class); - } - - /** - * All subjects contained in this ballot - * - * @return HasMany - */ - public function ballotItems(): HasMany - { - return $this->hasMany(BallotItem::class); - } -} +} \ No newline at end of file diff --git a/code/app/Models/Vote/BallotCompletion.php b/code/app/Models/Vote/BallotCompletion.php index aebbd578..2953f585 100644 --- a/code/app/Models/Vote/BallotCompletion.php +++ b/code/app/Models/Vote/BallotCompletion.php @@ -3,99 +3,57 @@ namespace App\Models\Vote; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Models\User\User; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Validation\Rule; +use App\Athenia\Models\Vote\BallotCompletion as AtheniaBallotCompletion; /** * Class BallotCompletion * + * @package App\Models\Vote * @property int $id * @property int $ballot_id * @property int $user_id + * @property string|null $completed_at + * @property string|null $response * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at - * @property-read \App\Models\Vote\Ballot $ballot + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \App\Athenia\Models\Vote\Ballot $ballot * @property-read \App\Models\User\User $user - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Vote\Vote[] $votes + * @property-read \Illuminate\Database\Eloquent\Collection $votes * @property-read int|null $votes_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\BallotCompletion newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\BallotCompletion newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\BallotCompletion query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotCompletion whereBallotId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotCompletion whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotCompletion whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotCompletion whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotCompletion whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotCompletion whereUserId($value) + * @method static \Database\Factories\Vote\BallotCompletionFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|BallotCompletion onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereBallotId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereCompletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereResponse($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotCompletion whereUserId($value) + * @method static \Illuminate\Database\Eloquent\Builder|BallotCompletion withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|BallotCompletion withoutTrashed() * @mixin \Eloquent */ -class BallotCompletion extends BaseModelAbstract implements HasValidationRulesContract +class BallotCompletion extends AtheniaBallotCompletion { - use HasValidationRules; - - /** - * The ballot that was completed - * - * @return BelongsTo - */ - public function ballot(): BelongsTo - { - return $this->belongsTo(Ballot::class); - } - - /** - * THe user that completed this ballot - * - * @return BelongsTo - */ - public function user(): BelongsTo - { - return $this->belongsTo(User::class); - } - - /** - * All votes that were cat in this ballot - * - * @return HasMany - */ - public function votes(): HasMany - { - return $this->hasMany(Vote::class); - } - - /** - * Build the model validation rules - * @param array $params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - static::VALIDATION_RULES_BASE => [ - 'votes' => [ - 'required', - 'array', - ], - 'votes.*' => [ - 'array', - ], - 'votes.*.result' => [ - 'required', - 'integer', - ], - 'votes.*.ballot_item_option_id' => [ - 'required', - 'integer', - Rule::exists('ballot_item_options', 'id'), - ], - ] - ]; - } -} +} \ No newline at end of file diff --git a/code/app/Models/Vote/BallotItem.php b/code/app/Models/Vote/BallotItem.php index 9c54e0ac..6432b653 100644 --- a/code/app/Models/Vote/BallotItem.php +++ b/code/app/Models/Vote/BallotItem.php @@ -3,58 +3,54 @@ namespace App\Models\Vote; -use App\Athenia\Models\BaseModelAbstract; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\MorphTo; +use App\Athenia\Models\Vote\BallotItem as AtheniaBallotItem; /** - * Class BallotSubject + * Class BallotItem * + * @package App\Models\Vote * @property int $id * @property int $ballot_id * @property int $votes_cast - * @property int $vote_count * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property string|null $name - * @property-read \App\Models\Vote\Ballot $ballot - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Vote\BallotItemOption[] $ballotItemOptions + * @property-read \App\Athenia\Models\Vote\Ballot $ballot + * @property-read \Illuminate\Database\Eloquent\Collection $ballotItemOptions * @property-read int|null $ballot_item_options_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\BallotItem newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\BallotItem newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\BallotItem query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItem whereBallotId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItem whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItem whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItem whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItem whereName($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItem whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItem whereVoteCount($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItem whereVotesCast($value) + * @method static \Database\Factories\Vote\BallotItemFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|BallotItem onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereBallotId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItem whereVotesCast($value) + * @method static \Illuminate\Database\Eloquent\Builder|BallotItem withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|BallotItem withoutTrashed() * @mixin \Eloquent */ -class BallotItem extends BaseModelAbstract +class BallotItem extends AtheniaBallotItem { - /** - * The ballot this is apart of - * - * @return BelongsTo - */ - public function ballot(): BelongsTo - { - return $this->belongsTo(Ballot::class); - } - - /** - * The subject that this represents - * - * @return MorphTo - */ - public function ballotItemOptions(): HasMany - { - return $this->hasMany(BallotItemOption::class); - } -} +} \ No newline at end of file diff --git a/code/app/Models/Vote/BallotItemOption.php b/code/app/Models/Vote/BallotItemOption.php index 531d446b..ab17bfc1 100644 --- a/code/app/Models/Vote/BallotItemOption.php +++ b/code/app/Models/Vote/BallotItemOption.php @@ -3,66 +3,57 @@ namespace App\Models\Vote; -use App\Athenia\Models\BaseModelAbstract; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\MorphTo; +use App\Athenia\Models\Vote\BallotItemOption as AtheniaBallotItemOption; /** * Class BallotItemOption * + * @package App\Models\Vote * @property int $id * @property int $ballot_item_id * @property int $vote_count * @property int $subject_id * @property string $subject_type * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at - * @property-read \App\Models\Vote\BallotItem $ballotItem + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \App\Athenia\Models\Vote\BallotItem $ballotItem * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $subject - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Vote\Vote[] $votes + * @property-read \Illuminate\Database\Eloquent\Collection $votes * @property-read int|null $votes_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\BallotItemOption newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\BallotItemOption newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\BallotItemOption query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItemOption whereBallotItemId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItemOption whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItemOption whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItemOption whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItemOption whereSubjectId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItemOption whereSubjectType($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItemOption whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\BallotItemOption whereVoteCount($value) + * @method static \Database\Factories\Vote\BallotItemOptionFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|BallotItemOption onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereBallotItemId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereSubjectId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereSubjectType($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|BallotItemOption whereVoteCount($value) + * @method static \Illuminate\Database\Eloquent\Builder|BallotItemOption withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|BallotItemOption withoutTrashed() * @mixin \Eloquent */ -class BallotItemOption extends BaseModelAbstract +class BallotItemOption extends AtheniaBallotItemOption { - /** - * @return BelongsTo - */ - public function ballotItem() - { - return $this->belongsTo(BallotItem::class); - } - - /** - * The subject that this represents - * - * @return MorphTo - */ - public function subject(): MorphTo - { - return $this->morphTo(); - } - - /** - * The votes that have been submitted where this option was selected - * - * @return HasMany - */ - public function votes(): HasMany - { - return $this->hasMany(Vote::class); - } -} +} \ No newline at end of file diff --git a/code/app/Models/Vote/Vote.php b/code/app/Models/Vote/Vote.php index bb7a0394..7ced79c2 100644 --- a/code/app/Models/Vote/Vote.php +++ b/code/app/Models/Vote/Vote.php @@ -3,60 +3,53 @@ namespace App\Models\Vote; -use App\Athenia\Events\Vote\VoteCreatedEvent; -use App\Athenia\Models\BaseModelAbstract; -use Illuminate\Database\Eloquent\Relations\BelongsTo; +use App\Athenia\Models\Vote\Vote as AtheniaVote; /** * Class Vote * + * @package App\Models\Vote * @property int $id * @property int $ballot_item_option_id * @property int $ballot_completion_id * @property int $result * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at - * @property-read \App\Models\Vote\BallotCompletion $ballotCompletion - * @property-read \App\Models\Vote\BallotItemOption $ballotItemOption - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\Vote newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\Vote newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Vote\Vote query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Vote whereBallotCompletionId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Vote whereBallotItemOptionId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Vote whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Vote whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Vote whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Vote whereResult($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Vote\Vote whereUpdatedAt($value) + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \App\Athenia\Models\Vote\BallotCompletion $ballotCompletion + * @property-read \App\Athenia\Models\Vote\BallotItemOption $ballotItemOption + * @method static \Database\Factories\Vote\VoteFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Vote onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereBallotCompletionId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereBallotItemOptionId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereResult($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Vote whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|Vote withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Vote withoutTrashed() * @mixin \Eloquent */ -class Vote extends BaseModelAbstract +class Vote extends AtheniaVote { - /** - * @var array - */ - protected $dispatchesEvents = [ - 'created' => VoteCreatedEvent::class, - ]; - - /** - * The ballot completion that this is part of - * - * @return BelongsTo - */ - public function ballotCompletion(): BelongsTo - { - return $this->belongsTo(BallotCompletion::class); - } - - /** - * The subject that was voted for - * - * @return BelongsTo - */ - public function ballotItemOption(): BelongsTo - { - return $this->belongsTo(BallotItemOption::class); - } -} +} \ No newline at end of file diff --git a/code/app/Models/Wiki/Article.php b/code/app/Models/Wiki/Article.php index 7cbc2383..20f6e7ee 100644 --- a/code/app/Models/Wiki/Article.php +++ b/code/app/Models/Wiki/Article.php @@ -3,174 +3,72 @@ namespace App\Models\Wiki; -use App\Athenia\Contracts\Models\CanBeIndexedContract; -use App\Athenia\Contracts\Models\HasPolicyContract; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\CanBeIndexed; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Models\User\User; -use Eloquent; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasMany; +use App\Athenia\Models\Wiki\Article as AtheniaArticle; /** * Class Article * + * @package App\Models\Wiki * @property int $id * @property int $created_by_id * @property string $title + * @property string|null $url + * @property string|null $authors * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property int $has_full_modification_history + * @property-read \Illuminate\Database\Eloquent\Collection $articleNotes + * @property-read int|null $article_notes_count + * @property-read \Illuminate\Database\Eloquent\Collection $categories + * @property-read int|null $categories_count * @property-read \App\Models\User\User $createdBy * @property-read null|string $content - * @property-read null|ArticleVersion $current_version + * @property-read null|\App\Models\Wiki\ArticleVersion $current_version * @property-read null|string $last_iteration_content - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Wiki\ArticleIteration[] $iterations + * @property-read \Illuminate\Database\Eloquent\Collection $iterations * @property-read int|null $iterations_count - * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Wiki\ArticleVersion[] $versions + * @property-read \Illuminate\Database\Eloquent\Collection $modifications + * @property-read int|null $modifications_count + * @property-read \App\Models\Resource|null $resource + * @property-read \Illuminate\Database\Eloquent\Collection $targetStatistics + * @property-read int|null $target_statistics_count + * @property-read \Illuminate\Database\Eloquent\Collection $versions * @property-read int|null $versions_count - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Wiki\Article newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Wiki\Article newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Wiki\Article query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\Article whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\Article whereCreatedById($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\Article whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\Article whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\Article whereTitle($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\Article whereUpdatedAt($value) + * @method static \Database\Factories\Wiki\ArticleFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|Article onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereAuthors($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereCreatedById($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereHasFullModificationHistory($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereTitle($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereUpdatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|Article whereUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|Article withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|Article withoutTrashed() * @mixin \Eloquent */ -class Article extends BaseModelAbstract implements HasPolicyContract, HasValidationRulesContract, CanBeIndexedContract +class Article extends AtheniaArticle { - use HasValidationRules, CanBeIndexed; - - /** - * Values that are appending on a toArray function call - * - * @var array - */ - protected $appends = [ - 'content', - 'last_iteration_content', - ]; - - /** - * The user that originally created this article - * - * @return BelongsTo - */ - public function createdBy() : BelongsTo - { - return $this->belongsTo(User::class, 'created_by_id'); - } - - /** - * All of the iterations - * - * @return HasMany - */ - public function iterations() : HasMany - { - return $this->hasMany(ArticleIteration::class) - ->orderByDesc('created_at')->orderByDesc('id'); - } - - /** - * All modifications for this article - * - * @return HasMany - */ - public function modifications() : HasMany - { - return $this->hasMany(ArticleModification::class) - ->orderByDesc('created_at')->orderByDesc('id'); - } - - /** - * All versions related to this article - * - * @return HasMany - */ - public function versions() : HasMany - { - return $this->hasMany(ArticleVersion::class) - ->orderByDesc('created_at')->orderByDesc('id'); - } - - /** - * Gets the content of the article - * - * @return null|string - */ - public function getContentAttribute() : ?string - { - return $this->current_version?->articleIteration?->content; - } - - /** - * Gets the content of the article - * - * @return null|ArticleVersion - */ - public function getCurrentVersionAttribute() : ?ArticleVersion - { - return $this->versions()->limit(1)->get()->first(); - } - - /** - * Gets the content of the article - * - * @return null|string - */ - public function getLastIterationContentAttribute() : ?string - { - if (isset($this->attributes['last_iteration_content'])) { - return $this->attributes['last_iteration_content']; - } - /** @var ArticleIteration|null $iteration */ - $iteration = $this->iterations()->limit(1)->get()->first(); - return $iteration ? $iteration->content : null; - } - - /** - * @return string - */ - public function morphRelationName(): string - { - return 'article'; - } - - /** - * Gets the content that will be indexed for this resource - * - * @return string|null - */ - public function getContentString(): ?string - { - return $this->title . ' ' . ($this->content ?? ''); - } - - /** - * Build the model validation rules - * @param array $params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - static::VALIDATION_RULES_BASE => [ - 'title' => [ - 'string', - 'max:120', - ], - ], - static::VALIDATION_RULES_CREATE => [ - static::VALIDATION_PREPEND_REQUIRED => [ - 'title', - ], - ], - ]; - } -} +} \ No newline at end of file diff --git a/code/app/Models/Wiki/ArticleIteration.php b/code/app/Models/Wiki/ArticleIteration.php index 1f249934..1cedcb37 100644 --- a/code/app/Models/Wiki/ArticleIteration.php +++ b/code/app/Models/Wiki/ArticleIteration.php @@ -3,153 +3,57 @@ namespace App\Models\Wiki; -use App\Athenia\Contracts\Models\HasPolicyContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Models\User\User; -use Eloquent; -use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasOne; +use App\Athenia\Models\Wiki\ArticleIteration as AtheniaArticleIteration; /** - * Class Iteration + * Class ArticleIteration * + * @package App\Models\Wiki * @property int $id * @property string $content * @property int $created_by_id * @property int $article_id - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $deleted_at + * @property int|null $article_modification_id * @property-read \App\Models\Wiki\Article $article * @property-read \App\Models\User\User $createdBy + * @property-read \App\Models\Wiki\ArticleModification|null $modification * @property-read \App\Models\Wiki\ArticleVersion|null $version - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Wiki\ArticleIteration newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Wiki\ArticleIteration newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Wiki\ArticleIteration query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleIteration whereArticleId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleIteration whereContent($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleIteration whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleIteration whereCreatedById($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleIteration whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleIteration whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleIteration whereUpdatedAt($value) + * @method static \Database\Factories\Wiki\ArticleIterationFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ArticleIteration onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereArticleId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereArticleModificationId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereContent($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereCreatedById($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleIteration whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleIteration withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleIteration withoutTrashed() * @mixin \Eloquent */ -class ArticleIteration extends BaseModelAbstract implements HasPolicyContract +class ArticleIteration extends AtheniaArticleIteration { - /** - * The article that this iteration is for - * - * @return BelongsTo - */ - public function article() : BelongsTo - { - return $this->belongsTo(Article::class); - } - - /** - * The user that originally created this article - * - * @return BelongsTo - */ - public function createdBy() : BelongsTo - { - return $this->belongsTo(User::class, 'created_by_id'); - } - - /** - * Any modification that is tagged that generated this iteration - * - * @return BelongsTo - */ - public function modification(): BelongsTo - { - return $this->belongsTo(ArticleModification::class, 'article_modification_id'); - } - - /** - * The version of this article if there is one - * - * @return HasOne - */ - public function version(): HasOne - { - return $this->hasOne(ArticleVersion::class); - } - - /** - * Makes sure everything is by default ordered by the created at date in reverse - * - * @return Builder - */ - public function newQuery() - { - $query = parent::newQuery(); - - $query->orderBy('created_at', 'desc'); - - return $query; - } - - /** - * Swagger definition below... - * - * @SWG\Definition( - * type="object", - * definition="Iteration", - * @SWG\Property( - * property="id", - * type="integer", - * format="int32", - * description="The primary id of the model", - * readOnly=true - * ), - * @SWG\Property( - * property="created_at", - * type="string", - * format="date-time", - * description="UTC date of the time this was created", - * readOnly=true - * ), - * @SWG\Property( - * property="updated_at", - * type="string", - * format="date-time", - * description="UTC date of the time this was last updated", - * readOnly=true - * ), - * @SWG\Property( - * property="content", - * type="string", - * description="The content of this iteration." - * ), - * @SWG\Property( - * property="article_id", - * type="integer", - * format="int32", - * description="The primary id of the article that created this iteration is for.", - * readOnly=true - * ), - * @SWG\Property( - * property="created_by_id", - * type="integer", - * format="int32", - * description="The primary id of the user that created this article.", - * readOnly=true - * ), - * @SWG\Property( - * property="createdBy", - * description="The users that created this article.", - * type="array", - * @SWG\Items(ref="#/definitions/User") - * ), - * @SWG\Property( - * property="article", - * description="The article that this iteration is for.", - * type="array", - * @SWG\Items(ref="#/definitions/Article") - * ) - * ) - */ -} +} \ No newline at end of file diff --git a/code/app/Models/Wiki/ArticleModification.php b/code/app/Models/Wiki/ArticleModification.php index 56ee03be..38927791 100644 --- a/code/app/Models/Wiki/ArticleModification.php +++ b/code/app/Models/Wiki/ArticleModification.php @@ -3,31 +3,57 @@ namespace App\Models\Wiki; -use App\Athenia\Models\BaseModelAbstract; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasOne; +use App\Athenia\Models\Wiki\ArticleModification as AtheniaArticleModification; /** * Class ArticleModification + * * @package App\Models\Wiki + * @property int $id + * @property int $article_id + * @property string $action + * @property int $start_position + * @property int|null $length + * @property string|null $content + * @property \Illuminate\Support\Carbon|null $deleted_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property-read \App\Models\Wiki\Article $article + * @property-read \App\Models\Wiki\ArticleIteration|null $iteration + * @method static \Database\Factories\Wiki\ArticleModificationFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ArticleModification onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereAction($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereArticleId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereContent($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereLength($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereStartPosition($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleModification whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleModification withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleModification withoutTrashed() + * @mixin \Eloquent */ -class ArticleModification extends BaseModelAbstract +class ArticleModification extends AtheniaArticleModification { - /** - * The article this modification is from - * - * @return BelongsTo - */ - public function article(): BelongsTo - { - return $this->belongsTo(Article::class); - } - - /** - * @return HasOne - */ - public function iteration(): HasOne - { - return $this->hasOne(ArticleIteration::class); - } -} +} \ No newline at end of file diff --git a/code/app/Models/Wiki/ArticleSummary.php b/code/app/Models/Wiki/ArticleSummary.php new file mode 100644 index 00000000..3c9e1d09 --- /dev/null +++ b/code/app/Models/Wiki/ArticleSummary.php @@ -0,0 +1,47 @@ +|ArticleSummary getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary newQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereArticleId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereContent($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleSummary whereUpdatedAt($value) + * @mixin \Eloquent + */ +class ArticleSummary extends AtheniaArticleSummary +{ +} diff --git a/code/app/Models/Wiki/ArticleVersion.php b/code/app/Models/Wiki/ArticleVersion.php index 5046edfa..9813945f 100644 --- a/code/app/Models/Wiki/ArticleVersion.php +++ b/code/app/Models/Wiki/ArticleVersion.php @@ -3,78 +3,53 @@ namespace App\Models\Wiki; -use App\Athenia\Contracts\Models\HasValidationRulesContract; -use App\Athenia\Models\BaseModelAbstract; -use App\Athenia\Models\Traits\HasValidationRules; -use App\Athenia\Validators\ArticleVersion\SelectedIterationBelongsToArticleValidator; -use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Validation\Rule; +use App\Athenia\Models\Wiki\ArticleVersion as AtheniaArticleVersion; /** * Class ArticleVersion * + * @package App\Models\Wiki * @property int $id * @property int $article_id - * @property int $iteration_id + * @property int $article_iteration_id * @property string|null $name * @property \Illuminate\Support\Carbon|null $deleted_at - * @property mixed|null $created_at - * @property mixed|null $updated_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * @property-read \App\Models\Wiki\Article $article * @property-read \App\Models\Wiki\ArticleIteration $articleIteration - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Wiki\ArticleVersion newModelQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Wiki\ArticleVersion newQuery() - * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|\App\Models\Wiki\ArticleVersion query() - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleVersion whereArticleId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleVersion whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleVersion whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleVersion whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleVersion whereIterationId($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleVersion whereName($value) - * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Wiki\ArticleVersion whereUpdatedAt($value) + * @method static \Database\Factories\Wiki\ArticleVersionFactory factory($count = null, $state = []) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion getAggregateMethod() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion isAppendRelationsCount() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion isLeftJoin() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion isUseTableAlias() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion joinRelations($relations, $leftJoin = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion newModelQuery() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|ArticleVersion onlyTrashed() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion orWhereInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion orWhereJoin($column, $operator, $value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion orWhereNotInJoin($column, $values) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion orderByJoin($column, $direction = 'asc', $aggregateMethod = null) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion query() + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion setAggregateMethod(string $aggregateMethod) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion setAppendRelationsCount(bool $appendRelationsCount) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion setLeftJoin(bool $leftJoin) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion setUseTableAlias(bool $useTableAlias) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereArticleId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereArticleIterationId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereCreatedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereDeletedAt($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereId($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereInJoin($column, $values, $boolean = 'and', $not = false) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereJoin($column, $operator, $value, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereName($value) + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereNotInJoin($column, $values, $boolean = 'and') + * @method static \AdminUI\Laravel\EloquentJoin\EloquentJoinBuilder|ArticleVersion whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleVersion withTrashed(bool $withTrashed = true) + * @method static \Illuminate\Database\Eloquent\Builder|ArticleVersion withoutTrashed() * @mixin \Eloquent */ -class ArticleVersion extends BaseModelAbstract implements HasValidationRulesContract +class ArticleVersion extends AtheniaArticleVersion { - use HasValidationRules; - - /** - * The article this version is for - * - * @return BelongsTo - */ - public function article(): BelongsTo - { - return $this->belongsTo(Article::class); - } - - /** - * The iteration that this version is for - * - * @return BelongsTo - */ - public function articleIteration(): BelongsTo - { - return $this->belongsTo(ArticleIteration::class); - } - - /** - * Build the model validation rules - * @param array $params - * @return array - */ - public function buildModelValidationRules(...$params): array - { - return [ - static::VALIDATION_RULES_BASE => [ - 'article_iteration_id' => [ - 'bail', - 'required', - 'int', - Rule::exists('article_iterations', 'id'), - SelectedIterationBelongsToArticleValidator::KEY, - ], - ], - ]; - } -} +} \ No newline at end of file diff --git a/code/app/Policies/User/ArticleNotePolicy.php b/code/app/Policies/User/ArticleNotePolicy.php new file mode 100644 index 00000000..b693a26a --- /dev/null +++ b/code/app/Policies/User/ArticleNotePolicy.php @@ -0,0 +1,81 @@ +id == $requestedUser->id; + } + + /** + * Any logged in user can create article notes for themselves + * + * @param User $loggedInUser + * @param User $requestedUser + * @return bool + */ + public function create(User $loggedInUser, User $requestedUser) + { + return $loggedInUser->id == $requestedUser->id; + } + + /** + * Any logged in user can view their own article note + * + * @param User $loggedInUser + * @param User $requestedUser + * @param ArticleNote $articleNote + * @return bool + */ + public function view(User $loggedInUser, User $requestedUser, ArticleNote $articleNote) + { + return $loggedInUser->id == $requestedUser->id && + $requestedUser->id == $articleNote->user_id; + } + + /** + * Any logged in user can update their own article note + * + * @param User $loggedInUser + * @param User $requestedUser + * @param ArticleNote $articleNote + * @return bool + */ + public function update(User $loggedInUser, User $requestedUser, ArticleNote $articleNote) + { + return $loggedInUser->id == $requestedUser->id && + $requestedUser->id == $articleNote->user_id; + } + + /** + * Any logged in user can delete their own article note + * + * @param User $loggedInUser + * @param User $requestedUser + * @param ArticleNote $articleNote + * @return bool + */ + public function delete(User $loggedInUser, User $requestedUser, ArticleNote $articleNote) + { + return $loggedInUser->id == $requestedUser->id && + $requestedUser->id == $articleNote->user_id; + } +} diff --git a/code/app/Policies/Wiki/ArticlePolicy.php b/code/app/Policies/Wiki/ArticlePolicy.php index 4d976063..c76cb33a 100644 --- a/code/app/Policies/Wiki/ArticlePolicy.php +++ b/code/app/Policies/Wiki/ArticlePolicy.php @@ -22,10 +22,7 @@ class ArticlePolicy extends BasePolicyAbstract */ public function all(User $user) { - return $user->hasRole([ - Role::ARTICLE_VIEWER, - Role::ARTICLE_EDITOR, - ]); + return true; } /** @@ -37,10 +34,7 @@ public function all(User $user) */ public function view(User $user, Article $model) { - return $user->hasRole([ - Role::ARTICLE_VIEWER, - Role::ARTICLE_EDITOR, - ]); + return true; } /** diff --git a/code/app/Policies/Wiki/ArticleSummaryPolicy.php b/code/app/Policies/Wiki/ArticleSummaryPolicy.php new file mode 100644 index 00000000..d704c820 --- /dev/null +++ b/code/app/Policies/Wiki/ArticleSummaryPolicy.php @@ -0,0 +1,64 @@ +hasRole(Role::ARTICLE_EDITOR); + } + + /** + * Only users with the ARTICLE_EDITOR role can update article summaries + * + * @param User $user + * @param Article $article + * @return bool + */ + public function update(User $user, Article $article) + { + return $user->hasRole(Role::ARTICLE_EDITOR); + } + + /** + * Only users with the ARTICLE_EDITOR role can delete article summaries + * + * @param User $user + * @param Article $article + * @return bool + */ + public function delete(User $user, Article $article) + { + return $user->hasRole(Role::ARTICLE_EDITOR); + } +} diff --git a/code/app/Providers/AppRepositoryProvider.php b/code/app/Providers/AppRepositoryProvider.php index 5c12b4c6..30a546c2 100644 --- a/code/app/Providers/AppRepositoryProvider.php +++ b/code/app/Providers/AppRepositoryProvider.php @@ -39,4 +39,4 @@ public function appMorphMaps(): array public function registerApp(): void { } -} \ No newline at end of file +} diff --git a/code/app/Providers/AppServiceProvider.php b/code/app/Providers/AppServiceProvider.php index 700a7c1c..7f75cb70 100644 --- a/code/app/Providers/AppServiceProvider.php +++ b/code/app/Providers/AppServiceProvider.php @@ -18,8 +18,7 @@ class AppServiceProvider extends BaseServiceProvider */ public function appProviders(): array { - return [ - ]; + return []; } /** diff --git a/code/composer.json b/code/composer.json index 511954f0..e86bc6c0 100644 --- a/code/composer.json +++ b/code/composer.json @@ -19,8 +19,10 @@ "pusher/pusher-php-server": "~7.0", "roave/security-advisories": "dev-master", "sebastian/diff": "^6.0", + "vemcogroup/laravel-sparkpost-driver": "^5.0", "xantios/mimey": "^2.0", "ext-zip": "*", + "ext-pdo": "*", "ext-imagick": "*" }, "require-dev": { diff --git a/code/composer.lock b/code/composer.lock index 219cab25..e065567e 100644 --- a/code/composer.lock +++ b/code/composer.lock @@ -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": "1028aa450a0d24fe309817955d849cfb", + "content-hash": "86aa40c5ef2780cdf88805305941c31b", "packages": [ { "name": "adminui/laravel-eloquent-joins", @@ -67,25 +67,25 @@ }, { "name": "brick/math", - "version": "0.12.3", + "version": "0.14.0", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", + "url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.2" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "6.8.8" + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" }, "type": "library", "autoload": { @@ -115,7 +115,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.3" + "source": "https://github.com/brick/math/tree/0.14.0" }, "funding": [ { @@ -123,7 +123,7 @@ "type": "github" } ], - "time": "2025-02-28T13:11:00+00:00" + "time": "2025-08-29T12:40:03+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -395,33 +395,32 @@ }, { "name": "doctrine/inflector", - "version": "2.0.10", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11.0", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25 || ^5.4" + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + "Doctrine\\Inflector\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -466,7 +465,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.10" + "source": "https://github.com/doctrine/inflector/tree/2.1.0" }, "funding": [ { @@ -482,7 +481,7 @@ "type": "tidelift" } ], - "time": "2024-02-18T20:23:39+00:00" + "time": "2025-08-10T19:31:58+00:00" }, { "name": "doctrine/lexer", @@ -563,29 +562,28 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.4.0", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "8c784d071debd117328803d86b2097615b457500" + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", - "reference": "8c784d071debd117328803d86b2097615b457500", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013", "shasum": "" }, "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" + "php": "^8.2|^8.3|^8.4|^8.5" }, "replace": { "mtdowling/cron-expression": "^1.0" }, "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.32|^2.1.31", + "phpunit/phpunit": "^8.5.48|^9.0" }, "type": "library", "extra": { @@ -616,7 +614,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0" }, "funding": [ { @@ -624,20 +622,20 @@ "type": "github" } ], - "time": "2024-10-09T13:47:03+00:00" + "time": "2025-10-31T18:51:33+00:00" }, { "name": "egulias/email-validator", - "version": "4.0.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "b115554301161fa21467629f1e1391c1936de517" + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b115554301161fa21467629f1e1391c1936de517", - "reference": "b115554301161fa21467629f1e1391c1936de517", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", "shasum": "" }, "require": { @@ -683,7 +681,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.3" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" }, "funding": [ { @@ -691,7 +689,7 @@ "type": "github" } ], - "time": "2024-12-27T00:36:43+00:00" + "time": "2025-03-06T22:45:56+00:00" }, { "name": "fruitcake/php-cors", @@ -828,22 +826,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.9.2", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.3", - "guzzlehttp/psr7": "^2.7.0", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -934,7 +932,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -950,20 +948,20 @@ "type": "tidelift" } ], - "time": "2024-07-24T11:22:20+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.4", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", - "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -971,7 +969,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -1017,7 +1015,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.4" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -1033,20 +1031,20 @@ "type": "tidelift" } ], - "time": "2024-10-17T10:06:22+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.0", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -1062,7 +1060,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1133,7 +1131,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.0" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -1149,20 +1147,20 @@ "type": "tidelift" } ], - "time": "2024-07-18T11:15:46+00:00" + "time": "2025-08-23T21:21:41+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.4", + "version": "v1.0.5", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "30e286560c137526eccd4ce21b2de477ab0676d2" + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/30e286560c137526eccd4ce21b2de477ab0676d2", - "reference": "30e286560c137526eccd4ce21b2de477ab0676d2", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", "shasum": "" }, "require": { @@ -1171,7 +1169,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", "uri-template/tests": "1.0.0" }, "type": "library", @@ -1219,7 +1217,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.4" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" }, "funding": [ { @@ -1235,24 +1233,24 @@ "type": "tidelift" } ], - "time": "2025-02-03T10:55:03+00:00" + "time": "2025-08-22T14:27:06+00:00" }, { "name": "laravel/framework", - "version": "v11.44.1", + "version": "v11.46.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "0883d4175f4e2b5c299e7087ad3c74f2ce195c6d" + "reference": "5fd457f807570a962a53b403b1346efe4cc80bb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/0883d4175f4e2b5c299e7087ad3c74f2ce195c6d", - "reference": "0883d4175f4e2b5c299e7087ad3c74f2ce195c6d", + "url": "https://api.github.com/repos/laravel/framework/zipball/5fd457f807570a962a53b403b1346efe4cc80bb8", + "reference": "5fd457f807570a962a53b403b1346efe4cc80bb8", "shasum": "" }, "require": { - "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12|^0.13|^0.14", "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", "dragonmantank/cron-expression": "^3.4", @@ -1269,7 +1267,7 @@ "guzzlehttp/uri-template": "^1.0", "laravel/prompts": "^0.1.18|^0.2.0|^0.3.0", "laravel/serializable-closure": "^1.3|^2.0", - "league/commonmark": "^2.6", + "league/commonmark": "^2.7", "league/flysystem": "^3.25.1", "league/flysystem-local": "^3.25.1", "league/uri": "^7.5.1", @@ -1356,7 +1354,7 @@ "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^9.11.2", + "orchestra/testbench-core": "^9.16.1", "pda/pheanstalk": "^5.0.6", "php-http/discovery": "^1.15", "phpstan/phpstan": "^2.0", @@ -1450,20 +1448,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-03-05T15:34:10+00:00" + "time": "2025-09-30T14:51:32+00:00" }, { "name": "laravel/prompts", - "version": "v0.3.5", + "version": "v0.3.7", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "57b8f7efe40333cdb925700891c7d7465325d3b1" + "reference": "a1891d362714bc40c8d23b0b1d7090f022ea27cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/57b8f7efe40333cdb925700891c7d7465325d3b1", - "reference": "57b8f7efe40333cdb925700891c7d7465325d3b1", + "url": "https://api.github.com/repos/laravel/prompts/zipball/a1891d362714bc40c8d23b0b1d7090f022ea27cc", + "reference": "a1891d362714bc40c8d23b0b1d7090f022ea27cc", "shasum": "" }, "require": { @@ -1480,8 +1478,8 @@ "illuminate/collections": "^10.0|^11.0|^12.0", "mockery/mockery": "^1.5", "pestphp/pest": "^2.3|^3.4", - "phpstan/phpstan": "^1.11", - "phpstan/phpstan-mockery": "^1.1" + "phpstan/phpstan": "^1.12.28", + "phpstan/phpstan-mockery": "^1.1.3" }, "suggest": { "ext-pcntl": "Required for the spinner to be animated." @@ -1507,22 +1505,22 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.5" + "source": "https://github.com/laravel/prompts/tree/v0.3.7" }, - "time": "2025-02-11T13:34:40+00:00" + "time": "2025-09-19T13:47:56+00:00" }, { "name": "laravel/serializable-closure", - "version": "v2.0.3", + "version": "v2.0.6", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "f379c13663245f7aa4512a7869f62eb14095f23f" + "reference": "038ce42edee619599a1debb7e81d7b3759492819" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f379c13663245f7aa4512a7869f62eb14095f23f", - "reference": "f379c13663245f7aa4512a7869f62eb14095f23f", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/038ce42edee619599a1debb7e81d7b3759492819", + "reference": "038ce42edee619599a1debb7e81d7b3759492819", "shasum": "" }, "require": { @@ -1570,7 +1568,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2025-02-11T15:03:05+00:00" + "time": "2025-10-09T13:42:30+00:00" }, { "name": "laravel/tinker", @@ -1640,22 +1638,22 @@ }, { "name": "lcobucci/jwt", - "version": "5.5.0", + "version": "5.6.0", "source": { "type": "git", "url": "https://github.com/lcobucci/jwt.git", - "reference": "a835af59b030d3f2967725697cf88300f579088e" + "reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/a835af59b030d3f2967725697cf88300f579088e", - "reference": "a835af59b030d3f2967725697cf88300f579088e", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/bb3e9f21e4196e8afc41def81ef649c164bca25e", + "reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e", "shasum": "" }, "require": { "ext-openssl": "*", "ext-sodium": "*", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", "psr/clock": "^1.0" }, "require-dev": { @@ -1697,7 +1695,7 @@ ], "support": { "issues": "https://github.com/lcobucci/jwt/issues", - "source": "https://github.com/lcobucci/jwt/tree/5.5.0" + "source": "https://github.com/lcobucci/jwt/tree/5.6.0" }, "funding": [ { @@ -1709,20 +1707,20 @@ "type": "patreon" } ], - "time": "2025-01-26T21:29:45+00:00" + "time": "2025-10-17T11:30:53+00:00" }, { "name": "league/commonmark", - "version": "2.6.1", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "d990688c91cedfb69753ffc2512727ec646df2ad" + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d990688c91cedfb69753ffc2512727ec646df2ad", - "reference": "d990688c91cedfb69753ffc2512727ec646df2ad", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/10732241927d3971d28e7ea7b5712721fa2296ca", + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca", "shasum": "" }, "require": { @@ -1751,7 +1749,7 @@ "symfony/process": "^5.4 | ^6.0 | ^7.0", "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", "unleashedtech/php-coding-standard": "^3.1.1", - "vimeo/psalm": "^4.24.0 || ^5.0.0" + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" }, "suggest": { "symfony/yaml": "v2.3+ required if using the Front Matter extension" @@ -1759,7 +1757,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.7-dev" + "dev-main": "2.8-dev" } }, "autoload": { @@ -1816,7 +1814,7 @@ "type": "tidelift" } ], - "time": "2024-12-29T14:10:59+00:00" + "time": "2025-07-20T12:47:49+00:00" }, { "name": "league/config", @@ -1902,16 +1900,16 @@ }, { "name": "league/flysystem", - "version": "3.29.1", + "version": "3.30.2", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319" + "reference": "5966a8ba23e62bdb518dd9e0e665c2dbd4b5b277" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319", - "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/5966a8ba23e62bdb518dd9e0e665c2dbd4b5b277", + "reference": "5966a8ba23e62bdb518dd9e0e665c2dbd4b5b277", "shasum": "" }, "require": { @@ -1935,13 +1933,13 @@ "composer/semver": "^3.0", "ext-fileinfo": "*", "ext-ftp": "*", - "ext-mongodb": "^1.3", + "ext-mongodb": "^1.3|^2", "ext-zip": "*", "friendsofphp/php-cs-fixer": "^3.5", "google/cloud-storage": "^1.23", "guzzlehttp/psr7": "^2.6", "microsoft/azure-storage-blob": "^1.1", - "mongodb/mongodb": "^1.2", + "mongodb/mongodb": "^1.2|^2", "phpseclib/phpseclib": "^3.0.36", "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.5.11|^10.0", @@ -1979,22 +1977,22 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.29.1" + "source": "https://github.com/thephpleague/flysystem/tree/3.30.2" }, - "time": "2024-10-08T08:58:34+00:00" + "time": "2025-11-10T17:13:11+00:00" }, { "name": "league/flysystem-local", - "version": "3.29.0", + "version": "3.30.2", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27" + "reference": "ab4f9d0d672f601b102936aa728801dd1a11968d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e0e8d52ce4b2ed154148453d321e97c8e931bd27", - "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/ab4f9d0d672f601b102936aa728801dd1a11968d", + "reference": "ab4f9d0d672f601b102936aa728801dd1a11968d", "shasum": "" }, "require": { @@ -2028,9 +2026,9 @@ "local" ], "support": { - "source": "https://github.com/thephpleague/flysystem-local/tree/3.29.0" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.30.2" }, - "time": "2024-08-09T21:24:39+00:00" + "time": "2025-11-10T11:23:37+00:00" }, { "name": "league/mime-type-detection", @@ -2090,33 +2088,38 @@ }, { "name": "league/uri", - "version": "7.5.1", + "version": "7.6.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "81fb5145d2644324614cc532b28efd0215bda430" + "reference": "f625804987a0a9112d954f9209d91fec52182344" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", - "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/f625804987a0a9112d954f9209d91fec52182344", + "reference": "f625804987a0a9112d954f9209d91fec52182344", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.5", - "php": "^8.1" + "league/uri-interfaces": "^7.6", + "php": "^8.1", + "psr/http-factory": "^1" }, "conflict": { "league/uri-schemes": "^1.0" }, "suggest": { "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", "ext-fileinfo": "to create Data URI from file contennts", "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", "league/uri-components": "Needed to easily manipulate URI objects components", + "league/uri-polyfill": "Needed to backport the PHP URI extension for older versions of PHP", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle WHATWG URL", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -2144,6 +2147,7 @@ "description": "URI manipulation library", "homepage": "https://uri.thephpleague.com", "keywords": [ + "URN", "data-uri", "file-uri", "ftp", @@ -2156,9 +2160,11 @@ "psr-7", "query-string", "querystring", + "rfc2141", "rfc3986", "rfc3987", "rfc6570", + "rfc8141", "uri", "uri-template", "url", @@ -2168,7 +2174,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.5.1" + "source": "https://github.com/thephpleague/uri/tree/7.6.0" }, "funding": [ { @@ -2176,26 +2182,25 @@ "type": "github" } ], - "time": "2024-12-08T08:40:02+00:00" + "time": "2025-11-18T12:17:23+00:00" }, { "name": "league/uri-interfaces", - "version": "7.5.0", + "version": "7.6.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + "reference": "ccbfb51c0445298e7e0b7f4481b942f589665368" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/ccbfb51c0445298e7e0b7f4481b942f589665368", + "reference": "ccbfb51c0445298e7e0b7f4481b942f589665368", "shasum": "" }, "require": { "ext-filter": "*", "php": "^8.1", - "psr/http-factory": "^1", "psr/http-message": "^1.1 || ^2.0" }, "suggest": { @@ -2203,6 +2208,7 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle WHATWG URL", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -2227,7 +2233,7 @@ "homepage": "https://nyamsprod.com" } ], - "description": "Common interfaces and classes for URI representation and interaction", + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", "homepage": "https://uri.thephpleague.com", "keywords": [ "data-uri", @@ -2252,7 +2258,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.6.0" }, "funding": [ { @@ -2260,20 +2266,20 @@ "type": "github" } ], - "time": "2024-12-08T08:18:47+00:00" + "time": "2025-11-18T12:17:23+00:00" }, { "name": "monolog/monolog", - "version": "3.8.1", + "version": "3.9.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4" + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/aef6ee73a77a66e404dd6540934a9ef1b3c855b4", - "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", "shasum": "" }, "require": { @@ -2351,7 +2357,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.8.1" + "source": "https://github.com/Seldaek/monolog/tree/3.9.0" }, "funding": [ { @@ -2363,7 +2369,7 @@ "type": "tidelift" } ], - "time": "2024-12-05T17:15:07+00:00" + "time": "2025-03-24T10:02:05+00:00" }, { "name": "namshi/jose", @@ -2434,16 +2440,16 @@ }, { "name": "nesbot/carbon", - "version": "3.8.6", + "version": "3.10.3", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd" + "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ff2f20cf83bd4d503720632ce8a426dc747bf7fd", - "reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", + "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", "shasum": "" }, "require": { @@ -2451,9 +2457,9 @@ "ext-json": "*", "php": "^8.1", "psr/clock": "^1.0", - "symfony/clock": "^6.3 || ^7.0", + "symfony/clock": "^6.3.12 || ^7.0", "symfony/polyfill-mbstring": "^1.0", - "symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0" + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -2461,14 +2467,13 @@ "require-dev": { "doctrine/dbal": "^3.6.3 || ^4.0", "doctrine/orm": "^2.15.2 || ^3.0", - "friendsofphp/php-cs-fixer": "^3.57.2", + "friendsofphp/php-cs-fixer": "^v3.87.1", "kylekatarnls/multi-tester": "^2.5.3", - "ondrejmirtes/better-reflection": "^6.25.0.4", "phpmd/phpmd": "^2.15.0", - "phpstan/extension-installer": "^1.3.1", - "phpstan/phpstan": "^1.11.2", - "phpunit/phpunit": "^10.5.20", - "squizlabs/php_codesniffer": "^3.9.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4" }, "bin": [ "bin/carbon" @@ -2536,29 +2541,29 @@ "type": "tidelift" } ], - "time": "2025-02-20T17:33:38+00:00" + "time": "2025-09-06T13:39:36+00:00" }, { "name": "nette/schema", - "version": "v1.3.2", + "version": "v1.3.3", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "url": "https://api.github.com/repos/nette/schema/zipball/2befc2f42d7c715fd9d95efc31b1081e5d765004", + "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004", "shasum": "" }, "require": { "nette/utils": "^4.0", - "php": "8.1 - 8.4" + "php": "8.1 - 8.5" }, "require-dev": { "nette/tester": "^2.5.2", - "phpstan/phpstan-nette": "^1.0", + "phpstan/phpstan-nette": "^2.0@stable", "tracy/tracy": "^2.8" }, "type": "library", @@ -2568,6 +2573,9 @@ } }, "autoload": { + "psr-4": { + "Nette\\": "src" + }, "classmap": [ "src/" ] @@ -2596,35 +2604,35 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.2" + "source": "https://github.com/nette/schema/tree/v1.3.3" }, - "time": "2024-10-06T23:10:23+00:00" + "time": "2025-10-30T22:57:59+00:00" }, { "name": "nette/utils", - "version": "v4.0.5", + "version": "v4.0.8", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96" + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", - "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", + "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede", "shasum": "" }, "require": { - "php": "8.0 - 8.4" + "php": "8.0 - 8.5" }, "conflict": { "nette/finder": "<3", "nette/schema": "<1.2.2" }, "require-dev": { - "jetbrains/phpstorm-attributes": "dev-master", + "jetbrains/phpstorm-attributes": "^1.2", "nette/tester": "^2.5", - "phpstan/phpstan": "^1.0", + "phpstan/phpstan-nette": "^2.0@stable", "tracy/tracy": "^2.9" }, "suggest": { @@ -2642,6 +2650,9 @@ } }, "autoload": { + "psr-4": { + "Nette\\": "src" + }, "classmap": [ "src/" ] @@ -2682,22 +2693,22 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.5" + "source": "https://github.com/nette/utils/tree/v4.0.8" }, - "time": "2024-08-07T15:39:19+00:00" + "time": "2025-08-06T21:43:34+00:00" }, { "name": "nikic/php-parser", - "version": "v5.4.0", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", "shasum": "" }, "require": { @@ -2716,7 +2727,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -2740,37 +2751,37 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" }, - "time": "2024-12-30T11:07:19+00:00" + "time": "2025-10-21T19:32:17+00:00" }, { "name": "nunomaduro/termwind", - "version": "v2.3.0", + "version": "v2.3.3", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda" + "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/52915afe6a1044e8b9cee1bcff836fb63acf9cda", - "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/6fb2a640ff502caace8e05fd7be3b503a7e1c017", + "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017", "shasum": "" }, "require": { "ext-mbstring": "*", "php": "^8.2", - "symfony/console": "^7.1.8" + "symfony/console": "^7.3.6" }, "require-dev": { - "illuminate/console": "^11.33.2", - "laravel/pint": "^1.18.2", + "illuminate/console": "^11.46.1", + "laravel/pint": "^1.25.1", "mockery/mockery": "^1.6.12", - "pestphp/pest": "^2.36.0", - "phpstan/phpstan": "^1.12.11", - "phpstan/phpstan-strict-rules": "^1.6.1", - "symfony/var-dumper": "^7.1.8", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.1.3", + "phpstan/phpstan": "^1.12.32", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.3.5", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -2813,7 +2824,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.3.0" + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.3" }, "funding": [ { @@ -2829,20 +2840,20 @@ "type": "github" } ], - "time": "2024-11-21T10:39:51+00:00" + "time": "2025-11-20T02:34:59+00:00" }, { "name": "paragonie/sodium_compat", - "version": "v2.1.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/paragonie/sodium_compat.git", - "reference": "a673d5f310477027cead2e2f2b6db5d8368157cb" + "reference": "547e2dc4d45107440e76c17ab5a46e4252460158" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/a673d5f310477027cead2e2f2b6db5d8368157cb", - "reference": "a673d5f310477027cead2e2f2b6db5d8368157cb", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/547e2dc4d45107440e76c17ab5a46e4252460158", + "reference": "547e2dc4d45107440e76c17ab5a46e4252460158", "shasum": "" }, "require": { @@ -2850,8 +2861,10 @@ "php-64bit": "*" }, "require-dev": { - "phpunit/phpunit": "^7|^8|^9", - "vimeo/psalm": "^4|^5" + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^7|^8|^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" }, "suggest": { "ext-sodium": "Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." @@ -2865,7 +2878,10 @@ "autoload": { "files": [ "autoload.php" - ] + ], + "psr-4": { + "ParagonIE\\Sodium\\": "namespaced/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2918,22 +2934,22 @@ ], "support": { "issues": "https://github.com/paragonie/sodium_compat/issues", - "source": "https://github.com/paragonie/sodium_compat/tree/v2.1.0" + "source": "https://github.com/paragonie/sodium_compat/tree/v2.4.0" }, - "time": "2024-09-04T12:51:01+00:00" + "time": "2025-10-06T08:47:40+00:00" }, { "name": "php-open-source-saver/jwt-auth", - "version": "v2.8.1", + "version": "v2.8.3", "source": { "type": "git", "url": "https://github.com/PHP-Open-Source-Saver/jwt-auth.git", - "reference": "cd55a88bc98635983c7f8d6fa915d81eaadeb6e1" + "reference": "563f7dc025f48b9ecbacc271da509bbb4c6b3b23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-Open-Source-Saver/jwt-auth/zipball/cd55a88bc98635983c7f8d6fa915d81eaadeb6e1", - "reference": "cd55a88bc98635983c7f8d6fa915d81eaadeb6e1", + "url": "https://api.github.com/repos/PHP-Open-Source-Saver/jwt-auth/zipball/563f7dc025f48b9ecbacc271da509bbb4c6b3b23", + "reference": "563f7dc025f48b9ecbacc271da509bbb4c6b3b23", "shasum": "" }, "require": { @@ -3016,20 +3032,20 @@ "issues": "https://github.com/PHP-Open-Source-Saver/jwt-auth/issues", "source": "https://github.com/PHP-Open-Source-Saver/jwt-auth" }, - "time": "2025-02-28T05:02:47+00:00" + "time": "2025-10-15T12:02:51+00:00" }, { "name": "phpoption/phpoption", - "version": "1.9.3", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", "shasum": "" }, "require": { @@ -3037,7 +3053,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" }, "type": "library", "extra": { @@ -3079,7 +3095,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" }, "funding": [ { @@ -3091,20 +3107,20 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:41:07+00:00" + "time": "2025-08-21T11:53:16+00:00" }, { "name": "predis/predis", - "version": "v2.3.0", + "version": "v2.4.1", "source": { "type": "git", "url": "https://github.com/predis/predis.git", - "reference": "bac46bfdb78cd6e9c7926c697012aae740cb9ec9" + "reference": "07105e050622ed80bd60808367ced9e379f31530" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/predis/predis/zipball/bac46bfdb78cd6e9c7926c697012aae740cb9ec9", - "reference": "bac46bfdb78cd6e9c7926c697012aae740cb9ec9", + "url": "https://api.github.com/repos/predis/predis/zipball/07105e050622ed80bd60808367ced9e379f31530", + "reference": "07105e050622ed80bd60808367ced9e379f31530", "shasum": "" }, "require": { @@ -3113,6 +3129,7 @@ "require-dev": { "friendsofphp/php-cs-fixer": "^3.3", "phpstan/phpstan": "^1.9", + "phpunit/phpcov": "^6.0 || ^8.0", "phpunit/phpunit": "^8.0 || ^9.4" }, "suggest": { @@ -3135,7 +3152,7 @@ "role": "Maintainer" } ], - "description": "A flexible and feature-complete Redis client for PHP.", + "description": "A flexible and feature-complete Redis/Valkey client for PHP.", "homepage": "http://github.com/predis/predis", "keywords": [ "nosql", @@ -3144,7 +3161,7 @@ ], "support": { "issues": "https://github.com/predis/predis/issues", - "source": "https://github.com/predis/predis/tree/v2.3.0" + "source": "https://github.com/predis/predis/tree/v2.4.1" }, "funding": [ { @@ -3152,7 +3169,7 @@ "type": "github" } ], - "time": "2024-11-21T20:00:02+00:00" + "time": "2025-11-12T18:00:11+00:00" }, { "name": "psr/clock", @@ -3568,16 +3585,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.7", + "version": "v0.12.14", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c" + "reference": "95c29b3756a23855a30566b745d218bee690bef2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/d73fa3c74918ef4522bb8a3bf9cab39161c4b57c", - "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/95c29b3756a23855a30566b745d218bee690bef2", + "reference": "95c29b3756a23855a30566b745d218bee690bef2", "shasum": "" }, "require": { @@ -3592,11 +3609,12 @@ "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.2" + "bamarni/composer-bin-plugin": "^1.2", + "composer/class-map-generator": "^1.6" }, "suggest": { + "composer/class-map-generator": "Improved tab completion performance with better class discovery.", "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-pdo-sqlite": "The doc command requires SQLite to work.", "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." }, "bin": [ @@ -3627,12 +3645,11 @@ "authors": [ { "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" + "email": "justin@justinhileman.info" } ], "description": "An interactive shell for modern PHP.", - "homepage": "http://psysh.org", + "homepage": "https://psysh.org", "keywords": [ "REPL", "console", @@ -3641,9 +3658,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.7" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.14" }, - "time": "2024-12-10T01:58:33+00:00" + "time": "2025-10-27T17:15:31+00:00" }, { "name": "pusher/pusher-php-server", @@ -3752,16 +3769,16 @@ }, { "name": "ramsey/collection", - "version": "2.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109" + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", - "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", "shasum": "" }, "require": { @@ -3822,27 +3839,26 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.1.0" + "source": "https://github.com/ramsey/collection/tree/2.1.1" }, - "time": "2025-03-02T04:48:29+00:00" + "time": "2025-03-22T05:38:12+00:00" }, { "name": "ramsey/uuid", - "version": "4.7.6", + "version": "4.9.1", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", - "ext-json": "*", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -3850,26 +3866,23 @@ "rhumsaa/uuid": "self.version" }, "require-dev": { - "captainhook/captainhook": "^5.10", + "captainhook/captainhook": "^5.25", "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.8", - "ergebnis/composer-normalize": "^2.15", - "mockery/mockery": "^1.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.2", - "php-mock/php-mock-mockery": "^1.3", - "php-parallel-lint/php-parallel-lint": "^1.1", - "phpbench/phpbench": "^1.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^8.5 || ^9", - "ramsey/composer-repl": "^1.4", - "slevomat/coding-standard": "^8.4", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.9" + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" }, "suggest": { "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", @@ -3904,19 +3917,9 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.7.6" + "source": "https://github.com/ramsey/uuid/tree/4.9.1" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", - "type": "tidelift" - } - ], - "time": "2024-04-27T21:32:50+00:00" + "time": "2025-09-04T20:59:21+00:00" }, { "name": "roave/security-advisories", @@ -3924,18 +3927,19 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "4da22c240293fcf0fe72fc0b1f2f265efeb44e2e" + "reference": "f5a58d09ea64ab1d080876ee3abbd3b90dd022d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/4da22c240293fcf0fe72fc0b1f2f265efeb44e2e", - "reference": "4da22c240293fcf0fe72fc0b1f2f265efeb44e2e", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/f5a58d09ea64ab1d080876ee3abbd3b90dd022d6", + "reference": "f5a58d09ea64ab1d080876ee3abbd3b90dd022d6", "shasum": "" }, "conflict": { "3f/pygmentize": "<1.2", - "admidio/admidio": "<4.3.12", - "adodb/adodb-php": "<=5.20.20|>=5.21,<=5.21.3", + "adaptcms/adaptcms": "<=1.3", + "admidio/admidio": "<=4.3.16", + "adodb/adodb-php": "<=5.22.9", "aheinze/cockpit": "<2.2", "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", "aimeos/ai-admin-jsonadm": "<2020.10.13|>=2021.04.1,<2021.10.6|>=2022.04.1,<2022.10.3|>=2023.04.1,<2023.10.4|==2024.04.1", @@ -3946,7 +3950,8 @@ "airesvsg/acf-to-rest-api": "<=3.1", "akaunting/akaunting": "<2.1.13", "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", - "alextselegidis/easyappointments": "<=1.5", + "alextselegidis/easyappointments": "<1.5.2.0-beta1", + "alt-design/alt-redirect": "<1.6.4", "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", "amazing/media2click": ">=1,<1.3.3", "ameos/ameos_tarteaucitron": "<1.2.23", @@ -3956,9 +3961,11 @@ "anchorcms/anchor-cms": "<=0.12.7", "andreapollastri/cipi": "<=3.1.15", "andrewhaine/silverstripe-form-capture": ">=0.2,<=0.2.3|>=1,<1.0.2|>=2,<2.2.5", + "aoe/restler": "<1.7.1", "apache-solr-for-typo3/solr": "<2.8.3", "apereo/phpcas": "<1.6", - "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6|>=2.6,<2.7.10|>=3,<3.0.12|>=3.1,<3.1.3", + "api-platform/core": "<3.4.17|>=4,<4.0.22|>=4.1,<4.1.5", + "api-platform/graphql": "<3.4.17|>=4,<4.0.22|>=4.1,<4.1.5", "appwrite/server-ce": "<=1.2.1", "arc/web": "<3", "area17/twill": "<1.2.5|>=2,<2.5.3", @@ -3968,29 +3975,36 @@ "athlon1600/php-proxy-app": "<=3", "athlon1600/youtube-downloader": "<=4", "austintoddj/canvas": "<=3.4.2", - "auth0/wordpress": "<=4.6", + "auth0/auth0-php": ">=3.3,<=8.16", + "auth0/login": "<=7.18", + "auth0/symfony": "<=5.4.1", + "auth0/wordpress": "<=5.3", "automad/automad": "<2.0.0.0-alpha5", "automattic/jetpack": "<9.8", "awesome-support/awesome-support": "<=6.0.7", "aws/aws-sdk-php": "<3.288.1", "azuracast/azuracast": "<0.18.3", - "backdrop/backdrop": "<1.27.3|>=1.28,<1.28.2", + "b13/seo_basics": "<0.8.2", + "backdrop/backdrop": "<=1.32", "backpack/crud": "<3.4.9", "backpack/filemanager": "<2.0.2|>=3,<3.0.9", - "bacula-web/bacula-web": "<8.0.0.0-RC2-dev", - "badaso/core": "<2.7", - "bagisto/bagisto": "<2.1", + "bacula-web/bacula-web": "<9.7.1", + "badaso/core": "<=2.9.11", + "bagisto/bagisto": "<=2.3.7", "barrelstrength/sprout-base-email": "<1.2.7", "barrelstrength/sprout-forms": "<3.9", - "barryvdh/laravel-translation-manager": "<0.6.2", + "barryvdh/laravel-translation-manager": "<0.6.8", "barzahlen/barzahlen-php": "<2.0.1", "baserproject/basercms": "<=5.1.1", "bassjobsen/bootstrap-3-typeahead": ">4.0.2", "bbpress/bbpress": "<2.6.5", + "bcit-ci/codeigniter": "<3.1.3", "bcosca/fatfree": "<3.7.2", "bedita/bedita": "<4", + "bednee/cooluri": "<1.0.30", "bigfork/silverstripe-form-capture": ">=3,<3.1.1", - "billz/raspap-webgui": "<=3.1.4", + "billz/raspap-webgui": "<3.3.6", + "binarytorch/larecipe": "<2.8.1", "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", "blueimp/jquery-file-upload": "==6.4.4", "bmarshall511/wordpress_zero_spam": "<5.2.13", @@ -4005,6 +4019,7 @@ "brotkrueml/typo3-matomo-integration": "<1.3.2", "buddypress/buddypress": "<7.2.1", "bugsnag/bugsnag-laravel": ">=2,<2.0.2", + "bvbmedia/multishop": "<2.0.39", "bytefury/crater": "<6.0.2", "cachethq/cachet": "<2.5.1", "cakephp/cakephp": "<3.10.3|>=4,<4.0.10|>=4.1,<4.1.4|>=4.2,<4.2.12|>=4.3,<4.3.11|>=4.4,<4.4.10", @@ -4020,28 +4035,36 @@ "centreon/centreon": "<22.10.15", "cesnet/simplesamlphp-module-proxystatistics": "<3.1", "chriskacerguis/codeigniter-restserver": "<=2.7.1", + "chrome-php/chrome": "<1.14", "civicrm/civicrm-core": ">=4.2,<4.2.9|>=4.3,<4.3.3", "ckeditor/ckeditor": "<4.25", - "cockpit-hq/cockpit": "<2.7|==2.7", + "clickstorm/cs-seo": ">=6,<6.8|>=7,<7.5|>=8,<8.4|>=9,<9.3", + "co-stack/fal_sftp": "<0.2.6", + "cockpit-hq/cockpit": "<2.11.4", + "code16/sharp": "<9.11.1", "codeception/codeception": "<3.1.3|>=4,<4.1.22", - "codeigniter/framework": "<3.1.9", - "codeigniter4/framework": "<4.5.8", + "codeigniter/framework": "<3.1.10", + "codeigniter4/framework": "<4.6.2", "codeigniter4/shield": "<1.0.0.0-beta8", "codiad/codiad": "<=2.8.4", + "codingms/additional-tca": ">=1.7,<1.15.17|>=1.16,<1.16.9", + "codingms/modules": "<4.3.11|>=5,<5.7.4|>=6,<6.4.2|>=7,<7.5.5", + "commerceteam/commerce": ">=0.9.6,<0.9.9", "components/jquery": ">=1.0.3,<3.5", "composer/composer": "<1.10.27|>=2,<2.2.24|>=2.3,<2.7.7", - "concrete5/concrete5": "<9.3.4", + "concrete5/concrete5": "<9.4.3", "concrete5/core": "<8.5.8|>=9,<9.1", "contao-components/mediaelement": ">=2.14.2,<2.21.1", "contao/comments-bundle": ">=2,<4.13.40|>=5.0.0.0-RC1-dev,<5.3.4", - "contao/contao": "<=5.4.1", + "contao/contao": ">=3,<3.5.37|>=4,<4.4.56|>=4.5,<4.13.56|>=5,<5.3.38|>=5.4.0.0-RC1-dev,<5.6.1", "contao/core": "<3.5.39", - "contao/core-bundle": "<4.13.49|>=5,<5.3.15|>=5.4,<5.4.3", + "contao/core-bundle": "<4.13.56|>=5,<5.3.38|>=5.4,<5.6.1", "contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8", "contao/managed-edition": "<=1.5", "corveda/phpsandbox": "<1.3.5", "cosenary/instagram": "<=2.3", - "craftcms/cms": "<4.13.8|>=5,<5.5.5", + "couleurcitron/tarteaucitron-wp": "<0.3", + "craftcms/cms": "<=4.16.5|>=5,<=5.8.6", "croogo/croogo": "<4", "cuyz/valinor": "<0.12", "czim/file-handling": "<1.5|>=2,<2.3", @@ -4050,6 +4073,7 @@ "dapphp/securimage": "<3.6.6", "darylldoyle/safe-svg": "<1.9.10", "datadog/dd-trace": ">=0.30,<0.30.2", + "datahihi1/tiny-env": "<1.0.3|>=1.0.9,<1.0.11", "datatables/datatables": "<1.10.10", "david-garcia/phpwhois": "<=4.3.1", "dbrisinajumi/d2files": "<1", @@ -4058,8 +4082,13 @@ "derhansen/sf_event_mgt": "<4.3.1|>=5,<5.1.1|>=7,<7.4", "desperado/xml-bundle": "<=0.1.7", "dev-lancer/minecraft-motd-parser": "<=1.0.5", + "devcode-it/openstamanager": "<=2.9.4", "devgroup/dotplant": "<2020.09.14-dev", + "digimix/wp-svg-upload": "<=1", "directmailteam/direct-mail": "<6.0.3|>=7,<7.0.3|>=8,<9.5.2", + "dl/yag": "<3.0.1", + "dmk/webkitpdf": "<1.1.4", + "dnadesign/silverstripe-elemental": "<5.3.12", "doctrine/annotations": "<1.2.7", "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", "doctrine/common": "<2.4.3|>=2.5,<2.5.1", @@ -4069,12 +4098,45 @@ "doctrine/mongodb-odm": "<1.0.2", "doctrine/mongodb-odm-bundle": "<3.0.1", "doctrine/orm": ">=1,<1.2.4|>=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", - "dolibarr/dolibarr": "<19.0.2|==21.0.0.0-beta", + "dolibarr/dolibarr": "<21.0.3", "dompdf/dompdf": "<2.0.4", "doublethreedigital/guest-entries": "<3.1.2", - "drupal/core": ">=6,<6.38|>=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal-pattern-lab/unified-twig-extensions": "<=0.1", + "drupal/access_code": "<2.0.5", + "drupal/acquia_dam": "<1.1.5", + "drupal/admin_audit_trail": "<1.0.5", + "drupal/ai": "<1.0.5", + "drupal/alogin": "<2.0.6", + "drupal/cache_utility": "<1.2.1", + "drupal/civictheme": "<1.12", + "drupal/commerce_alphabank_redirect": "<1.0.3", + "drupal/commerce_eurobank_redirect": "<2.1.1", + "drupal/config_split": "<1.10|>=2,<2.0.2", + "drupal/core": ">=6,<6.38|>=7,<7.102|>=8,<10.4.9|>=10.5,<10.5.6|>=11,<11.1.9|>=11.2,<11.2.8", "drupal/core-recommended": ">=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal/currency": "<3.5", "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal/email_tfa": "<2.0.6", + "drupal/formatter_suite": "<2.1", + "drupal/gdpr": "<3.0.1|>=3.1,<3.1.2", + "drupal/google_tag": "<1.8|>=2,<2.0.8", + "drupal/ignition": "<1.0.4", + "drupal/json_field": "<1.5", + "drupal/lightgallery": "<1.6", + "drupal/link_field_display_mode_formatter": "<1.6", + "drupal/matomo": "<1.24", + "drupal/oauth2_client": "<4.1.3", + "drupal/oauth2_server": "<2.1", + "drupal/obfuscate": "<2.0.1", + "drupal/plausible_tracking": "<1.0.2", + "drupal/quick_node_block": "<2", + "drupal/rapidoc_elements_field_formatter": "<1.0.1", + "drupal/reverse_proxy_header": "<1.1.2", + "drupal/simple_multistep": "<2", + "drupal/simple_oauth": ">=6,<6.0.7", + "drupal/spamspan": "<3.2.1", + "drupal/tfa": "<1.10", + "drupal/umami_analytics": "<1.0.1", "duncanmcclean/guest-entries": "<3.1.2", "dweeves/magmi": "<=0.7.24", "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2", @@ -4084,10 +4146,11 @@ "elefant/cms": "<2.0.7", "elgg/elgg": "<3.3.24|>=4,<4.0.5", "elijaa/phpmemcacheadmin": "<=1.3", + "elmsln/haxcms": "<11.0.14", "encore/laravel-admin": "<=1.8.19", "endroid/qr-code-bundle": "<3.4.2", "enhavo/enhavo-app": "<=0.13.1", - "enshrined/svg-sanitize": "<0.15", + "enshrined/svg-sanitize": "<0.22", "erusev/parsedown": "<1.7.2", "ether/logs": "<3.0.4", "evolutioncms/evolution": "<=3.2.3", @@ -4098,13 +4161,13 @@ "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1-dev", "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1-dev|>=5.4,<5.4.11.1-dev|>=2017.12,<2017.12.0.1-dev", "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", - "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.26|>=3.3,<3.3.39", - "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.39|>=3.3,<3.3.39", + "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1|>=5.3.0.0-beta1,<5.3.5", "ezsystems/ezplatform-graphql": ">=1.0.0.0-RC1-dev,<1.0.13|>=2.0.0.0-beta1,<2.3.12", "ezsystems/ezplatform-http-cache": "<2.3.16", "ezsystems/ezplatform-kernel": "<1.2.5.1-dev|>=1.3,<1.3.35", "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", - "ezsystems/ezplatform-richtext": ">=2.3,<2.3.7.1-dev|>=3.3,<3.3.40", + "ezsystems/ezplatform-richtext": ">=2.3,<2.3.26|>=3.3,<3.3.40", "ezsystems/ezplatform-solr-search-engine": ">=1.7,<1.7.12|>=2,<2.0.2|>=3.3,<3.3.15", "ezsystems/ezplatform-user": ">=1,<1.0.1", "ezsystems/ezpublish-kernel": "<6.13.8.2-dev|>=7,<7.5.31", @@ -4127,10 +4190,10 @@ "firebase/php-jwt": "<6", "fisharebest/webtrees": "<=2.1.18", "fixpunkt/fp-masterquiz": "<2.2.1|>=3,<3.5.2", - "fixpunkt/fp-newsletter": "<1.1.1|>=2,<2.1.2|>=2.2,<3.2.6", - "flarum/core": "<1.8.5", + "fixpunkt/fp-newsletter": "<1.1.1|>=1.2,<2.1.2|>=2.2,<3.2.6", + "flarum/core": "<1.8.10", "flarum/flarum": "<0.1.0.0-beta8", - "flarum/framework": "<1.8.5", + "flarum/framework": "<1.8.10", "flarum/mentions": "<1.6.3", "flarum/sticky": ">=0.1.0.0-beta14,<=0.1.0.0-beta15", "flarum/tags": "<=0.1.0.0-beta13", @@ -4151,26 +4214,29 @@ "friendsofsymfony1/symfony1": ">=1.1,<1.5.19", "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", "friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6", - "froala/wysiwyg-editor": "<3.2.7|>=4.0.1,<=4.1.3", - "froxlor/froxlor": "<=2.2.0.0-RC3", + "froala/wysiwyg-editor": "<=4.3", + "froxlor/froxlor": "<=2.2.5", "frozennode/administrator": "<=5.0.12", "fuel/core": "<1.8.1", "funadmin/funadmin": "<=5.0.2", "gaoming13/wechat-php-sdk": "<=1.10.2", "genix/cms": "<=1.1.11", - "getformwork/formwork": "<=2.0.0.0-beta3", + "georgringer/news": "<1.3.3", + "geshi/geshi": "<=1.0.9.1", + "getformwork/formwork": "<1.13.1|>=2.0.0.0-beta1,<2.0.0.0-beta4", "getgrav/grav": "<1.7.46", - "getkirby/cms": "<=3.6.6.5|>=3.7,<=3.7.5.4|>=3.8,<=3.8.4.3|>=3.9,<=3.9.8.1|>=3.10,<=3.10.1|>=4,<=4.3", - "getkirby/kirby": "<=2.5.12", + "getkirby/cms": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1|>=5,<5.1.4", + "getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1", "getkirby/panel": "<2.5.14", "getkirby/starterkit": "<=3.7.0.2", "gilacms/gila": "<=1.15.4", "gleez/cms": "<=1.3|==2", "globalpayments/php-sdk": "<2", - "goalgorilla/open_social": "<12.3.8|>=12.4,<12.4.5|>=13.0.0.0-alpha1,<13.0.0.0-alpha11", + "goalgorilla/open_social": "<12.3.11|>=12.4,<12.4.10|>=13.0.0.0-alpha1,<13.0.0.0-alpha11", "gogentooss/samlbase": "<1.2.7", - "google/protobuf": "<3.15", + "google/protobuf": "<3.4", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", + "gp247/core": "<1.1.24", "gree/jose": "<2.2.1", "gregwar/rst": "<1.0.3", "grumpydictator/firefly-iii": "<6.1.17", @@ -4179,6 +4245,7 @@ "guzzlehttp/oauth-subscriber": "<0.8.1", "guzzlehttp/psr7": "<1.9.1|>=2,<2.4.5", "haffner/jh_captcha": "<=2.1.3|>=3,<=3.0.2", + "handcraftedinthealps/goodby-csv": "<1.4.3", "harvesthq/chosen": "<1.8.7", "helloxz/imgurl": "<=2.31", "hhxsv5/laravel-s": "<3.7.36", @@ -4188,14 +4255,15 @@ "hov/jobfair": "<1.0.13|>=2,<2.0.2", "httpsoft/http-message": "<1.0.12", "hyn/multi-tenant": ">=5.6,<5.7.2", - "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6,<4.6.14", + "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6,<4.6.25|>=5,<5.0.3", + "ibexa/admin-ui-assets": ">=4.6.0.0-alpha1,<4.6.21", "ibexa/core": ">=4,<4.0.7|>=4.1,<4.1.4|>=4.2,<4.2.3|>=4.5,<4.5.6|>=4.6,<4.6.2", - "ibexa/fieldtype-richtext": ">=4.6,<4.6.10", + "ibexa/fieldtype-richtext": ">=4.6,<4.6.25|>=5,<5.0.3", "ibexa/graphql": ">=2.5,<2.5.31|>=3.3,<3.3.28|>=4.2,<4.2.3", "ibexa/http-cache": ">=4.6,<4.6.14", "ibexa/post-install": "<1.0.16|>=4.6,<4.6.14", "ibexa/solr": ">=4.5,<4.5.4", - "ibexa/user": ">=4,<4.4.3", + "ibexa/user": ">=4,<4.4.3|>=5,<5.0.3", "icecoder/icecoder": "<=8.1", "idno/known": "<=1.3.1", "ilicmiljan/secure-props": ">=1.2,<1.2.2", @@ -4206,11 +4274,11 @@ "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", "imdbphp/imdbphp": "<=5.1.1", "impresscms/impresscms": "<=1.4.5", - "impresspages/impresspages": "<=1.0.12", - "in2code/femanager": "<5.5.3|>=6,<6.3.4|>=7,<7.2.3", + "impresspages/impresspages": "<1.0.13", + "in2code/femanager": "<6.4.2|>=7,<7.5.3|>=8,<8.3.1", "in2code/ipandlanguageredirect": "<5.1.2", "in2code/lux": "<17.6.1|>=18,<24.0.2", - "in2code/powermail": "<7.5.1|>=8,<8.5.1|>=9,<10.9.1|>=11,<12.4.1", + "in2code/powermail": "<7.5.1|>=8,<8.5.1|>=9,<10.9.1|>=11,<12.5.3|==13", "innologi/typo3-appointments": "<2.0.6", "intelliants/subrion": "<4.2.2", "inter-mediator/inter-mediator": "==5.5", @@ -4219,25 +4287,30 @@ "islandora/islandora": ">=2,<2.4.1", "ivankristianto/phpwhois": "<=4.3", "jackalope/jackalope-doctrine-dbal": "<1.7.4", + "jambagecom/div2007": "<0.10.2", "james-heinrich/getid3": "<1.9.21", - "james-heinrich/phpthumb": "<1.7.12", + "james-heinrich/phpthumb": "<=1.7.23", "jasig/phpcas": "<1.3.3", + "jbartels/wec-map": "<3.0.3", "jcbrand/converse.js": "<3.3.3", "joelbutcher/socialstream": "<5.6|>=6,<6.2", - "johnbillion/wp-crontrol": "<1.16.2", + "johnbillion/wp-crontrol": "<1.16.2|>=1.17,<1.19.2", "joomla/application": "<1.0.13", "joomla/archive": "<1.1.12|>=2,<2.0.1", + "joomla/database": ">=1,<2.2|>=3,<3.4", "joomla/filesystem": "<1.6.2|>=2,<2.0.1", - "joomla/filter": "<1.4.4|>=2,<2.0.1", + "joomla/filter": "<2.0.6|>=3,<3.0.5|==4", "joomla/framework": "<1.5.7|>=2.5.4,<=3.8.12", "joomla/input": ">=2,<2.0.2", - "joomla/joomla-cms": ">=2.5,<3.9.12", + "joomla/joomla-cms": "<3.9.12|>=4,<4.4.13|>=5,<5.2.6", + "joomla/joomla-platform": "<1.5.4", "joomla/session": "<1.3.1", "joyqi/hyper-down": "<=2.4.27", "jsdecena/laracom": "<2.0.9", "jsmitty12/phpwhois": "<5.1", - "juzaweb/cms": "<=3.4", + "juzaweb/cms": "<=3.4.2", "jweiland/events2": "<8.3.8|>=9,<9.0.6", + "jweiland/kk-downloader": "<1.2.2", "kazist/phpwhois": "<=4.2.6", "kelvinmo/simplexrd": "<3.1.1", "kevinpapst/kimai2": "<1.16.7", @@ -4247,6 +4320,7 @@ "klaviyo/magento2-extension": ">=1,<3", "knplabs/knp-snappy": "<=1.4.2", "kohana/core": "<3.3.3", + "koillection/koillection": "<1.6.12", "krayin/laravel-crm": "<=1.3", "kreait/firebase-php": ">=3.2,<3.8.1", "kumbiaphp/kumbiapp": "<=1.1.1", @@ -4257,15 +4331,16 @@ "lara-zeus/artemis": ">=1,<=1.0.6", "lara-zeus/dynamic-dashboard": ">=3,<=3.0.1", "laravel/fortify": "<1.11.1", - "laravel/framework": "<11.44.1|>=12,<12.1.1", + "laravel/framework": "<10.48.29|>=11,<11.44.1|>=12,<12.1.1", "laravel/laravel": ">=5.4,<5.4.22", "laravel/pulse": "<1.3.1", "laravel/reverb": "<1.4", "laravel/socialite": ">=1,<2.0.10", "latte/latte": "<2.10.8", "lavalite/cms": "<=9|==10.1", + "lavitto/typo3-form-to-database": "<2.2.5|>=3,<3.2.2|>=4,<4.2.3|>=5,<5.0.2", "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", - "league/commonmark": "<2.6", + "league/commonmark": "<2.7", "league/flysystem": "<1.1.4|>=2,<2.1.1", "league/oauth2-server": ">=8.3.2,<8.4.2|>=8.5,<8.5.3", "leantime/leantime": "<3.3", @@ -4276,65 +4351,77 @@ "lightsaml/lightsaml": "<1.3.5", "limesurvey/limesurvey": "<6.5.12", "livehelperchat/livehelperchat": "<=3.91", - "livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<3.5.2", + "livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<3.6.4", "livewire/volt": "<1.7", "lms/routes": "<2.1.1", "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", + "lomkit/laravel-rest-api": "<2.13", + "luracast/restler": "<3.1", "luyadev/yii-helpers": "<1.2.1", + "macropay-solutions/laravel-crud-wizard-free": "<3.4.17", "maestroerror/php-heic-to-jpg": "<1.0.5", - "magento/community-edition": "<2.4.5|==2.4.5|>=2.4.5.0-patch1,<2.4.5.0-patch11|==2.4.6|>=2.4.6.0-patch1,<2.4.6.0-patch9|>=2.4.7.0-beta1,<2.4.7.0-patch4|>=2.4.8.0-beta1,<2.4.8.0-beta2", + "magento/community-edition": "<2.4.6.0-patch13|>=2.4.7.0-beta1,<2.4.7.0-patch8|>=2.4.8.0-beta1,<2.4.8.0-patch3|>=2.4.9.0-alpha1,<2.4.9.0-alpha3|==2.4.9", "magento/core": "<=1.9.4.5", "magento/magento1ce": "<1.9.4.3-dev", "magento/magento1ee": ">=1,<1.14.4.3-dev", "magento/product-community-edition": "<2.4.4.0-patch9|>=2.4.5,<2.4.5.0-patch8|>=2.4.6,<2.4.6.0-patch6|>=2.4.7,<2.4.7.0-patch1", "magento/project-community-edition": "<=2.0.2", "magneto/core": "<1.9.4.4-dev", + "mahocommerce/maho": "<25.9", "maikuolan/phpmussel": ">=1,<1.6", "mainwp/mainwp": "<=4.4.3.3", - "mantisbt/mantisbt": "<=2.26.3", + "manogi/nova-tiptap": "<=3.2.6", + "mantisbt/mantisbt": "<2.27.2", "marcwillmann/turn": "<0.3.3", + "marshmallow/nova-tiptap": "<5.7", + "matomo/matomo": "<1.11", "matyhtf/framework": "<3.0.6", - "mautic/core": "<5.2.3", + "mautic/core": "<5.2.8|>=6.0.0.0-alpha,<6.0.5", "mautic/core-lib": ">=1.0.0.0-beta,<4.4.13|>=5.0.0.0-alpha,<5.1.1", "maximebf/debugbar": "<1.19", "mdanter/ecc": "<2", "mediawiki/abuse-filter": "<1.39.9|>=1.40,<1.41.3|>=1.42,<1.42.2", - "mediawiki/cargo": "<3.6.1", + "mediawiki/cargo": "<3.8.3", "mediawiki/core": "<1.39.5|==1.40", "mediawiki/data-transfer": ">=1.39,<1.39.11|>=1.41,<1.41.3|>=1.42,<1.42.2", "mediawiki/matomo": "<2.4.3", "mediawiki/semantic-media-wiki": "<4.0.2", + "mehrwert/phpmyadmin": "<3.2", "melisplatform/melis-asset-manager": "<5.0.1", - "melisplatform/melis-cms": "<5.0.1", + "melisplatform/melis-cms": "<5.3.4", + "melisplatform/melis-cms-slider": "<5.3.1", + "melisplatform/melis-core": "<5.3.11", "melisplatform/melis-front": "<5.0.1", "mezzio/mezzio-swoole": "<3.7|>=4,<4.3", "mgallegos/laravel-jqgrid": "<=1.3", "microsoft/microsoft-graph": ">=1.16,<1.109.1|>=2,<2.0.1", "microsoft/microsoft-graph-beta": "<2.0.1", "microsoft/microsoft-graph-core": "<2.0.2", - "microweber/microweber": "<=2.0.16", + "microweber/microweber": "<=2.0.19", "mikehaertl/php-shellcommand": "<1.6.1", "miniorange/miniorange-saml": "<1.4.3", "mittwald/typo3_forum": "<1.2.1", "mobiledetect/mobiledetectlib": "<2.8.32", - "modx/revolution": "<=2.8.3.0-patch", + "modx/revolution": "<=3.1", "mojo42/jirafeau": "<4.4", "mongodb/mongodb": ">=1,<1.9.2", + "mongodb/mongodb-extension": "<1.21.2", "monolog/monolog": ">=1.8,<1.12", - "moodle/moodle": "<4.3.10|>=4.4,<4.4.6|>=4.5.0.0-beta,<4.5.2", + "moodle/moodle": "<4.4.11|>=4.5.0.0-beta,<4.5.7|>=5.0.0.0-beta,<5.0.3", + "moonshine/moonshine": "<=3.12.5", "mos/cimage": "<0.7.19", "movim/moxl": ">=0.8,<=0.10", "movingbytes/social-network": "<=1.2.1", "mpdf/mpdf": "<=7.1.7", - "munkireport/comment": "<4.1", + "munkireport/comment": "<4", "munkireport/managedinstalls": "<2.6", "munkireport/munki_facts": "<1.5", - "munkireport/munkireport": ">=2.5.3,<5.6.3", "munkireport/reportdata": "<3.5", "munkireport/softwareupdate": "<1.6", "mustache/mustache": ">=2,<2.14.1", "mwdelaney/wp-enable-svg": "<=0.2", "namshi/jose": "<2.2", + "nasirkhan/laravel-starter": "<11.11", "nategood/httpful": "<1", "neoan3-apps/template": "<1.1.1", "neorazorx/facturascripts": "<2022.04", @@ -4349,10 +4436,12 @@ "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", "nilsteampassnet/teampass": "<3.1.3.1-dev", + "nitsan/ns-backup": "<13.0.1", "nonfiction/nterchange": "<4.1.1", "notrinos/notrinos-erp": "<=0.7", "noumo/easyii": "<=0.9", "novaksolutions/infusionsoft-php-sdk": "<1", + "novosga/novosga": "<=2.2.12", "nukeviet/nukeviet": "<4.5.02", "nyholm/psr7": "<1.6.1", "nystudio107/craft-seomatic": "<3.4.12", @@ -4360,16 +4449,17 @@ "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", "october/backend": "<1.1.2", "october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1", - "october/october": "<=3.6.4", + "october/october": "<3.7.5", "october/rain": "<1.0.472|>=1.1,<1.1.2", - "october/system": "<1.0.476|>=1.1,<1.1.12|>=2,<2.2.34|>=3,<3.5.15", + "october/system": "<3.7.5", + "oliverklee/phpunit": "<3.5.15", "omeka/omeka-s": "<4.0.3", "onelogin/php-saml": "<2.10.4", "oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5", - "open-web-analytics/open-web-analytics": "<1.7.4", + "open-web-analytics/open-web-analytics": "<1.8.1", "opencart/opencart": ">=0", "openid/php-openid": "<2.3", - "openmage/magento-lts": "<20.12.3", + "openmage/magento-lts": "<20.16", "opensolutions/vimbadmin": "<=3.0.15", "opensource-workshop/connect-cms": "<1.8.7|>=2,<2.4.7", "orchid/platform": ">=8,<14.43", @@ -4380,7 +4470,7 @@ "oro/customer-portal": ">=4.1,<=4.1.13|>=4.2,<=4.2.10|>=5,<=5.0.11|>=5.1,<=5.1.3", "oro/platform": ">=1.7,<1.7.4|>=3.1,<3.1.29|>=4.1,<4.1.17|>=4.2,<=4.2.10|>=5,<=5.0.12|>=5.1,<=5.1.3", "oveleon/contao-cookiebar": "<1.16.3|>=2,<2.1.3", - "oxid-esales/oxideshop-ce": "<4.5", + "oxid-esales/oxideshop-ce": "<=7.0.5", "oxid-esales/paymorrow-module": ">=1,<1.0.2|>=2,<2.0.1", "packbackbooks/lti-1-3-php-library": "<5", "padraic/humbug_get_contents": "<1.1.2", @@ -4396,6 +4486,7 @@ "pear/archive_tar": "<1.4.14", "pear/auth": "<1.2.4", "pear/crypt_gpg": "<1.6.7", + "pear/http_request2": "<2.7", "pear/pear": "<=1.10.1", "pegasus/google-for-jobs": "<1.5.1|>=2,<2.1.1", "personnummer/personnummer": "<3.0.2", @@ -4409,10 +4500,12 @@ "phpmailer/phpmailer": "<6.5", "phpmussel/phpmussel": ">=1,<1.6", "phpmyadmin/phpmyadmin": "<5.2.2", - "phpmyfaq/phpmyfaq": "<3.2.5|==3.2.5|>=3.2.10,<=4.0.1", + "phpmyfaq/phpmyfaq": "<=4.0.13", "phpoffice/common": "<0.2.9", + "phpoffice/math": "<=0.2", "phpoffice/phpexcel": "<=1.8.2", - "phpoffice/phpspreadsheet": "<1.29.9|>=2,<2.1.8|>=2.2,<2.3.7|>=3,<3.9", + "phpoffice/phpspreadsheet": "<1.30|>=2,<2.1.12|>=2.2,<2.4|>=3,<3.10|>=4,<5", + "phppgadmin/phppgadmin": "<=7.13", "phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36", "phpservermon/phpservermon": "<3.6", "phpsysinfo/phpsysinfo": "<3.4.3", @@ -4421,18 +4514,19 @@ "phpxmlrpc/extras": "<0.6.1", "phpxmlrpc/phpxmlrpc": "<4.9.2", "pi/pi": "<=2.5", - "pimcore/admin-ui-classic-bundle": "<1.7.4", + "pimcore/admin-ui-classic-bundle": "<1.7.6", "pimcore/customer-management-framework-bundle": "<4.2.1", "pimcore/data-hub": "<1.2.4", "pimcore/data-importer": "<1.8.9|>=1.9,<1.9.3", "pimcore/demo": "<10.3", "pimcore/ecommerce-framework-bundle": "<1.0.10", "pimcore/perspective-editor": "<1.5.1", - "pimcore/pimcore": "<11.2.4|>=11.4.2,<11.5.3", - "pixelfed/pixelfed": "<0.11.11", + "pimcore/pimcore": "<11.5.4", + "piwik/piwik": "<1.11", + "pixelfed/pixelfed": "<0.12.5", "plotly/plotly.js": "<2.25.2", "pocketmine/bedrock-protocol": "<8.0.2", - "pocketmine/pocketmine-mp": "<5.11.2", + "pocketmine/pocketmine-mp": "<5.32.1", "pocketmine/raklib": ">=0.14,<0.14.6|>=0.15,<0.15.1", "pressbooks/pressbooks": "<5.18", "prestashop/autoupgrade": ">=4,<4.10.1", @@ -4440,20 +4534,22 @@ "prestashop/blockwishlist": ">=2,<2.1.1", "prestashop/contactform": ">=1.0.1,<4.3", "prestashop/gamification": "<2.3.2", - "prestashop/prestashop": "<8.1.6", + "prestashop/prestashop": "<8.2.3", "prestashop/productcomments": "<5.0.2", + "prestashop/ps_checkout": "<4.4.1|>=5,<5.0.5", "prestashop/ps_contactinfo": "<=3.3.2", "prestashop/ps_emailsubscription": "<2.6.1", "prestashop/ps_facetedsearch": "<3.4.1", "prestashop/ps_linklist": "<3.1", - "privatebin/privatebin": "<1.4|>=1.5,<1.7.4", - "processwire/processwire": "<=3.0.229", + "privatebin/privatebin": "<1.4|>=1.5,<1.7.4|>=1.7.7,<2.0.3", + "processwire/processwire": "<=3.0.246", "propel/propel": ">=2.0.0.0-alpha1,<=2.0.0.0-alpha7", "propel/propel1": ">=1,<=1.7.1", - "pterodactyl/panel": "<1.11.8", + "pterodactyl/panel": "<=1.11.10", "ptheofan/yii2-statemachine": ">=2.0.0.0-RC1-dev,<=2", "ptrofimov/beanstalk_console": "<1.7.14", "pubnub/pubnub": "<6.1", + "punktde/pt_extbase": "<1.5.1", "pusher/pusher-php-server": "<2.2.1", "pwweb/laravel-core": "<=0.3.6.0-beta", "pxlrbt/filament-excel": "<1.1.14|>=2.0.0.0-alpha,<2.3.3", @@ -4469,14 +4565,16 @@ "really-simple-plugins/complianz-gdpr": "<6.4.2", "redaxo/source": "<5.18.3", "remdex/livehelperchat": "<4.29", + "renolit/reint-downloadmanager": "<4.0.2|>=5,<5.0.1", "reportico-web/reportico": "<=8.1", "rhukster/dom-sanitizer": "<1.0.7", "rmccue/requests": ">=1.6,<1.8", "robrichards/xmlseclibs": ">=1,<3.0.4", "roots/soil": "<4.1", + "roundcube/roundcubemail": "<1.5.10|>=1.6,<1.6.11", "rudloff/alltube": "<3.0.3", "rudloff/rtmpdump-bin": "<=2.3.1", - "s-cart/core": "<6.9", + "s-cart/core": "<=9.0.5", "s-cart/s-cart": "<6.9", "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", "sabre/dav": ">=1.6,<1.7.11|>=1.8,<1.8.9", @@ -4484,14 +4582,15 @@ "scheb/two-factor-bundle": "<3.26|>=4,<4.11", "sensiolabs/connect": "<4.2.3", "serluck/phpwhois": "<=4.2.6", + "setasign/fpdi": "<2.6.4", "sfroemken/url_redirect": "<=1.2.1", "sheng/yiicms": "<1.2.1", - "shopware/core": "<=6.5.8.12|>=6.6,<=6.6.5", - "shopware/platform": "<=6.5.8.12|>=6.6,<=6.6.5", + "shopware/core": "<6.6.10.9-dev|>=6.7,<6.7.4.1-dev", + "shopware/platform": "<6.6.10.7-dev|>=6.7,<6.7.3.1-dev", "shopware/production": "<=6.3.5.2", - "shopware/shopware": "<=5.7.17", + "shopware/shopware": "<=5.7.17|>=6.7,<6.7.2.1-dev", "shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev", - "shopxo/shopxo": "<=6.1", + "shopxo/shopxo": "<=6.4", "showdoc/showdoc": "<2.10.4", "shuchkin/simplexlsx": ">=1.0.12,<1.1.13", "silverstripe-australia/advancedreports": ">=1,<=2", @@ -4500,7 +4599,7 @@ "silverstripe/cms": "<4.11.3", "silverstripe/comments": ">=1.3,<3.1.1", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", - "silverstripe/framework": "<5.3.8", + "silverstripe/framework": "<5.3.23", "silverstripe/graphql": ">=2,<2.0.5|>=3,<3.8.2|>=4,<4.3.7|>=5,<5.1.3", "silverstripe/hybridsessions": ">=1,<2.4.1|>=2.5,<2.5.1", "silverstripe/recipe-cms": ">=4.5,<4.5.3", @@ -4512,9 +4611,10 @@ "silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1", "silverstripe/userforms": "<3|>=5,<5.4.2", "silverstripe/versioned-admin": ">=1,<1.11.1", + "simogeo/filemanager": "<=2.5", "simple-updates/phpwhois": "<=1", - "simplesamlphp/saml2": "<4.6.14|==5.0.0.0-alpha12", - "simplesamlphp/saml2-legacy": "<4.6.14", + "simplesamlphp/saml2": "<=4.16.15|>=5.0.0.0-alpha1,<=5.0.0.0-alpha19", + "simplesamlphp/saml2-legacy": "<=4.16.15", "simplesamlphp/simplesamlphp": "<1.18.6", "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", "simplesamlphp/simplesamlphp-module-openid": "<1", @@ -4523,41 +4623,49 @@ "simplesamlphp/xml-security": "==1.6.11", "simplito/elliptic-php": "<1.0.6", "sitegeist/fluid-components": "<3.5", + "sjbr/sr-feuser-register": "<2.6.2|>=5.1,<12.5", "sjbr/sr-freecap": "<2.4.6|>=2.5,<2.5.3", + "sjbr/static-info-tables": "<2.3.1", "slim/psr7": "<1.4.1|>=1.5,<1.5.1|>=1.6,<1.6.1", "slim/slim": "<2.6", "slub/slub-events": "<3.0.3", "smarty/smarty": "<4.5.3|>=5,<5.1.1", - "snipe/snipe-it": "<=7.0.13", + "snipe/snipe-it": "<=8.3.4", "socalnick/scn-social-auth": "<1.15.2", "socialiteproviders/steam": "<1.1", + "solspace/craft-freeform": ">=5,<5.10.16", + "soosyze/soosyze": "<=2", "spatie/browsershot": "<5.0.5", "spatie/image-optimizer": "<1.7.3", "spencer14420/sp-php-email-handler": "<1", "spipu/html2pdf": "<5.2.8", + "spiral/roadrunner": "<2025.1", "spoon/library": "<1.4.1", "spoonity/tcpdf": "<6.2.22", "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", "ssddanbrown/bookstack": "<24.05.1", - "starcitizentools/citizen-skin": ">=2.6.3,<2.31", - "starcitizentools/tabber-neue": ">=1.9.1,<2.7.2", - "statamic/cms": "<=5.16", + "starcitizentools/citizen-skin": ">=1.9.4,<3.9", + "starcitizentools/short-description": ">=4,<4.0.1", + "starcitizentools/tabber-neue": ">=1.9.1,<2.7.2|>=3,<3.1.1", + "starcitizenwiki/embedvideo": "<=4", + "statamic/cms": "<=5.22", "stormpath/sdk": "<9.9.99", "studio-42/elfinder": "<=2.1.64", "studiomitte/friendlycaptcha": "<0.1.4", "subhh/libconnect": "<7.0.8|>=8,<8.1", "sukohi/surpass": "<1", "sulu/form-bundle": ">=2,<2.5.3", - "sulu/sulu": "<1.6.44|>=2,<2.5.21|>=2.6,<2.6.5", + "sulu/sulu": "<1.6.44|>=2,<2.5.25|>=2.6,<2.6.9|>=3.0.0.0-alpha1,<3.0.0.0-alpha3", "sumocoders/framework-user-bundle": "<1.4", "superbig/craft-audit": "<3.0.2", + "svewap/a21glossary": "<=0.4.10", "swag/paypal": "<5.4.4", "swiftmailer/swiftmailer": "<6.2.5", "swiftyedit/swiftyedit": "<1.2", "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", "sylius/grid-bundle": "<1.10.1", - "sylius/paypal-plugin": ">=1,<1.2.4|>=1.3,<1.3.1", + "sylius/paypal-plugin": "<1.6.2|>=1.7,<1.7.2|>=2,<2.0.2", "sylius/resource-bundle": ">=1,<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", "sylius/sylius": "<1.12.19|>=1.13.0.0-alpha1,<1.13.4", "symbiote/silverstripe-multivaluefield": ">=3,<3.1", @@ -4571,7 +4679,7 @@ "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<5.3.15|>=5.4.3,<5.4.4|>=6.0.3,<6.0.4", "symfony/http-client": ">=4.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", - "symfony/http-foundation": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7", + "symfony/http-foundation": "<5.4.50|>=6,<6.4.29|>=7,<7.3.7", "symfony/http-kernel": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", "symfony/maker-bundle": ">=1.27,<1.29.2|>=1.30,<1.31.1", @@ -4590,10 +4698,12 @@ "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", - "symfony/symfony": "<5.4.47|>=6,<6.4.15|>=7,<7.1.8", + "symfony/symfony": "<5.4.50|>=6,<6.4.29|>=7,<7.3.7", "symfony/translation": ">=2,<2.0.17", "symfony/twig-bridge": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8", "symfony/ux-autocomplete": "<2.11.2", + "symfony/ux-live-component": "<2.25.1", + "symfony/ux-twig-component": "<2.25.1", "symfony/validator": "<5.4.43|>=6,<6.4.11|>=7,<7.1.4", "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", @@ -4603,7 +4713,7 @@ "t3/dce": "<0.11.5|>=2.2,<2.6.2", "t3g/svg-sanitizer": "<1.0.3", "t3s/content-consent": "<1.0.3|>=2,<2.0.2", - "tastyigniter/tastyigniter": "<3.3", + "tastyigniter/tastyigniter": "<4", "tcg/voyager": "<=1.8", "tecnickcom/tc-lib-pdf-font": "<2.6.4", "tecnickcom/tcpdf": "<6.8", @@ -4612,7 +4722,7 @@ "thelia/thelia": ">=2.1,<2.1.3", "theonedemon/phpwhois": "<=4.2.5", "thinkcmf/thinkcmf": "<6.0.8", - "thorsten/phpmyfaq": "<=4.0.1", + "thorsten/phpmyfaq": "<=4.0.13", "tikiwiki/tiki-manager": "<=17.1", "timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1", "tinymce/tinymce": "<7.2", @@ -4623,29 +4733,35 @@ "topthink/framework": "<6.0.17|>=6.1,<=8.0.4", "topthink/think": "<=6.1.1", "topthink/thinkphp": "<=3.2.3|>=6.1.3,<=8.0.4", - "torrentpier/torrentpier": "<=2.4.3", + "torrentpier/torrentpier": "<=2.8.8", "tpwd/ke_search": "<4.0.3|>=4.1,<4.6.6|>=5,<5.0.2", "tribalsystems/zenario": "<=9.7.61188", "truckersmp/phpwhois": "<=4.3.1", "ttskch/pagination-service-provider": "<1", - "twbs/bootstrap": "<=3.4.1|>=4,<=4.6.2", + "twbs/bootstrap": "<3.4.1|>=4,<4.3.1", "twig/twig": "<3.11.2|>=3.12,<3.14.1|>=3.16,<3.19", "typo3/cms": "<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2", - "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<=9.5.24|>=10,<10.4.46|>=11,<11.5.40|>=12,<12.4.21|>=13,<13.3.1", + "typo3/cms-backend": "<4.1.14|>=4.2,<4.2.15|>=4.3,<4.3.7|>=4.4,<4.4.4|>=7,<=7.6.50|>=8,<=8.7.39|>=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/cms-belog": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", - "typo3/cms-beuser": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", - "typo3/cms-core": "<=8.7.56|>=9,<=9.5.48|>=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", - "typo3/cms-dashboard": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-beuser": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", + "typo3/cms-core": "<=8.7.56|>=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", + "typo3/cms-dashboard": ">=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/cms-extbase": "<6.2.24|>=7,<7.6.8|==8.1.1", "typo3/cms-extensionmanager": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-felogin": ">=4.2,<4.2.3", "typo3/cms-fluid": "<4.3.4|>=4.4,<4.4.1", "typo3/cms-form": ">=8,<=8.7.39|>=9,<=9.5.24|>=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", "typo3/cms-frontend": "<4.3.9|>=4.4,<4.4.5", "typo3/cms-indexed-search": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", "typo3/cms-install": "<4.1.14|>=4.2,<4.2.16|>=4.3,<4.3.9|>=4.4,<4.4.5|>=12.2,<12.4.8|==13.4.2", "typo3/cms-lowlevel": ">=11,<=11.5.41", + "typo3/cms-recordlist": ">=11,<11.5.48", + "typo3/cms-recycler": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/cms-rte-ckeditor": ">=9.5,<9.5.42|>=10,<10.4.39|>=11,<11.5.30", "typo3/cms-scheduler": ">=11,<=11.5.41", + "typo3/cms-setup": ">=9,<=9.5.50|>=10,<=10.4.49|>=11,<=11.5.43|>=12,<=12.4.30|>=13,<=13.4.11", + "typo3/cms-webhooks": ">=12,<=12.4.30|>=13,<=13.4.11", + "typo3/cms-workspaces": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", "typo3/html-sanitizer": ">=1,<=1.5.2|>=2,<=2.1.3", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", @@ -4655,33 +4771,38 @@ "ua-parser/uap-php": "<3.8", "uasoft-indonesia/badaso": "<=2.9.7", "unisharp/laravel-filemanager": "<2.9.1", - "unopim/unopim": "<0.1.5", + "universal-omega/dynamic-page-list3": "<3.6.4", + "unopim/unopim": "<=0.3", "userfrosting/userfrosting": ">=0.3.1,<4.6.3", "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", "uvdesk/community-skeleton": "<=1.1.1", "uvdesk/core-framework": "<=1.1.1", "vanilla/safecurl": "<0.9.2", "verbb/comments": "<1.5.5", - "verbb/formie": "<2.1.6", + "verbb/formie": "<=2.1.43", "verbb/image-resizer": "<2.0.9", "verbb/knock-knock": "<1.2.8", "verot/class.upload.php": "<=2.1.6", + "vertexvaar/falsftp": "<0.2.6", "villagedefrance/opencart-overclocked": "<=1.11.1", "vova07/yii2-fileapi-widget": "<0.1.9", - "vrana/adminer": "<4.8.1", + "vrana/adminer": "<=4.8.1", "vufind/vufind": ">=2,<9.1.1", "waldhacker/hcaptcha": "<2.1.2", "wallabag/tcpdf": "<6.2.22", - "wallabag/wallabag": "<2.6.7", + "wallabag/wallabag": "<2.6.11", "wanglelecc/laracms": "<=1.0.3", + "wapplersystems/a21glossary": "<=0.4.10", "web-auth/webauthn-framework": ">=3.3,<3.3.4|>=4.5,<4.9", "web-auth/webauthn-lib": ">=4.5,<4.9", "web-feet/coastercms": "==5.5", + "web-tp3/wec_map": "<3.0.3", "webbuilders-group/silverstripe-kapost-bridge": "<0.4", "webcoast/deferred-image-processing": "<1.0.2", "webklex/laravel-imap": "<5.3", "webklex/php-imap": "<5.3", "webpa/webpa": "<3.1.2", + "webreinvent/vaahcms": "<=2.3.1", "wikibase/wikibase": "<=1.39.3", "wikimedia/parsoid": "<0.12.2", "willdurand/js-translation-bundle": "<2.1.1", @@ -4702,23 +4823,24 @@ "xataface/xataface": "<3", "xpressengine/xpressengine": "<3.0.15", "yab/quarx": "<2.4.5", - "yeswiki/yeswiki": "<=4.4.5", + "yeswiki/yeswiki": "<=4.5.4", "yetiforce/yetiforce-crm": "<6.5", "yidashi/yii2cmf": "<=2", "yii2mod/yii2-cms": "<1.9.2", - "yiisoft/yii": "<1.1.29", - "yiisoft/yii2": "<2.0.49.4-dev", + "yiisoft/yii": "<1.1.31", + "yiisoft/yii2": "<2.0.52", "yiisoft/yii2-authclient": "<2.2.15", "yiisoft/yii2-bootstrap": "<2.0.4", - "yiisoft/yii2-dev": "<2.0.43", + "yiisoft/yii2-dev": "<=2.0.45", "yiisoft/yii2-elasticsearch": "<2.0.5", "yiisoft/yii2-gii": "<=2.2.4", "yiisoft/yii2-jui": "<2.0.4", - "yiisoft/yii2-redis": "<2.0.8", + "yiisoft/yii2-redis": "<2.0.20", "yikesinc/yikes-inc-easy-mailchimp-extender": "<6.8.6", "yoast-seo-for-typo3/yoast_seo": "<7.2.3", "yourls/yourls": "<=1.8.2", "yuan1994/tpadmin": "<=1.3.12", + "z-push/z-push-dev": "<2.7.6", "zencart/zencart": "<=1.5.7.0-beta", "zendesk/zendesk_api_client_php": "<2.2.11", "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", @@ -4792,7 +4914,7 @@ "type": "tidelift" } ], - "time": "2025-03-06T18:06:13+00:00" + "time": "2025-11-20T22:05:30+00:00" }, { "name": "sebastian/diff", @@ -4863,7 +4985,7 @@ }, { "name": "symfony/clock", - "version": "v7.2.0", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", @@ -4917,7 +5039,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.2.0" + "source": "https://github.com/symfony/clock/tree/v7.3.0" }, "funding": [ { @@ -4937,23 +5059,24 @@ }, { "name": "symfony/console", - "version": "v7.2.1", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3" + "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3", - "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "url": "https://api.github.com/repos/symfony/console/zipball/c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", + "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0" + "symfony/string": "^7.2" }, "conflict": { "symfony/dependency-injection": "<6.4", @@ -5010,7 +5133,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.2.1" + "source": "https://github.com/symfony/console/tree/v7.3.6" }, "funding": [ { @@ -5021,25 +5144,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-11T03:49:26+00:00" + "time": "2025-11-04T01:21:42+00:00" }, { "name": "symfony/css-selector", - "version": "v7.2.0", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + "reference": "84321188c4754e64273b46b406081ad9b18e8614" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/84321188c4754e64273b46b406081ad9b18e8614", + "reference": "84321188c4754e64273b46b406081ad9b18e8614", "shasum": "" }, "require": { @@ -5075,7 +5202,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.2.0" + "source": "https://github.com/symfony/css-selector/tree/v7.3.6" }, "funding": [ { @@ -5086,25 +5213,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2025-10-29T17:24:25+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { @@ -5117,7 +5248,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -5142,7 +5273,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { @@ -5158,20 +5289,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/error-handler", - "version": "v7.2.4", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "aabf79938aa795350c07ce6464dd1985607d95d5" + "reference": "bbe40bfab84323d99dab491b716ff142410a92a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/aabf79938aa795350c07ce6464dd1985607d95d5", - "reference": "aabf79938aa795350c07ce6464dd1985607d95d5", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/bbe40bfab84323d99dab491b716ff142410a92a8", + "reference": "bbe40bfab84323d99dab491b716ff142410a92a8", "shasum": "" }, "require": { @@ -5184,9 +5315,11 @@ "symfony/http-kernel": "<6.4" }, "require-dev": { + "symfony/console": "^6.4|^7.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-kernel": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "symfony/serializer": "^6.4|^7.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" }, "bin": [ "Resources/bin/patch-type-declarations" @@ -5217,7 +5350,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.2.4" + "source": "https://github.com/symfony/error-handler/tree/v7.3.6" }, "funding": [ { @@ -5228,25 +5361,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-02T20:27:07+00:00" + "time": "2025-10-31T19:12:50+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.2.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", - "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", "shasum": "" }, "require": { @@ -5297,7 +5434,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" }, "funding": [ { @@ -5308,25 +5445,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", - "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", "shasum": "" }, "require": { @@ -5340,7 +5481,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -5373,7 +5514,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" }, "funding": [ { @@ -5389,20 +5530,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/finder", - "version": "v7.2.2", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "87a71856f2f56e4100373e92529eed3171695cfb" + "reference": "9f696d2f1e340484b4683f7853b273abff94421f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb", - "reference": "87a71856f2f56e4100373e92529eed3171695cfb", + "url": "https://api.github.com/repos/symfony/finder/zipball/9f696d2f1e340484b4683f7853b273abff94421f", + "reference": "9f696d2f1e340484b4683f7853b273abff94421f", "shasum": "" }, "require": { @@ -5437,7 +5578,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.2.2" + "source": "https://github.com/symfony/finder/tree/v7.3.5" }, "funding": [ { @@ -5448,25 +5589,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-30T19:00:17+00:00" + "time": "2025-10-15T18:45:57+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.2.3", + "version": "v7.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0" + "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ee1b504b8926198be89d05e5b6fc4c3810c090f0", - "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/db488a62f98f7a81d5746f05eea63a74e55bb7c4", + "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4", "shasum": "" }, "require": { @@ -5483,6 +5628,7 @@ "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", "symfony/cache": "^6.4.12|^7.1.5", + "symfony/clock": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", "symfony/expression-language": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", @@ -5515,7 +5661,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.2.3" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.7" }, "funding": [ { @@ -5526,25 +5672,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-17T10:56:55+00:00" + "time": "2025-11-08T16:41:12+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.2.4", + "version": "v7.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "9f1103734c5789798fefb90e91de4586039003ed" + "reference": "10b8e9b748ea95fa4539c208e2487c435d3c87ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9f1103734c5789798fefb90e91de4586039003ed", - "reference": "9f1103734c5789798fefb90e91de4586039003ed", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/10b8e9b748ea95fa4539c208e2487c435d3c87ce", + "reference": "10b8e9b748ea95fa4539c208e2487c435d3c87ce", "shasum": "" }, "require": { @@ -5552,8 +5702,8 @@ "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/event-dispatcher": "^7.3", + "symfony/http-foundation": "^7.3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -5629,7 +5779,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.2.4" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.7" }, "funding": [ { @@ -5640,25 +5790,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-26T11:01:22+00:00" + "time": "2025-11-12T11:38:40+00:00" }, { "name": "symfony/mailer", - "version": "v7.2.3", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3" + "reference": "fd497c45ba9c10c37864e19466b090dcb60a50ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/f3871b182c44997cf039f3b462af4a48fb85f9d3", - "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3", + "url": "https://api.github.com/repos/symfony/mailer/zipball/fd497c45ba9c10c37864e19466b090dcb60a50ba", + "reference": "fd497c45ba9c10c37864e19466b090dcb60a50ba", "shasum": "" }, "require": { @@ -5709,7 +5863,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.2.3" + "source": "https://github.com/symfony/mailer/tree/v7.3.5" }, "funding": [ { @@ -5720,25 +5874,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-27T11:08:17+00:00" + "time": "2025-10-24T14:27:20+00:00" }, { "name": "symfony/mime", - "version": "v7.2.4", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "87ca22046b78c3feaff04b337f33b38510fd686b" + "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/87ca22046b78c3feaff04b337f33b38510fd686b", - "reference": "87ca22046b78c3feaff04b337f33b38510fd686b", + "url": "https://api.github.com/repos/symfony/mime/zipball/b1b828f69cbaf887fa835a091869e55df91d0e35", + "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35", "shasum": "" }, "require": { @@ -5793,7 +5951,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.2.4" + "source": "https://github.com/symfony/mime/tree/v7.3.4" }, "funding": [ { @@ -5804,16 +5962,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-19T08:51:20+00:00" + "time": "2025-09-16T08:38:17+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -5872,7 +6034,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -5883,6 +6045,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5892,16 +6058,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -5950,7 +6116,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -5961,25 +6127,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", - "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", "shasum": "" }, "require": { @@ -6033,7 +6203,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, "funding": [ { @@ -6044,16 +6214,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-09-10T14:38:51+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -6114,7 +6288,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -6125,6 +6299,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -6134,19 +6312,20 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -6194,7 +6373,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -6205,12 +6384,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/polyfill-php56", @@ -6282,16 +6465,16 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { @@ -6342,7 +6525,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -6353,25 +6536,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { @@ -6418,7 +6605,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, "funding": [ { @@ -6429,16 +6616,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-07-08T02:45:35+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", @@ -6497,7 +6688,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" }, "funding": [ { @@ -6508,6 +6699,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -6517,16 +6712,16 @@ }, { "name": "symfony/process", - "version": "v7.2.4", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf" + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", - "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", "shasum": "" }, "require": { @@ -6558,7 +6753,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.2.4" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -6569,25 +6764,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-05T08:33:46+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/routing", - "version": "v7.2.3", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "ee9a67edc6baa33e5fae662f94f91fd262930996" + "reference": "c97abe725f2a1a858deca629a6488c8fc20c3091" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/ee9a67edc6baa33e5fae662f94f91fd262930996", - "reference": "ee9a67edc6baa33e5fae662f94f91fd262930996", + "url": "https://api.github.com/repos/symfony/routing/zipball/c97abe725f2a1a858deca629a6488c8fc20c3091", + "reference": "c97abe725f2a1a858deca629a6488c8fc20c3091", "shasum": "" }, "require": { @@ -6639,7 +6838,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.2.3" + "source": "https://github.com/symfony/routing/tree/v7.3.6" }, "funding": [ { @@ -6650,25 +6849,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-17T10:56:55+00:00" + "time": "2025-11-05T07:57:47+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.5.1", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -6686,7 +6889,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -6722,7 +6925,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -6733,25 +6936,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/string", - "version": "v7.2.0", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", - "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -6766,7 +6973,6 @@ }, "require-dev": { "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -6809,7 +7015,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.2.0" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -6820,25 +7026,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-13T13:31:26+00:00" + "time": "2025-09-11T14:36:48+00:00" }, { "name": "symfony/translation", - "version": "v7.2.4", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "283856e6981286cc0d800b53bd5703e8e363f05a" + "reference": "ec25870502d0c7072d086e8ffba1420c85965174" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/283856e6981286cc0d800b53bd5703e8e363f05a", - "reference": "283856e6981286cc0d800b53bd5703e8e363f05a", + "url": "https://api.github.com/repos/symfony/translation/zipball/ec25870502d0c7072d086e8ffba1420c85965174", + "reference": "ec25870502d0c7072d086e8ffba1420c85965174", "shasum": "" }, "require": { @@ -6848,6 +7058,7 @@ "symfony/translation-contracts": "^2.5|^3.0" }, "conflict": { + "nikic/php-parser": "<5.0", "symfony/config": "<6.4", "symfony/console": "<6.4", "symfony/dependency-injection": "<6.4", @@ -6861,7 +7072,7 @@ "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { - "nikic/php-parser": "^4.18|^5.0", + "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", "symfony/config": "^6.4|^7.0", "symfony/console": "^6.4|^7.0", @@ -6904,7 +7115,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.2.4" + "source": "https://github.com/symfony/translation/tree/v7.3.4" }, "funding": [ { @@ -6915,25 +7126,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-13T10:27:23+00:00" + "time": "2025-09-07T11:39:36+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.5.1", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" + "reference": "65a8bc82080447fae78373aa10f8d13b38338977" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", - "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977", "shasum": "" }, "require": { @@ -6946,7 +7161,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -6982,7 +7197,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" }, "funding": [ { @@ -6993,25 +7208,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/uid", - "version": "v7.2.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "2d294d0c48df244c71c105a169d0190bfb080426" + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/2d294d0c48df244c71c105a169d0190bfb080426", - "reference": "2d294d0c48df244c71c105a169d0190bfb080426", + "url": "https://api.github.com/repos/symfony/uid/zipball/a69f69f3159b852651a6bf45a9fdd149520525bb", + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb", "shasum": "" }, "require": { @@ -7056,7 +7275,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.2.0" + "source": "https://github.com/symfony/uid/tree/v7.3.1" }, "funding": [ { @@ -7072,31 +7291,31 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.2.3", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "82b478c69745d8878eb60f9a049a4d584996f73a" + "reference": "476c4ae17f43a9a36650c69879dcf5b1e6ae724d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/82b478c69745d8878eb60f9a049a4d584996f73a", - "reference": "82b478c69745d8878eb60f9a049a4d584996f73a", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/476c4ae17f43a9a36650c69879dcf5b1e6ae724d", + "reference": "476c4ae17f43a9a36650c69879dcf5b1e6ae724d", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { "symfony/console": "<6.4" }, "require-dev": { - "ext-iconv": "*", "symfony/console": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/process": "^6.4|^7.0", @@ -7139,7 +7358,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.2.3" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.5" }, "funding": [ { @@ -7150,12 +7369,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-17T11:39:41+00:00" + "time": "2025-09-27T09:00:46+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -7212,18 +7435,78 @@ }, "time": "2024-12-21T16:25:41+00:00" }, + { + "name": "vemcogroup/laravel-sparkpost-driver", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/vemcogroup/laravel-sparkpost-driver.git", + "reference": "215e7d757699e7935a00c2e58e6b4d338406f994" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vemcogroup/laravel-sparkpost-driver/zipball/215e7d757699e7935a00c2e58e6b4d338406f994", + "reference": "215e7d757699e7935a00c2e58e6b4d338406f994", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^7.2", + "laravel/framework": "^9.0|^10.0|^11.0|^12.0", + "php": "^8.0.2", + "symfony/mailer": "^6.0|^7.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Vemcogroup\\SparkPostDriver\\SparkPostDriverServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Vemcogroup\\SparkPostDriver\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "vemDevs", + "homepage": "https://vemcogroup.com" + } + ], + "description": "SparkPost driver to use with Laravel 6.x|7.x|8.x|9.x|10.x", + "homepage": "https://github.com/vemcogroup/laravel-sparkpost-driver", + "keywords": [ + "laravel", + "mail", + "sparkpost" + ], + "support": { + "issues": "https://github.com/vemcogroup/laravel-sparkpost-driver/issues", + "source": "https://github.com/vemcogroup/laravel-sparkpost-driver/tree/5.1.0" + }, + "time": "2025-02-17T11:33:40+00:00" + }, { "name": "vlucas/phpdotenv", - "version": "v5.6.1", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", - "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af", "shasum": "" }, "require": { @@ -7282,7 +7565,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2" }, "funding": [ { @@ -7294,7 +7577,7 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:52:34+00:00" + "time": "2025-04-30T23:37:27+00:00" }, { "name": "voku/portable-ascii", @@ -7370,64 +7653,6 @@ ], "time": "2024-11-21T01:49:47+00:00" }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, { "name": "xantios/mimey", "version": "v2.2.0", @@ -7480,20 +7705,20 @@ "packages-dev": [ { "name": "barryvdh/laravel-ide-helper", - "version": "v3.5.5", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "8d441ec99f8612b942b55f5183151d91591b618a" + "reference": "8d00250cba25728373e92c1d8dcebcbf64623d29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/8d441ec99f8612b942b55f5183151d91591b618a", - "reference": "8d441ec99f8612b942b55f5183151d91591b618a", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/8d00250cba25728373e92c1d8dcebcbf64623d29", + "reference": "8d00250cba25728373e92c1d8dcebcbf64623d29", "shasum": "" }, "require": { - "barryvdh/reflection-docblock": "^2.3", + "barryvdh/reflection-docblock": "^2.4", "composer/class-map-generator": "^1.0", "ext-json": "*", "illuminate/console": "^11.15 || ^12", @@ -7558,7 +7783,7 @@ ], "support": { "issues": "https://github.com/barryvdh/laravel-ide-helper/issues", - "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.5.5" + "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.6.0" }, "funding": [ { @@ -7570,20 +7795,20 @@ "type": "github" } ], - "time": "2025-02-11T13:59:46+00:00" + "time": "2025-07-17T20:11:57+00:00" }, { "name": "barryvdh/reflection-docblock", - "version": "v2.3.1", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/barryvdh/ReflectionDocBlock.git", - "reference": "b6ff9f93603561f50e53b64310495d20b8dff5d8" + "reference": "d103774cbe7e94ddee7e4870f97f727b43fe7201" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/b6ff9f93603561f50e53b64310495d20b8dff5d8", - "reference": "b6ff9f93603561f50e53b64310495d20b8dff5d8", + "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/d103774cbe7e94ddee7e4870f97f727b43fe7201", + "reference": "d103774cbe7e94ddee7e4870f97f727b43fe7201", "shasum": "" }, "require": { @@ -7620,28 +7845,28 @@ } ], "support": { - "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.3.1" + "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.4.0" }, - "time": "2025-01-18T19:26:32+00:00" + "time": "2025-07-17T06:07:30+00:00" }, { "name": "composer/class-map-generator", - "version": "1.6.0", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/composer/class-map-generator.git", - "reference": "ffe442c5974c44a9343e37a0abcb1cc37319f5b9" + "reference": "2373419b7709815ed323ebf18c3c72d03ff4a8a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/ffe442c5974c44a9343e37a0abcb1cc37319f5b9", - "reference": "ffe442c5974c44a9343e37a0abcb1cc37319f5b9", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/2373419b7709815ed323ebf18c3c72d03ff4a8a6", + "reference": "2373419b7709815ed323ebf18c3c72d03ff4a8a6", "shasum": "" }, "require": { "composer/pcre": "^2.1 || ^3.1", "php": "^7.2 || ^8.0", - "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7 || ^8" }, "require-dev": { "phpstan/phpstan": "^1.12 || ^2", @@ -7649,7 +7874,7 @@ "phpstan/phpstan-phpunit": "^1 || ^2", "phpstan/phpstan-strict-rules": "^1.1 || ^2", "phpunit/phpunit": "^8", - "symfony/filesystem": "^5.4 || ^6" + "symfony/filesystem": "^5.4 || ^6 || ^7 || ^8" }, "type": "library", "extra": { @@ -7679,7 +7904,7 @@ ], "support": { "issues": "https://github.com/composer/class-map-generator/issues", - "source": "https://github.com/composer/class-map-generator/tree/1.6.0" + "source": "https://github.com/composer/class-map-generator/tree/1.7.0" }, "funding": [ { @@ -7689,13 +7914,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2025-02-05T10:05:34+00:00" + "time": "2025-11-19T10:41:15+00:00" }, { "name": "composer/pcre", @@ -7894,16 +8115,16 @@ }, { "name": "filp/whoops", - "version": "2.17.0", + "version": "2.18.4", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "075bc0c26631110584175de6523ab3f1652eb28e" + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/075bc0c26631110584175de6523ab3f1652eb28e", - "reference": "075bc0c26631110584175de6523ab3f1652eb28e", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", "shasum": "" }, "require": { @@ -7953,7 +8174,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.17.0" + "source": "https://github.com/filp/whoops/tree/2.18.4" }, "funding": [ { @@ -7961,24 +8182,24 @@ "type": "github" } ], - "time": "2025-01-25T12:00:00+00:00" + "time": "2025-08-08T12:00:00+00:00" }, { "name": "hamcrest/hamcrest-php", - "version": "v2.0.1", + "version": "v2.1.1", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", "shasum": "" }, "require": { - "php": "^5.3|^7.0|^8.0" + "php": "^7.4|^8.0" }, "replace": { "cordoval/hamcrest-php": "*", @@ -7986,8 +8207,8 @@ "kodova/hamcrest-php": "*" }, "require-dev": { - "phpunit/php-file-iterator": "^1.4 || ^2.0", - "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" }, "type": "library", "extra": { @@ -8010,9 +8231,9 @@ ], "support": { "issues": "https://github.com/hamcrest/hamcrest-php/issues", - "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" }, - "time": "2020-07-09T08:09:16+00:00" + "time": "2025-04-30T06:54:44+00:00" }, { "name": "mockery/mockery", @@ -8099,16 +8320,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.0", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -8147,7 +8368,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -8155,42 +8376,43 @@ "type": "tidelift" } ], - "time": "2025-02-12T12:17:51+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nunomaduro/collision", - "version": "v8.6.1", + "version": "v8.8.3", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "86f003c132143d5a2ab214e19933946409e0cae7" + "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/86f003c132143d5a2ab214e19933946409e0cae7", - "reference": "86f003c132143d5a2ab214e19933946409e0cae7", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/1dc9e88d105699d0fee8bb18890f41b274f6b4c4", + "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4", "shasum": "" }, "require": { - "filp/whoops": "^2.16.0", - "nunomaduro/termwind": "^2.3.0", + "filp/whoops": "^2.18.1", + "nunomaduro/termwind": "^2.3.1", "php": "^8.2.0", - "symfony/console": "^7.2.1" + "symfony/console": "^7.3.0" }, "conflict": { - "laravel/framework": "<11.39.1 || >=13.0.0", - "phpunit/phpunit": "<11.5.3 || >=12.0.0" + "laravel/framework": "<11.44.2 || >=13.0.0", + "phpunit/phpunit": "<11.5.15 || >=13.0.0" }, "require-dev": { - "larastan/larastan": "^2.9.12", - "laravel/framework": "^11.39.1", - "laravel/pint": "^1.20.0", - "laravel/sail": "^1.40.0", - "laravel/sanctum": "^4.0.7", - "laravel/tinker": "^2.10.0", - "orchestra/testbench-core": "^9.9.2", - "pestphp/pest": "^3.7.3", - "sebastian/environment": "^6.1.0 || ^7.2.0" + "brianium/paratest": "^7.8.3", + "larastan/larastan": "^3.4.2", + "laravel/framework": "^11.44.2 || ^12.18", + "laravel/pint": "^1.22.1", + "laravel/sail": "^1.43.1", + "laravel/sanctum": "^4.1.1", + "laravel/tinker": "^2.10.1", + "orchestra/testbench-core": "^9.12.0 || ^10.4", + "pestphp/pest": "^3.8.2 || ^4.0.0", + "sebastian/environment": "^7.2.1 || ^8.0" }, "type": "library", "extra": { @@ -8253,7 +8475,7 @@ "type": "patreon" } ], - "time": "2025-01-23T13:41:43+00:00" + "time": "2025-11-20T02:55:25+00:00" }, { "name": "phar-io/manifest", @@ -8375,16 +8597,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "11.0.9", + "version": "11.0.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "14d63fbcca18457e49c6f8bebaa91a87e8e188d7" + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/14d63fbcca18457e49c6f8bebaa91a87e8e188d7", - "reference": "14d63fbcca18457e49c6f8bebaa91a87e8e188d7", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", "shasum": "" }, "require": { @@ -8441,15 +8663,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.9" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2025-02-25T13:26:39+00:00" + "time": "2025-08-27T14:37:49+00:00" }, { "name": "phpunit/php-file-iterator", @@ -8698,16 +8932,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.12", + "version": "11.5.44", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d42785840519401ed2113292263795eb4c0f95da" + "reference": "c346885c95423eda3f65d85a194aaa24873cda82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d42785840519401ed2113292263795eb4c0f95da", - "reference": "d42785840519401ed2113292263795eb4c0f95da", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c346885c95423eda3f65d85a194aaa24873cda82", + "reference": "c346885c95423eda3f65d85a194aaa24873cda82", "shasum": "" }, "require": { @@ -8717,24 +8951,24 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.0", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.9", + "phpunit/php-code-coverage": "^11.0.11", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", - "sebastian/code-unit": "^3.0.2", - "sebastian/comparator": "^6.3.1", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.2", "sebastian/diff": "^6.0.2", - "sebastian/environment": "^7.2.0", - "sebastian/exporter": "^6.3.0", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.2", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", - "sebastian/type": "^5.1.0", + "sebastian/type": "^5.1.3", "sebastian/version": "^5.0.2", "staabm/side-effects-detector": "^1.0.5" }, @@ -8779,7 +9013,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.12" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.44" }, "funding": [ { @@ -8790,12 +9024,20 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2025-03-07T07:31:03+00:00" + "time": "2025-11-13T07:17:35+00:00" }, { "name": "sebastian/cli-parser", @@ -8856,16 +9098,16 @@ }, { "name": "sebastian/code-unit", - "version": "3.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca" + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca", - "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", "shasum": "" }, "require": { @@ -8901,7 +9143,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", "security": "https://github.com/sebastianbergmann/code-unit/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.2" + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" }, "funding": [ { @@ -8909,7 +9151,7 @@ "type": "github" } ], - "time": "2024-12-12T09:59:06+00:00" + "time": "2025-03-19T07:56:08+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -8969,16 +9211,16 @@ }, { "name": "sebastian/comparator", - "version": "6.3.1", + "version": "6.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959" + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/24b8fbc2c8e201bb1308e7b05148d6ab393b6959", - "reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", "shasum": "" }, "require": { @@ -9037,15 +9279,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.1" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2025-03-07T06:57:01+00:00" + "time": "2025-08-10T08:07:46+00:00" }, { "name": "sebastian/complexity", @@ -9107,23 +9361,23 @@ }, { "name": "sebastian/environment", - "version": "7.2.0", + "version": "7.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5" + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", - "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "suggest": { "ext-posix": "*" @@ -9159,28 +9413,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/7.2.0" + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" } ], - "time": "2024-07-03T04:54:44+00:00" + "time": "2025-05-21T11:55:47+00:00" }, { "name": "sebastian/exporter", - "version": "6.3.0", + "version": "6.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", - "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", "shasum": "" }, "require": { @@ -9194,7 +9460,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -9237,15 +9503,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-12-05T09:17:50+00:00" + "time": "2025-09-24T06:12:51+00:00" }, { "name": "sebastian/global-state", @@ -9483,23 +9761,23 @@ }, { "name": "sebastian/recursion-context", - "version": "6.0.2", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { @@ -9535,28 +9813,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2024-07-03T05:10:34+00:00" + "time": "2025-08-13T04:42:22+00:00" }, { "name": "sebastian/type", - "version": "5.1.0", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac" + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/461b9c5da241511a2a0e8f240814fb23ce5c0aac", - "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", "shasum": "" }, "require": { @@ -9592,15 +9882,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/5.1.0" + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" } ], - "time": "2024-09-17T13:12:04+00:00" + "time": "2025-08-09T06:55:48+00:00" }, { "name": "sebastian/version", @@ -9658,16 +9960,16 @@ }, { "name": "spatie/backtrace", - "version": "1.7.1", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/spatie/backtrace.git", - "reference": "0f2477c520e3729de58e061b8192f161c99f770b" + "reference": "8c0f16a59ae35ec8c62d85c3c17585158f430110" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/0f2477c520e3729de58e061b8192f161c99f770b", - "reference": "0f2477c520e3729de58e061b8192f161c99f770b", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/8c0f16a59ae35ec8c62d85c3c17585158f430110", + "reference": "8c0f16a59ae35ec8c62d85c3c17585158f430110", "shasum": "" }, "require": { @@ -9705,7 +10007,8 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/backtrace/tree/1.7.1" + "issues": "https://github.com/spatie/backtrace/issues", + "source": "https://github.com/spatie/backtrace/tree/1.8.1" }, "funding": [ { @@ -9717,7 +10020,7 @@ "type": "other" } ], - "time": "2024-12-02T13:28:15+00:00" + "time": "2025-08-26T08:22:30+00:00" }, { "name": "spatie/error-solutions", @@ -10090,16 +10393,16 @@ }, { "name": "symfony/thanks", - "version": "v1.4.0", + "version": "v1.4.1", "source": { "type": "git", "url": "https://github.com/symfony/thanks.git", - "reference": "ad3f07af819f058666f0cac3f0737f18d31e3d05" + "reference": "f455cc9ba4e0c61dcc18ea7e52bd725ee661aa64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/thanks/zipball/ad3f07af819f058666f0cac3f0737f18d31e3d05", - "reference": "ad3f07af819f058666f0cac3f0737f18d31e3d05", + "url": "https://api.github.com/repos/symfony/thanks/zipball/f455cc9ba4e0c61dcc18ea7e52bd725ee661aa64", + "reference": "f455cc9ba4e0c61dcc18ea7e52bd725ee661aa64", "shasum": "" }, "require": { @@ -10131,7 +10434,7 @@ "description": "Encourages sending ⭐ and 💵 to fellow PHP package maintainers (not limited to Symfony components)!", "support": { "issues": "https://github.com/symfony/thanks/issues", - "source": "https://github.com/symfony/thanks/tree/v1.4.0" + "source": "https://github.com/symfony/thanks/tree/v1.4.1" }, "funding": [ { @@ -10142,25 +10445,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-01T09:47:21+00:00" + "time": "2025-10-30T17:38:50+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -10189,7 +10496,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -10197,7 +10504,7 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], @@ -10210,6 +10517,7 @@ "platform": { "php": ">=8.1.0", "ext-zip": "*", + "ext-pdo": "*", "ext-imagick": "*" }, "platform-dev": {}, diff --git a/code/config/athenia.php b/code/config/athenia.php index fc078075..009b4be4 100644 --- a/code/config/athenia.php +++ b/code/config/athenia.php @@ -6,5 +6,7 @@ 'slack_enabled' => env('ATHENIA_SLACK_ENABLED', false), 'sms_enabled' => env('ATHENIA_SMS_ENABLED', false), 'push_enabled' => env('ATHENIA_PUSH_ENABLED', false), - ] + ], + + 'invitation_required' => env('INVITATION_REQUIRED', false), ]; \ No newline at end of file diff --git a/code/config/database.php b/code/config/database.php index 0de5ce6a..64f97ada 100644 --- a/code/config/database.php +++ b/code/config/database.php @@ -69,6 +69,21 @@ 'engine' => null, ], + 'mysql-tmp' => [ + 'driver' => 'mysql', + 'host' => env('TMP_DB_HOST', 'mysql-tmp'), + 'port' => env('TMP_DB_PORT', '3306'), + 'database' => env('TMP_DB_DATABASE', 'tmp'), + 'username' => env('TMP_DB_USERNAME', 'tmp'), + 'password' => env('TMP_DB_PASSWORD', 'tmppassword'), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'strict' => true, + 'engine' => null, + ], + 'pgsql' => [ 'driver' => 'pgsql', 'host' => env('DB_HOST', '127.0.0.1'), diff --git a/code/config/filesystems.php b/code/config/filesystems.php index 67158a5d..c2210fed 100644 --- a/code/config/filesystems.php +++ b/code/config/filesystems.php @@ -66,4 +66,8 @@ ], + 'wordpress_sites_home' => env('WORDPRESS_SITE_HOME', storage_path('wordpress')), + + 'wordpress_local_repo_home' => env('WORDPRESS_LOCAL_REPO_HOME', storage_path('repo')), + ]; diff --git a/code/config/mail.php b/code/config/mail.php index 7f4c5aa5..9e5c2326 100644 --- a/code/config/mail.php +++ b/code/config/mail.php @@ -53,6 +53,10 @@ 'path' => '/usr/sbin/sendmail -bs', ], + 'sparkpost' => [ + 'transport' => 'sparkpost' + ], + 'log' => [ 'transport' => 'log', 'channel' => env('MAIL_LOG_CHANNEL'), diff --git a/code/database/factories/User/ArticleNoteFactory.php b/code/database/factories/User/ArticleNoteFactory.php new file mode 100644 index 00000000..173de874 --- /dev/null +++ b/code/database/factories/User/ArticleNoteFactory.php @@ -0,0 +1,34 @@ + User::factory()->create()->id, + 'article_id' => Article::factory()->create()->id, + 'response' => $this->faker->optional()->text(), + 'completed_at' => $this->faker->optional()->dateTime(), + ]; + } +} diff --git a/code/database/factories/User/InvitationTokenFactory.php b/code/database/factories/User/InvitationTokenFactory.php new file mode 100644 index 00000000..8b040943 --- /dev/null +++ b/code/database/factories/User/InvitationTokenFactory.php @@ -0,0 +1,32 @@ + Str::random(40), + 'role_id' => null, + 'used_at' => null, + ]; + } +} diff --git a/code/database/factories/Wiki/ArticleFactory.php b/code/database/factories/Wiki/ArticleFactory.php index 17267014..dc266bed 100644 --- a/code/database/factories/Wiki/ArticleFactory.php +++ b/code/database/factories/Wiki/ArticleFactory.php @@ -25,6 +25,8 @@ public function definition() { return [ 'title' => $this->faker->title, + 'url' => $this->faker->optional()->url(), + 'authors' => $this->faker->optional()->name(), 'created_by_id' => User::factory()->create()->id, ]; } diff --git a/code/database/factories/Wiki/ArticleSummaryFactory.php b/code/database/factories/Wiki/ArticleSummaryFactory.php new file mode 100644 index 00000000..3a53cfbf --- /dev/null +++ b/code/database/factories/Wiki/ArticleSummaryFactory.php @@ -0,0 +1,31 @@ + Article::factory()->create()->id, + 'content' => $this->faker->paragraph(), + ]; + } +} diff --git a/code/database/migrations/2025_11_22_142752_create_article_category_pivot_table.php b/code/database/migrations/2025_11_22_142752_create_article_category_pivot_table.php new file mode 100644 index 00000000..b45cac7e --- /dev/null +++ b/code/database/migrations/2025_11_22_142752_create_article_category_pivot_table.php @@ -0,0 +1,43 @@ +id(); + $table->unsignedInteger('article_id'); + $table->unsignedInteger('category_id'); + $table->float('relevance')->default(1.0); + $table->timestamps(); + + $table->foreign('article_id') + ->references('id') + ->on('articles') + ->onDelete('cascade'); + + $table->foreign('category_id') + ->references('id') + ->on('categories') + ->onDelete('cascade'); + + $table->unique(['article_id', 'category_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('article_category'); + } +}; diff --git a/code/database/migrations/2025_11_23_173331_setup_article_notes.php b/code/database/migrations/2025_11_23_173331_setup_article_notes.php new file mode 100644 index 00000000..0918e248 --- /dev/null +++ b/code/database/migrations/2025_11_23_173331_setup_article_notes.php @@ -0,0 +1,65 @@ +id(); + $table->unsignedInteger('user_id'); + $table->unsignedInteger('article_id'); + $table->timestamp('completed_at')->nullable(); + $table->text('response')->nullable(); + $table->timestamps(); + $table->softDeletes(); + + $table->foreign('user_id') + ->references('id') + ->on('users') + ->onDelete('cascade'); + + $table->foreign('article_id') + ->references('id') + ->on('articles') + ->onDelete('cascade'); + + $table->unique(['user_id', 'article_id']); + }); + + // Add url and authors fields to articles table + Schema::table('articles', function (Blueprint $table) { + $table->string('url')->nullable()->after('title'); + $table->text('authors')->nullable()->after('url'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + // Drop url and authors columns from articles + Schema::table('articles', function (Blueprint $table) { + $table->dropColumn(['url', 'authors']); + }); + + // Drop article_notes table + Schema::dropIfExists('article_notes'); + } +} diff --git a/code/database/migrations/2025_11_23_201741_create_article_summaries_table.php b/code/database/migrations/2025_11_23_201741_create_article_summaries_table.php new file mode 100644 index 00000000..24d0dd56 --- /dev/null +++ b/code/database/migrations/2025_11_23_201741_create_article_summaries_table.php @@ -0,0 +1,43 @@ +id(); + $table->unsignedInteger('article_id'); + $table->text('content'); + $table->timestamps(); + $table->softDeletes(); + + $table->foreign('article_id') + ->references('id') + ->on('articles') + ->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::dropIfExists('article_summaries'); + } +} diff --git a/code/database/migrations/2025_11_24_000000_create_invitation_tokens_table.php b/code/database/migrations/2025_11_24_000000_create_invitation_tokens_table.php new file mode 100644 index 00000000..54b9a12e --- /dev/null +++ b/code/database/migrations/2025_11_24_000000_create_invitation_tokens_table.php @@ -0,0 +1,32 @@ +increments('id'); + $table->string('token', 40)->unique(); + $table->unsignedInteger('role_id')->nullable(); + $table->foreign('role_id')->references('id')->on('roles'); + $table->timestamp('used_at')->nullable(); + $table->softDeletes(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('invitation_tokens'); + } +}; diff --git a/code/database/seeders/ArticleNoteStatisticsSeeder.php b/code/database/seeders/ArticleNoteStatisticsSeeder.php new file mode 100644 index 00000000..736b4048 --- /dev/null +++ b/code/database/seeders/ArticleNoteStatisticsSeeder.php @@ -0,0 +1,52 @@ + 'total_notes', 'model' => 'article'], + [ + 'relation' => 'articleNotes', + 'public' => true, + ] + ); + + // Create the total_completed_notes statistic if it doesn't exist + $totalCompletedNotesStatistic = Statistic::firstOrCreate( + ['name' => 'total_completed_notes', 'model' => 'article'], + [ + 'relation' => 'articleNotes', + 'public' => true, + ] + ); + + // Add filter to only count notes where completed_at is not null + StatisticFilter::firstOrCreate( + [ + 'statistic_id' => $totalCompletedNotesStatistic->id, + 'field' => 'completed_at', + ], + [ + 'operator' => '!=', + 'value' => null, + ] + ); + } +} diff --git a/code/lang/en/validation.php b/code/lang/en/validation.php index 3ccf4b3a..bb1cddb0 100644 --- a/code/lang/en/validation.php +++ b/code/lang/en/validation.php @@ -130,6 +130,8 @@ 'token_is_not_expired' => 'The reset password token has expired. You are going to have to request a new one.', 'user_owns_token' => 'The reset password token does not seem to be for the entered email address.', 'not_present' => 'The :attribute field is not allowed or can not be set for this request.', + \App\Athenia\Validators\InvitationTokenIsValidValidator::KEY => + 'The :attribute is invalid.', \App\Athenia\Validators\Subscription\MembershipPlanRateIsActiveValidator::KEY => 'The membership plan rate must be active for you to purchase it.', \App\Athenia\Validators\Subscription\PaymentMethodIsOwnedByEntityValidator::KEY => diff --git a/code/phpunit.xml b/code/phpunit.xml index 243f95b4..f5f72a85 100644 --- a/code/phpunit.xml +++ b/code/phpunit.xml @@ -6,8 +6,6 @@ colors="true" processIsolation="false" beStrictAboutTestsThatDoNotTestAnything="false" - stopOnFailure="true" - stopOnError="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false" diff --git a/code/routes/core.php b/code/routes/core.php index f2165d05..da854c70 100644 --- a/code/routes/core.php +++ b/code/routes/core.php @@ -90,6 +90,12 @@ 'index', 'store', ], ]); + + // Article summary routes (singular resource pattern) + Route::get('article-summary', 'Wiki\ArticleSummaryController@show')->name('article-summary.show'); + Route::post('article-summary', 'Wiki\ArticleSummaryController@store')->name('article-summary.store'); + Route::put('article-summary', 'Wiki\ArticleSummaryController@update')->name('article-summary.update'); + Route::delete('article-summary', 'Wiki\ArticleSummaryController@destroy')->name('article-summary.destroy'); }); Route::resource('ballots', 'BallotController', [ @@ -161,6 +167,15 @@ Route::group(['prefix' => 'users/{user}', 'as' => 'user.'], function () { require 'entity-routes.php'; + Route::resource('article-notes', 'User\ArticleNoteController', [ + 'except' => [ + 'create', 'edit', + ], + ]); + + Route::post('random-article', 'User\ArticleNoteController@randomArticle') + ->name('random-article'); + Route::resource('ballot-completions', 'User\BallotCompletionController', [ 'only' => [ 'index', diff --git a/code/tests/Athenia/Feature/Http/Article/ArticleCreateTest.php b/code/tests/Athenia/Feature/Http/Article/ArticleCreateTest.php index d9924879..df877a0e 100644 --- a/code/tests/Athenia/Feature/Http/Article/ArticleCreateTest.php +++ b/code/tests/Athenia/Feature/Http/Article/ArticleCreateTest.php @@ -3,7 +3,9 @@ namespace Tests\Athenia\Feature\Http\Article; +use App\Models\Category; use App\Models\Role; +use App\Models\Wiki\Article; use Tests\DatabaseSetupTrait; use Tests\TestCase; use Tests\Traits\MocksApplicationLog; @@ -63,6 +65,64 @@ public function testCreateSuccessful(): void ]); } + public function testCreateSuccessfulWithCategories(): void + { + $this->actAs(Role::ARTICLE_EDITOR); + + $category1 = Category::factory()->create(); + $category2 = Category::factory()->create(); + + $response = $this->json('POST', $this->path, [ + 'title' => 'An Article', + 'categories' => [ + ['category_id' => $category1->id, 'relevance' => 0.8], + ['category_id' => $category2->id, 'relevance' => 0.5], + ], + ]); + + $response->assertStatus(201); + + $response->assertJson([ + 'title' => 'An Article', + 'created_by_id' => $this->actingAs->id, + ]); + + /** @var Article $article */ + $article = Article::find($response->json('id')); + + $this->assertCount(2, $article->categories); + + $category1Pivot = $article->categories->find($category1->id)->pivot; + $this->assertEquals(0.8, $category1Pivot->relevance); + + $category2Pivot = $article->categories->find($category2->id)->pivot; + $this->assertEquals(0.5, $category2Pivot->relevance); + } + + public function testCreateSuccessfulWithCategoriesDefaultRelevance(): void + { + $this->actAs(Role::ARTICLE_EDITOR); + + $category = Category::factory()->create(); + + $response = $this->json('POST', $this->path, [ + 'title' => 'An Article', + 'categories' => [ + ['category_id' => $category->id], + ], + ]); + + $response->assertStatus(201); + + /** @var Article $article */ + $article = Article::find($response->json('id')); + + $this->assertCount(1, $article->categories); + + $categoryPivot = $article->categories->find($category->id)->pivot; + $this->assertEquals(1.0, $categoryPivot->relevance); + } + public function testCreateFailsRequiredFieldsNotPresent(): void { $this->actAs(Role::ARTICLE_EDITOR); @@ -111,4 +171,82 @@ public function testCreateFailsStringsTooLong(): void ] ]); } + + public function testCreateFailsInvalidArrayFields(): void + { + $this->actAs(Role::ARTICLE_EDITOR); + + $response = $this->json('POST', $this->path, [ + 'title' => 'An Article', + 'categories' => 'not-an-array', + ]); + + $response->assertStatus(400); + + $response->assertJson([ + 'errors' => [ + 'categories' => ['The categories must be an array.'], + ] + ]); + } + + public function testCreateFailsInvalidIntegerFields(): void + { + $this->actAs(Role::ARTICLE_EDITOR); + + $response = $this->json('POST', $this->path, [ + 'title' => 'An Article', + 'categories' => [ + ['category_id' => 'not-an-integer'], + ], + ]); + + $response->assertStatus(400); + + $response->assertJson([ + 'errors' => [ + 'categories.0.category_id' => ['The categories.0.category_id must be an integer.'], + ] + ]); + } + + public function testCreateFailsInvalidNumericFields(): void + { + $this->actAs(Role::ARTICLE_EDITOR); + + $response = $this->json('POST', $this->path, [ + 'title' => 'An Article', + 'categories' => [ + ['category_id' => 1, 'relevance' => 'not-numeric'], + ], + ]); + + $response->assertStatus(400); + + $response->assertJson([ + 'errors' => [ + 'categories.0.relevance' => ['The categories.0.relevance must be a number.'], + ] + ]); + } + + public function testCreateFailsInvalidModelFields(): void + { + $this->actAs(Role::ARTICLE_EDITOR); + + $response = $this->json('POST', $this->path, [ + 'title' => 'An Article', + 'categories' => [ + ['category_id' => 99999], + ], + ]); + + $response->assertStatus(400); + + $response->assertJson([ + 'errors' => [ + 'categories.0.category_id' => ['The selected categories.0.category_id is invalid.'], + ] + ]); + } } \ No newline at end of file diff --git a/code/tests/Athenia/Feature/Http/Article/ArticleIndexTest.php b/code/tests/Athenia/Feature/Http/Article/ArticleIndexTest.php index c87a50b4..1a834eed 100644 --- a/code/tests/Athenia/Feature/Http/Article/ArticleIndexTest.php +++ b/code/tests/Athenia/Feature/Http/Article/ArticleIndexTest.php @@ -37,16 +37,6 @@ public function testNotLoggedUserBlocked(): void $response->assertStatus(403); } - public function testIncorrectUserRoleBlocked(): void - { - foreach ($this->rolesWithoutAdmins([Role::ARTICLE_VIEWER, Role::ARTICLE_EDITOR]) as $role ) { - $this->actAs($role); - $response = $this->json('GET', $this->path); - - $response->assertStatus(403); - } - } - public function testGetPaginationEmpty(): void { foreach ([Role::ARTICLE_EDITOR, Role::ARTICLE_VIEWER] as $role) { diff --git a/code/tests/Athenia/Feature/Http/Article/ArticleUpdateTest.php b/code/tests/Athenia/Feature/Http/Article/ArticleUpdateTest.php index 1598d0d2..5fe7fd1e 100644 --- a/code/tests/Athenia/Feature/Http/Article/ArticleUpdateTest.php +++ b/code/tests/Athenia/Feature/Http/Article/ArticleUpdateTest.php @@ -3,6 +3,7 @@ namespace Tests\Athenia\Feature\Http\Article; +use App\Models\Category; use App\Models\Role; use App\Models\Wiki\Article; use Tests\DatabaseSetupTrait; @@ -85,6 +86,79 @@ public function testUpdateSuccessful(): void $this->assertEquals('A different title', $updated->title); } + public function testUpdateSuccessfulWithCategories(): void + { + $this->actAs(Role::ARTICLE_EDITOR); + + $article = Article::factory()->create([ + 'created_by_id' => $this->actingAs->id, + ]); + + $category1 = Category::factory()->create(); + $category2 = Category::factory()->create(); + $category3 = Category::factory()->create(); + + // Attach initial categories + $article->categories()->attach([ + $category1->id => ['relevance' => 0.9], + $category2->id => ['relevance' => 0.7], + ]); + + // Update to different categories + $response = $this->json('PUT', $this->path . '/' . $article->id, [ + 'categories' => [ + ['category_id' => $category2->id, 'relevance' => 0.6], + ['category_id' => $category3->id, 'relevance' => 0.4], + ], + ]); + + $response->assertStatus(200); + + /** @var Article $updated */ + $updated = Article::find($article->id); + + // Should have 2 categories now (category1 removed, category3 added) + $this->assertCount(2, $updated->categories); + + // category1 should be removed + $this->assertNull($updated->categories->find($category1->id)); + + // category2 should have updated relevance + $category2Pivot = $updated->categories->find($category2->id)->pivot; + $this->assertEquals(0.6, $category2Pivot->relevance); + + // category3 should be newly added + $category3Pivot = $updated->categories->find($category3->id)->pivot; + $this->assertEquals(0.4, $category3Pivot->relevance); + } + + public function testUpdateSuccessfulWithCategoriesDefaultRelevance(): void + { + $this->actAs(Role::ARTICLE_EDITOR); + + $article = Article::factory()->create([ + 'created_by_id' => $this->actingAs->id, + ]); + + $category = Category::factory()->create(); + + $response = $this->json('PUT', $this->path . '/' . $article->id, [ + 'categories' => [ + ['category_id' => $category->id], + ], + ]); + + $response->assertStatus(200); + + /** @var Article $updated */ + $updated = Article::find($article->id); + + $this->assertCount(1, $updated->categories); + + $categoryPivot = $updated->categories->find($category->id)->pivot; + $this->assertEquals(1.0, $categoryPivot->relevance); + } + public function testUpdateBlockedUserHasNotCreatedArticle(): void { $this->actAs(Role::ARTICLE_EDITOR); @@ -136,4 +210,94 @@ public function testCreateFailsStringsTooLong(): void ] ]); } + + public function testUpdateFailsInvalidArrayFields(): void + { + $this->actAs(Role::ARTICLE_EDITOR); + + $article = Article::factory()->create([ + 'created_by_id' => $this->actingAs->id, + ]); + + $response = $this->json('PUT', $this->path . '/' . $article->id, [ + 'categories' => 'not-an-array', + ]); + + $response->assertStatus(400); + + $response->assertJson([ + 'errors' => [ + 'categories' => ['The categories must be an array.'], + ] + ]); + } + + public function testUpdateFailsInvalidIntegerFields(): void + { + $this->actAs(Role::ARTICLE_EDITOR); + + $article = Article::factory()->create([ + 'created_by_id' => $this->actingAs->id, + ]); + + $response = $this->json('PUT', $this->path . '/' . $article->id, [ + 'categories' => [ + ['category_id' => 'not-an-integer'], + ], + ]); + + $response->assertStatus(400); + + $response->assertJson([ + 'errors' => [ + 'categories.0.category_id' => ['The categories.0.category_id must be an integer.'], + ] + ]); + } + + public function testUpdateFailsInvalidNumericFields(): void + { + $this->actAs(Role::ARTICLE_EDITOR); + + $article = Article::factory()->create([ + 'created_by_id' => $this->actingAs->id, + ]); + + $response = $this->json('PUT', $this->path . '/' . $article->id, [ + 'categories' => [ + ['category_id' => 1, 'relevance' => 'not-numeric'], + ], + ]); + + $response->assertStatus(400); + + $response->assertJson([ + 'errors' => [ + 'categories.0.relevance' => ['The categories.0.relevance must be a number.'], + ] + ]); + } + + public function testUpdateFailsInvalidModelFields(): void + { + $this->actAs(Role::ARTICLE_EDITOR); + + $article = Article::factory()->create([ + 'created_by_id' => $this->actingAs->id, + ]); + + $response = $this->json('PUT', $this->path . '/' . $article->id, [ + 'categories' => [ + ['category_id' => 99999], + ], + ]); + + $response->assertStatus(400); + + $response->assertJson([ + 'errors' => [ + 'categories.0.category_id' => ['The selected categories.0.category_id is invalid.'], + ] + ]); + } } diff --git a/code/tests/Athenia/Feature/Http/Article/ArticleViewTest.php b/code/tests/Athenia/Feature/Http/Article/ArticleViewTest.php index 8ff26a6d..326cf548 100644 --- a/code/tests/Athenia/Feature/Http/Article/ArticleViewTest.php +++ b/code/tests/Athenia/Feature/Http/Article/ArticleViewTest.php @@ -47,17 +47,6 @@ public function testNotLoggedInUserBlocked(): void $response->assertStatus(403); } - public function testIncorrectUserRoleBlocked(): void - { - foreach ($this->rolesWithoutAdmins([Role::ARTICLE_EDITOR, Role::ARTICLE_VIEWER]) as $role) { - $this->actAs($role); - - $response = $this->json('GET', $this->path); - - $response->assertStatus(403); - } - } - public function testNotFound(): void { $this->actAsUser(); diff --git a/code/tests/Athenia/Feature/Http/Authentication/SignUpTest.php b/code/tests/Athenia/Feature/Http/Authentication/SignUpTest.php index 94fe7f1f..edf97693 100644 --- a/code/tests/Athenia/Feature/Http/Authentication/SignUpTest.php +++ b/code/tests/Athenia/Feature/Http/Authentication/SignUpTest.php @@ -3,9 +3,14 @@ namespace Tests\Athenia\Feature\Http\Authentication; +use App\Athenia\Events\User\InvitationAcceptedEvent; use App\Athenia\Events\User\SignUpEvent; +use App\Athenia\Listeners\User\InvitationAcceptedListener; +use App\Models\Role; +use App\Models\User\InvitationToken; use App\Models\User\User; use Illuminate\Contracts\Events\Dispatcher; +use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Hash; use Tests\DatabaseSetupTrait; use Tests\TestCase; @@ -164,4 +169,195 @@ public function testWebsiteSignUpFailsEmailInUse(): void $response->assertStatus(400); } + + public function testSignUpSuccessWithValidInvitationToken(): void + { + Config::set('athenia.invitation_required', true); + + $role = Role::find(Role::ARTICLE_EDITOR); + $invitationToken = InvitationToken::factory()->create([ + 'token' => 'test-token-123', + 'role_id' => $role->id, + 'used_at' => null, + ]); + + $dispatcher = mock(Dispatcher::class); + + $signUpEventHit = false; + $invitationAcceptedEventHit = false; + + $dispatcher->shouldReceive('dispatch')->with(\Mockery::on(function ($event) use (&$signUpEventHit, &$invitationAcceptedEventHit) { + if ($event instanceof SignUpEvent) { + $signUpEventHit = true; + } + if ($event instanceof InvitationAcceptedEvent) { + $invitationAcceptedEventHit = true; + } + return true; + })); + + $this->app->bind(Dispatcher::class, function () use ($dispatcher) { + return $dispatcher; + }); + + $properties = [ + 'email' => 'guy@smiley.com', + 'first_name' => 'Steve', + 'password' => 'complex!', + 'invitation_token' => 'test-token-123', + ]; + + $response = $this->json('POST', '/v1/auth/sign-up', $properties); + + $response->assertStatus(201); + $response->assertJsonStructure([ + 'token' + ]); + + $this->assertTrue($signUpEventHit); + $this->assertTrue($invitationAcceptedEventHit); + + // Since we mocked the dispatcher, manually call the listener to verify its behavior + $user = User::where('email', 'guy@smiley.com')->first(); + $invitationAcceptedListener = app(InvitationAcceptedListener::class); + $invitationAcceptedListener->handle(new InvitationAcceptedEvent($user, $invitationToken)); + + // Verify the token was marked as used + $invitationToken->refresh(); + $this->assertNotNull($invitationToken->used_at); + + // Verify the user has the role + $this->assertTrue($user->roles->contains($role)); + } + + public function testSignUpFailsWhenInvitationRequiredButNotProvided(): void + { + Config::set('athenia.invitation_required', true); + + $properties = [ + 'email' => 'guy@smiley.com', + 'first_name' => 'Steve', + 'password' => 'complex!', + ]; + + $response = $this->json('POST', '/v1/auth/sign-up', $properties); + + $response->assertStatus(400); + $response->assertJson([ + 'errors' => [ + 'invitation_token' => ['The invitation token field is required.'], + ] + ]); + } + + public function testSignUpFailsWhenInvitationTokenIsInvalid(): void + { + Config::set('athenia.invitation_required', true); + + $properties = [ + 'email' => 'guy@smiley.com', + 'first_name' => 'Steve', + 'password' => 'complex!', + 'invitation_token' => 'invalid-token', + ]; + + $response = $this->json('POST', '/v1/auth/sign-up', $properties); + + $response->assertStatus(400); + $response->assertJson([ + 'errors' => [ + 'invitation_token' => ['The invitation token is invalid.'], + ] + ]); + } + + public function testSignUpFailsWhenInvitationTokenAlreadyUsed(): void + { + Config::set('athenia.invitation_required', true); + + InvitationToken::factory()->create([ + 'token' => 'used-token', + 'used_at' => now(), + ]); + + $properties = [ + 'email' => 'guy@smiley.com', + 'first_name' => 'Steve', + 'password' => 'complex!', + 'invitation_token' => 'used-token', + ]; + + $response = $this->json('POST', '/v1/auth/sign-up', $properties); + + $response->assertStatus(400); + $response->assertJson([ + 'errors' => [ + 'invitation_token' => ['The invitation token is invalid.'], + ] + ]); + } + + public function testSignUpSuccessWithInvitationTokenWithoutRole(): void + { + Config::set('athenia.invitation_required', true); + + InvitationToken::factory()->create([ + 'token' => 'token-without-role', + 'role_id' => null, + 'used_at' => null, + ]); + + $dispatcher = mock(Dispatcher::class); + + $dispatcher->shouldReceive('dispatch')->with(\Mockery::on(function ($event) { + return true; + })); + + $this->app->bind(Dispatcher::class, function () use ($dispatcher) { + return $dispatcher; + }); + + $properties = [ + 'email' => 'guy@smiley.com', + 'first_name' => 'Steve', + 'password' => 'complex!', + 'invitation_token' => 'token-without-role', + ]; + + $response = $this->json('POST', '/v1/auth/sign-up', $properties); + + $response->assertStatus(201); + + // Verify the user was created but has no additional roles + $user = User::where('email', 'guy@smiley.com')->first(); + $this->assertNotNull($user); + } + + public function testSignUpSuccessWhenInvitationNotRequired(): void + { + Config::set('athenia.invitation_required', false); + + $dispatcher = mock(Dispatcher::class); + + $dispatcher->shouldReceive('dispatch')->with(\Mockery::on(function ($event) { + return true; + })); + + $this->app->bind(Dispatcher::class, function () use ($dispatcher) { + return $dispatcher; + }); + + $properties = [ + 'email' => 'guy@smiley.com', + 'first_name' => 'Steve', + 'password' => 'complex!', + ]; + + $response = $this->json('POST', '/v1/auth/sign-up', $properties); + + $response->assertStatus(201); + $response->assertJsonStructure([ + 'token' + ]); + } } diff --git a/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteCreateTest.php b/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteCreateTest.php new file mode 100644 index 00000000..06693896 --- /dev/null +++ b/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteCreateTest.php @@ -0,0 +1,180 @@ +setupDatabase(); + $this->mockApplicationLog(); + $this->user = User::factory()->create(); + + $this->path.= $this->user->id . '/article-notes'; + } + + public function testNotLoggedInUserBlocked(): void + { + $response = $this->json('POST', $this->path); + + $response->assertStatus(403); + } + + public function testDifferentUserBlocked(): void + { + $otherUser = User::factory()->create(); + $this->actingAs($otherUser); + + $response = $this->json('POST', $this->path); + + $response->assertStatus(403); + } + + public function testCreateSuccessful(): void + { + $this->actingAs($this->user); + + $article = Article::factory()->create(); + + $response = $this->json('POST', $this->path, [ + 'article_id' => $article->id, + 'response' => 'My note', + ]); + + $response->assertStatus(201); + + /** @var ArticleNote $articleNote */ + $articleNote = ArticleNote::first(); + $this->assertEquals($this->user->id, $articleNote->user_id); + $this->assertEquals($article->id, $articleNote->article_id); + $this->assertEquals('My note', $articleNote->response); + $this->assertNull($articleNote->completed_at); + } + + public function testCreateSuccessfulWithCompleted(): void + { + $this->actingAs($this->user); + + $article = Article::factory()->create(); + + $response = $this->json('POST', $this->path, [ + 'article_id' => $article->id, + 'completed' => true, + ]); + + $response->assertStatus(201); + + /** @var ArticleNote $articleNote */ + $articleNote = ArticleNote::first(); + $this->assertNotNull($articleNote->completed_at); + } + + public function testCreateFailsMissingRequiredFields(): void + { + $this->actingAs($this->user); + + $response = $this->json('POST', $this->path); + + $response->assertStatus(400); + $response->assertJson([ + 'errors' => [ + 'article_id' => ['The article id field is required.'], + ] + ]); + } + + public function testCreateFailsInvalidBooleanFields(): void + { + $this->actingAs($this->user); + + $article = Article::factory()->create(); + + $response = $this->json('POST', $this->path, [ + 'article_id' => $article->id, + 'completed' => 'not-a-boolean', + ]); + + $response->assertStatus(400); + $response->assertJson([ + 'errors' => [ + 'completed' => ['The completed field must be true or false.'], + ] + ]); + } + + public function testCreateFailsInvalidStringFields(): void + { + $this->actingAs($this->user); + + $article = Article::factory()->create(); + + $response = $this->json('POST', $this->path, [ + 'article_id' => $article->id, + 'response' => 12345, + ]); + + $response->assertStatus(400); + $response->assertJson([ + 'errors' => [ + 'response' => ['The response must be a string.'], + ] + ]); + } + + public function testCreateFailsInvalidIntegerFields(): void + { + $this->actingAs($this->user); + + $response = $this->json('POST', $this->path, [ + 'article_id' => 'not-an-integer', + ]); + + $response->assertStatus(400); + $response->assertJson([ + 'errors' => [ + 'article_id' => ['The article id must be an integer.'], + ] + ]); + } + + public function testCreateFailsInvalidModelFields(): void + { + $this->actingAs($this->user); + + $response = $this->json('POST', $this->path, [ + 'article_id' => 99999, + ]); + + $response->assertStatus(400); + $response->assertJson([ + 'errors' => [ + 'article_id' => ['The selected article id is invalid.'], + ] + ]); + } +} diff --git a/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteDeleteTest.php b/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteDeleteTest.php new file mode 100644 index 00000000..02f54d3a --- /dev/null +++ b/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteDeleteTest.php @@ -0,0 +1,87 @@ +setupDatabase(); + $this->mockApplicationLog(); + } + + public function testNotLoggedInUserBlocked(): void + { + $user = User::factory()->create(); + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $user->id, + ]); + + $response = $this->json('DELETE', '/v1/users/' . $user->id . '/article-notes/' . $articleNote->id); + $response->assertStatus(403); + } + + public function testDifferentUserBlocked(): void + { + $user = User::factory()->create(); + $otherUser = User::factory()->create(); + $this->actingAs($otherUser); + + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $user->id, + ]); + + $response = $this->json('DELETE', '/v1/users/' . $user->id . '/article-notes/' . $articleNote->id); + $response->assertStatus(403); + } + + public function testDeleteSingle(): void + { + $this->actAsUser(); + + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $this->actingAs->id, + ]); + + $response = $this->json('DELETE', '/v1/users/' . $this->actingAs->id . '/article-notes/' . $articleNote->id); + + $response->assertStatus(204); + $this->assertNull(ArticleNote::find($articleNote->id)); + } + + public function testDeleteSingleInvalidIdFails(): void + { + $this->actAsUser(); + + $response = $this->json('DELETE', '/v1/users/' . $this->actingAs->id . '/article-notes/a') + ->assertExactJson([ + 'message' => 'This item was not found.', + ]); + $response->assertStatus(404); + } + + public function testDeleteSingleNotFoundFails(): void + { + $this->actAsUser(); + + $response = $this->json('DELETE', '/v1/users/' . $this->actingAs->id . '/article-notes/99999') + ->assertExactJson([ + 'message' => 'This item was not found.' + ]); + $response->assertStatus(404); + } +} diff --git a/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteIndexTest.php b/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteIndexTest.php new file mode 100644 index 00000000..c18eb97a --- /dev/null +++ b/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteIndexTest.php @@ -0,0 +1,160 @@ +setupDatabase(); + $this->mockApplicationLog(); + User::unsetEventDispatcher(); + } + + public function testNotLoggedInUserBlocked(): void + { + $user = User::factory()->create(); + + $response = $this->json('GET', $this->path . $user->id . '/article-notes'); + + $response->assertStatus(403); + } + + public function testIncorrectUserBlocked(): void + { + $this->actAsUser(); + $user = User::factory()->create(); + + $response = $this->json('GET', $this->path . $user->id . '/article-notes'); + + $response->assertStatus(403); + } + + public function testUserNotFound(): void + { + $this->actAsUser(); + + $response = $this->json('GET', $this->path . '12/article-notes'); + + $response->assertStatus(404); + } + + public function testGetPaginationEmpty(): void + { + $this->actAsUser(); + + $response = $this->json('GET', $this->path. $this->actingAs->id . '/article-notes'); + + $response->assertStatus(200); + $response->assertJson([ + 'total' => 0, + 'data' => [] + ]); + } + + public function testGetPaginationResult(): void + { + $this->actAsUser(); + + ArticleNote::factory()->count(4)->create(); + ArticleNote::factory()->count(15)->create([ + 'user_id' => $this->actingAs->id, + ]); + + // first page + $response = $this->json('GET', $this->path . $this->actingAs->id . '/article-notes'); + $response->assertStatus(200); + $response->assertJson([ + 'total' => 15, + 'current_page' => 1, + 'per_page' => 10, + 'from' => 1, + 'to' => 10, + 'last_page' => 2 + ]) + ->assertJsonStructure([ + 'data' => [ + '*' => array_keys((new ArticleNote())->toArray()) + ] + ]); + + // second page + $response = $this->json('GET', $this->path . $this->actingAs->id . '/article-notes?page=2'); + $response->assertStatus(200); + $response->assertJson([ + 'total' => 15, + 'current_page' => 2, + 'per_page' => 10, + 'from' => 11, + 'to' => 15, + 'last_page' => 2 + ]) + ->assertJsonStructure([ + 'data' => [ + '*' => array_keys((new ArticleNote())->toArray()) + ] + ]); + + // page with limit + $response = $this->json('GET', $this->path . $this->actingAs->id . '/article-notes?page=2&limit=5'); + $response->assertStatus(200); + $response->assertJson([ + 'total' => 15, + 'current_page' => 2, + 'per_page' => 5, + 'from' => 6, + 'to' => 10, + 'last_page' => 3 + ]) + ->assertJsonStructure([ + 'data' => [ + '*' => array_keys((new ArticleNote())->toArray()) + ] + ]); + } + + public function testGetPaginationWithExpands(): void + { + $this->actAsUser(); + + ArticleNote::factory()->count(5)->create([ + 'user_id' => $this->actingAs->id, + ]); + + // with expands + $response = $this->json('GET', $this->path . $this->actingAs->id . '/article-notes?expand[user]=*&expand[article]=*'); + $response->assertStatus(200); + $response->assertJson([ + 'total' => 5, + 'current_page' => 1, + 'per_page' => 10, + 'from' => 1, + 'to' => 5, + 'last_page' => 1 + ]) + ->assertJsonStructure([ + 'data' => [ + '*' => array_keys((new ArticleNote())->toArray()) + ] + ]); + } +} diff --git a/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteRandomArticleTest.php b/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteRandomArticleTest.php new file mode 100644 index 00000000..468c2c60 --- /dev/null +++ b/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteRandomArticleTest.php @@ -0,0 +1,278 @@ +setupDatabase(); + $this->mockApplicationLog(); + + // Seed article note statistics + $this->seed(\Database\Seeders\ArticleNoteStatisticsSeeder::class); + } + + public function testNotLoggedInUserBlocked(): void + { + $user = User::factory()->create(); + $this->path = '/v1/users/' . $user->id . '/random-article'; + + $response = $this->json('POST', $this->path); + + $response->assertStatus(403); + } + + public function testNotUserBlocked(): void + { + $user = User::factory()->create(); + $this->path = '/v1/users/' . $user->id . '/random-article'; + + $requestingUser = User::factory()->create(); + $this->actingAs($requestingUser); + + $response = $this->json('POST', $this->path); + + $response->assertStatus(403); + } + + public function testRandomArticleSuccessCreatesNoteAndReturnsWithArticle(): void + { + $user = User::factory()->create(); + $this->path = '/v1/users/' . $user->id . '/random-article'; + + // Create some articles + $article1 = Article::factory()->create(['title' => 'Test Article 1']); + $article2 = Article::factory()->create(['title' => 'Test Article 2']); + + $this->actingAs($user); + + $response = $this->json('POST', $this->path); + + $response->assertStatus(201); + $response->assertJson([ + 'user_id' => $user->id, + ]); + + // Verify article is loaded + $data = $response->json(); + $this->assertArrayHasKey('article', $data); + $this->assertArrayHasKey('id', $data['article']); + $this->assertArrayHasKey('title', $data['article']); + + // Verify article note was created + $this->assertDatabaseHas('article_notes', [ + 'user_id' => $user->id, + 'article_id' => $data['article_id'], + ]); + } + + public function testRandomArticleSelectsArticleWithoutNoteFirst(): void + { + $user = User::factory()->create(); + $this->path = '/v1/users/' . $user->id . '/random-article'; + + // Create two articles + $articleWithIncompleteNote = Article::factory()->create(['title' => 'Has Incomplete']); + $articleWithoutNote = Article::factory()->create(['title' => 'No Note']); + + // Create incomplete note on first article + \App\Models\User\ArticleNote::factory()->create([ + 'user_id' => $user->id, + 'article_id' => $articleWithIncompleteNote->id, + 'completed_at' => null, + ]); + + $this->actingAs($user); + + $response = $this->json('POST', $this->path); + + $response->assertStatus(201); + + // Should select the article without a note (or any other article without a note) + $data = $response->json(); + + // The selected article should NOT be the one with incomplete note + // (unless all articles without notes are exhausted, which shouldn't happen in this test) + $this->assertNotEquals($articleWithIncompleteNote->id, $data['article_id']); + } + + public function testRandomArticleNeverReturnsCompletedArticle(): void + { + $user = User::factory()->create(); + $this->path = '/v1/users/' . $user->id . '/random-article'; + + // Create two articles + $completedArticle = Article::factory()->create(['title' => 'Completed']); + $availableArticle = Article::factory()->create(['title' => 'Available']); + + // Mark first article as completed + \App\Models\User\ArticleNote::factory()->create([ + 'user_id' => $user->id, + 'article_id' => $completedArticle->id, + 'completed_at' => now(), + ]); + + $this->actingAs($user); + + $response = $this->json('POST', $this->path); + + $response->assertStatus(201); + + $data = $response->json(); + + // Should never return the completed article + $this->assertNotEquals($completedArticle->id, $data['article_id']); + } + + public function testRandomArticlePrefersArticlesWithLowerCompletedNotesStatistic(): void + { + $currentUser = User::factory()->create(); + $this->path = '/v1/users/' . $currentUser->id . '/random-article'; + + // Create three articles + $articleLowCompleted = Article::factory()->create(['title' => 'Low Completed']); + $articleMedCompleted = Article::factory()->create(['title' => 'Medium Completed']); + $articleHighCompleted = Article::factory()->create(['title' => 'High Completed']); + + // Create other users who have completed notes + $otherUser1 = User::factory()->create(); + $otherUser2 = User::factory()->create(); + $otherUser3 = User::factory()->create(); + + // Article high has 3 completed notes + \App\Models\User\ArticleNote::factory()->create([ + 'user_id' => $otherUser1->id, + 'article_id' => $articleHighCompleted->id, + 'completed_at' => now(), + ]); + \App\Models\User\ArticleNote::factory()->create([ + 'user_id' => $otherUser2->id, + 'article_id' => $articleHighCompleted->id, + 'completed_at' => now(), + ]); + \App\Models\User\ArticleNote::factory()->create([ + 'user_id' => $otherUser3->id, + 'article_id' => $articleHighCompleted->id, + 'completed_at' => now(), + ]); + + // Article medium has 2 completed notes + \App\Models\User\ArticleNote::factory()->create([ + 'user_id' => $otherUser1->id, + 'article_id' => $articleMedCompleted->id, + 'completed_at' => now(), + ]); + \App\Models\User\ArticleNote::factory()->create([ + 'user_id' => $otherUser2->id, + 'article_id' => $articleMedCompleted->id, + 'completed_at' => now(), + ]); + + // Article low has 1 completed note + \App\Models\User\ArticleNote::factory()->create([ + 'user_id' => $otherUser1->id, + 'article_id' => $articleLowCompleted->id, + 'completed_at' => now(), + ]); + + // Synchronize statistics for all articles + $synchronizationService = app(\App\Athenia\Contracts\Services\Statistic\StatisticSynchronizationServiceContract::class); + $synchronizationService->synchronizeTargetStatistics($articleLowCompleted); + $synchronizationService->synchronizeTargetStatistics($articleMedCompleted); + $synchronizationService->synchronizeTargetStatistics($articleHighCompleted); + + $this->actingAs($currentUser); + + $response = $this->json('POST', $this->path); + + $response->assertStatus(201); + $data = $response->json(); + + // Should select an article based on statistics + // With random ordering, any article could be selected, but it should be one with lower statistics + $this->assertNotNull($data['article_id']); + $this->assertIsInt($data['article_id']); + + // Verify the article_note was created for the current user + $this->assertEquals($currentUser->id, $data['user_id']); + } + + public function testRandomArticlePrefersArticlesWithLowerTotalNotesWhenCompletedEqual(): void + { + $currentUser = User::factory()->create(); + $this->path = '/v1/users/' . $currentUser->id . '/random-article'; + + // Create two articles with same completed notes but different total notes + $articleLowTotal = Article::factory()->create(['title' => 'Low Total']); + $articleHighTotal = Article::factory()->create(['title' => 'High Total']); + + // Create other users + $otherUser1 = User::factory()->create(); + $otherUser2 = User::factory()->create(); + $otherUser3 = User::factory()->create(); + + // Both articles have 1 completed note (same) + \App\Models\User\ArticleNote::factory()->create([ + 'user_id' => $otherUser1->id, + 'article_id' => $articleLowTotal->id, + 'completed_at' => now(), + ]); + \App\Models\User\ArticleNote::factory()->create([ + 'user_id' => $otherUser2->id, + 'article_id' => $articleHighTotal->id, + 'completed_at' => now(), + ]); + + // Article high total has 2 additional incomplete notes (3 total notes vs 1) + \App\Models\User\ArticleNote::factory()->create([ + 'user_id' => $otherUser3->id, + 'article_id' => $articleHighTotal->id, + 'completed_at' => null, + ]); + \App\Models\User\ArticleNote::factory()->create([ + 'user_id' => User::factory()->create()->id, + 'article_id' => $articleHighTotal->id, + 'completed_at' => null, + ]); + + // Synchronize statistics + $synchronizationService = app(\App\Athenia\Contracts\Services\Statistic\StatisticSynchronizationServiceContract::class); + $synchronizationService->synchronizeTargetStatistics($articleLowTotal); + $synchronizationService->synchronizeTargetStatistics($articleHighTotal); + + $this->actingAs($currentUser); + + $response = $this->json('POST', $this->path); + + $response->assertStatus(201); + $data = $response->json(); + + // Should select an article based on statistics + // With random ordering, any article could be selected, but it should prioritize lower statistics + $this->assertNotNull($data['article_id']); + $this->assertIsInt($data['article_id']); + + // Verify the article_note was created for the current user + $this->assertEquals($currentUser->id, $data['user_id']); + } +} diff --git a/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteUpdateTest.php b/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteUpdateTest.php new file mode 100644 index 00000000..3197c336 --- /dev/null +++ b/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteUpdateTest.php @@ -0,0 +1,172 @@ +setupDatabase(); + $this->mockApplicationLog(); + $this->user = User::factory()->create(); + + $this->path.= $this->user->id . '/article-notes/'; + } + + public function testNotLoggedInUserBlocked(): void + { + $articleNote = ArticleNote::factory()->create(); + + $response = $this->json('PUT', $this->path . $articleNote->id); + + $response->assertStatus(403); + } + + public function testDifferentUserBlocked(): void + { + $otherUser = User::factory()->create(); + $this->actingAs($otherUser); + + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $this->user->id, + ]); + + $response = $this->json('PUT', $this->path . $articleNote->id); + + $response->assertStatus(403); + } + + public function testNotFound(): void + { + $this->actingAs($this->user); + + $response = $this->json('PUT', $this->path . '453'); + + $response->assertStatus(404); + } + + public function testUpdateSuccessful(): void + { + $this->actingAs($this->user); + + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $this->user->id, + 'response' => 'Original response', + 'completed_at' => null, + ]); + + $response = $this->json('PUT', $this->path . $articleNote->id, [ + 'response' => 'Updated response', + ]); + + $response->assertStatus(200); + + /** @var ArticleNote $updated */ + $updated = ArticleNote::find($articleNote->id); + + $this->assertEquals('Updated response', $updated->response); + } + + public function testUpdateSuccessfulMarkCompleted(): void + { + $this->actingAs($this->user); + + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $this->user->id, + 'completed_at' => null, + ]); + + $response = $this->json('PUT', $this->path . $articleNote->id, [ + 'completed' => true, + ]); + + $response->assertStatus(200); + + /** @var ArticleNote $updated */ + $updated = ArticleNote::find($articleNote->id); + + $this->assertNotNull($updated->completed_at); + } + + public function testUpdateSuccessfulUnmarkCompleted(): void + { + $this->actingAs($this->user); + + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $this->user->id, + 'completed_at' => now(), + ]); + + $response = $this->json('PUT', $this->path . $articleNote->id, [ + 'completed' => false, + ]); + + $response->assertStatus(200); + + /** @var ArticleNote $updated */ + $updated = ArticleNote::find($articleNote->id); + + $this->assertNull($updated->completed_at); + } + + public function testUpdateFailsInvalidBooleanFields(): void + { + $this->actingAs($this->user); + + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $this->user->id, + ]); + + $response = $this->json('PUT', $this->path . $articleNote->id, [ + 'completed' => 'not-a-boolean', + ]); + + $response->assertStatus(400); + $response->assertJson([ + 'errors' => [ + 'completed' => ['The completed field must be true or false.'], + ] + ]); + } + + public function testUpdateFailsInvalidStringFields(): void + { + $this->actingAs($this->user); + + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $this->user->id, + ]); + + $response = $this->json('PUT', $this->path . $articleNote->id, [ + 'response' => 12345, + ]); + + $response->assertStatus(400); + $response->assertJson([ + 'errors' => [ + 'response' => ['The response must be a string.'], + ] + ]); + } +} diff --git a/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteViewTest.php b/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteViewTest.php new file mode 100644 index 00000000..7e6aa050 --- /dev/null +++ b/code/tests/Athenia/Feature/Http/User/ArticleNote/UserArticleNoteViewTest.php @@ -0,0 +1,92 @@ +setupDatabase(); + $this->mockApplicationLog(); + } + + public function testNotLoggedInUserBlocked(): void + { + $user = User::factory()->create(); + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $user->id, + ]); + + $response = $this->json('GET', $this->path . $user->id . '/article-notes/' . $articleNote->id); + + $response->assertStatus(403); + } + + public function testDifferentUserBlocked(): void + { + $this->actAsUser(); + $otherUser = User::factory()->create(); + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $otherUser->id, + ]); + + $response = $this->json('GET', $this->path . $otherUser->id . '/article-notes/' . $articleNote->id); + + $response->assertStatus(403); + } + + public function testUserNotFound(): void + { + $this->actAsUser(); + + $response = $this->json('GET', $this->path . '99999/article-notes/1'); + + $response->assertStatus(404); + } + + public function testArticleNoteNotFound(): void + { + $this->actAsUser(); + + $response = $this->json('GET', $this->path . $this->actingAs->id . '/article-notes/99999'); + + $response->assertStatus(404); + } + + public function testGetSingleSuccess(): void + { + $this->actAsUser(); + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $this->actingAs->id, + 'response' => 'Test response', + ]); + + $response = $this->json('GET', $this->path . $this->actingAs->id . '/article-notes/' . $articleNote->id); + + $response->assertStatus(200); + $response->assertJson([ + 'id' => $articleNote->id, + 'user_id' => $this->actingAs->id, + 'response' => 'Test response', + ]); + } +} diff --git a/code/tests/Athenia/Feature/Wiki/ArticleSummary/ArticleSummaryCreateTest.php b/code/tests/Athenia/Feature/Wiki/ArticleSummary/ArticleSummaryCreateTest.php new file mode 100644 index 00000000..1fe8b2fa --- /dev/null +++ b/code/tests/Athenia/Feature/Wiki/ArticleSummary/ArticleSummaryCreateTest.php @@ -0,0 +1,94 @@ +setupDatabase(); + $this->mockApplicationLog(); + $this->article = Article::factory()->create(); + $this->path = '/v1/articles/' . $this->article->id . '/article-summary'; + } + + public function testNotLoggedInUserBlocked(): void + { + $response = $this->json('POST', $this->path); + + $response->assertStatus(403); + } + + public function testUserWithoutRoleBlocked(): void + { + $user = User::factory()->create(); + $this->actingAs($user); + + $response = $this->json('POST', $this->path, [ + 'content' => 'Test summary content', + ]); + + $response->assertStatus(403); + } + + public function testCreateSuccessful(): void + { + $user = User::factory()->create(); + $user->roles()->attach(Role::ARTICLE_EDITOR); + $this->actingAs($user); + + $response = $this->json('POST', $this->path, [ + 'content' => 'This is a test summary for the article.', + ]); + + $response->assertStatus(201); + $response->assertJson([ + 'article_id' => $this->article->id, + 'content' => 'This is a test summary for the article.', + ]); + } + + public function testCreateFailsMissingRequiredFields(): void + { + $user = User::factory()->create(); + $user->roles()->attach(Role::ARTICLE_EDITOR); + $this->actingAs($user); + + $response = $this->json('POST', $this->path, []); + + $response->assertStatus(400); + $response->assertJsonValidationErrors(['content']); + } + + public function testCreateFailsInvalidStringFields(): void + { + $user = User::factory()->create(); + $user->roles()->attach(Role::ARTICLE_EDITOR); + $this->actingAs($user); + + $response = $this->json('POST', $this->path, [ + 'content' => 12345, + ]); + + $response->assertStatus(400); + $response->assertJsonValidationErrors(['content']); + } +} diff --git a/code/tests/Athenia/Feature/Wiki/ArticleSummary/ArticleSummaryDeleteTest.php b/code/tests/Athenia/Feature/Wiki/ArticleSummary/ArticleSummaryDeleteTest.php new file mode 100644 index 00000000..d75cc1e9 --- /dev/null +++ b/code/tests/Athenia/Feature/Wiki/ArticleSummary/ArticleSummaryDeleteTest.php @@ -0,0 +1,87 @@ +setupDatabase(); + $this->mockApplicationLog(); + $this->article = Article::factory()->create(); + $this->path = '/v1/articles/' . $this->article->id . '/article-summary'; + } + + public function testNotLoggedInUserBlocked(): void + { + ArticleSummary::factory()->create([ + 'article_id' => $this->article->id, + ]); + + $response = $this->json('DELETE', $this->path); + + $response->assertStatus(403); + } + + public function testUserWithoutRoleBlocked(): void + { + $user = User::factory()->create(); + $this->actingAs($user); + + ArticleSummary::factory()->create([ + 'article_id' => $this->article->id, + ]); + + $response = $this->json('DELETE', $this->path); + + $response->assertStatus(403); + } + + public function testNotFound(): void + { + $user = User::factory()->create(); + $user->roles()->attach(Role::ARTICLE_EDITOR); + $this->actingAs($user); + + $response = $this->json('DELETE', $this->path); + + $response->assertStatus(404); + } + + public function testDeleteSuccessful(): void + { + $user = User::factory()->create(); + $user->roles()->attach(Role::ARTICLE_EDITOR); + $this->actingAs($user); + + $summary = ArticleSummary::factory()->create([ + 'article_id' => $this->article->id, + ]); + + $response = $this->json('DELETE', $this->path); + + $response->assertStatus(204); + + // Verify the summary was soft-deleted + $this->assertNull(ArticleSummary::find($summary->id)); + } +} diff --git a/code/tests/Athenia/Feature/Wiki/ArticleSummary/ArticleSummaryUpdateTest.php b/code/tests/Athenia/Feature/Wiki/ArticleSummary/ArticleSummaryUpdateTest.php new file mode 100644 index 00000000..bee9d0d4 --- /dev/null +++ b/code/tests/Athenia/Feature/Wiki/ArticleSummary/ArticleSummaryUpdateTest.php @@ -0,0 +1,113 @@ +setupDatabase(); + $this->mockApplicationLog(); + $this->article = Article::factory()->create(); + $this->path = '/v1/articles/' . $this->article->id . '/article-summary'; + } + + public function testNotLoggedInUserBlocked(): void + { + ArticleSummary::factory()->create([ + 'article_id' => $this->article->id, + ]); + + $response = $this->json('PUT', $this->path); + + $response->assertStatus(403); + } + + public function testUserWithoutRoleBlocked(): void + { + $user = User::factory()->create(); + $this->actingAs($user); + + ArticleSummary::factory()->create([ + 'article_id' => $this->article->id, + ]); + + $response = $this->json('PUT', $this->path, [ + 'content' => 'Updated summary content', + ]); + + $response->assertStatus(403); + } + + public function testNotFound(): void + { + $user = User::factory()->create(); + $user->roles()->attach(Role::ARTICLE_EDITOR); + $this->actingAs($user); + + $response = $this->json('PUT', $this->path, [ + 'content' => 'Updated content', + ]); + + $response->assertStatus(404); + } + + public function testUpdateSuccessful(): void + { + $user = User::factory()->create(); + $user->roles()->attach(Role::ARTICLE_EDITOR); + $this->actingAs($user); + + ArticleSummary::factory()->create([ + 'article_id' => $this->article->id, + 'content' => 'Original summary content', + ]); + + $response = $this->json('PUT', $this->path, [ + 'content' => 'Updated summary content', + ]); + + $response->assertStatus(200); + $response->assertJson([ + 'article_id' => $this->article->id, + 'content' => 'Updated summary content', + ]); + } + + public function testUpdateFailsInvalidStringFields(): void + { + $user = User::factory()->create(); + $user->roles()->attach(Role::ARTICLE_EDITOR); + $this->actingAs($user); + + ArticleSummary::factory()->create([ + 'article_id' => $this->article->id, + ]); + + $response = $this->json('PUT', $this->path, [ + 'content' => 12345, + ]); + + $response->assertStatus(400); + $response->assertJsonValidationErrors(['content']); + } +} diff --git a/code/tests/Athenia/Feature/Wiki/ArticleSummary/ArticleSummaryViewTest.php b/code/tests/Athenia/Feature/Wiki/ArticleSummary/ArticleSummaryViewTest.php new file mode 100644 index 00000000..fa9fff0f --- /dev/null +++ b/code/tests/Athenia/Feature/Wiki/ArticleSummary/ArticleSummaryViewTest.php @@ -0,0 +1,69 @@ +setupDatabase(); + $this->mockApplicationLog(); + $this->article = Article::factory()->create(); + $this->path = '/v1/articles/' . $this->article->id . '/article-summary'; + } + + public function testNotLoggedInUserBlocked(): void + { + $response = $this->json('GET', $this->path); + + $response->assertStatus(403); + } + + public function testViewSuccessful(): void + { + $user = User::factory()->create(); + $this->actingAs($user); + + $summary = ArticleSummary::factory()->create([ + 'article_id' => $this->article->id, + 'content' => 'Test summary content', + ]); + + $response = $this->json('GET', $this->path); + + $response->assertStatus(200); + $response->assertJson([ + 'id' => $summary->id, + 'article_id' => $this->article->id, + 'content' => 'Test summary content', + ]); + } + + public function testViewNotFound(): void + { + $user = User::factory()->create(); + $this->actingAs($user); + + $response = $this->json('GET', $this->path); + + $response->assertStatus(404); + } +} diff --git a/code/tests/Athenia/Integration/Policies/User/ArticleNotePolicyTest.php b/code/tests/Athenia/Integration/Policies/User/ArticleNotePolicyTest.php new file mode 100644 index 00000000..7b6cd26e --- /dev/null +++ b/code/tests/Athenia/Integration/Policies/User/ArticleNotePolicyTest.php @@ -0,0 +1,135 @@ +create(); + + $policy = new ArticleNotePolicy(); + + $this->assertTrue($policy->all($user, $user)); + } + + public function testAllFails(): void + { + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + + $policy = new ArticleNotePolicy(); + + $this->assertFalse($policy->all($user1, $user2)); + } + + public function testCreatePasses(): void + { + $user = User::factory()->create(); + + $policy = new ArticleNotePolicy(); + + $this->assertTrue($policy->create($user, $user)); + } + + public function testCreateFails(): void + { + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + + $policy = new ArticleNotePolicy(); + + $this->assertFalse($policy->create($user1, $user2)); + } + + public function testViewPasses(): void + { + $user = User::factory()->create(); + + $policy = new ArticleNotePolicy(); + + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $user->id, + ]); + $this->assertTrue($policy->view($user, $user, $articleNote)); + } + + public function testViewFails(): void + { + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $user2->id, + ]); + + $policy = new ArticleNotePolicy(); + + $this->assertFalse($policy->view($user1, $user2, $articleNote)); + $this->assertFalse($policy->view($user1, $user1, $articleNote)); + } + + public function testUpdatePasses(): void + { + $user = User::factory()->create(); + + $policy = new ArticleNotePolicy(); + + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $user->id, + ]); + $this->assertTrue($policy->update($user, $user, $articleNote)); + } + + public function testUpdateFails(): void + { + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $user2->id, + ]); + + $policy = new ArticleNotePolicy(); + + $this->assertFalse($policy->update($user1, $user2, $articleNote)); + $this->assertFalse($policy->update($user1, $user1, $articleNote)); + } + + public function testDeletePasses(): void + { + $user = User::factory()->create(); + + $policy = new ArticleNotePolicy(); + + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $user->id, + ]); + $this->assertTrue($policy->delete($user, $user, $articleNote)); + } + + public function testDeleteFails(): void + { + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + $articleNote = ArticleNote::factory()->create([ + 'user_id' => $user2->id, + ]); + + $policy = new ArticleNotePolicy(); + + $this->assertFalse($policy->delete($user1, $user2, $articleNote)); + $this->assertFalse($policy->delete($user1, $user1, $articleNote)); + } +} diff --git a/code/tests/Athenia/Integration/Policies/Wiki/ArticlePolicyTest.php b/code/tests/Athenia/Integration/Policies/Wiki/ArticlePolicyTest.php index 14cc99b2..89e2e5a5 100644 --- a/code/tests/Athenia/Integration/Policies/Wiki/ArticlePolicyTest.php +++ b/code/tests/Athenia/Integration/Policies/Wiki/ArticlePolicyTest.php @@ -29,17 +29,6 @@ public function testAllSuccess(): void } } - public function testAllBlocks(): void - { - $policy = new ArticlePolicy(); - - foreach ($this->rolesWithoutAdmins([Role::ARTICLE_EDITOR, Role::ARTICLE_VIEWER]) as $role) { - $user = $this->getUserOfRole($role); - - $this->assertFalse($policy->all($user)); - } - } - public function testViewSuccess(): void { $policy = new ArticlePolicy(); @@ -51,17 +40,6 @@ public function testViewSuccess(): void } } - public function testViewBlocks(): void - { - $policy = new ArticlePolicy(); - - foreach ($this->rolesWithoutAdmins([Role::ARTICLE_EDITOR, Role::ARTICLE_VIEWER]) as $role) { - $user = $this->getUserOfRole($role); - - $this->assertFalse($policy->view($user, new Article())); - } - } - public function testCreateSuccess(): void { $policy = new ArticlePolicy(); diff --git a/code/tests/Athenia/Integration/Policies/Wiki/ArticleSummaryPolicyTest.php b/code/tests/Athenia/Integration/Policies/Wiki/ArticleSummaryPolicyTest.php new file mode 100644 index 00000000..77271352 --- /dev/null +++ b/code/tests/Athenia/Integration/Policies/Wiki/ArticleSummaryPolicyTest.php @@ -0,0 +1,99 @@ +setupDatabase(); + } + + public function testViewPasses(): void + { + $user = User::factory()->create(); + $article = Article::factory()->create(); + + $policy = new ArticleSummaryPolicy(); + + $this->assertTrue($policy->view($user, $article)); + } + + public function testCreatePassesWithRole(): void + { + $user = User::factory()->create(); + $user->roles()->attach(Role::ARTICLE_EDITOR); + $article = Article::factory()->create(); + + $policy = new ArticleSummaryPolicy(); + + $this->assertTrue($policy->create($user, $article)); + } + + public function testCreateFailsWithoutRole(): void + { + $user = User::factory()->create(); + $article = Article::factory()->create(); + + $policy = new ArticleSummaryPolicy(); + + $this->assertFalse($policy->create($user, $article)); + } + + public function testUpdatePassesWithRole(): void + { + $user = User::factory()->create(); + $user->roles()->attach(Role::ARTICLE_EDITOR); + $article = Article::factory()->create(); + + $policy = new ArticleSummaryPolicy(); + + $this->assertTrue($policy->update($user, $article)); + } + + public function testUpdateFailsWithoutRole(): void + { + $user = User::factory()->create(); + $article = Article::factory()->create(); + + $policy = new ArticleSummaryPolicy(); + + $this->assertFalse($policy->update($user, $article)); + } + + public function testDeletePassesWithRole(): void + { + $user = User::factory()->create(); + $user->roles()->attach(Role::ARTICLE_EDITOR); + $article = Article::factory()->create(); + + $policy = new ArticleSummaryPolicy(); + + $this->assertTrue($policy->delete($user, $article)); + } + + public function testDeleteFailsWithoutRole(): void + { + $user = User::factory()->create(); + $article = Article::factory()->create(); + + $policy = new ArticleSummaryPolicy(); + + $this->assertFalse($policy->delete($user, $article)); + } +} diff --git a/code/tests/Athenia/Integration/Repositories/Statistic/StatisticRepositoryTest.php b/code/tests/Athenia/Integration/Repositories/Statistic/StatisticRepositoryTest.php index 1494468b..c228ab62 100644 --- a/code/tests/Athenia/Integration/Repositories/Statistic/StatisticRepositoryTest.php +++ b/code/tests/Athenia/Integration/Repositories/Statistic/StatisticRepositoryTest.php @@ -79,6 +79,46 @@ public function testFindAllWithFilterReturnsCollection() $this->assertCount(1, $models); } + public function testFindAllForModelReturnsOnlyStatisticsForThatModel() + { + foreach (Statistic::all() as $model) { + $model->delete(); + } + + // Create statistics for different models + Statistic::factory()->count(3)->create(['model' => 'article']); + Statistic::factory()->count(2)->create(['model' => 'user']); + Statistic::factory()->count(1)->create(['model' => 'organization']); + + $articleStatistics = $this->repository->findAllForModel('article'); + $userStatistics = $this->repository->findAllForModel('user'); + $organizationStatistics = $this->repository->findAllForModel('organization'); + + $this->assertCount(3, $articleStatistics); + $this->assertCount(2, $userStatistics); + $this->assertCount(1, $organizationStatistics); + + // Verify all returned statistics have the correct model + foreach ($articleStatistics as $statistic) { + $this->assertEquals('article', $statistic->model); + } + foreach ($userStatistics as $statistic) { + $this->assertEquals('user', $statistic->model); + } + } + + public function testFindAllForModelReturnsEmptyCollectionWhenNoStatisticsExist() + { + foreach (Statistic::all() as $model) { + $model->delete(); + } + + $statistics = $this->repository->findAllForModel('nonexistent'); + + $this->assertCount(0, $statistics); + $this->assertInstanceOf(Collection::class, $statistics); + } + public function testFindReturnsModel() { foreach (Statistic::all() as $model) { diff --git a/code/tests/Athenia/Integration/Repositories/User/ArticleNoteRepositoryTest.php b/code/tests/Athenia/Integration/Repositories/User/ArticleNoteRepositoryTest.php new file mode 100644 index 00000000..fb49ccc9 --- /dev/null +++ b/code/tests/Athenia/Integration/Repositories/User/ArticleNoteRepositoryTest.php @@ -0,0 +1,144 @@ +setupDatabase(); + + $this->repository = new ArticleNoteRepository( + new ArticleNote(), + $this->getGenericLogMock(), + ); + } + + public function testFindAllSuccess(): void + { + ArticleNote::factory()->count(5)->create(); + $items = $this->repository->findAll(); + $this->assertCount(5, $items); + } + + public function testFindAllEmpty(): void + { + $items = $this->repository->findAll(); + $this->assertEmpty($items); + } + + public function testFindOrFailSuccess(): void + { + $model = ArticleNote::factory()->create(); + + $foundModel = $this->repository->findOrFail($model->id); + $this->assertEquals($model->id, $foundModel->id); + } + + public function testFindOrFailFails(): void + { + ArticleNote::factory()->create(['id' => 19]); + + $this->expectException(ModelNotFoundException::class); + $this->repository->findOrFail(20); + } + + public function testCreateSuccess(): void + { + /** @var User $user */ + $user = User::factory()->create(); + + /** @var Article $article */ + $article = Article::factory()->create(); + + /** @var ArticleNote $articleNote */ + $articleNote = $this->repository->create([ + 'article_id' => $article->id, + 'response' => 'A response', + ], $user); + + $this->assertEquals($articleNote->user_id, $user->id); + $this->assertEquals($articleNote->article_id, $article->id); + $this->assertEquals('A response', $articleNote->response); + $this->assertNull($articleNote->completed_at); + } + + public function testCreateSuccessWithCompleted(): void + { + /** @var User $user */ + $user = User::factory()->create(); + + /** @var Article $article */ + $article = Article::factory()->create(); + + /** @var ArticleNote $articleNote */ + $articleNote = $this->repository->create([ + 'article_id' => $article->id, + 'completed' => true, + ], $user); + + $this->assertEquals($articleNote->user_id, $user->id); + $this->assertEquals($articleNote->article_id, $article->id); + $this->assertNotNull($articleNote->completed_at); + } + + public function testUpdateSuccess(): void + { + $model = ArticleNote::factory()->create([ + 'response' => 'Original response', + 'completed_at' => null, + ]); + + $updated = $this->repository->update($model, [ + 'response' => 'Updated response', + 'completed' => true, + ]); + + $this->assertEquals('Updated response', $updated->response); + $this->assertNotNull($updated->completed_at); + } + + public function testUpdateSuccessUnmarkCompleted(): void + { + $model = ArticleNote::factory()->create([ + 'completed_at' => now(), + ]); + + $updated = $this->repository->update($model, [ + 'completed' => false, + ]); + + $this->assertNull($updated->completed_at); + } + + public function testDeleteSuccess(): void + { + $model = ArticleNote::factory()->create(); + + $this->repository->delete($model); + + $this->assertNull(ArticleNote::find($model->id)); + } +} diff --git a/code/tests/Athenia/Integration/Repositories/User/InvitationTokenRepositoryTest.php b/code/tests/Athenia/Integration/Repositories/User/InvitationTokenRepositoryTest.php new file mode 100644 index 00000000..b65440fd --- /dev/null +++ b/code/tests/Athenia/Integration/Repositories/User/InvitationTokenRepositoryTest.php @@ -0,0 +1,139 @@ +setupDatabase(); + + $this->tokenGenerationService = mock(TokenGenerationServiceContract::class); + $this->repository = new InvitationTokenRepository( + new InvitationToken(), + $this->getGenericLogMock(), + $this->tokenGenerationService + ); + } + + public function testFindAllThrowsException(): void + { + $this->expectException(NotImplementedException::class); + + $this->repository->findAll(); + } + + public function testFindOrFailThrowsException(): void + { + $this->expectException(NotImplementedException::class); + + $this->repository->findOrFail(1); + } + + public function testDeleteThrowsException(): void + { + $this->expectException(NotImplementedException::class); + + $this->repository->delete(new InvitationToken()); + } + + public function testCreateSuccess(): void + { + $role = Role::find(Role::ARTICLE_EDITOR); + + /** @var InvitationToken $invitationToken */ + $invitationToken = $this->repository->create([ + 'token' => 'hello-world', + 'role_id' => $role->id, + ]); + + $this->assertEquals('hello-world', $invitationToken->token); + $this->assertEquals($role->id, $invitationToken->role_id); + $this->assertNull($invitationToken->used_at); + } + + public function testCreateSuccessWithoutRole(): void + { + /** @var InvitationToken $invitationToken */ + $invitationToken = $this->repository->create([ + 'token' => 'hello-world', + ]); + + $this->assertEquals('hello-world', $invitationToken->token); + $this->assertNull($invitationToken->role_id); + $this->assertNull($invitationToken->used_at); + } + + public function testUpdateSuccess(): void + { + $invitationToken = InvitationToken::factory()->create([ + 'token' => 'test-token', + 'used_at' => null, + ]); + + $this->assertNull($invitationToken->used_at); + + $this->repository->update($invitationToken, [ + 'used_at' => now(), + ]); + + $invitationToken->refresh(); + $this->assertNotNull($invitationToken->used_at); + } + + public function testFindByToken(): void + { + $invitationToken = InvitationToken::factory()->create([ + 'token' => '1234', + ]); + + $this->assertEquals($invitationToken->id, $this->repository->findByToken('1234')->id); + $this->assertNull($this->repository->findByToken('12345')); + } + + public function testGenerateUniqueTokenSuccess(): void + { + $this->tokenGenerationService->shouldReceive('generateToken')->once()->andReturn('unique-token-12345'); + + $this->assertEquals('unique-token-12345', $this->repository->generateUniqueToken()); + } + + public function testGenerateUniqueTokenThrowsException(): void + { + InvitationToken::factory()->create([ + 'token' => '12345', + ]); + + $this->tokenGenerationService->shouldReceive('generateToken')->times(5)->andReturn('12345'); + $this->expectException(\OverflowException::class); + + $this->repository->generateUniqueToken(); + } +} diff --git a/code/tests/Athenia/Integration/Repositories/Wiki/ArticleRepositorySelectArticleTest.php b/code/tests/Athenia/Integration/Repositories/Wiki/ArticleRepositorySelectArticleTest.php new file mode 100644 index 00000000..b04c28bd --- /dev/null +++ b/code/tests/Athenia/Integration/Repositories/Wiki/ArticleRepositorySelectArticleTest.php @@ -0,0 +1,417 @@ +setupDatabase(); + $this->repository = app(ArticleRepositoryContract::class); + + // Seed article note statistics + $this->seed(\Database\Seeders\ArticleNoteStatisticsSeeder::class); + } + + public function testSelectArticleForUserNeverReturnsCompletedArticles(): void + { + // Clean up any existing articles + Article::query()->delete(); + + /** @var User $user */ + $user = User::factory()->create(); + + // Create two articles + $article1 = Article::factory()->create(['title' => 'Article 1']); + $article2 = Article::factory()->create(['title' => 'Article 2']); + + // User has completed article 1 + ArticleNote::factory()->create([ + 'user_id' => $user->id, + 'article_id' => $article1->id, + 'completed_at' => now(), + ]); + + // Select article for user + $selected = $this->repository->selectArticleForUser($user); + + // Should return an article, but never article 1 (the completed one) + $this->assertNotNull($selected); + $this->assertNotEquals($article1->id, $selected->id); + + // Verify the user has NOT completed a note on the selected article + $userNote = ArticleNote::where('user_id', $user->id) + ->where('article_id', $selected->id) + ->first(); + if ($userNote) { + $this->assertNull($userNote->completed_at); + } + } + + public function testSelectArticleForUserPrioritizesArticlesWithNoNotes(): void + { + // Clean up any existing articles + Article::query()->delete(); + + /** @var User $user */ + $user = User::factory()->create(); + + // Create two articles + $articleWithNote = Article::factory()->create(['title' => 'Has Incomplete Note']); + $articleWithoutNote = Article::factory()->create(['title' => 'No Note']); + + // User has incomplete note on first article + ArticleNote::factory()->create([ + 'user_id' => $user->id, + 'article_id' => $articleWithNote->id, + 'completed_at' => null, + ]); + + // Select article for user + $selected = $this->repository->selectArticleForUser($user); + + // Should return an article (prefer one without note over one with incomplete note) + $this->assertNotNull($selected); + + // Verify the user has NOT completed a note on the selected article + $userNote = ArticleNote::where('user_id', $user->id) + ->where('article_id', $selected->id) + ->first(); + + if ($userNote) { + $this->assertNull($userNote->completed_at, 'Selected article should not have a completed note'); + } + } + + public function testSelectArticleForUserOrdersByLowestCompletedNotesStatistic(): void + { + // Clean up any existing articles + Article::query()->delete(); + + /** @var User $currentUser */ + $currentUser = User::factory()->create(); + + // Create three articles + $articleLowCompleted = Article::factory()->create(['title' => 'Low Completed']); + $articleHighCompleted = Article::factory()->create(['title' => 'High Completed']); + $articleNoCompleted = Article::factory()->create(['title' => 'No Completed']); + + // Create other users who have completed notes + $otherUser1 = User::factory()->create(); + $otherUser2 = User::factory()->create(); + $otherUser3 = User::factory()->create(); + + // Article 2 has 3 completed notes (high) + ArticleNote::factory()->create([ + 'user_id' => $otherUser1->id, + 'article_id' => $articleHighCompleted->id, + 'completed_at' => now(), + ]); + ArticleNote::factory()->create([ + 'user_id' => $otherUser2->id, + 'article_id' => $articleHighCompleted->id, + 'completed_at' => now(), + ]); + ArticleNote::factory()->create([ + 'user_id' => $otherUser3->id, + 'article_id' => $articleHighCompleted->id, + 'completed_at' => now(), + ]); + + // Article 1 has 1 completed note (low) + ArticleNote::factory()->create([ + 'user_id' => $otherUser1->id, + 'article_id' => $articleLowCompleted->id, + 'completed_at' => now(), + ]); + + // Synchronize statistics for all articles + $synchronizationService = app(StatisticSynchronizationServiceContract::class); + $synchronizationService->synchronizeTargetStatistics($articleLowCompleted); + $synchronizationService->synchronizeTargetStatistics($articleHighCompleted); + $synchronizationService->synchronizeTargetStatistics($articleNoCompleted); + + // Select article for current user + $selected = $this->repository->selectArticleForUser($currentUser); + + // Should return an article (will prefer ones with lower completed notes) + $this->assertNotNull($selected); + + // Mark all three test articles as completed for current user to test that they're excluded + ArticleNote::updateOrCreate( + ['user_id' => $currentUser->id, 'article_id' => $articleNoCompleted->id], + ['completed_at' => now()] + ); + ArticleNote::updateOrCreate( + ['user_id' => $currentUser->id, 'article_id' => $articleLowCompleted->id], + ['completed_at' => now()] + ); + ArticleNote::updateOrCreate( + ['user_id' => $currentUser->id, 'article_id' => $articleHighCompleted->id], + ['completed_at' => now()] + ); + + // Select again + $selected2 = $this->repository->selectArticleForUser($currentUser); + + // Should return a different article (not any of the completed ones) + $this->assertNotNull($selected2); + $this->assertNotEquals($articleNoCompleted->id, $selected2->id); + $this->assertNotEquals($articleLowCompleted->id, $selected2->id); + $this->assertNotEquals($articleHighCompleted->id, $selected2->id); + } + + public function testSelectArticleForUserOrdersByLowestTotalNotesStatistic(): void + { + // Clean up any existing articles + Article::query()->delete(); + + /** @var User $currentUser */ + $currentUser = User::factory()->create(); + + // Create two articles with same completed notes but different total notes + $articleLowTotal = Article::factory()->create(['title' => 'Low Total']); + $articleHighTotal = Article::factory()->create(['title' => 'High Total']); + + // Create other users + $otherUser1 = User::factory()->create(); + $otherUser2 = User::factory()->create(); + $otherUser3 = User::factory()->create(); + + // Both articles have 1 completed note (same) + ArticleNote::factory()->create([ + 'user_id' => $otherUser1->id, + 'article_id' => $articleLowTotal->id, + 'completed_at' => now(), + ]); + ArticleNote::factory()->create([ + 'user_id' => $otherUser2->id, + 'article_id' => $articleHighTotal->id, + 'completed_at' => now(), + ]); + + // Article high total has 2 additional incomplete notes (3 total notes vs 1) + ArticleNote::factory()->create([ + 'user_id' => $otherUser3->id, + 'article_id' => $articleHighTotal->id, + 'completed_at' => null, + ]); + ArticleNote::factory()->create([ + 'user_id' => User::factory()->create()->id, + 'article_id' => $articleHighTotal->id, + 'completed_at' => null, + ]); + + // Synchronize statistics + $synchronizationService = app(StatisticSynchronizationServiceContract::class); + $synchronizationService->synchronizeTargetStatistics($articleLowTotal); + $synchronizationService->synchronizeTargetStatistics($articleHighTotal); + + // Select article for current user + $selected = $this->repository->selectArticleForUser($currentUser); + + // Should return an article + $this->assertNotNull($selected); + + // Verify current user has no note (or no completed note) on the selected article + $userNote = ArticleNote::where('user_id', $currentUser->id) + ->where('article_id', $selected->id) + ->first(); + + if ($userNote) { + $this->assertNull($userNote->completed_at, 'Selected article should not have a completed note'); + } + } + + public function testSelectArticleForUserCanReturnArticleWithIncompleteNote(): void + { + // Clean up any existing articles + Article::query()->delete(); + + /** @var User $user */ + $user = User::factory()->create(); + + // Create an article with an incomplete note + $articleWithIncompleteNote = Article::factory()->create(['title' => 'Article With Incomplete Note']); + ArticleNote::factory()->create([ + 'user_id' => $user->id, + 'article_id' => $articleWithIncompleteNote->id, + 'completed_at' => null, + ]); + + // Select article for user + $selected = $this->repository->selectArticleForUser($user); + + // Should return an article (either one without notes or the one with incomplete note) + $this->assertNotNull($selected); + + // If it returned the article with incomplete note, verify it's correct + if ($selected->id === $articleWithIncompleteNote->id) { + $userNote = ArticleNote::where('user_id', $user->id) + ->where('article_id', $selected->id) + ->first(); + $this->assertNotNull($userNote); + $this->assertNull($userNote->completed_at); + } + } + + public function testSelectArticleForUserExcludesCompletedArticles(): void + { + // Clean up any existing articles + Article::query()->delete(); + + /** @var User $user */ + $user = User::factory()->create(); + + // Create three articles + $completedArticle1 = Article::factory()->create(['title' => 'Completed 1']); + $completedArticle2 = Article::factory()->create(['title' => 'Completed 2']); + $availableArticle = Article::factory()->create(['title' => 'Available']); + + // Mark first two as completed + ArticleNote::factory()->create([ + 'user_id' => $user->id, + 'article_id' => $completedArticle1->id, + 'completed_at' => now(), + ]); + ArticleNote::factory()->create([ + 'user_id' => $user->id, + 'article_id' => $completedArticle2->id, + 'completed_at' => now(), + ]); + + // Select article for user (should not return completed articles) + $selected = $this->repository->selectArticleForUser($user); + + // Should return an article + $this->assertNotNull($selected); + + // Verify the selected article is NOT one of the completed ones + $this->assertNotEquals($completedArticle1->id, $selected->id); + $this->assertNotEquals($completedArticle2->id, $selected->id); + + // Verify if user has a note on the selected article, it's not completed + $userNote = ArticleNote::where('user_id', $user->id) + ->where('article_id', $selected->id) + ->first(); + + if ($userNote) { + $this->assertNull($userNote->completed_at, 'Selected article should not have a completed note'); + } + } + + public function testSelectArticleForUserCombinesPriorityAndStatistics(): void + { + // Clean up any existing articles + Article::query()->delete(); + + /** @var User $currentUser */ + $currentUser = User::factory()->create(); + + // Create articles in different priority groups + $unstartedHighStats = Article::factory()->create(['title' => 'Unstarted High Stats']); + $unstartedLowStats = Article::factory()->create(['title' => 'Unstarted Low Stats']); + $incompleteHighStats = Article::factory()->create(['title' => 'Incomplete High Stats']); + $incompleteLowStats = Article::factory()->create(['title' => 'Incomplete Low Stats']); + + // Create other users to generate statistics + $otherUsers = User::factory()->count(5)->create(); + + // Generate high statistics for some articles + foreach ($otherUsers as $otherUser) { + ArticleNote::factory()->create([ + 'user_id' => $otherUser->id, + 'article_id' => $unstartedHighStats->id, + 'completed_at' => now(), + ]); + ArticleNote::factory()->create([ + 'user_id' => $otherUser->id, + 'article_id' => $incompleteHighStats->id, + 'completed_at' => now(), + ]); + } + + // Current user has incomplete notes on incomplete articles + ArticleNote::factory()->create([ + 'user_id' => $currentUser->id, + 'article_id' => $incompleteHighStats->id, + 'completed_at' => null, + ]); + ArticleNote::factory()->create([ + 'user_id' => $currentUser->id, + 'article_id' => $incompleteLowStats->id, + 'completed_at' => null, + ]); + + // Synchronize statistics + $synchronizationService = app(StatisticSynchronizationServiceContract::class); + foreach ([$unstartedHighStats, $unstartedLowStats, $incompleteHighStats, $incompleteLowStats] as $article) { + $synchronizationService->synchronizeTargetStatistics($article); + } + + // Select article for current user + $selected = $this->repository->selectArticleForUser($currentUser); + + // Should return an article + $this->assertNotNull($selected); + + // Verify it's not one of the articles the user already has an incomplete note on + // (unless all unstarted articles are exhausted) + $userNote = ArticleNote::where('user_id', $currentUser->id) + ->where('article_id', $selected->id) + ->first(); + + // The selected article should either have no note (priority 1) or an incomplete note (priority 2) + if ($userNote) { + $this->assertNull($userNote->completed_at, 'Selected article should not have a completed note'); + } + + // Mark the two test unstarted articles as completed + ArticleNote::updateOrCreate( + ['user_id' => $currentUser->id, 'article_id' => $unstartedLowStats->id], + ['completed_at' => now()] + ); + ArticleNote::updateOrCreate( + ['user_id' => $currentUser->id, 'article_id' => $unstartedHighStats->id], + ['completed_at' => now()] + ); + + // Select again + $selected2 = $this->repository->selectArticleForUser($currentUser); + + // Should still return an article + $this->assertNotNull($selected2); + + // Verify it's not a completed article + $this->assertNotEquals($unstartedLowStats->id, $selected2->id); + $this->assertNotEquals($unstartedHighStats->id, $selected2->id); + + // Verify if there's a user note, it's not completed + $userNote2 = ArticleNote::where('user_id', $currentUser->id) + ->where('article_id', $selected2->id) + ->first(); + + if ($userNote2) { + $this->assertNull($userNote2->completed_at, 'Selected article should not have a completed note'); + } + } +} diff --git a/code/tests/Athenia/Integration/Repositories/Wiki/ArticleRepositoryTest.php b/code/tests/Athenia/Integration/Repositories/Wiki/ArticleRepositoryTest.php index c9986e22..5d9da01c 100644 --- a/code/tests/Athenia/Integration/Repositories/Wiki/ArticleRepositoryTest.php +++ b/code/tests/Athenia/Integration/Repositories/Wiki/ArticleRepositoryTest.php @@ -3,6 +3,7 @@ namespace Tests\Athenia\Integration\Repositories\Wiki; +use App\Athenia\Contracts\Repositories\Statistic\StatisticRepositoryContract; use App\Athenia\Exceptions\NotImplementedException; use App\Athenia\Repositories\Wiki\ArticleRepository; use App\Models\User\User; @@ -30,7 +31,11 @@ protected function setUp(): void parent::setUp(); $this->setupDatabase(); - $this->repository = new ArticleRepository(new Article(), $this->getGenericLogMock()); + $this->repository = new ArticleRepository( + new Article(), + $this->getGenericLogMock(), + app(StatisticRepositoryContract::class) + ); } public function testDeleteThrowsException(): void diff --git a/code/tests/Athenia/Integration/Repositories/Wiki/ArticleSummaryRepositoryTest.php b/code/tests/Athenia/Integration/Repositories/Wiki/ArticleSummaryRepositoryTest.php new file mode 100644 index 00000000..312eefc2 --- /dev/null +++ b/code/tests/Athenia/Integration/Repositories/Wiki/ArticleSummaryRepositoryTest.php @@ -0,0 +1,102 @@ +setupDatabase(); + + $this->repository = new ArticleSummaryRepository( + new ArticleSummary(), + $this->getGenericLogMock() + ); + } + + public function testFindAllSuccess(): void + { + ArticleSummary::factory()->count(5)->create(); + $items = $this->repository->findAll(); + $this->assertCount(5, $items); + } + + public function testFindAllEmpty(): void + { + $items = $this->repository->findAll(); + $this->assertEmpty($items); + } + + public function testFindOrFailSuccess(): void + { + $model = ArticleSummary::factory()->create(); + + $foundModel = $this->repository->findOrFail($model->id); + $this->assertEquals($model->id, $foundModel->id); + } + + public function testFindOrFailFails(): void + { + ArticleSummary::factory()->create(['id' => 2]); + + $this->expectException(ModelNotFoundException::class); + $this->repository->findOrFail(1); + } + + public function testCreateSuccess(): void + { + $article = Article::factory()->create(); + + /** @var ArticleSummary $summary */ + $summary = $this->repository->create([ + 'article_id' => $article->id, + 'content' => 'This is a test summary.', + ]); + + $this->assertEquals('This is a test summary.', $summary->content); + $this->assertEquals($article->id, $summary->article_id); + } + + public function testUpdateSuccess(): void + { + $model = ArticleSummary::factory()->create([ + 'content' => 'Original summary' + ]); + $this->repository->update($model, [ + 'content' => 'Updated summary', + ]); + + $updated = ArticleSummary::find($model->id); + $this->assertEquals('Updated summary', $updated->content); + } + + public function testDeleteSuccess(): void + { + $model = ArticleSummary::factory()->create(); + + $this->repository->delete($model); + + $this->assertNull(ArticleSummary::find($model->id)); + } +} diff --git a/code/tests/Athenia/Integration/Statistic/ArticleNoteStatisticsTest.php b/code/tests/Athenia/Integration/Statistic/ArticleNoteStatisticsTest.php new file mode 100644 index 00000000..8d3d5b74 --- /dev/null +++ b/code/tests/Athenia/Integration/Statistic/ArticleNoteStatisticsTest.php @@ -0,0 +1,245 @@ +setupDatabase(); + + // Seed article note statistics + $this->seed(\Database\Seeders\ArticleNoteStatisticsSeeder::class); + } + + public function testTotalNotesStatisticUpdatesOnCreate(): void + { + /** @var Article $article */ + $article = Article::factory()->create(); + + /** @var User $user */ + $user = User::factory()->create(); + + // Get the total_notes statistic + $statistic = Statistic::where('name', 'total_notes')->first(); + $this->assertNotNull($statistic); + + // Synchronize to ensure TargetStatistics exist for this article + $synchronizationService = app(StatisticSynchronizationServiceContract::class); + $synchronizationService->synchronizeTargetStatistics($article); + + // Create an ArticleNote + ArticleNote::factory()->create([ + 'user_id' => $user->id, + 'article_id' => $article->id, + ]); + + // Refresh the article and check for target statistics + $article->refresh(); + $targetStatistic = TargetStatistic::where('target_id', $article->id) + ->where('target_type', 'article') + ->where('statistic_id', $statistic->id) + ->first(); + + $this->assertNotNull($targetStatistic); + $this->assertEquals(1, $targetStatistic->result['total']); + } + + public function testTotalCompletedNotesStatisticUpdatesOnCreate(): void + { + /** @var Article $article */ + $article = Article::factory()->create(); + + /** @var User $user */ + $user = User::factory()->create(); + + // Get the total_completed_notes statistic + $statistic = Statistic::where('name', 'total_completed_notes')->first(); + $this->assertNotNull($statistic); + + // Synchronize to ensure TargetStatistics exist for this article + $synchronizationService = app(StatisticSynchronizationServiceContract::class); + $synchronizationService->synchronizeTargetStatistics($article); + + // Create a completed ArticleNote + ArticleNote::factory()->create([ + 'user_id' => $user->id, + 'article_id' => $article->id, + 'completed_at' => now(), + ]); + + // Refresh the article and check for target statistics + $article->refresh(); + $targetStatistic = TargetStatistic::where('target_id', $article->id) + ->where('target_type', 'article') + ->where('statistic_id', $statistic->id) + ->first(); + + $this->assertNotNull($targetStatistic); + $this->assertEquals(1, $targetStatistic->result['total']); + } + + public function testTotalNotesStatisticUpdatesOnDelete(): void + { + /** @var Article $article */ + $article = Article::factory()->create(); + + /** @var User $user1 */ + $user1 = User::factory()->create(); + + /** @var User $user2 */ + $user2 = User::factory()->create(); + + // Get the total_notes statistic + $statistic = Statistic::where('name', 'total_notes')->first(); + $this->assertNotNull($statistic); + + // Synchronize to ensure TargetStatistics exist for this article + $synchronizationService = app(StatisticSynchronizationServiceContract::class); + $synchronizationService->synchronizeTargetStatistics($article); + + // Create two ArticleNotes with different users + $note1 = ArticleNote::factory()->create([ + 'user_id' => $user1->id, + 'article_id' => $article->id, + ]); + + ArticleNote::factory()->create([ + 'user_id' => $user2->id, + 'article_id' => $article->id, + ]); + + // Verify we have 2 notes + $targetStatistic = TargetStatistic::where('target_id', $article->id) + ->where('target_type', 'article') + ->where('statistic_id', $statistic->id) + ->first(); + + $this->assertNotNull($targetStatistic); + $this->assertEquals(2, $targetStatistic->result['total']); + + // Delete one note + $note1->delete(); + + // Verify we now have 1 note + $targetStatistic->refresh(); + $this->assertEquals(1, $targetStatistic->result['total']); + } + + public function testTotalCompletedNotesStatisticUpdatesOnUpdate(): void + { + /** @var Article $article */ + $article = Article::factory()->create(); + + /** @var User $user */ + $user = User::factory()->create(); + + // Get the total_completed_notes statistic + $statistic = Statistic::where('name', 'total_completed_notes')->first(); + $this->assertNotNull($statistic); + + // Synchronize to ensure TargetStatistics exist for this article + $synchronizationService = app(StatisticSynchronizationServiceContract::class); + $synchronizationService->synchronizeTargetStatistics($article); + + // Create an incomplete ArticleNote + $note = ArticleNote::factory()->create([ + 'user_id' => $user->id, + 'article_id' => $article->id, + 'completed_at' => null, + ]); + + // Verify we have 0 completed notes + $targetStatistic = TargetStatistic::where('target_id', $article->id) + ->where('target_type', 'article') + ->where('statistic_id', $statistic->id) + ->first(); + + $this->assertNotNull($targetStatistic); + $this->assertEquals(0, $targetStatistic->result['total']); + + // Mark note as completed + $note->update(['completed_at' => now()]); + + // Verify we now have 1 completed note + $targetStatistic->refresh(); + $this->assertEquals(1, $targetStatistic->result['total']); + } + + public function testMultipleNotesStatistics(): void + { + /** @var Article $article */ + $article = Article::factory()->create(); + + /** @var User $user1 */ + $user1 = User::factory()->create(); + + /** @var User $user2 */ + $user2 = User::factory()->create(); + + /** @var User $user3 */ + $user3 = User::factory()->create(); + + // Get both statistics + $totalNotesStatistic = Statistic::where('name', 'total_notes')->first(); + $totalCompletedNotesStatistic = Statistic::where('name', 'total_completed_notes')->first(); + + // Synchronize to ensure TargetStatistics exist for this article + $synchronizationService = app(StatisticSynchronizationServiceContract::class); + $synchronizationService->synchronizeTargetStatistics($article); + + // Create 3 notes: 2 completed, 1 incomplete (each with different user) + ArticleNote::factory()->create([ + 'user_id' => $user1->id, + 'article_id' => $article->id, + 'completed_at' => now(), + ]); + + ArticleNote::factory()->create([ + 'user_id' => $user2->id, + 'article_id' => $article->id, + 'completed_at' => now(), + ]); + + ArticleNote::factory()->create([ + 'user_id' => $user3->id, + 'article_id' => $article->id, + 'completed_at' => null, + ]); + + // Verify total notes = 3 + $totalNotesTargetStat = TargetStatistic::where('target_id', $article->id) + ->where('target_type', 'article') + ->where('statistic_id', $totalNotesStatistic->id) + ->first(); + + $this->assertNotNull($totalNotesTargetStat); + $this->assertEquals(3, $totalNotesTargetStat->result['total']); + + // Verify completed notes = 2 + $totalCompletedNotesTargetStat = TargetStatistic::where('target_id', $article->id) + ->where('target_type', 'article') + ->where('statistic_id', $totalCompletedNotesStatistic->id) + ->first(); + + $this->assertNotNull($totalCompletedNotesTargetStat); + $this->assertEquals(2, $totalCompletedNotesTargetStat->result['total']); + } +} diff --git a/code/tests/Athenia/Unit/Listeners/User/InvitationAcceptedListenerTest.php b/code/tests/Athenia/Unit/Listeners/User/InvitationAcceptedListenerTest.php new file mode 100644 index 00000000..6ee5c77f --- /dev/null +++ b/code/tests/Athenia/Unit/Listeners/User/InvitationAcceptedListenerTest.php @@ -0,0 +1,97 @@ +id = 1; + + $invitationToken = new InvitationToken(); + $invitationToken->id = 1; + $invitationToken->token = 'test-token'; + $invitationToken->role_id = Role::ARTICLE_EDITOR; + + $event = new InvitationAcceptedEvent($user, $invitationToken); + + // Mock the repository update call + $repository->shouldReceive('update')->once()->with($invitationToken, \Mockery::on(function ($data) { + $this->assertArrayHasKey('used_at', $data); + $this->assertInstanceOf(\Illuminate\Support\Carbon::class, $data['used_at']); + return true; + })); + + // Mock the roles relationship + $rolesRelation = mock(BelongsToMany::class); + $rolesRelation->shouldReceive('attach')->once()->with(Role::ARTICLE_EDITOR); + $user->setRelation('roles', $rolesRelation); + + // We need to mock the roles() method + $user = \Mockery::mock($user)->makePartial(); + $user->shouldReceive('roles')->once()->andReturn($rolesRelation); + + // Update the event with the mocked user + $event = new InvitationAcceptedEvent($user, $invitationToken); + + $listener->handle($event); + } + + public function testHandleWithoutRole(): void + { + /** @var InvitationTokenRepositoryContract|CustomMockInterface $repository */ + $repository = mock(InvitationTokenRepositoryContract::class); + + $listener = new InvitationAcceptedListener($repository); + + $user = new User(); + $user->id = 1; + + $invitationToken = new InvitationToken(); + $invitationToken->id = 1; + $invitationToken->token = 'test-token'; + $invitationToken->role_id = null; + + $event = new InvitationAcceptedEvent($user, $invitationToken); + + // Mock the repository update call + $repository->shouldReceive('update')->once()->with($invitationToken, \Mockery::on(function ($data) { + $this->assertArrayHasKey('used_at', $data); + $this->assertInstanceOf(\Illuminate\Support\Carbon::class, $data['used_at']); + return true; + })); + + // Mock the roles relationship - it should NOT be called + $rolesRelation = mock(BelongsToMany::class); + $rolesRelation->shouldReceive('attach')->never(); + + $user = \Mockery::mock($user)->makePartial(); + $user->shouldReceive('roles')->never(); + + // Update the event with the mocked user + $event = new InvitationAcceptedEvent($user, $invitationToken); + + $listener->handle($event); + } +} diff --git a/code/tests/Athenia/Unit/Models/User/ArticleNoteTest.php b/code/tests/Athenia/Unit/Models/User/ArticleNoteTest.php new file mode 100644 index 00000000..7b8e1be6 --- /dev/null +++ b/code/tests/Athenia/Unit/Models/User/ArticleNoteTest.php @@ -0,0 +1,32 @@ +user(); + + $this->assertEquals('article_notes.user_id', $relation->getQualifiedForeignKeyName()); + $this->assertEquals('users.id', $relation->getQualifiedOwnerKeyName()); + } + + public function testArticle(): void + { + $articleNote = new ArticleNote(); + $relation = $articleNote->article(); + + $this->assertEquals('article_notes.article_id', $relation->getQualifiedForeignKeyName()); + $this->assertEquals('articles.id', $relation->getQualifiedOwnerKeyName()); + } +} diff --git a/code/tests/Athenia/Unit/Models/User/InvitationTokenTest.php b/code/tests/Athenia/Unit/Models/User/InvitationTokenTest.php new file mode 100644 index 00000000..62acb395 --- /dev/null +++ b/code/tests/Athenia/Unit/Models/User/InvitationTokenTest.php @@ -0,0 +1,43 @@ +role(); + + $this->assertInstanceOf(BelongsTo::class, $relation); + + $this->assertEquals('invitation_tokens.role_id', $relation->getQualifiedForeignKeyName()); + $this->assertEquals('roles.id', $relation->getQualifiedOwnerKeyName()); + } + + public function testIsUsedReturnsTrueWhenUsedAtIsSet(): void + { + $model = new InvitationToken(); + $model->used_at = now(); + + $this->assertTrue($model->isUsed()); + } + + public function testIsUsedReturnsFalseWhenUsedAtIsNull(): void + { + $model = new InvitationToken(); + $model->used_at = null; + + $this->assertFalse($model->isUsed()); + } +} diff --git a/code/tests/Athenia/Unit/Models/Wiki/ArticleSummaryTest.php b/code/tests/Athenia/Unit/Models/Wiki/ArticleSummaryTest.php new file mode 100644 index 00000000..e4dc2317 --- /dev/null +++ b/code/tests/Athenia/Unit/Models/Wiki/ArticleSummaryTest.php @@ -0,0 +1,25 @@ + 324, + ]); + $relation = $summary->article(); + $this->assertEquals('article_id', $relation->getForeignKeyName()); + $this->assertInstanceOf(Article::class, $relation->getRelated()); + } +} diff --git a/code/tests/Athenia/Unit/Validators/InvitationTokenIsValidValidatorTest.php b/code/tests/Athenia/Unit/Validators/InvitationTokenIsValidValidatorTest.php new file mode 100644 index 00000000..5957bad8 --- /dev/null +++ b/code/tests/Athenia/Unit/Validators/InvitationTokenIsValidValidatorTest.php @@ -0,0 +1,90 @@ +token = 'valid-token'; + $invitationToken->used_at = null; + + $repository->shouldReceive('findByToken') + ->once() + ->with('valid-token') + ->andReturn($invitationToken); + + $validator = new InvitationTokenIsValidValidator($repository); + + $this->assertTrue($validator->validate('invitation_token', 'valid-token')); + } + + public function testValidateReturnsFalseWhenTokenNotFound(): void + { + /** @var InvitationTokenRepositoryContract|CustomMockInterface $repository */ + $repository = mock(InvitationTokenRepositoryContract::class); + + $repository->shouldReceive('findByToken') + ->once() + ->with('invalid-token') + ->andReturn(null); + + $validator = new InvitationTokenIsValidValidator($repository); + + $this->assertFalse($validator->validate('invitation_token', 'invalid-token')); + } + + public function testValidateReturnsFalseWhenTokenAlreadyUsed(): void + { + /** @var InvitationTokenRepositoryContract|CustomMockInterface $repository */ + $repository = mock(InvitationTokenRepositoryContract::class); + + $invitationToken = new InvitationToken(); + $invitationToken->token = 'used-token'; + $invitationToken->used_at = now(); + + $repository->shouldReceive('findByToken') + ->once() + ->with('used-token') + ->andReturn($invitationToken); + + $validator = new InvitationTokenIsValidValidator($repository); + + $this->assertFalse($validator->validate('invitation_token', 'used-token')); + } + + public function testValidateReturnsFalseWhenValueIsNotString(): void + { + /** @var InvitationTokenRepositoryContract|CustomMockInterface $repository */ + $repository = mock(InvitationTokenRepositoryContract::class); + + $validator = new InvitationTokenIsValidValidator($repository); + + $this->assertFalse($validator->validate('invitation_token', 123)); + } + + public function testValidateReturnsFalseWhenValueIsArray(): void + { + /** @var InvitationTokenRepositoryContract|CustomMockInterface $repository */ + $repository = mock(InvitationTokenRepositoryContract::class); + + $validator = new InvitationTokenIsValidValidator($repository); + + $this->assertFalse($validator->validate('invitation_token', ['invalid'])); + } +} diff --git a/code/tests/Traits/MocksConsoleOutput.php b/code/tests/Traits/MocksConsoleOutput.php index dceb2b98..f5390a88 100644 --- a/code/tests/Traits/MocksConsoleOutput.php +++ b/code/tests/Traits/MocksConsoleOutput.php @@ -4,6 +4,7 @@ namespace Tests\Traits; use Illuminate\Console\Command; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Style\OutputStyle; use Symfony\Component\Console\Style\SymfonyStyle; @@ -27,7 +28,11 @@ protected function mockConsoleOutput(Command $command) $progressMock = mock(ProgressBar::class); $progressMock->shouldIgnoreMissing(); + $formatterMock = mock(OutputFormatterInterface::class); + $formatterMock->shouldIgnoreMissing(); + $mockOutput->shouldIgnoreMissing($progressMock); + $mockOutput->shouldReceive('getFormatter')->andReturn($formatterMock); $output->setValue($command, $mockOutput); } diff --git a/pull.sh b/pull.sh new file mode 100755 index 00000000..f3277605 --- /dev/null +++ b/pull.sh @@ -0,0 +1,342 @@ +#!/bin/bash + +set -e + +# Configuration - Edit these arrays to customize what to skip +SKIP_EXTENSIONS=( + "jpg" + "jpeg" + "png" + "gif" + "bmp" + "tiff" + "webp" + "ico" + "svg" + "pdf" + "zip" + "tar" + "gz" + "7z" + "rar" + "mp3" + "mp4" + "avi" + "mov" + "wmv" + "flv" + "wav" + "ogg" + "sh" + "md" +) + +SKIP_PATHS=( + "node_modules/" + ".git/" + "vendor/" + "storage/" + "bootstrap/cache/" + "public/storage/" + ".env" + ".env.*" + "composer.lock" + "package-lock.json" + "yarn.lock" + "*.log" + "*.tmp" + "*.cache" + ".DS_Store" + "Thumbs.db" + "code/tests/Feature/" + "code/tests/Integration/" + "code/tests/Unit/" +) + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +print_usage() { + echo "Usage: $0 [options]" + echo "" + echo "Options:" + echo " -h, --help Show this help message" + echo " -n, --dry-run Show what would be copied without actually copying" + echo " -f, --force Copy files without prompting for confirmation" + echo " -v, --verbose Verbose output" + echo " --skip-ext EXT Skip files with extension EXT (can be used multiple times)" + echo " --skip-path PATH Skip files matching PATH pattern (can be used multiple times)" + echo "" + echo "Examples:" + echo " $0 /path/to/remote/project" + echo " $0 /path/to/remote/project --dry-run" + echo " $0 /path/to/remote/project --skip-ext txt --skip-path 'temp/'" +} + +should_skip_file() { + local file="$1" + local basename=$(basename "$file") + local extension="${basename##*.}" + + # Skip if extension is in SKIP_EXTENSIONS + for skip_ext in "${SKIP_EXTENSIONS[@]}"; do + if [[ "$extension" == "$skip_ext" ]]; then + return 0 + fi + done + + # Special case: Skip code/app/ but NOT code/app/Athenia/ + if [[ "$file" == code/app/* ]] && [[ "$file" != code/app/Athenia/* ]]; then + return 0 + fi + + # Skip if path matches any pattern in SKIP_PATHS + for skip_path in "${SKIP_PATHS[@]}"; do + if [[ "$file" == *"$skip_path"* ]]; then + return 0 + fi + done + + return 1 +} + +compare_files() { + local main_file="$1" + local remote_file="$2" + + # If remote file doesn't exist, we can't pull it + if [ ! -f "$remote_file" ]; then + return 1 + fi + + # If main file doesn't exist, it's definitely different + if [ ! -f "$main_file" ]; then + return 0 + fi + + # Compare files byte-for-byte + if ! diff -q "$main_file" "$remote_file" > /dev/null 2>&1; then + return 0 + fi + + return 1 +} + +# Parse command line arguments +REMOTE_PATH="" +DRY_RUN=false +FORCE=false +VERBOSE=false + +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + print_usage + exit 0 + ;; + -n|--dry-run) + DRY_RUN=true + shift + ;; + -f|--force) + FORCE=true + shift + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + --skip-ext) + SKIP_EXTENSIONS+=("$2") + shift 2 + ;; + --skip-path) + SKIP_PATHS+=("$2") + shift 2 + ;; + -*) + echo "Unknown option: $1" + print_usage + exit 1 + ;; + *) + if [ -z "$REMOTE_PATH" ]; then + REMOTE_PATH="$1" + else + echo "Too many arguments" + print_usage + exit 1 + fi + shift + ;; + esac +done + +if [ -z "$REMOTE_PATH" ]; then + echo "Error: Remote project path is required" + print_usage + exit 1 +fi + +if [ ! -d "$REMOTE_PATH" ]; then + echo "Error: Remote project path does not exist: $REMOTE_PATH" + exit 1 +fi + +# Get absolute paths +REMOTE_PATH=$(cd "$REMOTE_PATH" && pwd) +MAIN_PATH=$(pwd) + +echo -e "${BLUE}Pulling changes from: ${NC}$REMOTE_PATH" +echo -e "${BLUE}To main project: ${NC}$MAIN_PATH" +echo "" + +# Get all tracked files in the main project +TRACKED_FILES=() +while IFS= read -r line; do + TRACKED_FILES+=("$line") +done < <(git ls-files) + +# Arrays to store results +DIFFERENT_FILES=() +SKIPPED_FILES=() +MISSING_FILES=() + +echo -e "${BLUE}Analyzing files...${NC}" + +# Check each tracked file +for file in "${TRACKED_FILES[@]}"; do + if should_skip_file "$file"; then + SKIPPED_FILES+=("$file") + [ "$VERBOSE" = true ] && echo -e "${YELLOW}SKIP: ${NC}$file" + continue + fi + + main_file="$MAIN_PATH/$file" + remote_file="$REMOTE_PATH/$file" + + if [ ! -f "$remote_file" ]; then + MISSING_FILES+=("$file") + [ "$VERBOSE" = true ] && echo -e "${RED}MISSING: ${NC}$file (not found in remote)" + continue + fi + + if compare_files "$main_file" "$remote_file"; then + DIFFERENT_FILES+=("$file") + [ "$VERBOSE" = true ] && echo -e "${GREEN}DIFF: ${NC}$file" + else + [ "$VERBOSE" = true ] && echo -e "SAME: $file" + fi +done + +echo "" +echo -e "${BLUE}Analysis complete:${NC}" +echo -e "${GREEN}Different files: ${NC}${#DIFFERENT_FILES[@]}" +echo -e "${YELLOW}Skipped files: ${NC}${#SKIPPED_FILES[@]}" +echo -e "${RED}Missing files: ${NC}${#MISSING_FILES[@]}" +echo "" + +if [ ${#DIFFERENT_FILES[@]} -eq 0 ]; then + echo -e "${GREEN}No differences found. All files are up to date.${NC}" + exit 0 +fi + +# Show what will be copied +echo -e "${BLUE}Files that are DIFFERENT and will be copied from remote:${NC}" +for file in "${DIFFERENT_FILES[@]}"; do + echo " $file" +done + +if [ ${#MISSING_FILES[@]} -gt 0 ]; then + echo "" + echo -e "${RED}Files missing in remote project (will NOT be copied):${NC}" + for file in "${MISSING_FILES[@]}"; do + echo " $file" + done +fi + +echo "" + +# Ask for confirmation unless forced or dry run +if [ "$DRY_RUN" = true ]; then + echo -e "${YELLOW}DRY RUN: Would copy ${#DIFFERENT_FILES[@]} different files${NC}" + exit 0 +fi + +if [ "$FORCE" = false ]; then + echo -n "Do you want to proceed with copying these ${#DIFFERENT_FILES[@]} DIFFERENT files? (y/N): " + read -r response + if [[ ! "$response" =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 + fi +fi + +# Create report file +TIMESTAMP=$(date +%Y%m%d_%H%M%S) +REPORT_FILE="$MAIN_PATH/pull_report_$TIMESTAMP.txt" + +echo "Pull Report - $(date)" > "$REPORT_FILE" +echo "Remote: $REMOTE_PATH" >> "$REPORT_FILE" +echo "Main: $MAIN_PATH" >> "$REPORT_FILE" +echo "" >> "$REPORT_FILE" + +# Copy files +echo -e "${BLUE}Copying files...${NC}" +COPIED_COUNT=0 +FAILED_COUNT=0 + +for file in "${DIFFERENT_FILES[@]}"; do + main_file="$MAIN_PATH/$file" + remote_file="$REMOTE_PATH/$file" + + # Create directory if it doesn't exist + mkdir -p "$(dirname "$main_file")" + + # Copy file + if cp "$remote_file" "$main_file"; then + echo -e "${GREEN}✓${NC} $file" + echo "COPIED: $file" >> "$REPORT_FILE" + ((COPIED_COUNT++)) + else + echo -e "${RED}✗${NC} $file" + echo "FAILED: $file" >> "$REPORT_FILE" + ((FAILED_COUNT++)) + fi +done + +# Add skipped and missing files to report +if [ ${#SKIPPED_FILES[@]} -gt 0 ]; then + echo "" >> "$REPORT_FILE" + echo "SKIPPED FILES:" >> "$REPORT_FILE" + for file in "${SKIPPED_FILES[@]}"; do + echo " $file" >> "$REPORT_FILE" + done +fi + +if [ ${#MISSING_FILES[@]} -gt 0 ]; then + echo "" >> "$REPORT_FILE" + echo "MISSING FILES:" >> "$REPORT_FILE" + for file in "${MISSING_FILES[@]}"; do + echo " $file" >> "$REPORT_FILE" + done +fi + +echo "" +echo -e "${BLUE}Summary:${NC}" +echo -e "${GREEN}Successfully copied: ${NC}$COPIED_COUNT files" +if [ $FAILED_COUNT -gt 0 ]; then + echo -e "${RED}Failed to copy: ${NC}$FAILED_COUNT files" +fi +echo -e "${BLUE}Report saved to: ${NC}$REPORT_FILE" + +# Git status +echo "" +echo -e "${BLUE}Git status:${NC}" +git status --porcelain + +exit 0 \ No newline at end of file diff --git a/update.sh b/update.sh index 4b92a3f3..7afe1e21 100755 --- a/update.sh +++ b/update.sh @@ -98,8 +98,8 @@ for FILE in "${DELETED[@]}"; do done # 6. Compare diffs and report -# Get the latest tag for the 'to' version -TO_TAG=$(git describe --tags --abbrev=0) +# Get the latest tag for the 'to' version (from entire repository, not just current branch) +TO_TAG=$(git tag --list --sort=-version:refname | head -n 1) REPORT_NAME="update_report_${LAST_TAG}_to_${TO_TAG}.txt" REPORT="$CHILD_PATH/$REPORT_NAME" > "$REPORT"