From 463e18be77b1c0b3324b5a948a1237655134dd7f Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Thu, 13 Nov 2025 12:51:36 -0800 Subject: [PATCH 01/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Update=20Packag?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.lock | 1138 +++++++++++++++++++++++++-------------------- package-lock.json | 6 + readme.md | 3 + 3 files changed, 636 insertions(+), 511 deletions(-) create mode 100644 package-lock.json diff --git a/composer.lock b/composer.lock index 7abf9d6..b207df4 100644 --- a/composer.lock +++ b/composer.lock @@ -8,25 +8,25 @@ "packages": [ { "name": "brick/math", - "version": "0.12.3", + "version": "0.14.0", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", + "url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", + "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.2" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "6.8.8" + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" }, "type": "library", "autoload": { @@ -56,7 +56,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.3" + "source": "https://github.com/brick/math/tree/0.14.0" }, "funding": [ { @@ -64,7 +64,7 @@ "type": "github" } ], - "time": "2025-02-28T13:11:00+00:00" + "time": "2025-08-29T12:40:03+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -292,16 +292,16 @@ }, { "name": "directorytree/ldaprecord", - "version": "v3.8.2", + "version": "v3.8.5", "source": { "type": "git", "url": "https://github.com/DirectoryTree/LdapRecord.git", - "reference": "cff35dc485cdfbc68e53948e4842e37e68770dde" + "reference": "00e5f088f8c4028d5f398783cccc2e8119a27a65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DirectoryTree/LdapRecord/zipball/cff35dc485cdfbc68e53948e4842e37e68770dde", - "reference": "cff35dc485cdfbc68e53948e4842e37e68770dde", + "url": "https://api.github.com/repos/DirectoryTree/LdapRecord/zipball/00e5f088f8c4028d5f398783cccc2e8119a27a65", + "reference": "00e5f088f8c4028d5f398783cccc2e8119a27a65", "shasum": "" }, "require": { @@ -364,20 +364,20 @@ "type": "github" } ], - "time": "2025-05-20T20:49:13+00:00" + "time": "2025-10-06T02:22:34+00:00" }, { "name": "directorytree/ldaprecord-laravel", - "version": "v3.4.1", + "version": "v3.4.2", "source": { "type": "git", "url": "https://github.com/DirectoryTree/LdapRecord-Laravel.git", - "reference": "15f56e01319852d41023633d3688ac4aa139aa6e" + "reference": "28c5a7aa42aa3fa631f9c0f0c8236fd19bc7b00c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DirectoryTree/LdapRecord-Laravel/zipball/15f56e01319852d41023633d3688ac4aa139aa6e", - "reference": "15f56e01319852d41023633d3688ac4aa139aa6e", + "url": "https://api.github.com/repos/DirectoryTree/LdapRecord-Laravel/zipball/28c5a7aa42aa3fa631f9c0f0c8236fd19bc7b00c", + "reference": "28c5a7aa42aa3fa631f9c0f0c8236fd19bc7b00c", "shasum": "" }, "require": { @@ -423,7 +423,7 @@ ], "support": { "issues": "https://github.com/DirectoryTree/LdapRecord-Laravel/issues", - "source": "https://github.com/DirectoryTree/LdapRecord-Laravel/tree/v3.4.1" + "source": "https://github.com/DirectoryTree/LdapRecord-Laravel/tree/v3.4.2" }, "funding": [ { @@ -431,7 +431,7 @@ "type": "github" } ], - "time": "2025-03-21T19:16:44+00:00" + "time": "2025-06-13T15:46:25+00:00" }, { "name": "doctrine/annotations", @@ -507,37 +507,37 @@ "issues": "https://github.com/doctrine/annotations/issues", "source": "https://github.com/doctrine/annotations/tree/2.0.2" }, + "abandoned": true, "time": "2024-09-05T10:17:24+00:00" }, { "name": "doctrine/inflector", - "version": "2.0.10", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11.0", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25 || ^5.4" + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + "Doctrine\\Inflector\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -582,7 +582,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.10" + "source": "https://github.com/doctrine/inflector/tree/2.1.0" }, "funding": [ { @@ -598,7 +598,7 @@ "type": "tidelift" } ], - "time": "2024-02-18T20:23:39+00:00" + "time": "2025-08-10T19:31:58+00:00" }, { "name": "doctrine/lexer", @@ -679,29 +679,28 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.4.0", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "8c784d071debd117328803d86b2097615b457500" + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", - "reference": "8c784d071debd117328803d86b2097615b457500", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013", "shasum": "" }, "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" + "php": "^8.2|^8.3|^8.4|^8.5" }, "replace": { "mtdowling/cron-expression": "^1.0" }, "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.32|^2.1.31", + "phpunit/phpunit": "^8.5.48|^9.0" }, "type": "library", "extra": { @@ -732,7 +731,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0" }, "funding": [ { @@ -740,7 +739,7 @@ "type": "github" } ], - "time": "2024-10-09T13:47:03+00:00" + "time": "2025-10-31T18:51:33+00:00" }, { "name": "egulias/email-validator", @@ -944,22 +943,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.9.3", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.3", - "guzzlehttp/psr7": "^2.7.0", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -1050,7 +1049,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -1066,20 +1065,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:37:11+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -1087,7 +1086,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -1133,7 +1132,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.2.0" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -1149,20 +1148,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:27:01+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.1", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -1178,7 +1177,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1249,7 +1248,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.1" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -1265,20 +1264,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T12:30:47+00:00" + "time": "2025-08-23T21:21:41+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.4", + "version": "v1.0.5", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "30e286560c137526eccd4ce21b2de477ab0676d2" + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/30e286560c137526eccd4ce21b2de477ab0676d2", - "reference": "30e286560c137526eccd4ce21b2de477ab0676d2", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", "shasum": "" }, "require": { @@ -1287,7 +1286,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", "uri-template/tests": "1.0.0" }, "type": "library", @@ -1335,7 +1334,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.4" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" }, "funding": [ { @@ -1351,24 +1350,24 @@ "type": "tidelift" } ], - "time": "2025-02-03T10:55:03+00:00" + "time": "2025-08-22T14:27:06+00:00" }, { "name": "laravel/framework", - "version": "v11.45.1", + "version": "v11.46.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "b09ba32795b8e71df10856a2694706663984a239" + "reference": "5fd457f807570a962a53b403b1346efe4cc80bb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/b09ba32795b8e71df10856a2694706663984a239", - "reference": "b09ba32795b8e71df10856a2694706663984a239", + "url": "https://api.github.com/repos/laravel/framework/zipball/5fd457f807570a962a53b403b1346efe4cc80bb8", + "reference": "5fd457f807570a962a53b403b1346efe4cc80bb8", "shasum": "" }, "require": { - "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12|^0.13|^0.14", "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", "dragonmantank/cron-expression": "^3.4", @@ -1472,7 +1471,7 @@ "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^9.13.2", + "orchestra/testbench-core": "^9.16.1", "pda/pheanstalk": "^5.0.6", "php-http/discovery": "^1.15", "phpstan/phpstan": "^2.0", @@ -1566,20 +1565,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-06-03T14:01:40+00:00" + "time": "2025-09-30T14:51:32+00:00" }, { "name": "laravel/helpers", - "version": "v1.7.2", + "version": "v1.8.1", "source": { "type": "git", "url": "https://github.com/laravel/helpers.git", - "reference": "672d79d5b5f65dc821e57783fa11f22c4d762d70" + "reference": "d0094b4bc4364560c8ee3a9e956596d760d4afab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/helpers/zipball/672d79d5b5f65dc821e57783fa11f22c4d762d70", - "reference": "672d79d5b5f65dc821e57783fa11f22c4d762d70", + "url": "https://api.github.com/repos/laravel/helpers/zipball/d0094b4bc4364560c8ee3a9e956596d760d4afab", + "reference": "d0094b4bc4364560c8ee3a9e956596d760d4afab", "shasum": "" }, "require": { @@ -1621,9 +1620,9 @@ "laravel" ], "support": { - "source": "https://github.com/laravel/helpers/tree/v1.7.2" + "source": "https://github.com/laravel/helpers/tree/v1.8.1" }, - "time": "2025-01-24T15:41:25+00:00" + "time": "2025-09-02T15:31:25+00:00" }, { "name": "laravel/legacy-factories", @@ -1683,16 +1682,16 @@ }, { "name": "laravel/prompts", - "version": "v0.3.5", + "version": "v0.3.7", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "57b8f7efe40333cdb925700891c7d7465325d3b1" + "reference": "a1891d362714bc40c8d23b0b1d7090f022ea27cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/57b8f7efe40333cdb925700891c7d7465325d3b1", - "reference": "57b8f7efe40333cdb925700891c7d7465325d3b1", + "url": "https://api.github.com/repos/laravel/prompts/zipball/a1891d362714bc40c8d23b0b1d7090f022ea27cc", + "reference": "a1891d362714bc40c8d23b0b1d7090f022ea27cc", "shasum": "" }, "require": { @@ -1709,8 +1708,8 @@ "illuminate/collections": "^10.0|^11.0|^12.0", "mockery/mockery": "^1.5", "pestphp/pest": "^2.3|^3.4", - "phpstan/phpstan": "^1.11", - "phpstan/phpstan-mockery": "^1.1" + "phpstan/phpstan": "^1.12.28", + "phpstan/phpstan-mockery": "^1.1.3" }, "suggest": { "ext-pcntl": "Required for the spinner to be animated." @@ -1736,22 +1735,22 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.5" + "source": "https://github.com/laravel/prompts/tree/v0.3.7" }, - "time": "2025-02-11T13:34:40+00:00" + "time": "2025-09-19T13:47:56+00:00" }, { "name": "laravel/serializable-closure", - "version": "v2.0.4", + "version": "v2.0.6", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841" + "reference": "038ce42edee619599a1debb7e81d7b3759492819" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b352cf0534aa1ae6b4d825d1e762e35d43f8a841", - "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/038ce42edee619599a1debb7e81d7b3759492819", + "reference": "038ce42edee619599a1debb7e81d7b3759492819", "shasum": "" }, "require": { @@ -1799,7 +1798,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2025-03-19T13:51:03+00:00" + "time": "2025-10-09T13:42:30+00:00" }, { "name": "laravel/tinker", @@ -1932,34 +1931,34 @@ }, { "name": "lcobucci/clock", - "version": "3.3.1", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/lcobucci/clock.git", - "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b" + "reference": "a3139d9e97d47826f27e6a17bb63f13621f86058" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/clock/zipball/db3713a61addfffd615b79bf0bc22f0ccc61b86b", - "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/a3139d9e97d47826f27e6a17bb63f13621f86058", + "reference": "a3139d9e97d47826f27e6a17bb63f13621f86058", "shasum": "" }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" }, "require-dev": { - "infection/infection": "^0.29", - "lcobucci/coding-standard": "^11.1.0", + "infection/infection": "^0.31", + "lcobucci/coding-standard": "^11.2.0", "phpstan/extension-installer": "^1.3.1", - "phpstan/phpstan": "^1.10.25", - "phpstan/phpstan-deprecation-rules": "^1.1.3", - "phpstan/phpstan-phpunit": "^1.3.13", - "phpstan/phpstan-strict-rules": "^1.5.1", - "phpunit/phpunit": "^11.3.6" + "phpstan/phpstan": "^2.0.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0.0", + "phpstan/phpstan-strict-rules": "^2.0.0", + "phpunit/phpunit": "^12.0.0" }, "type": "library", "autoload": { @@ -1980,7 +1979,7 @@ "description": "Yet another clock abstraction", "support": { "issues": "https://github.com/lcobucci/clock/issues", - "source": "https://github.com/lcobucci/clock/tree/3.3.1" + "source": "https://github.com/lcobucci/clock/tree/3.5.0" }, "funding": [ { @@ -1992,7 +1991,7 @@ "type": "patreon" } ], - "time": "2024-09-24T20:45:14+00:00" + "time": "2025-10-27T09:03:17+00:00" }, { "name": "lcobucci/jwt", @@ -2070,16 +2069,16 @@ }, { "name": "league/commonmark", - "version": "2.7.0", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405" + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/6fbb36d44824ed4091adbcf4c7d4a3923cdb3405", - "reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/10732241927d3971d28e7ea7b5712721fa2296ca", + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca", "shasum": "" }, "require": { @@ -2108,7 +2107,7 @@ "symfony/process": "^5.4 | ^6.0 | ^7.0", "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", "unleashedtech/php-coding-standard": "^3.1.1", - "vimeo/psalm": "^4.24.0 || ^5.0.0" + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" }, "suggest": { "symfony/yaml": "v2.3+ required if using the Front Matter extension" @@ -2173,7 +2172,7 @@ "type": "tidelift" } ], - "time": "2025-05-05T12:20:28+00:00" + "time": "2025-07-20T12:47:49+00:00" }, { "name": "league/config", @@ -2259,16 +2258,16 @@ }, { "name": "league/flysystem", - "version": "3.29.1", + "version": "3.30.2", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319" + "reference": "5966a8ba23e62bdb518dd9e0e665c2dbd4b5b277" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319", - "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/5966a8ba23e62bdb518dd9e0e665c2dbd4b5b277", + "reference": "5966a8ba23e62bdb518dd9e0e665c2dbd4b5b277", "shasum": "" }, "require": { @@ -2292,13 +2291,13 @@ "composer/semver": "^3.0", "ext-fileinfo": "*", "ext-ftp": "*", - "ext-mongodb": "^1.3", + "ext-mongodb": "^1.3|^2", "ext-zip": "*", "friendsofphp/php-cs-fixer": "^3.5", "google/cloud-storage": "^1.23", "guzzlehttp/psr7": "^2.6", "microsoft/azure-storage-blob": "^1.1", - "mongodb/mongodb": "^1.2", + "mongodb/mongodb": "^1.2|^2", "phpseclib/phpseclib": "^3.0.36", "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.5.11|^10.0", @@ -2336,22 +2335,22 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.29.1" + "source": "https://github.com/thephpleague/flysystem/tree/3.30.2" }, - "time": "2024-10-08T08:58:34+00:00" + "time": "2025-11-10T17:13:11+00:00" }, { "name": "league/flysystem-local", - "version": "3.29.0", + "version": "3.30.2", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27" + "reference": "ab4f9d0d672f601b102936aa728801dd1a11968d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e0e8d52ce4b2ed154148453d321e97c8e931bd27", - "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/ab4f9d0d672f601b102936aa728801dd1a11968d", + "reference": "ab4f9d0d672f601b102936aa728801dd1a11968d", "shasum": "" }, "require": { @@ -2385,9 +2384,9 @@ "local" ], "support": { - "source": "https://github.com/thephpleague/flysystem-local/tree/3.29.0" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.30.2" }, - "time": "2024-08-09T21:24:39+00:00" + "time": "2025-11-10T11:23:37+00:00" }, { "name": "league/fractal", @@ -2794,16 +2793,16 @@ }, { "name": "nesbot/carbon", - "version": "3.9.1", + "version": "3.10.3", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "ced71f79398ece168e24f7f7710462f462310d4d" + "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ced71f79398ece168e24f7f7710462f462310d4d", - "reference": "ced71f79398ece168e24f7f7710462f462310d4d", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", + "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", "shasum": "" }, "require": { @@ -2811,9 +2810,9 @@ "ext-json": "*", "php": "^8.1", "psr/clock": "^1.0", - "symfony/clock": "^6.3 || ^7.0", + "symfony/clock": "^6.3.12 || ^7.0", "symfony/polyfill-mbstring": "^1.0", - "symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0" + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -2821,14 +2820,13 @@ "require-dev": { "doctrine/dbal": "^3.6.3 || ^4.0", "doctrine/orm": "^2.15.2 || ^3.0", - "friendsofphp/php-cs-fixer": "^3.57.2", + "friendsofphp/php-cs-fixer": "^v3.87.1", "kylekatarnls/multi-tester": "^2.5.3", - "ondrejmirtes/better-reflection": "^6.25.0.4", "phpmd/phpmd": "^2.15.0", - "phpstan/extension-installer": "^1.3.1", - "phpstan/phpstan": "^1.11.2", - "phpunit/phpunit": "^10.5.20", - "squizlabs/php_codesniffer": "^3.9.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4" }, "bin": [ "bin/carbon" @@ -2896,29 +2894,29 @@ "type": "tidelift" } ], - "time": "2025-05-01T19:51:51+00:00" + "time": "2025-09-06T13:39:36+00:00" }, { "name": "nette/schema", - "version": "v1.3.2", + "version": "v1.3.3", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "url": "https://api.github.com/repos/nette/schema/zipball/2befc2f42d7c715fd9d95efc31b1081e5d765004", + "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004", "shasum": "" }, "require": { "nette/utils": "^4.0", - "php": "8.1 - 8.4" + "php": "8.1 - 8.5" }, "require-dev": { "nette/tester": "^2.5.2", - "phpstan/phpstan-nette": "^1.0", + "phpstan/phpstan-nette": "^2.0@stable", "tracy/tracy": "^2.8" }, "type": "library", @@ -2928,6 +2926,9 @@ } }, "autoload": { + "psr-4": { + "Nette\\": "src" + }, "classmap": [ "src/" ] @@ -2956,35 +2957,35 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.2" + "source": "https://github.com/nette/schema/tree/v1.3.3" }, - "time": "2024-10-06T23:10:23+00:00" + "time": "2025-10-30T22:57:59+00:00" }, { "name": "nette/utils", - "version": "v4.0.7", + "version": "v4.0.8", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2" + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/e67c4061eb40b9c113b218214e42cb5a0dda28f2", - "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2", + "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede", "shasum": "" }, "require": { - "php": "8.0 - 8.4" + "php": "8.0 - 8.5" }, "conflict": { "nette/finder": "<3", "nette/schema": "<1.2.2" }, "require-dev": { - "jetbrains/phpstorm-attributes": "dev-master", + "jetbrains/phpstorm-attributes": "^1.2", "nette/tester": "^2.5", - "phpstan/phpstan": "^1.0", + "phpstan/phpstan-nette": "^2.0@stable", "tracy/tracy": "^2.9" }, "suggest": { @@ -3002,6 +3003,9 @@ } }, "autoload": { + "psr-4": { + "Nette\\": "src" + }, "classmap": [ "src/" ] @@ -3042,22 +3046,22 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.7" + "source": "https://github.com/nette/utils/tree/v4.0.8" }, - "time": "2025-06-03T04:55:08+00:00" + "time": "2025-08-06T21:43:34+00:00" }, { "name": "nikic/php-parser", - "version": "v5.5.0", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", "shasum": "" }, "require": { @@ -3076,7 +3080,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -3100,37 +3104,37 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" }, - "time": "2025-05-31T08:24:38+00:00" + "time": "2025-10-21T19:32:17+00:00" }, { "name": "nunomaduro/termwind", - "version": "v2.3.1", + "version": "v2.3.2", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "dfa08f390e509967a15c22493dc0bac5733d9123" + "reference": "eb61920a53057a7debd718a5b89c2178032b52c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/dfa08f390e509967a15c22493dc0bac5733d9123", - "reference": "dfa08f390e509967a15c22493dc0bac5733d9123", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/eb61920a53057a7debd718a5b89c2178032b52c0", + "reference": "eb61920a53057a7debd718a5b89c2178032b52c0", "shasum": "" }, "require": { "ext-mbstring": "*", "php": "^8.2", - "symfony/console": "^7.2.6" + "symfony/console": "^7.3.4" }, "require-dev": { - "illuminate/console": "^11.44.7", - "laravel/pint": "^1.22.0", + "illuminate/console": "^11.46.1", + "laravel/pint": "^1.25.1", "mockery/mockery": "^1.6.12", - "pestphp/pest": "^2.36.0 || ^3.8.2", - "phpstan/phpstan": "^1.12.25", + "pestphp/pest": "^2.36.0 || ^3.8.4", + "phpstan/phpstan": "^1.12.32", "phpstan/phpstan-strict-rules": "^1.6.2", - "symfony/var-dumper": "^7.2.6", + "symfony/var-dumper": "^7.3.4", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -3173,7 +3177,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.3.1" + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.2" }, "funding": [ { @@ -3189,20 +3193,20 @@ "type": "github" } ], - "time": "2025-05-08T08:14:37+00:00" + "time": "2025-10-18T11:10:27+00:00" }, { "name": "phpoption/phpoption", - "version": "1.9.3", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", "shasum": "" }, "require": { @@ -3210,7 +3214,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" }, "type": "library", "extra": { @@ -3252,7 +3256,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" }, "funding": [ { @@ -3264,7 +3268,7 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:41:07+00:00" + "time": "2025-08-21T11:53:16+00:00" }, { "name": "psr/cache", @@ -3729,16 +3733,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.8", + "version": "v0.12.14", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "85057ceedee50c49d4f6ecaff73ee96adb3b3625" + "reference": "95c29b3756a23855a30566b745d218bee690bef2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/85057ceedee50c49d4f6ecaff73ee96adb3b3625", - "reference": "85057ceedee50c49d4f6ecaff73ee96adb3b3625", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/95c29b3756a23855a30566b745d218bee690bef2", + "reference": "95c29b3756a23855a30566b745d218bee690bef2", "shasum": "" }, "require": { @@ -3753,11 +3757,12 @@ "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.2" + "bamarni/composer-bin-plugin": "^1.2", + "composer/class-map-generator": "^1.6" }, "suggest": { + "composer/class-map-generator": "Improved tab completion performance with better class discovery.", "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-pdo-sqlite": "The doc command requires SQLite to work.", "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." }, "bin": [ @@ -3788,12 +3793,11 @@ "authors": [ { "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" + "email": "justin@justinhileman.info" } ], "description": "An interactive shell for modern PHP.", - "homepage": "http://psysh.org", + "homepage": "https://psysh.org", "keywords": [ "REPL", "console", @@ -3802,9 +3806,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.8" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.14" }, - "time": "2025-03-16T03:05:19+00:00" + "time": "2025-10-27T17:15:31+00:00" }, { "name": "ralouphie/getallheaders", @@ -3928,21 +3932,20 @@ }, { "name": "ramsey/uuid", - "version": "4.8.1", + "version": "4.9.1", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28" + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28", - "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", - "ext-json": "*", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -4001,22 +4004,22 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.8.1" + "source": "https://github.com/ramsey/uuid/tree/4.9.1" }, - "time": "2025-06-01T06:28:46+00:00" + "time": "2025-09-04T20:59:21+00:00" }, { "name": "swagger-api/swagger-ui", - "version": "v5.22.0", + "version": "v5.30.2", "source": { "type": "git", "url": "https://github.com/swagger-api/swagger-ui.git", - "reference": "4b37bf2a25a8d82fb0092b25d8108d1bb4171181" + "reference": "30d8f985caeca5bb9400cf21403686ec5a82c1cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/4b37bf2a25a8d82fb0092b25d8108d1bb4171181", - "reference": "4b37bf2a25a8d82fb0092b25d8108d1bb4171181", + "url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/30d8f985caeca5bb9400cf21403686ec5a82c1cc", + "reference": "30d8f985caeca5bb9400cf21403686ec5a82c1cc", "shasum": "" }, "type": "library", @@ -4062,9 +4065,9 @@ ], "support": { "issues": "https://github.com/swagger-api/swagger-ui/issues", - "source": "https://github.com/swagger-api/swagger-ui/tree/v5.22.0" + "source": "https://github.com/swagger-api/swagger-ui/tree/v5.30.2" }, - "time": "2025-05-21T12:44:47+00:00" + "time": "2025-11-04T09:41:42+00:00" }, { "name": "symfony/clock", @@ -4142,16 +4145,16 @@ }, { "name": "symfony/console", - "version": "v7.3.0", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44" + "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/66c1440edf6f339fd82ed6c7caa76cb006211b44", - "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44", + "url": "https://api.github.com/repos/symfony/console/zipball/c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", + "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", "shasum": "" }, "require": { @@ -4216,7 +4219,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.0" + "source": "https://github.com/symfony/console/tree/v7.3.6" }, "funding": [ { @@ -4227,25 +4230,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-24T10:34:04+00:00" + "time": "2025-11-04T01:21:42+00:00" }, { "name": "symfony/css-selector", - "version": "v7.3.0", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + "reference": "84321188c4754e64273b46b406081ad9b18e8614" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/84321188c4754e64273b46b406081ad9b18e8614", + "reference": "84321188c4754e64273b46b406081ad9b18e8614", "shasum": "" }, "require": { @@ -4281,7 +4288,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.3.0" + "source": "https://github.com/symfony/css-selector/tree/v7.3.6" }, "funding": [ { @@ -4292,12 +4299,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2025-10-29T17:24:25+00:00" }, { "name": "symfony/deprecation-contracts", @@ -4368,16 +4379,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.3.0", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "cf68d225bc43629de4ff54778029aee6dc191b83" + "reference": "bbe40bfab84323d99dab491b716ff142410a92a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/cf68d225bc43629de4ff54778029aee6dc191b83", - "reference": "cf68d225bc43629de4ff54778029aee6dc191b83", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/bbe40bfab84323d99dab491b716ff142410a92a8", + "reference": "bbe40bfab84323d99dab491b716ff142410a92a8", "shasum": "" }, "require": { @@ -4425,7 +4436,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.3.0" + "source": "https://github.com/symfony/error-handler/tree/v7.3.6" }, "funding": [ { @@ -4436,25 +4447,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-29T07:19:49+00:00" + "time": "2025-10-31T19:12:50+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.3.0", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d" + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d", - "reference": "497f73ac996a598c92409b44ac43b6690c4f666d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", "shasum": "" }, "require": { @@ -4505,7 +4520,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" }, "funding": [ { @@ -4516,12 +4531,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-22T09:11:45+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -4601,16 +4620,16 @@ }, { "name": "symfony/finder", - "version": "v7.3.0", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d" + "reference": "9f696d2f1e340484b4683f7853b273abff94421f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d", - "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d", + "url": "https://api.github.com/repos/symfony/finder/zipball/9f696d2f1e340484b4683f7853b273abff94421f", + "reference": "9f696d2f1e340484b4683f7853b273abff94421f", "shasum": "" }, "require": { @@ -4645,7 +4664,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.0" + "source": "https://github.com/symfony/finder/tree/v7.3.5" }, "funding": [ { @@ -4656,25 +4675,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-30T19:00:26+00:00" + "time": "2025-10-15T18:45:57+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.3.0", + "version": "v7.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "4236baf01609667d53b20371486228231eb135fd" + "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4236baf01609667d53b20371486228231eb135fd", - "reference": "4236baf01609667d53b20371486228231eb135fd", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/db488a62f98f7a81d5746f05eea63a74e55bb7c4", + "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4", "shasum": "" }, "require": { @@ -4724,7 +4747,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.0" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.7" }, "funding": [ { @@ -4735,25 +4758,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-12T14:48:23+00:00" + "time": "2025-11-08T16:41:12+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.0", + "version": "v7.3.7", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "ac7b8e163e8c83dce3abcc055a502d4486051a9f" + "reference": "10b8e9b748ea95fa4539c208e2487c435d3c87ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/ac7b8e163e8c83dce3abcc055a502d4486051a9f", - "reference": "ac7b8e163e8c83dce3abcc055a502d4486051a9f", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/10b8e9b748ea95fa4539c208e2487c435d3c87ce", + "reference": "10b8e9b748ea95fa4539c208e2487c435d3c87ce", "shasum": "" }, "require": { @@ -4838,7 +4865,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.0" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.7" }, "funding": [ { @@ -4849,25 +4876,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-29T07:47:32+00:00" + "time": "2025-11-12T11:38:40+00:00" }, { "name": "symfony/mailer", - "version": "v7.3.0", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "0f375bbbde96ae8c78e4aa3e63aabd486e33364c" + "reference": "fd497c45ba9c10c37864e19466b090dcb60a50ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/0f375bbbde96ae8c78e4aa3e63aabd486e33364c", - "reference": "0f375bbbde96ae8c78e4aa3e63aabd486e33364c", + "url": "https://api.github.com/repos/symfony/mailer/zipball/fd497c45ba9c10c37864e19466b090dcb60a50ba", + "reference": "fd497c45ba9c10c37864e19466b090dcb60a50ba", "shasum": "" }, "require": { @@ -4918,7 +4949,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.3.0" + "source": "https://github.com/symfony/mailer/tree/v7.3.5" }, "funding": [ { @@ -4929,25 +4960,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-04T09:51:09+00:00" + "time": "2025-10-24T14:27:20+00:00" }, { "name": "symfony/mime", - "version": "v7.3.0", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "0e7b19b2f399c31df0cdbe5d8cbf53f02f6cfcd9" + "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/0e7b19b2f399c31df0cdbe5d8cbf53f02f6cfcd9", - "reference": "0e7b19b2f399c31df0cdbe5d8cbf53f02f6cfcd9", + "url": "https://api.github.com/repos/symfony/mime/zipball/b1b828f69cbaf887fa835a091869e55df91d0e35", + "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35", "shasum": "" }, "require": { @@ -5002,7 +5037,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.3.0" + "source": "https://github.com/symfony/mime/tree/v7.3.4" }, "funding": [ { @@ -5013,16 +5048,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-19T08:51:26+00:00" + "time": "2025-09-16T08:38:17+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -5081,7 +5120,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -5092,6 +5131,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5101,16 +5144,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -5159,7 +5202,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -5170,16 +5213,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -5242,7 +5289,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, "funding": [ { @@ -5253,6 +5300,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5262,7 +5313,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -5323,7 +5374,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -5334,6 +5385,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5343,7 +5398,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -5404,7 +5459,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -5415,6 +5470,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5424,7 +5483,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -5484,7 +5543,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -5495,6 +5554,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5504,16 +5567,16 @@ }, { "name": "symfony/polyfill-php83", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { @@ -5560,7 +5623,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, "funding": [ { @@ -5571,16 +5634,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-07-08T02:45:35+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", @@ -5639,7 +5706,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" }, "funding": [ { @@ -5650,6 +5717,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5659,16 +5730,16 @@ }, { "name": "symfony/process", - "version": "v7.3.0", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", - "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", "shasum": "" }, "require": { @@ -5700,7 +5771,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.0" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -5711,25 +5782,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-17T09:11:12+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/routing", - "version": "v7.3.0", + "version": "v7.3.6", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "8e213820c5fea844ecea29203d2a308019007c15" + "reference": "c97abe725f2a1a858deca629a6488c8fc20c3091" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/8e213820c5fea844ecea29203d2a308019007c15", - "reference": "8e213820c5fea844ecea29203d2a308019007c15", + "url": "https://api.github.com/repos/symfony/routing/zipball/c97abe725f2a1a858deca629a6488c8fc20c3091", + "reference": "c97abe725f2a1a858deca629a6488c8fc20c3091", "shasum": "" }, "require": { @@ -5781,7 +5856,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.3.0" + "source": "https://github.com/symfony/routing/tree/v7.3.6" }, "funding": [ { @@ -5792,25 +5867,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-24T20:43:28+00:00" + "time": "2025-11-05T07:57:47+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -5864,7 +5943,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -5875,25 +5954,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/string", - "version": "v7.3.0", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -5908,7 +5991,6 @@ }, "require-dev": { "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -5951,7 +6033,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.0" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -5962,25 +6044,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-20T20:19:01+00:00" + "time": "2025-09-11T14:36:48+00:00" }, { "name": "symfony/translation", - "version": "v7.3.0", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "4aba29076a29a3aa667e09b791e5f868973a8667" + "reference": "ec25870502d0c7072d086e8ffba1420c85965174" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/4aba29076a29a3aa667e09b791e5f868973a8667", - "reference": "4aba29076a29a3aa667e09b791e5f868973a8667", + "url": "https://api.github.com/repos/symfony/translation/zipball/ec25870502d0c7072d086e8ffba1420c85965174", + "reference": "ec25870502d0c7072d086e8ffba1420c85965174", "shasum": "" }, "require": { @@ -6047,7 +6133,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.3.0" + "source": "https://github.com/symfony/translation/tree/v7.3.4" }, "funding": [ { @@ -6058,25 +6144,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-29T07:19:49+00:00" + "time": "2025-09-07T11:39:36+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" + "reference": "65a8bc82080447fae78373aa10f8d13b38338977" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977", "shasum": "" }, "require": { @@ -6125,7 +6215,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" }, "funding": [ { @@ -6136,25 +6226,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-27T08:32:26+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/uid", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "7beeb2b885cd584cd01e126c5777206ae4c3c6a3" + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/7beeb2b885cd584cd01e126c5777206ae4c3c6a3", - "reference": "7beeb2b885cd584cd01e126c5777206ae4c3c6a3", + "url": "https://api.github.com/repos/symfony/uid/zipball/a69f69f3159b852651a6bf45a9fdd149520525bb", + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb", "shasum": "" }, "require": { @@ -6199,7 +6293,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.3.0" + "source": "https://github.com/symfony/uid/tree/v7.3.1" }, "funding": [ { @@ -6215,20 +6309,20 @@ "type": "tidelift" } ], - "time": "2025-05-24T14:28:13+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.3.0", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "548f6760c54197b1084e1e5c71f6d9d523f2f78e" + "reference": "476c4ae17f43a9a36650c69879dcf5b1e6ae724d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/548f6760c54197b1084e1e5c71f6d9d523f2f78e", - "reference": "548f6760c54197b1084e1e5c71f6d9d523f2f78e", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/476c4ae17f43a9a36650c69879dcf5b1e6ae724d", + "reference": "476c4ae17f43a9a36650c69879dcf5b1e6ae724d", "shasum": "" }, "require": { @@ -6240,7 +6334,6 @@ "symfony/console": "<6.4" }, "require-dev": { - "ext-iconv": "*", "symfony/console": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/process": "^6.4|^7.0", @@ -6283,7 +6376,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.0" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.5" }, "funding": [ { @@ -6294,25 +6387,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-27T18:39:23+00:00" + "time": "2025-09-27T09:00:46+00:00" }, { "name": "symfony/yaml", - "version": "v7.3.0", + "version": "v7.3.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "cea40a48279d58dc3efee8112634cb90141156c2" + "reference": "90208e2fc6f68f613eae7ca25a2458a931b1bacc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/cea40a48279d58dc3efee8112634cb90141156c2", - "reference": "cea40a48279d58dc3efee8112634cb90141156c2", + "url": "https://api.github.com/repos/symfony/yaml/zipball/90208e2fc6f68f613eae7ca25a2458a931b1bacc", + "reference": "90208e2fc6f68f613eae7ca25a2458a931b1bacc", "shasum": "" }, "require": { @@ -6355,7 +6452,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.0" + "source": "https://github.com/symfony/yaml/tree/v7.3.5" }, "funding": [ { @@ -6366,12 +6463,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-04T10:10:33+00:00" + "time": "2025-09-27T09:00:46+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -6670,64 +6771,6 @@ ], "time": "2024-11-21T01:49:47+00:00" }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, { "name": "zircote/swagger-php", "version": "4.11.1", @@ -6813,29 +6856,29 @@ "packages-dev": [ { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v1.0.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/composer-installer.git", - "reference": "4be43904336affa5c2f70744a348312336afd0da" + "reference": "845eb62303d2ca9b289ef216356568ccc075ffd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", - "reference": "4be43904336affa5c2f70744a348312336afd0da", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1", + "reference": "845eb62303d2ca9b289ef216356568ccc075ffd1", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0 || ^2.0", + "composer-plugin-api": "^2.2", "php": ">=5.4", - "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + "squizlabs/php_codesniffer": "^3.1.0 || ^4.0" }, "require-dev": { - "composer/composer": "*", + "composer/composer": "^2.2", "ext-json": "*", "ext-zip": "*", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev", "yoast/phpunit-polyfills": "^1.0" }, "type": "composer-plugin", @@ -6854,9 +6897,9 @@ "authors": [ { "name": "Franck Nijhof", - "email": "franck.nijhof@dealerdirect.com", - "homepage": "http://www.frenck.nl", - "role": "Developer / IT Manager" + "email": "opensource@frenck.dev", + "homepage": "https://frenck.dev", + "role": "Open source developer" }, { "name": "Contributors", @@ -6864,7 +6907,6 @@ } ], "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "homepage": "http://www.dealerdirect.com", "keywords": [ "PHPCodeSniffer", "PHP_CodeSniffer", @@ -6885,22 +6927,41 @@ ], "support": { "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", "source": "https://github.com/PHPCSStandards/composer-installer" }, - "time": "2023-01-05T11:28:13+00:00" + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-11-11T04:32:07+00:00" }, { "name": "filp/whoops", - "version": "2.18.1", + "version": "2.18.4", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26" + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26", - "reference": "8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", "shasum": "" }, "require": { @@ -6950,7 +7011,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.1" + "source": "https://github.com/filp/whoops/tree/2.18.4" }, "funding": [ { @@ -6958,7 +7019,7 @@ "type": "github" } ], - "time": "2025-06-03T18:56:14+00:00" + "time": "2025-08-08T12:00:00+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -7013,16 +7074,16 @@ }, { "name": "laravel/sail", - "version": "v1.43.1", + "version": "v1.48.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "3e7d899232a8c5e3ea4fc6dee7525ad583887e72" + "reference": "1bf3b8870b72a258a3b6b5119435835ece522e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/3e7d899232a8c5e3ea4fc6dee7525ad583887e72", - "reference": "3e7d899232a8c5e3ea4fc6dee7525ad583887e72", + "url": "https://api.github.com/repos/laravel/sail/zipball/1bf3b8870b72a258a3b6b5119435835ece522e8a", + "reference": "1bf3b8870b72a258a3b6b5119435835ece522e8a", "shasum": "" }, "require": { @@ -7035,7 +7096,7 @@ }, "require-dev": { "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", - "phpstan/phpstan": "^1.10" + "phpstan/phpstan": "^2.0" }, "bin": [ "bin/sail" @@ -7072,7 +7133,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2025-05-19T13:19:21+00:00" + "time": "2025-11-09T14:46:21+00:00" }, { "name": "mockery/mockery", @@ -7159,16 +7220,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -7207,7 +7268,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -7215,27 +7276,27 @@ "type": "tidelift" } ], - "time": "2025-04-29T12:36:36+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nunomaduro/collision", - "version": "v8.8.0", + "version": "v8.8.2", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8" + "reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/4cf9f3b47afff38b139fb79ce54fc71799022ce8", - "reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/60207965f9b7b7a4ce15a0f75d57f9dadb105bdb", + "reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb", "shasum": "" }, "require": { - "filp/whoops": "^2.18.0", - "nunomaduro/termwind": "^2.3.0", + "filp/whoops": "^2.18.1", + "nunomaduro/termwind": "^2.3.1", "php": "^8.2.0", - "symfony/console": "^7.2.5" + "symfony/console": "^7.3.0" }, "conflict": { "laravel/framework": "<11.44.2 || >=13.0.0", @@ -7243,15 +7304,15 @@ }, "require-dev": { "brianium/paratest": "^7.8.3", - "larastan/larastan": "^3.2", - "laravel/framework": "^11.44.2 || ^12.6", - "laravel/pint": "^1.21.2", - "laravel/sail": "^1.41.0", - "laravel/sanctum": "^4.0.8", + "larastan/larastan": "^3.4.2", + "laravel/framework": "^11.44.2 || ^12.18", + "laravel/pint": "^1.22.1", + "laravel/sail": "^1.43.1", + "laravel/sanctum": "^4.1.1", "laravel/tinker": "^2.10.1", - "orchestra/testbench-core": "^9.12.0 || ^10.1", - "pestphp/pest": "^3.8.0", - "sebastian/environment": "^7.2.0 || ^8.0" + "orchestra/testbench-core": "^9.12.0 || ^10.4", + "pestphp/pest": "^3.8.2", + "sebastian/environment": "^7.2.1 || ^8.0" }, "type": "library", "extra": { @@ -7314,7 +7375,7 @@ "type": "patreon" } ], - "time": "2025-04-03T14:33:09+00:00" + "time": "2025-06-25T02:12:12+00:00" }, { "name": "phar-io/manifest", @@ -7436,16 +7497,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "11.0.9", + "version": "11.0.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "14d63fbcca18457e49c6f8bebaa91a87e8e188d7" + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/14d63fbcca18457e49c6f8bebaa91a87e8e188d7", - "reference": "14d63fbcca18457e49c6f8bebaa91a87e8e188d7", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", "shasum": "" }, "require": { @@ -7502,15 +7563,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.9" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2025-02-25T13:26:39+00:00" + "time": "2025-08-27T14:37:49+00:00" }, { "name": "phpunit/php-file-iterator", @@ -7759,16 +7832,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.21", + "version": "11.5.44", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d565e2cdc21a7db9dc6c399c1fc2083b8010f289" + "reference": "c346885c95423eda3f65d85a194aaa24873cda82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d565e2cdc21a7db9dc6c399c1fc2083b8010f289", - "reference": "d565e2cdc21a7db9dc6c399c1fc2083b8010f289", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c346885c95423eda3f65d85a194aaa24873cda82", + "reference": "c346885c95423eda3f65d85a194aaa24873cda82", "shasum": "" }, "require": { @@ -7778,24 +7851,24 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.9", + "phpunit/php-code-coverage": "^11.0.11", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", "sebastian/code-unit": "^3.0.3", - "sebastian/comparator": "^6.3.1", + "sebastian/comparator": "^6.3.2", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.1", - "sebastian/exporter": "^6.3.0", + "sebastian/exporter": "^6.3.2", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", - "sebastian/type": "^5.1.2", + "sebastian/type": "^5.1.3", "sebastian/version": "^5.0.2", "staabm/side-effects-detector": "^1.0.5" }, @@ -7840,7 +7913,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.21" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.44" }, "funding": [ { @@ -7864,7 +7937,7 @@ "type": "tidelift" } ], - "time": "2025-05-21T12:35:00+00:00" + "time": "2025-11-13T07:17:35+00:00" }, { "name": "sebastian/cli-parser", @@ -8038,16 +8111,16 @@ }, { "name": "sebastian/comparator", - "version": "6.3.1", + "version": "6.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959" + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/24b8fbc2c8e201bb1308e7b05148d6ab393b6959", - "reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", "shasum": "" }, "require": { @@ -8106,15 +8179,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.1" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2025-03-07T06:57:01+00:00" + "time": "2025-08-10T08:07:46+00:00" }, { "name": "sebastian/complexity", @@ -8319,16 +8404,16 @@ }, { "name": "sebastian/exporter", - "version": "6.3.0", + "version": "6.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", - "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", "shasum": "" }, "require": { @@ -8342,7 +8427,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -8385,15 +8470,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-12-05T09:17:50+00:00" + "time": "2025-09-24T06:12:51+00:00" }, { "name": "sebastian/global-state", @@ -8631,23 +8728,23 @@ }, { "name": "sebastian/recursion-context", - "version": "6.0.2", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { @@ -8683,28 +8780,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2024-07-03T05:10:34+00:00" + "time": "2025-08-13T04:42:22+00:00" }, { "name": "sebastian/type", - "version": "5.1.2", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "a8a7e30534b0eb0c77cd9d07e82de1a114389f5e" + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/a8a7e30534b0eb0c77cd9d07e82de1a114389f5e", - "reference": "a8a7e30534b0eb0c77cd9d07e82de1a114389f5e", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", "shasum": "" }, "require": { @@ -8740,15 +8849,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/5.1.2" + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" } ], - "time": "2025-03-18T13:35:50+00:00" + "time": "2025-08-09T06:55:48+00:00" }, { "name": "sebastian/version", @@ -8806,37 +8927,32 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.13.0", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "65ff2489553b83b4597e89c3b8b721487011d186" + "reference": "0525c73950de35ded110cffafb9892946d7771b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/65ff2489553b83b4597e89c3b8b721487011d186", - "reference": "65ff2489553b83b4597e89c3b8b721487011d186", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0525c73950de35ded110cffafb9892946d7771b5", + "reference": "0525c73950de35ded110cffafb9892946d7771b5", "shasum": "" }, "require": { "ext-simplexml": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": ">=5.4.0" + "php": ">=7.2.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + "phpunit/phpunit": "^8.4.0 || ^9.3.4 || ^10.5.32 || 11.3.3 - 11.5.28 || ^11.5.31" }, "bin": [ "bin/phpcbf", "bin/phpcs" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" @@ -8855,7 +8971,7 @@ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" } ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "description": "PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.", "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", "keywords": [ "phpcs", @@ -8886,7 +9002,7 @@ "type": "thanks_dev" } ], - "time": "2025-05-11T03:36:00+00:00" + "time": "2025-11-10T16:43:36+00:00" }, { "name": "staabm/side-effects-detector", @@ -9003,5 +9119,5 @@ "platform-overrides": { "php": "8.3.6" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..1a01f0a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "html", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/readme.md b/readme.md index 29e0da7..41b62d3 100644 --- a/readme.md +++ b/readme.md @@ -76,6 +76,9 @@ Data API uses [Laravel Sail](https://laravel.com/docs/10.x/sail) for a local dev With [Docker](https://www.docker.com/) installed, run `./vendor/bin/sail up` to build and start the VM. +Alternativly, you can use Podman instead of docker with some additional configuration; good info on this +in [this medium article](https://medium.com/mamitech/the-shortest-path-to-replace-docker-with-podman-for-laravel-sail-b02d184a1b72) + Once Sail informs you of the IP address it is using, add the following entries to your hosts file: ``` # Laravel Sail From 05df04d116f15cfac2844f95c1d4c0ff7104a5c0 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Mon, 17 Nov 2025 12:48:36 -0800 Subject: [PATCH 02/45] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20Add=20Permissions?= =?UTF-8?q?=20DB=20and=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add database and UI structure to set and store permissions on 'clients'. Note that gates are not yet configured, so these currently have no effect! --- .composer/.htaccess | 1 + .composer/cache/.htaccess | 1 + app/Http/Controllers/AdminController.php | 24 ++++++++-- app/Models/Client.php | 19 ++++++++ app/Models/Permission.php | 21 +++++++++ composer.json | 13 ++--- composer.lock | 18 +++---- ..._11_14_231424_create_permissions_table.php | 29 ++++++++++++ ..._233716_create_client_permission_table.php | 42 +++++++++++++++++ database/seeders/PermissionSeeder.php | 27 +++++++++++ resources/views/admin/addclient.blade.php | 47 +++++++++++++------ resources/views/admin/index.blade.php | 12 ++++- 12 files changed, 217 insertions(+), 37 deletions(-) create mode 100644 .composer/.htaccess create mode 100644 .composer/cache/.htaccess create mode 100644 app/Models/Permission.php create mode 100644 database/migrations/2025_11_14_231424_create_permissions_table.php create mode 100644 database/migrations/2025_11_14_233716_create_client_permission_table.php create mode 100644 database/seeders/PermissionSeeder.php diff --git a/.composer/.htaccess b/.composer/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/.composer/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/.composer/cache/.htaccess b/.composer/cache/.htaccess new file mode 100644 index 0000000..14249c5 --- /dev/null +++ b/.composer/cache/.htaccess @@ -0,0 +1 @@ +Deny from all \ No newline at end of file diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index 6458cbf..f9ecea1 100644 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -7,6 +7,9 @@ use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Hash; use App\Models\Client; +use App\Models\Permission; + + class AdminController extends Controller { @@ -18,6 +21,7 @@ public function __construct() public function index() { $client_data = Client::all(); + //dd($client_data); return view('admin.index', ['data' => $client_data ]); } @@ -67,7 +71,10 @@ public function deleteClient($id){ } public function addClientShow() { - return view('admin.addclient'); + // Get available permissions + $permissions = Permission::all(); + //dd($permissions); + return view('admin.addclient', compact('permissions')); } public function addClientPost(Request $request) { @@ -77,7 +84,7 @@ public function addClientPost(Request $request) { 'clienturl' => 'required' ]); - $form_input = $request->only('clientname', 'clienturl'); + $form_input = $request->only('clientname', 'clienturl', 'permissions'); $new_clientkey = Client::generateClientKey(); @@ -89,14 +96,23 @@ public function addClientPost(Request $request) { try { $newclient->save(); + + // Sync permissions after the client is saved + if (isset($form_input['permissions'])) { + $newclient->syncPermissionsByName($form_input['permissions']); + } else { + $newclient->permissions()->detach(); + } } catch ( \Exception $e ) { return redirect()->back()->withError("There was an error while adding the client."); } + //dd($newclient); return view('admin.addclient')->with('success', true) ->with('clientname', $newclient->clientname) ->with('clientid', $newclient->clientid) - ->with('clientkey', $new_clientkey); + ->with('clientkey', $new_clientkey) + ->with('set_permissions', $form_input['permissions'] ?? []); } -} \ No newline at end of file +} diff --git a/app/Models/Client.php b/app/Models/Client.php index 3b30e1c..8e963b0 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -5,6 +5,7 @@ use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Support\Str; +use App\Models\Permission; class Client extends Authenticatable implements JWTSubject { use Notifiable; @@ -41,4 +42,22 @@ public static function generateClientKey() { return (string) Str::uuid(); } + + // Configure relationship with permissions + public function permissions() + { + return $this->belongsToMany(Permission::class, 'client_permission'); + } + + /** + * Sync client permissions by permission names + * + * @param array $permissionNames Array of permission names to sync + * @return array + */ + public function syncPermissionsByName(array $permissionNames) + { + $permissions = Permission::whereIn('name', $permissionNames)->pluck('id'); + return $this->permissions()->sync($permissions); + } } \ No newline at end of file diff --git a/app/Models/Permission.php b/app/Models/Permission.php new file mode 100644 index 0000000..37d2a20 --- /dev/null +++ b/app/Models/Permission.php @@ -0,0 +1,21 @@ +belongsToMany(Client::class, 'client_permission'); + } +} diff --git a/composer.json b/composer.json index 64ef23e..2037c0c 100644 --- a/composer.json +++ b/composer.json @@ -25,17 +25,12 @@ "phpunit/phpunit": "^11.5" }, "autoload": { - "autoload": { - "psr-4": { - "App\\": "app/", - "Database\\Factories\\": "database/factories/", - "Database\\Seeders\\": "database/seeders/" - } - }, "psr-4": { - "App\\": "app/" + "App\\": "app/", + "Database\\Factories\\": "database/factories/", + "Database\\Seeders\\": "database/seeders/" } - }, + }, "autoload-dev": { "psr-4": { "Tests\\": "tests/" diff --git a/composer.lock b/composer.lock index b207df4..63caa58 100644 --- a/composer.lock +++ b/composer.lock @@ -9058,16 +9058,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/d74205c497bfbca49f34d4bc4c19c17e22db4ebb", + "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb", "shasum": "" }, "require": { @@ -9096,7 +9096,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.0" }, "funding": [ { @@ -9104,20 +9104,20 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-13T13:44:09+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": { "php": "^8.3.6" }, - "platform-dev": {}, + "platform-dev": [], "platform-overrides": { "php": "8.3.6" }, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.3.0" } diff --git a/database/migrations/2025_11_14_231424_create_permissions_table.php b/database/migrations/2025_11_14_231424_create_permissions_table.php new file mode 100644 index 0000000..eac98fb --- /dev/null +++ b/database/migrations/2025_11_14_231424_create_permissions_table.php @@ -0,0 +1,29 @@ +create('permissions', function (Blueprint $table) { + $table->id(); + $table->string('name'); + $table->string('description')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::connection('da')->dropIfExists('permissions'); + } +}; diff --git a/database/migrations/2025_11_14_233716_create_client_permission_table.php b/database/migrations/2025_11_14_233716_create_client_permission_table.php new file mode 100644 index 0000000..31de265 --- /dev/null +++ b/database/migrations/2025_11_14_233716_create_client_permission_table.php @@ -0,0 +1,42 @@ +create('client_permission', function (Blueprint $table) { + $table->unsignedInteger('client_id'); + $table->unsignedBigInteger('permission_id'); + + // Now add the foreign key constraints + $table->foreign('client_id') + ->references('id') + ->on('Clients') + ->cascadeOnDelete(); + + $table->foreign('permission_id') + ->references('id') + ->on('permissions') + ->cascadeOnDelete(); + + $table->primary(['client_id', 'permission_id']); + }); + } + + + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::connection('da')->dropIfExists('client_permission'); + } +}; diff --git a/database/seeders/PermissionSeeder.php b/database/seeders/PermissionSeeder.php new file mode 100644 index 0000000..c2bfa99 --- /dev/null +++ b/database/seeders/PermissionSeeder.php @@ -0,0 +1,27 @@ + 'read-student-data', 'description' => 'Can view student records'], + ['name' => 'read-employee-data', 'description' => 'Can view employee records'], + ['name' => 'read-directory-data', 'description' => 'Can view employee directory records'], + ['name' => 'write-transaction-data', 'description' => 'Can create and update transaction records'], + ]; + + foreach ($permissions as $permission) { + Permission::create($permission); + } + } +} diff --git a/resources/views/admin/addclient.blade.php b/resources/views/admin/addclient.blade.php index b9f54c9..cb8b224 100644 --- a/resources/views/admin/addclient.blade.php +++ b/resources/views/admin/addclient.blade.php @@ -10,8 +10,8 @@ New client {{$clientname}} added successfully. The client will use the following ID and key to authenticate against the API. Note the key now as it is not retrievable. @endif @@ -24,17 +24,36 @@ @endif -
- @csrf -
- - -
-
- - -
- -
+ @if ( empty($success) ) + +
+ @csrf +
+ + +
+
+ + +
+
+
+ Permissions + + @if (isset($permissions) && count($permissions) > 0) + @foreach($permissions as $permission) + {{ dd($permission) }} +
+ + +
+ @endforeach + @endif +
+ + + @endif @endsection \ No newline at end of file diff --git a/resources/views/admin/index.blade.php b/resources/views/admin/index.blade.php index 2c2a362..2c8a57c 100644 --- a/resources/views/admin/index.blade.php +++ b/resources/views/admin/index.blade.php @@ -20,7 +20,8 @@ Client name Client id - + Permissions + Actions @@ -28,6 +29,15 @@ {{ $client->clientname }} {{ $client->clientid }} + + @if (isset($client->permissions) && count($client->permissions) > 0) + @foreach($client->permissions as $permission) + {{ $permission->name }} + @endforeach + @else + No permissions + @endif + Delete From 702dfbb5384f6ece79755a3b198f75ebb46fc85e Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Mon, 17 Nov 2025 13:32:18 -0800 Subject: [PATCH 03/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Upgrade=20Boots?= =?UTF-8?q?trap=20to=205.3.x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/views/admin/addclient.blade.php | 12 ++--- resources/views/admin/index.blade.php | 65 ++++++++++++----------- resources/views/layouts/admin.blade.php | 19 +++---- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/resources/views/admin/addclient.blade.php b/resources/views/admin/addclient.blade.php index cb8b224..b4cc92e 100644 --- a/resources/views/admin/addclient.blade.php +++ b/resources/views/admin/addclient.blade.php @@ -28,21 +28,20 @@
@csrf -
- +
+
-
- +
+
-
+
Permissions @if (isset($permissions) && count($permissions) > 0) @foreach($permissions as $permission) - {{ dd($permission) }}
+
@endif diff --git a/resources/views/admin/index.blade.php b/resources/views/admin/index.blade.php index 2c8a57c..90d7802 100644 --- a/resources/views/admin/index.blade.php +++ b/resources/views/admin/index.blade.php @@ -15,36 +15,41 @@ {{ session('error') }}
@endif - - - - - - - - - - - @foreach ($data as $client) - - - - - - - @endforeach - -
Client nameClient idPermissionsActions
{{ $client->clientname }}{{ $client->clientid }} - @if (isset($client->permissions) && count($client->permissions) > 0) - @foreach($client->permissions as $permission) - {{ $permission->name }} - @endforeach - @else - No permissions - @endif - - Delete -
+
+ + + + + + + + + + + @foreach ($data as $client) + + + + + + + @endforeach + +
Client nameClient idPermissionsActions
{{ $client->clientname }}{{ $client->clientid }} + @if (isset($client->permissions) && count($client->permissions) > 0) + @foreach($client->permissions as $permission) + {{ $permission->name }} + @endforeach + @else + No permissions + @endif + +
+ Edit + Delete +
+
+

Add new client diff --git a/resources/views/layouts/admin.blade.php b/resources/views/layouts/admin.blade.php index b498e70..b781f9c 100644 --- a/resources/views/layouts/admin.blade.php +++ b/resources/views/layouts/admin.blade.php @@ -9,13 +9,8 @@ DataAPI Admin - @yield('title') - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - From ad14638d2d3632b3a17d89fe5cc19f1d373239ce Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Thu, 20 Nov 2025 08:47:36 -0800 Subject: [PATCH 13/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20More=20L5=20Cle?= =?UTF-8?q?anup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/AuthController.php | 104 +-------- app/Http/Controllers/Controller.php | 43 ---- app/Http/Controllers/CourseController.php | 53 +++-- .../CourseYearQuarterController.php | 17 +- app/Http/Controllers/EmployeeController.php | 207 +++--------------- app/Http/Controllers/LinkFoundController.php | 20 +- app/Http/Controllers/StudentController.php | 5 +- app/Http/Controllers/SubjectController.php | 20 +- .../Controllers/YearQuarterController.php | 20 +- app/Virtual/Models/DirectoryEmployee.php | 124 ----------- app/Virtual/Models/Employee.php | 83 ------- 11 files changed, 127 insertions(+), 569 deletions(-) delete mode 100644 app/Virtual/Models/DirectoryEmployee.php delete mode 100644 app/Virtual/Models/Employee.php diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index a07c962..923f725 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -12,19 +12,7 @@ use Illuminate\Support\Facades\Hash; /** - * @OA\SecurityScheme( - * type="http", - * securityScheme="jwtAuth", - * scheme="bearer", - * bearerFormat="JWT", - * in="header", - * ) - * @OA\SecurityScheme( - * type="http", - * securityScheme="basicAuth", - * scheme="basic", - * in="header", - * ) + * Authentication Controller */ class AuthController extends Controller { @@ -39,64 +27,14 @@ public function __construct() } /** - * Get a JWT via given credentials. - * + * Login to get a Token + * * @return \Illuminate\Http\JsonResponse - * - * @OA\Post( - * path="/api/v1/auth/login", - * operationId="login", - * summary="Get JWT auth token", - * description="Get JSON Web Token for Authentication", - * @OA\RequestBody( - * @OA\MediaType( - * mediaType="application/x-www-form-urlencoded", - * @OA\Schema( - * type="object", - * @OA\Property( - * property="clientid", - * type="string", - * ), - * @OA\Property( - * property="clientkey", - * type="string", - * ) - * ) - * ) - * ), - * @OA\Response( - * response=200, - * description="Successful Operation", - * @OA\JsonContent( - * @OA\Property( - * property="access_token", - * description="JWT Access Token", - * type="string", - * ), - * @OA\Property( - * property="token_type", - * description="Token Type- always 'bearer'", - * type="string", - * ), - * @OA\Property( - * property="expires_in", - * description="Token expiration time in seconds", - * type="integer", - * ), - * ), - * ) - * ) */ public function login() { - /*$credentials = request(['clientid', 'clientkey']); - - if (! $token = auth()->attempt($credentials)) { - return response()->json(['error' => 'Unauthorized'], 401); - }*/ try { - //Config::set('auth.providers.users.model', \App\Models\Client::class); $req_creds = request()->only('clientid', 'clientkey'); $creds = [ 'clientid' => $req_creds['clientid'], 'password' => $req_creds['clientkey'] ]; @@ -162,39 +100,3 @@ protected function respondWithToken($token) ]); } } - /** - * @var \Tymon\JWTAuth\JWTAuth - */ - /* - protected $auth; - - public function __construct(JWTAuth $auth) - { - $this->auth = $auth; - } - - public function loginPost(Request $request) - { - $this->validate($request, [ - 'clientid' => 'required', - 'clientkey' => 'required', - ]); - - try { - $req_creds = $request->only('clientid', 'clientkey'); - $creds = [ 'clientid' => $req_creds['clientid'], 'password' => $req_creds['clientkey'] ]; - - if (! $token = $this->auth->attempt($creds)) { - return response()->json(['user_not_found'], 404); - } - } catch (TokenExpiredException $e) { - return response()->json(['token_expired'], $e->getStatusCode()); - } catch (TokenInvalidException $e) { - return response()->json(['token_invalid'], $e->getStatusCode()); - } catch (JWTException $e) { - return response()->json(['token_absent' => $e->getMessage()], $e->getStatusCode()); - } - - return response()->json(compact('token')); - } -}*/ diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index e26c68e..06fcb55 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -8,48 +8,5 @@ class Controller extends BaseController { - /** - * @OA\Info( - * version=APP_VERSION, - * title="Bellevue College Data API", - * description="The Bellevue College Data API is used to serve and collect BC data.", - * @OA\Contact( - * email="admin@admin.com" - * ), - * @OA\License( - * name="Apache 2.0", - * url="http://www.apache.org/licenses/LICENSE-2.0.html" - * ) - * ) - * - * @OA\Server( - * url=PUBLIC_URL, - * description="Primary API Server" - * ) - * @OA\Server( - * url=PRIVATE_URL, - * description="Private API Server" - * ) - * @OA\PathItem( - * path="/api/v1/internal", - * @OA\Server( - * url=PRIVATE_URL, - * description="Private API Server" - * ) - * ) - * - * @OA\Tag( - * name="Employees", - * description="API Endpoints for Employee Data" - * ) - * @OA\Tag( - * name="Directory", - * description="API Endpoints for Employee Directory Data" - * ) - * @OA\Tag( - * name="Internal", - * description="API Endpoints available on the internal domain" - * ) - */ use AuthorizesRequests, DispatchesJobs, ValidatesRequests; } diff --git a/app/Http/Controllers/CourseController.php b/app/Http/Controllers/CourseController.php index 65ab1d5..9c0aa26 100644 --- a/app/Http/Controllers/CourseController.php +++ b/app/Http/Controllers/CourseController.php @@ -17,10 +17,12 @@ class CourseController extends ApiController{ const WRAPPER = "courses"; /** - Get all courses. This should probably never be used. - Status: inactive - No active route exists. - **/ + * Get all courses. + * + * Inactive: No active route exists, should not be used + * + * @return \Illuminate\Http\JsonResponse + **/ public function index(){ $courses = Course::all(); @@ -29,7 +31,11 @@ public function index(){ } /** - Get a course based on a given CourseID. + * Get a course based on a given CourseID. + * + * @param int $courseid + * + * @return \Illuminate\Http\JsonResponse **/ public function getCourse($courseid) { @@ -48,7 +54,12 @@ public function getCourse($courseid) } /** - Get a course based on a given subject and course number. + * Get a course based on a given subject and course number. + * + * @param string $subject + * @param string $coursenum + * + * @return \Illuminate\Http\JsonResponse **/ public function getCourseBySubjectAndNumber($subject, $coursenum){ @@ -68,8 +79,12 @@ public function getCourseBySubjectAndNumber($subject, $coursenum){ return $this->respond($data); } - /** - Get active courses based on a given subject. + /** + * Get active courses based on a given subject. + * + * @param string $subject + * + * @return \Illuminate\Http\JsonResponse **/ public function getCoursesBySubject($subject) { @@ -95,7 +110,11 @@ public function getCoursesBySubject($subject) } /** - Get multiple courses based on course numbers passed via courses[] query parameter + * Get multiple courses based on course numbers passed via courses[] query parameter + * + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Http\JsonResponse **/ public function getMultipleCourses(Request $request){ @@ -104,25 +123,20 @@ public function getMultipleCourses(Request $request){ //be nice and don't fail non-array input for the case there's a single value $course_input[] = $course_input; } - //dd($course_input); if ( !empty($course_input) && count($course_input) > 0 ) { //valid courses parameter so get courses - //strip whitespace so we can do db comparison $courses_stripped = $course_input; array_walk($courses_stripped, array($this, 'stripWhitespace')); $placeholder = implode(', ', array_fill(0, count($courses_stripped), '?')); - //DB::connection('ods')->enableQueryLog(); $courses = Course::whereRaw("REPLACE(CourseID, ' ', '') IN ($placeholder)", $courses_stripped)->active()->get(); - //$queries = DB::connection('ods')->getQueryLog(); - //dd($queries); $data = $courses; if ( !is_null($courses) ) { $collection = new Collection($courses, new CourseTransformer, self::WRAPPER); - //define serializer + // define serializer $fractal = new Manager; $fractal->setSerializer(new CustomDataArraySerializer); $data = $fractal->createData($collection)->toArray(); @@ -130,17 +144,22 @@ public function getMultipleCourses(Request $request){ return $this->respond($data); } else { - throw new MissingParameterException("Invalid courses[] parameter provided."); + throw new MissingParameterException("Invalid courses[] parameter provided."); } } /** * Function to strip whitespace via regex + * + * @param string $value + * @param string $key + * + * @return void **/ private function stripWhitespace(&$value, $key){ $value = preg_replace('/\s+/', '', $value); } } -?> + diff --git a/app/Http/Controllers/CourseYearQuarterController.php b/app/Http/Controllers/CourseYearQuarterController.php index e3123ed..6e3efa0 100644 --- a/app/Http/Controllers/CourseYearQuarterController.php +++ b/app/Http/Controllers/CourseYearQuarterController.php @@ -19,6 +19,13 @@ class CourseYearQuarterController extends ApiController { /** * Return a CourseYearQuarter based on a YearQuarterID, subject, and course number. + * + * @param int $yqrid YearQuarterID + * @param int $subjectid SubjectID + * @param int $coursenum CourseNumber + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Http\JsonResponse **/ public function getCourseYearQuarter($yqrid, $subjectid, $coursenum, Request $request) { @@ -60,6 +67,12 @@ public function getCourseYearQuarter($yqrid, $subjectid, $coursenum, Request $re /** * Return CourseYearQuarters based on a given YearQuarterID and subject. + * + * @param int $yqrid YearQuarterID + * @param int $subjectid SubjectID + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Http\JsonResponse **/ public function getCourseYearQuartersBySubject($yqrid, $subjectid, Request $request) { @@ -91,9 +104,6 @@ public function getCourseYearQuartersBySubject($yqrid, $subjectid, Request $requ //dd($queries); } - - - $data = $cyqs; if ( !is_null($cyqs) && !$cyqs->isEmpty() ) { @@ -110,4 +120,3 @@ public function getCourseYearQuartersBySubject($yqrid, $subjectid, Request $requ return $this->respond($data); } } -?> diff --git a/app/Http/Controllers/EmployeeController.php b/app/Http/Controllers/EmployeeController.php index ddd8945..6821b1c 100644 --- a/app/Http/Controllers/EmployeeController.php +++ b/app/Http/Controllers/EmployeeController.php @@ -19,48 +19,14 @@ class EmployeeController extends ApiController { - /** - * Get an employee by username - * Status: active - * - * @OA\Get( - * path="/api/v1/internal/employee/{username}", - * operationId="getEmployeeByUsername", - * tags={"Employees", "Internal"}, - * summary="Get employee information", - * description="Returns employee data", - * @OA\Parameter( - * name="username", - * description="Employee Username", - * required=true, - * in="path", - * @OA\Schema( - * type="string" - * ) - * ), - * @OA\Response( - * response=200, - * description="Successful operation", - * @OA\JsonContent(ref="#/components/schemas/Employee") - * ), - * @OA\Response( - * response=400, - * description="Bad Request" - * ), - * @OA\Response( - * response=401, - * description="Unauthenticated", - * ), - * @OA\Response( - * response=403, - * description="Forbidden" - * ), - * security={ - * {"jwtAuth": {"read:true"}} - * } - * ) - */ + * Function to get an employee by username + * + * @param \Illuminate\Http\Request $request + * @param string $username Employee username + * + * @return \Illuminate\Http\JsonResponse + **/ public function getEmployeeByUsername(Request $request, $username) { $emp = Employee::where('ADUserName', '=', $username)->where('EmployeeStatusCode', '=', 'A')->first(); @@ -78,53 +44,13 @@ public function getEmployeeByUsername(Request $request, $username) /** - * Get an employee by username from the directory - * Status: active - * - * @OA\Get( - * path="/api/v1/directory/employee/{username}", - * operationId="getDirectoryEmployeeByUsername", - * tags={"Employees", "Directory"}, - * summary="Get employee directory information", - * description="Returns employee directory data", - * @OA\Parameter( - * name="username", - * description="Employee Username", - * required=true, - * in="path", - * @OA\Schema( - * type="string" - * ) - * ), - * @OA\Response( - * response=200, - * description="Successful operation", - * @OA\JsonContent( - * @OA\Property( - * property="data", - * type="object", - * ref="#/components/schemas/DirectoryEmployee" - * ) - * ) - * ), - * @OA\Response( - * response=400, - * description="Bad Request" - * ), - * @OA\Response( - * response=401, - * description="Unauthenticated", - * ), - * @OA\Response( - * response=403, - * description="Forbidden" - * ), - * security={ - * {"jwtAuth": {"read:true"}} - * } - * ) - */ - + * Function to get an employee by username from the directory + * + * @param \Illuminate\Http\Request $request + * @param string $username Employee username + * + * @return \Illuminate\Http\JsonResponse + **/ public function getDirectoryEmployeeByUsername(Request $request, $username) { @@ -143,51 +69,12 @@ public function getDirectoryEmployeeByUsername(Request $request, $username) } /** - * Get a list of all directory employee usernames - * Status: active - * - * @OA\Get( - * path="/api/v1/directory/employees", - * operationId="getDirectoryEmployees", - * tags={"Employees", "Directory"}, - * summary="Get directory employee usernames", - * description="Returns a list of usernames of employees in the directory", - * @OA\Response( - * response=200, - * description="Successful operation", - * @OA\JsonContent( - * @OA\Property( - * property="employees", - * description="List of employees", - * type="array", - * @OA\Items( - * type="object", - * @OA\Property( - * property="username", - * type="string", - * description="Employee username", - * ), - * ), - * ), - * ), - * ), - * @OA\Response( - * response=400, - * description="Bad Request" - * ), - * @OA\Response( - * response=401, - * description="Unauthenticated", - * ), - * @OA\Response( - * response=403, - * description="Forbidden" - * ), - * security={ - * {"jwtAuth": {"read:true"}} - * } - * ) - */ + * Function to get a list of all directory employee usernames + * + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Http\JsonResponse + **/ public function getDirectoryEmployees() { $emps = EmployeeDirectory::whereNotNull('ADAccountName')->get(); @@ -198,53 +85,14 @@ public function getDirectoryEmployees() return $this->respond($data); } - /* Additions by John begin */ /** - * Get a list of all directory employee usernames using substring search on DisplayName - * Status: active - * - * @OA\Get( - * path="/api/v1/directory/employeeDisplayNameSubstringSearch", - * operationId="getDirectoryEmployeeDisplayNameSubstringSearch", - * tags={"Employees", "Directory"}, - * summary="Get directory employee usernames by DisplayName substring search", - * description="Returns a list of usernames of employees in the directory by DisplayName substring search", - * @OA\Response( - * response=200, - * description="Successful operation", - * @OA\JsonContent( - * @OA\Property( - * property="employees", - * description="List of employees", - * type="array", - * @OA\Items( - * type="object", - * @OA\Property( - * property="username", - * type="string", - * description="Employee username", - * ), - * ), - * ), - * ), - * ), - * @OA\Response( - * response=400, - * description="Bad Request" - * ), - * @OA\Response( - * response=401, - * description="Unauthenticated", - * ), - * @OA\Response( - * response=403, - * description="Forbidden" - * ), - * security={ - * {"jwtAuth": {"read:true"}} - * } - * ) - */ + * Function to get a list of all directory employee usernames using substring search on DisplayName + * + * @param \Illuminate\Http\Request $request + * @param string $username Employee username + * + * @return \Illuminate\Http\JsonResponse + **/ public function getDirectoryEmployeeDisplayNameSubstringSearch(Request $request, $username) { $emps = EmployeeDirectory::whereNotNull('ADAccountName')->where('DisplayName','like','%'.$username.'%')->get(); @@ -254,5 +102,4 @@ public function getDirectoryEmployeeDisplayNameSubstringSearch(Request $request, $data = $fractal->createData($collection)->toArray(); return $this->respond($data); } - /* Additions by John end */ } diff --git a/app/Http/Controllers/LinkFoundController.php b/app/Http/Controllers/LinkFoundController.php index 185979d..0c407a9 100644 --- a/app/Http/Controllers/LinkFoundController.php +++ b/app/Http/Controllers/LinkFoundController.php @@ -17,9 +17,11 @@ class LinkFoundController extends ApiController const WRAPPER = "Links"; - /** - * Return all links - **/ + /** + * Return all links + * + * @return \Illuminate\Http\JsonResponse + **/ public function index(Manager $fractal, Request $request) { $links = LinkFound::all(); @@ -33,6 +35,10 @@ public function index(Manager $fractal, Request $request) /** * Return links with descriptions based on a provided SourceArea value + * + * @param string $sourceArea SourceArea value + * + * @return \Illuminate\Http\JsonResponse **/ public function getLinksBySourceArea($sourceArea) { @@ -42,7 +48,7 @@ public function getLinksBySourceArea($sourceArea) ->select('LinkText', 'LinkDescr') ->get(); - $data = $links; + $data = $links; if (!is_null($links) && !$links->isEmpty()) { //When using the Eloquent query builder, we must "hydrate" the results back to collection of objects $links_hydrated = LinkFound::hydrate($links->toArray()); @@ -54,11 +60,15 @@ public function getLinksBySourceArea($sourceArea) $data = $fractal->createData($collection)->toArray(); } - return $this->respond($data); + return $this->respond($data); } /** * Return the count of links based on a provided SourceArea value + * + * @param string $sourceArea SourceArea value + * + * @return \Illuminate\Http\JsonResponse **/ public function getLinkCountBySourceArea($sourceArea) { diff --git a/app/Http/Controllers/StudentController.php b/app/Http/Controllers/StudentController.php index 0fd70bf..12df1e8 100644 --- a/app/Http/Controllers/StudentController.php +++ b/app/Http/Controllers/StudentController.php @@ -14,7 +14,10 @@ class StudentController extends ApiController { /** * Get a student by username - * Status: active + * + * @param string $username Student username + * + * @return \Illuminate\Http\JsonResponse **/ public function getStudentByUsername($username) { diff --git a/app/Http/Controllers/SubjectController.php b/app/Http/Controllers/SubjectController.php index f5c219b..c284715 100644 --- a/app/Http/Controllers/SubjectController.php +++ b/app/Http/Controllers/SubjectController.php @@ -18,9 +18,11 @@ class SubjectController extends ApiController const WRAPPER = "subjects"; - /** - * Return all subjects - **/ + /** + * Return all subjects + * + * @return \Illuminate\Http\JsonResponse + **/ public function index(Manager $fractal, Request $request) { if ($request->input('filter') === 'active-credit') { @@ -47,6 +49,10 @@ public function index(Manager $fractal, Request $request) /** * Return a subject based on a provided slug + * + * @param string $slug Subject slug + * + * @return \Illuminate\Http\JsonResponse **/ public function getSubject($slug) { @@ -69,6 +75,10 @@ public function getSubject($slug) /** * Return subjects based on a provided YearQuarterID + * + * @param int $yqr YearQuarterID + * + * @return \Illuminate\Http\JsonResponse **/ public function getSubjectsByYearQuarter($yqr) { @@ -85,7 +95,7 @@ public function getSubjectsByYearQuarter($yqr) //$queries = DB::connection('cs')->getQueryLog(); //dd($queries); - $data = $subjects; + $data = $subjects; if (!is_null($subjects) && !$subjects->isEmpty()) { //When using the Eloquent query builder, we must "hydrate" the results back to collection of objects $subjects_hydrated = Subject::hydrate($subjects->toArray()); @@ -97,6 +107,6 @@ public function getSubjectsByYearQuarter($yqr) $data = $fractal->createData($collection)->toArray(); } - return $this->respond($data); + return $this->respond($data); } } diff --git a/app/Http/Controllers/YearQuarterController.php b/app/Http/Controllers/YearQuarterController.php index 6231979..6fa81d1 100644 --- a/app/Http/Controllers/YearQuarterController.php +++ b/app/Http/Controllers/YearQuarterController.php @@ -37,6 +37,11 @@ public function index(){ * Get YearQuarter based on a given YearQuarterID or STRM * * use ?format=strm or ?format=yrq + * + * @param int $yqrid YearQuarterID or STRM + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Http\JsonResponse **/ public function getYearQuarter($yqrid, Request $request){ if ( $request->input('format') === 'strm') { @@ -60,6 +65,8 @@ public function getYearQuarter($yqrid, Request $request){ /** * Get current YearQuarter + * + * @return \Illuminate\Http\JsonResponse **/ public function getCurrentYearQuarter() { $yqr = YearQuarter::current()->first(); @@ -79,6 +86,8 @@ public function getCurrentYearQuarter() { /** * Returns "active" YearQuarters + * + * @return \Illuminate\Http\JsonResponse **/ public function getViewableYearQuarters() { @@ -126,10 +135,10 @@ public function getViewableYearQuarters() { //dd($queries); //var_dump($yqrs); - //When using the Eloquent query builder, we must "hydrate" the results back to collection of objects - $data = $yqrs; + //When using the Eloquent query builder, we must "hydrate" the results back to collection of objects + $data = $yqrs; - if ( !empty($yqrs) && !$yqrs->isEmpty() ) { + if ( !empty($yqrs) && !$yqrs->isEmpty() ) { $yqr_hydrated = YearQuarter::hydrate($yqrs->toArray()); $collection = new Collection($yqr_hydrated, new YearQuarterTransformer, self::WRAPPER); @@ -137,10 +146,9 @@ public function getViewableYearQuarters() { $fractal = new Manager; $fractal->setSerializer(new CustomDataArraySerializer); $data = $fractal->createData($collection)->toArray(); - } + } - return $this->respond($data); + return $this->respond($data); } } -?> diff --git a/app/Virtual/Models/DirectoryEmployee.php b/app/Virtual/Models/DirectoryEmployee.php deleted file mode 100644 index c779ba7..0000000 --- a/app/Virtual/Models/DirectoryEmployee.php +++ /dev/null @@ -1,124 +0,0 @@ - Date: Thu, 20 Nov 2025 15:26:06 -0800 Subject: [PATCH 14/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Ensure=20Authen?= =?UTF-8?q?tication=20Endpoints=20Document=20Properly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/AuthController.php | 61 ++++++++++++++++++++---- app/Http/Resources/AuthTokenResource.php | 25 ++++++++++ routes/api.php | 4 +- 3 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 app/Http/Resources/AuthTokenResource.php diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 923f725..708a0e8 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -10,6 +10,7 @@ use Tymon\JWTAuth\Exceptions\TokenInvalidException; use Tymon\JWTAuth\JWTAuth; use Illuminate\Support\Facades\Hash; +use App\Http\Resources\AuthTokenResource; /** * Authentication Controller @@ -23,19 +24,64 @@ class AuthController extends Controller */ public function __construct() { - $this->middleware('auth:api', ['except' => ['login']]); + $this->middleware('auth:api', ['except' => ['internalLogin', 'publicLogin']]); } /** - * Login to get a Token + * Login on Internal API Server * - * @return \Illuminate\Http\JsonResponse + * Pass in the clientid and clientkey as parameters, and a token will be returned. + * + * **Note**: This endpoint is only available on the internal API server. + * + * @param \Illuminate\Http\Request $request + * + * @return AuthTokenResource + */ + public function internalLogin(Request $request) + { + $validated = $request->validate([ + /** @query */ + 'clientid' => ['required', 'string'], + /** @query */ + 'clientkey' => ['required', 'string'], + ]); + return $this->performLogin($request); + } + + /** + * Login on Public API Server + * + * Pass in the clientid and clientkey as parameters, and a token will be returned. + * + * **Note**: This endpoint is only available on the public API server. + * + * @param \Illuminate\Http\Request $request + * + * @return AuthTokenResource */ - public function login() + public function publicLogin(Request $request) { + $validated = $request->validate([ + /** @query */ + 'clientid' => ['required', 'string'], + /** @query */ + 'clientkey' => ['required', 'string'], + ]); + return $this->performLogin($request); + } + /** + * Shared Login Method + * + * @param \Illuminate\Http\Request $request + * + * @return AuthTokenResource + */ + public function performLogin(Request $request) + { try { - $req_creds = request()->only('clientid', 'clientkey'); + $req_creds = $request->only('clientid', 'clientkey'); $creds = [ 'clientid' => $req_creds['clientid'], 'password' => $req_creds['clientkey'] ]; if (! $token = auth()->guard('api')->attempt($creds)) { @@ -89,13 +135,12 @@ public function refresh() * * @param string $token * - * @return \Illuminate\Http\JsonResponse + * @return AuthTokenResource */ protected function respondWithToken($token) { - return response()->json([ + return new AuthTokenResource([ 'access_token' => $token, - 'token_type' => 'bearer', 'expires_in' => auth()->factory()->getTTL() * 60 ]); } diff --git a/app/Http/Resources/AuthTokenResource.php b/app/Http/Resources/AuthTokenResource.php new file mode 100644 index 0000000..1479fc4 --- /dev/null +++ b/app/Http/Resources/AuthTokenResource.php @@ -0,0 +1,25 @@ + + */ + public function toArray(Request $request): array + { + return [ + /** @var string Access token */ + 'access_token' => $this->resource['access_token'], + 'token_type' => 'bearer', + /** @var int Time in seconds until token expires */ + 'expires_in' => $this->resource['expires_in'] + ]; + } +} diff --git a/routes/api.php b/routes/api.php index 5cdf285..489ba0f 100644 --- a/routes/api.php +++ b/routes/api.php @@ -42,7 +42,7 @@ ], function ($router) { Route::post('internal/auth/login', [ - 'as' => 'login', 'uses' => 'AuthController@login' + 'as' => 'login', 'uses' => 'AuthController@internalLogin', 'name' => 'internal.auth.login' ]); }); @@ -124,7 +124,7 @@ ])->group(function () { Route::post('auth/login', [ - 'as' => 'login', 'uses' => 'AuthController@login' + 'as' => 'login', 'uses' => 'AuthController@publicLogin', 'name' => 'auth.login' ]); Route::get('subject/{slug}','SubjectController@getSubject'); From 559d22d931dc4bbb468aec76109c3a0d0d49f09e Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Thu, 20 Nov 2025 15:26:59 -0800 Subject: [PATCH 15/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Student=20Endpo?= =?UTF-8?q?int=20to=20use=20Resource=20instead=20of=20Transformer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/StudentController.php | 34 +++++++------------- app/Http/Resources/StudentResource.php | 29 +++++++++++++++++ app/Http/Transformers/StudentTransformer.php | 30 ----------------- 3 files changed, 41 insertions(+), 52 deletions(-) create mode 100644 app/Http/Resources/StudentResource.php delete mode 100644 app/Http/Transformers/StudentTransformer.php diff --git a/app/Http/Controllers/StudentController.php b/app/Http/Controllers/StudentController.php index 12df1e8..141500e 100644 --- a/app/Http/Controllers/StudentController.php +++ b/app/Http/Controllers/StudentController.php @@ -1,42 +1,32 @@ first(); - - $data = $stu; - - //handle gracefully if null - if ( !is_null($stu) ) { - $item = new Item($stu, new StudentTransformer); - - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - - $data = $fractal->createData($item)->toArray(); + try { + $stu = Student::where('NTUserName', '=', $username)->firstOrFail(); + return new StudentResource($stu); + } catch (\Exception $e) { + return response()->json(new stdClass()); } - - return $this->respond($data); } } diff --git a/app/Http/Resources/StudentResource.php b/app/Http/Resources/StudentResource.php new file mode 100644 index 0000000..1c8c321 --- /dev/null +++ b/app/Http/Resources/StudentResource.php @@ -0,0 +1,29 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'SID' => $this->SID, + 'EMPLID' => $this->EMPLID, + 'firstName' => $this->FirstName, + 'lastName' => $this->LastName, + 'email' => $this->Email, + 'phoneDaytime' => $this->DaytimePhone, + 'phoneEvening' => $this->EveningPhone, + 'username' => $this->NTUserName, + 'ferpaBlock' => $this->PrivateRecord, + ]; + } +} diff --git a/app/Http/Transformers/StudentTransformer.php b/app/Http/Transformers/StudentTransformer.php deleted file mode 100644 index b566c0e..0000000 --- a/app/Http/Transformers/StudentTransformer.php +++ /dev/null @@ -1,30 +0,0 @@ - $stu->SID, - 'EMPLID' => $stu->EMPLID, - 'firstName' => $stu->FirstName, - 'lastName' => $stu->LastName, - 'email' => $stu->Email, - 'phoneDaytime' => $stu->DaytimePhone, - 'phoneEvening' => $stu->EveningPhone, - 'username' => $stu->NTUserName, - 'ferpaBlock' => $stu->PrivateRecord, - ]; - } -} From d8f320574eae0e69397fb49f6fc0f077bce489b4 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Thu, 20 Nov 2025 15:27:26 -0800 Subject: [PATCH 16/45] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Primary=20Key=20in?= =?UTF-8?q?=20Employee=20Directory=20Model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Models/EmployeeDirectory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Models/EmployeeDirectory.php b/app/Models/EmployeeDirectory.php index c54656b..a601a97 100644 --- a/app/Models/EmployeeDirectory.php +++ b/app/Models/EmployeeDirectory.php @@ -11,7 +11,7 @@ class EmployeeDirectory extends Model //implements AuthenticatableContract, Auth //use Authenticatable, Authorizable; protected $connection = 'empdirectory'; protected $table = 'Employees'; - protected $primaryKey = 'SID'; + protected $primaryKey = 'EMPLID'; public $incrementing = false; public $timestamps = false; @@ -21,7 +21,7 @@ class EmployeeDirectory extends Model //implements AuthenticatableContract, Auth * @var array */ protected $hidden = [ - 'SID', + 'EMPLID', 'SearchField', 'SearchFieldDisplay', 'EmploymentDate', From c07ecbad5209f689b3a8b54ae193243d25c97765 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Thu, 20 Nov 2025 15:28:05 -0800 Subject: [PATCH 17/45] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20Add=20and=20Configu?= =?UTF-8?q?re=20Scramble?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automatically generate documentation via Scramble --- composer.json | 1 + composer.lock | 310 +++++++++++++++++++++++++++++++++++--------- config/scramble.php | 139 ++++++++++++++++++++ 3 files changed, 389 insertions(+), 61 deletions(-) create mode 100644 config/scramble.php diff --git a/composer.json b/composer.json index 25c5ad2..64442be 100644 --- a/composer.json +++ b/composer.json @@ -6,6 +6,7 @@ "type": "project", "require": { "php": "^8.3.6", + "dedoc/scramble": "^0.13.4", "directorytree/ldaprecord-laravel": "*", "laravel/framework": "^11.0", "laravel/helpers": "^1.1", diff --git a/composer.lock b/composer.lock index 8c709c5..cd85537 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dd6f34aa3d46185befba915968ef0a80", + "content-hash": "b19e241b7ca69307b29be9fa416a4eb2", "packages": [ { "name": "brick/math", @@ -135,6 +135,86 @@ ], "time": "2024-02-09T16:56:22+00:00" }, + { + "name": "dedoc/scramble", + "version": "v0.13.4", + "source": { + "type": "git", + "url": "https://github.com/dedoc/scramble.git", + "reference": "773f9d41b68a9bd52120648e55068bfbe9be567e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dedoc/scramble/zipball/773f9d41b68a9bd52120648e55068bfbe9be567e", + "reference": "773f9d41b68a9bd52120648e55068bfbe9be567e", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^10.0|^11.0|^12.0", + "myclabs/deep-copy": "^1.12", + "nikic/php-parser": "^5.0", + "php": "^8.1", + "phpstan/phpdoc-parser": "^1.0|^2.0", + "spatie/laravel-package-tools": "^1.9.2" + }, + "require-dev": { + "larastan/larastan": "^3.3", + "laravel/pint": "^v1.1.0", + "nunomaduro/collision": "^7.0|^8.0", + "orchestra/testbench": "^8.0|^9.0|^10.0", + "pestphp/pest": "^2.34|^3.7", + "pestphp/pest-plugin-laravel": "^2.3|^3.1", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5|^11.5.3", + "spatie/laravel-permission": "^6.10", + "spatie/pest-plugin-snapshots": "^2.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Dedoc\\Scramble\\ScrambleServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Dedoc\\Scramble\\": "src", + "Dedoc\\Scramble\\Database\\Factories\\": "database/factories" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Lytvynenko", + "email": "litvinenko95@gmail.com", + "role": "Developer" + } + ], + "description": "Automatic generation of API documentation for Laravel applications.", + "homepage": "https://github.com/dedoc/scramble", + "keywords": [ + "documentation", + "laravel", + "openapi" + ], + "support": { + "issues": "https://github.com/dedoc/scramble/issues", + "source": "https://github.com/dedoc/scramble/tree/v0.13.4" + }, + "funding": [ + { + "url": "https://github.com/romalytvynenko", + "type": "github" + } + ], + "time": "2025-11-16T07:10:35+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v3.0.3", @@ -2634,6 +2714,66 @@ ], "time": "2025-03-24T10:02:05+00:00" }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, { "name": "nesbot/carbon", "version": "3.10.3", @@ -3113,6 +3253,53 @@ ], "time": "2025-08-21T11:53:16+00:00" }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" + }, + "time": "2025-08-30T15:50:23+00:00" + }, { "name": "psr/clock", "version": "1.0.0", @@ -3802,6 +3989,67 @@ }, "time": "2025-09-04T20:59:21+00:00" }, + { + "name": "spatie/laravel-package-tools", + "version": "1.92.7", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-package-tools.git", + "reference": "f09a799850b1ed765103a4f0b4355006360c49a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/f09a799850b1ed765103a4f0b4355006360c49a5", + "reference": "f09a799850b1ed765103a4f0b4355006360c49a5", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.28|^10.0|^11.0|^12.0", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "orchestra/testbench": "^7.7|^8.0|^9.0|^10.0", + "pestphp/pest": "^1.23|^2.1|^3.1", + "phpunit/php-code-coverage": "^9.0|^10.0|^11.0", + "phpunit/phpunit": "^9.5.24|^10.5|^11.5", + "spatie/pest-plugin-test-time": "^1.1|^2.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\LaravelPackageTools\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "role": "Developer" + } + ], + "description": "Tools for creating Laravel packages", + "homepage": "https://github.com/spatie/laravel-package-tools", + "keywords": [ + "laravel-package-tools", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-package-tools/issues", + "source": "https://github.com/spatie/laravel-package-tools/tree/1.92.7" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-07-17T15:46:43+00:00" + }, { "name": "symfony/clock", "version": "v7.3.0", @@ -6794,66 +7042,6 @@ }, "time": "2024-05-16T03:13:13+00:00" }, - { - "name": "myclabs/deep-copy", - "version": "1.13.4", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3 <3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpspec/prophecy": "^1.10", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2025-08-01T08:46:24+00:00" - }, { "name": "nunomaduro/collision", "version": "v8.8.2", diff --git a/config/scramble.php b/config/scramble.php new file mode 100644 index 0000000..b0877ae --- /dev/null +++ b/config/scramble.php @@ -0,0 +1,139 @@ + 'api', + + /* + * Your API domain. By default, app domain is used. This is also a part of the default API routes + * matcher, so when implementing your own, make sure you use this config if needed. + */ + 'api_domain' => null, + + /* + * The path where your OpenAPI specification will be exported. + */ + 'export_path' => 'api.json', + + 'info' => [ + /* + * API version. + */ + 'version' => env('API_VERSION', '0.0.1'), + + /* + * Description rendered on the home page of the API documentation (`/docs/api`). + */ + 'description' => 'The Bellevue College Data API allows for interaction with some Bellevue College data via RESTful methods.', + ], + + /* + * Customize Stoplight Elements UI + */ + 'ui' => [ + /* + * Define the title of the documentation's website. App name is used when this config is `null`. + */ + 'title' => 'Bellevue College Data API', + + /* + * Define the theme of the documentation. Available options are `light`, `dark`, and `system`. + */ + 'theme' => 'system', + + /* + * Hide the `Try It` feature. Enabled by default. + */ + 'hide_try_it' => false, + + /* + * Hide the schemas in the Table of Contents. Enabled by default. + */ + 'hide_schemas' => false, + + /* + * URL to an image that displays as a small square logo next to the title, above the table of contents. + */ + 'logo' => '', + + /* + * Use to fetch the credential policy for the Try It feature. Options are: omit, include (default), and same-origin + */ + 'try_it_credentials_policy' => 'include', + + /* + * There are three layouts for Elements: + * - sidebar - (Elements default) Three-column design with a sidebar that can be resized. + * - responsive - Like sidebar, except at small screen sizes it collapses the sidebar into a drawer that can be toggled open. + * - stacked - Everything in a single column, making integrations with existing websites that have their own sidebar or other columns already. + */ + 'layout' => 'responsive', + ], + + /* + * The list of servers of the API. By default, when `null`, server URL will be created from + * `scramble.api_path` and `scramble.api_domain` config variables. When providing an array, you + * will need to specify the local server URL manually (if needed). + * + * Example of non-default config (final URLs are generated using Laravel `url` helper): + * + * ```php + * 'servers' => [ + * 'Live' => 'api', + * 'Prod' => 'https://scramble.dedoc.co/api', + * ], + * ``` + */ + 'servers' => [ + 'Main API - Use this by default' => config('app.url') . '/api', + 'Internal API - /internal/ prefix' => 'http://'. config('dataapi.api_internal_domain') . '/api', + ], + + /** + * Determines how Scramble stores the descriptions of enum cases. + * Available options: + * - 'description' – Case descriptions are stored as the enum schema's description using table formatting. + * - 'extension' – Case descriptions are stored in the `x-enumDescriptions` enum schema extension. + * + * @see https://redocly.com/docs-legacy/api-reference-docs/specification-extensions/x-enum-descriptions + * - false - Case descriptions are ignored. + */ + 'enum_cases_description_strategy' => 'description', + + /** + * Determines how Scramble stores the names of enum cases. + * Available options: + * - 'names' – Case names are stored in the `x-enumNames` enum schema extension. + * - 'varnames' - Case names are stored in the `x-enum-varnames` enum schema extension. + * - false - Case names are not stored. + */ + 'enum_cases_names_strategy' => false, + + /** + * When Scramble encounters deep objects in query parameters, it flattens the parameters so the generated + * OpenAPI document correctly describes the API. Flattening deep query parameters is relevant until + * OpenAPI 3.2 is released and query string structure can be described properly. + * + * For example, this nested validation rule describes the object with `bar` property: + * `['foo.bar' => ['required', 'int']]`. + * + * When `flatten_deep_query_parameters` is `true`, Scramble will document the parameter like so: + * `{"name":"foo[bar]", "schema":{"type":"int"}, "required":true}`. + * + * When `flatten_deep_query_parameters` is `false`, Scramble will document the parameter like so: + * `{"name":"foo", "schema": {"type":"object", "properties":{"bar":{"type": "int"}}, "required": ["bar"]}, "required":true}`. + */ + 'flatten_deep_query_parameters' => true, + + 'middleware' => [ + 'web', + RestrictedDocsAccess::class, + ], + + 'extensions' => [], +]; From 89845d5d0d14f4834371ead7d65d8d313eef3bb4 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Thu, 20 Nov 2025 15:30:36 -0800 Subject: [PATCH 18/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Employee=20Endp?= =?UTF-8?q?oint=20to=20use=20Resource=20instead=20of=20Transformer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/EmployeeController.php | 24 ++++++---------- app/Http/Resources/EmployeeResource.php | 28 +++++++++++++++++++ app/Http/Transformers/EmployeeTransformer.php | 28 ------------------- 3 files changed, 37 insertions(+), 43 deletions(-) create mode 100644 app/Http/Resources/EmployeeResource.php delete mode 100644 app/Http/Transformers/EmployeeTransformer.php diff --git a/app/Http/Controllers/EmployeeController.php b/app/Http/Controllers/EmployeeController.php index 6821b1c..774e191 100644 --- a/app/Http/Controllers/EmployeeController.php +++ b/app/Http/Controllers/EmployeeController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Models\Employee; +use App\Http\Resources\EmployeeResource; use App\Models\EmployeeDirectory; use App\Http\Controllers\ApiController; use Illuminate\Http\Request; @@ -20,31 +21,24 @@ class EmployeeController extends ApiController { /** - * Function to get an employee by username + * Get Employee by Username * * @param \Illuminate\Http\Request $request * @param string $username Employee username * - * @return \Illuminate\Http\JsonResponse + * @return EmployeeResource | stdClass **/ public function getEmployeeByUsername(Request $request, $username) { - $emp = Employee::where('ADUserName', '=', $username)->where('EmployeeStatusCode', '=', 'A')->first(); - - $data = $emp; - //handle gracefully if null - if (! is_null($emp)) { - $item = new Item($emp, new EmployeeTransformer); - $fractal = new Manager; - $data = $fractal->createData($item)->toArray(); + try{ + $emp = Employee::where('ADUserName', '=', $username)->where('EmployeeStatusCode', '=', 'A')->firstOrFail(); + return new EmployeeResource($emp); + } catch (\Exception $e) { + return response()->json(new stdClass()); } - - return $this->respond($data); + } - - /** - * Function to get an employee by username from the directory * * @param \Illuminate\Http\Request $request * @param string $username Employee username diff --git a/app/Http/Resources/EmployeeResource.php b/app/Http/Resources/EmployeeResource.php new file mode 100644 index 0000000..b5ec526 --- /dev/null +++ b/app/Http/Resources/EmployeeResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'SID' => $this->SID, + 'EMPLID' => $this->EMPLID, + 'firstName' => $this->FirstName, + 'lastName' => $this->LastName, + 'aliasName' => $this->AliasName, + 'email' => $this->WorkEmail, + 'phone' => $this->WorkPhoneNumber, + 'username' => $this->ADUserName + ]; + } +} diff --git a/app/Http/Transformers/EmployeeTransformer.php b/app/Http/Transformers/EmployeeTransformer.php deleted file mode 100644 index b862b3f..0000000 --- a/app/Http/Transformers/EmployeeTransformer.php +++ /dev/null @@ -1,28 +0,0 @@ - $emp->SID, - 'EMPLID' => $emp->EMPLID, - 'firstName' => $emp->FirstName, - 'lastName' => $emp->LastName, - 'aliasName' => $emp->AliasName, - 'email' => $emp->WorkEmail, - 'phone' => $emp->WorkPhoneNumber, - 'username' => $emp->ADUserName - ]; - } -} From b26242f17725690b364f8449ff36cbf82a24a06f Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 21 Nov 2025 08:19:52 -0800 Subject: [PATCH 19/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Employee=20Cont?= =?UTF-8?q?roller=20to=20use=20Resource=20and=20Collection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/EmployeeController.php | 77 ++++++++++--------- .../EmployeeDirectoryDetailResource.php | 40 ++++++++++ .../EmployeeDirectorySummaryCollection.php | 34 ++++++++ .../EmployeeDirectorySummaryResource.php | 22 ++++++ .../EmployeeDirectoryTransformer.php | 36 --------- .../Transformers/EmployeesTransformer.php | 22 ------ 6 files changed, 135 insertions(+), 96 deletions(-) create mode 100644 app/Http/Resources/EmployeeDirectoryDetailResource.php create mode 100644 app/Http/Resources/EmployeeDirectorySummaryCollection.php create mode 100644 app/Http/Resources/EmployeeDirectorySummaryResource.php delete mode 100644 app/Http/Transformers/EmployeeDirectoryTransformer.php delete mode 100644 app/Http/Transformers/EmployeesTransformer.php diff --git a/app/Http/Controllers/EmployeeController.php b/app/Http/Controllers/EmployeeController.php index 774e191..6f7a791 100644 --- a/app/Http/Controllers/EmployeeController.php +++ b/app/Http/Controllers/EmployeeController.php @@ -4,18 +4,14 @@ use App\Models\Employee; use App\Http\Resources\EmployeeResource; + use App\Models\EmployeeDirectory; +use App\Http\Resources\EmployeeDirectoryDetailResource; +use App\Http\Resources\EmployeeDirectorySummaryCollection; + use App\Http\Controllers\ApiController; use Illuminate\Http\Request; -//use App\Exceptions\MissingParameterException; -use League\Fractal\Manager; -use League\Fractal\Resource\Item; -use League\Fractal\Resource\Collection; -use App\Http\Transformers\EmployeeTransformer; -use App\Http\Transformers\EmployeesTransformer; -use App\Http\Transformers\EmployeeDirectoryTransformer; -use App\Http\Serializers\CustomDataArraySerializer; -//use App\Http\Serializers\CustomDataArraySerializer; + use DB; class EmployeeController extends ApiController @@ -39,61 +35,66 @@ public function getEmployeeByUsername(Request $request, $username) } + + /** + * Get Employee from Directory by Username + * + * Pass in the employee's username (e.g. "john.doe") as a parameter, and the employee's data will be returned. + * **Note**: This endpoint is authenticated, but available on the public API server. * * @param \Illuminate\Http\Request $request * @param string $username Employee username * - * @return \Illuminate\Http\JsonResponse + * @return EmployeeDirectoryDetailResource | stdClass **/ public function getDirectoryEmployeeByUsername(Request $request, $username) { - - $emp = EmployeeDirectory::where('ADAccountName', '=', $username)->first(); - - - $data = $emp; - //handle gracefully if null - if (! is_null($emp)) { - $item = new Item($emp, new EmployeeDirectoryTransformer); - $fractal = new Manager; - $data = $fractal->createData($item)->toArray(); + try { + $emp = EmployeeDirectory::where('ADAccountName', '=', $username)->firstOrFail(); + return new EmployeeDirectoryDetailResource($emp); + } catch (\Exception $e) { + return response()->json(new stdClass()); } - - return $this->respond($data); } /** - * Function to get a list of all directory employee usernames + * Get all Employee usernames from the Directory + * + * **Note**: This endpoint is authenticated, but available on the public API server. * * @param \Illuminate\Http\Request $request * - * @return \Illuminate\Http\JsonResponse + * @return EmployeeDirectorySummaryCollection | stdClass **/ public function getDirectoryEmployees() { - $emps = EmployeeDirectory::whereNotNull('ADAccountName')->get(); - $collection = new Collection($emps, new EmployeesTransformer, 'employees'); - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($collection)->toArray(); - return $this->respond($data); + try { + $emps = EmployeeDirectory::whereNotNull('ADAccountName')->get(); + return new EmployeeDirectorySummaryCollection($emps); + } catch (\Exception $e) { + return response()->json(new stdClass()); + } } /** - * Function to get a list of all directory employee usernames using substring search on DisplayName + * Search for Directory Employees by DisplayName using substring search + * + * Pass in the employee's partial display name (e.g. "john") as a parameter, and matching employee data will be returned. + * + * **Note**: This endpoint is authenticated, but available on the public API server. * * @param \Illuminate\Http\Request $request * @param string $username Employee username * - * @return \Illuminate\Http\JsonResponse + * @return EmployeeDirectorySummaryCollection | stdClass **/ public function getDirectoryEmployeeDisplayNameSubstringSearch(Request $request, $username) { - $emps = EmployeeDirectory::whereNotNull('ADAccountName')->where('DisplayName','like','%'.$username.'%')->get(); - $collection = new Collection($emps, new EmployeesTransformer, 'employees'); - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($collection)->toArray(); - return $this->respond($data); + try { + $emps = EmployeeDirectory::whereNotNull('ADAccountName')->where('DisplayName','like','%'.$username.'%')->get(); + return new EmployeeDirectorySummaryCollection($emps); + } catch (\Exception $e) { + return response()->json(new stdClass()); + } } } diff --git a/app/Http/Resources/EmployeeDirectoryDetailResource.php b/app/Http/Resources/EmployeeDirectoryDetailResource.php new file mode 100644 index 0000000..6b833cb --- /dev/null +++ b/app/Http/Resources/EmployeeDirectoryDetailResource.php @@ -0,0 +1,40 @@ + + */ + public function toArray(Request $request): array + { + $title = $this->WorkingTitle !== NULL ? $this->WorkingTitle : $this->OfficialTitle; + + // Remove whitespace and control characters + $title = preg_replace('/[\x00-\x1F\x7F\xA0]/u', ' ', $title); + + return [ + 'firstName' => $this->FirstName, + 'lastName' => $this->LastName, + /** @var string|null Alias/Nickname */ + 'aliasName' => $this->AKA, + 'displayName' => $this->DisplayName, + /** @var string|null Title */ + 'title' => $title, + 'department' => $this->DepartmentName, + 'email' => $this->BCCEmail, + 'phone' => $this->WorkPhone, + 'displayPhone' => $this->DisplayPhone, + 'office' => $this->WorkOffice, + 'mailstop' => $this->MailStop, + ]; + } +} diff --git a/app/Http/Resources/EmployeeDirectorySummaryCollection.php b/app/Http/Resources/EmployeeDirectorySummaryCollection.php new file mode 100644 index 0000000..5817d78 --- /dev/null +++ b/app/Http/Resources/EmployeeDirectorySummaryCollection.php @@ -0,0 +1,34 @@ + + */ + public function toArray(Request $request): array + { + return $this->collection->toArray(); + } +} diff --git a/app/Http/Resources/EmployeeDirectorySummaryResource.php b/app/Http/Resources/EmployeeDirectorySummaryResource.php new file mode 100644 index 0000000..1fe6fb5 --- /dev/null +++ b/app/Http/Resources/EmployeeDirectorySummaryResource.php @@ -0,0 +1,22 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'username' => $this->ADAccountName, + ]; + } +} diff --git a/app/Http/Transformers/EmployeeDirectoryTransformer.php b/app/Http/Transformers/EmployeeDirectoryTransformer.php deleted file mode 100644 index 2f00538..0000000 --- a/app/Http/Transformers/EmployeeDirectoryTransformer.php +++ /dev/null @@ -1,36 +0,0 @@ -WorkingTitle !== NULL ? $emp->WorkingTitle : $emp->OfficialTitle; - - // Remove Whitespace and control characters - $title = preg_replace('/[\x00-\x1F\x7F\xA0]/u', ' ', $title); - - return [ - 'firstName' => $emp->FirstName, - 'lastName' => $emp->LastName, - 'aliasName' => $emp->AKA, - 'displayName' => $emp->DisplayName, - 'title' => $title, - 'department' => $emp->DepartmentName, - 'email' => $emp->BCCEmail, - 'phone' => $emp->WorkPhone, - 'displayPhone' => $emp->DisplayPhone, - 'office' => $emp->WorkOffice, - 'mailstop' => $emp->MailStop, - ]; - } - -} diff --git a/app/Http/Transformers/EmployeesTransformer.php b/app/Http/Transformers/EmployeesTransformer.php deleted file mode 100644 index f77c0af..0000000 --- a/app/Http/Transformers/EmployeesTransformer.php +++ /dev/null @@ -1,22 +0,0 @@ -WorkingTitle !== NULL ? $emp->WorkingTitle : $emp->OfficialTitle; - return [ - 'username' => $emp->ADAccountName, - ]; - } - -} From 496cda08f8c872178233a50a4e94a9bd905ddf93 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 21 Nov 2025 09:26:54 -0800 Subject: [PATCH 20/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Convert=20Subje?= =?UTF-8?q?ct=20to=20use=20Resource?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/SubjectController.php | 122 ++++++++----------- app/Http/Resources/SubjectCollection.php | 24 ++++ app/Http/Resources/SubjectResource.php | 22 ++++ app/Http/Transformers/SubjectTransformer.php | 22 ---- 4 files changed, 98 insertions(+), 92 deletions(-) create mode 100644 app/Http/Resources/SubjectCollection.php create mode 100644 app/Http/Resources/SubjectResource.php delete mode 100644 app/Http/Transformers/SubjectTransformer.php diff --git a/app/Http/Controllers/SubjectController.php b/app/Http/Controllers/SubjectController.php index c284715..c8beccf 100644 --- a/app/Http/Controllers/SubjectController.php +++ b/app/Http/Controllers/SubjectController.php @@ -3,110 +3,92 @@ namespace App\Http\Controllers; use App\Models\Subject; -use App\Models\SubjectPrefix; -use App\Http\Transformers\SubjectTransformer; +use App\Http\Resources\SubjectResource; +use App\Http\Resources\SubjectCollection; use App\Http\Controllers\Controller; -use App\Http\Serializers\CustomDataArraySerializer; use Illuminate\Http\Request; -use League\Fractal\Manager; -use League\Fractal\Resource\Collection; -use League\Fractal\Resource\Item; use DB; +use stdClass; class SubjectController extends ApiController { - const WRAPPER = "subjects"; - /** - * Return all subjects + * List all Subjects * - * @return \Illuminate\Http\JsonResponse + * @param Request $request + + * @return SubjectCollection | stdClass **/ - public function index(Manager $fractal, Request $request) + public function index(Request $request) { - if ($request->input('filter') === 'active-credit') { - $subjects = Subject:: - where('DESCR', 'not like', 'CE%') - ->where('DESCR', 'not like', '% (Inactive)') - ->orderBy('SUBJECT', 'ASC') + try { + if ($request->input('filter') === 'active-credit') { + $subjects = Subject:: + where('DESCR', 'not like', 'CE%') + ->where('DESCR', 'not like', '% (Inactive)') + ->orderBy('SUBJECT', 'ASC') + ->get(); + } else { + $subjects = Subject:: + orderBy('SUBJECT', 'ASC') ->get(); - } else { - $subjects = Subject:: - orderBy('SUBJECT', 'ASC') - ->get(); - } - $data = $subjects; - if (!is_null($subjects)) { - $collection = new Collection($subjects, new SubjectTransformer, self::WRAPPER); + } - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($collection)->toArray(); + return new SubjectCollection($subjects); + } catch (\Exception $e) { + return response()->json(new stdClass()); } - - return $this->respond($data); } /** - * Return a subject based on a provided slug + * Return Subject by Slug * * @param string $slug Subject slug * + * @response array{subject: array{subject: string, name: string}} + * * @return \Illuminate\Http\JsonResponse **/ public function getSubject($slug) { - - $subject = Subject::where('SUBJECT', '=', $slug)->first(); - - $data = $subject; - //handle gracefully if null - if (!is_null($subject)) { - $item = new Item($subject, new SubjectTransformer, "subject"); - - $fractal = new Manager; - - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($item)->toArray(); + try { + $subject = Subject::where('SUBJECT', '=', $slug)->firstOrFail(); + // We're wrapping in a subject array to match the response format + // Normal wrapping methods don't work because of double 'subject' + // in both the wrapper and the resource. + // This also requires the response to be manually listed in the + // docblock to ensure documentation is correct. + return response()->json([ + 'subject' => new SubjectResource($subject) + ]); + } catch (\Exception $e) { + return response()->json(new stdClass()); } - - return $this->respond($data); } /** - * Return subjects based on a provided YearQuarterID + * List Subjects in a YRQ * - * @param int $yqr YearQuarterID + * @param string $yqr YearQuarterID * - * @return \Illuminate\Http\JsonResponse + * @return SubjectCollection | stdClass **/ public function getSubjectsByYearQuarter($yqr) { + try { + $subjects = DB::connection('ods') + ->table('vw_PSSubject') + ->join('vw_Class', 'vw_PSSubject.SUBJECT', '=', 'vw_Class.Department') + ->where('vw_Class.YearQuarterID', '=', $yqr) + ->select('vw_PSSubject.SUBJECT', 'vw_PSSubject.DESCR as DESCR') + ->groupBy('vw_PSSubject.SUBJECT', 'vw_PSSubject.DESCR') + ->orderBy('vw_PSSubject.SUBJECT', 'asc') + ->get(); - //DB::connection('cs')->enableQueryLog(); - $subjects = DB::connection('ods') - ->table('vw_PSSubject') - ->join('vw_Class', 'vw_PSSubject.SUBJECT', '=', 'vw_Class.Department') - ->where('vw_Class.YearQuarterID', '=', $yqr) - ->select('vw_PSSubject.SUBJECT', 'vw_PSSubject.DESCR as DESCR') - ->groupBy('vw_PSSubject.SUBJECT', 'vw_PSSubject.DESCR') - ->orderBy('vw_PSSubject.SUBJECT', 'asc') - ->get(); - //$queries = DB::connection('cs')->getQueryLog(); - //dd($queries); - - $data = $subjects; - if (!is_null($subjects) && !$subjects->isEmpty()) { - //When using the Eloquent query builder, we must "hydrate" the results back to collection of objects - $subjects_hydrated = Subject::hydrate($subjects->toArray()); - $collection = new Collection($subjects_hydrated, new SubjectTransformer, self::WRAPPER); - - //set data serializer - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($collection)->toArray(); + return new SubjectCollection($subjects); + } catch (\Exception $e) { + return response()->json(new stdClass()); } - - return $this->respond($data); } } diff --git a/app/Http/Resources/SubjectCollection.php b/app/Http/Resources/SubjectCollection.php new file mode 100644 index 0000000..49d142a --- /dev/null +++ b/app/Http/Resources/SubjectCollection.php @@ -0,0 +1,24 @@ + + */ + public function toArray(Request $request): array + { + return $this->collection->toArray(); + } +} diff --git a/app/Http/Resources/SubjectResource.php b/app/Http/Resources/SubjectResource.php new file mode 100644 index 0000000..1800558 --- /dev/null +++ b/app/Http/Resources/SubjectResource.php @@ -0,0 +1,22 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'subject' => $this->SUBJECT, + 'name' => $this->DESCR, + ]; + } +} diff --git a/app/Http/Transformers/SubjectTransformer.php b/app/Http/Transformers/SubjectTransformer.php deleted file mode 100644 index b5ee5c8..0000000 --- a/app/Http/Transformers/SubjectTransformer.php +++ /dev/null @@ -1,22 +0,0 @@ - $sub->SUBJECT, - 'name' => $sub->DESCR, - ]; - } -} From 8919ed98101c76220661668ee66d0ba18da44143 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 21 Nov 2025 09:44:29 -0800 Subject: [PATCH 21/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Convert=20YearQ?= =?UTF-8?q?uarter=20to=20Resource?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/YearQuarterController.php | 205 ++++++++---------- app/Http/Resources/YearQuarterCollection.php | 21 ++ app/Http/Resources/YearQuarterResource.php | 23 ++ .../Transformers/YearQuarterTransformer.php | 23 -- 4 files changed, 140 insertions(+), 132 deletions(-) create mode 100644 app/Http/Resources/YearQuarterCollection.php create mode 100644 app/Http/Resources/YearQuarterResource.php delete mode 100644 app/Http/Transformers/YearQuarterTransformer.php diff --git a/app/Http/Controllers/YearQuarterController.php b/app/Http/Controllers/YearQuarterController.php index 6fa81d1..e489e60 100644 --- a/app/Http/Controllers/YearQuarterController.php +++ b/app/Http/Controllers/YearQuarterController.php @@ -3,152 +3,139 @@ namespace App\Http\Controllers; use App\Models\YearQuarter; -use App\Http\Transformers\YearQuarterTransformer; +use App\Http\Resources\YearQuarterResource; +use App\Http\Resources\YearQuarterCollection; use App\Http\Controllers\Controller; -use App\Http\Serializers\CustomDataArraySerializer; use Illuminate\Http\Request; -use League\Fractal\Manager; -use League\Fractal\Resource\Collection; -use League\Fractal\Resource\Item; use DB; class YearQuarterController extends ApiController { - const WRAPPER = "quarters"; - /** - * Return all YearQuarters + * List all Terms (YearQuarters) * Status: inactive * No active route. No set serialization. + * + * @return YearQuarterCollection | stdClass **/ - public function index(){ - - $yqrs = YearQuarter::all(); - $collection = new Collection($yqrs, new YearQuarterTransformer); - - $fractal = new Manager; - $data = $fractal->createData($collection)->toArray(); - - return $this->respond($data); - + public function index() + { + try { + $yqrs = YearQuarter::all(); + return new YearQuarterCollection($yqrs); + } catch (\Exception $e) { + return response()->json(new stdClass()); + } } /** - * Get YearQuarter based on a given YearQuarterID or STRM + * Get Term (YearQuarter) by YearQuarterID or STRM * - * use ?format=strm or ?format=yrq + * use ?format=strm or ?format=yrq to specify which format to return * - * @param int $yqrid YearQuarterID or STRM + * @param string $yqrid YearQuarterID or STRM * @param \Illuminate\Http\Request $request * - * @return \Illuminate\Http\JsonResponse + * @response array{quarter: array{quarter: string, strm: string, title: string}} + * + * @return \Illuminate\Http\JsonResponse | stdClass **/ public function getYearQuarter($yqrid, Request $request){ - if ( $request->input('format') === 'strm') { - $yqr = YearQuarter::where('STRM', '=', $yqrid)->first(); - } else { - $yqr = YearQuarter::where('YearQuarterID', '=', $yqrid)->first(); + try { + if ( $request->input('format') === 'strm') { + $yqr = YearQuarter::where('STRM', '=', $yqrid)->firstOrFail(); + } else { + $yqr = YearQuarter::where('YearQuarterID', '=', $yqrid)->firstOrFail(); + } + // We're wrapping in a quarter array to match the response format + // Normal wrapping methods don't work because of double 'quarter' + // in both the wrapper and the resource. + // This also requires the response to be manually listed in the + // docblock to ensure documentation is correct. + return response()->json([ + 'quarter' => new YearQuarterResource($yqr) + ]); + } catch (\Exception $e) { + return response()->json(new stdClass()); } - - $data = $yqr; - //only serialize if not empty - if ( !is_null($yqr) ) { - $item = new Item($yqr, new YearQuarterTransformer, "quarter"); - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($item)->toArray(); - } - - return $this->respond($data); } /** - * Get current YearQuarter + * Get Current Term (YearQuarter) * + * @response array{quarter: array{quarter: string, strm: string, title: string}} + * @return \Illuminate\Http\JsonResponse **/ public function getCurrentYearQuarter() { - $yqr = YearQuarter::current()->first(); - - $data = $yqr; - //only serialize if not empty - if (!is_null($yqr)) { - $item = new Item($yqr, new YearQuarterTransformer, "quarter"); - - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($item)->toArray(); + try { + $yqr = YearQuarter::current()->firstOrFail(); + // We're wrapping in a quarter array to match the response format + // Normal wrapping methods don't work because of double 'quarter' + // in both the wrapper and the resource. + // This also requires the response to be manually listed in the + // docblock to ensure documentation is correct. + return response()->json([ + 'quarter' => new YearQuarterResource($yqr) + ]); + } catch (\Exception $e) { + return response()->json(new stdClass()); } - - return $this->respond($data); } /** - * Returns "active" YearQuarters + * List Active Terms (YearQuarters) * - * @return \Illuminate\Http\JsonResponse + * @return YearQuarterCollection | stdClass **/ public function getViewableYearQuarters() { - //get max quarters to be shown - $max_yqr = config('dataapi.yearquarter_maxactive'); - $lookahead = config('dataapi.yearquarter_lookahead'); - $timezone = new \DateTimeZone(config("dataapi.timezone")); - - //Create now date/time object - $now = new \DateTime(); - $now->setTimezone($timezone); - $now_string = $now->format("Y-m-d H:i:s"); - - //Create lookahead date - $la_date = $now->add(new \DateInterval('P'.$lookahead.'D')); - $la_string = $la_date->format("Y-m-d H:i:s"); - - //Use Eloquent query builder to query results - //DB::connection('ods')->enableQueryLog(); - $yqrs = DB::connection('ods') - ->table('vw_YearQuarter') - ->join('vw_WebRegistrationSetting', 'vw_WebRegistrationSetting.YearQuarterID', '=', 'vw_YearQuarter.YearQuarterID') - ->where(function ($query) { - $lookahead = config('dataapi.yearquarter_lookahead'); - $timezone = new \DateTimeZone(config("dataapi.timezone")); - - //Create now date/time object - $now = new \DateTime(); - $now->setTimezone($timezone); - $now_string = $now->format("Y-m-d H:i:s"); - - //Create lookahead date - $la_date = $now->add(new \DateInterval('P'.$lookahead.'D')); - $la_string = $la_date->format("Y-m-d H:i:s"); - - $query->whereNotNull('vw_WebRegistrationSetting.FirstRegistrationDate') - ->where('vw_WebRegistrationSetting.FirstRegistrationDate', '<=', $la_string ) - ->orWhere('vw_YearQuarter.LastClassDay', '<=', $now_string ); - }) - ->select('vw_YearQuarter.YearQuarterID', 'vw_YearQuarter.STRM', 'vw_YearQuarter.Title', 'vw_YearQuarter.FirstClassDay', 'vw_YearQuarter.LastClassDay', 'vw_YearQuarter.AcademicYear') - ->orderBy('vw_YearQuarter.FirstClassDay', 'desc') - ->take($max_yqr) - ->get(); - //$queries = DB::connection('ods')->getQueryLog(); - //dd($queries); - //var_dump($yqrs); - - //When using the Eloquent query builder, we must "hydrate" the results back to collection of objects - $data = $yqrs; - - if ( !empty($yqrs) && !$yqrs->isEmpty() ) { - $yqr_hydrated = YearQuarter::hydrate($yqrs->toArray()); - $collection = new Collection($yqr_hydrated, new YearQuarterTransformer, self::WRAPPER); - - //Set data serializer - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($collection)->toArray(); + try { + //get max quarters to be shown + $max_yqr = config('dataapi.yearquarter_maxactive'); + $lookahead = config('dataapi.yearquarter_lookahead'); + $timezone = new \DateTimeZone(config("dataapi.timezone")); + + //Create now date/time object + $now = new \DateTime(); + $now->setTimezone($timezone); + $now_string = $now->format("Y-m-d H:i:s"); + + //Create lookahead date + $la_date = $now->add(new \DateInterval('P'.$lookahead.'D')); + $la_string = $la_date->format("Y-m-d H:i:s"); + + //Use Eloquent query builder to query results + //DB::connection('ods')->enableQueryLog(); + $yqrs = DB::connection('ods') + ->table('vw_YearQuarter') + ->join('vw_WebRegistrationSetting', 'vw_WebRegistrationSetting.YearQuarterID', '=', 'vw_YearQuarter.YearQuarterID') + ->where(function ($query) { + $lookahead = config('dataapi.yearquarter_lookahead'); + $timezone = new \DateTimeZone(config("dataapi.timezone")); + + //Create now date/time object + $now = new \DateTime(); + $now->setTimezone($timezone); + $now_string = $now->format("Y-m-d H:i:s"); + + //Create lookahead date + $la_date = $now->add(new \DateInterval('P'.$lookahead.'D')); + $la_string = $la_date->format("Y-m-d H:i:s"); + + $query->whereNotNull('vw_WebRegistrationSetting.FirstRegistrationDate') + ->where('vw_WebRegistrationSetting.FirstRegistrationDate', '<=', $la_string ) + ->orWhere('vw_YearQuarter.LastClassDay', '<=', $now_string ); + }) + ->select('vw_YearQuarter.YearQuarterID', 'vw_YearQuarter.STRM', 'vw_YearQuarter.Title', 'vw_YearQuarter.FirstClassDay', 'vw_YearQuarter.LastClassDay', 'vw_YearQuarter.AcademicYear') + ->orderBy('vw_YearQuarter.FirstClassDay', 'desc') + ->take($max_yqr) + ->get(); + return new YearQuarterCollection($yqrs); + } catch (\Exception $e) { + return response()->json(new stdClass()); } - - return $this->respond($data); } } diff --git a/app/Http/Resources/YearQuarterCollection.php b/app/Http/Resources/YearQuarterCollection.php new file mode 100644 index 0000000..7ac6749 --- /dev/null +++ b/app/Http/Resources/YearQuarterCollection.php @@ -0,0 +1,21 @@ + + */ + public function toArray(Request $request): array + { + return $this->collection->toArray(); + } +} diff --git a/app/Http/Resources/YearQuarterResource.php b/app/Http/Resources/YearQuarterResource.php new file mode 100644 index 0000000..5730327 --- /dev/null +++ b/app/Http/Resources/YearQuarterResource.php @@ -0,0 +1,23 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'quarter' => $this->YearQuarterID, + 'strm' => $this->STRM, + 'title' => $this->Title + ]; + } +} diff --git a/app/Http/Transformers/YearQuarterTransformer.php b/app/Http/Transformers/YearQuarterTransformer.php deleted file mode 100644 index ea84a66..0000000 --- a/app/Http/Transformers/YearQuarterTransformer.php +++ /dev/null @@ -1,23 +0,0 @@ - $yqr->YearQuarterID, - 'strm' => $yqr->STRM, - 'title' => $yqr->Title - ]; - } - -} From e5f6e25a89b80abdf5f4cafbba5eb592ab1055cb Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 21 Nov 2025 13:10:53 -0800 Subject: [PATCH 22/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Move=20CourseYe?= =?UTF-8?q?arQuarter=20to=20use=20Resource?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a complex one, and includes some major updates: 1. Convert CourseYearQuarter and Section to use Resources and Collections 2. Update models and controller to handle hasMany relationship properly 3. Install `awobaz/compoships` to handle composite key relationships --- .../CourseYearQuarterController.php | 114 +++++------------- .../Resources/CourseYearQuarterCollection.php | 23 ++++ .../Resources/CourseYearQuarterResource.php | 49 ++++++++ app/Http/Resources/SectionCollection.php | 22 ++++ app/Http/Resources/SectionResource.php | 43 +++++++ .../CourseYearQuarterTransformer.php | 71 ----------- app/Http/Transformers/SectionTransformer.php | 44 ------- app/Models/CourseYearQuarter.php | 77 +++++++----- app/Models/Section.php | 74 ++++++------ composer.json | 1 + composer.lock | 64 +++++++++- 11 files changed, 315 insertions(+), 267 deletions(-) create mode 100644 app/Http/Resources/CourseYearQuarterCollection.php create mode 100644 app/Http/Resources/CourseYearQuarterResource.php create mode 100644 app/Http/Resources/SectionCollection.php create mode 100644 app/Http/Resources/SectionResource.php delete mode 100644 app/Http/Transformers/CourseYearQuarterTransformer.php delete mode 100644 app/Http/Transformers/SectionTransformer.php diff --git a/app/Http/Controllers/CourseYearQuarterController.php b/app/Http/Controllers/CourseYearQuarterController.php index 6e3efa0..c49aef5 100644 --- a/app/Http/Controllers/CourseYearQuarterController.php +++ b/app/Http/Controllers/CourseYearQuarterController.php @@ -3,15 +3,11 @@ namespace App\Http\Controllers; use App\Models\CourseYearQuarter; -use App\Http\Transformers\CourseYearQuarterTransformer; +use App\Http\Resources\CourseYearQuarterResource; +use App\Http\Resources\CourseYearQuarterCollection; use App\Http\Controllers\Controller; -use App\Http\Serializers\CourseDataArraySerializer; -use App\Http\Serializers\CustomDataArraySerializer; use Illuminate\Http\Request; -use League\Fractal\Manager; -use League\Fractal\Resource\Collection; -use League\Fractal\Resource\Item; -use DB; + class CourseYearQuarterController extends ApiController { @@ -20,103 +16,55 @@ class CourseYearQuarterController extends ApiController { /** * Return a CourseYearQuarter based on a YearQuarterID, subject, and course number. * - * @param int $yqrid YearQuarterID - * @param int $subjectid SubjectID - * @param int $coursenum CourseNumber + * @param string $yqrid YearQuarterID + * @param string $subjectid SubjectID + * @param string $coursenum CourseNumber * @param \Illuminate\Http\Request $request * - * @return \Illuminate\Http\JsonResponse + * @return CourseYearQuarterResource | \Illuminate\Http\JsonResponse **/ public function getCourseYearQuarter($yqrid, $subjectid, $coursenum, Request $request) { - if ($request->input('format') === 'strm'){ - //DB::connection('ods')->enableQueryLog(); - $cyq = CourseYearQuarter::where('STRM', '=', $yqrid) - ->where('Department', '=', $subjectid) - ->where('CourseNumber', '=', $coursenum) - ->first(); - //$queries = DB::connection('ods')->getQueryLog(); - //dd($queries); - //var_dump($cyq); - } else { - //DB::connection('ods')->enableQueryLog(); - $cyq = CourseYearQuarter::where('YearQuarterID', '=', $yqrid) + $validated = $request->validate([ + 'format' => 'sometimes|string|in:yrq,strm', + ]); + try { + $selector = $request->input('format') === 'strm' ? 'STRM' : 'YearQuarterID'; + $cyq = CourseYearQuarter::where($selector, '=', $yqrid) ->where('Department', '=', $subjectid) ->where('CourseNumber', '=', $coursenum) - ->first(); - //$queries = DB::connection('ods')->getQueryLog(); - //dd($queries); - //var_dump($cyq); - } - - - $data = $cyq; - - //handle gracefully if null - if ( !is_null($cyq) ) { - $item = new Item($cyq, new CourseYearQuarterTransformer, self::WRAPPER); - - //define serializer - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($item)->toArray(); + ->firstOrFail(); + + return new CourseYearQuarterResource($cyq); + } catch (\Exception $e) { + return response()->json(['classes' => []], 404); } - - return $this->respond($data); } /** * Return CourseYearQuarters based on a given YearQuarterID and subject. * - * @param int $yqrid YearQuarterID - * @param int $subjectid SubjectID + * @param string $yqrid YearQuarterID + * @param string $subjectid SubjectID * @param \Illuminate\Http\Request $request * - * @return \Illuminate\Http\JsonResponse + * @return CourseYearQuarterCollection | \Illuminate\Http\JsonResponse **/ public function getCourseYearQuartersBySubject($yqrid, $subjectid, Request $request) { - if ( $request->input('format') === 'strm') { - //DB::connection('cs')->enableQueryLog(); - $cyqs = DB::connection('ods') - ->table('vw_Class') - ->where('STRM', '=', $yqrid) + $validated = $request->validate([ + 'format' => 'sometimes|string|in:yrq,strm', + ]); + $selector = $request->input('format') === 'strm' ? 'STRM' : 'YearQuarterID'; + try { + $cyqs = CourseYearQuarter::where($selector, '=', $yqrid) ->where('Department', '=', $subjectid) - ->select('CourseID', 'Department', 'CourseNumber', 'YearQuarterID', 'STRM') - ->groupBy('CourseID', 'Department', 'CourseNumber', 'YearQuarterID', 'STRM') + ->distinctCourses() ->orderBy('CourseNumber', 'asc') ->get(); - - //$queries = DB::connection('cs')->getQueryLog(); - //dd($queries); - } else { - //DB::connection('cs')->enableQueryLog(); - $cyqs = DB::connection('ods') - ->table('vw_Class') - ->where('YearQuarterID', '=', $yqrid) - ->where('Department', '=', $subjectid) - ->select('CourseID', 'Department', 'CourseNumber', 'YearQuarterID', 'STRM') - ->groupBy('CourseID', 'Department', 'CourseNumber', 'YearQuarterID', 'STRM') - ->orderBy('CourseNumber', 'asc') - ->get(); - - //$queries = DB::connection('cs')->getQueryLog(); - //dd($queries); + return new CourseYearQuarterCollection($cyqs); + } catch (\Exception $e) { + return response()->json(['classes' => []], 404); } - - $data = $cyqs; - - if ( !is_null($cyqs) && !$cyqs->isEmpty() ) { - //When using the Eloquent query builder, we must "hydrate" the results back to collection of objects - $cyqs_hydrated = CourseYearQuarter::hydrate($cyqs->toArray()); - $collection = new Collection($cyqs_hydrated, new CourseYearQuarterTransformer, self::WRAPPER); - - //define serializer - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($collection)->toArray(); - } - - return $this->respond($data); } } diff --git a/app/Http/Resources/CourseYearQuarterCollection.php b/app/Http/Resources/CourseYearQuarterCollection.php new file mode 100644 index 0000000..f566f67 --- /dev/null +++ b/app/Http/Resources/CourseYearQuarterCollection.php @@ -0,0 +1,23 @@ + + */ + public function toArray(Request $request): array + { + return $this->collection->toArray(); + } +} diff --git a/app/Http/Resources/CourseYearQuarterResource.php b/app/Http/Resources/CourseYearQuarterResource.php new file mode 100644 index 0000000..0229141 --- /dev/null +++ b/app/Http/Resources/CourseYearQuarterResource.php @@ -0,0 +1,49 @@ + + */ + public function toArray($request) + { + // Get the course description logic from the transformer + $description = null; + $course = $this->course; + if ($course) { + $allDesc = $course->coursedescriptions(); + if ($allDesc) { + $activeDesc = $allDesc->activedescription($this->YearQuarterID)->first(); + if ($activeDesc) { + $description = utf8_encode($activeDesc->Description); + } + } + } + + return [ + 'title' => $this->title, + 'subject' => trim($this->Department), + 'courseNumber' => trim($this->CourseNumber), + 'description' => $description, + 'note' => $course->note ?? $this->note ?? null, + /** @var int */ + 'credits' => $this->course->Credits, + 'quarter' => $this->YearQuarterID, + 'strm' => $this->STRM, + 'isVariableCredits' => (bool)$course->isVariableCredits, + 'isCommonCourse' => (bool)$course->isCommonCourse, + 'sections' => [ 'data' => + new SectionCollection($this->sections), + ], + ]; + } +} diff --git a/app/Http/Resources/SectionCollection.php b/app/Http/Resources/SectionCollection.php new file mode 100644 index 0000000..a19a92d --- /dev/null +++ b/app/Http/Resources/SectionCollection.php @@ -0,0 +1,22 @@ + + */ + public function toArray(Request $request): array + { + return parent::toArray($request); + } +} diff --git a/app/Http/Resources/SectionResource.php b/app/Http/Resources/SectionResource.php new file mode 100644 index 0000000..60f21d4 --- /dev/null +++ b/app/Http/Resources/SectionResource.php @@ -0,0 +1,43 @@ + + */ + public function toArray(Request $request): array + { + /** + * Get asssociated days + */ + $days = trim($this->DayID); + if ($days == "ARR") + $days = "To be arranged"; + elseif ($days == "DALY") + $days = "Daily"; + elseif ($days != "") { + $days = str_replace("U","Su",$days); + $days = str_replace("R","Th",$days); + } + return [ + 'id' => $this->ClassID, + 'section' => $this->Section, + 'itemNumber' => $this->ItemNumber, + 'classNumber' => $this->ClassNumber, + 'instructor' => $this->InstructorName, + 'beginDate' => $this->getFormattedDate($this->StartDate), + 'endDate' => $this->getFormattedDate($this->EndDate), + 'room' => $this->location, + 'days' => $days, + 'schedule' => trim($this->schedule), + 'roomDescription' => $this->RoomDescr, + ]; + } +} diff --git a/app/Http/Transformers/CourseYearQuarterTransformer.php b/app/Http/Transformers/CourseYearQuarterTransformer.php deleted file mode 100644 index 4fb0486..0000000 --- a/app/Http/Transformers/CourseYearQuarterTransformer.php +++ /dev/null @@ -1,71 +0,0 @@ -course->coursedescriptions(); - $cd_desc = null; - if ( !is_null($all_desc) ) - { - $cd_active = $all_desc->activedescription($cyq->YearQuarterID)->first(); - - if ( !empty($cd_active) ) { - $cd_desc = utf8_encode($cd_active->Description); - } - } - - return [ - 'title' => $cyq->title, - 'subject' => trim($cyq->Department), - 'courseNumber' => trim($cyq->CourseNumber), - 'description' => $cd_desc, - 'note' => $cyq->course->note, - 'credits' => $cyq->course->Credits, - 'quarter' => $cyq->YearQuarterID, - 'strm' => $cyq->STRM, - 'isVariableCredits' => (bool)$cyq->course->VariableCredits, - 'isCommonCourse' => $cyq->course->isCommonCourse, - ]; - } - - /** - * Include sections for class - * - * @return League\Fractal\Resource\Collection - */ - public function includeSections(CourseYearQuarter $cyq) - { - $sections = $cyq->getSectionsAttribute(); - - if ( $sections->count() == 0) { - //return null resource - return $this->null(); - } - //set resource key as false so data isn't "double wrapped" with a section identifier - $sections_transformed = $this->collection($sections, new SectionTransformer, false); - - return $sections_transformed; - } - -} diff --git a/app/Http/Transformers/SectionTransformer.php b/app/Http/Transformers/SectionTransformer.php deleted file mode 100644 index a69d71f..0000000 --- a/app/Http/Transformers/SectionTransformer.php +++ /dev/null @@ -1,44 +0,0 @@ -DayID); - if ($days == "ARR") - $days = "To be arranged"; - elseif ($days == "DALY") - $days = "Daily"; - elseif ($days != "") { - $days = str_replace("U","Su",$days); - $days = str_replace("R","Th",$days); - } - - return [ - 'id' => $sec->ClassID, - 'section' => $sec->Section, - 'itemNumber' => $sec->ItemNumber, - 'classNumber' => $sec->ClassNumber, - 'instructor' => $sec->InstructorName, - 'beginDate' => $sec->getFormattedDate($sec->StartDate), - 'endDate' => $sec->getFormattedDate($sec->EndDate), - 'room' => $sec->location, - 'days' => $days, - 'schedule' => trim($sec->schedule), - 'roomDescription' => $sec->RoomDescr, - ]; - } -} diff --git a/app/Models/CourseYearQuarter.php b/app/Models/CourseYearQuarter.php index b6d41c9..4adecf0 100644 --- a/app/Models/CourseYearQuarter.php +++ b/app/Models/CourseYearQuarter.php @@ -2,54 +2,67 @@ use App\Models\Section; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Builder; use DB; + + class CourseYearQuarter extends Model { + // Allow for relationships based on composite keys + use \Awobaz\Compoships\Compoships; + /** * We can't use the name Class since it's a reserved word, so CourseYearQuarter to * represent a class (course offered in a year quarter) it is. This is NOT the same * as a section. * Relationship: A Course is offered in a YearQuarter (CourseYearQuarter), those year quarter offerings have Sections. **/ - protected $table = 'vw_Class'; - protected $connection = 'ods'; - protected $primaryKey = null; - public $timestamps = false; - - - public function course() { - //scope relationship to retrieve only course relevant to YearQuarter of this CourseYearQuarter - return $this->belongsTo('App\Models\Course', 'CourseID', 'CourseID')->activeasofyearquarter($this->YearQuarterID); - } - - /** - * Section is a child model of CourseYearQuarter - * Because of the data organization/definitions, we can't use standard relationship definitions (see course) - * so I made this accessor version instead. It will return the expected collection of child Section objects. + protected $table = 'vw_Class'; + protected $connection = 'ods'; + protected $primaryKey = null; + public $timestamps = false; + + + public function course() { + //scope relationship to retrieve only course relevant to YearQuarter of this CourseYearQuarter + return $this->belongsTo('App\Models\Course', 'CourseID', 'CourseID')->activeasofyearquarter($this->YearQuarterID); + } + + /** + * Set relationship for sections (use Compoships to handle composite keys) **/ - public function getSectionsAttribute() { + public function sections() { + return $this->hasMany( + Section::class, + ['YearQuarterID', 'Department', 'CourseNumber'], + ['YearQuarterID', 'Department', 'CourseNumber'] + ); + } - //Sections are children of CourseYearQuarter - $sections = Section::where('YearQuarterID', '=', $this->YearQuarterID) - ->where('Department', '=', $this->Department) - ->where('CourseNumber', '=', $this->CourseNumber) - ->get(); - - return $sections; - } - - /** + /** * Accessor that wraps logic around which course title should be returned **/ - public function getTitleAttribute() { - $title = $this->course->title; + public function getTitleAttribute() { + $title = $this->course->title; - if ( is_null($title) ) { - //if parent course title is null, default to CourseYearQuarter title - $title = $this->CourseTitle; + if ( is_null($title) ) { + //if parent course title is null, default to CourseYearQuarter title + $title = $this->CourseTitle; } return $title; - } + } + + /** + * Scope to only return distinct courses + * + * @param \Illuminate\Database\Eloquent\Builder $query + * @return void + */ + public function scopeDistinctCourses(Builder $query) + { + // previously was ->groupBy('CourseID', 'Department', 'CourseNumber', 'YearQuarterID', 'STRM') + $query->select('CourseID', 'Department', 'CourseNumber', 'YearQuarterID', 'STRM')->distinct(); + } } ?> \ No newline at end of file diff --git a/app/Models/Section.php b/app/Models/Section.php index caf1f55..cf8bde2 100644 --- a/app/Models/Section.php +++ b/app/Models/Section.php @@ -4,21 +4,23 @@ class Section extends Model { + // Allow for relationships based on composite keys + use \Awobaz\Compoships\Compoships; /** * Model for a section offered in a CourseYearQuarter. **/ - protected $table = 'vw_Class'; - protected $connection = 'ods'; - protected $primaryKey = 'PSClassID'; - public $incrementing = false; - public $timestamps = false; + protected $table = 'vw_Class'; + protected $connection = 'ods'; + protected $primaryKey = 'PSClassID'; + public $incrementing = false; + public $timestamps = false; - /** + /** * Defines child relationship for the section's schedule **/ - public function day() { - return $this->hasOne('App\Models\Day', 'DayID', 'DayID'); - } + public function day() { + return $this->hasOne('App\Models\Day', 'DayID', 'DayID'); + } /** * Defines child relationship for the section's schedules. @@ -28,22 +30,22 @@ public function classSchedules() : HasMany return $this->hasMany(classSchedule::class, 'PSClassID', 'PSClassID'); } - /** + /** * This accessor wraps some logic around the room/location for a class. **/ - public function getLocationAttribute() { - if ( $this->isOnlineSection() ) { - //can't have a room for an online section - return null; - } else { - return trim($this->Room); - } - } + public function getLocationAttribute() { + if ( $this->isOnlineSection() ) { + //can't have a room for an online section + return null; + } else { + return trim($this->Room); + } + } - /** + /** * This accessor wraps logic around the section schedule **/ - public function getScheduleAttribute() { + public function getScheduleAttribute() { $schedule = ""; if ( !is_null($this->StartTime) && !is_null($this->EndTime) ) { $st = new \DateTime($this->StartTime); @@ -54,33 +56,33 @@ public function getScheduleAttribute() { $schedule = $st_str . "-" . $et_str; } if ( $this->isOnlineSection() ) { - //online section so don't include day/time info - $schedule = "Online"; + //online section so don't include day/time info + $schedule = "Online"; } return $schedule; - } + } - /** + /** * Format a date to the format we want to use for section dates **/ - public function getFormattedDate($date) { + public function getFormattedDate($date) { $date_str = ""; if ( !is_null($date) ) { - $dateobj = new \DateTime($date); - $date_str = $dateobj->format("m-d-Y"); + $dateobj = new \DateTime($date); + $date_str = $dateobj->format("m-d-Y"); } return $date_str; - } + } - /** + /** * Logic to determine of section is an online section **/ - protected function isOnlineSection () { - if ( !empty($this->Section) && $this->Section == "OAS" ) { - return true; - } else { - return false; - } - } + protected function isOnlineSection () { + if ( !empty($this->Section) && $this->Section == "OAS" ) { + return true; + } else { + return false; + } + } } ?> diff --git a/composer.json b/composer.json index 64442be..a81760a 100644 --- a/composer.json +++ b/composer.json @@ -6,6 +6,7 @@ "type": "project", "require": { "php": "^8.3.6", + "awobaz/compoships": "^2.5", "dedoc/scramble": "^0.13.4", "directorytree/ldaprecord-laravel": "*", "laravel/framework": "^11.0", diff --git a/composer.lock b/composer.lock index cd85537..e6e824b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,70 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b19e241b7ca69307b29be9fa416a4eb2", + "content-hash": "4e266e47a69ea759f7e32a2d95ed0ca8", "packages": [ + { + "name": "awobaz/compoships", + "version": "2.5.1", + "source": { + "type": "git", + "url": "https://github.com/topclaudy/compoships.git", + "reference": "d8de30b57949d6021bb0312105f6d9d0920266b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/topclaudy/compoships/zipball/d8de30b57949d6021bb0312105f6d9d0920266b6", + "reference": "d8de30b57949d6021bb0312105f6d9d0920266b6", + "shasum": "" + }, + "require": { + "illuminate/database": ">=5.6 <13.0" + }, + "require-dev": { + "ext-sqlite3": "*", + "fakerphp/faker": "^1.18", + "phpunit/phpunit": "^6.0|^8.0|^9.0|^10.0|^11.0|^12.0" + }, + "suggest": { + "awobaz/blade-active": "Blade directives for the Laravel 'Active' package", + "awobaz/eloquent-auto-append": "Automatically append accessors to model serialization", + "awobaz/eloquent-mutators": "Reusable mutators (getters/setters) for Laravel 5's Eloquent", + "awobaz/syntactic": "Syntactic sugar for named and indexed parameters call." + }, + "type": "library", + "autoload": { + "psr-4": { + "Awobaz\\Compoships\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Claudin J. Daniel", + "email": "cdaniel@awobaz.com" + } + ], + "description": "Laravel relationships with support for composite/multiple keys", + "keywords": [ + "laravel", + "laravel composite keys", + "laravel relationships" + ], + "support": { + "issues": "https://github.com/topclaudy/compoships/issues", + "source": "https://github.com/topclaudy/compoships/tree/2.5.1" + }, + "funding": [ + { + "url": "https://paypal.me/awobaz", + "type": "custom" + } + ], + "time": "2025-10-10T14:07:12+00:00" + }, { "name": "brick/math", "version": "0.14.0", From 29c9e343f8a22a7b047885e4e40637519c4f3b16 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 21 Nov 2025 13:55:02 -0800 Subject: [PATCH 23/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Course=20Contro?= =?UTF-8?q?ller=20to=20use=20Resources?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/CourseController.php | 149 ++++++++------------ app/Http/Resources/CourseCollection.php | 24 ++++ app/Http/Resources/CourseResource.php | 43 ++++++ app/Http/Transformers/CourseTransformer.php | 45 ------ app/Models/Course.php | 7 + 5 files changed, 134 insertions(+), 134 deletions(-) create mode 100644 app/Http/Resources/CourseCollection.php create mode 100644 app/Http/Resources/CourseResource.php delete mode 100644 app/Http/Transformers/CourseTransformer.php diff --git a/app/Http/Controllers/CourseController.php b/app/Http/Controllers/CourseController.php index 9c0aa26..101d71c 100644 --- a/app/Http/Controllers/CourseController.php +++ b/app/Http/Controllers/CourseController.php @@ -3,14 +3,10 @@ namespace App\Http\Controllers; use App\Models\Course; +use App\Http\Resources\CourseResource; +use App\Http\Resources\CourseCollection; use Illuminate\Http\Request; use App\Exceptions\MissingParameterException; -use League\Fractal\Manager; -use League\Fractal\Resource\Item; -use League\Fractal\Resource\Collection; -use App\Http\Transformers\CourseTransformer; -use App\Http\Serializers\CustomDataArraySerializer; -use DB; class CourseController extends ApiController{ @@ -31,122 +27,98 @@ public function index(){ } /** - * Get a course based on a given CourseID. + * Get Course by CourseID + * + * Course ID is a combination of the Course Subject and Catalog Number, space separated * * @param int $courseid * - * @return \Illuminate\Http\JsonResponse + * @return CourseResource | \Illuminate\Http\JsonResponse **/ - public function getCourse($courseid) + public function getCourse($courseid, Request $request) { - - $course = Course::where('CourseID', '=', $courseid)->active()->first(); - - $data = $course; - if ( !is_null($course) ) { - $item = new Item($course, new CourseTransformer, "course"); - - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($item)->toArray(); + try { + $course = Course::where('CourseID', '=', $courseid)->active()->firstOrFail(); + return new CourseResource($course); + } catch (\Exception $e) { + return response()->json(['courses' => []], 404); } - return $this->respond($data); } /** - * Get a course based on a given subject and course number. + * Get Course by Subject and Catalog Number * * @param string $subject * @param string $coursenum * - * @return \Illuminate\Http\JsonResponse + * @return CourseResource | \Illuminate\Http\JsonResponse **/ public function getCourseBySubjectAndNumber($subject, $coursenum){ - - $course = Course::where('CourseSubject', '=', $subject) - ->where('CatalogNumber', '=', $coursenum) - ->active() - ->first(); - - $data = $course; - if ( !is_null($course) ) { - $item = new Item($course, new CourseTransformer, "course"); - - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($item)->toArray(); + try { + $course = Course::where('CourseSubject', '=', $subject) + ->where('CatalogNumber', '=', $coursenum) + ->active() + ->firstOrFail(); + return new CourseResource($course); + } catch (\Exception $e) { + return response()->json(['courses' => []], 404); } - return $this->respond($data); } /** - * Get active courses based on a given subject. + * List Active Courses by Subject * * @param string $subject * - * @return \Illuminate\Http\JsonResponse + * @return CourseCollection | \Illuminate\Http\JsonResponse **/ public function getCoursesBySubject($subject) { - - $courses = Course::where('CourseSubject', '=', $subject) - ->where(function ($query) { - $query->where('CourseTitle2', '<>', 'Transfer In Course') - ->where('CourseTitle', '<>', 'Transferred-In Course'); - }) - ->orderBy('CourseId', 'asc') - ->active() - ->get(); - - $data = $courses; - if ( !is_null($courses) ) { - $collection = new Collection($courses, new CourseTransformer, self::WRAPPER); - - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($collection)->toArray(); + try { + $courses = Course::where('CourseSubject', '=', $subject) + ->notTransferIn() + ->active() + ->orderBy('CourseId', 'asc') + ->get(); + + return new CourseCollection($courses); + } catch (\Exception $e) { + return response()->json(['courses' => []], 404); } - return $this->respond($data); } /** - * Get multiple courses based on course numbers passed via courses[] query parameter + * Get Multiple Courses by CourseID + * + * Courses are passed as an array via the courses[] query parameter * * @param \Illuminate\Http\Request $request * - * @return \Illuminate\Http\JsonResponse + * @return CourseCollection | \Illuminate\Http\JsonResponse **/ - public function getMultipleCourses(Request $request){ - - $course_input = $request->input('courses'); - if ( !is_array($course_input) ) { - //be nice and don't fail non-array input for the case there's a single value - $course_input[] = $course_input; - } - if ( !empty($course_input) && count($course_input) > 0 ) { - //valid courses parameter so get courses - //strip whitespace so we can do db comparison - $courses_stripped = $course_input; - array_walk($courses_stripped, array($this, 'stripWhitespace')); - $placeholder = implode(', ', array_fill(0, count($courses_stripped), '?')); - - $courses = Course::whereRaw("REPLACE(CourseID, ' ', '') IN ($placeholder)", $courses_stripped)->active()->get(); - - $data = $courses; - if ( !is_null($courses) ) { - $collection = new Collection($courses, new CourseTransformer, self::WRAPPER); - - // define serializer - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($collection)->toArray(); + public function getMultipleCourses(Request $request) + { + $validated = $request->validate([ + 'courses' => 'required|array', + ]); + try { + $course_input = $request->input('courses'); + + if ( !empty($course_input) && count($course_input) > 0 ) { + //valid courses parameter so get courses + //strip whitespace so we can do db comparison + $courses_stripped = $course_input; + array_walk($courses_stripped, array($this, 'stripWhitespace')); + $placeholder = implode(', ', array_fill(0, count($courses_stripped), '?')); + + $courses = Course::whereRaw("REPLACE(CourseID, ' ', '') IN ($placeholder)", $courses_stripped)->active()->get(); + return new CourseCollection($courses); + } else { + throw new MissingParameterException("Invalid courses[] parameter provided."); } - - return $this->respond($data); - } else { - throw new MissingParameterException("Invalid courses[] parameter provided."); + } catch (\Exception $e) { + return response()->json(['courses' => []], 404); } - } /** @@ -161,5 +133,4 @@ private function stripWhitespace(&$value, $key){ $value = preg_replace('/\s+/', '', $value); } -} - +} \ No newline at end of file diff --git a/app/Http/Resources/CourseCollection.php b/app/Http/Resources/CourseCollection.php new file mode 100644 index 0000000..63950d1 --- /dev/null +++ b/app/Http/Resources/CourseCollection.php @@ -0,0 +1,24 @@ + + */ + public function toArray(Request $request): array + { + return $this->collection->toArray(); + } +} diff --git a/app/Http/Resources/CourseResource.php b/app/Http/Resources/CourseResource.php new file mode 100644 index 0000000..94de8e5 --- /dev/null +++ b/app/Http/Resources/CourseResource.php @@ -0,0 +1,43 @@ + + */ + public function toArray(Request $request): array + { + //filter to get the active course description + $all_desc = $this->coursedescriptions(); + $cd_desc = null; + + if (!is_null($all_desc)) { + $cd_active = $all_desc->activedescription()->first(); + + if (!empty($cd_active)) { + $cd_desc = utf8_encode($cd_active->Description); + } + } + + return [ + 'title' => $this->title, + 'subject' => $this->CourseSubject, + 'courseNumber' => $this->CatalogNumber, + 'courseId' => $this->CourseID, + 'ctcCourseId' => $this->PSCourseID, + 'description' => $cd_desc, + 'note' => $this->note, + 'credits' => $this->Credits, + 'isVariableCredits' => (bool)$this->VariableCredits, + 'isCommonCourse' => (bool)$this->isCommonCourse, + ]; + } +} diff --git a/app/Http/Transformers/CourseTransformer.php b/app/Http/Transformers/CourseTransformer.php deleted file mode 100644 index 60166e0..0000000 --- a/app/Http/Transformers/CourseTransformer.php +++ /dev/null @@ -1,45 +0,0 @@ -coursedescriptions(); - $cd_desc = null; - - if (!is_null($all_desc)) { - $cd_active = $all_desc->activedescription()->first(); - - if (!empty($cd_active)) { - $cd_desc = utf8_encode($cd_active->Description); - } - } - - - return [ - 'title' => $course->title, - 'subject' => $course->CourseSubject, - 'courseNumber' => $course->CatalogNumber, - 'courseId' => $course->CourseID, - 'ctcCourseId' => $course->PSCourseID, - 'description' => $cd_desc, - 'note' => $course->note, - 'credits' => $course->Credits, - 'isVariableCredits' => (bool)$course->VariableCredits, - 'isCommonCourse' => $course->isCommonCourse, - ]; - } -} diff --git a/app/Models/Course.php b/app/Models/Course.php index 911dee9..0e13029 100644 --- a/app/Models/Course.php +++ b/app/Models/Course.php @@ -88,4 +88,11 @@ public function scopeActiveAsOfYearQuarter($query, $yqrid) ->orWhereNull('EffectiveYearQuarterEnd'); }); } + + /** Scope to filter out transfer-in courses */ + public function scopeNotTransferIn($query) + { + return $query->where('CourseTitle2', '<>', 'Transfer In Course') + ->where('CourseTitle', '<>', 'Transferred-In Course'); + } } From c060fd0dd6987e79707de0738b8b49978bec0331 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 21 Nov 2025 14:18:35 -0800 Subject: [PATCH 24/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20ClassSchedule?= =?UTF-8?q?=20to=20use=20Resource=20and=20Collection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/ClassScheduleController.php | 26 ++++-------- .../Resources/ClassScheduleCollection.php | 23 +++++++++++ app/Http/Resources/ClassScheduleResource.php | 36 +++++++++++++++++ .../Transformers/ClassScheduleTransformer.php | 40 ------------------- 4 files changed, 67 insertions(+), 58 deletions(-) create mode 100644 app/Http/Resources/ClassScheduleCollection.php create mode 100644 app/Http/Resources/ClassScheduleResource.php delete mode 100644 app/Http/Transformers/ClassScheduleTransformer.php diff --git a/app/Http/Controllers/ClassScheduleController.php b/app/Http/Controllers/ClassScheduleController.php index 51d40fc..30e488e 100644 --- a/app/Http/Controllers/ClassScheduleController.php +++ b/app/Http/Controllers/ClassScheduleController.php @@ -5,13 +5,7 @@ use App\Models\ClassSchedule; use Illuminate\Http\Request; use App\Exceptions\MissingParameterException; -use League\Fractal\Manager; -use League\Fractal\Resource\Item; -use League\Fractal\Resource\Collection; -use App\Http\Transformers\ClassScheduleTransformer; -use App\Http\Serializers\CustomDataArraySerializer; -use DB; - +use App\Http\Resources\ClassScheduleCollection; class ClassScheduleController extends ApiController{ @@ -22,17 +16,13 @@ class ClassScheduleController extends ApiController{ * Status: inactive * No route, not yet serialized **/ - public function getClassSchedules($psclassid, Request $request){ - $schedules = ClassSchedule::where('PSClassID', '=', $psclassid)->get(); - - $data = $schedules; - if ( !is_null($schedules) ) { - $collection = new Collection($schedules, new ClassScheduleTransformer, self::WRAPPER); - - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($collection)->toArray(); + public function getClassSchedules($psclassid, Request $request) + { + try { + $schedules = ClassSchedule::where('PSClassID', '=', $psclassid)->get(); + return new ClassScheduleCollection($schedules); + } catch (\Exception $e) { + return response()->json(['classSchedules' => []], 404); } - return $this->respond($data); } } diff --git a/app/Http/Resources/ClassScheduleCollection.php b/app/Http/Resources/ClassScheduleCollection.php new file mode 100644 index 0000000..fa9c4ab --- /dev/null +++ b/app/Http/Resources/ClassScheduleCollection.php @@ -0,0 +1,23 @@ + + */ + public function toArray(Request $request): array + { + return parent::toArray($request); + } +} diff --git a/app/Http/Resources/ClassScheduleResource.php b/app/Http/Resources/ClassScheduleResource.php new file mode 100644 index 0000000..1942ed0 --- /dev/null +++ b/app/Http/Resources/ClassScheduleResource.php @@ -0,0 +1,36 @@ + + */ + public function toArray(Request $request): array + { + $days = trim($this->DayID); + if ($days == "ARR") + $days = "To be arranged"; + elseif ($days == "DALY") + $days = "Daily"; + elseif ($days != "") { + $days = str_replace("U","Su",$days); + $days = str_replace("R","Th",$days); + } + return [ + 'days' => $days, + 'room' => $this->location, + 'roomDescription' => $this->RoomDescr, + 'instructor' => $this->InstructorName, + 'beginDate' => $this->getFormattedDate($this->StartDate), + 'endDate' => $this->getFormattedDate($this->EndDate), + 'schedule' => trim($this->schedule), + ]; + } +} diff --git a/app/Http/Transformers/ClassScheduleTransformer.php b/app/Http/Transformers/ClassScheduleTransformer.php deleted file mode 100644 index 1583e76..0000000 --- a/app/Http/Transformers/ClassScheduleTransformer.php +++ /dev/null @@ -1,40 +0,0 @@ -DayID); - if ($days == "ARR") - $days = "To be arranged"; - elseif ($days == "DALY") - $days = "Daily"; - elseif ($days != "") { - $days = str_replace("U","Su",$days); - $days = str_replace("R","Th",$days); - } - - return [ - 'days' => $days, - 'room' => $sch->location, - 'roomDescription' => $sch->RoomDescr, - 'instructor' => $sch->InstructorName, - 'beginDate' => $sch->getFormattedDate($sch->StartDate), - 'endDate' => $sch->getFormattedDate($sch->EndDate), - 'schedule' => trim($sch->schedule), - ]; - } -} From 053852360181684cdfe7b5413e3dae40a2a8210f Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 21 Nov 2025 14:38:04 -0800 Subject: [PATCH 25/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Convert=20LinkF?= =?UTF-8?q?ound=20to=20use=20Resource?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/LinkFoundController.php | 55 +++++-------------- app/Http/Resources/LinkFoundCollection.php | 22 ++++++++ app/Http/Resources/LinkFoundResource.php | 23 ++++++++ .../Transformers/LinkFoundTransformer.php | 22 -------- 4 files changed, 59 insertions(+), 63 deletions(-) create mode 100644 app/Http/Resources/LinkFoundCollection.php create mode 100644 app/Http/Resources/LinkFoundResource.php delete mode 100644 app/Http/Transformers/LinkFoundTransformer.php diff --git a/app/Http/Controllers/LinkFoundController.php b/app/Http/Controllers/LinkFoundController.php index 0c407a9..07d84ef 100644 --- a/app/Http/Controllers/LinkFoundController.php +++ b/app/Http/Controllers/LinkFoundController.php @@ -3,34 +3,21 @@ namespace App\Http\Controllers; use App\Models\LinkFound; -use App\Http\Transformers\LinkFoundTransformer; -use App\Http\Controllers\Controller; -use App\Http\Serializers\CustomDataArraySerializer; +use App\Http\Resources\LinkFoundCollection; use Illuminate\Http\Request; -use League\Fractal\Manager; -use League\Fractal\Resource\Collection; -use League\Fractal\Resource\Item; -use DB; +use Illuminate\Http\JsonResponse; class LinkFoundController extends ApiController { - - const WRAPPER = "Links"; - /** * Return all links * - * @return \Illuminate\Http\JsonResponse + * @return LinkFoundCollection **/ - public function index(Manager $fractal, Request $request) + public function index(Request $request) { $links = LinkFound::all(); - $collection = new Collection($links, new LinkFoundTransformer); - - $fractal = new Manager; - $data = $fractal->createData($collection)->toArray(); - - return $this->respond($data); + return new LinkFoundCollection($links); } /** @@ -38,29 +25,17 @@ public function index(Manager $fractal, Request $request) * * @param string $sourceArea SourceArea value * - * @return \Illuminate\Http\JsonResponse + * @return LinkFoundCollection | JsonResponse **/ public function getLinksBySourceArea($sourceArea) { - $links = DB::connection('copilot') - ->table('vw_LinkFound') - ->where('SourceArea', '=', str_replace("+", " ",$sourceArea)) - ->select('LinkText', 'LinkDescr') - ->get(); - - $data = $links; - if (!is_null($links) && !$links->isEmpty()) { - //When using the Eloquent query builder, we must "hydrate" the results back to collection of objects - $links_hydrated = LinkFound::hydrate($links->toArray()); - $collection = new Collection($links_hydrated, new LinkFoundTransformer, self::WRAPPER); - - //set data serializer - $fractal = new Manager; - $fractal->setSerializer(new CustomDataArraySerializer); - $data = $fractal->createData($collection)->toArray(); + try { + $links = LinkFound::where('SourceArea', '=', str_replace("+", " ",$sourceArea)) + ->get(); + return new LinkFoundCollection($links); + } catch (\Exception $e) { + return response()->json(['Links' => []]); } - - return $this->respond($data); } /** @@ -68,13 +43,11 @@ public function getLinksBySourceArea($sourceArea) * * @param string $sourceArea SourceArea value * - * @return \Illuminate\Http\JsonResponse + * @return int **/ public function getLinkCountBySourceArea($sourceArea) { - $count = DB::connection('copilot') - ->table('vw_LinkFound') - ->where('SourceArea', '=', str_replace("+", " ",$sourceArea)) + $count = LinkFound::where('SourceArea', '=', str_replace("+", " ",$sourceArea)) ->count(); return $this->respond($count); diff --git a/app/Http/Resources/LinkFoundCollection.php b/app/Http/Resources/LinkFoundCollection.php new file mode 100644 index 0000000..d6cb941 --- /dev/null +++ b/app/Http/Resources/LinkFoundCollection.php @@ -0,0 +1,22 @@ + + */ + public function toArray(Request $request): array + { + return $this->collection->toArray(); + } +} diff --git a/app/Http/Resources/LinkFoundResource.php b/app/Http/Resources/LinkFoundResource.php new file mode 100644 index 0000000..a369f37 --- /dev/null +++ b/app/Http/Resources/LinkFoundResource.php @@ -0,0 +1,23 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'Link' => $this->LinkText, + 'Description' => $this->LinkDescr, + ]; + } +} diff --git a/app/Http/Transformers/LinkFoundTransformer.php b/app/Http/Transformers/LinkFoundTransformer.php deleted file mode 100644 index 934ce19..0000000 --- a/app/Http/Transformers/LinkFoundTransformer.php +++ /dev/null @@ -1,22 +0,0 @@ - $link->LinkText, - 'Description' => $link->LinkDescr, - ]; - } -} From 03564f0dfe1070ab4d9e5505cf17e482a58901d6 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 21 Nov 2025 14:38:19 -0800 Subject: [PATCH 26/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Remove=20Fracta?= =?UTF-8?q?l=20(no=20longer=20used)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Serializers/CustomDataArraySerializer.php | 54 -------------- composer.json | 1 - composer.lock | 72 +------------------ 3 files changed, 1 insertion(+), 126 deletions(-) delete mode 100644 app/Http/Serializers/CustomDataArraySerializer.php diff --git a/app/Http/Serializers/CustomDataArraySerializer.php b/app/Http/Serializers/CustomDataArraySerializer.php deleted file mode 100644 index 6ba6e25..0000000 --- a/app/Http/Serializers/CustomDataArraySerializer.php +++ /dev/null @@ -1,54 +0,0 @@ - $data); - } - - /** - * Serialize an item. - * - * @param string $resourceKey - * @param array $data - * - * @return array - */ - public function item(?string $resourceKey, array $data):array - { - if ($resourceKey === false) { - return $data; - } - return array($resourceKey ?: 'data' => $data); - } - - /** - * Serialize null resource. - * - * @return array - */ - public function null():?array - { - return null; - } -} diff --git a/composer.json b/composer.json index a81760a..247ab31 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,6 @@ "laravel/legacy-factories": "^1.1", "laravel/tinker": "^2.0", "laravel/ui": "^4.2.2", - "league/fractal": "^0.20.1", "tymon/jwt-auth": "^2.0.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index e6e824b..1c4291a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4e266e47a69ea759f7e32a2d95ed0ca8", + "content-hash": "bf958b8761096e3bce8ffd67df3fd5e4", "packages": [ { "name": "awobaz/compoships", @@ -2373,76 +2373,6 @@ }, "time": "2025-11-10T11:23:37+00:00" }, - { - "name": "league/fractal", - "version": "0.20.2", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/fractal.git", - "reference": "573ca2e0e348a7fe573a3e8fbc29a6588ece8c4e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/fractal/zipball/573ca2e0e348a7fe573a3e8fbc29a6588ece8c4e", - "reference": "573ca2e0e348a7fe573a3e8fbc29a6588ece8c4e", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "doctrine/orm": "^2.5", - "illuminate/contracts": "~5.0", - "laminas/laminas-paginator": "~2.12", - "mockery/mockery": "^1.3", - "pagerfanta/pagerfanta": "~1.0.0|~4.0.0", - "phpstan/phpstan": "^1.4", - "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "~3.4", - "vimeo/psalm": "^4.30" - }, - "suggest": { - "illuminate/pagination": "The Illuminate Pagination component.", - "laminas/laminas-paginator": "Laminas Framework Paginator", - "pagerfanta/pagerfanta": "Pagerfanta Paginator" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.20.x-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Fractal\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Phil Sturgeon", - "email": "me@philsturgeon.uk", - "homepage": "http://philsturgeon.uk/", - "role": "Developer" - } - ], - "description": "Handle the output of complex data structures ready for API output.", - "homepage": "http://fractal.thephpleague.com/", - "keywords": [ - "api", - "json", - "league", - "rest" - ], - "support": { - "issues": "https://github.com/thephpleague/fractal/issues", - "source": "https://github.com/thephpleague/fractal/tree/0.20.2" - }, - "time": "2025-02-14T21:33:14+00:00" - }, { "name": "league/mime-type-detection", "version": "1.16.0", From c862946169daf453e78c572db556f2f43e822f7b Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Mon, 24 Nov 2025 08:41:36 -0800 Subject: [PATCH 27/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Clean=20Up=20Un?= =?UTF-8?q?used=20Endpoints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/SubjectController.php | 25 ---------------------- app/Http/Resources/YearQuarterResource.php | 2 +- routes/api.php | 8 ------- 3 files changed, 1 insertion(+), 34 deletions(-) diff --git a/app/Http/Controllers/SubjectController.php b/app/Http/Controllers/SubjectController.php index c8beccf..1335722 100644 --- a/app/Http/Controllers/SubjectController.php +++ b/app/Http/Controllers/SubjectController.php @@ -66,29 +66,4 @@ public function getSubject($slug) return response()->json(new stdClass()); } } - - /** - * List Subjects in a YRQ - * - * @param string $yqr YearQuarterID - * - * @return SubjectCollection | stdClass - **/ - public function getSubjectsByYearQuarter($yqr) - { - try { - $subjects = DB::connection('ods') - ->table('vw_PSSubject') - ->join('vw_Class', 'vw_PSSubject.SUBJECT', '=', 'vw_Class.Department') - ->where('vw_Class.YearQuarterID', '=', $yqr) - ->select('vw_PSSubject.SUBJECT', 'vw_PSSubject.DESCR as DESCR') - ->groupBy('vw_PSSubject.SUBJECT', 'vw_PSSubject.DESCR') - ->orderBy('vw_PSSubject.SUBJECT', 'asc') - ->get(); - - return new SubjectCollection($subjects); - } catch (\Exception $e) { - return response()->json(new stdClass()); - } - } } diff --git a/app/Http/Resources/YearQuarterResource.php b/app/Http/Resources/YearQuarterResource.php index 5730327..5d2f602 100644 --- a/app/Http/Resources/YearQuarterResource.php +++ b/app/Http/Resources/YearQuarterResource.php @@ -17,7 +17,7 @@ public function toArray(Request $request): array return [ 'quarter' => $this->YearQuarterID, 'strm' => $this->STRM, - 'title' => $this->Title + 'title' => $this->Title, ]; } } diff --git a/routes/api.php b/routes/api.php index 489ba0f..98bc702 100644 --- a/routes/api.php +++ b/routes/api.php @@ -145,18 +145,10 @@ Route::get('schedules/{psclassid}', 'ClassScheduleController@getClassSchedules'); - /* Additions by John begin */ // This is for Copilot Studio: Get an array of links and descriptions based on a provided SourceArea value Route::get('linksfound/{sourcearea}', 'LinkFoundController@getLinksBySourceArea'); // This is for Copilot Studio: Get a count of the links based on a provided SourceArea value Route::get('linkscount/{sourcearea}', 'LinkFoundController@getLinkCountBySourceArea'); - /* Additions by John end */ - //API endpoints specific to ModoLabs requirements - /*Route::get('catalog/terms', 'YearQuarterController@getViewableYearQuarters'); - Route::get('catalog/terms/{yqrid}', 'YearQuarterController@getYearQuarter'); - Route::get('catalog/catalogAreas/{yqrid}', 'SubjectController@getSubjectsByYearQuarter'); - Route::get('catalog/{yqrid}/{subjectid}', 'CourseYearQuarterController@getCourseYearQuartersBySubject'); - Route::get('catalog/{yqrid}/{subjectid}/{coursenum}', 'CourseYearQuarterController@getCourseYearQuarter');*/ }); From 9da08b57d4c636e44f73de8ec29cc3f0c004e131 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Mon, 24 Nov 2025 08:43:46 -0800 Subject: [PATCH 28/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Active=20Quarte?= =?UTF-8?q?r=20Logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved manual query to use elequent models instead. --- .../Controllers/YearQuarterController.php | 58 +++++++------------ app/Models/WebRegistrationSetting.php | 27 +++++++++ app/Models/YearQuarter.php | 23 +++++--- 3 files changed, 64 insertions(+), 44 deletions(-) create mode 100644 app/Models/WebRegistrationSetting.php diff --git a/app/Http/Controllers/YearQuarterController.php b/app/Http/Controllers/YearQuarterController.php index e489e60..9a577ba 100644 --- a/app/Http/Controllers/YearQuarterController.php +++ b/app/Http/Controllers/YearQuarterController.php @@ -7,7 +7,8 @@ use App\Http\Resources\YearQuarterCollection; use App\Http\Controllers\Controller; use Illuminate\Http\Request; -use DB; +use Carbon\Carbon; +use stdClass; class YearQuarterController extends ApiController { @@ -92,47 +93,30 @@ public function getCurrentYearQuarter() { public function getViewableYearQuarters() { try { - //get max quarters to be shown + // get max quarters to be shown $max_yqr = config('dataapi.yearquarter_maxactive'); - $lookahead = config('dataapi.yearquarter_lookahead'); - $timezone = new \DateTimeZone(config("dataapi.timezone")); - //Create now date/time object - $now = new \DateTime(); - $now->setTimezone($timezone); - $now_string = $now->format("Y-m-d H:i:s"); - - //Create lookahead date - $la_date = $now->add(new \DateInterval('P'.$lookahead.'D')); - $la_string = $la_date->format("Y-m-d H:i:s"); - - //Use Eloquent query builder to query results - //DB::connection('ods')->enableQueryLog(); - $yqrs = DB::connection('ods') - ->table('vw_YearQuarter') - ->join('vw_WebRegistrationSetting', 'vw_WebRegistrationSetting.YearQuarterID', '=', 'vw_YearQuarter.YearQuarterID') - ->where(function ($query) { - $lookahead = config('dataapi.yearquarter_lookahead'); - $timezone = new \DateTimeZone(config("dataapi.timezone")); - - //Create now date/time object - $now = new \DateTime(); - $now->setTimezone($timezone); - $now_string = $now->format("Y-m-d H:i:s"); - - //Create lookahead date - $la_date = $now->add(new \DateInterval('P'.$lookahead.'D')); - $la_string = $la_date->format("Y-m-d H:i:s"); - - $query->whereNotNull('vw_WebRegistrationSetting.FirstRegistrationDate') - ->where('vw_WebRegistrationSetting.FirstRegistrationDate', '<=', $la_string ) - ->orWhere('vw_YearQuarter.LastClassDay', '<=', $now_string ); + // get max days to look ahead + $lookaheadDays = config('dataapi.yearquarter_lookahead'); + + //Create now date/time object and lookahead date + $now = Carbon::now(); + $lookahead = Carbon::now()->addDays($lookaheadDays); + + // get active year quarters based on registration settings + // ideally this should move to the model in the future + $yrqs = YearQuarter::whereHas('webRegistrationSetting') + ->where(function($query) use ($lookahead, $now) { + $query->whereHas('webRegistrationSetting', function ($q) use ($lookahead) { + $q->whereNotNull('FirstRegistrationDate') + ->where('FirstRegistrationDate', '<=', $lookahead); + }) + ->orWhere('LastClassDay', '<=', $now); }) - ->select('vw_YearQuarter.YearQuarterID', 'vw_YearQuarter.STRM', 'vw_YearQuarter.Title', 'vw_YearQuarter.FirstClassDay', 'vw_YearQuarter.LastClassDay', 'vw_YearQuarter.AcademicYear') - ->orderBy('vw_YearQuarter.FirstClassDay', 'desc') + ->orderBy('FirstClassDay', 'desc') ->take($max_yqr) ->get(); - return new YearQuarterCollection($yqrs); + return new YearQuarterCollection($yrqs); } catch (\Exception $e) { return response()->json(new stdClass()); } diff --git a/app/Models/WebRegistrationSetting.php b/app/Models/WebRegistrationSetting.php new file mode 100644 index 0000000..af9f0c0 --- /dev/null +++ b/app/Models/WebRegistrationSetting.php @@ -0,0 +1,27 @@ + 'string', + 'FirstRegistrationDate' => 'datetime', + 'LastRegistrationDate' => 'datetime', + ]; + + /** + * Belongs to YearQuarter + */ + public function yearQuarter() + { + return $this->belongsTo(YearQuarter::class, 'STRM', 'STRM'); + } +} diff --git a/app/Models/YearQuarter.php b/app/Models/YearQuarter.php index 4a01da3..9771c31 100644 --- a/app/Models/YearQuarter.php +++ b/app/Models/YearQuarter.php @@ -1,5 +1,6 @@ - 'string', + 'FirstClassDay' => 'datetime', + 'LastClassDay' => 'datetime', ]; - protected $table = 'vw_YearQuarter'; - protected $connection = 'ods'; - protected $primaryKey = null; //Lumen will convert the YearQuarterID value to an integer if we make it aware of it - //protected $primaryKey = 'YearQuarterID'; + protected $table = 'vw_YearQuarter'; + protected $connection = 'ods'; + protected $primaryKey = null; - public function scopeCurrent($query) { + /** + * Has one WebRegistrationSetting + */ + public function webRegistrationSetting() + { + return $this->hasOne(WebRegistrationSetting::class, 'STRM', 'STRM'); + } + + public function scopeCurrent($query) { //Create now date/time object $timezone = new \DateTimeZone(config("app.timezone")); $now = new \DateTime(); @@ -29,4 +39,3 @@ public function scopeCurrent($query) { ->take(1); } } -?> From d22b251cec4898c88bb2c9cf90850aaac9dddb3e Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Wed, 17 Dec 2025 15:40:56 -0800 Subject: [PATCH 29/45] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20Base=20Azure=20OIDC?= =?UTF-8?q?=20SSO=20Config=20Using=20Socialite?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.azuredevops | 8 - .env.example | 9 - app/Http/.DS_Store | Bin 6148 -> 6148 bytes app/Providers/AppServiceProvider.php | 8 + app/Scopes/AdminGroupScope.php | 10 +- composer.json | 3 +- composer.lock | 716 +++++++++++++++++++++------ config/app.php | 1 + config/auth.php | 8 +- config/ldap.php | 81 --- config/ldap_auth.php | 341 ------------- config/services.php | 8 + routes/web.php | 25 +- 13 files changed, 620 insertions(+), 598 deletions(-) delete mode 100644 config/ldap.php delete mode 100644 config/ldap_auth.php diff --git a/.env.azuredevops b/.env.azuredevops index c7ce47a..698590b 100644 --- a/.env.azuredevops +++ b/.env.azuredevops @@ -59,14 +59,6 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" JWT_SECRET=#{jwt.secret}# -# Config for ADLDAP authentication -ADLDAP_CONTROLLERS=#{adldap.controllers}# -ADLDAP_BASEDN=#{adldap.basedn}# -ADLDAP_PORT=#{adldap.port}# -ADLDAP_ADMIN_USERNAME="#{adldap.username}#" -ADLDAP_ADMIN_PASSWORD="#{adldap.password}#" -ADLDAP_USE_SSL=#{adldap.useSSL}# -ADLDAP_USE_TLS=#{adldap.useTLS}# # Complete distinguished name of group to which to limit admin dashboard access ADMIN_GROUP="#{admin.group}#" diff --git a/.env.example b/.env.example index 7767a58..9a5c851 100644 --- a/.env.example +++ b/.env.example @@ -91,15 +91,6 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" JWT_SECRET= -# Config for ADLDAP authentication -ADLDAP_CONTROLLERS= -ADLDAP_BASEDN= -ADLDAP_PORT=636 -ADLDAP_ADMIN_USERNAME= -ADLDAP_ADMIN_PASSWORD= -ADLDAP_USE_SSL=true -ADLDAP_USE_TLS=false - # Complete distinguished name of group to which to limit admin dashboard access ADMIN_GROUP= diff --git a/app/Http/.DS_Store b/app/Http/.DS_Store index 43c267dc5d73121357591ac82d173688ca653cc6..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 100644 GIT binary patch delta 70 zcmZoMXfc=|#>AjHu~2NHo+1YW5HK<@2yEWS7{<2w0Am#6W_AvK4xj>{$am(+{342+ UKzW7)kiy9(Jj$D6L{=~Z04^gBI{*Lx delta 242 zcmZoMXfc=|#>B`mF;Q%yo}wrV0|Nsi1A_nqLn1>CLncENLn=ej#Kh(GAPH`UU=SB5 zj3miWoRe-CoSdIq08{}3I8^55ySSv3mLxMUTrahasPermission($permissionName); }); } + + /** + * Set up Azure AD Socialite + */ + Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) { + $event->extendSocialite('azure', \SocialiteProviders\Azure\Provider::class); + }); } /** diff --git a/app/Scopes/AdminGroupScope.php b/app/Scopes/AdminGroupScope.php index d476474..10a3907 100644 --- a/app/Scopes/AdminGroupScope.php +++ b/app/Scopes/AdminGroupScope.php @@ -2,8 +2,8 @@ namespace App\Scopes; -use Adldap\Query\Builder; -use Adldap\Laravel\Scopes\ScopeInterface; +// use Adldap\Query\Builder; +// use Adldap\Laravel\Scopes\ScopeInterface; class AdminGroupScope implements ScopeInterface { @@ -16,9 +16,9 @@ class AdminGroupScope implements ScopeInterface */ public function apply(Builder $query) { - // The distinguished name of our LDAP group. - $distname = config('dataapi.admin_group'); + // // The distinguished name of our LDAP group. + // $distname = config('dataapi.admin_group'); - $query->whereMemberOf($distname); + // $query->whereMemberOf($distname); } } \ No newline at end of file diff --git a/composer.json b/composer.json index 2037c0c..6687d08 100644 --- a/composer.json +++ b/composer.json @@ -7,13 +7,14 @@ "require": { "php": "^8.3.6", "darkaonline/l5-swagger": "^8.1", - "directorytree/ldaprecord-laravel": "*", "laravel/framework": "^11.0", "laravel/helpers": "^1.1", "laravel/legacy-factories": "^1.1", + "laravel/socialite": "^5.23", "laravel/tinker": "^2.0", "laravel/ui": "^4.2.2", "league/fractal": "^0.20.1", + "socialiteproviders/microsoft-azure": "^5.2", "tymon/jwt-auth": "^2.0.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 63caa58..27ad074 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d5fd8ff319891485729f14e1005418ca", + "content-hash": "a8c7f3d2e2c331f948bfb39ede435f33", "packages": [ { "name": "brick/math", @@ -290,149 +290,6 @@ }, "time": "2024-07-08T12:26:09+00:00" }, - { - "name": "directorytree/ldaprecord", - "version": "v3.8.5", - "source": { - "type": "git", - "url": "https://github.com/DirectoryTree/LdapRecord.git", - "reference": "00e5f088f8c4028d5f398783cccc2e8119a27a65" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/DirectoryTree/LdapRecord/zipball/00e5f088f8c4028d5f398783cccc2e8119a27a65", - "reference": "00e5f088f8c4028d5f398783cccc2e8119a27a65", - "shasum": "" - }, - "require": { - "ext-iconv": "*", - "ext-json": "*", - "ext-ldap": "*", - "illuminate/collections": "^8.0|^9.0|^10.0|^11.0|^12.0", - "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0", - "nesbot/carbon": "*", - "php": ">=8.1", - "psr/log": "*", - "psr/simple-cache": "^1.0|^2.0|^3.0" - }, - "require-dev": { - "fakerphp/faker": "^1.21", - "laravel/pint": "^1.6", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^9.0", - "spatie/ray": "^1.24" - }, - "type": "library", - "autoload": { - "psr-4": { - "LdapRecord\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Steve Bauman", - "email": "steven_bauman@outlook.com", - "role": "Developer" - } - ], - "description": "A fully-featured LDAP ORM.", - "homepage": "https://www.ldaprecord.com", - "keywords": [ - "active directory", - "ad", - "adLDAP", - "adldap2", - "directory", - "ldap", - "ldaprecord", - "orm", - "windows" - ], - "support": { - "docs": "https://ldaprecord.com", - "email": "steven_bauman@outlook.com", - "issues": "https://github.com/DirectoryTree/LdapRecord/issues", - "source": "https://github.com/DirectoryTree/LdapRecord" - }, - "funding": [ - { - "url": "https://github.com/stevebauman", - "type": "github" - } - ], - "time": "2025-10-06T02:22:34+00:00" - }, - { - "name": "directorytree/ldaprecord-laravel", - "version": "v3.4.2", - "source": { - "type": "git", - "url": "https://github.com/DirectoryTree/LdapRecord-Laravel.git", - "reference": "28c5a7aa42aa3fa631f9c0f0c8236fd19bc7b00c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/DirectoryTree/LdapRecord-Laravel/zipball/28c5a7aa42aa3fa631f9c0f0c8236fd19bc7b00c", - "reference": "28c5a7aa42aa3fa631f9c0f0c8236fd19bc7b00c", - "shasum": "" - }, - "require": { - "directorytree/ldaprecord": "^v3.3", - "ext-json": "*", - "ext-ldap": "*", - "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", - "php": ">=8.1", - "ramsey/uuid": "*" - }, - "require-dev": { - "laravel/pint": "^1.9", - "laravel/sanctum": "*", - "mockery/mockery": "^1.0", - "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0", - "phpunit/phpunit": "^8.0|^9.0|^10.0|^11.0", - "spatie/ray": "^1.28" - }, - "type": "project", - "extra": { - "laravel": { - "providers": [ - "LdapRecord\\Laravel\\LdapServiceProvider", - "LdapRecord\\Laravel\\LdapAuthServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "LdapRecord\\Laravel\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "LDAP Authentication & Management for Laravel.", - "keywords": [ - "adldap2", - "laravel", - "ldap", - "ldaprecord" - ], - "support": { - "issues": "https://github.com/DirectoryTree/LdapRecord-Laravel/issues", - "source": "https://github.com/DirectoryTree/LdapRecord-Laravel/tree/v3.4.2" - }, - "funding": [ - { - "url": "https://github.com/stevebauman", - "type": "github" - } - ], - "time": "2025-06-13T15:46:25+00:00" - }, { "name": "doctrine/annotations", "version": "2.0.2", @@ -808,6 +665,69 @@ ], "time": "2025-03-06T22:45:56+00:00" }, + { + "name": "firebase/php-jwt", + "version": "v6.11.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + }, + "time": "2025-04-09T20:32:01+00:00" + }, { "name": "fruitcake/php-cors", "version": "v1.3.0", @@ -1800,6 +1720,78 @@ }, "time": "2025-10-09T13:42:30+00:00" }, + { + "name": "laravel/socialite", + "version": "v5.23.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/socialite.git", + "reference": "83d7523c97c1101d288126948947891319eef800" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/socialite/zipball/83d7523c97c1101d288126948947891319eef800", + "reference": "83d7523c97c1101d288126948947891319eef800", + "shasum": "" + }, + "require": { + "ext-json": "*", + "firebase/php-jwt": "^6.4", + "guzzlehttp/guzzle": "^6.0|^7.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "league/oauth1-client": "^1.11", + "php": "^7.2|^8.0", + "phpseclib/phpseclib": "^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", + "phpstan/phpstan": "^1.12.23", + "phpunit/phpunit": "^8.0|^9.3|^10.4|^11.5" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Socialite": "Laravel\\Socialite\\Facades\\Socialite" + }, + "providers": [ + "Laravel\\Socialite\\SocialiteServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Socialite\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.", + "homepage": "https://laravel.com", + "keywords": [ + "laravel", + "oauth" + ], + "support": { + "issues": "https://github.com/laravel/socialite/issues", + "source": "https://github.com/laravel/socialite" + }, + "time": "2025-10-27T15:36:41+00:00" + }, { "name": "laravel/tinker", "version": "v2.10.1", @@ -2514,6 +2506,82 @@ ], "time": "2024-09-21T08:32:55+00:00" }, + { + "name": "league/oauth1-client", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth1-client.git", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "guzzlehttp/psr7": "^1.7|^2.0", + "php": ">=7.1||>=8.0" + }, + "require-dev": { + "ext-simplexml": "*", + "friendsofphp/php-cs-fixer": "^2.17", + "mockery/mockery": "^1.3.3", + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5||9.5" + }, + "suggest": { + "ext-simplexml": "For decoding XML-based responses." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev", + "dev-develop": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth1\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Corlett", + "email": "bencorlett@me.com", + "homepage": "http://www.webcomm.com.au", + "role": "Developer" + } + ], + "description": "OAuth 1.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "bitbucket", + "identity", + "idp", + "oauth", + "oauth1", + "single sign on", + "trello", + "tumblr", + "twitter" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth1-client/issues", + "source": "https://github.com/thephpleague/oauth1-client/tree/v1.11.0" + }, + "time": "2024-12-10T19:59:05+00:00" + }, { "name": "league/uri", "version": "7.5.1", @@ -3195,6 +3263,125 @@ ], "time": "2025-10-18T11:10:27+00:00" }, + { + "name": "paragonie/constant_time_encoding", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77", + "shasum": "" + }, + "require": { + "php": "^8" + }, + "require-dev": { + "infection/infection": "^0", + "nikic/php-fuzzer": "^0", + "phpunit/phpunit": "^9|^10|^11", + "vimeo/psalm": "^4|^5|^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2025-09-24T15:06:41+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.4", @@ -3270,6 +3457,116 @@ ], "time": "2025-08-21T11:53:16+00:00" }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.47", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9d6ca36a6c2dd434765b1071b2644a1c683b385d", + "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2|^3", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.47" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2025-10-06T01:07:24+00:00" + }, { "name": "psr/cache", "version": "3.0.0", @@ -4008,6 +4305,131 @@ }, "time": "2025-09-04T20:59:21+00:00" }, + { + "name": "socialiteproviders/manager", + "version": "v4.8.1", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Manager.git", + "reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/8180ec14bef230ec2351cff993d5d2d7ca470ef4", + "reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4", + "shasum": "" + }, + "require": { + "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", + "laravel/socialite": "^5.5", + "php": "^8.1" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "SocialiteProviders\\Manager\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "SocialiteProviders\\Manager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andy Wendt", + "email": "andy@awendt.com" + }, + { + "name": "Anton Komarev", + "email": "a.komarev@cybercog.su" + }, + { + "name": "Miguel Piedrafita", + "email": "soy@miguelpiedrafita.com" + }, + { + "name": "atymic", + "email": "atymicq@gmail.com", + "homepage": "https://atymic.dev" + } + ], + "description": "Easily add new or override built-in providers in Laravel Socialite.", + "homepage": "https://socialiteproviders.com", + "keywords": [ + "laravel", + "manager", + "oauth", + "providers", + "socialite" + ], + "support": { + "issues": "https://github.com/socialiteproviders/manager/issues", + "source": "https://github.com/socialiteproviders/manager" + }, + "time": "2025-02-24T19:33:30+00:00" + }, + { + "name": "socialiteproviders/microsoft-azure", + "version": "5.2.0", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Microsoft-Azure.git", + "reference": "453d62c9d7e3b3b76e94c913fb46e68a33347b16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/453d62c9d7e3b3b76e94c913fb46e68a33347b16", + "reference": "453d62c9d7e3b3b76e94c913fb46e68a33347b16", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^8.0", + "socialiteproviders/manager": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "SocialiteProviders\\Azure\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Hemmings", + "email": "chris@hemmin.gs" + } + ], + "description": "Microsoft Azure OAuth2 Provider for Laravel Socialite", + "keywords": [ + "azure", + "laravel", + "microsoft", + "oauth", + "provider", + "socialite" + ], + "support": { + "docs": "https://socialiteproviders.com/microsoft-azure", + "issues": "https://github.com/socialiteproviders/providers/issues", + "source": "https://github.com/socialiteproviders/providers" + }, + "time": "2024-03-15T03:02:10+00:00" + }, { "name": "swagger-api/swagger-ui", "version": "v5.30.2", @@ -9109,15 +9531,15 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { "php": "^8.3.6" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "8.3.6" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.9.0" } diff --git a/config/app.php b/config/app.php index d5b82e2..0a8e9ba 100644 --- a/config/app.php +++ b/config/app.php @@ -146,6 +146,7 @@ Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, + \SocialiteProviders\Manager\ServiceProvider::class, /* * Package Service Providers... diff --git a/config/auth.php b/config/auth.php index e19e244..34aab61 100644 --- a/config/auth.php +++ b/config/auth.php @@ -81,10 +81,10 @@ 'driver' => 'eloquent', 'model' => App\Models\Client::class, ], - 'adldap' => [ - 'driver' => 'ldap', - 'model' => LdapRecord\Models\ActiveDirectory\User::class, - ], + // 'adldap' => [ + // 'driver' => 'ldap', + // 'model' => LdapRecord\Models\ActiveDirectory\User::class, + // ], // 'users' => [ // 'driver' => 'database', // 'table' => 'users', diff --git a/config/ldap.php b/config/ldap.php deleted file mode 100644 index ac6f573..0000000 --- a/config/ldap.php +++ /dev/null @@ -1,81 +0,0 @@ - env('ADLDAP_CONNECTION', 'default'), - - /* - |-------------------------------------------------------------------------- - | LDAP Connections - |-------------------------------------------------------------------------- - | - | Below you may configure each LDAP connection your application requires - | access to. Be sure to include a valid base DN - otherwise you may - | not receive any results when performing LDAP search operations. - | - */ - - 'connections' => [ - - 'default' => [ - 'hosts' => [env('ADLDAP_CONTROLLERS', '127.0.0.1')], - 'username' => env('ADLDAP_ADMIN_USERNAME', 'cn=user,dc=local,dc=com'), - 'password' => env('ADLDAP_ADMIN_PASSWORD', 'secret'), - 'port' => env('ADLDAP_PORT', 389), - 'base_dn' => env('ADLDAP_BASEDN', 'dc=local,dc=com'), - 'timeout' => env('ADLDAP_TIMEOUT', 5), - 'use_ssl' => env('ADLDAP_USE_SSL', false), - 'use_tls' => env('ADLDAP_USE_TLS', false), - 'use_sasl' => env('ADLDAP_SASL', false), - 'sasl_options' => [ - // 'mech' => 'GSSAPI', - ], - ], - - ], - - /* - |-------------------------------------------------------------------------- - | LDAP Logging - |-------------------------------------------------------------------------- - | - | When LDAP logging is enabled, all LDAP search and authentication - | operations are logged using the default application logging - | driver. This can assist in debugging issues and more. - | - */ - - 'logging' => [ - 'enabled' => env('ADLDAP_LOGGING', false), - 'channel' => env('LOG_CHANNEL', 'stack'), - 'level' => env('LOG_LEVEL', 'info'), - ], - - /* - |-------------------------------------------------------------------------- - | LDAP Cache - |-------------------------------------------------------------------------- - | - | LDAP caching enables the ability of caching search results using the - | query builder. This is great for running expensive operations that - | may take many seconds to complete, such as a pagination request. - | - */ - - 'cache' => [ - 'enabled' => env('ADLDAP_CACHE', false), - 'driver' => env('CACHE_DRIVER', 'file'), - ], - -]; diff --git a/config/ldap_auth.php b/config/ldap_auth.php deleted file mode 100644 index de07c5c..0000000 --- a/config/ldap_auth.php +++ /dev/null @@ -1,341 +0,0 @@ - env('ADLDAP_CONNECTION', 'default'), - - /* - |-------------------------------------------------------------------------- - | Provider - |-------------------------------------------------------------------------- - | - | The LDAP authentication provider to use depending - | if you require database synchronization. - | - | For synchronizing LDAP users to your local applications database, use the provider: - | - | Adldap\Laravel\Auth\DatabaseUserProvider::class - | - | Otherwise, if you just require LDAP authentication, use the provider: - | - | Adldap\Laravel\Auth\NoDatabaseUserProvider::class - | - */ - - 'provider' => Adldap\Laravel\Auth\NoDatabaseUserProvider::class, - - /* - |-------------------------------------------------------------------------- - | Model - |-------------------------------------------------------------------------- - | - | The model to utilize for authentication and importing. - | - | This option is only applicable to the DatabaseUserProvider. - | - */ - - // 'model' => App\User::class, - - /* - |-------------------------------------------------------------------------- - | Rules - |-------------------------------------------------------------------------- - | - | Rules allow you to control user authentication requests depending on scenarios. - | - | You can create your own rules and insert them here. - | - | All rules must extend from the following class: - | - | Adldap\Laravel\Validation\Rules\Rule - | - */ - - 'rules' => [ - - // Denys deleted users from authenticating. - - Adldap\Laravel\Validation\Rules\DenyTrashed::class, - - // Allows only manually imported users to authenticate. - - // Adldap\Laravel\Validation\Rules\OnlyImported::class, - - ], - - /* - |-------------------------------------------------------------------------- - | Scopes - |-------------------------------------------------------------------------- - | - | Scopes allow you to restrict the LDAP query that locates - | users upon import and authentication. - | - | All scopes must implement the following interface: - | - | Adldap\Laravel\Scopes\ScopeInterface - | - */ - - 'scopes' => [ - - // Only allows users with a user principal name to authenticate. - // Suitable when using ActiveDirectory. - Adldap\Laravel\Scopes\UpnScope::class, - - // Only allows users with a uid to authenticate. - // Suitable when using OpenLDAP. - // Adldap\Laravel\Scopes\UidScope::class, - - // Only allow developers to log in. - App\Scopes\AdminGroupScope::class, - ], - - 'identifiers' => [ - - /* - |-------------------------------------------------------------------------- - | LDAP - |-------------------------------------------------------------------------- - | - | Locate Users By: - | - | This value is the users attribute you would like to locate LDAP - | users by in your directory. - | - | For example, using the default configuration below, if you're - | authenticating users with an email address, your LDAP server - | will be queried for a user with the a `userprincipalname` - | equal to the entered email address. - | - | Bind Users By: - | - | This value is the users attribute you would - | like to use to bind to your LDAP server. - | - | For example, when a user is located by the above attribute, - | the users attribute you specify below will be used as - | the 'username' to bind to your LDAP server. - | - | This is usually their distinguished name. - | - */ - - 'ldap' => [ - - 'locate_users_by' => 'samaccountname', - - 'bind_users_by' => 'distinguishedname', - - ], - - 'database' => [ - - /* - |-------------------------------------------------------------------------- - | GUID Column - |-------------------------------------------------------------------------- - | - | The value of this option is the database column that will contain the - | LDAP users global identifier. This column does not need to be added - | to the sync attributes below. It is synchronized automatically. - | - | This option is only applicable to the DatabaseUserProvider. - | - */ - - 'guid_column' => 'objectguid', - - /* - |-------------------------------------------------------------------------- - | Username Column - |-------------------------------------------------------------------------- - | - | The value of this option is the database column that contains your - | users login username. - | - | This column must be added to your sync attributes below to be - | properly synchronized. - | - | This option is only applicable to the DatabaseUserProvider. - | - */ - - 'username_column' => 'samaccountname', - - ], - - /* - |-------------------------------------------------------------------------- - | Windows Authentication Middleware (SSO) - |-------------------------------------------------------------------------- - | - | Local Users By: - | - | This value is the users attribute you would like to locate LDAP - | users by in your directory. - | - | For example, if 'samaccountname' is the value, then your LDAP server is - | queried for a user with the 'samaccountname' equal to the value of - | $_SERVER['AUTH_USER']. - | - | If a user is found, they are imported (if using the DatabaseUserProvider) - | into your local database, then logged in. - | - | Server Key: - | - | This value represents the 'key' of the $_SERVER - | array to pull the users account name from. - | - | For example, $_SERVER['AUTH_USER']. - | - */ - - 'windows' => [ - - 'locate_users_by' => 'samaccountname', - - 'server_key' => 'AUTH_USER', - - ], - - ], - - 'passwords' => [ - - /* - |-------------------------------------------------------------------------- - | Password Sync - |-------------------------------------------------------------------------- - | - | The password sync option allows you to automatically synchronize users - | LDAP passwords to your local database. These passwords are hashed - | natively by Laravel using the Hash::make() method. - | - | Enabling this option would also allow users to login to their accounts - | using the password last used when an LDAP connection was present. - | - | If this option is disabled, the local database account is applied a - | random 16 character hashed password upon first login, and will - | lose access to this account upon loss of LDAP connectivity. - | - | This option is only applicable to the DatabaseUserProvider. - | - */ - - 'sync' => env('ADLDAP_PASSWORD_SYNC', false), - - /* - |-------------------------------------------------------------------------- - | Column - |-------------------------------------------------------------------------- - | - | This is the column of your users database table - | that is used to store passwords. - | - | Set this to `null` if you do not have a password column. - | - | This option is only applicable to the DatabaseUserProvider. - | - */ - - 'column' => 'password', - - ], - - /* - |-------------------------------------------------------------------------- - | Login Fallback - |-------------------------------------------------------------------------- - | - | The login fallback option allows you to login as a user located in the - | local database if active directory authentication fails. - | - | Set this to true if you would like to enable it. - | - | This option is only applicable to the DatabaseUserProvider. - | - */ - - 'login_fallback' => env('ADLDAP_LOGIN_FALLBACK', false), - - /* - |-------------------------------------------------------------------------- - | Sync Attributes - |-------------------------------------------------------------------------- - | - | Attributes specified here will be added / replaced on the user model - | upon login, automatically synchronizing and keeping the attributes - | up to date. - | - | The array key represents the users Laravel model key, and - | the value represents the users LDAP attribute. - | - | You **must** include the users login attribute here. - | - | This option is only applicable to the DatabaseUserProvider. - | - */ - - 'sync_attributes' => [ - - 'email' => 'userprincipalname', - - 'name' => 'cn', - - ], - - /* - |-------------------------------------------------------------------------- - | Logging - |-------------------------------------------------------------------------- - | - | User authentication attempts will be logged using Laravel's - | default logger if this setting is enabled. - | - | No credentials are logged, only usernames. - | - | This is usually stored in the '/storage/logs' directory - | in the root of your application. - | - | This option is useful for debugging as well as auditing. - | - | You can freely remove any events you would not like to log below, - | as well as use your own listeners if you would prefer. - | - */ - - 'logging' => [ - - 'enabled' => env('ADLDAP_LOGGING', true), - - 'events' => [ - - \Adldap\Laravel\Events\Importing::class => \Adldap\Laravel\Listeners\LogImport::class, - \Adldap\Laravel\Events\Synchronized::class => \Adldap\Laravel\Listeners\LogSynchronized::class, - \Adldap\Laravel\Events\Synchronizing::class => \Adldap\Laravel\Listeners\LogSynchronizing::class, - \Adldap\Laravel\Events\Authenticated::class => \Adldap\Laravel\Listeners\LogAuthenticated::class, - \Adldap\Laravel\Events\Authenticating::class => \Adldap\Laravel\Listeners\LogAuthentication::class, - \Adldap\Laravel\Events\AuthenticationFailed::class => \Adldap\Laravel\Listeners\LogAuthenticationFailure::class, - \Adldap\Laravel\Events\AuthenticationRejected::class => \Adldap\Laravel\Listeners\LogAuthenticationRejection::class, - \Adldap\Laravel\Events\AuthenticationSuccessful::class => \Adldap\Laravel\Listeners\LogAuthenticationSuccess::class, - \Adldap\Laravel\Events\DiscoveredWithCredentials::class => \Adldap\Laravel\Listeners\LogDiscovery::class, - \Adldap\Laravel\Events\AuthenticatedWithWindows::class => \Adldap\Laravel\Listeners\LogWindowsAuth::class, - \Adldap\Laravel\Events\AuthenticatedModelTrashed::class => \Adldap\Laravel\Listeners\LogTrashedModel::class, - - ], - ], - -]; diff --git a/config/services.php b/config/services.php index aa1f7f8..cdfcfcd 100644 --- a/config/services.php +++ b/config/services.php @@ -35,4 +35,12 @@ 'secret' => env('STRIPE_SECRET'), ], + 'azure' => [ + 'client_id' => env('AZURE_CLIENT_ID'), + 'client_secret' => env('AZURE_CLIENT_SECRET'), + 'redirect' => env('AZURE_REDIRECT_URI'), + 'tenant' => env('AZURE_TENANT_ID'), + 'proxy' => env('AZURE_PROXY'), + ], + ]; diff --git a/routes/web.php b/routes/web.php index 193a90e..a7b54c2 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,4 +1,7 @@ group( function( $router ) { Route::name('admin.')->group(function () { - Route::get('admin/login', 'AdminController@loginShow')->name('login'); - Route::post('admin/login', 'AdminController@loginPost'); + // redirect to azure login route + //Route::get('admin/login', 'AdminController@loginShow')->name('login'); + //Route::post('admin/login', 'AdminController@loginPost'); + Route::get('admin/login', function () { + return Socialite::driver('azure')->redirect(); + }); + + Route::get('admin/login/callback', function () { + $azureUser = Socialite::driver('azure')->user(); + + $user = User::updateOrCreate([ + 'azure_id' => $azureUser->id, + ], [ + 'name' => $azureUser->name, + 'email' => $azureUser->email, + ]); + + Auth::login($user); + return redirect('index'); + }); Route::middleware(['auth:admin'])->group(function () { From 073e2245c4331596a32f31d5ee594757c7d9baa0 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 19 Dec 2025 07:44:16 -0800 Subject: [PATCH 30/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Add=20Azure=20I?= =?UTF-8?q?D=20Column=20to=20Users=20Table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ..._18_235342_add_azure_id_to_users_table.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 database/migrations/2025_12_18_235342_add_azure_id_to_users_table.php diff --git a/database/migrations/2025_12_18_235342_add_azure_id_to_users_table.php b/database/migrations/2025_12_18_235342_add_azure_id_to_users_table.php new file mode 100644 index 0000000..42a907b --- /dev/null +++ b/database/migrations/2025_12_18_235342_add_azure_id_to_users_table.php @@ -0,0 +1,28 @@ +string('azure_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('azure_id'); + }); + } +}; From bbde403ea1468710357e25baf94535867f91123c Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 19 Dec 2025 09:30:15 -0800 Subject: [PATCH 31/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Add=20Local=20H?= =?UTF-8?q?TTPS=20Support=20to=20Sail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + docker-compose.yml | 3 ++ docker/8.3/Dockerfile | 3 +- docker/8.3/supervisord.conf | 19 ++++++++--- docker/nginx/nginx.conf | 64 +++++++++++++++++++++++++++++++++++++ readme.md | 9 ++++++ 6 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 docker/nginx/nginx.conf diff --git a/.gitignore b/.gitignore index 35cfb75..3954011 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ yarn-error.log .env .DS_Store /storage/api-docs +/docker/nginx/ssl diff --git a/docker-compose.yml b/docker-compose.yml index 1096ced..f2c7b6d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,6 +12,7 @@ services: ports: - '${APP_PORT:-80}:80' - '${VITE_PORT:-5173}:${VITE_PORT:-5173}' + - '443:443' # SSL port environment: WWWUSER: '${WWWUSER}' LARAVEL_SAIL: 1 @@ -19,6 +20,8 @@ services: XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' volumes: - '.:/var/www/html' + - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf # Nginx configuration + - ./docker/nginx/ssl:/etc/nginx/ssl # SSL certificates networks: - sail networks: diff --git a/docker/8.3/Dockerfile b/docker/8.3/Dockerfile index 802e986..8b723dc 100644 --- a/docker/8.3/Dockerfile +++ b/docker/8.3/Dockerfile @@ -19,7 +19,8 @@ RUN apt-get update \ && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ && apt-get update \ && apt-get install -y php8.3-cli php8.3-dev \ - php8.3-pgsql php8.3-sqlite3 php8.3-gd \ + nginx \ + php8.3-pgsql php8.3-sqlite3 php8.3-gd php8.3-fpm \ php8.3-curl \ php8.3-imap php8.3-mysql php8.3-mbstring \ php8.3-xml php8.3-zip php8.3-bcmath php8.3-soap \ diff --git a/docker/8.3/supervisord.conf b/docker/8.3/supervisord.conf index 9d28479..215ab1f 100644 --- a/docker/8.3/supervisord.conf +++ b/docker/8.3/supervisord.conf @@ -4,10 +4,21 @@ user=root logfile=/var/log/supervisor/supervisord.log pidfile=/var/run/supervisord.pid -[program:php] -command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80 -user=sail -environment=LARAVEL_SAIL="1" +[program:php-fpm] +command=/usr/sbin/php-fpm8.3 -F +user=root +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:nginx] +command=/usr/sbin/nginx -g "daemon off;" +user=root +autostart=true +autorestart=true stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf new file mode 100644 index 0000000..99bb282 --- /dev/null +++ b/docker/nginx/nginx.conf @@ -0,0 +1,64 @@ +user www-data; +worker_processes auto; +pid /run/nginx.pid; +include /etc/nginx/modules-enabled/*.conf; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Main domain: data-api.test + server { + listen 80; + listen 443 ssl; + server_name data-api.test; + + ssl_certificate /etc/nginx/ssl/data-api.crt; + ssl_certificate_key /etc/nginx/ssl/data-api.key; + + root /var/www/html/public; + index index.php index.html; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + fastcgi_pass unix:/run/php/php8.3-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + include fastcgi_params; + } + } + + # Subdomain: no.data-api.test + server { + listen 80; + listen 443 ssl; + server_name no.data-api.test; + + ssl_certificate /etc/nginx/ssl/data-api.crt; + ssl_certificate_key /etc/nginx/ssl/data-api.key; + + root /var/www/html/public; + index index.php index.html; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + fastcgi_pass unix:/run/php/php8.3-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + include fastcgi_params; + } + } + + sendfile on; + keepalive_timeout 65; +} diff --git a/readme.md b/readme.md index 41b62d3..b79f3de 100644 --- a/readme.md +++ b/readme.md @@ -86,6 +86,15 @@ Once Sail informs you of the IP address it is using, add the following entries t 0.0.0.0 no.data-api.test ``` +To support SSL, you'll also need to generate a certificate and key. +```bash +openssl req -newkey rsa:2048 -nodes \ + -keyout docker/nginx/ssl/data-api.key \ + -x509 -days 365 \ + -out docker/nginx/ssl/data-api.crt \ + -subj "/CN=*.data-api.test" +``` + ### Upgrade Considerations When upgrading to a new version of PHP, the Dockerfile may need to be updated as well. From 0c78114d49c39c09ff0091915e69d68a23e02066 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 19 Dec 2025 09:31:53 -0800 Subject: [PATCH 32/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Finalize=20Azur?= =?UTF-8?q?e=20Setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/AdminController.php | 1 + app/Models/User.php | 9 +++++---- config/auth.php | 10 +--------- .../2025_11_17_230351_create_users_table.php | 3 +++ .../2025_11_18_215135_create_sessions_table.php | 2 +- ...12_19_172332_add_created_by_to_clients_table.php} | 12 ++++++++---- routes/web.php | 4 ++-- 7 files changed, 21 insertions(+), 20 deletions(-) rename database/migrations/{2025_12_18_235342_add_azure_id_to_users_table.php => 2025_12_19_172332_add_created_by_to_clients_table.php} (50%) diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index 2974bf1..bd264fc 100644 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -91,6 +91,7 @@ public function addClientPost(Request $request) { $newclient->clienturl = $form_input['clienturl']; $newclient->clientid = Client::generateClientID(); $newclient->password = Hash::make($new_clientkey); + $newclient->created_by = Auth::id(); try { $newclient->save(); diff --git a/app/Models/User.php b/app/Models/User.php index 3ac50b2..e0e8406 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -1,8 +1,6 @@ [ 'driver' => 'session', - 'provider' => 'adldap' + 'provider' => 'users' ], ], @@ -81,14 +81,6 @@ 'driver' => 'eloquent', 'model' => App\Models\Client::class, ], - // 'adldap' => [ - // 'driver' => 'ldap', - // 'model' => LdapRecord\Models\ActiveDirectory\User::class, - // ], - // 'users' => [ - // 'driver' => 'database', - // 'table' => 'users', - // ], ], /* diff --git a/database/migrations/2025_11_17_230351_create_users_table.php b/database/migrations/2025_11_17_230351_create_users_table.php index b3bb22f..13c0cc6 100644 --- a/database/migrations/2025_11_17_230351_create_users_table.php +++ b/database/migrations/2025_11_17_230351_create_users_table.php @@ -13,6 +13,9 @@ public function up(): void { Schema::create('users', function (Blueprint $table) { $table->id(); + $table->string('azure_id')->unique(); + $table->string('name'); + $table->string('email')->unique(); $table->timestamps(); }); } diff --git a/database/migrations/2025_11_18_215135_create_sessions_table.php b/database/migrations/2025_11_18_215135_create_sessions_table.php index 0946ebb..0fe0495 100644 --- a/database/migrations/2025_11_18_215135_create_sessions_table.php +++ b/database/migrations/2025_11_18_215135_create_sessions_table.php @@ -13,7 +13,7 @@ public function up(): void { Schema::connection('da')->create('sessions', function (Blueprint $table) { $table->string('id')->primary(); - $table->foreignUuid('user_id')->nullable()->index(); + $table->unsignedBigInteger('user_id')->nullable()->index(); $table->string('ip_address', 45)->nullable(); $table->text('user_agent')->nullable(); $table->longText('payload'); diff --git a/database/migrations/2025_12_18_235342_add_azure_id_to_users_table.php b/database/migrations/2025_12_19_172332_add_created_by_to_clients_table.php similarity index 50% rename from database/migrations/2025_12_18_235342_add_azure_id_to_users_table.php rename to database/migrations/2025_12_19_172332_add_created_by_to_clients_table.php index 42a907b..7af7dd4 100644 --- a/database/migrations/2025_12_18_235342_add_azure_id_to_users_table.php +++ b/database/migrations/2025_12_19_172332_add_created_by_to_clients_table.php @@ -11,8 +11,11 @@ */ public function up(): void { - Schema::table('users', function (Blueprint $table) { - $table->string('azure_id')->nullable(); + Schema::table('clients', function (Blueprint $table) { + $table->foreignId('created_by') + ->nullable() + ->constrained('users') + ->nullOnDelete(); }); } @@ -21,8 +24,9 @@ public function up(): void */ public function down(): void { - Schema::table('users', function (Blueprint $table) { - $table->dropColumn('azure_id'); + Schema::table('clients', function (Blueprint $table) { + $table->dropForeign(['created_by']); + $table->dropColumn('created_by'); }); } }; diff --git a/routes/web.php b/routes/web.php index a7b54c2..472effd 100644 --- a/routes/web.php +++ b/routes/web.php @@ -34,8 +34,8 @@ 'email' => $azureUser->email, ]); - Auth::login($user); - return redirect('index'); + Auth::guard('admin')->login($user); + return redirect()->route('admin.index'); }); Route::middleware(['auth:admin'])->group(function () { From 6086bfe4dc5d2f21f2e6e2be97014bda6dc5aabb Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 19 Dec 2025 09:41:34 -0800 Subject: [PATCH 33/45] =?UTF-8?q?=F0=9F=93=96=20DOC:=20Add=20Azure=20Confi?= =?UTF-8?q?g=20to=20Docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.azuredevops | 9 +++++---- .env.example | 9 ++++++--- readme.md | 10 ++++++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.env.azuredevops b/.env.azuredevops index 698590b..62163a7 100644 --- a/.env.azuredevops +++ b/.env.azuredevops @@ -31,6 +31,11 @@ COPILOT_DB_DATABASE=#{copilot.database}# # connection info for PCI Forms db connection PCIFORMS_DB_DATABASE=#{pciforms.database}# +# Azure Entra ID Credentials +AZURE_CLIENT_ID=#{azure.client.id}# +AZURE_CLIENT_SECRET=#{azure.client.secret}# +AZURE_TENANT_ID=#{azure.tenant.id}# +AZURE_REDIRECT_URI=#{azure.redirect.uri}# BROADCAST_DRIVER=#{broadcast.driver}# CACHE_DRIVER=#{cache.driver}# @@ -59,10 +64,6 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" JWT_SECRET=#{jwt.secret}# - -# Complete distinguished name of group to which to limit admin dashboard access -ADMIN_GROUP="#{admin.group}#" - # Client id/key used for test cases TEST_CLIENTID= TEST_CLIENTKEY= diff --git a/.env.example b/.env.example index 9a5c851..8dce4eb 100644 --- a/.env.example +++ b/.env.example @@ -64,6 +64,12 @@ COPILOT_DB_DATABASE= COPILOT_DB_USERNAME= COPILOT_DB_PASSWORD= +# Azure Entra ID Credentials +AZURE_CLIENT_ID= +AZURE_CLIENT_SECRET= +AZURE_TENANT_ID= +AZURE_REDIRECT_URI= + BROADCAST_DRIVER=log CACHE_DRIVER=file SESSION_DRIVER=file @@ -91,9 +97,6 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" JWT_SECRET= -# Complete distinguished name of group to which to limit admin dashboard access -ADMIN_GROUP= - # Client id/key used for test cases TEST_CLIENTID= TEST_CLIENTKEY= diff --git a/readme.md b/readme.md index b79f3de..9999e38 100644 --- a/readme.md +++ b/readme.md @@ -105,6 +105,16 @@ Lines 28 and 46 of `/docker/8.1/Dockerfile` were added in order to add SQL Serve | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [![Build status](https://dev.azure.com/bcintegration/data-api/_apis/build/status/data-api-master)](https://dev.azure.com/bcintegration/data-api/_build/latest?definitionId=20) | [![Build status](https://dev.azure.com/bcintegration/data-api/_apis/build/status/data-api-dev)](https://dev.azure.com/bcintegration/data-api/_build/latest?definitionId=19) | +## Configuration 🛠️ + +### Azure Entra ID +Make sure to set the following environment variables: + +- `AZURE_CLIENT_ID` - The client ID of the Azure Entra ID application +- `AZURE_CLIENT_SECRET` - The client secret of the Azure Entra ID application +- `AZURE_TENANT_ID` - The tenant ID of the Azure Entra ID application +- `AZURE_REDIRECT_URI` - The redirect URI of the Azure Entra ID application (e.g. `https://no.data-api.test/admin/login/callback`) + ## The BadgeZone 💫 [![emoji-log](https://cdn.rawgit.com/ahmadawais/stuff/ca97874/emoji-log/flat-round.svg)](https://github.com/ahmadawais/Emoji-Log/) From 5877ad2ecc09fdbc8f07d26d7440c8936bb86d44 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 19 Dec 2025 12:33:56 -0800 Subject: [PATCH 34/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Update=20Larave?= =?UTF-8?q?l=20to=20v12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 4 +- composer.lock | 973 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 582 insertions(+), 395 deletions(-) diff --git a/composer.json b/composer.json index 6687d08..f38c63a 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "require": { "php": "^8.3.6", "darkaonline/l5-swagger": "^8.1", - "laravel/framework": "^11.0", + "laravel/framework": "^12.0", "laravel/helpers": "^1.1", "laravel/legacy-factories": "^1.1", "laravel/socialite": "^5.23", @@ -15,7 +15,7 @@ "laravel/ui": "^4.2.2", "league/fractal": "^0.20.1", "socialiteproviders/microsoft-azure": "^5.2", - "tymon/jwt-auth": "^2.0.0" + "tymon/jwt-auth": "^2.2.0" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", diff --git a/composer.lock b/composer.lock index 27ad074..4c0882d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a8c7f3d2e2c331f948bfb39ede435f33", + "content-hash": "28061dff131b572edb6606d23dbafddb", "packages": [ { "name": "brick/math", - "version": "0.14.0", + "version": "0.14.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2" + "reference": "f05858549e5f9d7bb45875a75583240a38a281d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", - "reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2", + "url": "https://api.github.com/repos/brick/math/zipball/f05858549e5f9d7bb45875a75583240a38a281d0", + "reference": "f05858549e5f9d7bb45875a75583240a38a281d0", "shasum": "" }, "require": { @@ -56,7 +56,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.14.0" + "source": "https://github.com/brick/math/tree/0.14.1" }, "funding": [ { @@ -64,7 +64,7 @@ "type": "github" } ], - "time": "2025-08-29T12:40:03+00:00" + "time": "2025-11-24T14:40:29+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -730,31 +730,31 @@ }, { "name": "fruitcake/php-cors", - "version": "v1.3.0", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/fruitcake/php-cors.git", - "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", - "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", "shasum": "" }, "require": { - "php": "^7.4|^8.0", - "symfony/http-foundation": "^4.4|^5.4|^6|^7" + "php": "^8.1", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8" }, "require-dev": { - "phpstan/phpstan": "^1.4", + "phpstan/phpstan": "^2", "phpunit/phpunit": "^9", - "squizlabs/php_codesniffer": "^3.5" + "squizlabs/php_codesniffer": "^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -785,7 +785,7 @@ ], "support": { "issues": "https://github.com/fruitcake/php-cors/issues", - "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0" }, "funding": [ { @@ -797,7 +797,7 @@ "type": "github" } ], - "time": "2023-10-12T05:21:21+00:00" + "time": "2025-12-03T09:33:47+00:00" }, { "name": "graham-campbell/result-type", @@ -1274,20 +1274,20 @@ }, { "name": "laravel/framework", - "version": "v11.46.1", + "version": "v12.43.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "5fd457f807570a962a53b403b1346efe4cc80bb8" + "reference": "195b893593a9298edee177c0844132ebaa02102f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/5fd457f807570a962a53b403b1346efe4cc80bb8", - "reference": "5fd457f807570a962a53b403b1346efe4cc80bb8", + "url": "https://api.github.com/repos/laravel/framework/zipball/195b893593a9298edee177c0844132ebaa02102f", + "reference": "195b893593a9298edee177c0844132ebaa02102f", "shasum": "" }, "require": { - "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12|^0.13|^0.14", + "brick/math": "^0.11|^0.12|^0.13|^0.14", "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", "dragonmantank/cron-expression": "^3.4", @@ -1302,32 +1302,34 @@ "fruitcake/php-cors": "^1.3", "guzzlehttp/guzzle": "^7.8.2", "guzzlehttp/uri-template": "^1.0", - "laravel/prompts": "^0.1.18|^0.2.0|^0.3.0", + "laravel/prompts": "^0.3.0", "laravel/serializable-closure": "^1.3|^2.0", "league/commonmark": "^2.7", "league/flysystem": "^3.25.1", "league/flysystem-local": "^3.25.1", "league/uri": "^7.5.1", "monolog/monolog": "^3.0", - "nesbot/carbon": "^2.72.6|^3.8.4", + "nesbot/carbon": "^3.8.4", "nunomaduro/termwind": "^2.0", "php": "^8.2", "psr/container": "^1.1.1|^2.0.1", "psr/log": "^1.0|^2.0|^3.0", "psr/simple-cache": "^1.0|^2.0|^3.0", "ramsey/uuid": "^4.7", - "symfony/console": "^7.0.3", - "symfony/error-handler": "^7.0.3", - "symfony/finder": "^7.0.3", + "symfony/console": "^7.2.0", + "symfony/error-handler": "^7.2.0", + "symfony/finder": "^7.2.0", "symfony/http-foundation": "^7.2.0", - "symfony/http-kernel": "^7.0.3", - "symfony/mailer": "^7.0.3", - "symfony/mime": "^7.0.3", - "symfony/polyfill-php83": "^1.31", - "symfony/process": "^7.0.3", - "symfony/routing": "^7.0.3", - "symfony/uid": "^7.0.3", - "symfony/var-dumper": "^7.0.3", + "symfony/http-kernel": "^7.2.0", + "symfony/mailer": "^7.2.0", + "symfony/mime": "^7.2.0", + "symfony/polyfill-php83": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-php85": "^1.33", + "symfony/process": "^7.2.0", + "symfony/routing": "^7.2.0", + "symfony/uid": "^7.2.0", + "symfony/var-dumper": "^7.2.0", "tijsverkoyen/css-to-inline-styles": "^2.2.5", "vlucas/phpdotenv": "^5.6.1", "voku/portable-ascii": "^2.0.2" @@ -1359,6 +1361,7 @@ "illuminate/filesystem": "self.version", "illuminate/hashing": "self.version", "illuminate/http": "self.version", + "illuminate/json-schema": "self.version", "illuminate/log": "self.version", "illuminate/macroable": "self.version", "illuminate/mail": "self.version", @@ -1368,6 +1371,7 @@ "illuminate/process": "self.version", "illuminate/queue": "self.version", "illuminate/redis": "self.version", + "illuminate/reflection": "self.version", "illuminate/routing": "self.version", "illuminate/session": "self.version", "illuminate/support": "self.version", @@ -1391,17 +1395,18 @@ "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^9.16.1", - "pda/pheanstalk": "^5.0.6", + "opis/json-schema": "^2.4.1", + "orchestra/testbench-core": "^10.8.1", + "pda/pheanstalk": "^5.0.6|^7.0.0", "php-http/discovery": "^1.15", "phpstan/phpstan": "^2.0", - "phpunit/phpunit": "^10.5.35|^11.3.6|^12.0.1", - "predis/predis": "^2.3", - "resend/resend-php": "^0.10.0", - "symfony/cache": "^7.0.3", - "symfony/http-client": "^7.0.3", - "symfony/psr-http-message-bridge": "^7.0.3", - "symfony/translation": "^7.0.3" + "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", + "predis/predis": "^2.3|^3.0", + "resend/resend-php": "^0.10.0|^1.0", + "symfony/cache": "^7.2.0", + "symfony/http-client": "^7.2.0", + "symfony/psr-http-message-bridge": "^7.2.0", + "symfony/translation": "^7.2.0" }, "suggest": { "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", @@ -1416,7 +1421,7 @@ "ext-pdo": "Required to use all database features.", "ext-posix": "Required to use all features of the queue worker.", "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", - "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).", "filp/whoops": "Required for friendly error pages in development (^2.14.3).", "laravel/tinker": "Required to use the tinker console command (^2.0).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", @@ -1427,22 +1432,22 @@ "mockery/mockery": "Required to use mocking (^1.6).", "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", - "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.3.6|^12.0.1).", - "predis/predis": "Required to use the predis connector (^2.3).", + "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).", + "predis/predis": "Required to use the predis connector (^2.3|^3.0).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", - "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^7.0).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^7.0).", - "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.0).", - "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.0).", - "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.0).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.0)." + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0|^1.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "11.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { @@ -1453,6 +1458,7 @@ "src/Illuminate/Filesystem/functions.php", "src/Illuminate/Foundation/helpers.php", "src/Illuminate/Log/functions.php", + "src/Illuminate/Reflection/helpers.php", "src/Illuminate/Support/functions.php", "src/Illuminate/Support/helpers.php" ], @@ -1461,7 +1467,8 @@ "Illuminate\\Support\\": [ "src/Illuminate/Macroable/", "src/Illuminate/Collections/", - "src/Illuminate/Conditionable/" + "src/Illuminate/Conditionable/", + "src/Illuminate/Reflection/" ] } }, @@ -1485,20 +1492,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-09-30T14:51:32+00:00" + "time": "2025-12-16T18:53:08+00:00" }, { "name": "laravel/helpers", - "version": "v1.8.1", + "version": "v1.8.2", "source": { "type": "git", "url": "https://github.com/laravel/helpers.git", - "reference": "d0094b4bc4364560c8ee3a9e956596d760d4afab" + "reference": "98499eea4c1cca76fb0fb37ed365a468773daf0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/helpers/zipball/d0094b4bc4364560c8ee3a9e956596d760d4afab", - "reference": "d0094b4bc4364560c8ee3a9e956596d760d4afab", + "url": "https://api.github.com/repos/laravel/helpers/zipball/98499eea4c1cca76fb0fb37ed365a468773daf0a", + "reference": "98499eea4c1cca76fb0fb37ed365a468773daf0a", "shasum": "" }, "require": { @@ -1507,7 +1514,7 @@ }, "require-dev": { "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^7.0|^8.0|^9.0|^10.0" + "phpunit/phpunit": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0" }, "type": "library", "extra": { @@ -1540,9 +1547,9 @@ "laravel" ], "support": { - "source": "https://github.com/laravel/helpers/tree/v1.8.1" + "source": "https://github.com/laravel/helpers/tree/v1.8.2" }, - "time": "2025-09-02T15:31:25+00:00" + "time": "2025-11-25T14:46:28+00:00" }, { "name": "laravel/legacy-factories", @@ -1602,16 +1609,16 @@ }, { "name": "laravel/prompts", - "version": "v0.3.7", + "version": "v0.3.8", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "a1891d362714bc40c8d23b0b1d7090f022ea27cc" + "reference": "096748cdfb81988f60090bbb839ce3205ace0d35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/a1891d362714bc40c8d23b0b1d7090f022ea27cc", - "reference": "a1891d362714bc40c8d23b0b1d7090f022ea27cc", + "url": "https://api.github.com/repos/laravel/prompts/zipball/096748cdfb81988f60090bbb839ce3205ace0d35", + "reference": "096748cdfb81988f60090bbb839ce3205ace0d35", "shasum": "" }, "require": { @@ -1627,7 +1634,7 @@ "require-dev": { "illuminate/collections": "^10.0|^11.0|^12.0", "mockery/mockery": "^1.5", - "pestphp/pest": "^2.3|^3.4", + "pestphp/pest": "^2.3|^3.4|^4.0", "phpstan/phpstan": "^1.12.28", "phpstan/phpstan-mockery": "^1.1.3" }, @@ -1655,22 +1662,22 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.7" + "source": "https://github.com/laravel/prompts/tree/v0.3.8" }, - "time": "2025-09-19T13:47:56+00:00" + "time": "2025-11-21T20:52:52+00:00" }, { "name": "laravel/serializable-closure", - "version": "v2.0.6", + "version": "v2.0.7", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "038ce42edee619599a1debb7e81d7b3759492819" + "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/038ce42edee619599a1debb7e81d7b3759492819", - "reference": "038ce42edee619599a1debb7e81d7b3759492819", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/cb291e4c998ac50637c7eeb58189c14f5de5b9dd", + "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd", "shasum": "" }, "require": { @@ -1679,7 +1686,7 @@ "require-dev": { "illuminate/support": "^10.0|^11.0|^12.0", "nesbot/carbon": "^2.67|^3.0", - "pestphp/pest": "^2.36|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", "phpstan/phpstan": "^2.0", "symfony/var-dumper": "^6.2.0|^7.0.0" }, @@ -1718,20 +1725,20 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2025-10-09T13:42:30+00:00" + "time": "2025-11-21T20:52:36+00:00" }, { "name": "laravel/socialite", - "version": "v5.23.1", + "version": "v5.24.0", "source": { "type": "git", "url": "https://github.com/laravel/socialite.git", - "reference": "83d7523c97c1101d288126948947891319eef800" + "reference": "1d19358c28e8951dde6e36603b89d8f09e6cfbfd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/socialite/zipball/83d7523c97c1101d288126948947891319eef800", - "reference": "83d7523c97c1101d288126948947891319eef800", + "url": "https://api.github.com/repos/laravel/socialite/zipball/1d19358c28e8951dde6e36603b89d8f09e6cfbfd", + "reference": "1d19358c28e8951dde6e36603b89d8f09e6cfbfd", "shasum": "" }, "require": { @@ -1747,9 +1754,9 @@ }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", + "orchestra/testbench": "^4.18|^5.20|^6.47|^7.55|^8.36|^9.15|^10.8", "phpstan/phpstan": "^1.12.23", - "phpunit/phpunit": "^8.0|^9.3|^10.4|^11.5" + "phpunit/phpunit": "^8.0|^9.3|^10.4|^11.5|^12.0" }, "type": "library", "extra": { @@ -1790,20 +1797,20 @@ "issues": "https://github.com/laravel/socialite/issues", "source": "https://github.com/laravel/socialite" }, - "time": "2025-10-27T15:36:41+00:00" + "time": "2025-12-09T15:37:06+00:00" }, { "name": "laravel/tinker", - "version": "v2.10.1", + "version": "v2.10.2", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3" + "reference": "3bcb5f62d6f837e0f093a601e26badafb127bd4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/22177cc71807d38f2810c6204d8f7183d88a57d3", - "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3", + "url": "https://api.github.com/repos/laravel/tinker/zipball/3bcb5f62d6f837e0f093a601e26badafb127bd4c", + "reference": "3bcb5f62d6f837e0f093a601e26badafb127bd4c", "shasum": "" }, "require": { @@ -1854,9 +1861,9 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.10.1" + "source": "https://github.com/laravel/tinker/tree/v2.10.2" }, - "time": "2025-01-27T14:24:01+00:00" + "time": "2025-11-20T16:29:12+00:00" }, { "name": "laravel/ui", @@ -2061,16 +2068,16 @@ }, { "name": "league/commonmark", - "version": "2.7.1", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "10732241927d3971d28e7ea7b5712721fa2296ca" + "reference": "4efa10c1e56488e658d10adf7b7b7dcd19940bfb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/10732241927d3971d28e7ea7b5712721fa2296ca", - "reference": "10732241927d3971d28e7ea7b5712721fa2296ca", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/4efa10c1e56488e658d10adf7b7b7dcd19940bfb", + "reference": "4efa10c1e56488e658d10adf7b7b7dcd19940bfb", "shasum": "" }, "require": { @@ -2107,7 +2114,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.8-dev" + "dev-main": "2.9-dev" } }, "autoload": { @@ -2164,7 +2171,7 @@ "type": "tidelift" } ], - "time": "2025-07-20T12:47:49+00:00" + "time": "2025-11-26T21:48:24+00:00" }, { "name": "league/config", @@ -2584,33 +2591,38 @@ }, { "name": "league/uri", - "version": "7.5.1", + "version": "7.7.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "81fb5145d2644324614cc532b28efd0215bda430" + "reference": "8d587cddee53490f9b82bf203d3a9aa7ea4f9807" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", - "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/8d587cddee53490f9b82bf203d3a9aa7ea4f9807", + "reference": "8d587cddee53490f9b82bf203d3a9aa7ea4f9807", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.5", - "php": "^8.1" + "league/uri-interfaces": "^7.7", + "php": "^8.1", + "psr/http-factory": "^1" }, "conflict": { "league/uri-schemes": "^1.0" }, "suggest": { "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", "ext-fileinfo": "to create Data URI from file contennts", "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", "league/uri-components": "Needed to easily manipulate URI objects components", + "league/uri-polyfill": "Needed to backport the PHP URI extension for older versions of PHP", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle WHATWG URL", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -2638,6 +2650,7 @@ "description": "URI manipulation library", "homepage": "https://uri.thephpleague.com", "keywords": [ + "URN", "data-uri", "file-uri", "ftp", @@ -2650,9 +2663,11 @@ "psr-7", "query-string", "querystring", + "rfc2141", "rfc3986", "rfc3987", "rfc6570", + "rfc8141", "uri", "uri-template", "url", @@ -2662,7 +2677,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.5.1" + "source": "https://github.com/thephpleague/uri/tree/7.7.0" }, "funding": [ { @@ -2670,26 +2685,25 @@ "type": "github" } ], - "time": "2024-12-08T08:40:02+00:00" + "time": "2025-12-07T16:02:06+00:00" }, { "name": "league/uri-interfaces", - "version": "7.5.0", + "version": "7.7.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + "reference": "62ccc1a0435e1c54e10ee6022df28d6c04c2946c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/62ccc1a0435e1c54e10ee6022df28d6c04c2946c", + "reference": "62ccc1a0435e1c54e10ee6022df28d6c04c2946c", "shasum": "" }, "require": { "ext-filter": "*", "php": "^8.1", - "psr/http-factory": "^1", "psr/http-message": "^1.1 || ^2.0" }, "suggest": { @@ -2697,6 +2711,7 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle WHATWG URL", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -2721,7 +2736,7 @@ "homepage": "https://nyamsprod.com" } ], - "description": "Common interfaces and classes for URI representation and interaction", + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", "homepage": "https://uri.thephpleague.com", "keywords": [ "data-uri", @@ -2746,7 +2761,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.7.0" }, "funding": [ { @@ -2754,7 +2769,7 @@ "type": "github" } ], - "time": "2024-12-08T08:18:47+00:00" + "time": "2025-12-07T16:03:21+00:00" }, { "name": "monolog/monolog", @@ -2861,16 +2876,16 @@ }, { "name": "nesbot/carbon", - "version": "3.10.3", + "version": "3.11.0", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f" + "reference": "bdb375400dcd162624531666db4799b36b64e4a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", - "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/bdb375400dcd162624531666db4799b36b64e4a1", + "reference": "bdb375400dcd162624531666db4799b36b64e4a1", "shasum": "" }, "require": { @@ -2878,9 +2893,9 @@ "ext-json": "*", "php": "^8.1", "psr/clock": "^1.0", - "symfony/clock": "^6.3.12 || ^7.0", + "symfony/clock": "^6.3.12 || ^7.0 || ^8.0", "symfony/polyfill-mbstring": "^1.0", - "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0" + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -2962,7 +2977,7 @@ "type": "tidelift" } ], - "time": "2025-09-06T13:39:36+00:00" + "time": "2025-12-02T21:04:28+00:00" }, { "name": "nette/schema", @@ -3031,20 +3046,20 @@ }, { "name": "nette/utils", - "version": "v4.0.8", + "version": "v4.1.0", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede" + "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede", - "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "url": "https://api.github.com/repos/nette/utils/zipball/fa1f0b8261ed150447979eb22e373b7b7ad5a8e0", + "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0", "shasum": "" }, "require": { - "php": "8.0 - 8.5" + "php": "8.2 - 8.5" }, "conflict": { "nette/finder": "<3", @@ -3067,7 +3082,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -3114,22 +3129,22 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.8" + "source": "https://github.com/nette/utils/tree/v4.1.0" }, - "time": "2025-08-06T21:43:34+00:00" + "time": "2025-12-01T17:49:23+00:00" }, { "name": "nikic/php-parser", - "version": "v5.6.2", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -3172,37 +3187,37 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-10-21T19:32:17+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "nunomaduro/termwind", - "version": "v2.3.2", + "version": "v2.3.3", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "eb61920a53057a7debd718a5b89c2178032b52c0" + "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/eb61920a53057a7debd718a5b89c2178032b52c0", - "reference": "eb61920a53057a7debd718a5b89c2178032b52c0", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/6fb2a640ff502caace8e05fd7be3b503a7e1c017", + "reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017", "shasum": "" }, "require": { "ext-mbstring": "*", "php": "^8.2", - "symfony/console": "^7.3.4" + "symfony/console": "^7.3.6" }, "require-dev": { "illuminate/console": "^11.46.1", "laravel/pint": "^1.25.1", "mockery/mockery": "^1.6.12", - "pestphp/pest": "^2.36.0 || ^3.8.4", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.1.3", "phpstan/phpstan": "^1.12.32", "phpstan/phpstan-strict-rules": "^1.6.2", - "symfony/var-dumper": "^7.3.4", + "symfony/var-dumper": "^7.3.5", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -3245,7 +3260,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.3.2" + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.3" }, "funding": [ { @@ -3261,7 +3276,7 @@ "type": "github" } ], - "time": "2025-10-18T11:10:27+00:00" + "time": "2025-11-20T02:34:59+00:00" }, { "name": "paragonie/constant_time_encoding", @@ -3459,16 +3474,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.47", + "version": "3.0.48", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d" + "reference": "64065a5679c50acb886e82c07aa139b0f757bb89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9d6ca36a6c2dd434765b1071b2644a1c683b385d", - "reference": "9d6ca36a6c2dd434765b1071b2644a1c683b385d", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/64065a5679c50acb886e82c07aa139b0f757bb89", + "reference": "64065a5679c50acb886e82c07aa139b0f757bb89", "shasum": "" }, "require": { @@ -3549,7 +3564,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.47" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.48" }, "funding": [ { @@ -3565,7 +3580,7 @@ "type": "tidelift" } ], - "time": "2025-10-06T01:07:24+00:00" + "time": "2025-12-15T11:51:42+00:00" }, { "name": "psr/cache", @@ -4030,16 +4045,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.14", + "version": "v0.12.18", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "95c29b3756a23855a30566b745d218bee690bef2" + "reference": "ddff0ac01beddc251786fe70367cd8bbdb258196" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/95c29b3756a23855a30566b745d218bee690bef2", - "reference": "95c29b3756a23855a30566b745d218bee690bef2", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/ddff0ac01beddc251786fe70367cd8bbdb258196", + "reference": "ddff0ac01beddc251786fe70367cd8bbdb258196", "shasum": "" }, "require": { @@ -4047,8 +4062,8 @@ "ext-tokenizer": "*", "nikic/php-parser": "^5.0 || ^4.0", "php": "^8.0 || ^7.4", - "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", - "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" }, "conflict": { "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" @@ -4103,9 +4118,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.14" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.18" }, - "time": "2025-10-27T17:15:31+00:00" + "time": "2025-12-17T14:35:46+00:00" }, { "name": "ralouphie/getallheaders", @@ -4229,20 +4244,20 @@ }, { "name": "ramsey/uuid", - "version": "4.9.1", + "version": "4.9.2", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440" + "reference": "8429c78ca35a09f27565311b98101e2826affde0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440", - "reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", + "reference": "8429c78ca35a09f27565311b98101e2826affde0", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", + "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -4301,9 +4316,9 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.1" + "source": "https://github.com/ramsey/uuid/tree/4.9.2" }, - "time": "2025-09-04T20:59:21+00:00" + "time": "2025-12-14T04:43:48+00:00" }, { "name": "socialiteproviders/manager", @@ -4432,16 +4447,16 @@ }, { "name": "swagger-api/swagger-ui", - "version": "v5.30.2", + "version": "v5.31.0", "source": { "type": "git", "url": "https://github.com/swagger-api/swagger-ui.git", - "reference": "30d8f985caeca5bb9400cf21403686ec5a82c1cc" + "reference": "61dddd44389bf25922639afd682f50b8a0fb5cf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/30d8f985caeca5bb9400cf21403686ec5a82c1cc", - "reference": "30d8f985caeca5bb9400cf21403686ec5a82c1cc", + "url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/61dddd44389bf25922639afd682f50b8a0fb5cf4", + "reference": "61dddd44389bf25922639afd682f50b8a0fb5cf4", "shasum": "" }, "type": "library", @@ -4487,22 +4502,22 @@ ], "support": { "issues": "https://github.com/swagger-api/swagger-ui/issues", - "source": "https://github.com/swagger-api/swagger-ui/tree/v5.30.2" + "source": "https://github.com/swagger-api/swagger-ui/tree/v5.31.0" }, - "time": "2025-11-04T09:41:42+00:00" + "time": "2025-12-11T15:57:42+00:00" }, { "name": "symfony/clock", - "version": "v7.3.0", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" + "reference": "9169f24776edde469914c1e7a1442a50f7a4e110" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", - "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "url": "https://api.github.com/repos/symfony/clock/zipball/9169f24776edde469914c1e7a1442a50f7a4e110", + "reference": "9169f24776edde469914c1e7a1442a50f7a4e110", "shasum": "" }, "require": { @@ -4547,7 +4562,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.3.0" + "source": "https://github.com/symfony/clock/tree/v7.4.0" }, "funding": [ { @@ -4558,25 +4573,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2025-11-12T15:39:26+00:00" }, { "name": "symfony/console", - "version": "v7.3.6", + "version": "v7.4.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a" + "reference": "6d9f0fbf2ec2e9785880096e3abd0ca0c88b506e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", - "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", + "url": "https://api.github.com/repos/symfony/console/zipball/6d9f0fbf2ec2e9785880096e3abd0ca0c88b506e", + "reference": "6d9f0fbf2ec2e9785880096e3abd0ca0c88b506e", "shasum": "" }, "require": { @@ -4584,7 +4603,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" + "symfony/string": "^7.2|^8.0" }, "conflict": { "symfony/dependency-injection": "<6.4", @@ -4598,16 +4617,16 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -4641,7 +4660,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.6" + "source": "https://github.com/symfony/console/tree/v7.4.1" }, "funding": [ { @@ -4661,20 +4680,20 @@ "type": "tidelift" } ], - "time": "2025-11-04T01:21:42+00:00" + "time": "2025-12-05T15:23:39+00:00" }, { "name": "symfony/css-selector", - "version": "v7.3.6", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "84321188c4754e64273b46b406081ad9b18e8614" + "reference": "ab862f478513e7ca2fe9ec117a6f01a8da6e1135" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/84321188c4754e64273b46b406081ad9b18e8614", - "reference": "84321188c4754e64273b46b406081ad9b18e8614", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/ab862f478513e7ca2fe9ec117a6f01a8da6e1135", + "reference": "ab862f478513e7ca2fe9ec117a6f01a8da6e1135", "shasum": "" }, "require": { @@ -4710,7 +4729,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.3.6" + "source": "https://github.com/symfony/css-selector/tree/v7.4.0" }, "funding": [ { @@ -4730,7 +4749,7 @@ "type": "tidelift" } ], - "time": "2025-10-29T17:24:25+00:00" + "time": "2025-10-30T13:39:42+00:00" }, { "name": "symfony/deprecation-contracts", @@ -4801,32 +4820,33 @@ }, { "name": "symfony/error-handler", - "version": "v7.3.6", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "bbe40bfab84323d99dab491b716ff142410a92a8" + "reference": "48be2b0653594eea32dcef130cca1c811dcf25c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/bbe40bfab84323d99dab491b716ff142410a92a8", - "reference": "bbe40bfab84323d99dab491b716ff142410a92a8", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/48be2b0653594eea32dcef130cca1c811dcf25c2", + "reference": "48be2b0653594eea32dcef130cca1c811dcf25c2", "shasum": "" }, "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/polyfill-php85": "^1.32", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/deprecation-contracts": "<2.5", "symfony/http-kernel": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0|^8.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", "symfony/webpack-encore-bundle": "^1.0|^2.0" }, "bin": [ @@ -4858,7 +4878,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.3.6" + "source": "https://github.com/symfony/error-handler/tree/v7.4.0" }, "funding": [ { @@ -4878,20 +4898,20 @@ "type": "tidelift" } ], - "time": "2025-10-31T19:12:50+00:00" + "time": "2025-11-05T14:29:59+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.3.3", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" + "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", - "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9dddcddff1ef974ad87b3708e4b442dc38b2261d", + "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d", "shasum": "" }, "require": { @@ -4908,13 +4928,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/stopwatch": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -4942,7 +4963,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.0" }, "funding": [ { @@ -4962,7 +4983,7 @@ "type": "tidelift" } ], - "time": "2025-08-13T11:49:31+00:00" + "time": "2025-10-28T09:38:46+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -5042,23 +5063,23 @@ }, { "name": "symfony/finder", - "version": "v7.3.5", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "9f696d2f1e340484b4683f7853b273abff94421f" + "reference": "340b9ed7320570f319028a2cbec46d40535e94bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/9f696d2f1e340484b4683f7853b273abff94421f", - "reference": "9f696d2f1e340484b4683f7853b273abff94421f", + "url": "https://api.github.com/repos/symfony/finder/zipball/340b9ed7320570f319028a2cbec46d40535e94bd", + "reference": "340b9ed7320570f319028a2cbec46d40535e94bd", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "symfony/filesystem": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -5086,7 +5107,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.5" + "source": "https://github.com/symfony/finder/tree/v7.4.0" }, "funding": [ { @@ -5106,27 +5127,26 @@ "type": "tidelift" } ], - "time": "2025-10-15T18:45:57+00:00" + "time": "2025-11-05T05:42:40+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.3.7", + "version": "v7.4.1", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4" + "reference": "bd1af1e425811d6f077db240c3a588bdb405cd27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/db488a62f98f7a81d5746f05eea63a74e55bb7c4", - "reference": "db488a62f98f7a81d5746f05eea63a74e55bb7c4", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/bd1af1e425811d6f077db240c3a588bdb405cd27", + "reference": "bd1af1e425811d6f077db240c3a588bdb405cd27", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php83": "^1.27" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.1" }, "conflict": { "doctrine/dbal": "<3.6", @@ -5135,13 +5155,13 @@ "require-dev": { "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5", - "symfony/clock": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0" + "symfony/cache": "^6.4.12|^7.1.5|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -5169,7 +5189,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.7" + "source": "https://github.com/symfony/http-foundation/tree/v7.4.1" }, "funding": [ { @@ -5189,29 +5209,29 @@ "type": "tidelift" } ], - "time": "2025-11-08T16:41:12+00:00" + "time": "2025-12-07T11:13:10+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.7", + "version": "v7.4.2", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "10b8e9b748ea95fa4539c208e2487c435d3c87ce" + "reference": "f6e6f0a5fa8763f75a504b930163785fb6dd055f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/10b8e9b748ea95fa4539c208e2487c435d3c87ce", - "reference": "10b8e9b748ea95fa4539c208e2487c435d3c87ce", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f6e6f0a5fa8763f75a504b930163785fb6dd055f", + "reference": "f6e6f0a5fa8763f75a504b930163785fb6dd055f", "shasum": "" }, "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.4|^7.0", - "symfony/event-dispatcher": "^7.3", - "symfony/http-foundation": "^7.3", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/http-foundation": "^7.4|^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -5221,6 +5241,7 @@ "symfony/console": "<6.4", "symfony/dependency-injection": "<6.4", "symfony/doctrine-bridge": "<6.4", + "symfony/flex": "<2.10", "symfony/form": "<6.4", "symfony/http-client": "<6.4", "symfony/http-client-contracts": "<2.5", @@ -5238,27 +5259,27 @@ }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/clock": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/css-selector": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/dom-crawler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/dom-crawler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^7.1", - "symfony/routing": "^6.4|^7.0", - "symfony/serializer": "^7.1", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^7.1|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/serializer": "^7.1|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0", - "symfony/var-exporter": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0", "twig/twig": "^3.12" }, "type": "library", @@ -5287,7 +5308,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.7" + "source": "https://github.com/symfony/http-kernel/tree/v7.4.2" }, "funding": [ { @@ -5307,20 +5328,20 @@ "type": "tidelift" } ], - "time": "2025-11-12T11:38:40+00:00" + "time": "2025-12-08T07:43:37+00:00" }, { "name": "symfony/mailer", - "version": "v7.3.5", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "fd497c45ba9c10c37864e19466b090dcb60a50ba" + "reference": "a3d9eea8cfa467ece41f0f54ba28185d74bd53fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/fd497c45ba9c10c37864e19466b090dcb60a50ba", - "reference": "fd497c45ba9c10c37864e19466b090dcb60a50ba", + "url": "https://api.github.com/repos/symfony/mailer/zipball/a3d9eea8cfa467ece41f0f54ba28185d74bd53fd", + "reference": "a3d9eea8cfa467ece41f0f54ba28185d74bd53fd", "shasum": "" }, "require": { @@ -5328,8 +5349,8 @@ "php": ">=8.2", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/mime": "^7.2", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.2|^8.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -5340,10 +5361,10 @@ "symfony/twig-bridge": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -5371,7 +5392,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.3.5" + "source": "https://github.com/symfony/mailer/tree/v7.4.0" }, "funding": [ { @@ -5391,24 +5412,25 @@ "type": "tidelift" } ], - "time": "2025-10-24T14:27:20+00:00" + "time": "2025-11-21T15:26:00+00:00" }, { "name": "symfony/mime", - "version": "v7.3.4", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35" + "reference": "bdb02729471be5d047a3ac4a69068748f1a6be7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/b1b828f69cbaf887fa835a091869e55df91d0e35", - "reference": "b1b828f69cbaf887fa835a091869e55df91d0e35", + "url": "https://api.github.com/repos/symfony/mime/zipball/bdb02729471be5d047a3ac4a69068748f1a6be7a", + "reference": "bdb02729471be5d047a3ac4a69068748f1a6be7a", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, @@ -5423,11 +5445,11 @@ "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/serializer": "^6.4.3|^7.0.3" + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.3|^7.0.3|^8.0" }, "type": "library", "autoload": { @@ -5459,7 +5481,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.3.4" + "source": "https://github.com/symfony/mime/tree/v7.4.0" }, "funding": [ { @@ -5479,7 +5501,7 @@ "type": "tidelift" } ], - "time": "2025-09-16T08:38:17+00:00" + "time": "2025-11-16T10:14:42+00:00" }, { "name": "symfony/polyfill-ctype", @@ -6067,6 +6089,166 @@ ], "time": "2025-07-08T02:45:35+00:00" }, + { + "name": "symfony/polyfill-php84", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-24T13:30:11+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-06-23T16:12:55+00:00" + }, { "name": "symfony/polyfill-uuid", "version": "v1.33.0", @@ -6152,16 +6334,16 @@ }, { "name": "symfony/process", - "version": "v7.3.4", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" + "reference": "7ca8dc2d0dcf4882658313aba8be5d9fd01026c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", + "url": "https://api.github.com/repos/symfony/process/zipball/7ca8dc2d0dcf4882658313aba8be5d9fd01026c8", + "reference": "7ca8dc2d0dcf4882658313aba8be5d9fd01026c8", "shasum": "" }, "require": { @@ -6193,7 +6375,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.4" + "source": "https://github.com/symfony/process/tree/v7.4.0" }, "funding": [ { @@ -6213,20 +6395,20 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2025-10-16T11:21:06+00:00" }, { "name": "symfony/routing", - "version": "v7.3.6", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "c97abe725f2a1a858deca629a6488c8fc20c3091" + "reference": "4720254cb2644a0b876233d258a32bf017330db7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/c97abe725f2a1a858deca629a6488c8fc20c3091", - "reference": "c97abe725f2a1a858deca629a6488c8fc20c3091", + "url": "https://api.github.com/repos/symfony/routing/zipball/4720254cb2644a0b876233d258a32bf017330db7", + "reference": "4720254cb2644a0b876233d258a32bf017330db7", "shasum": "" }, "require": { @@ -6240,11 +6422,11 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6278,7 +6460,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.3.6" + "source": "https://github.com/symfony/routing/tree/v7.4.0" }, "funding": [ { @@ -6298,7 +6480,7 @@ "type": "tidelift" } ], - "time": "2025-11-05T07:57:47+00:00" + "time": "2025-11-27T13:27:24+00:00" }, { "name": "symfony/service-contracts", @@ -6389,22 +6571,23 @@ }, { "name": "symfony/string", - "version": "v7.3.4", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f96476035142921000338bad71e5247fbc138872" + "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", - "reference": "f96476035142921000338bad71e5247fbc138872", + "url": "https://api.github.com/repos/symfony/string/zipball/d50e862cb0a0e0886f73ca1f31b865efbb795003", + "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-grapheme": "~1.33", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, @@ -6412,11 +6595,11 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.1", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6455,7 +6638,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.4" + "source": "https://github.com/symfony/string/tree/v7.4.0" }, "funding": [ { @@ -6475,27 +6658,27 @@ "type": "tidelift" } ], - "time": "2025-09-11T14:36:48+00:00" + "time": "2025-11-27T13:27:24+00:00" }, { "name": "symfony/translation", - "version": "v7.3.4", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "ec25870502d0c7072d086e8ffba1420c85965174" + "reference": "2d01ca0da3f092f91eeedb46f24aa30d2fca8f68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/ec25870502d0c7072d086e8ffba1420c85965174", - "reference": "ec25870502d0c7072d086e8ffba1420c85965174", + "url": "https://api.github.com/repos/symfony/translation/zipball/2d01ca0da3f092f91eeedb46f24aa30d2fca8f68", + "reference": "2d01ca0da3f092f91eeedb46f24aa30d2fca8f68", "shasum": "" }, "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5|^3.0" + "symfony/translation-contracts": "^2.5.3|^3.3" }, "conflict": { "nikic/php-parser": "<5.0", @@ -6514,17 +6697,17 @@ "require-dev": { "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0" + "symfony/yaml": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6555,7 +6738,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.3.4" + "source": "https://github.com/symfony/translation/tree/v7.4.0" }, "funding": [ { @@ -6575,7 +6758,7 @@ "type": "tidelift" } ], - "time": "2025-09-07T11:39:36+00:00" + "time": "2025-11-27T13:27:24+00:00" }, { "name": "symfony/translation-contracts", @@ -6661,16 +6844,16 @@ }, { "name": "symfony/uid", - "version": "v7.3.1", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb" + "reference": "2498e9f81b7baa206f44de583f2f48350b90142c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/a69f69f3159b852651a6bf45a9fdd149520525bb", - "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb", + "url": "https://api.github.com/repos/symfony/uid/zipball/2498e9f81b7baa206f44de583f2f48350b90142c", + "reference": "2498e9f81b7baa206f44de583f2f48350b90142c", "shasum": "" }, "require": { @@ -6678,7 +6861,7 @@ "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6715,7 +6898,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.3.1" + "source": "https://github.com/symfony/uid/tree/v7.4.0" }, "funding": [ { @@ -6726,25 +6909,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T19:55:54+00:00" + "time": "2025-09-25T11:02:55+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.3.5", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "476c4ae17f43a9a36650c69879dcf5b1e6ae724d" + "reference": "41fd6c4ae28c38b294b42af6db61446594a0dece" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/476c4ae17f43a9a36650c69879dcf5b1e6ae724d", - "reference": "476c4ae17f43a9a36650c69879dcf5b1e6ae724d", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/41fd6c4ae28c38b294b42af6db61446594a0dece", + "reference": "41fd6c4ae28c38b294b42af6db61446594a0dece", "shasum": "" }, "require": { @@ -6756,10 +6943,10 @@ "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/uid": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", "twig/twig": "^3.12" }, "bin": [ @@ -6798,7 +6985,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.5" + "source": "https://github.com/symfony/var-dumper/tree/v7.4.0" }, "funding": [ { @@ -6818,32 +7005,32 @@ "type": "tidelift" } ], - "time": "2025-09-27T09:00:46+00:00" + "time": "2025-10-27T20:36:44+00:00" }, { "name": "symfony/yaml", - "version": "v7.3.5", + "version": "v7.4.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "90208e2fc6f68f613eae7ca25a2458a931b1bacc" + "reference": "24dd4de28d2e3988b311751ac49e684d783e2345" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/90208e2fc6f68f613eae7ca25a2458a931b1bacc", - "reference": "90208e2fc6f68f613eae7ca25a2458a931b1bacc", + "url": "https://api.github.com/repos/symfony/yaml/zipball/24dd4de28d2e3988b311751ac49e684d783e2345", + "reference": "24dd4de28d2e3988b311751ac49e684d783e2345", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -6874,7 +7061,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.5" + "source": "https://github.com/symfony/yaml/tree/v7.4.1" }, "funding": [ { @@ -6894,27 +7081,27 @@ "type": "tidelift" } ], - "time": "2025-09-27T09:00:46+00:00" + "time": "2025-12-04T18:11:45+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "v2.3.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "0d72ac1c00084279c1816675284073c5a337c20d" + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d", - "reference": "0d72ac1c00084279c1816675284073c5a337c20d", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "php": "^7.4 || ^8.0", - "symfony/css-selector": "^5.4 || ^6.0 || ^7.0" + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { "phpstan/phpstan": "^2.0", @@ -6947,9 +7134,9 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0" }, - "time": "2024-12-21T16:25:41+00:00" + "time": "2025-12-02T11:56:42+00:00" }, { "name": "tymon/jwt-auth", @@ -7496,16 +7683,16 @@ }, { "name": "laravel/sail", - "version": "v1.48.0", + "version": "v1.51.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "1bf3b8870b72a258a3b6b5119435835ece522e8a" + "reference": "1c74357df034e869250b4365dd445c9f6ba5d068" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/1bf3b8870b72a258a3b6b5119435835ece522e8a", - "reference": "1bf3b8870b72a258a3b6b5119435835ece522e8a", + "url": "https://api.github.com/repos/laravel/sail/zipball/1c74357df034e869250b4365dd445c9f6ba5d068", + "reference": "1c74357df034e869250b4365dd445c9f6ba5d068", "shasum": "" }, "require": { @@ -7555,7 +7742,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2025-11-09T14:46:21+00:00" + "time": "2025-12-09T13:33:49+00:00" }, { "name": "mockery/mockery", @@ -7702,16 +7889,16 @@ }, { "name": "nunomaduro/collision", - "version": "v8.8.2", + "version": "v8.8.3", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb" + "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/60207965f9b7b7a4ce15a0f75d57f9dadb105bdb", - "reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/1dc9e88d105699d0fee8bb18890f41b274f6b4c4", + "reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4", "shasum": "" }, "require": { @@ -7733,7 +7920,7 @@ "laravel/sanctum": "^4.1.1", "laravel/tinker": "^2.10.1", "orchestra/testbench-core": "^9.12.0 || ^10.4", - "pestphp/pest": "^3.8.2", + "pestphp/pest": "^3.8.2 || ^4.0.0", "sebastian/environment": "^7.2.1 || ^8.0" }, "type": "library", @@ -7797,7 +7984,7 @@ "type": "patreon" } ], - "time": "2025-06-25T02:12:12+00:00" + "time": "2025-11-20T02:55:25+00:00" }, { "name": "phar-io/manifest", @@ -8254,16 +8441,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.44", + "version": "11.5.46", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c346885c95423eda3f65d85a194aaa24873cda82" + "reference": "75dfe79a2aa30085b7132bb84377c24062193f33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c346885c95423eda3f65d85a194aaa24873cda82", - "reference": "c346885c95423eda3f65d85a194aaa24873cda82", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/75dfe79a2aa30085b7132bb84377c24062193f33", + "reference": "75dfe79a2aa30085b7132bb84377c24062193f33", "shasum": "" }, "require": { @@ -8335,7 +8522,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.44" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.46" }, "funding": [ { @@ -8359,7 +8546,7 @@ "type": "tidelift" } ], - "time": "2025-11-13T07:17:35+00:00" + "time": "2025-12-06T08:01:15+00:00" }, { "name": "sebastian/cli-parser", @@ -9480,16 +9667,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/d74205c497bfbca49f34d4bc4c19c17e22db4ebb", - "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -9518,7 +9705,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.3.0" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -9526,7 +9713,7 @@ "type": "github" } ], - "time": "2025-11-13T13:44:09+00:00" + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], From fa83a24ce1695ff802f3445c1a3d9816f8885aad Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 19 Dec 2025 12:41:07 -0800 Subject: [PATCH 35/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Make=20Scramble?= =?UTF-8?q?=20Docs=20Public?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/scramble.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/config/scramble.php b/config/scramble.php index b0877ae..5724495 100644 --- a/config/scramble.php +++ b/config/scramble.php @@ -1,7 +1,4 @@ [ 'web', - RestrictedDocsAccess::class, ], 'extensions' => [], From cded0ca60eda4814415cd756152e7cd3db13165e Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 19 Dec 2025 12:56:28 -0800 Subject: [PATCH 36/45] =?UTF-8?q?=F0=9F=93=96=20DOC:=20Add=20Scramble=20En?= =?UTF-8?q?dpoint=20to=20Readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/readme.md b/readme.md index 7651618..ad8ff3d 100644 --- a/readme.md +++ b/readme.md @@ -2,6 +2,10 @@ The Data API is a RESTful, primarily read-only web service for accessing Bellevue College data in JSON format. +## Documentation Endpoint 📚 + +- `docs/api` - The documentation endpoint for the Data API (thanks to [Scramble](https://scramble.dedoc.co/)) + ## API Endpoints 🌐 ### Class/course data 🎓 From 1967b03adfca7ca295db9b7a41f174554ca4aecc Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Thu, 8 Jan 2026 14:19:02 -0800 Subject: [PATCH 37/45] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20URL=20Generation=20?= =?UTF-8?q?for=20Named=20Routes=20in=20Subfolder=20Deployment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Providers/AppServiceProvider.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 60cf691..0d77c57 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Event; use App\Models\Client; +use Illuminate\Support\Facades\URL; class AppServiceProvider extends ServiceProvider { @@ -14,8 +15,27 @@ class AppServiceProvider extends ServiceProvider * * @return void */ - public function boot() + public function boot(): void { + // Fix URL generation for subdomain + subfolder deployments + $subdir = trim(parse_url(config('app.url'), PHP_URL_PATH) ?? '', '/'); + + if ($subdir !== '') { + // Use current request host, or APP_URL for CLI + URL::useOrigin( + app()->runningInConsole() + ? preg_replace('#(/.*)?$#', '', config('app.url')) + : request()->getSchemeAndHttpHost() + ); + + // Prefix all generated paths with subdirectory + URL::formatPathUsing(fn($path) => + str_starts_with($path = '/' . ltrim($path, '/'), "/$subdir/") || $path === "/$subdir" + ? $path + : ($path === '/' ? "/$subdir" : "/$subdir$path") + ); + } + /** * Define Gates for each Permission */ From 5bcb9f28f96d068d9b109ff4eefc4d6b7527476d Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Thu, 20 Nov 2025 08:15:21 -0800 Subject: [PATCH 38/45] =?UTF-8?q?=F0=9F=93=A6=20NEW:=20Add=20AzureID=20and?= =?UTF-8?q?=20UPN=20to=20Student=20and=20Employee=20Endpoints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Resources/EmployeeResource.php | 4 +++- app/Http/Resources/StudentResource.php | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Http/Resources/EmployeeResource.php b/app/Http/Resources/EmployeeResource.php index b5ec526..4a85682 100644 --- a/app/Http/Resources/EmployeeResource.php +++ b/app/Http/Resources/EmployeeResource.php @@ -17,12 +17,14 @@ public function toArray(Request $request): array return [ 'SID' => $this->SID, 'EMPLID' => $this->EMPLID, + 'AzureID' => $this->AzureID, 'firstName' => $this->FirstName, 'lastName' => $this->LastName, 'aliasName' => $this->AliasName, 'email' => $this->WorkEmail, 'phone' => $this->WorkPhoneNumber, - 'username' => $this->ADUserName + 'username' => $this->ADUserName, + 'UPN' => $this->UserPrincipalName, ]; } } diff --git a/app/Http/Resources/StudentResource.php b/app/Http/Resources/StudentResource.php index 1c8c321..6b2f879 100644 --- a/app/Http/Resources/StudentResource.php +++ b/app/Http/Resources/StudentResource.php @@ -17,12 +17,14 @@ public function toArray(Request $request): array return [ 'SID' => $this->SID, 'EMPLID' => $this->EMPLID, + 'AzureID' => $this->AzureID, 'firstName' => $this->FirstName, 'lastName' => $this->LastName, 'email' => $this->Email, 'phoneDaytime' => $this->DaytimePhone, 'phoneEvening' => $this->EveningPhone, 'username' => $this->NTUserName, + 'UPN' => $this->UserPrincipalName, 'ferpaBlock' => $this->PrivateRecord, ]; } From e274ed165d8334f2734e204a7de47fab1da51cc1 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Tue, 13 Jan 2026 09:04:42 -0800 Subject: [PATCH 39/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Allow=20UPN=20T?= =?UTF-8?q?o=20Search=20Employees=20and=20Students?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/EmployeeController.php | 7 ++++++- app/Http/Controllers/StudentController.php | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/EmployeeController.php b/app/Http/Controllers/EmployeeController.php index 6f7a791..178d86e 100644 --- a/app/Http/Controllers/EmployeeController.php +++ b/app/Http/Controllers/EmployeeController.php @@ -27,7 +27,12 @@ class EmployeeController extends ApiController public function getEmployeeByUsername(Request $request, $username) { try{ - $emp = Employee::where('ADUserName', '=', $username)->where('EmployeeStatusCode', '=', 'A')->firstOrFail(); + // If username contains @, search UserPrincipal + if (strpos($username, '@') !== false) { + $emp = Employee::where('UserPrincipalName', '=', $username)->where('EmployeeStatusCode', '=', 'A')->firstOrFail(); + } else { + $emp = Employee::where('ADUserName', '=', $username)->where('EmployeeStatusCode', '=', 'A')->firstOrFail(); + } return new EmployeeResource($emp); } catch (\Exception $e) { return response()->json(new stdClass()); diff --git a/app/Http/Controllers/StudentController.php b/app/Http/Controllers/StudentController.php index 141500e..88be286 100644 --- a/app/Http/Controllers/StudentController.php +++ b/app/Http/Controllers/StudentController.php @@ -22,7 +22,12 @@ class StudentController extends ApiController { public function getStudentByUsername($username) { try { - $stu = Student::where('NTUserName', '=', $username)->firstOrFail(); + // If username contains @, search UserPrincipal + if (strpos($username, '@') !== false) { + $stu = Student::where('UserPrincipalName', '=', $username)->firstOrFail(); + } else { + $stu = Student::where('NTUserName', '=', $username)->firstOrFail(); + } return new StudentResource($stu); } catch (\Exception $e) { return response()->json(new stdClass()); From 17306436906ce76e0c7ad9dcfc883d4ba81c98bd Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 16 Jan 2026 11:31:22 -0800 Subject: [PATCH 40/45] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Server=20Paths=20in?= =?UTF-8?q?=20Scramble?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Providers/AppServiceProvider.php | 24 ++++++++++++++++++++++++ config/scramble.php | 12 ++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 0d77c57..68ae687 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -7,6 +7,7 @@ use Illuminate\Support\Facades\Event; use App\Models\Client; use Illuminate\Support\Facades\URL; +use Dedoc\Scramble\Scramble; class AppServiceProvider extends ServiceProvider { @@ -35,6 +36,29 @@ public function boot(): void : ($path === '/' ? "/$subdir" : "/$subdir$path") ); } + // Fix Scramble's auto-generated internal domain servers + if (class_exists(Scramble::class)) { + $subdir = trim(parse_url(config('app.url'), PHP_URL_PATH) ?? '', '/'); + $internalDomain = config('dataapi.api_internal_domain'); + + if ($subdir && $internalDomain) { + Scramble::afterOpenApiGenerated(function ($openApi) use ($subdir, $internalDomain) { + $badUrl = "https://{$internalDomain}/api"; + $goodUrl = "https://{$internalDomain}/{$subdir}/api"; + + foreach ($openApi->paths as $pathItem) { + if (isset($pathItem->servers)) { + foreach ($pathItem->servers as $server) { + if ($server->url === $badUrl) { + $server->url = $goodUrl; + } + } + } + } + return $openApi; + }); + } + } /** * Define Gates for each Permission diff --git a/config/scramble.php b/config/scramble.php index 5724495..6d870d5 100644 --- a/config/scramble.php +++ b/config/scramble.php @@ -1,4 +1,12 @@ [ - 'Main API - Use this by default' => config('app.url') . '/api', - 'Internal API - /internal/ prefix' => 'http://'. config('dataapi.api_internal_domain') . '/api', + 'Main API - Use this by default' => $appUrl . '/api', + 'Internal API - /internal/ prefix' => $scheme.'://'.$internalDomain.$subdirPrefix.'/api', ], /** From 4cd905d25d81f55142a834ccc2a92f5c628e2c32 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Fri, 16 Jan 2026 11:34:20 -0800 Subject: [PATCH 41/45] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Incorrect=20Typehin?= =?UTF-8?q?t=20on=20CourseId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/CourseController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/CourseController.php b/app/Http/Controllers/CourseController.php index 101d71c..2172e31 100644 --- a/app/Http/Controllers/CourseController.php +++ b/app/Http/Controllers/CourseController.php @@ -31,11 +31,11 @@ public function index(){ * * Course ID is a combination of the Course Subject and Catalog Number, space separated * - * @param int $courseid + * @param string $courseid * * @return CourseResource | \Illuminate\Http\JsonResponse **/ - public function getCourse($courseid, Request $request) + public function getCourse(string $courseid, Request $request) { try { $course = Course::where('CourseID', '=', $courseid)->active()->firstOrFail(); From 59baf7feef82ff900bdf12c3c907d308f3707d24 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Tue, 20 Jan 2026 09:57:47 -0800 Subject: [PATCH 42/45] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Authorization=20on?= =?UTF-8?q?=20Basic=20Auth=20Endpoints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- routes/api.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/routes/api.php b/routes/api.php index 98bc702..03fd7bc 100644 --- a/routes/api.php +++ b/routes/api.php @@ -55,9 +55,11 @@ Route::prefix('v1')->middleware([ 'auth.basic:api-basic,clientid', 'throttle:60,1', - 'can:write_transactions' ])->group(function () { - Route::prefix('forms/pci')->group(function () { + + // Tell Laravel to use the api-basic guard for gates + Auth::shouldUse('api-basic'); + Route::prefix('forms/pci')->middleware('can:write_transactions')->group(function () { // Error Message Route::get('transactions','TransactionController@getTransactions'); @@ -71,7 +73,7 @@ }); - Route::prefix('forms/evaluations')->group(function () { + Route::prefix('forms/evaluations')->middleware('can:write_transactions')->group(function () { // Route::get('transactions','TransactionController@getTransactions'); @@ -89,9 +91,11 @@ Route::prefix('v1')->middleware([ 'auth.basic:api-basic,clientid', 'throttle:60,1', - 'can:write_user_questions' ])->group(function () { - Route::prefix('copilot')->group(function () { + + // Tell Laravel to use the api-basic guard for gates + Auth::shouldUse('api-basic'); + Route::prefix('copilot')->middleware('can:write_user_questions')->group(function () { // Record User Question Route::post('userquestion','UserQuestionController@postUserQuestion'); From 5bcaa4ed0a91fec8fff6640f51f03b8ea24a87a6 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Tue, 20 Jan 2026 11:29:34 -0800 Subject: [PATCH 43/45] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Regressions=20from?= =?UTF-8?q?=20Guard=20Changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to using middleware to define auth method. This prevents overflow into other routes. --- app/Http/Kernel.php | 1 + app/Http/Middleware/SetApiBasicGuard.php | 18 ++++++++++++++++++ routes/api.php | 16 +++++++--------- 3 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 app/Http/Middleware/SetApiBasicGuard.php diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index e5b33c6..6a5713d 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -53,6 +53,7 @@ class Kernel extends HttpKernel protected $middlewareAliases = [ 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, + 'guard.api-basic' => \App\Http\Middleware\SetApiBasicGuard::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, diff --git a/app/Http/Middleware/SetApiBasicGuard.php b/app/Http/Middleware/SetApiBasicGuard.php new file mode 100644 index 0000000..5a3db32 --- /dev/null +++ b/app/Http/Middleware/SetApiBasicGuard.php @@ -0,0 +1,18 @@ +middleware([ 'auth.basic:api-basic,clientid', + 'guard.api-basic', 'throttle:60,1', + 'can:write_transactions' ])->group(function () { - - // Tell Laravel to use the api-basic guard for gates - Auth::shouldUse('api-basic'); - Route::prefix('forms/pci')->middleware('can:write_transactions')->group(function () { + Route::prefix('forms/pci')->group(function () { // Error Message Route::get('transactions','TransactionController@getTransactions'); @@ -73,7 +72,7 @@ }); - Route::prefix('forms/evaluations')->middleware('can:write_transactions')->group(function () { + Route::prefix('forms/evaluations')->group(function () { // Route::get('transactions','TransactionController@getTransactions'); @@ -90,12 +89,11 @@ **/ Route::prefix('v1')->middleware([ 'auth.basic:api-basic,clientid', + 'guard.api-basic', 'throttle:60,1', + 'can:write_user_questions' ])->group(function () { - - // Tell Laravel to use the api-basic guard for gates - Auth::shouldUse('api-basic'); - Route::prefix('copilot')->middleware('can:write_user_questions')->group(function () { + Route::prefix('copilot')->group(function () { // Record User Question Route::post('userquestion','UserQuestionController@postUserQuestion'); From 2aa8ddcf12972dcdf8f3a3f643190af5965db903 Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Wed, 21 Jan 2026 15:01:34 -0800 Subject: [PATCH 44/45] Bump Version to 1.16 --- config/dataapi.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/dataapi.php b/config/dataapi.php index 5e7f77e..6e466ca 100644 --- a/config/dataapi.php +++ b/config/dataapi.php @@ -6,7 +6,7 @@ 'yearquarter_max' => '9999', 'timezone' => 'America/Los_Angeles', 'common_course_char' => '&', - 'app_version' => '1.15.4', + 'app_version' => '1.16.0', 'admin_group' => env('ADMIN_GROUP', null), 'api_internal_domain' => env('API_INTERNAL_DOMAIN', 'internal.localhost.test'), ]; From 395c1fb6e2eba23a87172fb34887376b0d7afc1a Mon Sep 17 00:00:00 2001 From: Taija Tevia-Clark Date: Wed, 21 Jan 2026 15:03:09 -0800 Subject: [PATCH 45/45] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Update=20Packag?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.lock | 144 +++++++++++++++++++++++++------------------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/composer.lock b/composer.lock index 6064c4c..8f88d11 100644 --- a/composer.lock +++ b/composer.lock @@ -652,16 +652,16 @@ }, { "name": "firebase/php-jwt", - "version": "v6.11.1", + "version": "v7.0.2", "source": { "type": "git", "url": "https://github.com/firebase/php-jwt.git", - "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + "reference": "5645b43af647b6947daac1d0f659dd1fbe8d3b65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", - "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/5645b43af647b6947daac1d0f659dd1fbe8d3b65", + "reference": "5645b43af647b6947daac1d0f659dd1fbe8d3b65", "shasum": "" }, "require": { @@ -709,9 +709,9 @@ ], "support": { "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + "source": "https://github.com/firebase/php-jwt/tree/v7.0.2" }, - "time": "2025-04-09T20:32:01+00:00" + "time": "2025-12-16T22:17:28+00:00" }, { "name": "fruitcake/php-cors", @@ -1259,16 +1259,16 @@ }, { "name": "laravel/framework", - "version": "v12.44.0", + "version": "v12.48.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "592bbf1c036042958332eb98e3e8131b29102f33" + "reference": "0f0974a9769378ccd9c9935c09b9927f3a606830" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/592bbf1c036042958332eb98e3e8131b29102f33", - "reference": "592bbf1c036042958332eb98e3e8131b29102f33", + "url": "https://api.github.com/repos/laravel/framework/zipball/0f0974a9769378ccd9c9935c09b9927f3a606830", + "reference": "0f0974a9769378ccd9c9935c09b9927f3a606830", "shasum": "" }, "require": { @@ -1381,7 +1381,7 @@ "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", "opis/json-schema": "^2.4.1", - "orchestra/testbench-core": "^10.8.1", + "orchestra/testbench-core": "^10.9.0", "pda/pheanstalk": "^5.0.6|^7.0.0", "php-http/discovery": "^1.15", "phpstan/phpstan": "^2.0", @@ -1477,7 +1477,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-12-23T15:29:43+00:00" + "time": "2026-01-20T16:12:36+00:00" }, { "name": "laravel/helpers", @@ -1594,16 +1594,16 @@ }, { "name": "laravel/prompts", - "version": "v0.3.8", + "version": "v0.3.10", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "096748cdfb81988f60090bbb839ce3205ace0d35" + "reference": "360ba095ef9f51017473505191fbd4ab73e1cab3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/096748cdfb81988f60090bbb839ce3205ace0d35", - "reference": "096748cdfb81988f60090bbb839ce3205ace0d35", + "url": "https://api.github.com/repos/laravel/prompts/zipball/360ba095ef9f51017473505191fbd4ab73e1cab3", + "reference": "360ba095ef9f51017473505191fbd4ab73e1cab3", "shasum": "" }, "require": { @@ -1647,22 +1647,22 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.8" + "source": "https://github.com/laravel/prompts/tree/v0.3.10" }, - "time": "2025-11-21T20:52:52+00:00" + "time": "2026-01-13T20:29:29+00:00" }, { "name": "laravel/serializable-closure", - "version": "v2.0.7", + "version": "v2.0.8", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd" + "reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/cb291e4c998ac50637c7eeb58189c14f5de5b9dd", - "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/7581a4407012f5f53365e11bafc520fd7f36bc9b", + "reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b", "shasum": "" }, "require": { @@ -1710,25 +1710,25 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2025-11-21T20:52:36+00:00" + "time": "2026-01-08T16:22:46+00:00" }, { "name": "laravel/socialite", - "version": "v5.24.0", + "version": "v5.24.2", "source": { "type": "git", "url": "https://github.com/laravel/socialite.git", - "reference": "1d19358c28e8951dde6e36603b89d8f09e6cfbfd" + "reference": "5cea2eebf11ca4bc6c2f20495c82a70a9b3d1613" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/socialite/zipball/1d19358c28e8951dde6e36603b89d8f09e6cfbfd", - "reference": "1d19358c28e8951dde6e36603b89d8f09e6cfbfd", + "url": "https://api.github.com/repos/laravel/socialite/zipball/5cea2eebf11ca4bc6c2f20495c82a70a9b3d1613", + "reference": "5cea2eebf11ca4bc6c2f20495c82a70a9b3d1613", "shasum": "" }, "require": { "ext-json": "*", - "firebase/php-jwt": "^6.4", + "firebase/php-jwt": "^6.4|^7.0", "guzzlehttp/guzzle": "^6.0|^7.0", "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", @@ -1782,20 +1782,20 @@ "issues": "https://github.com/laravel/socialite/issues", "source": "https://github.com/laravel/socialite" }, - "time": "2025-12-09T15:37:06+00:00" + "time": "2026-01-10T16:07:28+00:00" }, { "name": "laravel/tinker", - "version": "v2.10.2", + "version": "v2.11.0", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "3bcb5f62d6f837e0f093a601e26badafb127bd4c" + "reference": "3d34b97c9a1747a81a3fde90482c092bd8b66468" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/3bcb5f62d6f837e0f093a601e26badafb127bd4c", - "reference": "3bcb5f62d6f837e0f093a601e26badafb127bd4c", + "url": "https://api.github.com/repos/laravel/tinker/zipball/3d34b97c9a1747a81a3fde90482c092bd8b66468", + "reference": "3d34b97c9a1747a81a3fde90482c092bd8b66468", "shasum": "" }, "require": { @@ -1804,7 +1804,7 @@ "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", "php": "^7.2.5|^8.0", "psy/psysh": "^0.11.1|^0.12.0", - "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0|^8.0" }, "require-dev": { "mockery/mockery": "~1.3.3|^1.4.2", @@ -1846,9 +1846,9 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.10.2" + "source": "https://github.com/laravel/tinker/tree/v2.11.0" }, - "time": "2025-11-20T16:29:12+00:00" + "time": "2025-12-19T19:16:45+00:00" }, { "name": "laravel/ui", @@ -2576,20 +2576,20 @@ }, { "name": "league/uri", - "version": "7.7.0", + "version": "7.8.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "8d587cddee53490f9b82bf203d3a9aa7ea4f9807" + "reference": "4436c6ec8d458e4244448b069cc572d088230b76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/8d587cddee53490f9b82bf203d3a9aa7ea4f9807", - "reference": "8d587cddee53490f9b82bf203d3a9aa7ea4f9807", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/4436c6ec8d458e4244448b069cc572d088230b76", + "reference": "4436c6ec8d458e4244448b069cc572d088230b76", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.7", + "league/uri-interfaces": "^7.8", "php": "^8.1", "psr/http-factory": "^1" }, @@ -2603,11 +2603,11 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "ext-uri": "to use the PHP native URI class", - "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", - "league/uri-components": "Needed to easily manipulate URI objects components", - "league/uri-polyfill": "Needed to backport the PHP URI extension for older versions of PHP", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle WHATWG URL", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -2662,7 +2662,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.7.0" + "source": "https://github.com/thephpleague/uri/tree/7.8.0" }, "funding": [ { @@ -2670,20 +2670,20 @@ "type": "github" } ], - "time": "2025-12-07T16:02:06+00:00" + "time": "2026-01-14T17:24:56+00:00" }, { "name": "league/uri-interfaces", - "version": "7.7.0", + "version": "7.8.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "62ccc1a0435e1c54e10ee6022df28d6c04c2946c" + "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/62ccc1a0435e1c54e10ee6022df28d6c04c2946c", - "reference": "62ccc1a0435e1c54e10ee6022df28d6c04c2946c", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/c5c5cd056110fc8afaba29fa6b72a43ced42acd4", + "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4", "shasum": "" }, "require": { @@ -2696,7 +2696,7 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle WHATWG URL", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -2746,7 +2746,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.7.0" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.0" }, "funding": [ { @@ -2754,7 +2754,7 @@ "type": "github" } ], - "time": "2025-12-07T16:03:21+00:00" + "time": "2026-01-15T06:54:53+00:00" }, { "name": "monolog/monolog", @@ -3629,16 +3629,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "2.3.0", + "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" + "reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/16dbf9937da8d4528ceb2145c9c7c0bd29e26374", + "reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374", "shasum": "" }, "require": { @@ -3670,9 +3670,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.1" }, - "time": "2025-08-30T15:50:23+00:00" + "time": "2026-01-12T11:33:04+00:00" }, { "name": "psr/clock", @@ -7569,16 +7569,16 @@ }, { "name": "laravel/sail", - "version": "v1.51.0", + "version": "v1.52.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "1c74357df034e869250b4365dd445c9f6ba5d068" + "reference": "64ac7d8abb2dbcf2b76e61289451bae79066b0b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/1c74357df034e869250b4365dd445c9f6ba5d068", - "reference": "1c74357df034e869250b4365dd445c9f6ba5d068", + "url": "https://api.github.com/repos/laravel/sail/zipball/64ac7d8abb2dbcf2b76e61289451bae79066b0b3", + "reference": "64ac7d8abb2dbcf2b76e61289451bae79066b0b3", "shasum": "" }, "require": { @@ -7628,7 +7628,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2025-12-09T13:33:49+00:00" + "time": "2026-01-01T02:46:03+00:00" }, { "name": "mockery/mockery", @@ -8267,16 +8267,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.46", + "version": "11.5.48", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "75dfe79a2aa30085b7132bb84377c24062193f33" + "reference": "fe3665c15e37140f55aaf658c81a2eb9030b6d89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/75dfe79a2aa30085b7132bb84377c24062193f33", - "reference": "75dfe79a2aa30085b7132bb84377c24062193f33", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fe3665c15e37140f55aaf658c81a2eb9030b6d89", + "reference": "fe3665c15e37140f55aaf658c81a2eb9030b6d89", "shasum": "" }, "require": { @@ -8290,7 +8290,7 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.11", + "phpunit/php-code-coverage": "^11.0.12", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", @@ -8348,7 +8348,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.46" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.48" }, "funding": [ { @@ -8372,7 +8372,7 @@ "type": "tidelift" } ], - "time": "2025-12-06T08:01:15+00:00" + "time": "2026-01-16T16:26:27+00:00" }, { "name": "sebastian/cli-parser",