Initial Laravel API import
Some checks failed
continuous-integration/drone/push Build is failing

- Complete GGZ Ecademy Laravel backend application
- RESTful API for learning products, members, filters
- Authentication and authorization system
- Database migrations and seeders
- Custom CRUD generator commands
- Email notification system
- Integration with frontend applications
This commit is contained in:
Joris Slagter
2025-12-02 17:40:21 +01:00
parent 786b6b6a78
commit df155bb13d
341 changed files with 116385 additions and 2 deletions

View File

@@ -0,0 +1,82 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Arr;
use App\Services\AccreditationService;
use App\Repositories\FilterItemsAssociation;
use App\Http\Requests\Learning\AccreditationStore;
class AccreditationController extends Controller
{
private $accreditationService;
public function __construct(AccreditationService $accreditationService)
{
$this->accreditationService = $accreditationService;
$this->middleware('auth:sanctum');
}
public function index()
{
$accreditations = $this->accreditationService->getAll();
return response()->json($accreditations, 201);
}
public function store(AccreditationStore $request)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
if ($request['filter_items']) {
$filter_items = Arr::collapse($request['filter_items']);
$filter_items = Arr::flatten($filter_items);
}
$data = Arr::except($request->validated(), ['filter_items']);
$accreditation = $this->accreditationService->save($data);
if (isset($filter_items) && $filter_items) {
$accreditation->filters()->delete();
foreach ($filter_items as $filter_item_id) {
$filter_association = new FilterItemsAssociation();
$filter_association->filter_item_id = $filter_item_id;
$accreditation->filters()->save($filter_association);
}
}
// return $request;
return response()->json($accreditation->load('filters'), 201);
}
public function show($id)
{
$accreditation = $this->accreditationService->get($id);
return response()->json($accreditation);
}
public function destroy($id)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->accreditationService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace App\Http\Controllers;
use App\Services\MemberService;
use App\Services\AddressService;
use App\Http\Requests\Member\AddressStore;
class AddressController extends Controller
{
private $addressService;
private $memberService;
public function __construct(
MemberService $memberService,
AddressService $addressService
) {
$this->memberService = $memberService;
$this->addressService = $addressService;
$this->middleware('auth:sanctum');
}
public function index()
{
$addresses = $this->addressService->getAll();
return response()->json($addresses, 201);
}
public function store(AddressStore $request)
{
$member = $this->memberService->get($request->member_id);
if (!$member) {
return response()->json(['message' => 'Member not found.'], 404);
}
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isSuperAdminOrAdmin = $isSuperAdmin || $isAdmin;
// $isUserDelegated = $member->user_id === auth()->user()->id;
if (!$isSuperAdmin && !$isAdmin) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$request_data = $request->validated();
// if is an user delegated to work with that member, remove approved_by and approved_at
$request_data['revisor_id'] = $isSuperAdminOrAdmin ? auth()->user()->id : null;
$request_data['approved_at'] = $isSuperAdminOrAdmin ? now() : null;
$address = $this->addressService->save($request_data);
return response()->json($address, 201);
}
public function show($id)
{
$address = $this->addressService->get($id);
return response()->json($address);
}
public function destroy($id)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isSuperAdminOrAdmin = $isSuperAdmin || $isAdmin;
if (!$isSuperAdminOrAdmin) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->addressService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,199 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Response\ApiResponse;
use Illuminate\Http\Request;
use App\Services\UserService;
use App\Services\MemberService;
use Illuminate\Support\Carbon;
use App\Http\Requests\User\Login;
use Illuminate\Support\Facades\Hash;
use App\Http\Resources\UserLoggedResource;
use App\Repositories\Role;
use Redirect;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Support\Facades\Mail;
use Laravel\Sanctum\PersonalAccessToken;
use Illuminate\Support\Facades\Log;
class AuthController extends Controller
{
private $userService;
private $memberService;
public function __construct(
UserService $userService,
MemberService $memberService
) {
$this->userService = $userService;
$this->memberService = $memberService;
$this->middleware('auth:sanctum', [
'except' => [
'get_sso_data',
'login',
'signup',
'sso',
'status',
],
]);
}
public function login(Login $request)
{
if (empty($request->token)) {
$user = $this->userService->getByEmailWith($request->email, ['roles']);
if (!$user || !Hash::check($request->password, $user->password)) {
return response(['error' => ['The provided credentials are incorrect.']]);
}
} else {
$client = new \GuzzleHttp\Client(['base_uri' => static::determineIdpUri()]);
try {
$response = $client->request('POST', 'protocol/openid-connect/token', [
'form_params' => [
'code' => $request->token,
'client_id' => config('sso.client_id'),
'client_secret' => config('sso.secret'),
'redirect_uri' => config('sso.redirect_uri'),
'scope' => 'openid profile',
'grant_type' => 'authorization_code'
]
]);
$token = json_decode($response->getBody(), true)['id_token'];
$decodedToken = \Firebase\JWT\JWT::decode($token, $this->getJwks(), false);
} catch (ClientException $e) {
$html = 'Login faild with token:' . $request->token;
Mail::send([], [], function ($message) use ($html) {
$message
->to('joris@ggzecademy.nl')
->subject('Login failed via SSO')
->setBody($html, 'text/html');
});
}
if (empty($decodedToken->kg) || !stristr($decodedToken->kg, 'KG')) {
return response(['error' => ['The provided credentials are incorrect.']]);
}
$organization_id = str_replace('KG_', '', $decodedToken->kg);
$member = $this->memberService->get($organization_id);
$user = $this->userService->getByEmailWith($decodedToken->email, ['roles']);
if (!$user) {
$new_user = [
'first_name' => $decodedToken->given_name,
'last_name' => $decodedToken->family_name,
'email' => $decodedToken->email,
'password' => 'aT@5*Wb*W7gseVhC',
];
$user = $this->userService->save($new_user, false);
$user->roles()->attach(Role::whereName('user')->firstOrFail());
}
$member->users()->syncWithoutDetaching([$user->id]);
$member->save();
}
$user->last_login_at = $user->logged_at;
$user->last_login_ip = $request->getClientIp();
$user->logged_at = Carbon::now();
$user->save();
$token = $user->createToken('cms-token')->plainTextToken;
$response = ['token' => $token];
return response($response, 201);
}
private function getJwks(): array
{
$jwkData = json_decode(
file_get_contents(static::determineIdpUri() . 'protocol/openid-connect/certs'),
true
);
return \Firebase\JWT\JWK::parseKeySet($jwkData);
}
public function logout(Request $request)
{
$request->user()->tokens()->delete();
return response()->json('Logged-out', 201);
}
public function status(Request $request)
{
$bearerToken = $request->bearerToken();
if (is_null($bearerToken)) {
return ApiResponse::error(
new Error\AuthController\MissingBearerTokenError(),
);
} else {
$accessToken = PersonalAccessToken::findToken($bearerToken);
return ApiResponse::success([
'authenticated' => $accessToken instanceof PersonalAccessToken,
'user' => $accessToken ? $accessToken->tokenable->email : null,
]);
}
}
public function me(Request $request)
{
return response()->json(new UserLoggedResource($request->user()));
}
public function sso()
{
return Redirect::to(static::buildSsoEndpointUri());
}
private static function buildSsoEndpointUri(): string
{
$endpointQuery = http_build_query([
'client_id' => config('sso.client_id'),
'redirect_uri' => config('sso.redirect_uri'),
'scope' => 'openid profile',
'response_type' => 'code',
'nonce' => md5(rand()), // TODO: proper nonce handling
], '', '&', PHP_QUERY_RFC3986);
$endpointQuery = str_replace('%2B', '+', $endpointQuery);
return sprintf('%s?%s', static::determineIdpUri('protocol/openid-connect/auth'), $endpointQuery);
}
private static function determineIdpUri(string $path = ''): string
{
$baseUri = rtrim(config('sso.idp_base_uri'), '/');
return sprintf('%s/%s', $baseUri, $path);
}
public function get_sso_data() {
$client = new \GuzzleHttp\Client([
'base_uri' => static::determineIdpUri(),
'headers' => [
'Authorization' => sprintf('Bearer %s', $_GET['token']),
'Accept' => 'application/json',
],
]);
$response = $client->request('GET', 'oidc/userinfo.php');
return response($response->getBody()->getContents(), 201);
}
}

View File

@@ -0,0 +1,213 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Response\ApiResponse;
use Illuminate\Http\Request;
use App\Services\UserService;
use App\Services\MemberService;
use Illuminate\Support\Carbon;
use App\Http\Requests\User\Login;
use Illuminate\Support\Facades\Hash;
use App\Http\Resources\UserLoggedResource;
use App\Repositories\Role;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Support\Facades\Mail;
use Laravel\Sanctum\PersonalAccessToken;
class AuthController extends Controller
{
private $userService;
private $memberService;
public function __construct(
UserService $userService,
MemberService $memberService
) {
$this->userService = $userService;
$this->memberService = $memberService;
$this->middleware('auth:sanctum', [
'except' => [
'get_sso_data',
'login',
'signup',
'sso',
'status',
],
]);
}
public function login(Login $request)
{
if (empty($request->token)) {
$user = $this->userService->getByEmailWith($request->email, ['roles']);
if (!$user || !Hash::check($request->password, $user->password)) {
return response(['error' => ['The provided credentials are incorrect.']]);
}
} else {
$client = new \GuzzleHttp\Client(['base_uri' => static::determineIdpUri()]);
$headers = [
'Authorization' => 'Bearer ' . $request->token,
'Accept' => 'application/json',
];
try {
$response = $client->request('GET', 'oidc/userinfo.php', [
'headers' => $headers
]);
} catch (ClientException $e) {
$html = 'Login faild with token:' . $request->token;
Mail::send([], [], function ($message) use ($html) {
$message
->to('jasper@3110.nl')
->subject('Login failed via SSO')
->setBody($html, 'text/html');
});
}
$data = $response->getBody()->getContents();
$data = json_decode($data);
if (empty($data->profile) || !stristr($data->profile, 'KG')) {
return response(['error' => ['The provided credentials are incorrect.']]);
}
$profile_arr = explode(',', $data->profile);
$organization_id = str_replace('KG_', '', $profile_arr[0]);
$member = $this->memberService->get($organization_id);
$user = $this->userService->getByEmailWith($data->sub, ['roles']);
if (!$user) {
$new_user = [
'first_name' => $data->given_name,
'last_name' => $data->family_name,
'email' => $data->sub,
'password' => 'aT@5*Wb*W7gseVhC',
];
$user = $this->userService->save($new_user, false);
$user->roles()->attach(Role::whereName('user')->firstOrFail());
}
$member->users()->syncWithoutDetaching([$user->id]);
$member->save();
}
$user->last_login_at = $user->logged_at;
$user->last_login_ip = $request->getClientIp();
$user->logged_at = Carbon::now();
$user->save();
$token = $user->createToken('cms-token')->plainTextToken;
$response = ['token' => $token];
return response($response, 201);
}
public function logout(Request $request)
{
$request->user()->tokens()->delete();
return response()->json('Logged-out', 201);
}
public function status(Request $request)
{
$bearerToken = $request->bearerToken();
if (is_null($bearerToken)) {
return ApiResponse::error(
new Error\AuthController\MissingBearerTokenError(),
);
} else {
$accessToken = PersonalAccessToken::findToken($bearerToken);
return ApiResponse::success([
'authenticated' => $accessToken instanceof PersonalAccessToken,
'user' => $accessToken ? $accessToken->tokenable->email : null,
]);
}
}
public function me(Request $request)
{
return response()->json(new UserLoggedResource($request->user()));
}
private static function dumpLocal(...$values): void
{
if (static::isLocal()) {
dd(...$values);
}
}
private static function isLocal(): bool
{
return $_SERVER['REMOTE_ADDR'] === '89.98.81.170';
}
public function sso()
{
$cookieFileHandle = tmpfile();
$cookieFilePath = stream_get_meta_data($cookieFileHandle)['uri'];
$ch = curl_init(static::buildSsoEndpointUri());
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIESESSION, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookieFilePath);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookieFilePath);
$result = curl_exec($ch);
curl_close($ch);
fclose($cookieFileHandle);
if (static::isLocal()) {
echo $result; exit;
}
echo '<div style="display: none;">' . $result . '</div>';
}
private static function buildSsoEndpointUri(): string
{
$endpointQuery = http_build_query([
'client_id' => config('sso.client_id'),
'redirect_uri' => config('sso.redirect_uri'),
'scope' => implode('+', config('sso.scopes')),
'response_type' => 'id_token+token',
'response_mode' => 'form_post',
'nonce' => md5(rand()), // TODO: proper nonce handling
], '', '&', PHP_QUERY_RFC3986);
$endpointQuery = str_replace('%2B', '+', $endpointQuery);
return sprintf('%s?%s', static::determineIdpUri('oidc/authorize.php'), $endpointQuery);
}
private static function determineIdpUri(string $path = ''): string
{
$baseUri = rtrim(config('sso.idp_base_uri'), '/');
return sprintf('%s/%s', $baseUri, $path);
}
public function get_sso_data() {
$client = new \GuzzleHttp\Client([
'base_uri' => static::determineIdpUri(),
'headers' => [
'Authorization' => sprintf('Bearer %s', $_GET['token']),
'Accept' => 'application/json',
],
]);
$response = $client->request('GET', 'oidc/userinfo.php');
return response($response->getBody()->getContents(), 201);
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace App\Http\Controllers;
use App\Services\BranchService;
use App\Http\Requests\Member\BranchStore;
class BranchController extends Controller
{
private $branchService;
public function __construct(BranchService $branchService)
{
$this->branchService = $branchService;
$this->middleware('auth:sanctum');
}
public function index()
{
$branches = $this->branchService->getAll();
return response()->json($branches, 201);
}
public function store(BranchStore $request)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$branch = $this->branchService->save($request->all());
return response()->json($branch, 201);
}
public function show($id)
{
$branch = $this->branchService->get($id);
return response()->json($branch);
}
public function destroy($id)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->branchService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ChecklistCategoryRequest;
use App\Services\ChecklistCategoryService;
class ChecklistCategoryController extends Controller
{
private $checklistCategoryService;
public function __construct(ChecklistCategoryService $checklistCategoryService)
{
$this->checklistCategoryService = $checklistCategoryService;
}
public function index()
{
$checklistCategories = $this->checklistCategoryService->with(['items']);
return response()->json($checklistCategories, 201);
}
public function store(ChecklistCategoryRequest $request)
{
$checklistcategory = $this->checklistCategoryService->save($request->all());
return response()->json($checklistcategory, 201);
}
public function show($id)
{
$checklistcategory = $this->checklistCategoryService->get($id);
return response()->json($checklistcategory);
}
public function destroy($id)
{
$this->checklistCategoryService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ChecklistRequest;
use App\Services\ChecklistService;
class ChecklistController extends Controller
{
private $checklistService;
public function __construct(ChecklistService $checklistService)
{
$this->checklistService = $checklistService;
}
public function index()
{
$checklists = $this->checklistService->getAll();
return response()->json($checklists, 201);
}
public function store(ChecklistRequest $request)
{
$checklist = $this->checklistService->save($request->all());
return response()->json($checklist, 201);
}
public function show($id)
{
$checklist = $this->checklistService->get($id);
return response()->json($checklist);
}
public function destroy($id)
{
$this->checklistService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ChecklistVersionRequest;
use App\Services\ChecklistVersionService;
class ChecklistVersionController extends Controller
{
private $checklistversionService;
public function __construct(ChecklistVersionService $checklistversionService)
{
$this->checklistversionService = $checklistversionService;
}
public function index()
{
$checklistversions = $this->checklistversionService->getAll();
return response()->json($checklistversions, 201);
}
public function store(ChecklistVersionRequest $request)
{
$checklistversion = $this->checklistversionService->save($request->all());
return response()->json($checklistversion, 201);
}
public function show($id)
{
$checklistversion = $this->checklistversionService->get($id);
return response()->json($checklistversion);
}
public function destroy($id)
{
$this->checklistversionService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace App\Http\Controllers;
use App\Services\MemberService;
use App\Services\ContactService;
use App\Http\Requests\Member\ContactStore;
class ContactController extends Controller
{
private $contactService;
private $memberService;
public function __construct(
MemberService $memberService,
ContactService $contactService
) {
$this->contactService = $contactService;
$this->memberService = $memberService;
$this->middleware('auth:sanctum');
}
public function index()
{
$contacts = $this->contactService->getAll();
return response()->json($contacts, 201);
}
public function store(ContactStore $request)
{
$member = $this->memberService->get($request->member_id);
if (!$member) {
return response()->json(['message' => 'Member not found.'], 404);
}
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isSuperAdminOrAdmin = $isSuperAdmin || $isAdmin;
$isUserDelegated = $member->user_id === auth()->user()->id;
if (
!$isSuperAdmin &&
!$isAdmin &&
!$isUserDelegated
) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$request_data = $request->validated();
// if is an user delegated to work with that member, remove approved_by and approved_at
$request_data['revisor_id'] = $isSuperAdminOrAdmin ? auth()->user()->id : null;
$request_data['approved_at'] = $isSuperAdminOrAdmin ? now() : null;
$contact = $this->contactService->save($request_data);
return response()->json($contact, 201);
}
public function show($id)
{
$contact = $this->contactService->get($id);
return response()->json($contact);
}
public function destroy($id)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isSuperAdminOrAdmin = $isSuperAdmin || $isAdmin;
if (!$isSuperAdminOrAdmin) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->contactService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace App\Http\Controllers;
use App\Services\MemberService;
use App\Services\UserService;
use App\Services\ContributionService;
use App\Http\Requests\Member\ContributionStore;
use Illuminate\Support\Facades\Mail;
use App\Mail\MemberChanges;
class ContributionController extends Controller
{
private $contributionService;
private $userService;
public function __construct(
MemberService $memberService,
UserService $userService,
ContributionService $contributionService
) {
$this->memberService = $memberService;
$this->userService = $userService;
$this->contributionService = $contributionService;
$this->middleware('auth:sanctum');
}
public function index()
{
$contributions = $this->contributionService->getAll();
return response()->json($contributions, 201);
}
public function store(ContributionStore $request)
{
$member = $this->memberService->get($request->member_id);
if (!$member) {
return response()->json(['message' => 'Member not found.'], 404);
}
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isSuperAdminOrAdmin = $isSuperAdmin || $isAdmin;
$isAppliedToAll = $request->has('toAll') ? true : false;
if (!$isSuperAdminOrAdmin) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$request_data = $request->validated();
$request_data['revisor_id'] = auth()->user()->id;
$request_data['approved_at'] = now();
$contribution = null;
if ($isAppliedToAll) {
$members = $this->memberService->getAll();
// Store for all existing members
foreach ($members as $member) {
$request_data['member_id'] = $member['id'];
if ($request_data['member_id'] === $member['user_id']) {
$contribution = $this->contributionService->save($request_data);
} else {
$this->contributionService->save($request_data);
}
}
} else {
$contribution = $this->contributionService->save($request_data);
}
return response()->json($contribution, 201);
}
public function show($id)
{
$contribution = $this->contributionService->get($id);
return response()->json($contribution);
}
public function destroy($id)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isSuperAdminOrAdmin = $isSuperAdmin || $isAdmin;
if (!$isSuperAdminOrAdmin) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->contributionService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

View File

@@ -0,0 +1,80 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Artisan;
use App\Services\CourseNotificationService;
use App\Http\Requests\Learning\CourseNotificationStore;
class CourseNotificationController extends Controller
{
private $courseNotificationService;
public function __construct(CourseNotificationService $courseNotificationService)
{
$this->courseNotificationService = $courseNotificationService;
$this->middleware('auth:sanctum');
}
public function index()
{
if (!auth()->user()->hasRole('admin')) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$course_notifications = $this->courseNotificationService->getAll();
return response()->json($course_notifications, 201);
}
public function store(CourseNotificationStore $request)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$course_notification = $this->courseNotificationService->save($request->validated());
return response()->json($course_notification, 201);
}
public function show($id)
{
$course_notification = $this->courseNotificationService->get($id);
return response()->json($course_notification);
}
public function destroy($id)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->courseNotificationService->delete($id);
return response()->json(null, 204);
}
public function testCommand()
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
Artisan::call('send:lp-notifications');
return 0;
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
class CsvExportController extends Controller
{
public function downloadCSV()
{
// Execute SQL query
$results = DB::select("
SELECT
l.id,
l.title,
l.lead_time,
l.short_description,
l.learning_goals,
l.review,
l.certification,
l.extra_information,
l.target_audience,
l.quality_standards,
l.contract_agreements,
(
SELECT CONCAT('https://mijnggzbackend.ggzecademy.nl/storage/', m.id, '/', m.file_name)
FROM media m
WHERE m.model_id = l.id AND m.collection_name = 'learning_products_covers'
LIMIT 1
) AS cover,
(
SELECT CONCAT('https://mijnggzbackend.ggzecademy.nl/storage/', m.id, '/', m.file_name)
FROM media m
WHERE m.model_id = l.id AND m.collection_name = 'learning_products_tiles'
LIMIT 1
) AS thumb,
(
SELECT GROUP_CONCAT(CONCAT(i.title, '-', ac.credits))
FROM filter_items_associations a
LEFT JOIN filter_items AS i ON i.id = a.filter_item_id
LEFT JOIN accreditations AS ac ON ac.id = a.filter_items_associations_id
WHERE ac.learning_product_id = l.id AND i.filter_id = 10
) AS titles_and_credits
FROM
learning_products l
WHERE
l.published = 1 AND l.deleted_at IS NULL
GROUP BY
l.title, l.id, l.lead_time, l.short_description, l.learning_goals, l.review,
l.certification, l.extra_information, l.target_audience, l.quality_standards, l.contract_agreements
");
// Format the results into CSV
$csvOutput = $this->formatToCsv($results);
// Send the CSV file to the client
$filename = "export.csv";
return response($csvOutput, 200)
->header('Content-Type', 'text/csv')
->header('Content-Disposition', "attachment; filename=$filename");
}
protected function formatToCsv($data)
{
$handle = fopen('php://temp', 'w');
// Add headers for CSV (if needed)
fputcsv($handle, [
'ID', 'Title', 'Lead Time', 'Short Description',
'Learning Goals', 'Review', 'Certification', 'Extra Information',
'Target Audience', 'Quality Standards', 'Contract Agreements',
'Cover', 'Thumb', 'Titles and Credits'
]);
foreach ($data as $row) {
fputcsv($handle, [
$row->id,
strip_tags($row->title),
strip_tags($row->lead_time),
strip_tags($row->short_description),
strip_tags($row->learning_goals),
strip_tags($row->review),
strip_tags($row->certification),
strip_tags($row->extra_information),
strip_tags($row->target_audience),
strip_tags($row->quality_standards),
strip_tags($row->contract_agreements),
$row->cover,
$row->thumb,
$row->titles_and_credits
]);
}
rewind($handle);
$contents = stream_get_contents($handle);
fclose($handle);
return $contents;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Http\Controllers\Error\AuthController;
use App\Http\Controllers\Error\ErrorInterface;
use Illuminate\Http\Response;
class MissingBearerTokenError implements ErrorInterface
{
public function getStatusCode(): int
{
return Response::HTTP_BAD_REQUEST;
}
public function getErrorCode(): string
{
return 'missing.bearer_token';
}
public function getErrorMessage(): string
{
return 'Missing bearer token';
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace App\Http\Controllers\Error;
interface ErrorInterface
{
public function getStatusCode(): int;
public function getErrorCode(): string;
public function getErrorMessage(): string;
}

View File

@@ -0,0 +1,77 @@
<?php
namespace App\Http\Controllers;
use App\Services\FilterService;
use App\Services\FilterItemService;
use App\Http\Resources\FiltersResource;
use App\Http\Requests\Filter\FilterStore;
use App\Http\Requests\Filter\FilterItemStore;
class FilterController extends Controller
{
private $filterService;
private $filterItemService;
public function __construct(FilterService $filterService, FilterItemService $filterItemService)
{
$this->filterService = $filterService;
$this->filterItemService = $filterItemService;
$this->middleware('auth:sanctum', [
'except' => [
'index', 'show'
]
]);
}
public function index()
{
$filters = $this->filterService->with(['items']);
return response()->json(FiltersResource::collection($filters), 201);
}
public function indexWithCount()
{
$filters = $this->filterService->withCount(['items']);
return response()->json(FiltersResource::collection($filters), 201);
}
public function store(FilterStore $request)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$filter = $this->filterService->save($request->all());
return response()->json($filter, 201);
}
public function show($id)
{
$filter = $this->filterService->getOneWith($id, ['items']);
return response()->json($filter);
}
public function destroy($id)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->filterService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Http\Controllers;
use App\Services\FilterItemService;
use App\Http\Resources\FiltersResource;
use App\Http\Requests\Filter\FilterItemStore;
use App\Services\FilterItemsAssociationService;
class FilterItemController extends Controller
{
private $filterItemService;
public function __construct(
FilterItemService $filterItemService,
FilterItemsAssociationService $filterItemsAssociationService
) {
$this->filterItemService = $filterItemService;
$this->filterItemsAssociationService = $filterItemsAssociationService;
$this->middleware('auth:sanctum');
}
public function index()
{
$filter_items = $this->filterItemService->getAll();
return response()->json($filter_items, 201);
}
public function store(FilterItemStore $request)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$filter_item = $this->filterItemService->save($request->all());
return response()->json(new FiltersResource($filter_item), 201);
}
public function show($id)
{
$filter_item = $this->filterItemService->get($id);
return response()->json($filter_item);
}
public function destroy($id)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->filterItemsAssociationService->deleteAllWithFilterItemId($id);
$this->filterItemService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\FilterItemsAssociationRequest;
use App\Services\FilterItemsAssociationService;
class FilterItemsAssociationController extends Controller
{
private $filterItemsAssociationService;
public function __construct(FilterItemsAssociationService $filterItemsAssociationService)
{
$this->filterItemsAssociationService = $filterItemsAssociationService;
}
public function index()
{
$filterItemsAssociations = $this->filterItemsAssociationService->getAll();
return response()->json($filterItemsAssociations, 201);
}
public function store(FilterItemsAssociationRequest $request)
{
$filterItemsAssociations = $this->filterItemsAssociationService->save($request->all());
return response()->json($filterItemsAssociations, 201);
}
public function show($id)
{
$filterItemsAssociations = $this->filterItemsAssociationService->get($id);
return response()->json($filterItemsAssociations);
}
public function destroy($id)
{
$this->filterItemsAssociationService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
protected function sendResetLinkResponse(Request $request, $response)
{
return response(['message' => $response]);
}
protected function sendResetLinkFailedResponse(Request $request, $response)
{
return response(['error' => $response], 422);
}
}

View File

@@ -0,0 +1,340 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Arr;
use App\Services\LearningProductService;
use App\Repositories\FilterItemsAssociation;
use App\Http\Resources\LearningProductResource;
use App\Http\Requests\Learning\LearningProductId;
use App\Http\Requests\Learning\LearningProductStore;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Log;
class LearningProductController extends Controller
{
private $learningProductService;
public function __construct(LearningProductService $learningProductService)
{
$this->learningProductService = $learningProductService;
$this->middleware('auth:sanctum', [
'except' => [
'getPublished',
]
]);
}
public function index()
{
$learningProducts = $this->learningProductService->withTrashedAndChildren([
'filters',
'filters.filter_item.filter',
// 'versions',
'accreditations',
'notifications',
])->sortDesc();
return response()->json(
LearningProductResource::collection($learningProducts),
201,
);;
}
public function getPublished()
{
$withColumnAll = [
'filters',
'filters.filter_item.filter',
'accreditations',
'accreditations.filters.filter_item',
'notifications'
];
$learningProductAll = $this->learningProductService->getPublishedWith($withColumnAll)
->groupBy('code')
->map(function (Collection $groupedLearningProductAll) {
return $groupedLearningProductAll->last();
})
->sortDesc()
->flatten(1);
return response()->json(
LearningProductResource::collection($learningProductAll),
201,
);
}
public function store(LearningProductStore $request)
{
try {
Log::info('Store method called');
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
Log::info('User roles checked', ['isSuperAdmin' => $isSuperAdmin, 'isAdmin' => $isAdmin, 'isOperator' => $isOperator]);
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
// Is it draft with parent_id marked as published?
$is_draft = isset($request['parent_id']);
$is_draft_published = $is_draft && $request['published'];
Log::info('Draft status checked', ['is_draft' => $is_draft, 'is_draft_published' => $is_draft_published]);
$hasCover = isset($request['cover']) && $request->hasFile('cover');
$hasTile = isset($request['tile']) && $request->hasFile('tile');
Log::info('File status checked', ['hasCover' => $hasCover, 'hasTile' => $hasTile]);
// Validate and prepare request data
$request_data = $request->validated();
if ($hasCover) {
$request_data = Arr::except($request_data, ['cover']);
}
if ($hasTile) {
$request_data = Arr::except($request_data, ['tile']);
}
// Publish without parent_id
if ($is_draft_published) {
$request_data['parent_id'] = null;
}
$request_data = Arr::except($request_data, ['filtersGrouped', 'synonymsSelected']);
// Ensure voor_opleiders has a default value
if (!isset($request_data['voor_opleiders']) || $request_data['voor_opleiders'] === null) {
$request_data['voor_opleiders'] = false;
}
Log::info('Request data prepared', ['request_data' => $request_data]);
// Save finally the product
$learning_product = $this->learningProductService->save($request_data);
Log::info('Learning product saved', ['learning_product' => $learning_product]);
// Get Filter Items passed
$filter_items = json_decode(html_entity_decode(stripslashes($request['filtersGrouped'])));
$synonyms_selected = json_decode(html_entity_decode(stripslashes($request['synonymsSelected'])));
Log::info('Filters and synonyms decoded', ['filter_items' => $filter_items, 'synonyms_selected' => $synonyms_selected]);
if ($synonyms_selected) {
$learning_product->synonyms()->sync($synonyms_selected);
}
$arrayTmp = [];
if ($filter_items) {
foreach ($filter_items as $key => $value) {
$arrayTmp[] = $value;
}
$filter_items = Arr::collapse($arrayTmp);
$filter_items = Arr::flatten($arrayTmp);
// Saves filter items associations with this learning product
$learning_product->filters()->delete();
foreach ($filter_items as $filter_item_id) {
$filter_association = new FilterItemsAssociation();
$filter_association->filter_item_id = $filter_item_id;
$learning_product->filters()->save($filter_association);
}
}
// if a file was uploaded as cover or tile, add them
if ($hasCover) {
$learning_product->addMediaFromRequest('cover')->toMediaCollection('learning_products_covers');
}
if ($hasTile) {
$learning_product->addMediaFromRequest('tile')->toMediaCollection('learning_products_tiles');
}
if ($is_draft) {
// 1st draft of the published product
$original_product_published = $this->learningProductService->get($request['parent_id']);
// If no cover has been attached before
if (!$hasCover && !$learning_product->cover) {
$image = $original_product_published->getMedia('learning_products_covers')->first();
if ($image) {
$learning_product->copyMedia($image->getPath())->toMediaCollection('learning_products_covers');
}
}
// If no tile has been attached before
if (!$hasTile && !$learning_product->tile) {
$image = $original_product_published->getMedia('learning_products_tiles')->first();
if ($image) {
$learning_product->copyMedia($image->getPath())->toMediaCollection('learning_products_tiles');
}
}
}
// ForceDelete the parent if draft published
if ($is_draft_published && $learning_product) {
$this->destroy($request['parent_id'], true);
}
Log::info('Learning product processed successfully', ['learning_product' => $learning_product]);
// Emit Event to update products
// broadcast(new \App\Events\ProductsCatalogUpdated);
if ($learning_product) {
return response()->json(new LearningProductResource($learning_product), 201);
} else {
return response()->json(['message' => 'Learning Product not found.'], 404);
}
} catch (\Exception $e) {
Log::error('Error in LearningProductController@store: ' . $e->getMessage(), ['exception' => $e]);
return response()->json(['message' => 'Internal Server Error'], 500);
}
}
public function show($id)
{
$learning_product = $this->learningProductService->getOneWithChildrenAndTrashed($id, [
'filters',
'filters.filter_item.filter',
'versions',
'accreditations',
'notifications',
'synonyms',
]);
return response()->json(new LearningProductResource($learning_product));
}
public function countAll()
{
return response()->json($this->learningProductService->countAll());
}
public function destroy(String $id, $forceDelete = false)
{
try {
Log::info('Destroy method called', ['id' => $id, 'forceDelete' => $forceDelete]);
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$learning_product = $this->learningProductService->get($id);
if (!$learning_product) {
return response()->json(['message' => 'Learning Product not found.'], 404);
}
// Verwijder gerelateerde records in de versions tabel
\DB::table('versions')->where('learning_product_id', $id)->delete();
if ($forceDelete) {
$learning_product->forceDelete();
} else {
$learning_product->delete();
}
Log::info('Learning product deleted successfully', ['id' => $id]);
return response()->json(null, 204);
} catch (\Exception $e) {
Log::error('Error in LearningProductController@destroy: ' . $e->getMessage(), ['exception' => $e]);
return response()->json(['message' => 'Internal Server Error'], 500);
}
}
public function clone(LearningProductId $request)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
if (!isset($request['product_id']) || !$request['product_id']) {
return response()->json(['message' => 'Product id missing.'], 400);
}
// get original product to clone
$original_product = $this->learningProductService->getOneWith($request['product_id'], [
'filters',
'filters.filter_item.filter',
'versions',
'accreditations',
'notifications',
'synonyms',
]);
// Return if not published
if (!$original_product->published) {
return response()->json([
'error' => 'You cannot duplicate a draft'
], 400);
}
// Return already has a draft
if ($original_product->draft) {
return response()->json(['message' => 'There is already a draft for this product'], 400);
}
// set as draft
$draft = $original_product->replicate();
$draft->parent_id = $original_product->id;
$draft->published = false;
// clone cover & tiles, finally save
$cover = $original_product->getMedia('learning_products_covers')->first();
$tile = $original_product->getMedia('learning_products_tiles')->first();
if ($cover) {
$draft
->copyMedia($cover->getPath())
->toMediaCollection('learning_products_covers');
}
if ($tile) {
$draft
->copyMedia($tile->getPath())
->toMediaCollection('learning_products_covers');
}
$draft->save();
// TODO: clone accreditations, versions too?
// Clone Filter Items
if ($original_product->filters()) {
$filter_items_ids = $original_product->filters->pluck('filter_item_id');
foreach ($filter_items_ids as $filter_item_id) {
$filter_association = new FilterItemsAssociation();
$filter_association->filter_item_id = $filter_item_id;
$draft->filters()->save($filter_association);
}
}
// Clone Synonyms
if ($original_product->synonyms()) {
$synonyms_ids = $original_product->synonyms->pluck('id');
$draft->synonyms()->sync($synonyms_ids);
}
return response()->json(null, 201);
}
}

View File

@@ -0,0 +1,335 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Arr;
use App\Services\LearningProductService;
use App\Repositories\FilterItemsAssociation;
use App\Http\Resources\LearningProductResource;
use App\Http\Requests\Learning\LearningProductId;
use App\Http\Requests\Learning\LearningProductStore;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Log;
class LearningProductController extends Controller
{
private $learningProductService;
public function __construct(LearningProductService $learningProductService)
{
$this->learningProductService = $learningProductService;
$this->middleware('auth:sanctum', [
'except' => [
'getPublished',
]
]);
}
public function index()
{
$learningProducts = $this->learningProductService->withTrashedAndChildren([
'filters',
'filters.filter_item.filter',
// 'versions',
'accreditations',
'notifications',
])->sortDesc();
return response()->json(
LearningProductResource::collection($learningProducts),
201,
);;
}
public function getPublished()
{
$withColumnAll = [
'filters',
'filters.filter_item.filter',
'accreditations',
'accreditations.filters.filter_item',
'notifications'
];
$learningProductAll = $this->learningProductService->getPublishedWith($withColumnAll)
->groupBy('code')
->map(function (Collection $groupedLearningProductAll) {
return $groupedLearningProductAll->last();
})
->sortDesc()
->flatten(1);
return response()->json(
LearningProductResource::collection($learningProductAll),
201,
);
}
public function store(LearningProductStore $request)
{
try {
Log::info('Store method called');
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
Log::info('User roles checked', ['isSuperAdmin' => $isSuperAdmin, 'isAdmin' => $isAdmin, 'isOperator' => $isOperator]);
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
// Is it draft with parent_id marked as published?
$is_draft = isset($request['parent_id']);
$is_draft_published = $is_draft && $request['published'];
Log::info('Draft status checked', ['is_draft' => $is_draft, 'is_draft_published' => $is_draft_published]);
$hasCover = isset($request['cover']) && $request->hasFile('cover');
$hasTile = isset($request['tile']) && $request->hasFile('tile');
Log::info('File status checked', ['hasCover' => $hasCover, 'hasTile' => $hasTile]);
// Validate and prepare request data
$request_data = $request->validated();
if ($hasCover) {
$request_data = Arr::except($request_data, ['cover']);
}
if ($hasTile) {
$request_data = Arr::except($request_data, ['tile']);
}
// Publish without parent_id
if ($is_draft_published) {
$request_data['parent_id'] = null;
}
$request_data = Arr::except($request_data, ['filtersGrouped', 'synonymsSelected']);
Log::info('Request data prepared', ['request_data' => $request_data]);
// Save finally the product
$learning_product = $this->learningProductService->save($request_data);
Log::info('Learning product saved', ['learning_product' => $learning_product]);
// Get Filter Items passed
$filter_items = json_decode(html_entity_decode(stripslashes($request['filtersGrouped'])));
$synonyms_selected = json_decode(html_entity_decode(stripslashes($request['synonymsSelected'])));
Log::info('Filters and synonyms decoded', ['filter_items' => $filter_items, 'synonyms_selected' => $synonyms_selected]);
if ($synonyms_selected) {
$learning_product->synonyms()->sync($synonyms_selected);
}
$arrayTmp = [];
if ($filter_items) {
foreach ($filter_items as $key => $value) {
$arrayTmp[] = $value;
}
$filter_items = Arr::collapse($arrayTmp);
$filter_items = Arr::flatten($arrayTmp);
// Saves filter items associations with this learning product
$learning_product->filters()->delete();
foreach ($filter_items as $filter_item_id) {
$filter_association = new FilterItemsAssociation();
$filter_association->filter_item_id = $filter_item_id;
$learning_product->filters()->save($filter_association);
}
}
// if a file was uploaded as cover or tile, add them
if ($hasCover) {
$learning_product->addMediaFromRequest('cover')->toMediaCollection('learning_products_covers');
}
if ($hasTile) {
$learning_product->addMediaFromRequest('tile')->toMediaCollection('learning_products_tiles');
}
if ($is_draft) {
// 1st draft of the published product
$original_product_published = $this->learningProductService->get($request['parent_id']);
// If no cover has been attached before
if (!$hasCover && !$learning_product->cover) {
$image = $original_product_published->getMedia('learning_products_covers')->first();
if ($image) {
$learning_product->copyMedia($image->getPath())->toMediaCollection('learning_products_covers');
}
}
// If no tile has been attached before
if (!$hasTile && !$learning_product->tile) {
$image = $original_product_published->getMedia('learning_products_tiles')->first();
if ($image) {
$learning_product->copyMedia($image->getPath())->toMediaCollection('learning_products_tiles');
}
}
}
// ForceDelete the parent if draft published
if ($is_draft_published && $learning_product) {
$this->destroy($request['parent_id'], true);
}
Log::info('Learning product processed successfully', ['learning_product' => $learning_product]);
// Emit Event to update products
// broadcast(new \App\Events\ProductsCatalogUpdated);
if ($learning_product) {
return response()->json(new LearningProductResource($learning_product), 201);
} else {
return response()->json(['message' => 'Learning Product not found.'], 404);
}
} catch (\Exception $e) {
Log::error('Error in LearningProductController@store: ' . $e->getMessage(), ['exception' => $e]);
return response()->json(['message' => 'Internal Server Error'], 500);
}
}
public function show($id)
{
$learning_product = $this->learningProductService->getOneWithChildrenAndTrashed($id, [
'filters',
'filters.filter_item.filter',
'versions',
'accreditations',
'notifications',
'synonyms',
]);
return response()->json(new LearningProductResource($learning_product));
}
public function countAll()
{
return response()->json($this->learningProductService->countAll());
}
public function destroy(String $id, $forceDelete = false)
{
try {
Log::info('Destroy method called', ['id' => $id, 'forceDelete' => $forceDelete]);
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$learning_product = $this->learningProductService->get($id);
if (!$learning_product) {
return response()->json(['message' => 'Learning Product not found.'], 404);
}
// Verwijder gerelateerde records in de versions tabel
\DB::table('versions')->where('learning_product_id', $id)->delete();
if ($forceDelete) {
$learning_product->forceDelete();
} else {
$learning_product->delete();
}
Log::info('Learning product deleted successfully', ['id' => $id]);
return response()->json(null, 204);
} catch (\Exception $e) {
Log::error('Error in LearningProductController@destroy: ' . $e->getMessage(), ['exception' => $e]);
return response()->json(['message' => 'Internal Server Error'], 500);
}
}
public function clone(LearningProductId $request)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
if (!isset($request['product_id']) || !$request['product_id']) {
return response()->json(['message' => 'Product id missing.'], 400);
}
// get original product to clone
$original_product = $this->learningProductService->getOneWith($request['product_id'], [
'filters',
'filters.filter_item.filter',
'versions',
'accreditations',
'notifications',
'synonyms',
]);
// Return if not published
if (!$original_product->published) {
return response()->json([
'error' => 'You cannot duplicate a draft'
], 400);
}
// Return already has a draft
if ($original_product->draft) {
return response()->json(['message' => 'There is already a draft for this product'], 400);
}
// set as draft
$draft = $original_product->replicate();
$draft->parent_id = $original_product->id;
$draft->published = false;
// clone cover & tiles, finally save
$cover = $original_product->getMedia('learning_products_covers')->first();
$tile = $original_product->getMedia('learning_products_tiles')->first();
if ($cover) {
$draft
->copyMedia($cover->getPath())
->toMediaCollection('learning_products_covers');
}
if ($tile) {
$draft
->copyMedia($tile->getPath())
->toMediaCollection('learning_products_covers');
}
$draft->save();
// TODO: clone accreditations, versions too?
// Clone Filter Items
if ($original_product->filters()) {
$filter_items_ids = $original_product->filters->pluck('filter_item_id');
foreach ($filter_items_ids as $filter_item_id) {
$filter_association = new FilterItemsAssociation();
$filter_association->filter_item_id = $filter_item_id;
$draft->filters()->save($filter_association);
}
}
// Clone Synonyms
if ($original_product->synonyms()) {
$synonyms_ids = $original_product->synonyms->pluck('id');
$draft->synonyms()->sync($synonyms_ids);
}
return response()->json(null, 201);
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ManagementLinkRequest;
use App\Services\ManagementLinkService;
use App\Http\Requests\Member\ManagementLinkStore;
class ManagementLinkController extends Controller
{
private $managementLinkService;
public function __construct(ManagementLinkService $managementLinkService)
{
$this->managementLinkService = $managementLinkService;
}
public function index()
{
$management_links = $this->managementLinkService->getAll();
return response()->json($management_links, 201);
}
public function store(ManagementLinkService $request)
{
$management_links = $this->managementLinkService->save($request->all());
return response()->json($management_links, 201);
}
public function show($id)
{
$management_links = $this->managementLinkService->get($id);
return response()->json($management_links);
}
public function destroy(ManagementLinkStore $request)
{
$this->managementLinkService->delete($request->link_id);
return response()->json(null, 204);
}
public function storeManagementLink(ManagementLinkStore $request) {
$managementLink = [
'member_id' => $request->member_id
];
$link = $this->managementLinkService->save($managementLink);
return response()->json($request, 201);
}
public function changeManagementLink(ManagementLinkStore $request) {
$managementLink = [
'id' => $request->link_id,
$request->field => $request->value
];
$link = $this->managementLinkService->save($managementLink);
return response()->json($request, 201);
}
}

View File

@@ -0,0 +1,154 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Arr;
use App\Services\MemberService;
use App\Services\RevisionService;
use App\Http\Resources\MemberResource;
use App\Http\Requests\Member\MemberStore;
use App\Http\Requests\Member\RevisionStore;
use App\Repositories\Member;
use App\Repositories\User;
use App\Services\QueryBuilderService;
use Illuminate\Http\Request;
class MemberController extends Controller
{
private MemberService $memberService;
private RevisionService $revisionService;
private QueryBuilderService $queryBuilderService;
public function __construct(
MemberService $memberService,
RevisionService $revisionService,
QueryBuilderService $queryBuilderService
) {
$this->memberService = $memberService;
$this->revisionService = $revisionService;
$this->queryBuilderService = $queryBuilderService;
$this->middleware('auth:sanctum', ['except' => ['storeMemberRevisions']]);
}
public function index(Request $request)
{
$members = $this->queryBuilderService
->createQueryBuilder(Member::class, Member::class, $request)
->with([
'addresses',
'contacts',
'main_branch',
'management_links',
'revision',
'sub_branches',
'summaries',
'contributions',
'users',
])
->withTrashed()
->defaultSort('-id')
->get();
return response()->json(MemberResource::collection($members), 201);
}
public function store(MemberStore $request)
{
/** @var User */
$user = auth()->user();
$isSuperAdmin = $user->hasRole('super_admin');
$isAdmin = $user->hasRole('admin');
// $isOperator = $user->hasRole('operator');
// $isAdminOrOperator = $isAdmin || $isOperator;
// $isUserDelegated = $member->user_id === $user->id;
if (!$isSuperAdmin && !$isAdmin) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$hasLogo = isset($request['logo']) && $request->hasFile('logo');
$request_data = Arr::except($request->validated(), ['sub_branches']);
if (!isset($request_data['user_id']) || !is_int($request_data['user_id'])) {
$request_data['user_id'] = $user->id;
}
$member = $this->memberService->save($request_data);
if ($request->revisor_id && $request->revisor_id == $user->id) {
$member->revision->revisor_id = $user->id;
$member->revision->touch();
$member->revision->accepted_at = $member->revision->updated_at;
$member->revision->timestamps = false;
$member->revision->save();
}
$sub_branches = json_decode(html_entity_decode(stripslashes($request['sub_branches'])));
$member->sub_branches()->sync($sub_branches);
if ($hasLogo) $member->addMediaFromRequest('logo')->toMediaCollection('members_logos');
// Emit Event to update members
broadcast(new \App\Events\MembersUpdated);
return response()->json($member, 201);
}
public function storeMemberRevision(RevisionStore $request)
{
if ($request->user_id != auth()->user()->id) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$member = $this->memberService->get($request->member_id);
if (!$member) {
return response()->json(['message' => 'Member not found.'], 404);
}
if ($member->user_id != auth()->user()->id) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$hasLogo = isset($request['logo']) && $request->hasFile('logo');
$revision = $this->revisionService->save($request->validated());
if ($hasLogo) $member->addMediaFromRequest('logo')->toMediaCollection('members_logos');
// Emit Event to update members
broadcast(new \App\Events\MembersUpdated);
return response()->json($revision, 201);
}
public function show($id)
{
$member = $this->memberService->getOneWith($id, ['summaries', 'addresses', 'contacts', 'contributions', 'sub_branches', 'revision', 'management_links']);
return response()->json(new MemberResource($member));
}
public function destroy(String $id, $forceDelete = false)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isSuperAdminOrAdmin = $isSuperAdmin || $isAdmin;
if (!$isSuperAdminOrAdmin) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->memberService->delete($id, $forceDelete);
return response()->json(null, 204);
}
public function countAll()
{
return response()->json($this->memberService->countAll());
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Notifications\CustomNotification;
use App\Http\Resources\UserLoggedResource;
class NotificationController extends Controller
{
public function __construct()
{
$this->middleware('auth:sanctum');
}
public function markAsRead(Request $request)
{
$notification = auth()->user()->notifications()->find($request->id);
if ($notification) {
$notification->markAsRead();
return response()->json(new UserLoggedResource(auth()->user()));
}
}
public function markAsUnread(Request $request)
{
$notification = auth()->user()->notifications()->find($request->id);
if ($notification) {
$notification->read_at = null;
$notification->save();
return response()->json(new UserLoggedResource(auth()->user()));
}
}
public function delete(Request $request)
{
$notification = auth()->user()->notifications()->find($request->id);
if ($notification) {
$notification->delete();
return response()->json(new UserLoggedResource(auth()->user()));
}
}
public function test()
{
if (!auth()->user()) return;
$demo_notification = new CustomNotification(
'Subject',
'Lorem ipsum dolor sit amet consectetur adipisicing elit. Ab dolores libero at dolorem unde, consequuntur sed eveniet totam aperiam aspernatur.'
);
auth()->user()->notify($demo_notification);
broadcast(new \App\Events\UserInfoUpdated(auth()->user()));
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Http\Controllers;
class PingController extends Controller
{
public function index()
{
return response('', 200);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
protected function sendResetResponse(Request $request, $response)
{
return response(['message' => trans($response)]);
}
protected function sendResetFailedResponse(Request $request, $response)
{
return response(['error' => trans($response)], 422);
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace App\Http\Controllers\Response;
use App\Http\Controllers\Error\ErrorInterface;
use Illuminate\Http\Response;
class ApiResponse
{
public static function success(
array $content,
int $status = Response::HTTP_OK,
array $headers = []
): Response
{
return static::determineResponse($content, $status, false, $headers);
}
private static function determineResponse(
array $content,
int $status,
bool $error,
array $headers = []
): Response
{
return response(
[
'error' => $error,
'status' => [
'code' => $status,
'text' => Response::$statusTexts[$status],
],
'content' => $content,
],
$status,
$headers,
);
}
public static function error(
ErrorInterface $error,
array $headers = []
): Response
{
return static::determineResponse(
[
'code' => $error->getErrorCode(),
'message' => $error->getErrorMessage(),
],
$error->getStatusCode(),
true,
$headers,
);
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Http\Controllers;
use App\Services\RoleService;
class RoleController extends Controller
{
public function __construct(RoleService $roleService)
{
$this->roleService = $roleService;
}
public function index()
{
return $this->roleService->getAll();
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace App\Http\Controllers;
use App\Mail\MemberChanges;
use App\Services\MemberService;
use App\Services\SummaryService;
use App\Services\UserService;
use Illuminate\Support\Facades\Mail;
use App\Http\Requests\Member\SummaryStore;
class SummaryController extends Controller
{
private $summaryService;
private $memberService;
public function __construct(
MemberService $memberService,
UserService $userService,
SummaryService $summaryService
) {
$this->memberService = $memberService;
$this->summaryService = $summaryService;
$this->userService = $userService;
$this->middleware('auth:sanctum');
}
public function index()
{
$summaries = $this->summaryService->getAll();
return response()->json($summaries, 201);
}
public function store(SummaryStore $request)
{
$member = $this->memberService->get($request->member_id);
if (!$member) {
return response()->json(['message' => 'Member not found.'], 404);
}
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isSuperAdminOrAdmin = $isSuperAdmin || $isAdmin;
$isUserDelegated = $member->user_id === auth()->user()->id;
$isAppliedToAll = $request->has('toAll') ? true : false;
if (!$isSuperAdminOrAdmin && !$isUserDelegated) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$request_data = $request->validated();
$is_edit_mode = isset($request_data['id']);
$is_create_mode = !$is_edit_mode;
$summary = null;
if ($is_edit_mode) {
if ($isSuperAdminOrAdmin) {
$request_data['revisor_id'] = auth()->user()->id;
$request_data['approved_at'] = now();
}
$summary = $this->summaryService->get($request_data['id']);
$is_already_approved = $summary->approved_at;
if ($isUserDelegated && !$isSuperAdminOrAdmin && $is_already_approved) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
}
if ($isAppliedToAll && $is_create_mode) {
if ($isUserDelegated && !$isSuperAdminOrAdmin) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$members = $this->memberService->with(['summaries']);
// Store for all existing members
foreach ($members as $member) {
// If the member doesn't have that year set, store the record
if (!$member->summaries->contains('year', $request_data['year'])) {
$request_data['member_id'] = $member['id'];
// Gives back the summary to update the page
if ($request_data['member_id'] === $member['user_id']) {
$summary = $this->summaryService->save($request_data);
} else {
$this->summaryService->save($request_data);
}
}
}
} else {
$summary = $this->summaryService->save($request_data);
}
// If is a user delegated to make changes, send a mail
if ($isUserDelegated) {
// Get super admins & admins, send them an email
$super_admins_and_admins = $this->userService->getAllWithRoles(['super_admin', 'admin']);
$notification = (object) array();
$notification->member = $member;
$notification->subject = 'Er zijn wijzigingen doorgevoerd';
$notification->message = sprintf(
'De volgende wijzigingen kunnen worden beoordeeld, voor het volgende lid: <em>%s</em>',
$member->informal_name,
);
// Add emails to queue | * php artisan queue:listen
foreach ($super_admins_and_admins as $user) {
Mail::to($user)->send(new MemberChanges($notification));
}
}
return response()->json($summary, 201);
}
public function show($id)
{
$summary = $this->summaryService->get($id);
return response()->json($summary);
}
public function destroy($id)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isSuperAdminOrAdmin = $isSuperAdmin || $isAdmin;
if (!$isSuperAdminOrAdmin) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->summaryService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers;
use App\Services\SynonymService;
use App\Http\Requests\Learning\SynonymStore;
class SynonymController extends Controller
{
private $synonymService;
public function __construct(SynonymService $synonymService)
{
$this->synonymService = $synonymService;
$this->middleware('auth:sanctum', [
'except' => [
'index',
]
]);
}
public function index()
{
$synonyms = $this->synonymService->getAll();
return response()->json($synonyms, 201);
}
public function store(SynonymStore $request)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$synonym = $this->synonymService->save($request->all());
return response()->json($synonym, 201);
}
public function show($id)
{
$synonym = $this->synonymService->get($id);
return response()->json($synonym);
}
public function destroy($id)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->synonymService->delete($id);
return response()->json(null, 204);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Controllers;
use App\Services\MemberService;
use Illuminate\Http\Request;
class TypeController extends Controller
{
private MemberService $memberService;
public function __construct(MemberService $memberService)
{
$this->memberService = $memberService;
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
return response()->json($this->memberService->getValidTypes());
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Arr;
use App\Services\UserService;
use App\Http\Resources\UsersList;
use App\Http\Resources\UserResource;
use App\Http\Requests\User\UserStore;
use App\Http\Resources\UserLoggedResource;
class UserController extends Controller
{
private $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
$this->middleware('auth:sanctum');
}
public function index()
{
$users = $this->userService->getWith(['roles']);
return response()->json(UserResource::collection($users), 200);
}
public function show($id)
{
$isAdmin = auth()->user()->hasRole('admin');
$isTheUserOwner = auth()->user()->id === (int) $id;
if (!$isAdmin && !$isTheUserOwner) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$user = $this->userService->getOneWith($id, ['roles']);
if ($user) {
return response()->json(new UserResource($user));
} else return response()->json(['message' => 'User not found.'], 404);
}
public function store(UserStore $request)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isEditingHimself = auth()->user()->id === (int) $request['id'];
if (!$isSuperAdmin && !$isEditingHimself) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$hasImage = isset($request['image']) && $request->hasFile('image');
$request_data = $hasImage ? Arr::except($request->validated(), ['image']) : $request->validated();
// only super admin can manage users and rules
$user = $this->userService->save($request_data, $isSuperAdmin);
if ($hasImage) {
$user->addMedia($request['image'])->toMediaCollection('profile_pics');
}
if ($user) {
return response()->json($isEditingHimself ? new UserLoggedResource($user) : new UserResource($user));
} else return response()->json(['message' => 'User not found.'], 404);
}
public function destroy(String $id)
{
if (!auth()->user()->hasRole('admin')) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->userService->delete($id);
return response()->json(null, 204);
}
public function getList()
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$users = $this->userService->getAll();
return response()->json(UsersList::collection($users), 200);
}
}

View File

@@ -0,0 +1,112 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Arr;
use App\Services\VersionService;
use App\Http\Requests\Learning\VersionStore;
use App\Repositories\FilterItemsAssociation;
class VersionController extends Controller
{
private $versionService;
public function __construct(VersionService $versionService)
{
$this->versionService = $versionService;
$this->middleware('auth:sanctum');
}
public function index()
{
$versions = $this->versionService->getAll();
return response()->json($versions, 201);
}
public function store(VersionStore $request)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
if ($request['filter_items']) {
$filter_items = Arr::collapse($request['filter_items']);
$filter_items = Arr::flatten($filter_items);
}
if ($request['checklists_selected']) {
$checklists_selected = $request['checklists_selected'];
}
$data = Arr::except($request->validated(), ['filter_items', 'checklists_selected']);
$version = $this->versionService->save($data);
if (isset($filter_items) && $filter_items) {
$version->filters()->delete();
foreach ($filter_items as $filter_item_id) {
$filter_association = new FilterItemsAssociation();
$filter_association->filter_item_id = $filter_item_id;
$version->filters()->save($filter_association);
}
}
if (isset($checklists_selected) && $checklists_selected) {
// Fetch all checklists ids already stored
$checklists_ids_stored = $version->checklists()->pluck('checklist_id');
// If an id from $checklists_ids_stored is not present in the uploaded checklists_selected, delete it
foreach ($checklists_ids_stored as $checklist_id_stored) {
if (!in_array($checklist_id_stored, $checklists_selected)) {
$version->checklists()->where('checklist_id', $checklist_id_stored)->delete();
}
}
// If a checklist from the uploaded is not present in $version->checklists(), create
foreach ($checklists_selected as $checklist_selected) {
if (!in_array($checklist_selected, $checklists_ids_stored->toArray())) {
$version->checklists()->create([
'user_id' => auth()->user()->id,
'version_id' => $version->id,
'checklist_id' => $checklist_selected,
]);
}
}
}
return response()->json($version->load(['filters', 'checklists']), 201);
}
public function show($id)
{
$version = $this->versionService->get($id);
return response()->json($version);
}
public function destroy($id)
{
$isSuperAdmin = auth()->user()->hasRole('super_admin');
$isAdmin = auth()->user()->hasRole('admin');
$isOperator = auth()->user()->hasRole('operator');
if (!$isSuperAdmin && !$isAdmin && !$isOperator) {
return response()->json(['message' => 'You have no rights to do this'], 401);
}
$this->versionService->delete($id);
return response()->json(null, 204);
}
}