From 287c1f67c59f9b76f2235b5255151e8c2886effb Mon Sep 17 00:00:00 2001 From: Michel Date: Wed, 30 Oct 2024 19:40:26 +0100 Subject: [PATCH] I rightly do not know what this all is --- .gitignore | 254 +++++----- composer.json | 3 +- composer.lock | 452 +++++++++++------- src/css/common/fonts.scss | 4 +- src/css/common/index.scss | 86 ++-- src/js/common/index.ts | 4 +- src/js/common/theme.ts | 76 +-- src/js/pages/admin/accounts.ts | 202 ++++---- src/js/pages/index.ts | 2 +- src/js/pages/keys/import.ts | 294 ++++++------ src/js/pages/keys/index.ts | 48 +- src/js/pages/keys/share.ts | 24 + src/js/pages/keys/table.ts | 214 ++++----- src/js/pages/keys/userlists.ts | 140 +++--- src/js/pages/types/entities.ts | 50 +- src/js/pages/types/keyState.ts | 68 +-- src/php/Api/DiscordAPI.php | 156 +++--- src/php/ContainerHandler.php | 76 +-- src/php/DoctrineManager.php | 68 +-- src/php/Entities/Account/User.php | 168 +++---- src/php/Entities/Games/Game.php | 70 +-- src/php/Entities/Games/Key.php | 190 ++++---- src/php/Entities/Games/KeyAttribute.php | 42 +- src/php/Entities/Games/KeyState.php | 24 +- src/php/Entities/Games/Store.php | 30 +- src/php/Entities/GamesList.php | 122 ++--- src/php/Entities/SystemAttribute.php | 66 +-- src/php/Environment/DatabaseEnvironment.php | 40 +- src/php/Environment/DiscordEnvironment.php | 24 +- src/php/Environment/EnvironmentHandler.php | 78 +-- src/php/Errors/ExtendedException.php | 50 +- src/php/Errors/WhoopsHandler.php | 14 +- src/php/Importer/GameImporter.php | 330 ++++++------- .../Importer/ImportColumnInterpretation.php | 50 +- src/php/Login/DiscordLoginProvider.php | 92 ++-- src/php/Login/LoginHandler.php | 150 +++--- src/php/Login/LoginMethod.php | 46 +- src/php/Login/LoginProvider.php | 22 +- src/php/Login/UserPermission.php | 50 +- src/php/Paths.php | 24 +- src/php/Routing/AdminAccountConfigRoute.php | 96 ++-- src/php/Routing/Api/APIRoutes.php | 30 +- .../Api/DataTables/AccountsEndpoint.php | 158 +++--- .../Api/DataTables/DataTablesAPIRoutes.php | 30 +- .../Api/DataTables/ProviderKeysEndpoint.php | 154 +++--- .../Routing/Api/Web/CreateKeyListRoute.php | 96 ++-- .../Api/Web/ImportKeysPrepareRoute.php | 98 ++-- src/php/Routing/Api/Web/ImportKeysRoute.php | 130 ++--- src/php/Routing/Api/Web/UserModifyRoute.php | 90 ++-- src/php/Routing/Api/Web/WebAPIRoutes.php | 34 +- src/php/Routing/ErrorRoute.php | 38 +- src/php/Routing/IndexRoute.php | 64 +-- src/php/Routing/KeysRoute.php | 88 ++-- src/php/Routing/LoginRoutes.php | 142 +++--- src/php/Routing/ResourceRoute.php | 108 ++--- .../Routing/Responses/TemplateResponse.php | 38 +- src/php/Routing/Router.php | 120 ++--- src/php/Routing/SetupRoute.php | 102 ++-- src/php/SetupHandler.php | 24 +- src/php/Templates/NavigationHeader.php | 32 +- src/php/Templates/ResourceEntry.php | 30 +- src/php/Templates/ResourceIndex.php | 100 ++-- src/php/Templates/TemplateEngine.php | 66 +-- src/php/bin/doctrine.php | 28 +- src/php/bootstrap.php | 20 +- src/php/index.dev.php | 48 +- src/php/index.prod.php | 2 +- src/templates/layout/accountDisplay.php | 70 +-- src/templates/layout/keyTable.php | 10 +- src/templates/layout/main.php | 98 ++-- src/templates/layout/navbar.php | 104 ++-- src/templates/pages/admin/accounts.php | 114 ++--- src/templates/pages/error.php | 22 +- src/templates/pages/index.php | 16 +- src/templates/pages/key-manager.php | 296 ++++++------ src/templates/pages/login.php | 28 +- tsconfig.json | 20 +- webpack.config.js | 202 ++++---- 78 files changed, 3484 insertions(+), 3365 deletions(-) create mode 100644 src/js/pages/keys/share.ts diff --git a/.gitignore b/.gitignore index 92835b4..cf369b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,127 +1,127 @@ -# ----------------------------------------------------------------- -# .gitignore -# Bare Minimum Git -# https://salferrarello.com/starter-gitignore-file/ -# ver 20221125 -# -# From the root of your project run -# curl -O https://gist.githubusercontent.com/salcode/10017553/raw/.gitignore -# to download this file -# -# This file is tailored for a general web project, it -# is NOT optimized for a WordPress project. See -# https://gist.github.com/salcode/b515f520d3f8207ecd04 -# for a WordPress specific .gitignore -# -# This file specifies intentionally untracked files to ignore -# http://git-scm.com/docs/gitignore -# -# NOTES: -# The purpose of gitignore files is to ensure that certain files not -# tracked by Git remain untracked. -# -# To ignore uncommitted changes in a file that is already tracked, -# use `git update-index --assume-unchanged`. -# -# To stop tracking a file that is currently tracked, -# use `git rm --cached` -# -# Change Log: -# 20221125 ignore /dist directory -# unignore /.git-blame-ignore-revs -# 20220720 ignore /build directory -# 20220128 unignore .nvmrc -# 20210211 unignore .env.example -# 20190705 ignore private/secret files -# 20181206 remove trailing whitespaces -# 20180714 unignore .phpcs.xml.dist -# 20170502 unignore composer.lock -# 20170502 ignore components loaded via Bower -# 20150326 ignore jekyll build directory `/_site` -# 20150324 Reorganized file to list ignores first and whitelisted last, -# change WordPress .gitignore link to preferred gist, -# add curl line for quick installation -# ignore composer files (vendor directory and lock file) -# 20140606 Add .editorconfig as a tracked file -# 20140418 remove explicit inclusion -# of readme.md (this is not an ignored file by default) -# 20140407 Initially Published -# -# ----------------------------------------------------------------- - -# ignore all files starting with . or ~ -.* -~* - -# ignore node/grunt dependency directories -node_modules/ - -# Ignore build directories. -/build -/dist - -# ignore composer vendor directory -/vendor - -# ignore components loaded via Bower -/bower_components - -# ignore jekyll build directory -/_site - -# ignore OS generated files -ehthumbs.db -Thumbs.db - -# ignore Editor files -*.sublime-project -*.sublime-workspace -*.komodoproject - -# ignore log files and databases -*.log -*.sql -*.sqlite - -# ignore compiled files -*.com -*.class -*.dll -*.exe -*.o -*.so - -# ignore packaged files -*.7z -*.dmg -*.gz -*.iso -*.jar -*.rar -*.tar -*.zip - -# ignore private/secret files -*.der -*.key -*.pem - -# -------------------------------------------------------- -# BEGIN Explictly Allowed Files (i.e. do NOT ignore these) -# -------------------------------------------------------- - -# track these files, if they exist -!.editorconfig -!.env.example -!.git-blame-ignore-revs -!.gitignore -!.nvmrc -!.phpcs.xml.dist - -# ------------ -# USER CHANGES -# ------------ - -# ignore public directory (we build it on server) -/public -/data/ +# ----------------------------------------------------------------- +# .gitignore +# Bare Minimum Git +# https://salferrarello.com/starter-gitignore-file/ +# ver 20221125 +# +# From the root of your project run +# curl -O https://gist.githubusercontent.com/salcode/10017553/raw/.gitignore +# to download this file +# +# This file is tailored for a general web project, it +# is NOT optimized for a WordPress project. See +# https://gist.github.com/salcode/b515f520d3f8207ecd04 +# for a WordPress specific .gitignore +# +# This file specifies intentionally untracked files to ignore +# http://git-scm.com/docs/gitignore +# +# NOTES: +# The purpose of gitignore files is to ensure that certain files not +# tracked by Git remain untracked. +# +# To ignore uncommitted changes in a file that is already tracked, +# use `git update-index --assume-unchanged`. +# +# To stop tracking a file that is currently tracked, +# use `git rm --cached` +# +# Change Log: +# 20221125 ignore /dist directory +# unignore /.git-blame-ignore-revs +# 20220720 ignore /build directory +# 20220128 unignore .nvmrc +# 20210211 unignore .env.example +# 20190705 ignore private/secret files +# 20181206 remove trailing whitespaces +# 20180714 unignore .phpcs.xml.dist +# 20170502 unignore composer.lock +# 20170502 ignore components loaded via Bower +# 20150326 ignore jekyll build directory `/_site` +# 20150324 Reorganized file to list ignores first and whitelisted last, +# change WordPress .gitignore link to preferred gist, +# add curl line for quick installation +# ignore composer files (vendor directory and lock file) +# 20140606 Add .editorconfig as a tracked file +# 20140418 remove explicit inclusion +# of readme.md (this is not an ignored file by default) +# 20140407 Initially Published +# +# ----------------------------------------------------------------- + +# ignore all files starting with . or ~ +.* +~* + +# ignore node/grunt dependency directories +node_modules/ + +# Ignore build directories. +/build +/dist + +# ignore composer vendor directory +/vendor + +# ignore components loaded via Bower +/bower_components + +# ignore jekyll build directory +/_site + +# ignore OS generated files +ehthumbs.db +Thumbs.db + +# ignore Editor files +*.sublime-project +*.sublime-workspace +*.komodoproject + +# ignore log files and databases +*.log +*.sql +*.sqlite + +# ignore compiled files +*.com +*.class +*.dll +*.exe +*.o +*.so + +# ignore packaged files +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# ignore private/secret files +*.der +*.key +*.pem + +# -------------------------------------------------------- +# BEGIN Explictly Allowed Files (i.e. do NOT ignore these) +# -------------------------------------------------------- + +# track these files, if they exist +!.editorconfig +!.env.example +!.git-blame-ignore-revs +!.gitignore +!.nvmrc +!.phpcs.xml.dist + +# ------------ +# USER CHANGES +# ------------ + +# ignore public directory (we build it on server) +/public +/data/ diff --git a/composer.json b/composer.json index a2bed0f..77eddf5 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,7 @@ "symfony/uid": "^7.1", "php-curl-class/php-curl-class": "^9.19", "symfony/cache": "^7.1", - "phpoffice/phpspreadsheet": "^2.1" + "phpoffice/phpspreadsheet": "^2.1", + "symfony/polyfill-iconv": "^1.31" } } diff --git a/composer.lock b/composer.lock index 5ac48fa..e6ff606 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": "89e0df3a5a9ac052a0102b007850e150", + "content-hash": "05654f94aa961d630f571a3d533e8543", "packages": [ { "name": "doctrine/collections", @@ -94,16 +94,16 @@ }, { "name": "doctrine/dbal", - "version": "4.0.4", + "version": "4.2.1", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "50fda19f80724b55ff770bb4ff352407008e63c5" + "reference": "dadd35300837a3a2184bd47d403333b15d0a9bd0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/50fda19f80724b55ff770bb4ff352407008e63c5", - "reference": "50fda19f80724b55ff770bb4ff352407008e63c5", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/dadd35300837a3a2184bd47d403333b15d0a9bd0", + "reference": "dadd35300837a3a2184bd47d403333b15d0a9bd0", "shasum": "" }, "require": { @@ -116,16 +116,16 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.2", - "phpstan/phpstan": "1.11.5", + "phpstan/phpstan": "1.12.6", "phpstan/phpstan-phpunit": "1.4.0", "phpstan/phpstan-strict-rules": "^1.6", - "phpunit/phpunit": "10.5.22", + "phpunit/phpunit": "10.5.30", "psalm/plugin-phpunit": "0.19.0", "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.10.1", + "squizlabs/php_codesniffer": "3.10.2", "symfony/cache": "^6.3.8|^7.0", "symfony/console": "^5.4|^6.3|^7.0", - "vimeo/psalm": "5.24.0" + "vimeo/psalm": "5.25.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -182,7 +182,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/4.0.4" + "source": "https://github.com/doctrine/dbal/tree/4.2.1" }, "funding": [ { @@ -198,7 +198,7 @@ "type": "tidelift" } ], - "time": "2024-06-19T11:57:23+00:00" + "time": "2024-10-10T18:01:27+00:00" }, { "name": "doctrine/deprecations", @@ -578,16 +578,16 @@ }, { "name": "doctrine/orm", - "version": "3.2.1", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "722cea6536775206e81744542b36fa7c9a4ea3e5" + "reference": "69958152e661aa9c14e80d1ee4962863485aa60b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/722cea6536775206e81744542b36fa7c9a4ea3e5", - "reference": "722cea6536775206e81744542b36fa7c9a4ea3e5", + "url": "https://api.github.com/repos/doctrine/orm/zipball/69958152e661aa9c14e80d1ee4962863485aa60b", + "reference": "69958152e661aa9c14e80d1ee4962863485aa60b", "shasum": "" }, "require": { @@ -609,7 +609,10 @@ "require-dev": { "doctrine/coding-standard": "^12.0", "phpbench/phpbench": "^1.0", - "phpstan/phpstan": "1.11.1", + "phpdocumentor/guides-cli": "^1.4", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "1.12.6", + "phpstan/phpstan-deprecation-rules": "^1.2", "phpunit/phpunit": "^10.4.0", "psr/log": "^1 || ^2 || ^3", "squizlabs/php_codesniffer": "3.7.2", @@ -660,9 +663,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/3.2.1" + "source": "https://github.com/doctrine/orm/tree/3.3.0" }, - "time": "2024-06-26T21:48:58+00:00" + "time": "2024-10-12T20:07:18+00:00" }, { "name": "doctrine/persistence", @@ -816,26 +819,26 @@ }, { "name": "filp/whoops", - "version": "2.15.4", + "version": "2.16.0", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" + "reference": "befcdc0e5dce67252aa6322d82424be928214fa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", + "url": "https://api.github.com/repos/filp/whoops/zipball/befcdc0e5dce67252aa6322d82424be928214fa2", + "reference": "befcdc0e5dce67252aa6322d82424be928214fa2", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0 || ^8.0", + "php": "^7.1 || ^8.0", "psr/log": "^1.0.1 || ^2.0 || ^3.0" }, "require-dev": { - "mockery/mockery": "^0.9 || ^1.0", - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" }, "suggest": { "symfony/var-dumper": "Pretty print complex values better with var-dumper available", @@ -875,7 +878,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.15.4" + "source": "https://github.com/filp/whoops/tree/2.16.0" }, "funding": [ { @@ -883,28 +886,28 @@ "type": "github" } ], - "time": "2023-11-03T12:00:00+00:00" + "time": "2024-09-25T12:00:00+00:00" }, { "name": "graham-campbell/result-type", - "version": "v1.1.2", + "version": "v1.1.3", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862" + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862", - "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.2" + "phpoption/phpoption": "^1.9.3" }, "require-dev": { - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" }, "type": "library", "autoload": { @@ -933,7 +936,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" }, "funding": [ { @@ -945,29 +948,32 @@ "type": "tidelift" } ], - "time": "2023-11-12T22:16:48+00:00" + "time": "2024-07-20T21:45:45+00:00" }, { "name": "laminas/laminas-diactoros", - "version": "3.3.1", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45" + "reference": "143a16306602ce56b8b092a7914fef03c37f9ed2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45", - "reference": "74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/143a16306602ce56b8b092a7914fef03c37f9ed2", + "reference": "143a16306602ce56b8b092a7914fef03c37f9ed2", "shasum": "" }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", - "psr/http-factory": "^1.0.2", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "psr/http-factory": "^1.1", "psr/http-message": "^1.1 || ^2.0" }, + "conflict": { + "amphp/amp": "<2.6.4" + }, "provide": { - "psr/http-factory-implementation": "^1.1 || ^2.0", + "psr/http-factory-implementation": "^1.0", "psr/http-message-implementation": "^1.1 || ^2.0" }, "require-dev": { @@ -975,12 +981,12 @@ "ext-dom": "*", "ext-gd": "*", "ext-libxml": "*", - "http-interop/http-factory-tests": "^0.9.0", + "http-interop/http-factory-tests": "^2.2.0", "laminas/laminas-coding-standard": "~2.5.0", - "php-http/psr7-integration-tests": "^1.3", - "phpunit/phpunit": "^9.6.16", - "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.22.1" + "php-http/psr7-integration-tests": "^1.4.0", + "phpunit/phpunit": "^10.5.36", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.26.1" }, "type": "library", "extra": { @@ -1030,34 +1036,34 @@ "type": "community_bridge" } ], - "time": "2024-02-16T16:06:16+00:00" + "time": "2024-10-14T11:59:49+00:00" }, { "name": "laminas/laminas-httphandlerrunner", - "version": "2.10.0", + "version": "2.11.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-httphandlerrunner.git", - "reference": "35a0ba92e940a2f9533754f5a56187fa321f7693" + "reference": "c428d9f67f280d155637cbe2b7245b5188c8cdae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/35a0ba92e940a2f9533754f5a56187fa321f7693", - "reference": "35a0ba92e940a2f9533754f5a56187fa321f7693", + "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/c428d9f67f280d155637cbe2b7245b5188c8cdae", + "reference": "c428d9f67f280d155637cbe2b7245b5188c8cdae", "shasum": "" }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", "psr/http-message": "^1.0 || ^2.0", "psr/http-message-implementation": "^1.0 || ^2.0", "psr/http-server-handler": "^1.0" }, "require-dev": { - "laminas/laminas-coding-standard": "~2.5.0", - "laminas/laminas-diactoros": "^3.3.0", - "phpunit/phpunit": "^10.5.5", - "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.18" + "laminas/laminas-coding-standard": "~3.0.0", + "laminas/laminas-diactoros": "^3.4.0", + "phpunit/phpunit": "^10.5.36", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.26.1" }, "type": "library", "extra": { @@ -1097,20 +1103,20 @@ "type": "community_bridge" } ], - "time": "2024-01-04T10:50:34+00:00" + "time": "2024-10-17T20:37:17+00:00" }, { "name": "league/container", - "version": "4.2.2", + "version": "4.2.3", "source": { "type": "git", "url": "https://github.com/thephpleague/container.git", - "reference": "ff346319ca1ff0e78277dc2311a42107cc1aab88" + "reference": "72f9bebe7bd623007782a40f5ec305661ab706d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/container/zipball/ff346319ca1ff0e78277dc2311a42107cc1aab88", - "reference": "ff346319ca1ff0e78277dc2311a42107cc1aab88", + "url": "https://api.github.com/repos/thephpleague/container/zipball/72f9bebe7bd623007782a40f5ec305661ab706d8", + "reference": "72f9bebe7bd623007782a40f5ec305661ab706d8", "shasum": "" }, "require": { @@ -1171,7 +1177,7 @@ ], "support": { "issues": "https://github.com/thephpleague/container/issues", - "source": "https://github.com/thephpleague/container/tree/4.2.2" + "source": "https://github.com/thephpleague/container/tree/4.2.3" }, "funding": [ { @@ -1179,7 +1185,7 @@ "type": "github" } ], - "time": "2024-03-13T13:12:53+00:00" + "time": "2024-10-23T12:06:58+00:00" }, { "name": "league/plates", @@ -1336,16 +1342,16 @@ }, { "name": "maennchen/zipstream-php", - "version": "3.1.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/maennchen/ZipStream-PHP.git", - "reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1" + "reference": "6187e9cc4493da94b9b63eb2315821552015fca9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/b8174494eda667f7d13876b4a7bfef0f62a7c0d1", - "reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/6187e9cc4493da94b9b63eb2315821552015fca9", + "reference": "6187e9cc4493da94b9b63eb2315821552015fca9", "shasum": "" }, "require": { @@ -1401,19 +1407,15 @@ ], "support": { "issues": "https://github.com/maennchen/ZipStream-PHP/issues", - "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.0" + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.1" }, "funding": [ { "url": "https://github.com/maennchen", "type": "github" - }, - { - "url": "https://opencollective.com/zipstream", - "type": "open_collective" } ], - "time": "2023-06-21T14:59:35+00:00" + "time": "2024-10-10T12:33:01+00:00" }, { "name": "markbaker/complex", @@ -1719,16 +1721,16 @@ }, { "name": "phpoffice/phpspreadsheet", - "version": "2.1.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "dbed77bd3a0f68f96c0dd68ad4499d5674fecc3e" + "reference": "c972c146ddd5e8350ea839355b9bb0ce6a8fa33e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/dbed77bd3a0f68f96c0dd68ad4499d5674fecc3e", - "reference": "dbed77bd3a0f68f96c0dd68ad4499d5674fecc3e", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/c972c146ddd5e8350ea839355b9bb0ce6a8fa33e", + "reference": "c972c146ddd5e8350ea839355b9bb0ce6a8fa33e", "shasum": "" }, "require": { @@ -1748,21 +1750,21 @@ "maennchen/zipstream-php": "^2.1 || ^3.0", "markbaker/complex": "^3.0", "markbaker/matrix": "^3.0", - "php": "^8.0", + "php": "^8.1", "psr/http-client": "^1.0", "psr/http-factory": "^1.0", "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "dev-main", - "dompdf/dompdf": "^2.0", + "dompdf/dompdf": "^2.0 || ^3.0", "friendsofphp/php-cs-fixer": "^3.2", "mitoteam/jpgraph": "^10.3", "mpdf/mpdf": "^8.1.1", "phpcompatibility/php-compatibility": "^9.3", "phpstan/phpstan": "^1.1", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.6", + "phpunit/phpunit": "^9.6 || ^10.5", "squizlabs/php_codesniffer": "^3.7", "tecnickcom/tcpdf": "^6.5" }, @@ -1817,22 +1819,22 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/2.1.0" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/2.3.0" }, - "time": "2024-05-11T04:17:56+00:00" + "time": "2024-09-29T07:06:02+00:00" }, { "name": "phpoption/phpoption", - "version": "1.9.2", + "version": "1.9.3", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820" + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820", - "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", "shasum": "" }, "require": { @@ -1840,13 +1842,13 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" }, "type": "library", "extra": { "bamarni-bin": { "bin-links": true, - "forward-command": true + "forward-command": false }, "branch-alias": { "dev-master": "1.9-dev" @@ -1882,7 +1884,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.2" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" }, "funding": [ { @@ -1894,7 +1896,7 @@ "type": "tidelift" } ], - "time": "2023-11-12T21:59:55+00:00" + "time": "2024-07-20T21:41:07+00:00" }, { "name": "psr/cache", @@ -2273,16 +2275,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -2317,9 +2319,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "psr/simple-cache", @@ -2418,16 +2420,16 @@ }, { "name": "symfony/cache", - "version": "v7.1.2", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "e933e1d947ffb88efcdd34a2bd51561cab7deaae" + "reference": "567ef6de47fdcba56eb6c0b344b857d1fce1cce0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/e933e1d947ffb88efcdd34a2bd51561cab7deaae", - "reference": "e933e1d947ffb88efcdd34a2bd51561cab7deaae", + "url": "https://api.github.com/repos/symfony/cache/zipball/567ef6de47fdcba56eb6c0b344b857d1fce1cce0", + "reference": "567ef6de47fdcba56eb6c0b344b857d1fce1cce0", "shasum": "" }, "require": { @@ -2495,7 +2497,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.1.2" + "source": "https://github.com/symfony/cache/tree/v7.1.6" }, "funding": [ { @@ -2511,7 +2513,7 @@ "type": "tidelift" } ], - "time": "2024-06-11T13:32:38+00:00" + "time": "2024-10-25T15:39:55+00:00" }, { "name": "symfony/cache-contracts", @@ -2591,16 +2593,16 @@ }, { "name": "symfony/console", - "version": "v7.1.2", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0aa29ca177f432ab68533432db0de059f39c92ae" + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0aa29ca177f432ab68533432db0de059f39c92ae", - "reference": "0aa29ca177f432ab68533432db0de059f39c92ae", + "url": "https://api.github.com/repos/symfony/console/zipball/bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", "shasum": "" }, "require": { @@ -2664,7 +2666,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.2" + "source": "https://github.com/symfony/console/tree/v7.1.6" }, "funding": [ { @@ -2680,7 +2682,7 @@ "type": "tidelift" } ], - "time": "2024-06-28T10:03:55+00:00" + "time": "2024-10-09T08:46:59+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2751,20 +2753,20 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -2810,7 +2812,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -2826,24 +2828,104 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.30.0", + "name": "symfony/polyfill-iconv", + "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + "url": "https://github.com/symfony/polyfill-iconv.git", + "reference": "48becf00c920479ca2e910c22a5a39e5d47ca956" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/48becf00c920479ca2e910c22a5a39e5d47ca956", + "reference": "48becf00c920479ca2e910c22a5a39e5d47ca956", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" + }, + "provide": { + "ext-iconv": "*" + }, + "suggest": { + "ext-iconv": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Iconv\\": "" + } + }, + "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 for the Iconv extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "iconv", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "" + }, + "require": { + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -2888,7 +2970,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -2904,24 +2986,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -2969,7 +3051,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -2985,24 +3067,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -3049,7 +3131,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -3065,24 +3147,24 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -3129,7 +3211,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" }, "funding": [ { @@ -3145,24 +3227,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9" + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/2ba1f33797470debcda07fe9dce20a0003df18e9", - "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-uuid": "*" @@ -3208,7 +3290,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0" }, "funding": [ { @@ -3224,7 +3306,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/service-contracts", @@ -3311,16 +3393,16 @@ }, { "name": "symfony/string", - "version": "v7.1.2", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8" + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/14221089ac66cf82e3cf3d1c1da65de305587ff8", - "reference": "14221089ac66cf82e3cf3d1c1da65de305587ff8", + "url": "https://api.github.com/repos/symfony/string/zipball/61b72d66bf96c360a727ae6232df5ac83c71f626", + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626", "shasum": "" }, "require": { @@ -3378,7 +3460,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.2" + "source": "https://github.com/symfony/string/tree/v7.1.6" }, "funding": [ { @@ -3394,20 +3476,20 @@ "type": "tidelift" } ], - "time": "2024-06-28T09:27:18+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/uid", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "bb59febeecc81528ff672fad5dab7f06db8c8277" + "reference": "65befb3bb2d503bbffbd08c815aa38b472999917" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/bb59febeecc81528ff672fad5dab7f06db8c8277", - "reference": "bb59febeecc81528ff672fad5dab7f06db8c8277", + "url": "https://api.github.com/repos/symfony/uid/zipball/65befb3bb2d503bbffbd08c815aa38b472999917", + "reference": "65befb3bb2d503bbffbd08c815aa38b472999917", "shasum": "" }, "require": { @@ -3452,7 +3534,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.1.1" + "source": "https://github.com/symfony/uid/tree/v7.1.6" }, "funding": [ { @@ -3468,20 +3550,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.1.2", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "b80a669a2264609f07f1667f891dbfca25eba44c" + "reference": "90173ef89c40e7c8c616653241048705f84130ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/b80a669a2264609f07f1667f891dbfca25eba44c", - "reference": "b80a669a2264609f07f1667f891dbfca25eba44c", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/90173ef89c40e7c8c616653241048705f84130ef", + "reference": "90173ef89c40e7c8c616653241048705f84130ef", "shasum": "" }, "require": { @@ -3528,7 +3610,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.1.2" + "source": "https://github.com/symfony/var-exporter/tree/v7.1.6" }, "funding": [ { @@ -3544,27 +3626,27 @@ "type": "tidelift" } ], - "time": "2024-06-28T08:00:31+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v5.6.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4" + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", - "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.1.2", + "graham-campbell/result-type": "^1.1.3", "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.2", + "phpoption/phpoption": "^1.9.3", "symfony/polyfill-ctype": "^1.24", "symfony/polyfill-mbstring": "^1.24", "symfony/polyfill-php80": "^1.24" @@ -3581,7 +3663,7 @@ "extra": { "bamarni-bin": { "bin-links": true, - "forward-command": true + "forward-command": false }, "branch-alias": { "dev-master": "5.6-dev" @@ -3616,7 +3698,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" }, "funding": [ { @@ -3628,16 +3710,16 @@ "type": "tidelift" } ], - "time": "2023-11-12T22:43:29+00:00" + "time": "2024-07-20T21:52:34+00:00" } ], "packages-dev": [], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [], + "platform": {}, + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/src/css/common/fonts.scss b/src/css/common/fonts.scss index aa4daf9..12bddd2 100644 --- a/src/css/common/fonts.scss +++ b/src/css/common/fonts.scss @@ -1,3 +1,3 @@ -@import "@fortawesome/fontawesome-free/scss/fontawesome"; -@import "@fortawesome/fontawesome-free/scss/solid"; +@import "@fortawesome/fontawesome-free/scss/fontawesome"; +@import "@fortawesome/fontawesome-free/scss/solid"; @import "@fortawesome/fontawesome-free/scss/brands"; \ No newline at end of file diff --git a/src/css/common/index.scss b/src/css/common/index.scss index 1b272c9..b423bd3 100644 --- a/src/css/common/index.scss +++ b/src/css/common/index.scss @@ -1,44 +1,44 @@ -@import "bootstrap"; -@import "fonts"; - -$main-container-max-width: 992px; - -.cursor-pointer { - cursor: pointer; -} - -.navigation-container, -main { - width: 100%; - max-width: $main-container-max-width; - - margin: 0 auto; -} - -.ratio-1 { - aspect-ratio: 1; -} - -.avatar { - height: 3rem; - - .avatar-login-method-icon { - scale: 1.5; - } -} - -@include media-breakpoint-down(lg) { - .mode-switch { - text-align: center; - padding-top: 0.5rem; - margin-left: auto; - } - - .navigation-container { - .navbar-brand { - margin: 0 auto !important; - } - - } - +@import "bootstrap"; +@import "fonts"; + +$main-container-max-width: 992px; + +.cursor-pointer { + cursor: pointer; +} + +.navigation-container, +main { + width: 100%; + max-width: $main-container-max-width; + + margin: 0 auto; +} + +.ratio-1 { + aspect-ratio: 1; +} + +.avatar { + height: 3rem; + + .avatar-login-method-icon { + scale: 1.5; + } +} + +@include media-breakpoint-down(lg) { + .mode-switch { + text-align: center; + padding-top: 0.5rem; + margin-left: auto; + } + + .navigation-container { + .navbar-brand { + margin: 0 auto !important; + } + + } + } \ No newline at end of file diff --git a/src/js/common/index.ts b/src/js/common/index.ts index adb695b..07b5db2 100644 --- a/src/js/common/index.ts +++ b/src/js/common/index.ts @@ -1,3 +1,3 @@ -import "./theme"; - +import "./theme"; + import "bootstrap/js/src/collapse"; \ No newline at end of file diff --git a/src/js/common/theme.ts b/src/js/common/theme.ts index 26cc047..9b28bf0 100644 --- a/src/js/common/theme.ts +++ b/src/js/common/theme.ts @@ -1,39 +1,39 @@ -(() => { - function setTheme (mode = 'auto') { - const userMode = localStorage.getItem('bs-theme'); - const sysMode = window.matchMedia('(prefers-color-scheme: light)').matches; - const useSystem = mode === 'system' || (!userMode && mode === 'auto'); - - const modeChosen = selectMode(mode); - - if (useSystem) { - localStorage.removeItem('bs-theme'); - } else { - localStorage.setItem('bs-theme', modeChosen); - } - - document.documentElement.setAttribute('data-bs-theme', useSystem ? (sysMode ? 'light' : 'dark') : modeChosen); - document.querySelectorAll('.mode-switch .btn').forEach(e => e.classList.remove('text-body')); - document.getElementById(modeChosen)?.classList.add('text-body'); - } - - function selectMode(mode: string): string { - const userMode = localStorage.getItem('bs-theme'); - const useSystem = mode === 'system' || (!userMode && mode === 'auto'); - - if (useSystem) { - return 'system'; - } - - if (mode === 'dark' || mode === 'light') { - return mode; - } - return userMode; - } - - document.addEventListener('DOMContentLoaded', () => { - setTheme(); - document.querySelectorAll('.mode-switch .btn').forEach(e => e.addEventListener('click', () => setTheme(e.id))); - window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', () => setTheme()); - }); +(() => { + function setTheme (mode = 'auto') { + const userMode = localStorage.getItem('bs-theme'); + const sysMode = window.matchMedia('(prefers-color-scheme: light)').matches; + const useSystem = mode === 'system' || (!userMode && mode === 'auto'); + + const modeChosen = selectMode(mode); + + if (useSystem) { + localStorage.removeItem('bs-theme'); + } else { + localStorage.setItem('bs-theme', modeChosen); + } + + document.documentElement.setAttribute('data-bs-theme', useSystem ? (sysMode ? 'light' : 'dark') : modeChosen); + document.querySelectorAll('.mode-switch .btn').forEach(e => e.classList.remove('text-body')); + document.getElementById(modeChosen)?.classList.add('text-body'); + } + + function selectMode(mode: string): string { + const userMode = localStorage.getItem('bs-theme'); + const useSystem = mode === 'system' || (!userMode && mode === 'auto'); + + if (useSystem) { + return 'system'; + } + + if (mode === 'dark' || mode === 'light') { + return mode; + } + return userMode; + } + + document.addEventListener('DOMContentLoaded', () => { + setTheme(); + document.querySelectorAll('.mode-switch .btn').forEach(e => e.addEventListener('click', () => setTheme(e.id))); + window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', () => setTheme()); + }); })() \ No newline at end of file diff --git a/src/js/pages/admin/accounts.ts b/src/js/pages/admin/accounts.ts index 6133a7b..f8d25a2 100644 --- a/src/js/pages/admin/accounts.ts +++ b/src/js/pages/admin/accounts.ts @@ -1,102 +1,102 @@ -import '../../../css/common/index.scss'; -import 'datatables.net-bs5/css/dataTables.bootstrap5.css'; -import '../../common/index'; - -import DataTable from 'datatables.net-bs5'; -import {Modal} from "bootstrap"; - -const DT_SERVERSIDE_PROCESSING_URL = '/api/dt/accounts'; -const TABLE = document.querySelector('#user-table'); - -function displayEdit(data: any) { - - const modalElem = document.querySelector('#edit-modal'); - // @ts-ignore - modalElem.querySelector('.name-input').textContent = data.name; - // @ts-ignore - modalElem.querySelector('.login-method').textContent = data.loginMethod; - // @ts-ignore - modalElem.querySelector('.permission-editor').value = data.permissionIndex; - - // @ts-ignore - modalElem?.querySelector('.js--save').dataset.userid = data.userid; - - Modal.getOrCreateInstance('#edit-modal').show(); -} - -document.addEventListener('DOMContentLoaded', () => { - const dt = new DataTable('#user-table', { - ajax: { - url: DT_SERVERSIDE_PROCESSING_URL, - }, - serverSide: true, - columns: [ - { - data: 'profilePictureUrl', - render(data, type) { - if (type !== 'display') { - return data; - } - - return `Profile Picture` - }, - orderable: false, - searchable: false - }, - { - name: 'Name', - data: 'name' - }, - { - name: 'Permission', - data: 'permission' - }, - { - name: 'Login-Method', - data: 'loginMethod' - } - ], - order: [ [1, 'asc'] ], - drawCallback: function (settings) { - const api = new DataTable.Api(settings); - api.rows().every(function (row) { - const node = this.node(); - const data = this.data(); - - node.addEventListener('click', (e) => { - displayEdit(data); - }); - }); - } - }); - - const button = document.querySelector('#edit-modal .js--save'); - if (!button) { - return; - } - - button.addEventListener('click', async (e) => { - const permissionEditor = document.querySelector('#edit-modal .permission-editor'); - if (!permissionEditor) { - return; - } - - const formData = new FormData(); - formData.set('permission', permissionEditor.value); - - const response = await fetch( - `/api/web/users/${button.dataset.userid}`, - { - method: 'POST', - body: formData - } - ); - - if (!response.ok) { - throw new Error(response.statusText); - } - - Modal.getOrCreateInstance('#edit-modal').hide(); - dt.draw(); - }) +import '../../../css/common/index.scss'; +import 'datatables.net-bs5/css/dataTables.bootstrap5.css'; +import '../../common/index'; + +import DataTable from 'datatables.net-bs5'; +import {Modal} from "bootstrap"; + +const DT_SERVERSIDE_PROCESSING_URL = '/api/dt/accounts'; +const TABLE = document.querySelector('#user-table'); + +function displayEdit(data: any) { + + const modalElem = document.querySelector('#edit-modal'); + // @ts-ignore + modalElem.querySelector('.name-input').textContent = data.name; + // @ts-ignore + modalElem.querySelector('.login-method').textContent = data.loginMethod; + // @ts-ignore + modalElem.querySelector('.permission-editor').value = data.permissionIndex; + + // @ts-ignore + modalElem?.querySelector('.js--save').dataset.userid = data.userid; + + Modal.getOrCreateInstance('#edit-modal').show(); +} + +document.addEventListener('DOMContentLoaded', () => { + const dt = new DataTable('#user-table', { + ajax: { + url: DT_SERVERSIDE_PROCESSING_URL, + }, + serverSide: true, + columns: [ + { + data: 'profilePictureUrl', + render(data, type) { + if (type !== 'display') { + return data; + } + + return `Profile Picture` + }, + orderable: false, + searchable: false + }, + { + name: 'Name', + data: 'name' + }, + { + name: 'Permission', + data: 'permission' + }, + { + name: 'Login-Method', + data: 'loginMethod' + } + ], + order: [ [1, 'asc'] ], + drawCallback: function (settings) { + const api = new DataTable.Api(settings); + api.rows().every(function (row) { + const node = this.node(); + const data = this.data(); + + node.addEventListener('click', (e) => { + displayEdit(data); + }); + }); + } + }); + + const button = document.querySelector('#edit-modal .js--save'); + if (!button) { + return; + } + + button.addEventListener('click', async (e) => { + const permissionEditor = document.querySelector('#edit-modal .permission-editor'); + if (!permissionEditor) { + return; + } + + const formData = new FormData(); + formData.set('permission', permissionEditor.value); + + const response = await fetch( + `/api/web/users/${button.dataset.userid}`, + { + method: 'POST', + body: formData + } + ); + + if (!response.ok) { + throw new Error(response.statusText); + } + + Modal.getOrCreateInstance('#edit-modal').hide(); + dt.draw(); + }) }) \ No newline at end of file diff --git a/src/js/pages/index.ts b/src/js/pages/index.ts index 7de9e74..600609c 100644 --- a/src/js/pages/index.ts +++ b/src/js/pages/index.ts @@ -1,2 +1,2 @@ -import '../../css/common/index.scss'; +import '../../css/common/index.scss'; import '../common/index'; \ No newline at end of file diff --git a/src/js/pages/keys/import.ts b/src/js/pages/keys/import.ts index a7c90d9..af44007 100644 --- a/src/js/pages/keys/import.ts +++ b/src/js/pages/keys/import.ts @@ -1,148 +1,148 @@ -import {getCurrentlySelectedList} from "./userlists"; -import {getTableAPI} from "./table"; - -async function checkImportFile() { - const fileInput = document.querySelector('#import'); - - if (!fileInput) { - return; - } - - if (!fileInput.files || !fileInput.files[0]) { - return; - } - - const formData = new FormData(); - formData.set('file', fileInput.files[0]); - - const response = await fetch( - `/api/web/keys/import/prepare`, - { - method: 'POST', - body: formData - } - ); - - if (!response.ok) { - throw new Error(response.statusText); - } - - const jsonData = await response.json(); - - const container = document.getElementById('import-info-container'); - if (!container) { - return - } - - container.classList.remove('d-none'); - createTableContents(jsonData); -} - -function createTableContents(data: {index: string, displayName: string, guessedAttribute: string}[]) { - const table = document.querySelector('#import-attribute-table'); - if (!table) { - return; - } - - const tbody = table.querySelector('tbody'); - if (!tbody) { - return; - } - - for (let child of tbody.children) { - child.remove(); - } - - const keyAttributeMeta = document.querySelector('meta[name="key-attributes"]')?.content; - const attributes = JSON.parse(keyAttributeMeta ?? ''); - - const select = document.createElement('select'); - select.classList.add('form-select', 'w-100'); - for (let attributeName in attributes) { - const attributeValue = attributes[attributeName]; - const option = document.createElement('option'); - option.value = attributeValue; - option.textContent = attributeName; - - select.add(option); - } - - data.forEach((datum) => { - const row = tbody.insertRow(); - const indexCell = row.insertCell(); - indexCell.classList.add('index-cell'); - indexCell.innerText = datum.index; - - const displayNameCEll = row.insertCell(); - displayNameCEll.classList.add('name-cell'); - displayNameCEll.innerText = datum.displayName; - - const attributeCell = row.insertCell(); - attributeCell.classList.add('attribute-cell'); - const rowSelect = select.cloneNode(true); - rowSelect.value = datum.guessedAttribute; - attributeCell.appendChild(rowSelect); - }) -} - -async function doImport() { - const fileInput = document.querySelector('#import'); - - if (!fileInput) { - return; - } - - if (!fileInput.files || !fileInput.files[0]) { - return; - } - - const formData = new FormData(); - formData.set('file', fileInput.files[0]); - - const tbody = document.querySelector('#import-attribute-table tbody'); - if (!tbody) { - return; - } - - for (let row of tbody.querySelectorAll('tr')) { - const columnIndex = row.querySelector('.index-cell')?.textContent; - const attribute = row.querySelector('.attribute-cell select')?.value ?? 'none'; - - formData.set(`columns[${columnIndex}]`, attribute); - } - - const listId = getCurrentlySelectedList() ?? 0; - formData.set('listid', listId.toString()); - - const response = await fetch( - `/api/web/keys/import/perform`, - { - method: 'POST', - body: formData - } - ); - - if (!response.ok) { - throw new Error(response.statusText); - } - - const container = document.getElementById('import-info-container'); - if (!container) { - return - } - - container.classList.add('d-none'); - fileInput.value = ''; - - getTableAPI().ajax.reload(); -} - -export function init() { - - - const importButton = document.querySelector('.js--send-import'); - importButton?.addEventListener('click', checkImportFile); - - const doImportButton = document.querySelector('.js--do-import'); - doImportButton?.addEventListener('click', doImport); +import {getCurrentlySelectedList} from "./userlists"; +import {getTableAPI} from "./table"; + +async function checkImportFile() { + const fileInput = document.querySelector('#import'); + + if (!fileInput) { + return; + } + + if (!fileInput.files || !fileInput.files[0]) { + return; + } + + const formData = new FormData(); + formData.set('file', fileInput.files[0]); + + const response = await fetch( + `/api/web/keys/import/prepare`, + { + method: 'POST', + body: formData + } + ); + + if (!response.ok) { + throw new Error(response.statusText); + } + + const jsonData = await response.json(); + + const container = document.getElementById('import-info-container'); + if (!container) { + return + } + + container.classList.remove('d-none'); + createTableContents(jsonData); +} + +function createTableContents(data: {index: string, displayName: string, guessedAttribute: string}[]) { + const table = document.querySelector('#import-attribute-table'); + if (!table) { + return; + } + + const tbody = table.querySelector('tbody'); + if (!tbody) { + return; + } + + for (let child of tbody.children) { + child.remove(); + } + + const keyAttributeMeta = document.querySelector('meta[name="key-attributes"]')?.content; + const attributes = JSON.parse(keyAttributeMeta ?? ''); + + const select = document.createElement('select'); + select.classList.add('form-select', 'w-100'); + for (let attributeName in attributes) { + const attributeValue = attributes[attributeName]; + const option = document.createElement('option'); + option.value = attributeValue; + option.textContent = attributeName; + + select.add(option); + } + + data.forEach((datum) => { + const row = tbody.insertRow(); + const indexCell = row.insertCell(); + indexCell.classList.add('index-cell'); + indexCell.innerText = datum.index; + + const displayNameCEll = row.insertCell(); + displayNameCEll.classList.add('name-cell'); + displayNameCEll.innerText = datum.displayName; + + const attributeCell = row.insertCell(); + attributeCell.classList.add('attribute-cell'); + const rowSelect = select.cloneNode(true); + rowSelect.value = datum.guessedAttribute; + attributeCell.appendChild(rowSelect); + }) +} + +async function doImport() { + const fileInput = document.querySelector('#import'); + + if (!fileInput) { + return; + } + + if (!fileInput.files || !fileInput.files[0]) { + return; + } + + const formData = new FormData(); + formData.set('file', fileInput.files[0]); + + const tbody = document.querySelector('#import-attribute-table tbody'); + if (!tbody) { + return; + } + + for (let row of tbody.querySelectorAll('tr')) { + const columnIndex = row.querySelector('.index-cell')?.textContent; + const attribute = row.querySelector('.attribute-cell select')?.value ?? 'none'; + + formData.set(`columns[${columnIndex}]`, attribute); + } + + const listId = getCurrentlySelectedList() ?? 0; + formData.set('listid', listId.toString()); + + const response = await fetch( + `/api/web/keys/import/perform`, + { + method: 'POST', + body: formData + } + ); + + if (!response.ok) { + throw new Error(response.statusText); + } + + const container = document.getElementById('import-info-container'); + if (!container) { + return + } + + container.classList.add('d-none'); + fileInput.value = ''; + + getTableAPI().ajax.reload(); +} + +export function init() { + + + const importButton = document.querySelector('.js--send-import'); + importButton?.addEventListener('click', checkImportFile); + + const doImportButton = document.querySelector('.js--do-import'); + doImportButton?.addEventListener('click', doImport); } \ No newline at end of file diff --git a/src/js/pages/keys/index.ts b/src/js/pages/keys/index.ts index dd3d6f3..b22fecc 100644 --- a/src/js/pages/keys/index.ts +++ b/src/js/pages/keys/index.ts @@ -1,24 +1,26 @@ -import '../../../css/common/index.scss'; -import '../../common/index'; - -import { Tab } from 'bootstrap'; - -import {init as initImport} from "./import"; -import {init as initTable} from "./table"; -import {init as initUserLists} from "./userlists"; - -document.addEventListener('DOMContentLoaded', () => { - const triggerTabList = document.querySelectorAll('#key-tab button') - triggerTabList.forEach(triggerEl => { - const tabTrigger = new Tab(triggerEl) - - triggerEl.addEventListener('click', event => { - event.preventDefault() - tabTrigger.show() - }) - }) - - initImport(); - initTable(); - initUserLists(); +import '../../../css/common/index.scss'; +import '../../common/index'; + +import { Tab } from 'bootstrap'; + +import {init as initImport} from "./import"; +import {init as initTable} from "./table"; +import {init as initUserLists} from "./userlists"; +import {initShare} from "./share"; + +document.addEventListener('DOMContentLoaded', () => { + const triggerTabList = document.querySelectorAll('#key-tab button') + triggerTabList.forEach(triggerEl => { + const tabTrigger = new Tab(triggerEl) + + triggerEl.addEventListener('click', event => { + event.preventDefault() + tabTrigger.show() + }) + }) + + initImport(); + initTable(); + initUserLists(); + initShare(); }) \ No newline at end of file diff --git a/src/js/pages/keys/share.ts b/src/js/pages/keys/share.ts new file mode 100644 index 0000000..fad468a --- /dev/null +++ b/src/js/pages/keys/share.ts @@ -0,0 +1,24 @@ +import DataTable from "datatables.net-bs5"; + +export function initShare() { + const table = document.querySelector('#shared-users-table'); + if (!table) { + return; + } + + new DataTable(table, { + columns: [ + { + name: '', + data: 'icon', + searchable: false, + orderable: false, + }, + { + name: 'Name', + data: 'name', + } + ], + order: [ [1, "desc"] ] + }); +} \ No newline at end of file diff --git a/src/js/pages/keys/table.ts b/src/js/pages/keys/table.ts index e4fd877..3efb658 100644 --- a/src/js/pages/keys/table.ts +++ b/src/js/pages/keys/table.ts @@ -1,108 +1,108 @@ -import DataTable, {Api} from "datatables.net-bs5"; -import 'datatables.net-bs5/css/dataTables.bootstrap5.css'; -import {Key} from "../types/entities"; -import {getIconForKeyState, getKeyStateExplanation} from "../types/keyState"; - -import {Dropdown, Tooltip} from "bootstrap"; -import {getCurrentlySelectedList} from "./userlists"; - -const TABLE_AJAX_URL = '/api/dt/keys/provider'; - -let tableAPI: Api; - -export function getTableAPI(): Api { - return tableAPI; -} - -function getKeyDisplay(parent: HTMLElement, keys: Key[]) { - const header = document.createElement("h1"); - header.innerText = 'Keys'; - header.classList.add('h6'); - - const table = document.createElement("table"); - table.classList.add('table', 'table-striped', 'w-100'); - const body = table.createTBody(); - - for (const {fromWhere, state, key, store, store_link} of keys) { - const row = body.insertRow(); - row.classList.add('cursor-pointer'); - - const stateCell = row.insertCell(); - stateCell.classList.add('w-auto', 'align-content-center'); - stateCell.innerHTML = `` - - const anchor = stateCell.querySelector('a'); - if (!anchor) { - return; - } - new Tooltip(anchor); - - row.insertCell().textContent = key; - row.insertCell().textContent = store === 'external' ? store_link : store; - row.insertCell().textContent = fromWhere; - - row.addEventListener('click', () => { - console.log('Key options'); - }) - } - - parent.appendChild(header); - parent.appendChild(table); -} - -export function init() { - const keyTable = document.querySelector('.key-table'); - if (!keyTable) { - return; - } - - const table = tableAPI = new DataTable(keyTable, { - ajax: { - url: TABLE_AJAX_URL, - data: function (d) { - // @ts-ignore - d.listid = getCurrentlySelectedList(); - } - }, - processing: true, - columns: [ - { - data: 'gamePicture', - searchable: false - }, - { - data: 'name', - }, - { - data: 'keysAmount', - searchable: false, - }, - { - data: 'igdbState', - searchable: false, - } - ], - ordering: false, - order: [ [1, 'asc'] ], - createdRow(row: Node, data: any) { - const tableRow = row; - tableRow.classList.add('cursor-pointer'); - - tableRow.addEventListener('click', () => { - const rowAPI = table.row(row); - if (rowAPI.child.isShown()) { - rowAPI.child.hide(); - return; - } - - const childRow = document.createElement('tr'); - const cell = childRow.insertCell(); - cell.colSpan = row.childNodes.length; - - getKeyDisplay(cell, data.keys); - - rowAPI.child(childRow).show(); - }) - }, - }); +import DataTable, {Api} from "datatables.net-bs5"; +import 'datatables.net-bs5/css/dataTables.bootstrap5.css'; +import {Key} from "../types/entities"; +import {getIconForKeyState, getKeyStateExplanation} from "../types/keyState"; + +import {Dropdown, Tooltip} from "bootstrap"; +import {getCurrentlySelectedList} from "./userlists"; + +const TABLE_AJAX_URL = '/api/dt/keys/provider'; + +let tableAPI: Api; + +export function getTableAPI(): Api { + return tableAPI; +} + +function getKeyDisplay(parent: HTMLElement, keys: Key[]) { + const header = document.createElement("h1"); + header.innerText = 'Keys'; + header.classList.add('h6'); + + const table = document.createElement("table"); + table.classList.add('table', 'table-striped', 'w-100'); + const body = table.createTBody(); + + for (const {fromWhere, state, key, store, store_link} of keys) { + const row = body.insertRow(); + row.classList.add('cursor-pointer'); + + const stateCell = row.insertCell(); + stateCell.classList.add('w-auto', 'align-content-center'); + stateCell.innerHTML = `` + + const anchor = stateCell.querySelector('a'); + if (!anchor) { + return; + } + new Tooltip(anchor); + + row.insertCell().textContent = key; + row.insertCell().textContent = store === 'external' ? store_link : store; + row.insertCell().textContent = fromWhere; + + row.addEventListener('click', () => { + console.log('Key options'); + }) + } + + parent.appendChild(header); + parent.appendChild(table); +} + +export function init() { + const keyTable = document.querySelector('.key-table'); + if (!keyTable) { + return; + } + + const table = tableAPI = new DataTable(keyTable, { + ajax: { + url: TABLE_AJAX_URL, + data: function (d) { + // @ts-ignore + d.listid = getCurrentlySelectedList(); + } + }, + processing: true, + columns: [ + { + data: 'gamePicture', + searchable: false, + orderable: false + }, + { + data: 'name', + }, + { + data: 'keysAmount', + searchable: false, + }, + { + data: 'igdbState', + searchable: false, + } + ], + order: [ [1, 'asc'] ], + createdRow(row: Node, data: any) { + const tableRow = row; + tableRow.classList.add('cursor-pointer'); + + tableRow.addEventListener('click', () => { + const rowAPI = table.row(row); + if (rowAPI.child.isShown()) { + rowAPI.child.hide(); + return; + } + + const childRow = document.createElement('tr'); + const cell = childRow.insertCell(); + cell.colSpan = row.childNodes.length; + + getKeyDisplay(cell, data.keys); + + rowAPI.child(childRow).show(); + }) + }, + }); } \ No newline at end of file diff --git a/src/js/pages/keys/userlists.ts b/src/js/pages/keys/userlists.ts index f38b482..7ac1fc4 100644 --- a/src/js/pages/keys/userlists.ts +++ b/src/js/pages/keys/userlists.ts @@ -1,71 +1,71 @@ -import {Modal} from "bootstrap"; -import {getTableAPI} from "./table"; - -export function getCurrentlySelectedList(): number|null { - const listSelect = document.querySelector('#list-select'); - if (!listSelect) { - return null; - } - - return parseInt(listSelect.value); -} - -export function init() { - const modal = document.querySelector('#create-list-modal'); - if (!modal) { - return; - } - - const modalObj = new Modal(modal); - - modal.addEventListener('show.bs.modal', (e) => { - const input = modal.querySelector('#createListName'); - if (!input) { - return; - } - - input.value = ''; - }) - modal.querySelector('.js--create-list')?.addEventListener('click', async (e) => { - const input = modal.querySelector('#createListName'); - if (!input) { - return; - } - - const newName = input.value; - - const formData = new FormData(); - formData.append('name', newName); - - const response = await fetch( - `/api/web/keys/list/create`, - { - method: 'POST', - body: formData - } - ); - - if (!response.ok) { - throw new Error(response.statusText); - } - - window.location.reload(); - }); - - const listSelect = document.querySelector('#list-select'); - if (listSelect) { - listSelect.addEventListener('change', (e) => { - if (listSelect.value === '_create') { - modalObj.show() - return; - } - - getTableAPI().ajax.reload(); - }) - } - - const newButton = document.querySelector('.js--create-list-button'); - if (newButton) { - newButton.addEventListener('click', () => modalObj.show()) - } +import {Modal} from "bootstrap"; +import {getTableAPI} from "./table"; + +export function getCurrentlySelectedList(): number|null { + const listSelect = document.querySelector('#list-select'); + if (!listSelect) { + return null; + } + + return parseInt(listSelect.value); +} + +export function init() { + const modal = document.querySelector('#create-list-modal'); + if (!modal) { + return; + } + + const modalObj = new Modal(modal); + + modal.addEventListener('show.bs.modal', (e) => { + const input = modal.querySelector('#createListName'); + if (!input) { + return; + } + + input.value = ''; + }) + modal.querySelector('.js--create-list')?.addEventListener('click', async (e) => { + const input = modal.querySelector('#createListName'); + if (!input) { + return; + } + + const newName = input.value; + + const formData = new FormData(); + formData.append('name', newName); + + const response = await fetch( + `/api/web/keys/list/create`, + { + method: 'POST', + body: formData + } + ); + + if (!response.ok) { + throw new Error(response.statusText); + } + + window.location.reload(); + }); + + const listSelect = document.querySelector('#list-select'); + if (listSelect) { + listSelect.addEventListener('change', (e) => { + if (listSelect.value === '_create') { + modalObj.show() + return; + } + + getTableAPI().ajax.reload(); + }) + } + + const newButton = document.querySelector('.js--create-list-button'); + if (newButton) { + newButton.addEventListener('click', () => modalObj.show()) + } } \ No newline at end of file diff --git a/src/js/pages/types/entities.ts b/src/js/pages/types/entities.ts index 626f0c3..11b2665 100644 --- a/src/js/pages/types/entities.ts +++ b/src/js/pages/types/entities.ts @@ -1,25 +1,25 @@ -import {KeyState} from "./keyState"; - -enum Store { - STEAM = 'steam', - GOG = 'gog', - EPICGAMES = 'epicgames', - ORIGIN = 'origin', - UPLAY = 'uplay', - BATTLENET = 'battlenet', - EXTERNAL = 'external' -} - -export type Game = { - name: string -} - -export type Key = { - game?: Game, - key: string, - store: Store, - state: KeyState, - store_link: string|null, - fromWhere: string|null, -} - +import {KeyState} from "./keyState"; + +enum Store { + STEAM = 'steam', + GOG = 'gog', + EPICGAMES = 'epicgames', + ORIGIN = 'origin', + UPLAY = 'uplay', + BATTLENET = 'battlenet', + EXTERNAL = 'external' +} + +export type Game = { + name: string +} + +export type Key = { + game?: Game, + key: string, + store: Store, + state: KeyState, + store_link: string|null, + fromWhere: string|null, +} + diff --git a/src/js/pages/types/keyState.ts b/src/js/pages/types/keyState.ts index 3de76f2..964f8ef 100644 --- a/src/js/pages/types/keyState.ts +++ b/src/js/pages/types/keyState.ts @@ -1,35 +1,35 @@ -export enum KeyState { - AVAILABLE = 1, - UNKNOWN = 0, - RESERVED_FOR_GIFT = -1, - CLAIMED = -10 -} - -export function getIconForKeyState(keyState: KeyState): string { - switch (keyState) { - case KeyState.AVAILABLE: - return "fa-check text-success"; - default: - case KeyState.UNKNOWN: - return 'fa-question text-info'; - case KeyState.RESERVED_FOR_GIFT: - return 'fa-gift text-warning'; - case KeyState.CLAIMED: - return 'fa-x text-danger'; - - } -} - -export function getKeyStateExplanation(keyState: KeyState): string { - switch (keyState) { - case KeyState.AVAILABLE: - return "This key is available"; - default: - case KeyState.UNKNOWN: - return 'The state of this key is unknown'; - case KeyState.RESERVED_FOR_GIFT: - return 'This key is reserved for a gift'; - case KeyState.CLAIMED: - return 'This key was claimed'; - } +export enum KeyState { + AVAILABLE = 1, + UNKNOWN = 0, + RESERVED_FOR_GIFT = -1, + CLAIMED = -10 +} + +export function getIconForKeyState(keyState: KeyState): string { + switch (keyState) { + case KeyState.AVAILABLE: + return "fa-check text-success"; + default: + case KeyState.UNKNOWN: + return 'fa-question text-info'; + case KeyState.RESERVED_FOR_GIFT: + return 'fa-gift text-warning'; + case KeyState.CLAIMED: + return 'fa-x text-danger'; + + } +} + +export function getKeyStateExplanation(keyState: KeyState): string { + switch (keyState) { + case KeyState.AVAILABLE: + return "This key is available"; + default: + case KeyState.UNKNOWN: + return 'The state of this key is unknown'; + case KeyState.RESERVED_FOR_GIFT: + return 'This key is reserved for a gift'; + case KeyState.CLAIMED: + return 'This key was claimed'; + } } \ No newline at end of file diff --git a/src/php/Api/DiscordAPI.php b/src/php/Api/DiscordAPI.php index 67e122a..6adda8e 100644 --- a/src/php/Api/DiscordAPI.php +++ b/src/php/Api/DiscordAPI.php @@ -1,79 +1,79 @@ -env->getDiscordEnvironment(); - - $curl = new Curl(); - $curl->setHeader('Content-Type', 'application/x-www-form-urlencoded'); - $curl->setBasicAuthentication($discordEnv->clientId, $discordEnv->clientSecret); - - $data =[ - 'grant_type' => 'authorization_code', - 'code' => $code, - 'redirect_uri' => $redirectUri - ]; - $curl->post(self::OAUTH_TOKEN_URL, $data); - - if ($curl->error) { - $curl->diagnose(); - - throw new ExtendedException($curl->errorMessage, [ 'response' => $curl->response, 'data' => $data ]); - } - - $accessToken = $curl->response->access_token; - $tokenType = $curl->response->token_type; - - $curl = new Curl(); - $curl->setHeader("authorization", "$tokenType $accessToken"); - $curl->get(self::USER_ME_URL); - - if ($curl->error) { - $curl->diagnose(); - - throw new ExtendedException($curl->errorMessage, [ 'response' => $curl->response, ]); - } - - return [ - 'id' => $curl->response->id, - 'global_name' => $curl->response->global_name, - 'avatar' => $curl->response->avatar, - 'discriminator' => (int) $curl->response->discriminator - ]; - } - - public function getAvatarURL(string $userId, string|int $avatarHash) { - if (is_int($avatarHash)) { - return "https://cdn.discordapp.com/embed/avatars/{$avatarHash}.png"; - } - - $extension = 'png'; - if (str_starts_with($avatarHash, 'a_')) { - $extension = 'gif'; - } - - return "https://cdn.discordapp.com/avatars/{$userId}/{$avatarHash}.{$extension}"; - } +env->getDiscordEnvironment(); + + $curl = new Curl(); + $curl->setHeader('Content-Type', 'application/x-www-form-urlencoded'); + $curl->setBasicAuthentication($discordEnv->clientId, $discordEnv->clientSecret); + + $data =[ + 'grant_type' => 'authorization_code', + 'code' => $code, + 'redirect_uri' => $redirectUri + ]; + $curl->post(self::OAUTH_TOKEN_URL, $data); + + if ($curl->error) { + $curl->diagnose(); + + throw new ExtendedException($curl->errorMessage, [ 'response' => $curl->response, 'data' => $data ]); + } + + $accessToken = $curl->response->access_token; + $tokenType = $curl->response->token_type; + + $curl = new Curl(); + $curl->setHeader("authorization", "$tokenType $accessToken"); + $curl->get(self::USER_ME_URL); + + if ($curl->error) { + $curl->diagnose(); + + throw new ExtendedException($curl->errorMessage, [ 'response' => $curl->response, ]); + } + + return [ + 'id' => $curl->response->id, + 'global_name' => $curl->response->global_name, + 'avatar' => $curl->response->avatar, + 'discriminator' => (int) $curl->response->discriminator + ]; + } + + public function getAvatarURL(string $userId, string|int $avatarHash) { + if (is_int($avatarHash)) { + return "https://cdn.discordapp.com/embed/avatars/{$avatarHash}.png"; + } + + $extension = 'png'; + if (str_starts_with($avatarHash, 'a_')) { + $extension = 'gif'; + } + + return "https://cdn.discordapp.com/avatars/{$userId}/{$avatarHash}.{$extension}"; + } } \ No newline at end of file diff --git a/src/php/ContainerHandler.php b/src/php/ContainerHandler.php index 34c8427..a959e5a 100644 --- a/src/php/ContainerHandler.php +++ b/src/php/ContainerHandler.php @@ -1,39 +1,39 @@ -|string $id - * - * @return RequestedType|mixed - */ - public static function get(string $id) { - return self::getInstance()->get($id); - } - - private static function createInstance() - { - self::$instance = new Container(); - $reflectionContainer = new ReflectionContainer(true); - self::$instance->delegate($reflectionContainer); - } +|string $id + * + * @return RequestedType|mixed + */ + public static function get(string $id) { + return self::getInstance()->get($id); + } + + private static function createInstance() + { + self::$instance = new Container(); + $reflectionContainer = new ReflectionContainer(true); + self::$instance->delegate($reflectionContainer); + } } \ No newline at end of file diff --git a/src/php/DoctrineManager.php b/src/php/DoctrineManager.php index 9545e45..42f59f0 100644 --- a/src/php/DoctrineManager.php +++ b/src/php/DoctrineManager.php @@ -1,35 +1,35 @@ -get(EnvironmentHandler::class); - - $config = ORMSetup::createAttributeMetadataConfiguration( - paths: [ Paths::PHP_SOURCE_PATH . '/Entities' ], - isDevMode: !$environmentHandler->isProduction() - ); - - $dbEnvironment = $environmentHandler->getDatabaseEnvironment(); - $connection = DriverManager::getConnection($dbEnvironment->getDoctrineConfig()); - - $entityManager = new EntityManager($connection, $config); - - $container->addShared(EntityManager::class, $entityManager); - $container->addShared(EntityManagerInterface::class, $entityManager); - $container->addShared(Connection::class, $connection); - } - +get(EnvironmentHandler::class); + + $config = ORMSetup::createAttributeMetadataConfiguration( + paths: [ Paths::PHP_SOURCE_PATH . '/Entities' ], + isDevMode: !$environmentHandler->isProduction() + ); + + $dbEnvironment = $environmentHandler->getDatabaseEnvironment(); + $connection = DriverManager::getConnection($dbEnvironment->getDoctrineConfig()); + + $entityManager = new EntityManager($connection, $config); + + $container->addShared(EntityManager::class, $entityManager); + $container->addShared(EntityManagerInterface::class, $entityManager); + $container->addShared(Connection::class, $connection); + } + } \ No newline at end of file diff --git a/src/php/Entities/Account/User.php b/src/php/Entities/Account/User.php index 26131d9..daf3684 100644 --- a/src/php/Entities/Account/User.php +++ b/src/php/Entities/Account/User.php @@ -1,85 +1,85 @@ -loginMethod = $loginMethod; - $this->foreignLoginId = $foreignLoginId; - $this->name = $name; - $this->profilePictureUrl = $profilePictureUrl; - $this->permission = $permission; - } - - public function getId(): ?int - { - return $this->id; - } - - public function getLoginMethod(): LoginMethod - { - return $this->loginMethod; - } - - public function getForeignLoginId(): ?string - { - return $this->foreignLoginId; - } - - public function getName(): string - { - return $this->name; - } - - public function getProfilePictureUrl(): string - { - return $this->profilePictureUrl; - } - - public function getPermission(): UserPermission - { - return $this->permission; - } - - public function setName(string $name): void - { - $this->name = $name; - } - - public function setProfilePictureUrl(string $profilePictureUrl): void - { - $this->profilePictureUrl = $profilePictureUrl; - } - - public function setPermission(UserPermission $permission): void - { - $this->permission = $permission; - } +loginMethod = $loginMethod; + $this->foreignLoginId = $foreignLoginId; + $this->name = $name; + $this->profilePictureUrl = $profilePictureUrl; + $this->permission = $permission; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getLoginMethod(): LoginMethod + { + return $this->loginMethod; + } + + public function getForeignLoginId(): ?string + { + return $this->foreignLoginId; + } + + public function getName(): string + { + return $this->name; + } + + public function getProfilePictureUrl(): string + { + return $this->profilePictureUrl; + } + + public function getPermission(): UserPermission + { + return $this->permission; + } + + public function setName(string $name): void + { + $this->name = $name; + } + + public function setProfilePictureUrl(string $profilePictureUrl): void + { + $this->profilePictureUrl = $profilePictureUrl; + } + + public function setPermission(UserPermission $permission): void + { + $this->permission = $permission; + } } \ No newline at end of file diff --git a/src/php/Entities/Games/Game.php b/src/php/Entities/Games/Game.php index 0f7d498..90ed9be 100644 --- a/src/php/Entities/Games/Game.php +++ b/src/php/Entities/Games/Game.php @@ -1,36 +1,36 @@ - true])] - #[ORM\GeneratedValue] - private int|null $id; - #[ORM\Column] - private string $name; - - public function getName(): string - { - return $this->name; - } - - public function getId(): ?int - { - return $this->id; - } - - /** - * @param string $name - */ - public function __construct(string $name) - { - $this->name = $name; - } + true])] + #[ORM\GeneratedValue] + private int|null $id; + #[ORM\Column] + private string $name; + + public function getName(): string + { + return $this->name; + } + + public function getId(): ?int + { + return $this->id; + } + + /** + * @param string $name + */ + public function __construct(string $name) + { + $this->name = $name; + } } \ No newline at end of file diff --git a/src/php/Entities/Games/Key.php b/src/php/Entities/Games/Key.php index 67e865f..cb1cc17 100644 --- a/src/php/Entities/Games/Key.php +++ b/src/php/Entities/Games/Key.php @@ -1,96 +1,96 @@ - true])] - #[ORM\GeneratedValue] - private int|null $id; - #[ORM\ManyToOne] - private Game $game; - #[ORM\ManyToOne] - private GamesList $list; - #[ORM\Column] - private string $key; - #[ORM\Column(type: 'string', enumType: Store::class)] - private Store $store; - #[ORM\Column(nullable: true)] - private string|null $storeLink; - #[ORM\Column] - private string|null $fromWhere; - #[ORM\Column(type: 'integer', enumType: KeyState::class)] - private KeyState $state; - - public function __construct(Game $game, GamesList $list, string $key, Store $store, ?string $storeLink, ?string $fromWhere) - { - $this->game = $game; - $this->list = $list; - $this->key = $key; - $this->store = $store; - $this->storeLink = $storeLink; - $this->fromWhere = $fromWhere; - $this->state = KeyState::AVAILABLE; - } - - public function getId(): ?int - { - return $this->id; - } - - public function getGame(): Game - { - return $this->game; - } - - public function getContributedUser(): User - { - return $this->contributedUser; - } - - public function getKey(): string - { - return $this->key; - } - - public function getStore(): Store - { - return $this->store; - } - - public function getStoreLink(): ?string - { - return $this->storeLink; - } - - public function getFromWhere(): ?string - { - return $this->fromWhere; - } - - public function getState(): KeyState - { - return $this->state; - } - - public function jsonSerialize(): mixed - { - return [ - 'id' => $this->id, - 'key' => $this->key, - 'store' => $this->store->value, - 'store_link' => $this->storeLink, - 'from_where' => $this->fromWhere, - 'state' => $this->state->value, - ]; - } + true])] + #[ORM\GeneratedValue] + private int|null $id; + #[ORM\ManyToOne] + private Game $game; + #[ORM\ManyToOne] + private GamesList $list; + #[ORM\Column] + private string $key; + #[ORM\Column(type: 'string', enumType: Store::class)] + private Store $store; + #[ORM\Column(nullable: true)] + private string|null $storeLink; + #[ORM\Column] + private string|null $fromWhere; + #[ORM\Column(type: 'integer', enumType: KeyState::class)] + private KeyState $state; + + public function __construct(Game $game, GamesList $list, string $key, Store $store, ?string $storeLink, ?string $fromWhere) + { + $this->game = $game; + $this->list = $list; + $this->key = $key; + $this->store = $store; + $this->storeLink = $storeLink; + $this->fromWhere = $fromWhere; + $this->state = KeyState::AVAILABLE; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getGame(): Game + { + return $this->game; + } + + public function getContributedUser(): User + { + return $this->contributedUser; + } + + public function getKey(): string + { + return $this->key; + } + + public function getStore(): Store + { + return $this->store; + } + + public function getStoreLink(): ?string + { + return $this->storeLink; + } + + public function getFromWhere(): ?string + { + return $this->fromWhere; + } + + public function getState(): KeyState + { + return $this->state; + } + + public function jsonSerialize(): mixed + { + return [ + 'id' => $this->id, + 'key' => $this->key, + 'store' => $this->store->value, + 'store_link' => $this->storeLink, + 'from_where' => $this->fromWhere, + 'state' => $this->state->value, + ]; + } } \ No newline at end of file diff --git a/src/php/Entities/Games/KeyAttribute.php b/src/php/Entities/Games/KeyAttribute.php index 345458f..d040dd0 100644 --- a/src/php/Entities/Games/KeyAttribute.php +++ b/src/php/Entities/Games/KeyAttribute.php @@ -1,22 +1,22 @@ -name] = $case->value; - } - - return $result; - } +name] = $case->value; + } + + return $result; + } } \ No newline at end of file diff --git a/src/php/Entities/Games/KeyState.php b/src/php/Entities/Games/KeyState.php index cbeb934..02e22df 100644 --- a/src/php/Entities/Games/KeyState.php +++ b/src/php/Entities/Games/KeyState.php @@ -1,12 +1,12 @@ - true])] - #[ORM\GeneratedValue] - private int|null $id; - - #[ORM\ManyToOne] - private User $owner; - - #[ORM\Column(nullable: true)] - private string|null $name; - - #[ORM\JoinTable(name: 'games_list_claimer')] - #[ORM\JoinColumn(name: 'id', referencedColumnName: 'id')] - #[ORM\ManyToMany(targetEntity: User::class)] - private Collection $claimer; - - /** - * @param User $owner - * @param string|null $name - */ - public function __construct(User $owner, ?string $name) - { - $this->owner = $owner; - $this->name = $name; - } - - - public function getId(): ?int - { - return $this->id; - } - - public function getOwner(): User - { - return $this->owner; - } - - public function getName(): ?string - { - return $this->name; - } - - public function getClaimer(): array - { - return $this->claimer; - } - - + true])] + #[ORM\GeneratedValue] + private int|null $id; + + #[ORM\ManyToOne] + private User $owner; + + #[ORM\Column(nullable: true)] + private string|null $name; + + #[ORM\JoinTable(name: 'games_list_claimer')] + #[ORM\JoinColumn(name: 'id', referencedColumnName: 'id')] + #[ORM\ManyToMany(targetEntity: User::class)] + private Collection $claimer; + + /** + * @param User $owner + * @param string|null $name + */ + public function __construct(User $owner, ?string $name) + { + $this->owner = $owner; + $this->name = $name; + } + + + public function getId(): ?int + { + return $this->id; + } + + public function getOwner(): User + { + return $this->owner; + } + + public function getName(): ?string + { + return $this->name; + } + + public function getClaimer(): array + { + return $this->claimer; + } + + } \ No newline at end of file diff --git a/src/php/Entities/SystemAttribute.php b/src/php/Entities/SystemAttribute.php index 5eeab0f..f4f2bb7 100644 --- a/src/php/Entities/SystemAttribute.php +++ b/src/php/Entities/SystemAttribute.php @@ -1,34 +1,34 @@ -name = $name; - $this->value = $value; - } - - public function getName(): string - { - return $this->name; - } - - public function getValue(): string - { - return $this->value; - } +name = $name; + $this->value = $value; + } + + public function getName(): string + { + return $this->name; + } + + public function getValue(): string + { + return $this->value; + } } \ No newline at end of file diff --git a/src/php/Environment/DatabaseEnvironment.php b/src/php/Environment/DatabaseEnvironment.php index 7e5ab3e..6f2d7de 100644 --- a/src/php/Environment/DatabaseEnvironment.php +++ b/src/php/Environment/DatabaseEnvironment.php @@ -1,21 +1,21 @@ - $this->driver, - 'path' => $this->path - ]; - } + $this->driver, + 'path' => $this->path + ]; + } } \ No newline at end of file diff --git a/src/php/Environment/DiscordEnvironment.php b/src/php/Environment/DiscordEnvironment.php index eb3e8a3..52f910a 100644 --- a/src/php/Environment/DiscordEnvironment.php +++ b/src/php/Environment/DiscordEnvironment.php @@ -1,13 +1,13 @@ -safeLoad(); - } - - public function getDiscordEnvironment(): DiscordEnvironment { - return new DiscordEnvironment( - $_SERVER['DISCORD_CLIENT_ID'], - $_SERVER['DISCORD_CLIENT_SECRET'], - $_SERVER['DISCORD_CLIENT_LOGIN_URI'], - ); - } - - public function getDatabaseEnvironment(): DatabaseEnvironment - { - return new DatabaseEnvironment( - $_SERVER['DB_DRIVER'], - $_SERVER['DB_PATH'] - ); - } - - public function isProduction(): bool { - return $_SERVER['PRODUCTION'] === 'true'; - } +safeLoad(); + } + + public function getDiscordEnvironment(): DiscordEnvironment { + return new DiscordEnvironment( + $_SERVER['DISCORD_CLIENT_ID'], + $_SERVER['DISCORD_CLIENT_SECRET'], + $_SERVER['DISCORD_CLIENT_LOGIN_URI'], + ); + } + + public function getDatabaseEnvironment(): DatabaseEnvironment + { + return new DatabaseEnvironment( + $_SERVER['DB_DRIVER'], + $_SERVER['DB_PATH'] + ); + } + + public function isProduction(): bool { + return $_SERVER['PRODUCTION'] === 'true'; + } } \ No newline at end of file diff --git a/src/php/Errors/ExtendedException.php b/src/php/Errors/ExtendedException.php index f06ff35..745d54a 100644 --- a/src/php/Errors/ExtendedException.php +++ b/src/php/Errors/ExtendedException.php @@ -1,26 +1,26 @@ -addDataTable("Additional Info", $additionals); - } +addDataTable("Additional Info", $additionals); + } } \ No newline at end of file diff --git a/src/php/Errors/WhoopsHandler.php b/src/php/Errors/WhoopsHandler.php index a5e17a8..a611db2 100644 --- a/src/php/Errors/WhoopsHandler.php +++ b/src/php/Errors/WhoopsHandler.php @@ -1,8 +1,8 @@ - Store::EPICGAMES, - 'ea' => Store::ORIGIN, - 'eaplay' => Store::ORIGIN, - 'ubisoft' => Store::UPLAY, - 'activision' => Store::BATTLENET - ]; - - public function __construct( - private readonly EntityManagerInterface $entityManager, - ) { } - - /** - * @param string $path - * @return ImportColumnInterpretation[] - */ - public function interpret(string $path): array { - $spreadsheet = IOFactory::load($path); - - $worksheet = $spreadsheet->getSheet(0); - $result = []; - foreach ($worksheet->getColumnIterator() as $column) { - $columnIndex = $column->getColumnIndex(); - - $value = $worksheet->getCell(sprintf('%s%d', $columnIndex, self::HEADER_ROW_INDEX))->getValueString(); - - if (empty(trim($value))) { - continue; - } - - $guessedAttribute = $this->guessAttribute($value); - $result[] = new ImportColumnInterpretation( - $columnIndex, - $value, - $guessedAttribute - ); - } - return $result; - } - - private function guessAttribute(string $value): KeyAttribute|null { - $value = trim($value); - $value = strtolower($value); - $value = str_replace(' ', '_', $value); - - $attribute = match($value) { - 'key' => KeyAttribute::KEY, - 'name', 'game_name', 'game' => KeyAttribute::GAME_NAME, - 'from' => KeyAttribute::FROM, - 'store', 'for' => KeyAttribute::STORE, - default => null - }; - - return $attribute; - } - - /** - * @param string[] $columnDefinitions - */ - public function import(string $path, array $columnDefinitions, GamesList $list): array { - $spreadsheet = IOFactory::load($path); - - $worksheet = $spreadsheet->getSheet(0); - - $totalRows = 0; - $addedAmount = 0; - - foreach ($worksheet->getRowIterator(self::HEADER_ROW_INDEX + 1) as $row) { - $totalRows++; - $values = [ - 'name' => null, - 'key' => null, - 'from' => null, - 'store' => null, - 'store_link' => null - ]; - - foreach ($columnDefinitions as $columnIndex => $attribute) { - $value = $worksheet->getCell(sprintf('%s%d', $columnIndex, $row->getRowIndex()))->getValueString(); - - switch(KeyAttribute::from($attribute)) { - case KeyAttribute::NONE: - break; - case KeyAttribute::GAME_NAME: - $values['name'] = $value; - break; - case KeyAttribute::KEY: - $values['key'] = $value; - break; - case KeyAttribute::FROM: - $values['from'] = $value; - break; - case KeyAttribute::STORE: - $store = $this->interpretStore($value); - $values['store'] = $store; - if ($store === Store::EXTERNAL) { - $values['store_link'] = $value; - } - break; - } - } - - if ($values['key'] === null || $values['name'] === null || $values['store'] === null) { - continue; - } - - $game = $this->entityManager->getRepository(Game::class)->findOneBy([ 'name' => $values['name'] ]); - if ($game === null) { - $game = new Game($values['name']); - } - - $key = new Key( - $game, - $list, - $values['key'], - $values['store'], - $values['store_link'], - $values['from'], - ); - - $this->entityManager->persist($game); - $this->entityManager->persist($key); - $addedAmount++; - } - - $this->entityManager->flush(); - - return [$totalRows, $addedAmount]; - } - - private function interpretStore(string $storeString): Store { - $storeString = trim($storeString); - $storeString = strtolower($storeString); - $storeString = str_replace(' ', '', $storeString); - - $triedConversion = Store::tryFrom($storeString); - if ($triedConversion !== null) { - return $triedConversion; - } - - if (array_key_exists($storeString, self::STORE_ADDITIONAL_CASES)) { - return self::STORE_ADDITIONAL_CASES[$storeString]; - } - - return Store::EXTERNAL; - } + Store::EPICGAMES, + 'ea' => Store::ORIGIN, + 'eaplay' => Store::ORIGIN, + 'ubisoft' => Store::UPLAY, + 'activision' => Store::BATTLENET + ]; + + public function __construct( + private readonly EntityManagerInterface $entityManager, + ) { } + + /** + * @param string $path + * @return ImportColumnInterpretation[] + */ + public function interpret(string $path): array { + $spreadsheet = IOFactory::load($path); + + $worksheet = $spreadsheet->getSheet(0); + $result = []; + foreach ($worksheet->getColumnIterator() as $column) { + $columnIndex = $column->getColumnIndex(); + + $value = $worksheet->getCell(sprintf('%s%d', $columnIndex, self::HEADER_ROW_INDEX))->getValueString(); + + if (empty(trim($value))) { + continue; + } + + $guessedAttribute = $this->guessAttribute($value); + $result[] = new ImportColumnInterpretation( + $columnIndex, + $value, + $guessedAttribute + ); + } + return $result; + } + + private function guessAttribute(string $value): KeyAttribute|null { + $value = trim($value); + $value = strtolower($value); + $value = str_replace(' ', '_', $value); + + $attribute = match($value) { + 'key' => KeyAttribute::KEY, + 'name', 'game_name', 'game' => KeyAttribute::GAME_NAME, + 'from' => KeyAttribute::FROM, + 'store', 'for' => KeyAttribute::STORE, + default => null + }; + + return $attribute; + } + + /** + * @param string[] $columnDefinitions + */ + public function import(string $path, array $columnDefinitions, GamesList $list): array { + $spreadsheet = IOFactory::load($path); + + $worksheet = $spreadsheet->getSheet(0); + + $totalRows = 0; + $addedAmount = 0; + + foreach ($worksheet->getRowIterator(self::HEADER_ROW_INDEX + 1) as $row) { + $totalRows++; + $values = [ + 'name' => null, + 'key' => null, + 'from' => null, + 'store' => null, + 'store_link' => null + ]; + + foreach ($columnDefinitions as $columnIndex => $attribute) { + $value = $worksheet->getCell(sprintf('%s%d', $columnIndex, $row->getRowIndex()))->getValueString(); + + switch(KeyAttribute::from($attribute)) { + case KeyAttribute::NONE: + break; + case KeyAttribute::GAME_NAME: + $values['name'] = $value; + break; + case KeyAttribute::KEY: + $values['key'] = $value; + break; + case KeyAttribute::FROM: + $values['from'] = $value; + break; + case KeyAttribute::STORE: + $store = $this->interpretStore($value); + $values['store'] = $store; + if ($store === Store::EXTERNAL) { + $values['store_link'] = $value; + } + break; + } + } + + if ($values['key'] === null || $values['name'] === null || $values['store'] === null) { + continue; + } + + $game = $this->entityManager->getRepository(Game::class)->findOneBy([ 'name' => $values['name'] ]); + if ($game === null) { + $game = new Game($values['name']); + } + + $key = new Key( + $game, + $list, + $values['key'], + $values['store'], + $values['store_link'], + $values['from'], + ); + + $this->entityManager->persist($game); + $this->entityManager->persist($key); + $addedAmount++; + } + + $this->entityManager->flush(); + + return [$totalRows, $addedAmount]; + } + + private function interpretStore(string $storeString): Store { + $storeString = trim($storeString); + $storeString = strtolower($storeString); + $storeString = str_replace(' ', '', $storeString); + + $triedConversion = Store::tryFrom($storeString); + if ($triedConversion !== null) { + return $triedConversion; + } + + if (array_key_exists($storeString, self::STORE_ADDITIONAL_CASES)) { + return self::STORE_ADDITIONAL_CASES[$storeString]; + } + + return Store::EXTERNAL; + } } \ No newline at end of file diff --git a/src/php/Importer/ImportColumnInterpretation.php b/src/php/Importer/ImportColumnInterpretation.php index 1e5b97a..767d8c6 100644 --- a/src/php/Importer/ImportColumnInterpretation.php +++ b/src/php/Importer/ImportColumnInterpretation.php @@ -1,26 +1,26 @@ - $this->index, - 'displayName' => $this->displayName, - 'guessedAttribute' => $this->guessedAttribute ?? null - ]; - } + $this->index, + 'displayName' => $this->displayName, + 'guessedAttribute' => $this->guessedAttribute ?? null + ]; + } } \ No newline at end of file diff --git a/src/php/Login/DiscordLoginProvider.php b/src/php/Login/DiscordLoginProvider.php index 653b84e..6e38991 100644 --- a/src/php/Login/DiscordLoginProvider.php +++ b/src/php/Login/DiscordLoginProvider.php @@ -1,47 +1,47 @@ -getUserFromCode($request->getQueryParams()['code'], (string)$request->getUri()->withQuery('')); - - $repo = $this->entityManager->getRepository(User::class); - $users = $repo->findBy(['loginMethod' => LoginMethod::DISCORD, 'foreignLoginId' => $result['id']]); - - $profilePictureUrl = $discordApiHandler->getAvatarURL($result['id'], $result['avatar'] ?? $result['discriminator'] % 5); - if (!empty($users)) { - $user = $users[0]; - $user->setName($result['global_name']); - $user->setProfilePictureUrl($profilePictureUrl); - - return $user; - } - - $newUser = new User( - LoginMethod::DISCORD, - $result['id'], - $result['global_name'], - $profilePictureUrl, - UserPermission::VIEWER - ); - - return $newUser; - } +getUserFromCode($request->getQueryParams()['code'], (string)$request->getUri()->withQuery('')); + + $repo = $this->entityManager->getRepository(User::class); + $users = $repo->findBy(['loginMethod' => LoginMethod::DISCORD, 'foreignLoginId' => $result['id']]); + + $profilePictureUrl = $discordApiHandler->getAvatarURL($result['id'], $result['avatar'] ?? $result['discriminator'] % 5); + if (!empty($users)) { + $user = $users[0]; + $user->setName($result['global_name']); + $user->setProfilePictureUrl($profilePictureUrl); + + return $user; + } + + $newUser = new User( + LoginMethod::DISCORD, + $result['id'], + $result['global_name'], + $profilePictureUrl, + UserPermission::VIEWER + ); + + return $newUser; + } } \ No newline at end of file diff --git a/src/php/Login/LoginHandler.php b/src/php/Login/LoginHandler.php index 407d00f..ded94f9 100644 --- a/src/php/Login/LoginHandler.php +++ b/src/php/Login/LoginHandler.php @@ -1,76 +1,76 @@ -ensureSession(); - - return isset($_SESSION['accountid']); - } - - /** - * @throws Exception - */ - public function getLoginProvider(string $method): LoginProvider { - $providers = self::getProviders(); - if (!array_key_exists($method, $providers)) { - throw new Exception("Couldn't find method for login '{$method}'"); - } - - return ContainerHandler::get($providers[$method]); - } - - public function setCurrentUser(User $user) { - $this->ensureSession(); - - $_SESSION['accountid'] = $user->getId(); - } - - public function getCurrentUser(): User { - $this->ensureSession(); - - $userid = $_SESSION['accountid']; - return $this->entityManager->getRepository(User::class)->find($userid); - } - - public function deleteSession() { - $this->ensureSession(); - session_destroy(); - } - - private function ensureSession() - { - if (session_status() !== PHP_SESSION_ACTIVE) { - session_start(); - } - } - - /** - * @return class-string[] - */ - private static function getProviders(): array - { - return self::$providers ??= [ - 'discord' => DiscordLoginProvider::class - ]; - } +ensureSession(); + + return isset($_SESSION['accountid']); + } + + /** + * @throws Exception + */ + public function getLoginProvider(string $method): LoginProvider { + $providers = self::getProviders(); + if (!array_key_exists($method, $providers)) { + throw new Exception("Couldn't find method for login '{$method}'"); + } + + return ContainerHandler::get($providers[$method]); + } + + public function setCurrentUser(User $user) { + $this->ensureSession(); + + $_SESSION['accountid'] = $user->getId(); + } + + public function getCurrentUser(): User { + $this->ensureSession(); + + $userid = $_SESSION['accountid']; + return $this->entityManager->getRepository(User::class)->find($userid); + } + + public function deleteSession() { + $this->ensureSession(); + session_destroy(); + } + + private function ensureSession() + { + if (session_status() !== PHP_SESSION_ACTIVE) { + session_start(); + } + } + + /** + * @return class-string[] + */ + private static function getProviders(): array + { + return self::$providers ??= [ + 'discord' => DiscordLoginProvider::class + ]; + } } \ No newline at end of file diff --git a/src/php/Login/LoginMethod.php b/src/php/Login/LoginMethod.php index ce4eaa0..b9e26d2 100644 --- a/src/php/Login/LoginMethod.php +++ b/src/php/Login/LoginMethod.php @@ -1,23 +1,23 @@ - 'fa-discord', - }; - } - - public function getHumanReadableName(): string - { - return match ($this) { - self::DISCORD => 'Discord', - }; - } -} + 'fa-discord', + }; + } + + public function getHumanReadableName(): string + { + return match ($this) { + self::DISCORD => 'Discord', + }; + } +} diff --git a/src/php/Login/LoginProvider.php b/src/php/Login/LoginProvider.php index 51ecd43..0b47dfa 100644 --- a/src/php/Login/LoginProvider.php +++ b/src/php/Login/LoginProvider.php @@ -1,12 +1,12 @@ -value >= $userPermission->value; - } - - public function getHumanReadableName() { - return match ($this) { - self::VIEWER => "Claimer", - self::PROVIDER => "Provider", - self::ADMIN => "Admin", - default => "None", - }; - } -} +value >= $userPermission->value; + } + + public function getHumanReadableName() { + return match ($this) { + self::VIEWER => "Claimer", + self::PROVIDER => "Provider", + self::ADMIN => "Admin", + default => "None", + }; + } +} diff --git a/src/php/Paths.php b/src/php/Paths.php index 6a1d42c..8675071 100644 --- a/src/php/Paths.php +++ b/src/php/Paths.php @@ -1,13 +1,13 @@ -loginHandler->isLoggedIn()) { - throw new UnauthorizedException(); - } - - $user = $this->loginHandler->getCurrentUser(); - if ($user->getPermission()->value < UserPermission::ADMIN->value) { - throw new ForbiddenException(); - } - - - return new TemplateResponse('admin/accounts'); - } - - public static function applyRoutes(\League\Route\Router $router) { - $router->get('/accounts', self::class); - } - +loginHandler->isLoggedIn()) { + throw new UnauthorizedException(); + } + + $user = $this->loginHandler->getCurrentUser(); + if ($user->getPermission()->value < UserPermission::ADMIN->value) { + throw new ForbiddenException(); + } + + + return new TemplateResponse('admin/accounts'); + } + + public static function applyRoutes(\League\Route\Router $router) { + $router->get('/accounts', self::class); + } + } \ No newline at end of file diff --git a/src/php/Routing/Api/APIRoutes.php b/src/php/Routing/Api/APIRoutes.php index 349545e..a0a92df 100644 --- a/src/php/Routing/Api/APIRoutes.php +++ b/src/php/Routing/Api/APIRoutes.php @@ -1,16 +1,16 @@ -group('/api/dt', DataTablesAPIRoutes::setupRoutes(...)); - $router->group('/api/web', WebAPIRoutes::applyRoutes(...)); - } +group('/api/dt', DataTablesAPIRoutes::setupRoutes(...)); + $router->group('/api/web', WebAPIRoutes::applyRoutes(...)); + } } \ No newline at end of file diff --git a/src/php/Routing/Api/DataTables/AccountsEndpoint.php b/src/php/Routing/Api/DataTables/AccountsEndpoint.php index 8a2ed61..991ad8b 100644 --- a/src/php/Routing/Api/DataTables/AccountsEndpoint.php +++ b/src/php/Routing/Api/DataTables/AccountsEndpoint.php @@ -1,80 +1,80 @@ -loginHandler->isLoggedIn()) { - throw new UnauthorizedException(); - } - - $user = $this->loginHandler->getCurrentUser(); - if (!$user->getPermission()->hasLevel(UserPermission::ADMIN)) { - throw new ForbiddenException(); - } - - - $params = $request->getQueryParams(); - $draw = $params['draw']; - $start = $params['start']; - $length = $params['length']; - - $searchValue = $params['search']['value']; - - $repo = $this->entityManager->getRepository(User::class); - $total = $repo->count(); - - $criteria = Criteria::create(); - $criteria->where(Criteria::expr()->contains('name', $searchValue)); - $criteria->setFirstResult((int)$start); - $criteria->setMaxResults((int)$length); - - $values = $repo->matching($criteria); - $filteredCount = $values->count(); - - return new JsonResponse([ - 'draw' => $draw, - 'recordsTotal' => $total, - 'recordsFiltered' => $filteredCount, - 'data' => - $values->map(function (User $user) { - return [ - 'userid' => $user->getId(), - 'name' => $user->getName(), - 'profilePictureUrl' => $user->getProfilePictureUrl(), - 'permission' => $user->getPermission()->getHumanReadableName(), - 'permissionIndex' => $user->getPermission()->value, - 'loginMethod' => $user->getLoginMethod()->getHumanReadableName(), - ]; - })->toArray() - ]); - } - - public static function applyRoutes(RouteGroup $router) { - $router->get('/accounts', AccountsEndpoint::class); - } +loginHandler->isLoggedIn()) { + throw new UnauthorizedException(); + } + + $user = $this->loginHandler->getCurrentUser(); + if (!$user->getPermission()->hasLevel(UserPermission::ADMIN)) { + throw new ForbiddenException(); + } + + + $params = $request->getQueryParams(); + $draw = $params['draw']; + $start = $params['start']; + $length = $params['length']; + + $searchValue = $params['search']['value']; + + $repo = $this->entityManager->getRepository(User::class); + $total = $repo->count(); + + $criteria = Criteria::create(); + $criteria->where(Criteria::expr()->contains('name', $searchValue)); + $criteria->setFirstResult((int)$start); + $criteria->setMaxResults((int)$length); + + $values = $repo->matching($criteria); + $filteredCount = $values->count(); + + return new JsonResponse([ + 'draw' => $draw, + 'recordsTotal' => $total, + 'recordsFiltered' => $filteredCount, + 'data' => + $values->map(function (User $user) { + return [ + 'userid' => $user->getId(), + 'name' => $user->getName(), + 'profilePictureUrl' => $user->getProfilePictureUrl(), + 'permission' => $user->getPermission()->getHumanReadableName(), + 'permissionIndex' => $user->getPermission()->value, + 'loginMethod' => $user->getLoginMethod()->getHumanReadableName(), + ]; + })->toArray() + ]); + } + + public static function applyRoutes(RouteGroup $router) { + $router->get('/accounts', AccountsEndpoint::class); + } } \ No newline at end of file diff --git a/src/php/Routing/Api/DataTables/DataTablesAPIRoutes.php b/src/php/Routing/Api/DataTables/DataTablesAPIRoutes.php index 58bc16a..3d6a860 100644 --- a/src/php/Routing/Api/DataTables/DataTablesAPIRoutes.php +++ b/src/php/Routing/Api/DataTables/DataTablesAPIRoutes.php @@ -1,16 +1,16 @@ -get('/keys/provider', ProviderKeysEndpoint::class); - } +get('/keys/provider', ProviderKeysEndpoint::class); + } } \ No newline at end of file diff --git a/src/php/Routing/Api/DataTables/ProviderKeysEndpoint.php b/src/php/Routing/Api/DataTables/ProviderKeysEndpoint.php index bd2aefa..5f34b53 100644 --- a/src/php/Routing/Api/DataTables/ProviderKeysEndpoint.php +++ b/src/php/Routing/Api/DataTables/ProviderKeysEndpoint.php @@ -1,78 +1,78 @@ -loginHandler->isLoggedIn()) { - throw new UnauthorizedException(); - } - - $user = $this->loginHandler->getCurrentUser(); - if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) { - throw new ForbiddenException(); - } - - $body = $request->getQueryParams(); - if (!array_key_exists('listid', $body)) { - throw new BadRequestException(); - } - - $list = $this->entityManager->getRepository(GamesList::class)->findOneBy([ 'owner' => $user, 'id' => $body['listid'] ]); - if (!$list instanceof GamesList) { - throw new BadRequestException(); - } - - $keys = $this->entityManager->getRepository(Key::class)->findBy(['list' => $list]); - $gameToKeyArray = []; - foreach ($keys as $key) { - $game = $key->getGame(); - $id = $game->getId(); - - if (!array_key_exists($id, $gameToKeyArray)) { - $gameToKeyArray[$id] = [ $game, [] ]; - } - - $gameToKeyArray[$id][1][] = $key; - } - - $result = []; - foreach ($gameToKeyArray as [$game, $keys]) { - $result[] = [ - 'gamePicture' => '', - 'name' => $game->getName(), - 'keysAmount' => count($keys), - 'igdbState' => 'not implermented', - 'keys' => $keys, - ]; - } - - return new JsonResponse([ 'data' => $result ]); - } +loginHandler->isLoggedIn()) { + throw new UnauthorizedException(); + } + + $user = $this->loginHandler->getCurrentUser(); + if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) { + throw new ForbiddenException(); + } + + $body = $request->getQueryParams(); + if (!array_key_exists('listid', $body)) { + throw new BadRequestException(); + } + + $list = $this->entityManager->getRepository(GamesList::class)->findOneBy([ 'owner' => $user, 'id' => $body['listid'] ]); + if (!$list instanceof GamesList) { + throw new BadRequestException(); + } + + $keys = $this->entityManager->getRepository(Key::class)->findBy(['list' => $list]); + $gameToKeyArray = []; + foreach ($keys as $key) { + $game = $key->getGame(); + $id = $game->getId(); + + if (!array_key_exists($id, $gameToKeyArray)) { + $gameToKeyArray[$id] = [ $game, [] ]; + } + + $gameToKeyArray[$id][1][] = $key; + } + + $result = []; + foreach ($gameToKeyArray as [$game, $keys]) { + $result[] = [ + 'gamePicture' => '', + 'name' => $game->getName(), + 'keysAmount' => count($keys), + 'igdbState' => 'not implermented', + 'keys' => $keys, + ]; + } + + return new JsonResponse([ 'data' => $result ]); + } } \ No newline at end of file diff --git a/src/php/Routing/Api/Web/CreateKeyListRoute.php b/src/php/Routing/Api/Web/CreateKeyListRoute.php index a96c5a4..bb73bae 100644 --- a/src/php/Routing/Api/Web/CreateKeyListRoute.php +++ b/src/php/Routing/Api/Web/CreateKeyListRoute.php @@ -1,49 +1,49 @@ -loginHandler->isLoggedIn()) { - throw new UnauthorizedException(); - } - - $user = $this->loginHandler->getCurrentUser(); - if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) { - throw new ForbiddenException(); - } - - $body = $request->getParsedBody(); - if (!array_key_exists('name', $body)) { - throw new BadRequestException(); - } - - $name = $body['name']; - $list = new GamesList($user, $name); - $this->entityManager->persist($list); - $this->entityManager->flush(); - - return new Response(); - } +loginHandler->isLoggedIn()) { + throw new UnauthorizedException(); + } + + $user = $this->loginHandler->getCurrentUser(); + if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) { + throw new ForbiddenException(); + } + + $body = $request->getParsedBody(); + if (!array_key_exists('name', $body)) { + throw new BadRequestException(); + } + + $name = $body['name']; + $list = new GamesList($user, $name); + $this->entityManager->persist($list); + $this->entityManager->flush(); + + return new Response(); + } } \ No newline at end of file diff --git a/src/php/Routing/Api/Web/ImportKeysPrepareRoute.php b/src/php/Routing/Api/Web/ImportKeysPrepareRoute.php index ba9cea4..6935d8e 100644 --- a/src/php/Routing/Api/Web/ImportKeysPrepareRoute.php +++ b/src/php/Routing/Api/Web/ImportKeysPrepareRoute.php @@ -1,50 +1,50 @@ -loginHandler->isLoggedIn()) { - throw new UnauthorizedException(); - } - - $user = $this->loginHandler->getCurrentUser(); - if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) { - throw new ForbiddenException(); - } - - /** - * @var UploadedFile $file - */ - $file = $request->getUploadedFiles()['file'] ?? null; - if (!$file === null) { - throw new BadRequestException(); - } - - $fileName = tempnam(sys_get_temp_dir(), 'ImportKeys'); - $file->moveTo($fileName); - $results = $this->importer->interpret($fileName); - unlink($fileName); - - return new JsonResponse($results); - } +loginHandler->isLoggedIn()) { + throw new UnauthorizedException(); + } + + $user = $this->loginHandler->getCurrentUser(); + if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) { + throw new ForbiddenException(); + } + + /** + * @var UploadedFile $file + */ + $file = $request->getUploadedFiles()['file'] ?? null; + if (!$file === null) { + throw new BadRequestException(); + } + + $fileName = tempnam(sys_get_temp_dir(), 'ImportKeys'); + $file->moveTo($fileName); + $results = $this->importer->interpret($fileName); + unlink($fileName); + + return new JsonResponse($results); + } } \ No newline at end of file diff --git a/src/php/Routing/Api/Web/ImportKeysRoute.php b/src/php/Routing/Api/Web/ImportKeysRoute.php index be5f98b..9909b3f 100644 --- a/src/php/Routing/Api/Web/ImportKeysRoute.php +++ b/src/php/Routing/Api/Web/ImportKeysRoute.php @@ -1,66 +1,66 @@ -loginHandler->isLoggedIn()) { - throw new UnauthorizedException(); - } - - $user = $this->loginHandler->getCurrentUser(); - if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) { - throw new ForbiddenException(); - } - - $body = $request->getParsedBody(); - if (!array_key_exists('listid', $body)) { - throw new BadRequestException(); - } - - $list = $this->entityManager->getRepository(GamesList::class)->findOneBy([ 'owner' => $user, 'id' => $body['listid'] ]); - if (!$list instanceof GamesList) { - throw new BadRequestException(); - } - - /** - * @var UploadedFile $file - */ - $file = $request->getUploadedFiles()['file'] ?? null; - if (!$file === null) { - throw new BadRequestException(); - } - - $fileName = tempnam(sys_get_temp_dir(), 'ImportKeys'); - $file->moveTo($fileName); - - $columnDefs = $request->getParsedBody()['columns']; - - [$total, $imported] = $this->importer->import($fileName, $columnDefs, $list); - unlink($fileName); - - return new JsonResponse([ 'success' => true, 'total' => $total, 'imported' => $imported ]); - } +loginHandler->isLoggedIn()) { + throw new UnauthorizedException(); + } + + $user = $this->loginHandler->getCurrentUser(); + if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) { + throw new ForbiddenException(); + } + + $body = $request->getParsedBody(); + if (!array_key_exists('listid', $body)) { + throw new BadRequestException(); + } + + $list = $this->entityManager->getRepository(GamesList::class)->findOneBy([ 'owner' => $user, 'id' => $body['listid'] ]); + if (!$list instanceof GamesList) { + throw new BadRequestException(); + } + + /** + * @var UploadedFile $file + */ + $file = $request->getUploadedFiles()['file'] ?? null; + if (!$file === null) { + throw new BadRequestException(); + } + + $fileName = tempnam(sys_get_temp_dir(), 'ImportKeys'); + $file->moveTo($fileName); + + $columnDefs = $request->getParsedBody()['columns']; + + [$total, $imported] = $this->importer->import($fileName, $columnDefs, $list); + unlink($fileName); + + return new JsonResponse([ 'success' => true, 'total' => $total, 'imported' => $imported ]); + } } \ No newline at end of file diff --git a/src/php/Routing/Api/Web/UserModifyRoute.php b/src/php/Routing/Api/Web/UserModifyRoute.php index 1ea16d2..76170c1 100644 --- a/src/php/Routing/Api/Web/UserModifyRoute.php +++ b/src/php/Routing/Api/Web/UserModifyRoute.php @@ -1,46 +1,46 @@ -loginHandler->isLoggedIn()) { - throw new UnauthorizedException(); - } - - $user = $this->loginHandler->getCurrentUser(); - if (!$user->getPermission()->hasLevel(UserPermission::ADMIN)) { - throw new ForbiddenException(); - } - - $permissions = $request->getParsedBody()['permission']; - - $toChangeUser = $this->entityManager->getRepository(User::class)->find((int)$args['id']); - $toChangeUser->setPermission(UserPermission::from((int)$permissions)); - - $this->entityManager->flush(); - - return new EmptyResponse(200); - } +loginHandler->isLoggedIn()) { + throw new UnauthorizedException(); + } + + $user = $this->loginHandler->getCurrentUser(); + if (!$user->getPermission()->hasLevel(UserPermission::ADMIN)) { + throw new ForbiddenException(); + } + + $permissions = $request->getParsedBody()['permission']; + + $toChangeUser = $this->entityManager->getRepository(User::class)->find((int)$args['id']); + $toChangeUser->setPermission(UserPermission::from((int)$permissions)); + + $this->entityManager->flush(); + + return new EmptyResponse(200); + } } \ No newline at end of file diff --git a/src/php/Routing/Api/Web/WebAPIRoutes.php b/src/php/Routing/Api/Web/WebAPIRoutes.php index f64744a..350f210 100644 --- a/src/php/Routing/Api/Web/WebAPIRoutes.php +++ b/src/php/Routing/Api/Web/WebAPIRoutes.php @@ -1,18 +1,18 @@ -post('/users/{id:number}', UserModifyRoute::class); - - $group->post('/keys/import/prepare', ImportKeysPrepareRoute::class); - $group->post('/keys/import/perform', ImportKeysRoute::class); - - $group->post('/keys/list/create', CreateKeyListRoute::class); - } +post('/users/{id:number}', UserModifyRoute::class); + + $group->post('/keys/import/prepare', ImportKeysPrepareRoute::class); + $group->post('/keys/import/perform', ImportKeysRoute::class); + + $group->post('/keys/list/create', CreateKeyListRoute::class); + } } \ No newline at end of file diff --git a/src/php/Routing/ErrorRoute.php b/src/php/Routing/ErrorRoute.php index a9a71f8..51bc0c0 100644 --- a/src/php/Routing/ErrorRoute.php +++ b/src/php/Routing/ErrorRoute.php @@ -1,20 +1,20 @@ -renderErrorPage($errorCode); - - $response = new Response(status: $errorCode); - $response->getBody()->write($pageContent); - return $response; - } +renderErrorPage($errorCode); + + $response = new Response(status: $errorCode); + $response->getBody()->write($pageContent); + return $response; + } } \ No newline at end of file diff --git a/src/php/Routing/IndexRoute.php b/src/php/Routing/IndexRoute.php index 3f1a5f8..1a43a56 100644 --- a/src/php/Routing/IndexRoute.php +++ b/src/php/Routing/IndexRoute.php @@ -1,33 +1,33 @@ -isLoggedIn()) { - return new RedirectResponse('/login'); - } - - $pageContent = ContainerHandler::get(TemplateEngine::class)->renderPage('index'); - - $response = new Response; - $response->getBody()->write($pageContent); - return $response; - } - - public static function applyRoutes(\League\Route\Router $router): void { - $router->get('/', self::class); - } +isLoggedIn()) { + return new RedirectResponse('/login'); + } + + $pageContent = ContainerHandler::get(TemplateEngine::class)->renderPage('index'); + + $response = new Response; + $response->getBody()->write($pageContent); + return $response; + } + + public static function applyRoutes(\League\Route\Router $router): void { + $router->get('/', self::class); + } } \ No newline at end of file diff --git a/src/php/Routing/KeysRoute.php b/src/php/Routing/KeysRoute.php index b66e8d8..72c31ee 100644 --- a/src/php/Routing/KeysRoute.php +++ b/src/php/Routing/KeysRoute.php @@ -1,45 +1,45 @@ -loginHandler->isLoggedIn()) { - throw new UnauthorizedException(); - } - - $user = $this->loginHandler->getCurrentUser(); - if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) { - throw new ForbiddenException(); - } - - $entityManager = $this->entityManager->getRepository(GamesList::class); - $lists = $entityManager->findBy([ 'owner' => $user ]); - return new TemplateResponse('key-manager', [ 'usersLists' => $lists ]); - } - - public static function applyRoutes(\League\Route\Router $router): void - { - $router->get('/keys', KeysRoute::class); - } +loginHandler->isLoggedIn()) { + throw new UnauthorizedException(); + } + + $user = $this->loginHandler->getCurrentUser(); + if (!$user->getPermission()->hasLevel(UserPermission::PROVIDER)) { + throw new ForbiddenException(); + } + + $entityManager = $this->entityManager->getRepository(GamesList::class); + $lists = $entityManager->findBy([ 'owner' => $user ]); + return new TemplateResponse('key-manager', [ 'usersLists' => $lists ]); + } + + public static function applyRoutes(\League\Route\Router $router): void + { + $router->get('/keys', KeysRoute::class); + } } \ No newline at end of file diff --git a/src/php/Routing/LoginRoutes.php b/src/php/Routing/LoginRoutes.php index 4e3ef4c..246879a 100644 --- a/src/php/Routing/LoginRoutes.php +++ b/src/php/Routing/LoginRoutes.php @@ -1,72 +1,72 @@ -getDiscordEnvironment(); - - return new TemplateResponse('login', [ - 'discordUrl' => $discordEnv->loginUrl - ]); - } - - /** - * @throws OptimisticLockException - * @throws ORMException - */ - public function loginCallback(ServerRequestInterface $request, array $args): ResponseInterface { - if (array_key_exists('error', $request->getQueryParams())) { - return new Response\RedirectResponse('/login'); - } - - $method = $args['method']; - $loginHandler = ContainerHandler::get(LoginHandler::class); - $loginProvider = $loginHandler->getLoginProvider($method); - - $user = $loginProvider->getUser($request); - if ($user->getId() === null) { - $this->entityManager->persist($user); - $this->entityManager->flush(); - } - - $loginHandler->setCurrentUser($user); - return new Response\RedirectResponse('/'); - } - - public function logout(ServerRequestInterface $request): ResponseInterface - { - $loginHandler = ContainerHandler::get(LoginHandler::class); - $loginHandler->deleteSession(); - - return new Response\RedirectResponse('/login'); - } - - public static function addRoutes(\League\Route\Router $router): void { - $routes = ContainerHandler::get(LoginRoutes::class); - - $router->get('/login', $routes->login(...)); - $router->get('/login-callback/{method:word}', $routes->loginCallback(...)); - $router->get('/logout', $routes->logout(...)); - } +getDiscordEnvironment(); + + return new TemplateResponse('login', [ + 'discordUrl' => $discordEnv->loginUrl + ]); + } + + /** + * @throws OptimisticLockException + * @throws ORMException + */ + public function loginCallback(ServerRequestInterface $request, array $args): ResponseInterface { + if (array_key_exists('error', $request->getQueryParams())) { + return new Response\RedirectResponse('/login'); + } + + $method = $args['method']; + $loginHandler = ContainerHandler::get(LoginHandler::class); + $loginProvider = $loginHandler->getLoginProvider($method); + + $user = $loginProvider->getUser($request); + if ($user->getId() === null) { + $this->entityManager->persist($user); + $this->entityManager->flush(); + } + + $loginHandler->setCurrentUser($user); + return new Response\RedirectResponse('/'); + } + + public function logout(ServerRequestInterface $request): ResponseInterface + { + $loginHandler = ContainerHandler::get(LoginHandler::class); + $loginHandler->deleteSession(); + + return new Response\RedirectResponse('/login'); + } + + public static function addRoutes(\League\Route\Router $router): void { + $routes = ContainerHandler::get(LoginRoutes::class); + + $router->get('/login', $routes->login(...)); + $router->get('/login-callback/{method:word}', $routes->loginCallback(...)); + $router->get('/logout', $routes->logout(...)); + } } \ No newline at end of file diff --git a/src/php/Routing/ResourceRoute.php b/src/php/Routing/ResourceRoute.php index 03fcf79..d9e6be2 100644 --- a/src/php/Routing/ResourceRoute.php +++ b/src/php/Routing/ResourceRoute.php @@ -1,55 +1,55 @@ -getPath(); - foreach (self::RESOURCE_EXTENSIONS as $extension) { - if (!str_ends_with($path, $extension)) { - continue; - } - - return true; - } - return false; - } - - public function getResponse(Uri $uri): ResponseInterface { - $filePath = Paths::PUBLIC_PATH . $uri->getPath(); - - if (!file_exists($filePath)) { - $response = new Response(status: 404); - $response->getBody()->write('File not found'); - - return $response; - } - - $mimey = ContainerHandler::get(MimeTypes::class); - $response = new Response( - headers: [ - 'Content-Type' => $mimey->getMimeType(pathinfo($filePath, PATHINFO_EXTENSION)), - 'Cache-Control' => 'public, max-age=3600, must-revalidate', - ] - ); - $response->getBody()->write(file_get_contents($filePath)); - - return $response; - } +getPath(); + foreach (self::RESOURCE_EXTENSIONS as $extension) { + if (!str_ends_with($path, $extension)) { + continue; + } + + return true; + } + return false; + } + + public function getResponse(Uri $uri): ResponseInterface { + $filePath = Paths::PUBLIC_PATH . $uri->getPath(); + + if (!file_exists($filePath)) { + $response = new Response(status: 404); + $response->getBody()->write('File not found'); + + return $response; + } + + $mimey = ContainerHandler::get(MimeTypes::class); + $response = new Response( + headers: [ + 'Content-Type' => $mimey->getMimeType(pathinfo($filePath, PATHINFO_EXTENSION)), + 'Cache-Control' => 'public, max-age=3600, must-revalidate', + ] + ); + $response->getBody()->write(file_get_contents($filePath)); + + return $response; + } } \ No newline at end of file diff --git a/src/php/Routing/Responses/TemplateResponse.php b/src/php/Routing/Responses/TemplateResponse.php index 4731539..bd3d6a2 100644 --- a/src/php/Routing/Responses/TemplateResponse.php +++ b/src/php/Routing/Responses/TemplateResponse.php @@ -1,20 +1,20 @@ -renderPage($templateName, $data); - $this->getBody()->write($body); - } +renderPage($templateName, $data); + $this->getBody()->write($body); + } } \ No newline at end of file diff --git a/src/php/Routing/Router.php b/src/php/Routing/Router.php index 0df7188..a0b4ee2 100644 --- a/src/php/Routing/Router.php +++ b/src/php/Routing/Router.php @@ -1,61 +1,61 @@ -resourceRoute->isValid($request->getUri())) { - return $this->resourceRoute->getResponse($request->getUri()); - } - - $router = new \League\Route\Router; - $strategy = (new ApplicationStrategy)->setContainer(ContainerHandler::getInstance()); - $router->setStrategy($strategy); - - IndexRoute::applyRoutes($router); - LoginRoutes::addRoutes($router); - SetupRoute::applyRoutes($router); - - KeysRoute::applyRoutes($router); - AdminAccountConfigRoute::applyRoutes($router); - - APIRoutes::applyRoutes($router); - - try { - return $router->dispatch($request); - } catch (NotFoundException $e) { - return (new ErrorRoute())->renderErrorPage(404); - } catch (UnauthorizedException) { - return (new ErrorRoute())->renderErrorPage(401); - } catch (ForbiddenException) { - return (new ErrorRoute())->renderErrorPage(403); - } - } +resourceRoute->isValid($request->getUri())) { + return $this->resourceRoute->getResponse($request->getUri()); + } + + $router = new \League\Route\Router; + $strategy = (new ApplicationStrategy)->setContainer(ContainerHandler::getInstance()); + $router->setStrategy($strategy); + + IndexRoute::applyRoutes($router); + LoginRoutes::addRoutes($router); + SetupRoute::applyRoutes($router); + + KeysRoute::applyRoutes($router); + AdminAccountConfigRoute::applyRoutes($router); + + APIRoutes::applyRoutes($router); + + try { + return $router->dispatch($request); + } catch (NotFoundException $e) { + return (new ErrorRoute())->renderErrorPage(404); + } catch (UnauthorizedException) { + return (new ErrorRoute())->renderErrorPage(401); + } catch (ForbiddenException) { + return (new ErrorRoute())->renderErrorPage(403); + } + } } \ No newline at end of file diff --git a/src/php/Routing/SetupRoute.php b/src/php/Routing/SetupRoute.php index 0334b39..56fa767 100644 --- a/src/php/Routing/SetupRoute.php +++ b/src/php/Routing/SetupRoute.php @@ -1,52 +1,52 @@ -loginHandler->isLoggedIn()) { - return new RedirectResponse('/login'); - } - - $repo = $this->entityManager->getRepository(SystemAttribute::class); - - $attribute = $repo->find('ADMIN_SETUP_COMPLETED'); - if ($attribute) { - return new RedirectResponse('/'); - } - - $user = $this->loginHandler->getCurrentUser(); - $user->setPermission(UserPermission::ADMIN); - - $attribute = new SystemAttribute( - 'ADMIN_SETUP_COMPLETED', - 'true' - ); - $this->entityManager->persist($attribute); - $this->entityManager->flush(); - - return new RedirectResponse('/'); - } - - public static function applyRoutes(\League\Route\Router $router) { - $router->get('/setup-admin', self::class); - } - +loginHandler->isLoggedIn()) { + return new RedirectResponse('/login'); + } + + $repo = $this->entityManager->getRepository(SystemAttribute::class); + + $attribute = $repo->find('ADMIN_SETUP_COMPLETED'); + if ($attribute) { + return new RedirectResponse('/'); + } + + $user = $this->loginHandler->getCurrentUser(); + $user->setPermission(UserPermission::ADMIN); + + $attribute = new SystemAttribute( + 'ADMIN_SETUP_COMPLETED', + 'true' + ); + $this->entityManager->persist($attribute); + $this->entityManager->flush(); + + return new RedirectResponse('/'); + } + + public static function applyRoutes(\League\Route\Router $router) { + $router->get('/setup-admin', self::class); + } + } \ No newline at end of file diff --git a/src/php/SetupHandler.php b/src/php/SetupHandler.php index e93e04b..b230824 100644 --- a/src/php/SetupHandler.php +++ b/src/php/SetupHandler.php @@ -1,13 +1,13 @@ - $resource) { - $js = $resource['js']; - if (is_string($js)) { - $js = [$js]; - } - - $css = $resource['css']; - if (is_string($css)) { - $css = [$css]; - } - - $this->resources[$entryKey] = new ResourceEntry( - $js, $css - ); - } - } - - /** - * @throws Exception - */ - public function getResource(string $entry): ResourceEntry - { - if (!array_key_exists($entry, $this->resources)) { - throw new Exception("Entry '$entry' not found"); - } - - return $this->resources[$entry]; - } + $resource) { + $js = $resource['js']; + if (is_string($js)) { + $js = [$js]; + } + + $css = $resource['css']; + if (is_string($css)) { + $css = [$css]; + } + + $this->resources[$entryKey] = new ResourceEntry( + $js, $css + ); + } + } + + /** + * @throws Exception + */ + public function getResource(string $entry): ResourceEntry + { + if (!array_key_exists($entry, $this->resources)) { + throw new Exception("Entry '$entry' not found"); + } + + return $this->resources[$entry]; + } } \ No newline at end of file diff --git a/src/php/Templates/TemplateEngine.php b/src/php/Templates/TemplateEngine.php index 9b3229a..db607a6 100644 --- a/src/php/Templates/TemplateEngine.php +++ b/src/php/Templates/TemplateEngine.php @@ -1,34 +1,34 @@ -addData([ - 'resources' => $this->resourceIndex, - 'activeUser' => $loginHandler->isLoggedIn() ? $loginHandler->getCurrentUser() : null, - ]); - } - - public function renderPage(string $page, array $data = array()) - { - return parent::render("pages/$page", $data); - } - - public function renderErrorPage(int $error) { - return self::renderPage('error', [ 'errorCode' => $error ]); - } +addData([ + 'resources' => $this->resourceIndex, + 'activeUser' => $loginHandler->isLoggedIn() ? $loginHandler->getCurrentUser() : null, + ]); + } + + public function renderPage(string $page, array $data = array()) + { + return parent::render("pages/$page", $data); + } + + public function renderErrorPage(int $error) { + return self::renderPage('error', [ 'errorCode' => $error ]); + } } \ No newline at end of file diff --git a/src/php/bin/doctrine.php b/src/php/bin/doctrine.php index bfa3dcc..b13df31 100644 --- a/src/php/bin/doctrine.php +++ b/src/php/bin/doctrine.php @@ -1,15 +1,15 @@ -#!/usr/bin/env php - -load(); +load(); ContainerHandler::get(DoctrineManager::class)->setup(); \ No newline at end of file diff --git a/src/php/index.dev.php b/src/php/index.dev.php index cb62482..820ee03 100644 --- a/src/php/index.dev.php +++ b/src/php/index.dev.php @@ -1,25 +1,25 @@ -pushHandler($prettyPageHandler); -$whoops->register(); - -ContainerHandler::getInstance()->addShared(HandlerInterface::class, $prettyPageHandler); - -$router = ContainerHandler::getInstance()->get(Router::class); -$result = $router->route(); - +pushHandler($prettyPageHandler); +$whoops->register(); + +ContainerHandler::getInstance()->addShared(HandlerInterface::class, $prettyPageHandler); + +$router = ContainerHandler::getInstance()->get(Router::class); +$result = $router->route(); + (new SapiEmitter)->emit($result); \ No newline at end of file diff --git a/src/php/index.prod.php b/src/php/index.prod.php index c458ee0..79414ee 100644 --- a/src/php/index.prod.php +++ b/src/php/index.prod.php @@ -1,2 +1,2 @@ - - - -
- -
- User Profile Picture - -
- -
- - getName() ?> - - - getPermission()->getHumanReadableName() ?> - -
-
- - - -
-
- + + + +
+ +
+ User Profile Picture + +
+ +
+ + getName() ?> + + + getPermission()->getHumanReadableName() ?> + +
+
+ + + +
+
+ diff --git a/src/templates/layout/keyTable.php b/src/templates/layout/keyTable.php index 0d79473..ec43800 100644 --- a/src/templates/layout/keyTable.php +++ b/src/templates/layout/keyTable.php @@ -1,5 +1,5 @@ - - - + + + diff --git a/src/templates/layout/main.php b/src/templates/layout/main.php index 78d6de1..6eb5f31 100644 --- a/src/templates/layout/main.php +++ b/src/templates/layout/main.php @@ -1,49 +1,49 @@ -getResource($resourceEntry); -?> - - - - - - Game Shop - - js as $js): ?> - - - css as $css): ?> - - - - - insert('layout/navbar') ?> - -
- section('content'); ?> -
- - section('modal') ?> - -
- PROTOTYPE / POC -
- - - +getResource($resourceEntry); +?> + + + + + + Game Shop + + js as $js): ?> + + + css as $css): ?> + + + + + insert('layout/navbar') ?> + +
+ section('content'); ?> +
+ + section('modal') ?> + +
+ PROTOTYPE / POC +
+ + + diff --git a/src/templates/layout/navbar.php b/src/templates/layout/navbar.php index afc01c9..e16c5ba 100644 --- a/src/templates/layout/navbar.php +++ b/src/templates/layout/navbar.php @@ -1,53 +1,53 @@ -getPermission(); - -?> - -