add notifications logic and add support of both JWT and Sanctum

This commit is contained in:
root
2026-03-11 01:20:31 -04:00
parent f6be51576c
commit 182036cc41
141 changed files with 8685 additions and 648 deletions
View File
+28
View File
@@ -0,0 +1,28 @@
<?php
use App\Models\UserRoleModel;
use App\Models\RolePermissionModel;
use App\Models\PermissionModel;
function has_permission($userId, $permissionName)
{
$userRoleModel = new UserRoleModel();
$rolePermissionModel = new RolePermissionModel();
$permissionModel = new PermissionModel();
$permission = $permissionModel->where('name', $permissionName)->first();
if (!$permission) {
return false;
}
$permissionId = $permission['id'];
$roles = $userRoleModel->where('user_id', $userId)->findAll();
foreach ($roles as $role) {
$permissions = $rolePermissionModel->where('role_id', $role['role_id'])->where('permission_id', $permissionId)->findAll();
if (!empty($permissions)) {
return true;
}
}
return false;
}
View File
+18
View File
@@ -0,0 +1,18 @@
<?php
use App\Models\ConfigurationModel;
// app/Helpers/GlobalConfigHelper.php
if (!function_exists('getSemester')) {
function getSemester() {
$configModel = new \App\Models\ConfigurationModel();
return $configModel->getConfig('semester');
}
}
if (!function_exists('getSchoolYear')) {
function getSchoolYear() {
$configModel = new \App\Models\ConfigurationModel();
return $configModel->getConfig('school_year');
}
}
@@ -0,0 +1,60 @@
<?php
use App\Models\AttendanceCommentTemplateModel;
if (!function_exists('attendance_comment_from_score')) {
function attendance_comment_from_score($score, string $firstName = ''): ?string
{
if ($score === null || $score === '') {
return null;
}
$score = (float) $score;
$template = attendance_comment_template_for_score($score);
if ($template === null) {
return null;
}
$text = trim((string) ($template['template_text'] ?? ''));
if ($text === '') {
return null;
}
$name = trim($firstName) !== '' ? trim($firstName) : 'This student';
$lastChar = $name !== '' ? substr($name, -1) : '';
$namePossessive = ($name === 'This student')
? "This student's"
: ($lastChar === 's' || $lastChar === 'S' ? ($name . "'") : ($name . "'s"));
if (strpos($text, '{name}') !== false) {
return str_replace('{name}', $namePossessive, $text);
}
return $namePossessive . ' ' . $text;
}
}
if (!function_exists('attendance_comment_template_for_score')) {
function attendance_comment_template_for_score(float $score): ?array
{
static $templates = null;
if ($templates === null) {
try {
$model = new AttendanceCommentTemplateModel();
$templates = $model->getActiveTemplates();
} catch (\Throwable $e) {
$templates = [];
}
}
foreach ($templates as $template) {
$min = isset($template['min_score']) ? (float) $template['min_score'] : 0.0;
$max = isset($template['max_score']) ? (float) $template['max_score'] : 100.0;
if ($score >= $min && $score <= $max) {
return $template;
}
}
return null;
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
if (!function_exists('base64url_encode')) {
function base64url_encode(string $data): string
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
}
if (!function_exists('jwt_encode')) {
/**
* Minimal HS256 JWT encoder.
*
* @param array $payload
* @param string $secret
* @param string $alg Only HS256 supported here
* @return string JWT string
*/
function jwt_encode(array $payload, string $secret, string $alg = 'HS256'): string
{
$header = ['typ' => 'JWT', 'alg' => $alg];
$segments = [];
$segments[] = base64url_encode(json_encode($header, JSON_UNESCAPED_SLASHES));
$segments[] = base64url_encode(json_encode($payload, JSON_UNESCAPED_SLASHES));
$signingInput = implode('.', $segments);
switch ($alg) {
case 'HS256':
$signature = hash_hmac('sha256', $signingInput, $secret, true);
break;
default:
throw new \InvalidArgumentException('Unsupported JWT alg: ' . $alg);
}
$segments[] = base64url_encode($signature);
return implode('.', $segments);
}
}
if (!function_exists('base64url_decode')) {
function base64url_decode(string $data): string
{
$padding = strlen($data) % 4;
if ($padding > 0) {
$data .= str_repeat('=', 4 - $padding);
}
return base64_decode(strtr($data, '-_', '+/'));
}
}
if (!function_exists('jwt_decode')) {
function jwt_decode(string $token, string $secret, string $alg = 'HS256'): ?array
{
$parts = explode('.', $token);
if (count($parts) !== 3) {
return null;
}
[$header64, $payload64, $signature64] = $parts;
$header = json_decode(base64url_decode($header64), true);
$payload = json_decode(base64url_decode($payload64), true);
$signature = base64url_decode($signature64);
if (!is_array($header) || !is_array($payload)) {
return null;
}
$signingInput = $header64 . '.' . $payload64;
switch ($header['alg'] ?? $alg) {
case 'HS256':
$expected = hash_hmac('sha256', $signingInput, $secret, true);
break;
default:
return null;
}
if (!hash_equals($expected, $signature)) {
return null;
}
return $payload;
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
if (!function_exists('pbkdf2_hash')) {
function pbkdf2_hash(string $password, string $algo = 'sha256', int $iterations = 100000, int $length = 64): string
{
$salt = random_bytes(16); // 16 bytes = 32 hex characters
$salt_hex = bin2hex($salt);
$derived_key = hash_pbkdf2($algo, $password, $salt, $iterations, $length, true);
$derived_key_hex = bin2hex($derived_key);
return "{$algo}\${$iterations}\${$salt_hex}\${$derived_key_hex}";
}
}
if (!function_exists('pbkdf2_verify')) {
function pbkdf2_verify(string $password, string $stored_hash): bool
{
$parts = explode('$', $stored_hash);
if (count($parts) !== 4) {
return false; // Invalid format
}
[$algo, $iterations, $salt_hex, $derived_key_hex] = $parts;
if (strlen($salt_hex) % 2 !== 0 || strlen($derived_key_hex) % 2 !== 0) {
return false; // Not valid hex
}
$salt = hex2bin($salt_hex);
$derived_key = hex2bin($derived_key_hex);
$length = strlen($derived_key);
$new_derived_key = hash_pbkdf2($algo, $password, $salt, (int)$iterations, $length, true);
return hash_equals($derived_key, $new_derived_key);
}
}
@@ -0,0 +1,27 @@
<?php
use App\Models\ConfigurationModel;
if (!function_exists('selected_teacher_semester')) {
function selected_teacher_semester(): string
{
$session = session();
$selected = trim((string) ($session->get('teacher_scores_selected_semester') ?? ''));
if ($selected !== '') {
return ucfirst(strtolower($selected));
}
$fallback = trim((string) ($session->get('semester') ?? ''));
if ($fallback !== '') {
return ucfirst(strtolower($fallback));
}
$config = new ConfigurationModel();
$configSemester = trim((string) ($config->getConfig('semester') ?? ''));
if ($configSemester !== '') {
return ucfirst(strtolower($configSemester));
}
return 'Fall';
}
}
+75
View File
@@ -0,0 +1,75 @@
<?php
use CodeIgniter\I18n\Time;
if (! function_exists('user_timezone')) {
function user_timezone(): string
{
return service('timeService')->userTimezone();
}
}
if (! function_exists('server_timezone')) {
function server_timezone(): string
{
return service('timeService')->serverTimezone();
}
}
if (! function_exists('utc_now')) {
function utc_now(string $format = 'Y-m-d H:i:s'): string
{
return service('timeService')->nowUTC()->format($format);
}
}
if (! function_exists('local_now')) {
function local_now(string $format = 'Y-m-d H:i:s', ?string $tz = null): string
{
return service('timeService')->nowLocal($tz)->format($format);
}
}
if (! function_exists('to_utc')) {
/**
* Convert a time to UTC with optional source TZ and format.
*
* @param string|Time|\DateTimeInterface|null $value
*/
function to_utc($value, ?string $fromTz = null, string $format = 'Y-m-d H:i:s'): ?string
{
return service('timeService')->toUTC($value, $fromTz, $format);
}
}
if (! function_exists('to_local')) {
/**
* Convert a time to user-local with optional source TZ and format.
*
* @param string|Time|\DateTimeInterface|null $value
*/
function to_local($value, ?string $sourceTz = null, ?string $targetTz = null, string $format = 'Y-m-d H:i:s'): ?string
{
return service('timeService')->toLocal($value, $sourceTz, $targetTz, $format);
}
}
if (! function_exists('local_date')) {
/**
* Format to user-local date (Y-m-d by default). Assumes input is UTC unless source TZ provided.
*/
function local_date($value, string $format = 'Y-m-d', ?string $sourceTz = null): string
{
return service('timeService')->formatLocal($value, $format, $sourceTz);
}
}
if (! function_exists('local_datetime')) {
/**
* Format to user-local datetime (Y-m-d H:i by default). Assumes input is UTC unless source TZ provided.
*/
function local_datetime($value, string $format = 'Y-m-d H:i', ?string $sourceTz = null): string
{
return service('timeService')->formatLocal($value, $format, $sourceTz);
}
}