Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
9dc552e
finished pull script
bryce13950 Jul 10, 2025
16d8816
Merge pull request #64 from ProjectAthenia/pull-script
bryce13950 Jul 10, 2025
357cb63
fixed tag check
bryce13950 Jul 10, 2025
55b32bc
opened search capabilities a bit
bryce13950 Jul 10, 2025
d05aa9d
Merge pull request #65 from ProjectAthenia/vg-sync
bryce13950 Jul 10, 2025
c780a16
revised mailer to now require a user to be passed in
bryce13950 Aug 5, 2025
18e82fc
fixed status check
bryce13950 Aug 6, 2025
2dd597e
fixed article policy
bryce13950 Aug 6, 2025
ccfabc6
Merge pull request #67 from ProjectAthenia/feature-message-flexibility
bryce13950 Aug 6, 2025
75f9875
moved models
bryce13950 Aug 11, 2025
06f3487
refacotred more thigns
bryce13950 Nov 22, 2025
d11dfef
fixed db
bryce13950 Nov 22, 2025
5cbe9dd
claned up tests
bryce13950 Nov 22, 2025
b2a44f8
added ignore
bryce13950 Nov 22, 2025
f7a3e6c
ran composer lock
bryce13950 Nov 22, 2025
efc4ac3
added category article link
bryce13950 Nov 22, 2025
188b341
Merge pull request #68 from ProjectAthenia/refactor-model-location
bryce13950 Nov 22, 2025
c21bc34
Merge pull request #69 from ProjectAthenia/article-category-link
bryce13950 Nov 22, 2025
5a7633a
set up article note data
bryce13950 Nov 23, 2025
2452772
setup statistics for notes
bryce13950 Nov 23, 2025
c82d0af
added endpoint for randomized artile selection
bryce13950 Nov 23, 2025
736dce2
generated model headers
bryce13950 Nov 23, 2025
04dab01
generated model doc string again
bryce13950 Nov 23, 2025
9360e2d
claned up query
bryce13950 Nov 23, 2025
c731e8b
setup article summary model
bryce13950 Nov 23, 2025
e65bae0
set up article summary endpoint
bryce13950 Nov 23, 2025
52acfbb
Merge pull request #70 from ProjectAthenia/feature-article-note
bryce13950 Nov 23, 2025
e3e3d5d
Merge pull request #71 from ProjectAthenia/feature-article-summary
bryce13950 Nov 23, 2025
d200b57
setup invitation token
bryce13950 Nov 24, 2025
49dee2a
fixed test
bryce13950 Nov 24, 2025
09993d4
Merge pull request #72 from ProjectAthenia/invitiation-token
bryce13950 Nov 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ npm-debug.log
vagrant-credentials.rb
ubuntu-bionic-18.04-cloudimg-console.log
.phpunit.result.cache
code/.laravel
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);

namespace App\Athenia\Contracts\Repositories\User;

use App\Athenia\Contracts\Repositories\BaseRepositoryContract;

/**
* Interface ArticleNoteRepositoryContract
* @package App\Athenia\Contracts\Repositories\User
*/
interface ArticleNoteRepositoryContract extends BaseRepositoryContract
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);

namespace App\Athenia\Contracts\Repositories\User;

use App\Athenia\Contracts\Repositories\BaseRepositoryContract;
use App\Athenia\Models\User\InvitationToken;

/**
* Interface InvitationTokenRepositoryContract
* @package App\Contracts\Repositories\User
*/
interface InvitationTokenRepositoryContract extends BaseRepositoryContract
{
/**
* Generates a unique token, or throws an exception if it cannot do so.
*
* @throws \OverflowException
* @return string
*/
public function generateUniqueToken(): string;

/**
* Finds an invitation token by its token string
*
* @param string $token
* @return InvitationToken|null
*/
public function findByToken(string $token): ?InvitationToken;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,20 @@
namespace App\Athenia\Contracts\Repositories\Wiki;

use App\Athenia\Contracts\Repositories\BaseRepositoryContract;
use App\Models\User\User;
use App\Models\Wiki\Article;

/**
* Interface ArticleRepositoryContract
* @package App\Contracts\Repositories\Wiki
*/
interface ArticleRepositoryContract extends BaseRepositoryContract
{}
{
/**
* Selects an article for a user based on their note completion status and article statistics
*
* @param User $user
* @return Article|null
*/
public function selectArticleForUser(User $user): ?Article;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);

namespace App\Athenia\Contracts\Repositories\Wiki;

use App\Athenia\Contracts\Repositories\BaseRepositoryContract;

/**
* Interface ArticleSummaryRepositoryContract
* @package App\Athenia\Contracts\Repositories\Wiki
*/
interface ArticleSummaryRepositoryContract extends BaseRepositoryContract
{}
51 changes: 51 additions & 0 deletions code/app/Athenia/Events/User/InvitationAcceptedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);

namespace App\Athenia\Events\User;

use App\Athenia\Models\User\InvitationToken;
use App\Models\User\User;

/**
* Class InvitationAcceptedEvent
* @package App\Events\User
*/
class InvitationAcceptedEvent
{
/**
* @var User
*/
private User $user;

/**
* @var InvitationToken
*/
private InvitationToken $invitationToken;

/**
* InvitationAcceptedEvent constructor.
* @param User $user
* @param InvitationToken $invitationToken
*/
public function __construct(User $user, InvitationToken $invitationToken)
{
$this->user = $user;
$this->invitationToken = $invitationToken;
}

/**
* @return User
*/
public function getUser(): User
{
return $this->user;
}

/**
* @return InvitationToken
*/
public function getInvitationToken(): InvitationToken
{
return $this->invitationToken;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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']),
];
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<?php
declare(strict_types=1);

namespace App\Athenia\Http\Core\Controllers\User;

use App\Athenia\Contracts\Repositories\User\ArticleNoteRepositoryContract;
use App\Athenia\Contracts\Repositories\Wiki\ArticleRepositoryContract;
use App\Athenia\Http\Core\Controllers\BaseControllerAbstract;
use App\Athenia\Http\Core\Controllers\Traits\HasIndexRequests;
use App\Athenia\Models\BaseModelAbstract;
use App\Http\Core\Requests;
use App\Models\User\ArticleNote;
use App\Models\User\User;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;

/**
* Class ArticleNoteControllerAbstract
* @package App\Athenia\Http\Core\Controllers\User
*/
abstract class ArticleNoteControllerAbstract extends BaseControllerAbstract
{
use HasIndexRequests;

/**
* @var ArticleNoteRepositoryContract
*/
private ArticleNoteRepositoryContract $repository;

/**
* @var ArticleRepositoryContract
*/
private ArticleRepositoryContract $articleRepository;

/**
* ArticleNoteController constructor.
* @param ArticleNoteRepositoryContract $repository
* @param ArticleRepositoryContract $articleRepository
*/
public function __construct(ArticleNoteRepositoryContract $repository, ArticleRepositoryContract $articleRepository)
{
$this->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);
}
}
Loading