From 58da2e76d5c3054f49d2305eaaccaea9295f64d2 Mon Sep 17 00:00:00 2001 From: Mark Suckerberg Date: Mon, 18 Nov 2024 20:33:28 -0600 Subject: [PATCH] MORE cleanup mass dump --- .gitignore | 1 - .vscode/launch.json | 20 + .vscode/settings.json | 3 + Readme.md | 3 +- composer.json | 30 +- composer.lock | 1713 +++++++++-------- public/index.php | 28 +- sql/alt_db.sql | 2 +- src/Statbus/Controllers/AuthController.php | 89 +- src/Statbus/Controllers/BanController.php | 22 +- src/Statbus/Controllers/Controller.php | 35 +- src/Statbus/Controllers/DBController.php | 53 +- src/Statbus/Controllers/DeathController.php | 127 +- src/Statbus/Controllers/LibraryController.php | 216 ++- src/Statbus/Controllers/LogsController.php | 577 +++--- src/Statbus/Controllers/MessageController.php | 43 +- .../Controllers/NameVoteController.php | 92 +- src/Statbus/Controllers/PlayerController.php | 182 +- src/Statbus/Controllers/PollController.php | 41 +- src/Statbus/Controllers/RoundController.php | 511 +++-- src/Statbus/Controllers/StatController.php | 124 +- src/Statbus/Controllers/StatbusController.php | 264 ++- src/Statbus/Controllers/TicketController.php | 133 +- src/Statbus/Controllers/UserController.php | 97 +- src/Statbus/Extensions/CsrfExtension.php | 42 - src/Statbus/Extensions/PrefixedDB.php | 123 ++ src/Statbus/Middleware/UserGuard.php | 57 +- src/Statbus/Models/Death.php | 94 +- src/Statbus/Models/Library.php | 27 +- src/Statbus/Models/Messages.php | 37 +- src/Statbus/Models/Round.php | 42 +- src/Statbus/Models/Stat.php | 169 +- src/Statbus/Models/Ticket.php | 82 +- src/conf/Statbus.php | 186 +- src/conf/stat_filters.json | 280 +-- src/dependencies.php | 70 +- src/routes.php | 294 +-- src/session.php | 38 +- src/settings.php | 54 +- templates/base/common/footer.html | 2 +- templates/base/common/header.html | 23 +- templates/base/css/style.css | 10 +- templates/base/error_critical.tpl | 4 +- templates/base/nav.tpl | 213 +- templates/base/tracker.html | 14 + templates/components/pagination.html | 4 +- templates/death/html/listingrow.tpl | 77 +- templates/gallery/gallery.tpl | 16 +- templates/gallery/html/artwork.tpl | 4 +- templates/info/admins.tpl | 10 +- templates/info/heatmap.tpl | 81 +- templates/info/retention.tpl | 47 + templates/me/index.tpl | 99 +- templates/misc/namevote/results.tpl | 61 +- templates/misc/namevote/vote.tpl | 152 +- templates/player/roles.tpl | 142 +- templates/player/single.tpl | 392 ++-- templates/polls/single.tpl | 87 +- templates/rounds/html/basic.tpl | 2 +- templates/rounds/html/header.tpl | 1 - templates/rounds/html/listingrow.tpl | 1 - templates/rounds/listing.tpl | 3 +- templates/rounds/logs/generic.tpl | 18 +- templates/rounds/logs/manifest.log.tpl | 68 +- templates/rounds/logs/newscaster.json.tpl | 61 +- templates/rounds/logs/pda.log.tpl | 54 +- templates/rounds/logs/profiler.json.tpl | 56 +- templates/rounds/logs/qdel.log.tpl | 28 + templates/stats/collated.tpl | 5 +- templates/stats/listing.tpl | 4 +- templates/stats/rounds.tpl | 62 +- templates/stats/single/crate_ordered.tpl | 35 + templates/stats/single/mission.tpl | 46 + templates/stats/single/testmerged_prs-2.tpl | 18 +- templates/stats/single/testmerged_prs.tpl | 17 +- .../stats/single/time_dilation_current.tpl | 39 + templates/stats/testmerge.tpl | 68 + templates/stats/type/associative.tpl | 4 +- templates/stats/type/tally.tpl | 52 +- templates/stats/type/text.tpl | 26 +- 80 files changed, 4666 insertions(+), 3441 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json delete mode 100755 src/Statbus/Extensions/CsrfExtension.php create mode 100644 src/Statbus/Extensions/PrefixedDB.php create mode 100644 templates/base/tracker.html create mode 100755 templates/info/retention.tpl create mode 100644 templates/rounds/logs/qdel.log.tpl create mode 100644 templates/stats/single/crate_ordered.tpl create mode 100644 templates/stats/single/mission.tpl create mode 100644 templates/stats/single/time_dilation_current.tpl create mode 100644 templates/stats/testmerge.tpl diff --git a/.gitignore b/.gitignore index 62e8e3c..5ca9f0d 100755 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ composer.lock src/conf/servers.json src/conf/ranks.json -src/conf/jobs.json src/conf/alert.html* src/conf/candidates.json composer.lock diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..eb3013e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for Xdebug", + "type": "php", + "request": "launch", + "port": 9003, + "skipFiles": ["!**/statbus/**/*.php"], + "ignoreExceptions": ["Mediawiki\\**"], + "maxConnections": 1, + "pathMappings": { + "/opt/shiptest/statbus": "${workspaceFolder}" + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b05cbd2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "php.version": "8.2" +} \ No newline at end of file diff --git a/Readme.md b/Readme.md index 64d6fdd..f3512ff 100755 --- a/Readme.md +++ b/Readme.md @@ -35,8 +35,7 @@ Some data, when parsed, can be saved to a second database. To enable this functi There are several files you can edit in order to tailor Statbus to your codebase: - `src/conf/servers.json` can be used to map server information. At the minimum, you must specify a server port and name. See `src/conf/example-servers.json`. -- `src/conf/ranks.json` holds the definitions for admin rank badge colors and icons. See `src/conf/example-ranks.json` for examples. The icon field sources icons from [FontAwsome](https://fontawesome.com/icons?d=gallery&s=solid&m=free). You only need the name of the icon, the part after `fa-`. -- `src/conf/jobs.json` can be used to customize what jobs are looked at for querying role_time data (as seen on `/me`). You should copy `src/conf/example-jobs.json` into your `jobs.json` and add or remove jobs from that listing. +- `src/conf/ranks.json` holds the definitions for admin rank badge colors and icons. See `src/conf/example-ranks.json` for examples. The icon field sources icons from [FontAwsome](https://fontawesome.com/icons?d=gallery&s=solid&m=free). You only need the name of the icon, the part after `fa-`. ##Development Slimbus can be set up in a local development environment with Docker. diff --git a/composer.json b/composer.json index 0bb41aa..d0c5632 100755 --- a/composer.json +++ b/composer.json @@ -1,25 +1,21 @@ { - "repositories": [ - { - "type":"git", - "url":"https://github.com/nfreader/easydb" - } - ], + "repositories": [], "require": { - "php": ">=5.5.0", - "slim/slim": "^3.1", - "slim/php-view": "^2.0", - "vlucas/phpdotenv": "^2.5", - "paragonie/easydb": "@dev", - "slim/twig-view": "^2.4", - "guzzlehttp/guzzle": "^6.3", + "php": ">=7.4.0", + "slim/slim": "^3", + "slim/php-view": "^3.4", + "vlucas/phpdotenv": "^5.6", + "paragonie/easydb": "^3", + "slim/twig-view": "^2.5", + "guzzlehttp/guzzle": "^7.9", "erusev/parsedown": "^1.7", - "kevinrob/guzzle-cache-middleware": "^3.2", + "kevinrob/guzzle-cache-middleware": "^5.1", "league/flysystem": "^1.0", "doctrine/cache": "^1.7", - "slim/csrf": "0.8.3", - "sensiolabs/security-checker": "^5.0", - "twig/extensions": "*" + "slim/csrf": "^0", + "twig/twig": "^3.11", + "php-di/php-di": "^6.4", + "twig/intl-extra": "^3.13" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index a755690..09ecd00 100755 --- a/composer.lock +++ b/composer.lock @@ -4,84 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7c8ea9e28d9c253b87f4cb429a81f7f4", + "content-hash": "7674b248d58160223e2943c6c3eabe2b", "packages": [ - { - "name": "composer/ca-bundle", - "version": "1.5.2", - "source": { - "type": "git", - "url": "https://github.com/composer/ca-bundle.git", - "reference": "48a792895a2b7a6ee65dd5442c299d7b835b6137" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/48a792895a2b7a6ee65dd5442c299d7b835b6137", - "reference": "48a792895a2b7a6ee65dd5442c299d7b835b6137", - "shasum": "" - }, - "require": { - "ext-openssl": "*", - "ext-pcre": "*", - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8 || ^9", - "psr/log": "^1.0 || ^2.0 || ^3.0", - "symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\CaBundle\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", - "keywords": [ - "cabundle", - "cacert", - "certificate", - "ssl", - "tls" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.5.2" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2024-09-25T07:49:53+00:00" - }, { "name": "doctrine/cache", "version": "1.13.0", @@ -231,39 +155,111 @@ }, "time": "2019-12-30T22:54:17+00:00" }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + }, + "dist": { + "type": "zip", + "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.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:45:45+00:00" + }, { "name": "guzzlehttp/guzzle", - "version": "6.5.8", + "version": "7.9.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "a52f0440530b54fa079ce76e8c5d196a42cad981" + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/a52f0440530b54fa079ce76e8c5d196a42cad981", - "reference": "a52f0440530b54fa079ce76e8c5d196a42cad981", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.9", - "php": ">=5.5", - "symfony/polyfill-intl-idn": "^1.17" + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" }, "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.1" + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "6.5-dev" + "bamarni-bin": { + "bin-links": true, + "forward-command": false } }, "autoload": { @@ -316,19 +312,20 @@ } ], "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", "keywords": [ "client", "curl", "framework", "http", "http client", + "psr-18", + "psr-7", "rest", "web service" ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/6.5.8" + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" }, "funding": [ { @@ -344,33 +341,37 @@ "type": "tidelift" } ], - "time": "2022-06-20T22:16:07+00:00" + "time": "2024-07-24T11:22:20+00:00" }, { "name": "guzzlehttp/promises", - "version": "1.5.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e" + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/67ab6e18aaa14d753cc148911d273f6e6cb6721e", - "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "^7.2.5 || ^8.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\Promise\\": "src/" } @@ -407,7 +408,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.3" + "source": "https://github.com/guzzle/promises/tree/2.0.4" }, "funding": [ { @@ -423,42 +424,48 @@ "type": "tidelift" } ], - "time": "2023-05-21T12:31:43+00:00" + "time": "2024-10-17T10:06:22+00:00" }, { "name": "guzzlehttp/psr7", - "version": "1.9.1", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b" + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/e4490cabc77465aaee90b20cfc9a770f8c04be6b", - "reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", "shasum": "" }, "require": { - "php": ">=5.4.0", - "psr/http-message": "~1.0", - "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" }, "provide": { + "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { - "ext-zlib": "*", - "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\Psr7\\": "src/" } @@ -497,6 +504,11 @@ "name": "Tobias Schultze", "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" } ], "description": "PSR-7 message implementation that also provides common utility methods", @@ -512,7 +524,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.9.1" + "source": "https://github.com/guzzle/psr7/tree/2.7.0" }, "funding": [ { @@ -528,39 +540,41 @@ "type": "tidelift" } ], - "time": "2023-04-17T16:00:37+00:00" + "time": "2024-07-18T11:15:46+00:00" }, { "name": "kevinrob/guzzle-cache-middleware", - "version": "v3.5.0", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/Kevinrob/guzzle-cache-middleware.git", - "reference": "c699f4623a7be6c92468876a1b2404d499a4f22f" + "reference": "6bd64dbbe5155107d84a0f67140a8822a709c6d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Kevinrob/guzzle-cache-middleware/zipball/c699f4623a7be6c92468876a1b2404d499a4f22f", - "reference": "c699f4623a7be6c92468876a1b2404d499a4f22f", + "url": "https://api.github.com/repos/Kevinrob/guzzle-cache-middleware/zipball/6bd64dbbe5155107d84a0f67140a8822a709c6d0", + "reference": "6bd64dbbe5155107d84a0f67140a8822a709c6d0", "shasum": "" }, "require": { "guzzlehttp/guzzle": "^6.0 || ^7.0", + "guzzlehttp/promises": "^1.4 || ^2.0", "guzzlehttp/psr7": "^1.7.0 || ^2.0.0", - "php": ">=5.5.0" + "php": ">=7.2.0" }, "require-dev": { "cache/array-adapter": "^0.4 || ^0.5 || ^1.0", "cache/simple-cache-bridge": "^0.1 || ^1.0", - "doctrine/cache": "^1.0", + "doctrine/cache": "^1.10", "illuminate/cache": "^5.0", - "league/flysystem": "^1.0", - "phpunit/phpunit": "^4.8.36 || ^5.0", + "league/flysystem": "^2.5", + "phpunit/phpunit": "^8.5.15 || ^9.5", "psr/cache": "^1.0", + "symfony/cache": "^4.4 || ^5.0", "symfony/phpunit-bridge": "^4.4 || ^5.0" }, "suggest": { - "doctrine/cache": "This library has a lot of ready-to-use cache storage (to be used with Kevinrob\\GuzzleCache\\Storage\\DoctrineCacheStorage).", + "doctrine/cache": "This library has a lot of ready-to-use cache storage (to be used with Kevinrob\\GuzzleCache\\Storage\\DoctrineCacheStorage). Use only versions >=1.4.0 < 2.0.0", "guzzlehttp/guzzle": "For using this library. It was created for Guzzle6 (but you can use it with any PSR-7 HTTP client).", "laravel/framework": "To be used with Kevinrob\\GuzzleCache\\Storage\\LaravelCacheStorage", "league/flysystem": "To be used with Kevinrob\\GuzzleCache\\Storage\\FlysystemStorage", @@ -609,9 +623,70 @@ ], "support": { "issues": "https://github.com/Kevinrob/guzzle-cache-middleware/issues", - "source": "https://github.com/Kevinrob/guzzle-cache-middleware/tree/v3.5.0" + "source": "https://github.com/Kevinrob/guzzle-cache-middleware/tree/v5.1.0" + }, + "time": "2023-11-09T06:53:45+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v1.3.5", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c", + "reference": "1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "illuminate/support": "^8.0|^9.0|^10.0|^11.0", + "nesbot/carbon": "^2.61|^3.0", + "pestphp/pest": "^1.21.3", + "phpstan/phpstan": "^1.8.2", + "symfony/var-dumper": "^5.4.11|^6.2.0|^7.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" }, - "time": "2022-01-04T16:40:46+00:00" + "time": "2024-09-23T13:33:08+00:00" }, { "name": "league/flysystem", @@ -814,55 +889,89 @@ "time": "2018-02-13T20:26:39+00:00" }, { - "name": "paragonie/easydb", - "version": "dev-master", + "name": "paragonie/corner", + "version": "v2.0.1", "source": { "type": "git", - "url": "https://github.com/nfreader/easydb", - "reference": "0bea1f81af1282486bfd907d1e906d00bffb8466" + "url": "https://github.com/paragonie/corner.git", + "reference": "f222adc1da32547e20a3c5007f4a711b0cf633d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/corner/zipball/f222adc1da32547e20a3c5007f4a711b0cf633d0", + "reference": "f222adc1da32547e20a3c5007f4a711b0cf633d0", + "shasum": "" }, "require": { - "ext-pdo": "*" + "php": "^7.1|^8" }, "require-dev": { - "phpunit/phpunit": "^5", - "spatie/7to5": "^1.0", - "squizlabs/php_codesniffer": "^2.7", - "vimeo/psalm": "^0|^1" + "phpunit/phpunit": "^7", + "psalm/plugin-phpunit": "<1", + "vimeo/psalm": "^3" }, - "default-branch": true, "type": "library", "autoload": { "psr-4": { - "ParagonIE\\EasyDB\\": "src" + "ParagonIE\\Corner\\": "src/" } }, - "autoload-dev": { - "psr-4": { - "ParagonIE\\EasyDB\\Tests\\": "tests" + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com" } + ], + "description": "Generic PHP Exceptions and Errors, made user-friendly.", + "support": { + "issues": "https://github.com/paragonie/corner/issues", + "source": "https://github.com/paragonie/corner/tree/v2.0.1" }, - "scripts": { - "php5ize": [ - "php7to5 convert --overwrite --copy-all -- src/ src-php5/", - "php7to5 convert --overwrite --copy-all -- tests/ tests-php5/", - "rm -rf src && mv src-php5 src", - "rm -rf tests && mv tests-php5 tests" - ], - "check-style": [ - "phpcs -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests" - ], - "fix-style": [ - "phpcbf -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests" - ] + "time": "2020-01-20T00:57:57+00:00" + }, + { + "name": "paragonie/easydb", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/paragonie/easydb.git", + "reference": "b8d12d444ca84010f5f48ceacb39d3f2822f0c6f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/easydb/zipball/b8d12d444ca84010f5f48ceacb39d3f2822f0c6f", + "reference": "b8d12d444ca84010f5f48ceacb39d3f2822f0c6f", + "shasum": "" + }, + "require": { + "ext-pdo": "*", + "paragonie/corner": "^2", + "php": "^8" }, + "require-dev": { + "phpunit/phpunit": "^9", + "psalm/plugin-phpunit": "<1", + "squizlabs/php_codesniffer": "^3", + "vimeo/psalm": "^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\EasyDB\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { - "name": "Scott Arciszewski", - "email": "scott@paragonie.com", + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", "homepage": "https://paragonie.com", "role": "Developer" }, @@ -879,17 +988,17 @@ ], "description": "Easy-to-use database abstraction", "keywords": [ - "PDO", "database", + "pdo", "security", "sql" ], "support": { - "issues": "https://github.com/paragonie/easydb/issues", "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/easydb/issues", "source": "https://github.com/paragonie/easydb" }, - "time": "2018-03-15T17:22:00+00:00" + "time": "2022-06-08T05:23:05+00:00" }, { "name": "paragonie/random_compat", @@ -942,108 +1051,463 @@ "time": "2020-10-15T08:29:30+00:00" }, { - "name": "pimple/pimple", - "version": "v3.5.0", + "name": "php-di/invoker", + "version": "2.3.4", "source": { "type": "git", - "url": "https://github.com/silexphp/Pimple.git", - "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed" + "url": "https://github.com/PHP-DI/Invoker.git", + "reference": "33234b32dafa8eb69202f950a1fc92055ed76a86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a94b3a4db7fb774b3d78dad2315ddc07629e1bed", - "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed", + "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/33234b32dafa8eb69202f950a1fc92055ed76a86", + "reference": "33234b32dafa8eb69202f950a1fc92055ed76a86", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1 || ^2.0" + "php": ">=7.3", + "psr/container": "^1.0|^2.0" }, "require-dev": { - "symfony/phpunit-bridge": "^5.4@dev" + "athletic/athletic": "~0.1.8", + "mnapoli/hard-mode": "~0.3.0", + "phpunit/phpunit": "^9.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4.x-dev" - } - }, "autoload": { - "psr-0": { - "Pimple": "src/" + "psr-4": { + "Invoker\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Pimple, a simple Dependency Injection Container", - "homepage": "https://pimple.symfony.com", + "description": "Generic and extensible callable invoker", + "homepage": "https://github.com/PHP-DI/Invoker", "keywords": [ - "container", - "dependency injection" + "callable", + "dependency", + "dependency-injection", + "injection", + "invoke", + "invoker" ], "support": { - "source": "https://github.com/silexphp/Pimple/tree/v3.5.0" + "issues": "https://github.com/PHP-DI/Invoker/issues", + "source": "https://github.com/PHP-DI/Invoker/tree/2.3.4" }, - "time": "2021-10-28T11:13:42+00:00" + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + } + ], + "time": "2023-09-08T09:24:21+00:00" }, { - "name": "psr/container", - "version": "1.1.2", + "name": "php-di/php-di", + "version": "6.4.0", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "url": "https://github.com/PHP-DI/PHP-DI.git", + "reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/ae0f1b3b03d8b29dff81747063cbfd6276246cc4", + "reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4", "shasum": "" }, "require": { - "php": ">=7.4.0" + "laravel/serializable-closure": "^1.0", + "php": ">=7.4.0", + "php-di/invoker": "^2.0", + "php-di/phpdoc-reader": "^2.0.1", + "psr/container": "^1.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "require-dev": { + "doctrine/annotations": "~1.10", + "friendsofphp/php-cs-fixer": "^2.4", + "mnapoli/phpunit-easymock": "^1.2", + "ocramius/proxy-manager": "^2.11.2", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "doctrine/annotations": "Install it if you want to use annotations (version ~1.2)", + "ocramius/proxy-manager": "Install it if you want to use lazy injection (version ~2.0)" }, "type": "library", "autoload": { + "files": [ + "src/functions.php" + ], "psr-4": { - "Psr\\Container\\": "src/" + "DI\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "The dependency injection container for humans", + "homepage": "https://php-di.org/", "keywords": [ "PSR-11", "container", - "container-interface", "container-interop", - "psr" + "dependency injection", + "di", + "ioc", + "psr11" ], "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "issues": "https://github.com/PHP-DI/PHP-DI/issues", + "source": "https://github.com/PHP-DI/PHP-DI/tree/6.4.0" }, - "time": "2021-11-05T16:50:12+00:00" - }, - { - "name": "psr/http-message", + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/php-di/php-di", + "type": "tidelift" + } + ], + "time": "2022-04-09T16:46:38+00:00" + }, + { + "name": "php-di/phpdoc-reader", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/PhpDocReader.git", + "reference": "66daff34cbd2627740ffec9469ffbac9f8c8185c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/PhpDocReader/zipball/66daff34cbd2627740ffec9469ffbac9f8c8185c", + "reference": "66daff34cbd2627740ffec9469ffbac9f8c8185c", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "require-dev": { + "mnapoli/hard-mode": "~0.3.0", + "phpunit/phpunit": "^8.5|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpDocReader\\": "src/PhpDocReader" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PhpDocReader parses @var and @param values in PHP docblocks (supports namespaced class names with the same resolution rules as PHP)", + "keywords": [ + "phpdoc", + "reflection" + ], + "support": { + "issues": "https://github.com/PHP-DI/PhpDocReader/issues", + "source": "https://github.com/PHP-DI/PhpDocReader/tree/2.2.1" + }, + "time": "2020-10-12T12:39:22+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.3", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:41:07+00:00" + }, + { + "name": "pimple/pimple", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/silexphp/Pimple.git", + "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a94b3a4db7fb774b3d78dad2315ddc07629e1bed", + "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1 || ^2.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^5.4@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pimple": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Pimple, a simple Dependency Injection Container", + "homepage": "https://pimple.symfony.com", + "keywords": [ + "container", + "dependency injection" + ], + "support": { + "source": "https://github.com/silexphp/Pimple/tree/v3.5.0" + }, + "time": "2021-10-28T11:13:42+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", "version": "1.1", "source": { "type": "git", @@ -1135,60 +1599,9 @@ "description": "A polyfill for getallheaders.", "support": { "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "sensiolabs/security-checker", - "version": "v5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sensiolabs/security-checker.git", - "reference": "46be3f58adac13084497961e10eed9a7fb4d44d1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/46be3f58adac13084497961e10eed9a7fb4d44d1", - "reference": "46be3f58adac13084497961e10eed9a7fb4d44d1", - "shasum": "" - }, - "require": { - "composer/ca-bundle": "^1.0", - "php": ">=5.5.9", - "symfony/console": "~2.7|~3.0|~4.0" - }, - "bin": [ - "security-checker" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "autoload": { - "psr-4": { - "SensioLabs\\Security\\": "SensioLabs/Security" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien.potencier@gmail.com" - } - ], - "description": "A security checker for your composer.lock", - "support": { - "issues": "https://github.com/sensiolabs/security-checker/issues", - "source": "https://github.com/sensiolabs/security-checker/tree/master" + "source": "https://github.com/ralouphie/getallheaders/tree/develop" }, - "abandoned": "https://github.com/fabpot/local-php-security-checker", - "time": "2018-12-19T17:14:59+00:00" + "time": "2019-03-08T08:55:37+00:00" }, { "name": "slim/csrf", @@ -1246,24 +1659,27 @@ }, { "name": "slim/php-view", - "version": "2.2.1", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/slimphp/PHP-View.git", - "reference": "a13ada9d7962ca1b48799c0d9ffbca4c33245aed" + "reference": "ef1821663a6a028b9e446e8c6818fd257bf70313" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/PHP-View/zipball/a13ada9d7962ca1b48799c0d9ffbca4c33245aed", - "reference": "a13ada9d7962ca1b48799c0d9ffbca4c33245aed", + "url": "https://api.github.com/repos/slimphp/PHP-View/zipball/ef1821663a6a028b9e446e8c6818fd257bf70313", + "reference": "ef1821663a6a028b9e446e8c6818fd257bf70313", "shasum": "" }, "require": { - "psr/http-message": "^1.0" + "php": "^7.4 || ^8.0", + "psr/http-message": "^1.1 || ^2.0" }, "require-dev": { - "phpunit/phpunit": "^4.8", - "slim/slim": "^3.0" + "phpstan/phpstan": "^1", + "phpunit/phpunit": "^9 || ^10", + "slim/psr7": "^1.6", + "squizlabs/php_codesniffer": "^3.10" }, "type": "library", "autoload": { @@ -1293,9 +1709,9 @@ ], "support": { "issues": "https://github.com/slimphp/PHP-View/issues", - "source": "https://github.com/slimphp/PHP-View/tree/2.2.1" + "source": "https://github.com/slimphp/PHP-View/tree/3.4.0" }, - "time": "2019-04-15T20:43:28+00:00" + "time": "2024-07-19T18:54:54+00:00" }, { "name": "slim/slim", @@ -1382,333 +1798,35 @@ "type": "tidelift" } ], - "time": "2023-07-23T04:32:51+00:00" - }, - { - "name": "slim/twig-view", - "version": "2.5.1", - "source": { - "type": "git", - "url": "https://github.com/slimphp/Twig-View.git", - "reference": "47bd5cc1cbbdf5196d0873ece0ee97c6c7b352e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/slimphp/Twig-View/zipball/47bd5cc1cbbdf5196d0873ece0ee97c6c7b352e9", - "reference": "47bd5cc1cbbdf5196d0873ece0ee97c6c7b352e9", - "shasum": "" - }, - "require": { - "php": ">=5.5.0", - "psr/http-message": "^1.0", - "twig/twig": "^1.38|^2.7|^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8|^5.7", - "slim/slim": "^3.10" - }, - "type": "library", - "autoload": { - "psr-4": { - "Slim\\Views\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Josh Lockhart", - "email": "hello@joshlockhart.com", - "homepage": "http://joshlockhart.com" - } - ], - "description": "Slim Framework 3 view helper built on top of the Twig 2 templating component", - "homepage": "http://slimframework.com", - "keywords": [ - "framework", - "slim", - "template", - "twig", - "view" - ], - "support": { - "issues": "https://github.com/slimphp/Twig-View/issues", - "source": "https://github.com/slimphp/Twig-View/tree/master" - }, - "time": "2019-11-28T18:03:50+00:00" - }, - { - "name": "symfony/console", - "version": "v4.4.49", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/33fa45ffc81fdcc1ca368d4946da859c8cdb58d9", - "reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9", - "shasum": "" - }, - "require": { - "php": ">=7.1.3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2" - }, - "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<4.3|>=5", - "symfony/lock": "<4.4", - "symfony/process": "<3.3" - }, - "provide": { - "psr/log-implementation": "1.0|2.0" - }, - "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^3.4|^4.0|^5.0", - "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/event-dispatcher": "^4.3", - "symfony/lock": "^4.4|^5.0", - "symfony/process": "^3.4|^4.0|^5.0", - "symfony/var-dumper": "^4.3|^5.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/console/tree/v4.4.49" - }, - "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": "2022-11-05T17:10:16+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v2.5.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "80d075412b557d41002320b96a096ca65aa2c98d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/80d075412b557d41002320b96a096ca65aa2c98d", - "reference": "80d075412b557d41002320b96a096ca65aa2c98d", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "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": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.3" - }, - "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": "2023-01-24T14:02:46+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.31.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/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" + "time": "2023-07-23T04:32:51+00:00" }, { - "name": "symfony/polyfill-intl-idn", - "version": "v1.31.0", + "name": "slim/twig-view", + "version": "2.5.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" + "url": "https://github.com/slimphp/Twig-View.git", + "reference": "47bd5cc1cbbdf5196d0873ece0ee97c6c7b352e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", - "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", + "url": "https://api.github.com/repos/slimphp/Twig-View/zipball/47bd5cc1cbbdf5196d0873ece0ee97c6c7b352e9", + "reference": "47bd5cc1cbbdf5196d0873ece0ee97c6c7b352e9", "shasum": "" }, "require": { - "php": ">=7.2", - "symfony/polyfill-intl-normalizer": "^1.10" + "php": ">=5.5.0", + "psr/http-message": "^1.0", + "twig/twig": "^1.38|^2.7|^3.0" }, - "suggest": { - "ext-intl": "For best performance" + "require-dev": { + "phpunit/phpunit": "^4.8|^5.7", + "slim/slim": "^3.10" }, "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" + "Slim\\Views\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1717,83 +1835,56 @@ ], "authors": [ { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" } ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", - "homepage": "https://symfony.com", + "description": "Slim Framework 3 view helper built on top of the Twig 2 templating component", + "homepage": "http://slimframework.com", "keywords": [ - "compatibility", - "idn", - "intl", - "polyfill", - "portable", - "shim" + "framework", + "slim", + "template", + "twig", + "view" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" + "issues": "https://github.com/slimphp/Twig-View/issues", + "source": "https://github.com/slimphp/Twig-View/tree/master" }, - "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" + "time": "2019-11-28T18:03:50+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.31.0", + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", "shasum": "" }, "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" + "php": ">=8.1" }, "type": "library", "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" } }, "autoload": { "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" + "function.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1810,18 +1901,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", + "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -1837,45 +1920,42 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "name": "symfony/intl", + "version": "v7.1.6", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "url": "https://github.com/symfony/intl.git", + "reference": "c65630cc9c22acd9b511a87a3f4734e48e500932" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/symfony/intl/zipball/c65630cc9c22acd9b511a87a3f4734e48e500932", + "reference": "c65630cc9c22acd9b511a87a3f4734e48e500932", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" }, - "provide": { - "ext-mbstring": "*" + "conflict": { + "symfony/string": "<7.1" }, - "suggest": { - "ext-mbstring": "For best performance" + "require-dev": { + "symfony/filesystem": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } + "Symfony\\Component\\Intl\\": "" + }, + "exclude-from-classmap": [ + "/Tests/", + "/Resources/data/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1883,25 +1963,34 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Eriksen Costa", + "email": "eriksen.costa@infranology.com.br" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Provides access to the localization data of the ICU library", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" + "i18n", + "icu", + "internationalization", + "intl", + "l10n", + "localization" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "source": "https://github.com/symfony/intl/tree/v7.1.6" }, "funding": [ { @@ -1917,56 +2006,70 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-10-09T08:46:59+00:00" }, { - "name": "symfony/polyfill-php72", + "name": "symfony/polyfill-ctype", "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce", - "reference": "fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { "php": ">=7.2" }, - "type": "metapackage", + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", "extra": { "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", + "ctype", "polyfill", - "portable", - "shim" + "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -1985,22 +2088,28 @@ "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-php73", + "name": "symfony/polyfill-mbstring", "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", - "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { "php": ">=7.2" }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, "type": "library", "extra": { "thanks": { @@ -2013,11 +2122,8 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "classmap": [ - "Resources/stubs" - ] + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2033,16 +2139,17 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ "compatibility", + "mbstring", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -2141,44 +2248,39 @@ "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/service-contracts", - "version": "v2.5.3", + "name": "symfony/polyfill-php81", + "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a2329596ddc8fd568900e3fc76cba42489ecc7f3", - "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "suggest": { - "symfony/service-implementation": "" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2194,18 +2296,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to writing services", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.3" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" }, "funding": [ { @@ -2221,45 +2321,38 @@ "type": "tidelift" } ], - "time": "2023-04-21T15:04:16+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "twig/extensions", - "version": "v1.5.4", + "name": "twig/intl-extra", + "version": "v3.13.0", "source": { "type": "git", - "url": "https://github.com/twigphp/Twig-extensions.git", - "reference": "57873c8b0c1be51caa47df2cdb824490beb16202" + "url": "https://github.com/twigphp/intl-extra.git", + "reference": "1b8d78c5db08bdc61015fd55009d2e84b3aa7e38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig-extensions/zipball/57873c8b0c1be51caa47df2cdb824490beb16202", - "reference": "57873c8b0c1be51caa47df2cdb824490beb16202", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/1b8d78c5db08bdc61015fd55009d2e84b3aa7e38", + "reference": "1b8d78c5db08bdc61015fd55009d2e84b3aa7e38", "shasum": "" }, "require": { - "twig/twig": "^1.27|^2.0" + "php": ">=8.0.2", + "symfony/intl": "^5.4|^6.4|^7.0", + "twig/twig": "^3.13|^4.0" }, "require-dev": { - "symfony/phpunit-bridge": "^3.4", - "symfony/translation": "^2.7|^3.4" - }, - "suggest": { - "symfony/translation": "Allow the time_diff output to be translated" + "symfony/phpunit-bridge": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, "autoload": { - "psr-0": { - "Twig_Extensions_": "lib/" - }, "psr-4": { - "Twig\\Extensions\\": "src/" - } + "Twig\\Extra\\Intl\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2268,55 +2361,65 @@ "authors": [ { "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" } ], - "description": "Common additional features for Twig that do not directly belong in core", + "description": "A Twig extension for Intl", + "homepage": "https://twig.symfony.com", "keywords": [ - "i18n", - "text" + "intl", + "twig" ], "support": { - "issues": "https://github.com/twigphp/Twig-extensions/issues", - "source": "https://github.com/twigphp/Twig-extensions/tree/master" + "source": "https://github.com/twigphp/intl-extra/tree/v3.13.0" }, - "abandoned": true, - "time": "2018-12-05T18:34:18+00:00" + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2024-09-03T13:08:40+00:00" }, { "name": "twig/twig", - "version": "v2.16.1", + "version": "v3.14.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "19185947ec75d433a3ac650af32fc05649b95ee1" + "reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/19185947ec75d433a3ac650af32fc05649b95ee1", - "reference": "19185947ec75d433a3ac650af32fc05649b95ee1", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/126b2c97818dbff0cdf3fbfc881aedb3d40aae72", + "reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72", "shasum": "" }, "require": { - "php": ">=7.1.3", + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php72": "^1.8" + "symfony/polyfill-php81": "^1.29" }, "require-dev": { - "psr/container": "^1.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3" + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.16-dev" - } - }, "autoload": { - "psr-0": { - "Twig_": "lib/" - }, + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -2349,7 +2452,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v2.16.1" + "source": "https://github.com/twigphp/Twig/tree/v3.14.0" }, "funding": [ { @@ -2361,39 +2464,47 @@ "type": "tidelift" } ], - "time": "2024-09-09T17:53:56+00:00" + "time": "2024-09-09T17:55:12+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v2.6.9", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "2e93cc98e2e8e869f8d9cfa61bb3a99ba4fc4141" + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2e93cc98e2e8e869f8d9cfa61bb3a99ba4fc4141", - "reference": "2e93cc98e2e8e869f8d9cfa61bb3a99ba4fc4141", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", "shasum": "" }, "require": { - "php": "^5.3.9 || ^7.0 || ^8.0", - "symfony/polyfill-ctype": "^1.17" + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.3", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" }, "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", "ext-filter": "*", - "ext-pcre": "*", - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.21" + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" }, "suggest": { - "ext-filter": "Required to use the boolean validator.", - "ext-pcre": "Required to use most of the library." + "ext-filter": "Required to use the boolean validator." }, "type": "library", "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, "branch-alias": { - "dev-master": "2.6-dev" + "dev-master": "5.6-dev" } }, "autoload": { @@ -2425,7 +2536,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v2.6.9" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" }, "funding": [ { @@ -2437,19 +2548,17 @@ "type": "tidelift" } ], - "time": "2021-12-12T22:59:22+00:00" + "time": "2024-07-20T21:52:34+00:00" } ], "packages-dev": [], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "paragonie/easydb": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.5.0" + "php": ">=7.4.0" }, "platform-dev": [], "plugin-api-version": "2.3.0" diff --git a/public/index.php b/public/index.php index 6840895..906a56e 100755 --- a/public/index.php +++ b/public/index.php @@ -1,6 +1,6 @@ load(); -if(getenv('DEBUG')){ - ini_set('xdebug.var_display_max_depth',-1); - ini_set('xdebug.var_display_max_data',-1); - ini_set('xdebug.var_display_max_children',-1); +if ($_ENV['DEBUG']) { + ini_set('xdebug.var_display_max_depth', -1); + ini_set('xdebug.var_display_max_data', -1); + ini_set('xdebug.var_display_max_children', -1); } // Instantiate the app $settings = require __DIR__ . '/../src/settings.php'; $settings['settings']['statbus'] = require __DIR__ . '/../src/conf/Statbus.php'; -if(file_exists(__DIR__ . '/../src/conf/servers.json')){ +if (file_exists(__DIR__ . '/../src/conf/servers.json')) { $settings['settings']['statbus']['servers'] = json_decode(file_get_contents(__DIR__ . '/../src/conf/servers.json'), true); } -if(file_exists(__DIR__ . '/../src/conf/ranks.json')){ +if (file_exists(__DIR__ . '/../src/conf/ranks.json')) { $settings['settings']['statbus']['ranks'] = json_decode(file_get_contents(__DIR__ . '/../src/conf/ranks.json'), true); } -if ($refresh = filter_input(INPUT_GET,'refresh', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH)){ - if(password_verify($refresh, password_hash($settings['settings']['refresh_key'], PASSWORD_DEFAULT))){ +if ($refresh = filter_input(INPUT_GET, 'refresh', FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH)) { + if (password_verify($refresh, password_hash($settings['settings']['refresh_key'], PASSWORD_DEFAULT))) { $settings['settings']['twig']['auto_reload'] = TRUE; } } // Instantiate the app -$app = new \Slim\App($settings); +#phpinfo(); +#xdebug_info(); + +use Slim\App; + +$app = new App($settings); + // Set up dependencies $dependencies = require __DIR__ . '/../src/dependencies.php'; diff --git a/sql/alt_db.sql b/sql/alt_db.sql index 226251c..b11eaca 100755 --- a/sql/alt_db.sql +++ b/sql/alt_db.sql @@ -4,7 +4,7 @@ CREATE TABLE `art_vote` ( `artwork` varchar(32) NOT NULL DEFAULT '', `ckey` varchar(32) NOT NULL DEFAULT '', `rating` int(1) unsigned NOT NULL DEFAULT 1, - `server` varchar(16) NOT NULL DEFAULT '', + `server` varchar(32) NOT NULL DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `artwork` (`artwork`,`ckey`), KEY `id` (`id`) diff --git a/src/Statbus/Controllers/AuthController.php b/src/Statbus/Controllers/AuthController.php index 035a098..f5e997a 100755 --- a/src/Statbus/Controllers/AuthController.php +++ b/src/Statbus/Controllers/AuthController.php @@ -3,33 +3,39 @@ namespace Statbus\Controllers; use Psr\Container\ContainerInterface; use Statbus\Controllers\Controller as Controller; +use GuzzleHttp\Client; -class AuthController Extends Controller{ +class AuthController extends Controller +{ // protected $DB; - + private $site_private_token; private $session_private_token; + private $session_public_token; private $return_uri; + private $AuthUrl; + private $TokenUrl; + private $AuthSessionUrl; + + private string $remote; - public function __construct(ContainerInterface $container) { + public function __construct(ContainerInterface $container) + { parent::__construct($container); $this->settings = $container->get('settings')['statbus']['auth']; - if(!$this->settings['remote_auth']) { - return $this->view->render($this->response, 'base/error.tpl', [ - 'message' => "Authentication not supported", - 'code' => 501, - ]); + if (!$this->settings['remote_auth']) { + return; } else { $this->remote = $this->settings['remote_auth']; - $this->AuthUrl = $this->remote."oauth_create_session.php"; - $this->TokenUrl = $this->remote."oauth.php"; - $this->AuthSessionUrl = $this->remote."oauth_get_session_info.php"; - $this->return_uri = $this->request->getUri()->getBaseUrl().$this->router->pathFor('auth_return'); + $this->AuthUrl = $this->remote . "oauth_create_session.php"; + $this->TokenUrl = $this->remote . "oauth.php"; + $this->AuthSessionUrl = $this->remote . "oauth_get_session_info.php"; + $this->return_uri = $this->request->getUri()->getBaseUrl() . $this->router->pathFor('auth_return'); - if(isset($_SESSION['site_private_token'])){ + if (isset($_SESSION['site_private_token'])) { $this->site_private_token = $_SESSION['site_private_token']; } else { $this->site_private_token = $this->generateToken(TRUE); @@ -38,16 +44,21 @@ public function __construct(ContainerInterface $container) { } } - public function generateToken(bool $secure=TRUE){ + public function generateToken(bool $secure = TRUE) + { $r_bytes = openssl_random_pseudo_bytes(5120, $secure); return hash('sha512', $r_bytes); } - public function auth($request, $response, $args) { - if(!$this->settings['remote_auth']) { - return false; + public function auth($request, $response, $args) + { + if (!$this->settings['remote_auth']) { + return $this->view->render($response, 'base/error.tpl', [ + 'message' => "Authentication not supported", + 'code' => 501, + ]); } - $client = new \GuzzleHttp\Client(); + $client = new Client(); $res = $client->request('GET', $this->AuthUrl, [ 'query' => [ 'site_private_token' => $this->site_private_token, @@ -57,59 +68,62 @@ public function auth($request, $response, $args) { $res = json_decode($res->getBody()); $this->response = $res; - $_SESSION['session_public_token'] = $res->session_public_token; + $_SESSION['session_public_token'] = $res->session_public_token; $_SESSION['session_private_token'] = $res->session_private_token; - $this->session_public_token = $_SESSION['session_public_token']; + $this->session_public_token = $_SESSION['session_public_token']; $this->session_private_token = $_SESSION['session_private_token']; return $this->view->render($response, 'auth/confirm.tpl'); } - public function auth_redirect(){ - $this->session_public_token = $_SESSION['session_public_token']; + public function auth_redirect($request, $response, $args) + { + $this->session_public_token = $_SESSION['session_public_token']; $this->session_private_token = $_SESSION['session_private_token']; - return $this->response->withRedirect("$this->TokenUrl?session_public_token=".urlencode($this->session_public_token)); + return $response->withRedirect("$this->TokenUrl?session_public_token=" . urlencode($this->session_public_token)); } - public function auth_return($request, $response, $args) { + public function auth_return($request, $response, $args) + { $this->site_private_token = $_SESSION['site_private_token']; $this->session_private_token = $_SESSION['session_private_token']; - $client = new \GuzzleHttp\Client(); + $client = new Client(); $res = $client->request('GET', $this->AuthSessionUrl, [ - 'query'=> [ - 'site_private_token' => $this->site_private_token, + 'query' => [ + 'site_private_token' => $this->site_private_token, 'session_private_token' => $this->session_private_token, ] ]); $res = json_decode($res->getBody()); - if('OK' != $res->status){ + if ('OK' != $res->status) { die("Something went wrong!"); } - foreach($res as $k => $v){ + foreach ($res as $k => $v) { $_SESSION['sb'][$k] = $v; } - if(isset($_SESSION['return_uri'])) { + if (isset($_SESSION['return_uri'])) { $return_uri = $_SESSION['return_uri']; } else { $return_uri = false; } - return $this->view->render($response, 'auth/return.tpl',[ + return $this->view->render($response, 'auth/return.tpl', [ 'return_uri' => $return_uri ]); } - public function doubleCheckRemote() { + public function doubleCheckRemote() + { if ($_SESSION['canary'] < time() - 300) { - $client = new \GuzzleHttp\Client(); + $client = new Client(); $res = $client->request('GET', $this->AuthSessionUrl, [ - 'query'=> [ - 'site_private_token' => $this->site_private_token, + 'query' => [ + 'site_private_token' => $this->site_private_token, 'session_private_token' => $this->session_private_token, ] ]); $res = json_decode($res->getBody()); - if('OK' != $res->status){ + if ('OK' != $res->status) { session_unset(); session_destroy(); session_start(); @@ -120,7 +134,8 @@ public function doubleCheckRemote() { } } - public function logout($request, $response, $args) { + public function logout($request, $response, $args) + { $_SESSION = ''; session_destroy(); $this->user = null; diff --git a/src/Statbus/Controllers/BanController.php b/src/Statbus/Controllers/BanController.php index d842de0..ac16630 100755 --- a/src/Statbus/Controllers/BanController.php +++ b/src/Statbus/Controllers/BanController.php @@ -4,15 +4,19 @@ use Psr\Container\ContainerInterface; use Statbus\Controllers\Controller as Controller; -use Statbus\Models\Player as Player; -class BanController extends Controller { - public function __construct(ContainerInterface $container) { +use stdClass; + +class BanController extends Controller +{ + public function __construct(ContainerInterface $container) + { parent::__construct($container); } - public function getPlayerStanding($ckey){ - $standing = new \stdclass; + public function getPlayerStanding($ckey) + { + $standing = new stdClass; $standing->bans = $this->DB->run(" SELECT B.role, B.id, @@ -21,13 +25,13 @@ public function getPlayerStanding($ckey){ WHERE ckey = ? AND ((B.expiration_time > NOW() AND B.unbanned_ckey IS NULL) OR (B.expiration_time IS NULL AND B.unbanned_ckey IS NULL))", $ckey); - foreach($standing->bans as &$b){ + foreach ($standing->bans as &$b) { //Loop through all the active bans we found //If there's no expiration time set, flag it as perma $b->perm = (isset($b->expiration_time)) ? FALSE : TRUE; //If we find a ban with a perma flag set, and if it's a server role, //exit the loop. We got what we needed. - if ($b->perm && 'Server' === $b->role){ + if ($b->perm && 'Server' === $b->role) { $standing->class = "perma"; $standing->text = "Permabanned"; continue; @@ -36,9 +40,9 @@ public function getPlayerStanding($ckey){ $standing->text = "Active Bans"; } } - if(!$standing->bans){ + if (!$standing->bans) { $standing->class = 'success'; - $standing->text = 'Not Banned'; + $standing->text = 'Not Banned'; return $standing; } return $standing; diff --git a/src/Statbus/Controllers/Controller.php b/src/Statbus/Controllers/Controller.php index 66bdd9f..1958272 100755 --- a/src/Statbus/Controllers/Controller.php +++ b/src/Statbus/Controllers/Controller.php @@ -2,14 +2,18 @@ namespace Statbus\Controllers; +use ParagonIE\EasyDB\EasyDB; use Psr\Container\ContainerInterface; +use Slim\Router; +use Statbus\Extensions\PrefixedDB; -class Controller { +class Controller +{ - protected $container; + protected ContainerInterface $container; protected $view; - protected $DB; - protected $router; + protected PrefixedDB $DB; + protected Router $router; protected $settings; public $page = 1; @@ -19,7 +23,11 @@ class Controller { public $breadcrumbs = []; public $ogdata = []; - public function __construct(ContainerInterface $container) { + public $request; + public $response; + + public function __construct(ContainerInterface $container) + { $this->container = $container; $this->DB = $this->container->get('DB'); $this->view = $this->container->get('view'); @@ -28,15 +36,15 @@ public function __construct(ContainerInterface $container) { $this->response = $this->container->get('response'); $this->ogdata = [ 'site_name' => $this->container->get('settings')['statbus']['app_name'], - 'url' => $this->request->getUri(), - 'type' => 'object', - 'title' => $this->container->get('settings')['statbus']['app_name'], - 'image' => 'https://shiptest.net/images/shiptest-logo.png' + 'url' => $this->request->getUri(), + 'type' => 'object', + 'title' => $this->container->get('settings')['statbus']['app_name'], + 'image' => 'https://shiptest.net/images/shiptest-logo.png' ]; $this->view->getEnvironment()->addGlobal('ogdata', $this->ogdata); $this->view->getEnvironment()->addGlobal('settings', $this->container->get('settings')['statbus']); - if(!$this->DB){ - $error = $this->view->render($this->response, 'base/error_critical.tpl',[ + if (!$this->DB) { + $error = $this->view->render($this->response, 'base/error_critical.tpl', [ 'message' => "Unable to establish a connection to the statistics database.", 'text' => 'This means that the game server database is down, or otherwise unreachable. This error has been logged and your Statbus administrators have been made aware of the issue.', 'code' => 500, @@ -47,8 +55,9 @@ public function __construct(ContainerInterface $container) { } } - public function getFullURL($path){ + public function getFullURL($path) + { $base = str_replace("/stats", "", trim($this->request->getUri()->getBaseUrl(), '/')); - return $base.$path; + return $base . $path; } } diff --git a/src/Statbus/Controllers/DBController.php b/src/Statbus/Controllers/DBController.php index 67ebbf8..8fee850 100755 --- a/src/Statbus/Controllers/DBController.php +++ b/src/Statbus/Controllers/DBController.php @@ -3,9 +3,12 @@ namespace Statbus\Controllers; use ParagonIE\EasyDB\Exception\ConstructorFailed as CFException; +use PDO; +use Statbus\Extensions\PrefixedDB as DB; + +class DBController +{ -class DBController { - private $database; private $username; private $password; @@ -15,40 +18,40 @@ class DBController { public $db = null; - public function __construct(array $conn){ + public function __construct(array $conn) + { $this->database = $conn['database']; $this->username = $conn['username']; $this->password = $conn['password']; - $this->port = $conn['port']; - $this->host = $conn['host']; - $this->prefix = $conn['prefix']; + $this->port = $conn['port']; + $this->host = $conn['host']; + $this->prefix = $conn['prefix']; $options = [ - \PDO::ATTR_PERSISTENT => TRUE, - \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, - \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_OBJ, - \PDO::ATTR_STRINGIFY_FETCHES => FALSE, - \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE, - \PDO::MYSQL_ATTR_COMPRESS => TRUE, - \PDO::ATTR_TIMEOUT => 5, - 'TABLE_PREFIX' => $this->prefix + PDO::ATTR_PERSISTENT => TRUE, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ, + PDO::ATTR_STRINGIFY_FETCHES => FALSE, + PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE, + PDO::MYSQL_ATTR_COMPRESS => TRUE, + PDO::ATTR_TIMEOUT => 5 ]; - try{ - $this->db = \ParagonIE\EasyDB\Factory::create( - "mysql:host=$this->host;port=$this->port;dbname=$this->database", - $this->username, - $this->password, - $options + try { + $this->db = new DB( + "mysql:host=$this->host;port=$this->port;dbname=$this->database", + $this->username, + $this->password, + $options, + $this->prefix ); - } catch (CFException $e){ - if(isset($conn['canFail']) && $conn['canFail']){ + } catch (CFException $e) { + if (isset($conn['canFail']) && $conn['canFail']) { $this->db = false; - } - else { + } else { return $e->getMessage(); } } - + } } \ No newline at end of file diff --git a/src/Statbus/Controllers/DeathController.php b/src/Statbus/Controllers/DeathController.php index 2868401..ba2b2b7 100755 --- a/src/Statbus/Controllers/DeathController.php +++ b/src/Statbus/Controllers/DeathController.php @@ -6,9 +6,13 @@ use Statbus\Models\Death as Death; use Statbus\Controllers\Controller as Controller; -class DeathController Extends Controller{ +class DeathController extends Controller +{ + private Death $deathModel; - public function __construct(ContainerInterface $container) { + + public function __construct(ContainerInterface $container) + { parent::__construct($container); $this->router = $this->container->get('router'); @@ -23,8 +27,9 @@ public function __construct(ContainerInterface $container) { $this->url = $this->router->pathFor('death.index'); } - public function index($request, $response, $args) { - if(isset($args['page'])) { + public function index($request, $response, $args) + { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); } $deaths = $this->DB->run("SELECT @@ -36,7 +41,6 @@ public function index($request, $response, $args) { tbl_death.server_port AS port, tbl_death.server_ip AS ip, tbl_death.round_id AS round, - tbl_death.mapname, tbl_death.tod, tbl_death.job, tbl_death.special, @@ -58,32 +62,34 @@ public function index($request, $response, $args) { WHERE tbl_round.end_datetime IS NOT NULL ORDER BY tbl_death.tod DESC LIMIT ?,?", ($this->page * $this->per_page) - $this->per_page, $this->per_page); - foreach ($deaths as &$death){ + foreach ($deaths as &$death) { $death = $this->deathModel->parseDeath($death); } - return $this->view->render($response, 'death/listing.tpl',[ - 'deaths' => $deaths, - 'death' => $this, - 'wide' => true, + return $this->view->render($response, 'death/listing.tpl', [ + 'deaths' => $deaths, + 'death' => $this, + 'wide' => true, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function DeathsForRound($request, $response, $args) { - if(isset($args['round'])) { + public function DeathsForRound($request, $response, $args) + { + if (isset($args['round'])) { $round = filter_var($args['round'], FILTER_VALIDATE_INT); } - if(isset($args['page'])) { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); - } $format = null; - if(isset($request->getQueryParams()['format'])) { - $format = filter_var($request->getQueryParams()['format'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); } - if(!$format){ - $this->pages = ceil($this->DB->cell("SELECT count(tbl_death.id) FROM tbl_death WHERE tbl_death.round_id = ?", $round) / $this->per_page); + $format = null; + if (isset($request->getQueryParams()['format'])) { + $format = htmlspecialchars($request->getQueryParams()['format']); + } + if (!$format) { + $this->pages = ceil($this->DB->cell("SELECT count(tbl_death.id) FROM tbl_death WHERE tbl_death.round_id = ?", $round) / $this->per_page); } else { - $this->per_page = 1000; + $this->per_page = 1000; } $deaths = $this->DB->run("SELECT tbl_death.id, @@ -94,7 +100,6 @@ public function DeathsForRound($request, $response, $args) { tbl_death.server_port AS port, tbl_death.server_ip AS ip, tbl_death.round_id AS round, - tbl_death.mapname, tbl_death.tod, tbl_death.job, tbl_death.special, @@ -116,36 +121,37 @@ public function DeathsForRound($request, $response, $args) { WHERE tbl_round.end_datetime IS NOT NULL AND tbl_death.round_id = ? ORDER BY tbl_death.tod DESC - LIMIT ?,?", - $round, - ($this->page * $this->per_page) - $this->per_page, - $this->per_page - ); - foreach ($deaths as &$death){ + LIMIT ?,?", + $round, + ($this->page * $this->per_page) - $this->per_page, + $this->per_page + ); + foreach ($deaths as &$death) { $death = $this->deathModel->parseDeath($death); } - if('json' === $format){ - return $response->withJson($deaths); + if ('json' === $format) { + return $response->withJson($deaths); } - $url = parent::getFullURL($this->router->pathFor('round.single',['id'=>$args['round']])); + $url = parent::getFullURL($this->router->pathFor('round.single', ['id' => $args['round']])); $this->ogdata['url'] = $url; $this->ogdata['title'] = "Deaths for Round #$round"; - $this->breadcrumbs['Round '.$args['round']] = $url; + $this->breadcrumbs['Round ' . $args['round']] = $url; - $this->url = $this->router->pathFor('death.round',['round'=>$args['round']]); + $this->url = $this->router->pathFor('death.round', ['round' => $args['round']]); - return $this->view->render($response, 'death/listing.tpl',[ - 'deaths' => $deaths, - 'death' => $this, - 'wide' => true, + return $this->view->render($response, 'death/listing.tpl', [ + 'deaths' => $deaths, + 'death' => $this, + 'wide' => true, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function single($request, $response, $args) { - $id = filter_var($args['id'],FILTER_VALIDATE_INT); - $death = $this->DB->row("SELECT + public function single($request, $response, $args) + { + $id = filter_var($args['id'], FILTER_VALIDATE_INT); + $death = $this->DB->rowObj("SELECT tbl_death.id, tbl_death.pod, tbl_death.x_coord AS x, @@ -154,7 +160,6 @@ public function single($request, $response, $args) { tbl_death.server_port AS port, tbl_death.server_ip AS ip, tbl_death.round_id AS round, - tbl_death.mapname, tbl_death.tod, tbl_death.job, tbl_death.special, @@ -175,28 +180,41 @@ public function single($request, $response, $args) { LEFT JOIN tbl_round ON tbl_round.id = tbl_death.round_id WHERE tbl_round.shutdown_datetime IS NOT NULL AND tbl_death.id = ?", $id); + + if (!$death->id) { + return $this->view->render($response, 'base/error.tpl', [ + 'message' => "Death #$id not found.", + 'code' => 404, + 'linkText' => "Back to all deaths", + 'link' => parent::getFullURL($this->router->pathFor( + 'death.index' + )) + ]); + } + $death = $this->deathModel->parseDeath($death); - $url = parent::getFullURL($this->router->pathFor('death.single',['id'=>$death->id])); + $url = parent::getFullURL($this->router->pathFor('death.single', ['id' => $death->id])); $this->breadcrumbs[$death->id] = $url; - if($death->lakey) { + if ($death->lakey) { $this->ogdata['title'] = "RIP $death->name - $death->tod, murdered by $death->laname"; } else { $this->ogdata['title'] = "RIP $death->name - $death->tod"; } - $this->ogdata['description']= "At $death->mapname's $death->pod during round $death->round. "; - if($death->last_words) { - $this->ogdata['description'].= "Their last words were '$death->last_words'. "; + $this->ogdata['description'] = "At $death->pod during round $death->round. "; + if ($death->last_words) { + $this->ogdata['description'] .= "Their last words were '$death->last_words'. "; } - $this->ogdata['description'].= "Cause of death: $death->cause"; + $this->ogdata['description'] .= "Cause of death: $death->cause"; $this->ogdata['url'] = $url; - return $this->view->render($response, 'death/death.tpl',[ - 'death' => $death, + return $this->view->render($response, 'death/death.tpl', [ + 'death' => $death, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function lastWords($request, $response, $args) { + public function lastWords($request, $response, $args) + { $deaths = $this->DB->run("SELECT tbl_death.id, tbl_death.last_words @@ -208,13 +226,14 @@ public function lastWords($request, $response, $args) { ORDER BY RAND() LIMIT 0, 1000"); $this->breadcrumbs['Last Words'] = $this->router->pathFor('death.lastwords'); - return $this->view->render($response, 'death/lastwords.tpl',[ - 'deaths' => $deaths, + return $this->view->render($response, 'death/lastwords.tpl', [ + 'deaths' => $deaths, 'breadcrumbs' => $this->breadcrumbs ]); } - public function deathMap($round){ + public function deathMap($round) + { $deaths = $this->DB->run("SELECT tbl_death.id, tbl_death.pod, diff --git a/src/Statbus/Controllers/LibraryController.php b/src/Statbus/Controllers/LibraryController.php index 0a2377d..b1d444a 100755 --- a/src/Statbus/Controllers/LibraryController.php +++ b/src/Statbus/Controllers/LibraryController.php @@ -2,14 +2,28 @@ namespace Statbus\Controllers; +use ParagonIE\EasyDB\EasyDB; +use ParagonIE\EasyDB\EasyStatement; use Psr\Container\ContainerInterface; +use GuzzleHttp\Client as HttpClient; +use Slim\Csrf\Guard; use Statbus\Controllers\Controller as Controller; +use Statbus\Extensions\PrefixedDB; use Statbus\Models\Library as Library; use Statbus\Controllers\UserController as User; +use GuzzleHttp\Exception\ClientException as GCeption; -class LibraryController Extends Controller { - - public function __construct(ContainerInterface $container) { +class LibraryController extends Controller +{ + private string $query; + private Library $libraryModel; + private HttpClient $guzzle; + + private PrefixedDB $altDB; + private Guard $csrf; + + public function __construct(ContainerInterface $container) + { parent::__construct($container); $this->pages = ceil($this->DB->cell("SELECT count(tbl_library.id) FROM tbl_library WHERE tbl_library.content != '' AND (tbl_library.deleted IS NULL OR tbl_library.deleted = 0)") / $this->per_page); @@ -20,15 +34,18 @@ public function __construct(ContainerInterface $container) { $this->breadcrumbs['Library'] = $this->router->pathFor('library.index'); $this->url = $this->router->pathFor('library.index'); $this->query = false; + + $this->altDB = $this->container->get('ALT_DB'); } - public function index($request, $response, $args) { - if(isset($args['page'])) { + public function index($request, $response, $args) + { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); } - $this->query = filter_var($request->getQueryParams()['query'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); - if($this->query) { - $statement = \ParagonIE\EasyDB\EasyStatement::open()->andWith('AND tbl_library.content like ?', '%'.$this->DB->escapeLikeValue($this->query).'%'); + $this->query = htmlspecialchars($request->getQueryParams()['query'] ?? ''); + if ($this->query) { + $statement = EasyStatement::open()->andWith('AND tbl_library.content like ?', '%' . $this->DB->escapeLikeValue($this->query) . '%'); $this->pages = ceil($this->DB->cell("SELECT count(tbl_library.id) FROM tbl_library @@ -61,29 +78,31 @@ public function index($request, $response, $args) { AND (tbl_library.deleted IS NULL OR tbl_library.deleted = 0) ORDER BY tbl_library.datetime DESC LIMIT ?,?", ($this->page * $this->per_page) - $this->per_page, $this->per_page); - } + } foreach ($books as &$book) { $book = $this->libraryModel->parseBook($book); } - return $this->view->render($response, 'library/listing.tpl',[ - 'books' => $books, - 'library' => $this, + return $this->view->render($response, 'library/listing.tpl', [ + 'books' => $books, + 'library' => $this, 'breadcrumbs' => $this->breadcrumbs ]); } - public function single($request, $response, $args) { + public function single($request, $response, $args) + { $book = $this->getBook($args['id']); - return $this->view->render($response, 'library/single.tpl',[ - 'book' => $book, + return $this->view->render($response, 'library/single.tpl', [ + 'book' => $book, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function getBook(int $id){ + public function getBook(int $id) + { $id = filter_var($id, FILTER_VALIDATE_INT); - $book = $this->DB->row("SELECT + $book = $this->DB->rowObj("SELECT L.id, L.author, L.title, @@ -97,8 +116,8 @@ public function getBook(int $id){ FROM tbl_library AS L WHERE L.id = ?", $id); $book = $this->libraryModel->parseBook($book); - $url = parent::getFullURL($this->router->pathFor('library.single',['id'=>$book->id])); - if(!$book->deleted) { + $url = parent::getFullURL($this->router->pathFor('library.single', ['id' => $book->id])); + if (!$book->deleted) { $this->breadcrumbs[$book->title] = $url; } else { $this->breadcrumbs['[Book Deleted]'] = $url; @@ -106,81 +125,85 @@ public function getBook(int $id){ return $book; } - public function deleteBook($request, $response, $args){ + public function deleteBook($request, $response, $args) + { $id = filter_var($args['id'], FILTER_VALIDATE_INT); $book = $this->getBook($id); - $url = parent::getFullURL($this->router->pathFor('library.single',['id'=>$book->id])); - if(FALSE === $request->getAttribute('csrf_status')){ - return $this->view->render($response, 'base/error.tpl',[ - 'message' => "CSRF failure. This action is denied.", - 'code' => 403, - 'link' => $url, + $url = parent::getFullURL($this->router->pathFor('library.single', ['id' => $book->id])); + if (FALSE === $request->getAttribute('csrf_status')) { + return $this->view->render($response, 'base/error.tpl', [ + 'message' => "CSRF failure. This action is denied.", + 'code' => 403, + 'link' => $url, 'linkText' => 'Back' ]); } - $user = (new User($this->container))->fetchUser(); + $user = $this->container->get('user'); if (!$user->canAccessTGDB) { - return $this->view->render($response, 'base/error.tpl',[ + return $this->view->render($response, 'base/error.tpl', [ 'message' => "You do not have permission to access this page.", - 'code' => 403 + 'code' => 403 ]); } $delete = TRUE; $action = 'F451'; $text = "Deleted book $id"; - if($book->deleted){ + if ($book->deleted) { $delete = FALSE; $action = 'F452'; $text = "Undeleted book $id"; } - $this->DB->update('tbl_library',[ + $this->DB->update('tbl_library', [ 'deleted' => $delete - ],[ + ], [ 'id' => $id ]); $book = $this->getBook($id); (new StatbusController($this->container))->submitToAuditLog($action, $text); - return $this->view->render($response, 'library/single.tpl',[ - 'book' => $book, + return $this->view->render($response, 'library/single.tpl', [ + 'book' => $book, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function artGallery($request, $response, $args){ - $this->alt_db = $this->container->get('ALT_DB'); - $servers = $this->container->get('settings')['statbus']['servers']; - if(isset($args['server'])){ + public function artGallery($request, $response, $args) + { + $servers = array_filter($this->container->get('settings')['statbus']['servers'], function ($server) { + return isset($server['public_logs']); + }); + if (isset($args['server'])) { $server = ucfirst($args['server']); - if($server = $servers[array_search($server, array_column($servers,'name'))]){ - $json_url = str_replace("logs", "", $server['public_logs'].'paintings.json'); - try{ + if ($server = $servers[array_search($server, array_column($servers, 'name'))]) { + $json_url = str_replace("logs", "", $server['public_logs'] . 'paintings.json'); + try { $res = $this->guzzle->request('GET', $json_url); - } catch (GCeption $e){ + } catch (GCeption $e) { return false; } $art = $res->getBody()->getContents(); - if(!$art){ + if (!$art) { return false; } $art = json_decode($art); - if($this->alt_db) { + if ($this->altDB) { $art = $this->mapVotes($server['name'], $art); // $this->spamVotes($server['name'], $art); } - if('GET' === $request->getMethod()){ //Just browsing - return $this->view->render($response, 'gallery/gallery.tpl',[ + $this->csrf = $this->container->get('csrf'); + if ('GET' === $request->getMethod()) { //Just browsing + return $this->view->render($response, 'gallery/gallery.tpl', [ 'art' => get_object_vars($art), 'url' => str_replace('logs/', 'paintings/', $server['public_logs']), + 'csrf' => $this->csrf->generateToken(), 'server' => $server ]); - } elseif ($this->alt_db && 'POST' === $request->getMethod()){ - if(false === $request->getAttribute('csrf_status')){ + } elseif ($this->altDB && 'POST' === $request->getMethod()) { + if (false === $request->getAttribute('csrf_status')) { $response = $response->withStatus(403); return $response->withJson(json_encode('CSRF failed')); } - $this->csrf = $this->container->get('csrf'); - $user = (new User($this->container))->fetchUser(); + $user = $this->container->get('user'); $data = $request->getParsedBody(); $data['server'] = $server['name']; $data['ckey'] = $user->ckey; @@ -189,76 +212,65 @@ public function artGallery($request, $response, $args){ } } } - return $this->view->render($response, 'gallery/index.tpl',[ + return $this->view->render($response, 'gallery/index.tpl', [ 'servers' => $servers ]); } - private function castVote($data){ + private function castVote($data) + { - $this->alt_db->run("INSERT INTO art_vote (artwork, rating, ckey, `server`) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE rating = ?", - $data['artwork'], - $data['rating'], - $data['ckey'], - $data['server'], - $data['rating']); + $this->altDB->run( + "INSERT INTO art_vote (artwork, rating, ckey, `server`) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE rating = ?", + $data['artwork'], + $data['rating'], + $data['ckey'], + $data['server'], + $data['rating'] + ); - $this->csrf = new \Statbus\Extensions\CsrfExtension($this->container->get('csrf')); - $return['csrf'] = $this->csrf->getGlobals(); + $return['csrf'] = $this->csrf->generateToken(); $return['votes'] = $this->getArtRating($data['artwork']); return $return; } - private function getArtRating($artwork){ - return $this->alt_db->row("SELECT format(avg(rating),2) as rating, artwork, count(id) as votes FROM art_vote WHERE artwork = ?;", $artwork); + private function getArtRating($artwork) + { + return $this->altDB->rowObj("SELECT format(avg(rating),2) as rating, artwork, count(id) as votes FROM art_vote WHERE artwork = ?;", $artwork); } - private function mapVotes($server, $art){ - $votes = $this->alt_db->run("SELECT format(avg(rating),2) as rating, artwork, count(id) as votes FROM art_vote WHERE `server` = ? GROUP BY artwork;",$server); - foreach($art->library as &$a){ - $a->rating = '?'; - $a->votes = 'No votes'; - foreach($votes as $v){ - if($v->artwork === $a->md5){ - $a->rating = (float) $v->rating; - $a->votes = $v->votes; - } - } - } - foreach($art->library_private as &$a){ - $a->rating = '?'; - $a->votes = 'No votes'; - foreach($votes as $v){ - if($v->artwork === $a->md5){ - $a->rating = (float) $v->rating; - $a->votes = $v->votes; - } - } - } - foreach($art->library_secure as &$a){ - $a->rating = '?'; - $a->votes = 'No votes'; - foreach($votes as $v){ - if($v->artwork === $a->md5){ - $a->rating = (float) $v->rating; - $a->votes = $v->votes; + private function mapVotes($server, $art) + { + $votes = $this->altDB->run("SELECT format(avg(rating),2) as rating, artwork, count(id) as votes FROM art_vote WHERE `server` = ? GROUP BY artwork;", $server); + foreach ($art as &$gallery) { + foreach ($gallery as &$a) { + $a->rating = '?'; + $a->votes = 'No votes'; + foreach ($votes as $v) { + if ($v->artwork === $a->md5) { + $a->rating = (float) $v->rating; + $a->votes = $v->votes; + } } } } return $art; } - private function spamVotes($server, $art){ - while ($i < 1000){ - $this->alt_db->run("INSERT INTO art_vote (artwork, rating, ckey, `server`) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE rating = ?", - pick($art->library)->md5, - (int) floor(rand(1,5)), - substr(hash('sha512',base64_encode(random_bytes(16))),0,16), - $server, - (int) floor(rand(1,5))); + private function spamVotes($server, $art) + { + while ($i < 1000) { + $this->altDB->run( + "INSERT INTO art_vote (artwork, rating, ckey, `server`) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE rating = ?", + pick($art->library)->md5, + (int) floor(rand(1, 5)), + substr(hash('sha512', base64_encode(random_bytes(16))), 0, 16), + $server, + (int) floor(rand(1, 5)) + ); $i++; } - + } } diff --git a/src/Statbus/Controllers/LogsController.php b/src/Statbus/Controllers/LogsController.php index a94cab5..6de365e 100755 --- a/src/Statbus/Controllers/LogsController.php +++ b/src/Statbus/Controllers/LogsController.php @@ -2,231 +2,257 @@ namespace Statbus\Controllers; +use Exception; +use GuzzleHttp\Client; +use GuzzleHttp\RequestOptions; +use ParagonIE\EasyDB\EasyDB; use Psr\Container\ContainerInterface; +use RecursiveDirectoryIterator; use Statbus\Controllers\Controller as Controller; -use Statbus\Controllers\DBController as DBController; use GuzzleHttp\Exception\ClientException as GCeption; -class LogsController Extends Controller { +class LogsController extends Controller +{ private $hash; private $round; private $hasLogs = false; private $file; + private string $zip; + private EasyDB $altDB; public $listing; - public function __construct(ContainerInterface $container, $round) { + public function __construct(ContainerInterface $container, $round) + { parent::__construct($container); - $settings = $this->container->get('settings'); $this->round = $round; $this->hash = hash('sha256', $this->round->remote_logs); - $this->zip = ROOTDIR."/tmp/logs/$this->hash.zip"; + $this->zip = ROOTDIR . "/tmp/logs/$this->hash.zip"; $this->hasLogs = $this->ensureLocalLogs(); - $this->alt_db = (new DBController($settings['database']['alt']))->db; + $this->altDB = $this->container->get('ALT_DB'); } - private function ensureLocalLogs(){ - if(file_exists($this->zip)){ + private function ensureLocalLogs() + { + if (file_exists($this->zip)) { return true; } - try{ - $client = new \GuzzleHttp\Client(); - $res = $client->request('GET',$this->round->remote_logs,[ - 'headers' => ['Accept-Encoding' => 'gzip'], - 'curl' => [ + try { + $client = new Client(); + $res = $client->request('GET', $this->round->remote_logs, [ + RequestOptions::HTTP_ERRORS => false, + 'headers' => ['Accept-Encoding' => 'gzip'], + 'curl' => [ CURLOPT_FOLLOWLOCATION => TRUE, CURLOPT_REFERER => "shiptest.net", ] ]); - } catch (GCeption $e){ + } catch (Exception $e) { return false; } $logs = $res->getBody()->getContents(); - if(!$logs){ + if (!$logs) { return false; } - try{ + try { $handle = fopen($this->zip, 'w'); fwrite($handle, $logs); fclose($handle); - } catch(Excention $e){ - die($e->getMessage()); + } catch (Exception $e) { + return false; } return true; } - public function listing(){ - $files = new \RecursiveDirectoryIterator("phar://".$this->zip); - foreach ($files as $name => $file){ - if (!$file->isFile()) continue; + public function listing() + { + if (!$this->hasLogs) { + return []; + } + + $files = new RecursiveDirectoryIterator("phar://" . $this->zip); + foreach ($files as $name => $file) { + if (!$file->isFile()) + continue; $name = str_replace("phar://$this->zip/$file/", '', $name); $this->listing[$name] = $file; } return $this->listing; } - public function getFile($file, $format = false){ - if (!in_array($file, [ - 'asset.log', - 'atmos.html', - 'attack.log', - 'config_error.log', - 'cargo.html', - 'gravity.html', - 'hallucinations.html', - 'initialize.log', - 'job_debug.log', - 'manifest.log', - 'map_errors.log', - 'newscaster.json', - 'overlay.log', - 'pda.log', - 'portals.html', - 'profiler.json', - 'qdel.log', - 'radiation.html', - 'research.html', - 'round_end_data.html', - 'round_end_data.json', - 'runtime.log', - 'sendmaps.json', - 'singulo.html', - 'shuttle.log', - 'telecomms.log', - 'supermatter.html', - 'wires.html', - ])) { + public function getFile($file, $format = false) + { + if ( + !in_array($file, [ + 'asset.log', + 'atmos.html', + 'attack.log', + 'config_error.log', + 'cargo.html', + 'gravity.html', + 'hallucinations.html', + 'initialize.log', + 'job_debug.log', + 'manifest.log', + 'map_errors.log', + 'newscaster.json', + 'overlay.log', + 'pda.log', + 'portals.html', + 'profiler.json', + 'qdel.log', + 'radiation.html', + 'research.html', + 'round_end_data.html', + 'round_end_data.json', + 'runtime.log', + 'sendmaps.json', + 'singulo.html', + 'shuttle.log', + 'sql.log', + 'telecomms.log', + 'supermatter.html', + 'wires.html', + ]) + ) { return false; } - if(file_exists("phar://".$this->zip.'/'.$file)){ - $this->file = file_get_contents("phar://".$this->zip."/".$file); + if (file_exists("phar://" . $this->zip . '/' . $file)) { + $this->file = file_get_contents("phar://" . $this->zip . "/" . $file); } - if(in_array($file,[ - 'newscaster.json' - ])){ - $this->file = strip_tags($this->file,'
'); + if ( + in_array($file, [ + 'newscaster.json' + ]) + ) { + $this->file = strip_tags($this->file, '
'); } else { - $this->file = filter_var($this->file, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_NO_ENCODE_QUOTES); + $this->file = filter_var($this->file, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_NO_ENCODE_QUOTES); } - switch($format){ + switch ($format) { case 'raw': return $this->file; - break; - - case 'json': - $this->parseLogFile($file); - return $this->file; - break; default: $this->parseLogFile($file); return $this->file; - break; } } - private function parseLogFile($file){ - switch($file){ + private function parseLogFile($file) + { + switch ($file) { case 'atmos.html': - $this->genericLogParse('atmos.html'); - $this->parseAtmosLogs(); - break; + $this->genericLogParse(); + break; case 'gravity.html': - $this->genericLogParse('gravity.html'); - break; + $this->genericLogParse(); + break; case 'hallucinations.html': - $this->genericLogParse('hallucinations.html'); - break; + $this->genericLogParse(); + break; case 'manifest.log': $this->parseManifest(); - break; + break; case 'newscaster.json': $this->parseNewscaster(); - break; + break; case 'pda.log': $this->parsePDA(); - break; + break; case 'profiler.json': $this->parseProfiler(); - break; + break; case 'records.html': - $this->genericLogParse('records.html'); + $this->genericLogParse(); $this->parseRecords(); - break; + break; case 'research.html': - $this->genericLogParse('research.html'); - break; + $this->genericLogParse(); + break; case 'singulo.html': - $this->genericLogParse('singulo.html'); - break; + $this->genericLogParse(); + break; case 'supermatter.html': - $this->genericLogParse('supermatter.html'); - break; + $this->genericLogParse(); + break; case 'wires.html': - $this->genericLogParse('wires.html'); - break; + $this->genericLogParse(); + break; case 'runtime.log': - $this->genericTxtLogParse('runtime.log'); - break; + $this->genericTxtLogParse(); + break; case 'shuttle.log': - $this->genericTxtLogParse('shuttle.log'); - break; + $this->genericTxtLogParse(); + break; case 'map_errors.log': - $this->genericTxtLogParse('map_errors.log'); - break; + $this->genericTxtLogParse(); + break; case 'config_error.log': - $this->genericTxtLogParse('config_error.log'); - break; + $this->genericTxtLogParse(); + break; - default: - $this->genericTxtLogParse($file); - break; + case 'overlay.log': + $this->parseOverlays(); + break; + + case 'qdel.log': + $this->parseDels(); + break; } } - private function parseAtmosLogs(){ + private function parseAtmosLogs() + { } - private function parseProfiler(){ + private function parseProfiler() + { $profile = json_decode($this->file); - foreach ($profile as $k => $v){ - if (0 === $v->self && 0 === $v->total && 0 === $v->real && 0 === $v->over && 1 === $v->calls){ + foreach ($profile as $k => $v) { + if (0.25 > $v->self && 0.25 > $v->total && 0.25 > $v->real && 0.25 > $v->over && 25 > $v->calls) { unset($profile[$k]); } + $v->selfAvg = round($v->self / $v->calls, 6); + $v->totalAvg = round($v->total / $v->calls, 6); + $v->realAvg = round($v->real / $v->calls, 6); + $v->overAvg = round($v->over / $v->calls, 6); } $this->file = $profile; } - private function parseRecords(){ - foreach ($this->file as &$l){ + private function parseRecords() + { + foreach ($this->file as &$l) { $l['text'] = str_replace(' None ', "
None
", $l['text']); $l['text'] = str_replace('*Arrest*', "
Arrest
", $l['text']); $l['text'] = str_replace('Incarcerated', "
Incarcerated
", $l['text']); $l['text'] = str_replace('Discharged', "
Discharged
", $l['text']); } - return $file; + return $this->file; } - private function genericLogParse($file){ + private function genericLogParse() + { $lines = []; $matches = []; preg_match_all("/(\d\d:\d\d:\d\d) \[(\S*)\] \((\d{1,3}),(\d{1,3}),(\d{1,3})\) \|\| (.*)$/mi", $this->file, $matches, PREG_SET_ORDER); - foreach ($matches as $tmp){ + foreach ($matches as $tmp) { $entry = []; $entry['timestamp'] = $tmp[1]; $entry['device'] = $tmp[2]; @@ -240,33 +266,78 @@ private function genericLogParse($file){ $this->file = $lines; } - private function genericTxtLogParse($file){ + private function genericTxtLogParse() + { $lines = []; $matches = []; - preg_match_all("/\[(?:\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}.\d{3})\] ([a-zA-Z# ]*: )(.*(?:\n -.*)*)/mi", $this->file, $matches, PREG_SET_ORDER); - foreach ($matches as $tmp){ + preg_match_all("/\[(?:\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}.\d{3})\] ([a-zA-Z# ]*: )([a-zA-Z# ]*: )?(.*(?:\n -.*)*)/mi", $this->file, $matches, PREG_SET_ORDER); + foreach ($matches as $tmp) { $entry = []; $entry['timestamp'] = $tmp[1]; - $entry['device'] = $tmp[2]; - $entry['text'] = $tmp[3]; - $entry['color'] = substr(sha1($entry['device']), 0, 6); + $entry['category'] = $tmp[2]; + $entry['info'] = $tmp[3]; + $entry['text'] = $tmp[4]; + $entry['color'] = substr(sha1($entry['category']), 0, 6); + $entry['infocolor'] = substr(sha1($entry['info']), 0, 6); + $lines[] = $entry; + } + $this->file = $lines; + } + + private function parseOverlays() + { + $lines = []; + $matches = []; + preg_match_all("/(\/[A-z\/]+) => ([0-9.]+ms) \(([0-9]+)\) \(avg:(0\.[0-9\[\]a-z]+)\)/mi", $this->file, $matches, PREG_SET_ORDER); + foreach ($matches as $tmp) { + $entry = []; + $entry['timestamp'] = $tmp[2]; + $entry['category'] = $tmp[1]; + $entry['info'] = $tmp[3] . ' overlays'; + $entry['text'] = $tmp[4]; + $lines[] = $entry; + } + $this->file = $lines; + } + + private function parseDels() + { + $lines = []; + $matches = []; + preg_match_all("/^ - Path: ([\/A-z]+)\n - \tFailures: ([0-9]+)\n - \tqdel\(\) Count: ([0-9]+)\n - \tDestroy\(\) Cost: ([0-9.]+)ms\n - \tTotal Hard Deletes: ([0-9]+)\n - \tTime Spent Hard Deleting: ([0-9.]+)ms\n - \tHighest Time Spent Hard Deleting: ([0-9.]+)ms/mi", $this->file, $matches, PREG_SET_ORDER); + foreach ($matches as $tmp) { + $entry = []; + $entry['path'] = $tmp[1]; + + $entry['destroyTime'] = $tmp[4]; + + $entry['totalTime'] = $tmp[6]; + $entry['longestTime'] = $tmp[7]; + + $entry['failures'] = $tmp[2]; + $entry['harddels'] = $tmp[5]; + $entry['totalDel'] = $tmp[3]; + $entry['sortRatio'] = $tmp[2] / $tmp[3]; + $lines[] = $entry; } $this->file = $lines; } - private function parseManifest(){ + private function parseManifest() + { $lines = []; $matches = []; preg_match_all("/\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3})\] ([a-zA-Z0-9]+) \\\\ (.*) \\\\ (.*) \\\\ (.*) \\\\ (ROUNDSTART|LATEJOIN)$/mi", $this->file, $matches, PREG_SET_ORDER); - foreach ($matches as $tmp){ + foreach ($matches as $tmp) { $entry = []; $entry['timestamp'] = $tmp[1]; $entry['ckey'] = $tmp[2]; $entry['character'] = $tmp[3]; $entry['job'] = $tmp[4]; $entry['special'] = ucwords($tmp[5]); - if('NONE' === $entry['special']) $entry['special'] = false; + if ('NONE' === $entry['special']) + $entry['special'] = false; $entry['when'] = $tmp[6]; ('ROUNDSTART' === $entry['when']) ? $entry['when'] = TRUE : $entry['when'] = FALSE; $lines[] = $entry; @@ -275,189 +346,207 @@ private function parseManifest(){ $this->saveManifest(); } - private function parseNewscaster(){ + private function parseNewscaster() + { $file = json_decode($this->file, TRUE); - foreach ($file as &$c){ - foreach ($c['messages'] as $k => &$v){ - if (!is_array($v)){ + foreach ($file as &$c) { + foreach ($c['messages'] as $k => &$v) { + if (!is_array($v)) { $tmp = $c['messages']; - $tmp['id'] = substr(sha1($tmp['body'].$tmp['time stamp']),0,7); + $tmp['id'] = substr(sha1($tmp['body'] . $tmp['time stamp']), 0, 7); $tmp['body'] = filter_var($tmp['body'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_NO_ENCODE_QUOTES); unset($c['messages']); $c['messages'][] = $tmp; - continue 2; + continue 2; } else { - $v['id'] = substr(sha1($v['body'].$v['time stamp']),0,7); + $v['id'] = substr(sha1($v['body'] . $v['time stamp']), 0, 7); $v['body'] = filter_var($v['body'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_NO_ENCODE_QUOTES); - if('' != $v['photo file']){ - $v['photo file'] = base64_encode(file_get_contents("phar://".$this->zip."/photos/".$v['photo file'].".png")); + if ('' != $v['photo file']) { + $v['photo file'] = base64_encode(file_get_contents("phar://" . $this->zip . "/photos/" . $v['photo file'] . ".png")); } - } + } } } $this->file = $file; } - private function saveManifest(){ - if(!$this->alt_db) return false; - if($this->round->id === $this->alt_db->cell("SELECT round_id FROM manifest WHERE round_id = ?", $this->round->id)) return false; - foreach($this->file as $e){ - try{ - $this->alt_db->insert('manifest',[ - 'round_id' => $this->round->id, - 'name' => $e['character'], - 'ckey' => $e['ckey'], - 'job' => $e['job'], - 'role' => $e['special'], + private function saveManifest() + { + if (!$this->altDB) { + return false; + } + if ($this->round->id === $this->altDB->cell("SELECT round_id FROM manifest WHERE round_id = ?", $this->round->id)) { + return false; + } + foreach ($this->file as $e) { + try { + $this->altDB->insert('manifest', [ + 'round_id' => $this->round->id, + 'name' => $e['character'], + 'ckey' => $e['ckey'], + 'job' => $e['job'], + 'role' => $e['special'], 'roundstart' => (int) $e['when'] ]); - } catch (Exception $e){ + } catch (Exception $e) { var_dump($e->getMessage()); } } } - private function parsePDA(){ + private function parsePDA() + { $lines = []; $matches = []; //EDGECASE: Sometimes the PDA name isn't specified $this->file = str_replace(") (PDA: PDA to ", ") (PDA: Unknown Device PDA to ", $this->file); preg_match_all('/\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3})\] (PDA|COMMENT): (.+?)\/\((.+?)\) \(PDA: (.+?)PDA to (.+?) \((.+?)\)\) "(.+?)" \((.+?) \((\d{1,3}), (\d{1,3}), (\d{1,3})/mi', $this->file, $matches, PREG_SET_ORDER); - foreach ($matches as $tmp){ + foreach ($matches as $tmp) { $entry = []; - $entry['timestamp'] = $tmp[1]; - $entry['type'] = $tmp[2]; - $entry['from_ckey'] = $tmp[3]; + $entry['timestamp'] = $tmp[1]; + $entry['type'] = $tmp[2]; + $entry['from_ckey'] = $tmp[3]; $entry['from_character'] = $tmp[4]; - $entry['from_device'] = ucwords($tmp[5]); - $entry['to_character'] = $tmp[6]; - $entry['to_job'] = $tmp[7]; - $entry['message'] = $tmp[8]; - $entry['from_area'] = $tmp[9]; - $entry['from_x'] = $tmp[10]; - $entry['from_y'] = $tmp[11]; - $entry['from_z'] = $tmp[12]; - $entry['id'] = substr(hash('sha512', $entry['timestamp'].$entry['message']), 0,6); + $entry['from_device'] = ucwords($tmp[5]); + $entry['to_character'] = $tmp[6]; + $entry['to_job'] = $tmp[7]; + $entry['message'] = $tmp[8]; + $entry['from_area'] = $tmp[9]; + $entry['from_x'] = $tmp[10]; + $entry['from_y'] = $tmp[11]; + $entry['from_z'] = $tmp[12]; + $entry['id'] = substr(hash('sha512', $entry['timestamp'] . $entry['message']), 0, 6); $lines[] = $entry; } $this->file = $lines; } - public function getPages(){ - if(!$this->alt_db) return false; - $pages = $this->alt_db->cell("SELECT count(*) FROM round_logs WHERE round = ?", $this->round->id); + public function getPages() + { + if (!$this->altDB) + return false; + $pages = $this->altDB->cell("SELECT count(*) FROM round_logs WHERE round = ?", $this->round->id); $this->pages = ceil($pages / 1000); return ceil($pages / 1000); } - public function getGameLogs(){ - return false; - if(!$this->alt_db) return false; + public function getGameLogs() + { + if (!$this->altDB || !$this->hasLogs) + return false; //if(!$this->checkForLogs()) $this->processGameLogs(); - return $this->alt_db->run("SELECT `timestamp`, `type`, `text`, x, y, z, area, id + return $this->altDB->run("SELECT `timestamp`, `type`, `text`, x, y, z, area, id FROM round_logs WHERE round = ? ORDER BY `timestamp` ASC - LIMIT ?,?", $this->round->id, ($this->round->page*1000) - 1000, 1000); + LIMIT ?,?", $this->round->id, ($this->round->page * 1000) - 1000, 1000); } - public function checkForLogs(){ - return $this->alt_db->cell("SELECT round FROM round_logs WHERE round = ?",$this->round->id); + public function checkForLogs() + { + return $this->altDB->cell("SELECT round FROM round_logs WHERE round = ?", $this->round->id); } - public function processGameLogs(){ + public function processGameLogs() + { + if (!$this->hasLogs) { + return; + } + $i = 0; - $handle = fopen("phar://".$this->zip."/game.log", "r"); + $handle = fopen("phar://" . $this->zip . "/game.log", "r"); if ($handle) { - while (($line = fgets($handle)) !== false) { - $tmp = []; - $entry = []; - if(!preg_match("/\[(\d{4}-\d{2}-\d{2} )?(\d{2}:\d{2}:\d{2})(\.\d{3})?\] ?(\w*): (.*)/", $line, $tmp)) continue; - $entry['type'] = $tmp[2]; - $entry['time'] = $tmp[1]; - $entry['text'] = $tmp[3]; - $entry['x'] = null; - $entry['y'] = null; - $entry['z'] = null; - $entry['area'] = null; - if(strpos($entry['text'], ') (DEAD) "') !== FALSE) { - $entry['type'] = "DSAY"; - } - if(strpos($entry['text'], ') (ghost) "') !== FALSE) { - continue; + while (($line = fgets($handle)) !== false) { + $tmp = []; + $entry = []; + if (!preg_match("/\[(\d{4}-\d{2}-\d{2} )?(\d{2}:\d{2}:\d{2})(\.\d{3})?\] ?(\w*): (.*)/", $line, $tmp)) + continue; + $entry['type'] = $tmp[2]; + $entry['time'] = $tmp[1]; + $entry['text'] = $tmp[3]; + $entry['x'] = null; + $entry['y'] = null; + $entry['z'] = null; + $entry['area'] = null; + if (strpos($entry['text'], ') (DEAD) "') !== FALSE) { + $entry['type'] = "DSAY"; + } + if (strpos($entry['text'], ') (ghost) "') !== FALSE) { + continue; + } + unset($tmp); + if (isset($entry['text'])) { + $t = []; + if (preg_match("/(.*) \((.*)\((\d{1,}), (\d{1,}), (\d{1,})\)\)/", $entry['text'], $t)) { + $entry['text'] = $t[1]; + $entry['x'] = $t[3]; + $entry['y'] = $t[4]; + $entry['z'] = $t[5]; + $entry['area'] = $t[2]; + // var_dump($merge); } - unset($tmp); - if(isset($entry['text'])){ - $t = []; - if(preg_match("/(.*) \((.*)\((\d{1,}), (\d{1,}), (\d{1,})\)\)/", $entry['text'], $t)){ - $entry['text'] = $t[1]; - $entry['x'] = $t[3]; - $entry['y'] = $t[4]; - $entry['z'] = $t[5]; - $entry['area'] = $t[2]; - // var_dump($merge); - } - try{ - $this->alt_db->insert('round_logs',[ - 'round' => $this->round->id, + try { + $this->altDB->insert('round_logs', [ + 'round' => $this->round->id, 'timestamp' => $entry['time'], - 'type' => $entry['type'], - 'text' => filter_var($entry['text'], FILTER_SANITIZE_FULL_SPECIAL_CHARS,FILTER_FLAG_NO_ENCODE_QUOTES), - 'x' => $entry['x'], - 'y' => $entry['y'], - 'z' => $entry['z'], - 'area' => filter_var($entry['area'],FILTER_SANITIZE_FULL_SPECIAL_CHARS,FILTER_FLAG_NO_ENCODE_QUOTES) + 'type' => $entry['type'], + 'text' => filter_var($entry['text'], FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES), + 'x' => $entry['x'], + 'y' => $entry['y'], + 'z' => $entry['z'], + 'area' => filter_var($entry['area'], FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES) ]); - $i++; - } catch (Exception $e){ - echo $e->getMessage(); - } + $i++; + } catch (Exception $e) { + echo $e->getMessage(); } } + } fclose($handle); } - $handle = fopen("phar://".$this->zip."/attack.log", "r"); + $handle = fopen("phar://" . $this->zip . "/attack.log", "r"); if ($handle) { - while (($line = fgets($handle)) !== false) { - $tmp = []; - $entry = []; - if(!preg_match("/\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3})] (\w*): (.*)/", $line, $tmp)) continue; - $entry['type'] = $tmp[2]; - $entry['time'] = $tmp[1]; - $entry['text'] = $tmp[3]; - $entry['x'] = null; - $entry['y'] = null; - $entry['z'] = null; - $entry['area'] = null; - unset($tmp); - if(isset($entry['text'])){ - $t = []; - if(preg_match("/(.*) \((.*)\((\d{1,}), (\d{1,}), (\d{1,})\)\)/", $entry['text'], $t)){ - $entry['text'] = $t[1]; - $entry['x'] = $t[3]; - $entry['y'] = $t[4]; - $entry['z'] = $t[5]; - $entry['area'] = $t[2]; - // var_dump($merge); - } - try{ - $this->alt_db->insert('round_logs',[ - 'round' => $this->round->id, + while (($line = fgets($handle)) !== false) { + $tmp = []; + $entry = []; + if (!preg_match("/\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3})] (\w*): (.*)/", $line, $tmp)) + continue; + $entry['type'] = $tmp[2]; + $entry['time'] = $tmp[1]; + $entry['text'] = $tmp[3]; + $entry['x'] = null; + $entry['y'] = null; + $entry['z'] = null; + $entry['area'] = null; + unset($tmp); + if (isset($entry['text'])) { + $t = []; + if (preg_match("/(.*) \((.*)\((\d{1,}), (\d{1,}), (\d{1,})\)\)/", $entry['text'], $t)) { + $entry['text'] = $t[1]; + $entry['x'] = $t[3]; + $entry['y'] = $t[4]; + $entry['z'] = $t[5]; + $entry['area'] = $t[2]; + // var_dump($merge); + } + try { + $this->altDB->insert('round_logs', [ + 'round' => $this->round->id, 'timestamp' => $entry['time'], - 'type' => $entry['type'], - 'text' => filter_var($entry['text'], FILTER_SANITIZE_FULL_SPECIAL_CHARS,FILTER_FLAG_NO_ENCODE_QUOTES), - 'x' => $entry['x'], - 'y' => $entry['y'], - 'z' => $entry['z'], - 'area' => filter_var($entry['area'], FILTER_SANITIZE_FULL_SPECIAL_CHARS,FILTER_FLAG_NO_ENCODE_QUOTES) + 'type' => $entry['type'], + 'text' => filter_var($entry['text'], FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES), + 'x' => $entry['x'], + 'y' => $entry['y'], + 'z' => $entry['z'], + 'area' => filter_var($entry['area'], FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES) ]); - $i++; - } catch (Exception $e){ - echo $e->getMessage(); - } + $i++; + } catch (Exception $e) { + echo $e->getMessage(); } } + } fclose($handle); } } diff --git a/src/Statbus/Controllers/MessageController.php b/src/Statbus/Controllers/MessageController.php index a958a5c..74411ee 100755 --- a/src/Statbus/Controllers/MessageController.php +++ b/src/Statbus/Controllers/MessageController.php @@ -7,8 +7,10 @@ use Statbus\Models\Messages as Message; use Statbus\Models\Player as Player; -class MessageController extends Controller { - public function __construct(ContainerInterface $container) { +class MessageController extends Controller +{ + public function __construct(ContainerInterface $container) + { parent::__construct($container); $this->messageModel = new Message($this->container->get('settings')['statbus']); $this->pm = new Player($this->container->get('settings')['statbus']); @@ -17,7 +19,8 @@ public function __construct(ContainerInterface $container) { $this->url = $this->router->pathFor('message.index'); } - public function getAdminMemos(){ + public function getAdminMemos() + { $memos = $this->DB->run("SELECT M.id, M.type, @@ -45,8 +48,9 @@ public function getAdminMemos(){ return $memos; } - public function listing($request, $response, $args){ - if(isset($args['page'])) { + public function listing($request, $response, $args) + { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); } $messages = $this->DB->run("SELECT @@ -91,15 +95,16 @@ public function listing($request, $response, $args){ $m->editor->rank = $m->editorrank; $m->editor = $this->pm->parsePlayer($m->editor); } - return $this->view->render($this->response, 'messages/listing.tpl',[ + return $this->view->render($this->response, 'messages/listing.tpl', [ 'messages' => $messages, - 'message' => $this + 'message' => $this ]); } - public function getMessagesForCkey($ckey, $hide_secret = false, $page = 1){ + public function getMessagesForCkey($ckey, $hide_secret = false, $page = 1) + { $secret = ""; - if($hide_secret){ + if ($hide_secret) { $secret = "AND M.SECRET = 0"; } $this->page = filter_var($page, FILTER_VALIDATE_INT); @@ -107,8 +112,9 @@ public function getMessagesForCkey($ckey, $hide_secret = false, $page = 1){ AND (M.expire_timestamp > NOW() OR M.expire_timestamp IS NULL) AND M.targetckey = ? $secret", $ckey) / $this->per_page); - $this->url = $this->router->pathFor('player.messages',['ckey'=>$ckey]); - $messages = $this->DB->run("SELECT + $this->url = $this->router->pathFor('player.messages', ['ckey' => $ckey]); + $messages = $this->DB->run( + "SELECT M.id, M.type, M.adminckey, @@ -134,9 +140,11 @@ public function getMessagesForCkey($ckey, $hide_secret = false, $page = 1){ AND M.targetckey = ? $secret ORDER BY M.timestamp DESC - LIMIT ?,?", $ckey, - ($this->page * $this->per_page) - $this->per_page, - $this->per_page); + LIMIT ?,?", + $ckey, + ($this->page * $this->per_page) - $this->per_page, + $this->per_page + ); foreach ($messages as $m) { $m = $this->messageModel->parseMessage($m); $m->admin = new \stdclass; @@ -157,9 +165,10 @@ public function getMessagesForCkey($ckey, $hide_secret = false, $page = 1){ return $messages; } - public function single($request, $response, $args){ + public function single($request, $response, $args) + { $id = filter_var($args['id'], FILTER_VALIDATE_INT); - $message = $this->DB->row("SELECT + $message = $this->DB->rowObj("SELECT M.id, M.type, M.adminckey, @@ -200,7 +209,7 @@ public function single($request, $response, $args){ $message->editor->rank = $message->editorrank; $message->editor = $this->pm->parsePlayer($message->editor); - return $this->view->render($this->response, 'messages/single.tpl',[ + return $this->view->render($this->response, 'messages/single.tpl', [ 'message' => $message ]); } diff --git a/src/Statbus/Controllers/NameVoteController.php b/src/Statbus/Controllers/NameVoteController.php index 54b086f..bae5fec 100755 --- a/src/Statbus/Controllers/NameVoteController.php +++ b/src/Statbus/Controllers/NameVoteController.php @@ -2,59 +2,79 @@ namespace Statbus\Controllers; +use Exception; use Psr\Container\ContainerInterface; +use Slim\Csrf\Guard; use Statbus\Controllers\Controller as Controller; -// use Statbus\Controllers\User as User; +use Statbus\Extensions\PrefixedDB; +use Statbus\Models\Player; -class NameVoteController Extends Controller { - - public function __construct(ContainerInterface $container) { +class NameVoteController extends Controller +{ + + private PrefixedDB $alt_db; + private Guard $csrf; + private $user; + + public function __construct(ContainerInterface $container) + { parent::__construct($container); $settings = $this->container->get('settings'); - $this->alt_db = (new DBController($settings['database']['alt']))->db; - $this->user = $this->container->get('user')->user; + $this->alt_db = $this->container->get('ALT_DB'); + $this->user = $this->container->get('user'); + + $this->ogdata['title'] = "Name Voter 5000"; } - public function index($request, $response, $args){ - return $this->view->render($response, 'misc/namevote/vote.tpl',[ - 'name' => $this->getname() + public function index($request, $response, $args) + { + $this->csrf = $this->container->get('csrf'); + return $this->view->render($response, 'misc/namevote/vote.tpl', [ + 'name' => $this->getname(), + 'csrf' => $this->csrf->generateToken() ]); } - public function cast($request, $response, $args){ - $args = $request->getParams(); - if(!isset($args)){ - return json_encode(['error'=>'Missing vote arguments']); + public function cast($request, $response, $args) + { + if (false === $request->getAttribute('csrf_status')) { + $response = $response->withStatus(403); + return $response->withJson(json_encode(['CSRF failed', $request->getParsedBody()])); } - if(!$this->user){ - return json_encode(['error'=>'You must be logged in to vote for names!']); - } - if('nay'===$args['vote']) { - $args['vote'] = 0; - } else { - $args['vote'] = 1; + $args = $request->getParsedBody(); + if (!isset($args['vote']) || !isset($args['name'])) { + return $response->withJson(json_encode(['error' => "Missing vote arguments"])); } - if($this->alt_db->row("SELECT name, ckey FROM name_vote WHERE name = ? and ckey = ?",$args['name'], $this->user->ckey)){ - return json_encode(['name'=>$this->getName(),'args'=>$args]); + + $vote = htmlspecialchars($args['vote']); + $name = htmlspecialchars($args['name']); + + $vote = 'nay' === $vote ? 0 : 1; + + if ($this->alt_db->rowObj("SELECT name, ckey FROM name_vote WHERE name = ? and ckey = ?", $args['name'], $this->user->ckey)) { + return $this->index($request, $response, $args); } - try{ - $this->alt_db->insert('name_vote',[ - 'name' => $args['name'], - 'good' => $args['vote'], + + try { + $this->alt_db->insert('name_vote', [ + 'name' => $name, + 'good' => $vote, 'ckey' => $this->user->ckey, ]); - } catch (Exception $e){ - return json_encode(['name'=>$this->getName(), 'args'=>$args]); + } catch (Exception $e) { + return json_encode($e); } - return json_encode(['name'=>$this->getName(),'args'=>$args]); + + return $response->withRedirect($this->router->pathFor('nameVoter')); } - public function rankings($request, $response, $args) { + public function rankings($request, $response, $args) + { $rank = 'best'; - $rank = filter_var($args['rank'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); - if('worst' === $rank){ + $rank = htmlspecialchars($args['rank']); + if ('worst' === $rank) { $ranking = $this->alt_db->run("SELECT `name`, IFNULL(count(good),1) - sum(good) AS `no`, sum(good) AS `yes` @@ -71,15 +91,15 @@ public function rankings($request, $response, $args) { ORDER BY `yes` DESC LIMIT 0, 100;"); } - return $this->view->render($response, 'misc/namevote/results.tpl',[ + return $this->view->render($response, 'misc/namevote/results.tpl', [ 'ranking' => $ranking ]); } - public function getName(){ - $name = $this->DB->row("SELECT DISTINCT `name`, job + public function getName() + { + $name = $this->DB->rowObj("SELECT DISTINCT `name`, job FROM tbl_death - WHERE YEAR(`tod`) = 2018 AND `job` IN ('Assistant', 'Atmospheric Technician', 'Bartender', 'Botanist', 'Captain', 'Cargo Technician', 'Chaplain', 'Chemist', 'Chief Engineer', 'Chief Medical Officer', 'Cook', 'Curator', 'Detective', 'Geneticist', 'Head of Personnel', 'Head of Security', 'Janitor', 'Lawyer', 'Librarian', 'Medical Doctor', 'Quartermaster', 'Research Director', 'Roboticist', 'Scientist', 'Security Officer', 'Shaft Miner', 'Station Engineer', 'Virologist', 'Warden') ORDER BY RAND() LIMIT 0,1;"); return $name; diff --git a/src/Statbus/Controllers/PlayerController.php b/src/Statbus/Controllers/PlayerController.php index 6c14a6b..3aa85fd 100755 --- a/src/Statbus/Controllers/PlayerController.php +++ b/src/Statbus/Controllers/PlayerController.php @@ -1,58 +1,62 @@ sb = $this->container->get('settings')['statbus']; $this->playerModel = new Player($this->sb); - $dbConn = $this->container->get('settings')['database']['alt']; - $this->alt_db = (new DBController($dbConn))->db; + $this->alt_db = $this->container->get('ALT_DB'); } - public function getPlayer($request, $response, $args) { - $ckey = filter_var($args['ckey'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + public function getPlayer($request, $response, $args) + { + $ckey = htmlspecialchars($args['ckey']); $player = $this->getPlayerByCkey($ckey); if (!$player->ckey) { return $this->view->render($this->response, 'base/error.tpl', [ 'message' => "Ckey not found", - 'code' => 404, + 'code' => 404, ]); } $player = $this->gatherAdditionalData($player); $player = $this->playerModel->parsePlayer($player); - return $this->view->render($response, 'player/single.tpl',[ + return $this->view->render($response, 'player/single.tpl', [ 'player' => $player ]); } - public function getPlayerPublic($request, $response, $args) { - $player = $this->getPlayerByCkey(filter_var($args['ckey'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH),TRUE); + public function getPlayerPublic($request, $response, $args) + { + $player = $this->getPlayerByCkey(htmlspecialchars($args['ckey']), TRUE); if (!$player->ckey) { return $this->view->render($this->response, 'base/error.tpl', [ 'message' => "Ckey not found", - 'code' => 404, + 'code' => 404, ]); } $player->achievements = (new AchievementController($this->container))->getPlayerAchievements($player->ckey); - return $this->view->render($response, 'player/public.tpl',[ + return $this->view->render($response, 'player/public.tpl', [ 'player' => $player ]); } - public function getPlayerByCkey($ckey, $public = false){ - if($public){ - return $this->DB->row("SELECT tbl_player.ckey + public function getPlayerByCkey($ckey, $public = false) + { + if ($public) { + return $this->DB->rowObj("SELECT tbl_player.ckey FROM tbl_player WHERE tbl_player.ckey = ?", $ckey); } - return $this->DB->row("SELECT tbl_player.ckey, + return $this->DB->rowObj("SELECT tbl_player.ckey, tbl_player.firstseen, tbl_player.firstseen_round_id, tbl_player.lastseen, @@ -76,58 +80,63 @@ public function getPlayerByCkey($ckey, $public = false){ } - public function getPlayerRoleTime($request, $response, $args) { - if(isset($args['ckey'])){ - $ckey = filter_var($args['ckey'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + public function getPlayerRoleTime($request, $response, $args) + { + if (isset($args['ckey'])) { + $ckey = htmlspecialchars($args['ckey']); } else { - $ckey = $this->container->user->ckey; + $ckey = $this->container->get('user')->ckey; } $player = $this->getPlayerByCkey($ckey); if (!$player->ckey) { return $this->view->render($this->response, 'base/error.tpl', [ 'message' => "Ckey not found", - 'code' => 404, + 'code' => 404, ]); } // $player = $this->gatherAdditionalData($player); $p = $request->getQueryParams(); $start = null; $end = null; - if(isset($p['start']) && isset($p['end'])){ - $start = filter_var($p['start'], FILTER_SANITIZE_STRING, - FILTER_FLAG_STRIP_HIGH); - $end = filter_var($p['end'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + if (isset($p['start']) && isset($p['end'])) { + $start = filter_var( + $p['start'], + FILTER_SANITIZE_STRING, + FILTER_FLAG_STRIP_HIGH + ); + $end = htmlspecialchars($p['end']); } - $jobs = "('".implode("','",$this->sb['jobs'])."')"; - $minmax = $this->DB->row("SELECT + $minmax = $this->DB->rowObj("SELECT min(STR_TO_DATE(R.datetime, '%Y-%m-%d')) AS min, max(STR_TO_DATE(R.datetime, '%Y-%m-%d')) AS max FROM tbl_role_time_log AS R WHERE R.datetime != '0000-00-00 00:00:00' - AND R.job IN $jobs AND ckey = ?;", $ckey); - if(!$start) { + if (!$start) { $start = $minmax->min; $end = $minmax->max; } else { - $startDate = new \dateTime($start); + $startDate = new DateTime($start); $start = $startDate->format('Y-m-d'); - $endDate = new \dateTime($end); + $endDate = new DateTime($end); $end = $endDate->format('Y-m-d'); } - $player->role_time = $this->getRoleData($player->ckey, $start, $end, $jobs); + $player->role_time = $this->getRoleData($player->ckey, $start, $end); $player = $this->playerModel->parsePlayer($player); - return $this->view->render($response, 'player/roles.tpl',[ + #return $response->withJson($player->role_time); + + return $this->view->render($response, 'player/roles.tpl', [ 'player' => $player, - 'start' => $start, - 'end' => $end, - 'min' => $minmax->min, - 'max' => $minmax->max + 'start' => $start, + 'end' => $end, + 'min' => $minmax->min, + 'max' => $minmax->max ]); } - public function getPlayerByIP(int $IP){ - return $this->DB->row("SELECT tbl_player.ckey, + public function getPlayerByIP(int $IP) + { + return $this->DB->rowObj("SELECT tbl_player.ckey, tbl_player.firstseen, tbl_player.firstseen_round_id, tbl_player.lastseen, @@ -150,23 +159,25 @@ public function getPlayerByIP(int $IP){ WHERE tbl_player.ip = ?", $IP); } - public function getRoleData($ckey, $start = null, $end = null, $jobs) { + public function getRoleData($ckey, $start = null, $end = null) + { return json_encode($this->DB->run("SELECT job, SUM(delta) AS minutes FROM tbl_role_time_log WHERE ckey = ? - AND tbl_role_time_log.job IN $jobs AND tbl_role_time_log.datetime BETWEEN ? AND ? GROUP BY job ORDER BY job ASC", $ckey, $start, $end)); } - public function getPlayerNames($ckey) { + public function getPlayerNames($ckey) + { $names['deaths'] = $this->getPlayerNamesFromDeath($ckey); $names['manifest'] = $this->getPlayerNamesFromManifest($ckey); return $names; } - public function getPlayerNamesFromDeath($ckey) { + public function getPlayerNamesFromDeath($ckey) + { return $this->DB->run("SELECT DISTINCT(`name`), count(id) AS `times` FROM tbl_death @@ -177,8 +188,10 @@ public function getPlayerNamesFromDeath($ckey) { LIMIT 0,5;", $ckey); } - public function getPlayerNamesFromManifest($ckey) { - if(!$this->alt_db) return false; + public function getPlayerNamesFromManifest($ckey) + { + if (!$this->alt_db) + return false; return $this->alt_db->run("SELECT DISTINCT(`name`), count(`name`) AS `times` FROM manifest @@ -189,23 +202,26 @@ public function getPlayerNamesFromManifest($ckey) { LIMIT 0,5;", $ckey); } - public function getIPs($key, $value){ + public function getIPs($key, $value) + { $ips = $this->DB->run("SELECT count(id) as connections, ip FROM tbl_connection_log WHERE $key = ? GROUP BY ip;", $value); - foreach($ips as &$ip){ + foreach ($ips as &$ip) { $ip->real = long2ip($ip->ip); } return $ips; } - public function getCIDs($key, $value){ + public function getCIDs($key, $value) + { return $this->DB->run("SELECT count(id) as connections, computerid FROM tbl_connection_log WHERE $key = ? GROUP BY computerid;", $value); } - public function findAlts($ckey){ + public function findAlts($ckey) + { $alts = $this->DB->run("SELECT I.ckey AS ip_alts, C.ckey AS cid_alts @@ -214,7 +230,7 @@ public function findAlts($ckey){ LEFT JOIN tbl_connection_log AS C ON C.computerid = P.computerid AND P.ckey != C.ckey WHERE P.ckey = ? GROUP BY ip_alts, cid_alts;", $ckey); - foreach ($alts as $a){ + foreach ($alts as $a) { $return['ip_alts'][] = $a->ip_alts; $return['cid_alts'][] = $a->cid_alts; } @@ -224,21 +240,24 @@ public function findAlts($ckey){ return $return; } - public function countRounds($ckey){ + public function countRounds($ckey) + { return $this->DB->cell("SELECT count(tbl_round.id) FROM tbl_connection_log LEFT JOIN tbl_round ON tbl_connection_log.round_id = tbl_round.id WHERE tbl_connection_log.ckey = ? AND tbl_round.shutdown_datetime IS NOT NULL", $ckey); } - public function countMessages($ckey){ + public function countMessages($ckey) + { return $this->DB->cell("SELECT count(M.id) FROM tbl_messages M WHERE M.deleted = 0 AND (M.expire_timestamp > NOW() OR M.expire_timestamp IS NULL) AND M.targetckey = ?", $ckey); } - public function gatherAdditionalData(&$player){ + public function gatherAdditionalData(&$player) + { $player->names = $this->getPlayerNames($player->ckey); $player->standing = (new BanController($this->container))->getPlayerStanding($player->ckey); $player->ips = $this->getIPs('ckey', $player->ckey); @@ -249,59 +268,63 @@ public function gatherAdditionalData(&$player){ return $player; } - public function getLastWords($ckey){ + public function getLastWords($ckey) + { return $this->DB->run("SELECT last_words, id FROM tbl_death WHERE byondkey = ? AND last_words IS NOT NULL AND last_words != '';", $ckey); } - public function findCkeys($request, $response, $args){ + public function findCkeys($request, $response, $args) + { $args = $request->getQueryParams(); - $ckey = filter_var($args['ckey'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); - if ($ckey){ + $ckey = htmlspecialchars($args['ckey']); + if ($ckey) { $results = $this->DB->run("SELECT tbl_player.ckey FROM tbl_player WHERE tbl_player.ckey LIKE ? ORDER BY lastseen DESC - LIMIT 0, 15", '%'.$this->DB->escapeLikeValue($ckey).'%'); + LIMIT 0, 15", '%' . $this->DB->escapeLikeValue($ckey) . '%'); return $response->withJson($results); } } - public function getPlayerMessages($request, $response, $args) { + public function getPlayerMessages($request, $response, $args) + { $page = 1; - if(isset($args['page'])) { + if (isset($args['page'])) { $page = filter_var($args['page'], FILTER_VALIDATE_INT); } - if(isset($args['ckey'])){ - $ckey = filter_var($args['ckey'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + if (isset($args['ckey'])) { + $ckey = htmlspecialchars($args['ckey']); } $player = $this->getPlayerByCkey($ckey); if (!$player->ckey) { return $this->view->render($this->response, 'base/error.tpl', [ 'message' => "Ckey not found", - 'code' => 404, + 'code' => 404, ]); } $mc = new MessageController($this->container); $messages = $mc->getMessagesForCkey($player->ckey, FALSE, $page); - $breadcrumbs[$player->ckey] = $this->router->pathFor('player.single',['ckey'=>$player->ckey]); - $breadcrumbs["Messages"] = $this->router->pathFor('player.messages',['ckey'=>$player->ckey]); - return $this->view->render($response, 'messages/player.tpl',[ + $breadcrumbs[$player->ckey] = $this->router->pathFor('player.single', ['ckey' => $player->ckey]); + $breadcrumbs["Messages"] = $this->router->pathFor('player.messages', ['ckey' => $player->ckey]); + return $this->view->render($response, 'messages/player.tpl', [ 'player' => $player, - 'messages' => $messages, + 'messages' => $messages, 'message' => $mc, 'breadcrumbs' => $breadcrumbs ]); } - public function getMyMessages($request, $response, $args) { + public function getMyMessages($request, $response, $args) + { $page = 1; - if(isset($args['page'])) { + if (isset($args['page'])) { $page = filter_var($args['page'], FILTER_VALIDATE_INT); } $player = $this->getPlayerByCkey($this->container->user->ckey); if (!$player->ckey) { return $this->view->render($this->response, 'base/error.tpl', [ 'message' => "Ckey not found", - 'code' => 404, + 'code' => 404, ]); } $mc = new MessageController($this->container); @@ -309,28 +332,29 @@ public function getMyMessages($request, $response, $args) { $mc->url = $this->router->pathFor('me.messages'); $breadcrumbs[$player->ckey] = $this->router->pathFor('me'); $breadcrumbs["Messages"] = $this->router->pathFor('me.messages'); - return $this->view->render($response, 'messages/player.tpl',[ + return $this->view->render($response, 'messages/player.tpl', [ 'player' => $player, - 'messages' => $messages, + 'messages' => $messages, 'message' => $mc, 'breadcrumbs' => $breadcrumbs ]); } - public function name2ckey($request, $response, $args){ - if($request->isPost()){ - $name = filter_var($request->getParam('name'), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); - if ($name){ + public function name2ckey($request, $response, $args) + { + if ($request->isPost()) { + $name = htmlspecialchars($request->getParam('name')); + if ($name) { $results = $this->DB->run("SELECT count(D.id) AS count, D.byondkey, D.name FROM tbl_death D WHERE D.name LIKE ? GROUP BY `name` - LIMIT 0, 15", '%'.$this->DB->escapeLikeValue($name).'%'); + LIMIT 0, 15", '%' . $this->DB->escapeLikeValue($name) . '%'); } } - return $this->view->render($response, 'tgdb/name2ckey.tpl',[ + return $this->view->render($response, 'tgdb/name2ckey.tpl', [ 'results' => $results, 'name' => $name ]); diff --git a/src/Statbus/Controllers/PollController.php b/src/Statbus/Controllers/PollController.php index 3754113..e915898 100755 --- a/src/Statbus/Controllers/PollController.php +++ b/src/Statbus/Controllers/PollController.php @@ -6,9 +6,11 @@ use Statbus\Controllers\Controller as Controller; use Statbus\Models\Poll as Poll; -class PollController Extends Controller { - - public function __construct(ContainerInterface $container) { +class PollController extends Controller +{ + + public function __construct(ContainerInterface $container) + { parent::__construct($container); $this->pages = ceil($this->DB->cell("SELECT count(tbl_poll_question.id) FROM tbl_poll_question WHERE tbl_poll_question.adminonly != 1 AND tbl_poll_question.dontshow IS NULL") / $this->per_page); @@ -18,8 +20,9 @@ public function __construct(ContainerInterface $container) { } - public function index($request, $response, $args){ - if(isset($args['page'])) { + public function index($request, $response, $args) + { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); } $polls = $this->DB->run("SELECT P.*, @@ -34,31 +37,33 @@ public function index($request, $response, $args){ GROUP BY P.id ORDER BY P.id DESC LIMIT ?,?", ($this->page * $this->per_page) - $this->per_page, $this->per_page); - foreach($polls as &$p){ + foreach ($polls as &$p) { $p = $this->pollModel->parsePoll($p); } - return $this->view->render($response, 'polls/listing.tpl',[ - 'polls' => $polls, - 'poll' => $this, + return $this->view->render($response, 'polls/listing.tpl', [ + 'polls' => $polls, + 'poll' => $this, 'breadcrumbs' => $this->breadcrumbs ]); } - public function single($request, $response, $args) { + public function single($request, $response, $args) + { $poll = $this->getPoll($args['id']); - $url = parent::getFullURL($this->router->pathFor('poll.single',['id'=>"#$poll->id"])); + $url = parent::getFullURL($this->router->pathFor('poll.single', ['id' => "#$poll->id"])); $this->breadcrumbs[$poll->id] = $url; - return $this->view->render($response, 'polls/single.tpl',[ - 'poll' => $poll, + return $this->view->render($response, 'polls/single.tpl', [ + 'poll' => $poll, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function getPoll($id){ + public function getPoll($id) + { $id = filter_var($id, FILTER_VALIDATE_INT); - $poll = $this->DB->row("SELECT tbl_poll_question.*, + $poll = $this->DB->rowObj("SELECT tbl_poll_question.*, SEC_TO_TIME(TIMESTAMPDIFF(SECOND, tbl_poll_question.starttime, tbl_poll_question.endtime)) AS duration, IF(tbl_poll_question.endtime < NOW(), 1, 0) AS ended, count(tbl_poll_vote.id) + count(tbl_poll_textreply.id) as totalVotes @@ -71,10 +76,10 @@ public function getPoll($id){ GROUP BY tbl_poll_question.id ORDER BY tbl_poll_question.id DESC", $id); - if('TEXT' == $poll->polltype){ + if ('TEXT' == $poll->polltype) { $poll->results = $this->DB->run("SELECT * FROM tbl_poll_textreply WHERE pollid = ?", $poll->id); } else { - if (!$filter){ + if (!$filter) { $poll->results = $this->DB->run("SELECT COUNT(tbl_poll_vote.id) AS votes, tbl_poll_option.text AS `option` FROM tbl_poll_vote diff --git a/src/Statbus/Controllers/RoundController.php b/src/Statbus/Controllers/RoundController.php index e052184..6d54973 100755 --- a/src/Statbus/Controllers/RoundController.php +++ b/src/Statbus/Controllers/RoundController.php @@ -2,14 +2,16 @@ namespace Statbus\Controllers; +use DateTime; use Psr\Container\ContainerInterface; use Statbus\Controllers\Controller as Controller; use Statbus\Models\Round as Round; use Statbus\Controllers\StatController as StatController; -use Statbus\Controllers\DeathController as DeathContorller; use Statbus\Controllers\LogsController as LogsController; +use Statbus\Controllers\UserController as UserController; -class RoundController Extends Controller { +class RoundController extends Controller +{ private $columns = "tbl_round.id, tbl_round.initialize_datetime, @@ -29,19 +31,24 @@ class RoundController Extends Controller { SEC_TO_TIME(TIMESTAMPDIFF(SECOND, tbl_round.start_datetime, tbl_round.end_datetime)) AS round_duration, SEC_TO_TIME(TIMESTAMPDIFF(SECOND, tbl_round.initialize_datetime, tbl_round.start_datetime)) AS init_time, SEC_TO_TIME(TIMESTAMPDIFF(SECOND, tbl_round.end_datetime, tbl_round.shutdown_datetime)) AS shutdown_time"; - - public function __construct(ContainerInterface $container) { + + private Round $roundModel; + private StatController $statController; + + public function __construct(ContainerInterface $container) + { parent::__construct($container); - $this->pages = ceil($this->DB->cell("SELECT count(tbl_round.id) FROM tbl_round") / $this->per_page); - $this->sc = new StatController($this->container); + $this->pages = ceil($this->DB->cell("SELECT count(tbl_round.id) FROM tbl_round WHERE tbl_round.end_state IS NOT NULL") / $this->per_page); + $this->statController = new StatController($this->container); $this->roundModel = new Round($this->container->get('settings')['statbus']); $this->breadcrumbs['Rounds'] = $this->router->pathFor('round.index'); } - public function index($request, $response, $args) { - if(isset($args['page'])) { + public function index($request, $response, $args) + { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); } $rounds = $this->DB->run("SELECT $this->columns @@ -50,112 +57,138 @@ public function index($request, $response, $args) { ORDER BY tbl_round.shutdown_datetime DESC LIMIT ?,?", ($this->page * $this->per_page) - $this->per_page, $this->per_page); - foreach ($rounds as &$round){ + foreach ($rounds as &$round) { $round = $this->roundModel->parseRound($round); } - if($rounds){ + if ($rounds) { $this->firstListing = $rounds[0]->end_datetime; $this->lastListing = end($rounds)->start_datetime; } else { $this->firstListing = null; $this->lastListing = null; } - return $this->view->render($response, 'rounds/listing.tpl',[ - 'rounds' => $rounds, - 'round' => $this, - 'wide' => true, + return $this->view->render($response, 'rounds/listing.tpl', [ + 'rounds' => $rounds, + 'round' => $this, + 'wide' => true, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function single($request, $response, $args) { - $round = $this->getRound($args['id']); - if(!$round->id) { - return $this->view->render($response, 'base/error.tpl',[ - 'code' => 404, + public function single($request, $response, $args) + { + $id = $args['id']; + if ($id == 'latest') { + $values = $this->DB->run("SELECT MAX(id) as id FROM tbl_round WHERE shutdown_datetime IS NOT NULL;"); + + if (count($values) != 1) { + return $this->view->render($response, 'base/error.tpl', [ + 'code' => 404, + 'message' => 'Unable to find latest round', + 'link' => $this->router->pathFor('round.index'), + 'linkText' => 'Round Listing', + 'ogdata' => $this->ogdata + ]); + } + + $id = $values[0]->id; + } + + $round = $this->getRound($id); + if (!$round->id) { + return $this->view->render($response, 'base/error.tpl', [ + 'code' => 404, 'message' => 'Round not found, or is ongoing', - 'link' => $this->router->pathFor('round.index'), - 'linkText'=> 'Round Listing', - 'ogdata' => $this->ogdata + 'link' => $this->router->pathFor('round.index'), + 'linkText' => 'Round Listing', + 'ogdata' => $this->ogdata ]); } $format = null; - if(isset($request->getQueryParams()['format'])) { - $format = filter_var($request->getQueryParams()['format'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + if (isset($request->getQueryParams()['format'])) { + $format = htmlspecialchars($request->getQueryParams()['format']); } - if(isset($args['stat'])){ - $this->stat($round, $args['stat'], $response); + if (isset($args['stat'])) { + $this->stat($round, $args['stat'], $response, $format); - if('json' === $format){ + if ('json' === $format) { unset($round->stat->json); return $response->withJson($round->stat); } return $round->stat; } - $round->stats = $this->sc->getStatsForRound($round->id); - $round->data = $this->sc->getStatsForRound($round->id,[ + $round->stats = $this->statController->getStatsForRound($round->id); + $round->data = $this->statController->getStatsForRound($round->id, [ 'nuclear_challenge_mode', 'testmerged_prs', 'newscaster_stories' ]); - if('json' === $format){ + if ('json' === $format) { return $response->withJson($round); } - return $this->view->render($response, 'rounds/round.tpl',[ - 'round' => $round, + return $this->view->render($response, 'rounds/round.tpl', [ + 'round' => $round, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function stat(object $round, string $stat, $response){ - $stat = filter_var($stat, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + public function stat(object $round, string $stat, $response, $format) + { + $stat = htmlspecialchars($stat); $round->stat = (new StatController($this->container))->getRoundStat($round->id, $stat); - $url = parent::getFullURL($this->router->pathFor('round.single',[ - 'id' =>$round->id, - 'stat' =>$stat + $url = parent::getFullURL($this->router->pathFor('round.single', [ + 'id' => $round->id, + 'stat' => $stat ])); $this->breadcrumbs[$stat] = $url; $this->ogdata['url'] = $url; $this->ogdata['description'] = "Stats for $stat from round $round->id on $round->server"; - return $this->view->render($response, 'stats/single.tpl',[ - 'round' => $round, - 'stat' => $round->stat, + + if ($format === 'json') { + return $response->withJson($stat); + } + + return $this->view->render($response, 'stats/single.tpl', [ + 'round' => $round, + 'stat' => $round->stat, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function stationNames($request, $response, $args){ + public function stationNames($request, $response, $args) + { $names = $this->DB->run("SELECT station_name, id FROM tbl_round WHERE station_name IS NOT NULL ORDER BY RAND() DESC LIMIT 0, 1000;"); - return $this->view->render($response, 'rounds/stationnames.tpl',[ - 'names' => $names, - 'ogdata' => $this->ogdata + return $this->view->render($response, 'rounds/stationnames.tpl', [ + 'names' => $names, + 'ogdata' => $this->ogdata ]); } - public function mapView($request, $response, $args){ + public function mapView($request, $response, $args) + { $round = $this->getRound($args['id']); - $this->breadcrumbs['Map'] = $this->router->pathFor('round.map',[ - 'id' =>$round->id, + $this->breadcrumbs['Map'] = $this->router->pathFor('round.map', [ + 'id' => $round->id, ]); - return $this->view->render($response, 'rounds/map.tpl',[ - 'round' => $round, + return $this->view->render($response, 'rounds/map.tpl', [ + 'round' => $round, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function getRound(int $id){ - (new StatbusController($this->container))->submitToAuditLog('RND', "did a thing"); + public function getRound(int $id) + { $id = filter_var($id, FILTER_VALIDATE_INT); - $round = $this->DB->row("SELECT $this->columns, + $round = $this->DB->rowObj("SELECT $this->columns, MAX(next.id) AS next, MAX(prev.id) AS prev, - COUNT(D.id) AS deaths, - COUNT(T.id) as tickets + COUNT(DISTINCT D.id) AS deaths, + COUNT(DISTINCT T.id) as tickets FROM tbl_round LEFT JOIN tbl_round AS next ON next.id = tbl_round.id + 1 LEFT JOIN tbl_round AS prev ON prev.id = tbl_round.id - 1 @@ -164,7 +197,7 @@ public function getRound(int $id){ WHERE tbl_round.id = ? AND tbl_round.shutdown_datetime IS NOT NULL", $id); $round = $this->roundModel->parseRound($round); - $url = parent::getFullURL($this->router->pathFor('round.single',['id'=>$round->id])); + $url = parent::getFullURL($this->router->pathFor('round.single', ['id' => $round->id])); $this->breadcrumbs[$round->id] = $url; $this->ogdata['url'] = $url; $this->ogdata['title'] = "Round #$round->id on $round->server"; @@ -172,145 +205,268 @@ public function getRound(int $id){ return $round; } - public function listLogs($request, $response, $args){ + public function listLogs($request, $response, $args) + { $round = $this->getRound($args['id']); - if(isset($request->getQueryParams()['format'])) { - $format = filter_var($request->getQueryParams()['format'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + if (isset($request->getQueryParams()['format'])) { + $format = htmlspecialchars($request->getQueryParams()['format']); } $logs = (new LogsController($this->container, $round))->listing(); - if('json' === $format){ + if (count($logs) == 0) { + return $this->view->render($response, 'base/error.tpl', [ + 'code' => 404, + 'link' => $this->router->pathFor('round.single', [ + 'id' => $round->id + ]), + 'linkText' => "Back to round " . $round->id, + 'message' => "Logfiles not found for round $round->id", + ]); + } + + if ('json' === $format) { return $response->withJson($logs); } - $url = parent::getFullURL($this->router->pathFor('round.logs',['id'=>$round->id])); + $url = parent::getFullURL($this->router->pathFor('round.logs', ['id' => $round->id])); $this->breadcrumbs['Logs'] = $url; $this->ogdata['url'] = $url; $this->ogdata['title'] = "Log listing for round #$round->id on $round->server"; - $this->ogdata['description'] = (($logs) ? count($logs) : 0)." log files available"; + $this->ogdata['description'] = (($logs) ? count($logs) : 0) . " log files available"; - return $this->view->render($response, 'rounds/logs.tpl',[ - 'round' => $round, + return $this->view->render($response, 'rounds/logs.tpl', [ + 'round' => $round, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata, - 'logs' => $logs + 'ogdata' => $this->ogdata, + 'logs' => $logs ]); } - public function getLogFile($request, $response, $args){ + public function getLogFile($request, $response, $args) + { $round = $this->getRound($args['id']); - $file = filter_var($args['file'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); - if(isset($args['format'])) { - $format = filter_var($args['format'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + $file = htmlspecialchars($args['file']); + $format = ''; + if (isset($args['format'])) { + $format = htmlspecialchars($args['format']); } $logs = (new LogsController($this->container, $round))->getFile($file, $format); - if('json' === $format){ + if ($format === 'json') { return $response->withJson($logs); } - $this->breadcrumbs['Logs'] = parent::getFullURL($this->router->pathFor('round.logs',[ - 'id' => $round->id, + $this->breadcrumbs['Logs'] = parent::getFullURL($this->router->pathFor('round.logs', [ + 'id' => $round->id, ])); - $url = parent::getFullURL($this->router->pathFor('round.log',[ - 'id' => $round->id, + $url = parent::getFullURL($this->router->pathFor('round.log', [ + 'id' => $round->id, 'file' => $file ])); - + $this->breadcrumbs[$file] = $url; $this->ogdata['url'] = $url; $this->ogdata['title'] = "$file logfile for Round #$round->id on $round->server"; #$this->ogdata['description'] = (($logs) ? count($logs) : 0)." lines found in $file."; - if(!$logs){ - return $this->view->render($response, 'base/error.tpl',[ - 'code' => 404, + if (!$logs) { + return $this->view->render($response, 'base/error.tpl', [ + 'code' => 404, + 'link' => $this->router->pathFor('round.logs', [ + 'id' => $round->id + ]), + 'linkText' => "Back to logs for round " . $round->id, 'message' => "Logfile not found: $file", ]); } - return $this->view->render($response, 'rounds/log.tpl',[ - 'round' => $round, + return $this->view->render($response, 'rounds/log.tpl', [ + 'round' => $round, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata, - 'file' => $logs, - 'filename' => $file, - 'format' => $format, - 'wide' => true + 'ogdata' => $this->ogdata, + 'file' => $logs, + 'filename' => $file, + 'format' => $format, + 'wide' => true ]); } - public function getGameLogs($request, $response, $args){ + public function getGameLogs($request, $response, $args) + { $file = 'game.log'; $round = $this->getRound($args['id']); - if(isset($args['page'])) { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); } $round->page = $this->page; $this->pages = 1; - $logs = (new LogsController($this->container, $round))->getGameLogs($this->page); + $logs = (new LogsController($this->container, $round))->getGameLogs(); $round->pages = (new LogsController($this->container, $round))->getPages(); - if(!$logs){ - return $this->view->render($response, 'base/error.tpl',[ - 'message' => "Alt DB not configured. No parsed logs available", - 'code' => 500, + if (!$logs) { + return $this->view->render($response, 'base/error.tpl', [ + 'message' => "Alt DB not configured. No parsed logs available", + 'code' => 500, 'linkText' => "Back", - 'link' => parent::getFullURL($this->router->pathFor('round.single',[ - 'id' =>$round->id, - ] - )) + 'link' => parent::getFullURL($this->router->pathFor( + 'round.single', + [ + 'id' => $round->id, + ] + )) ]); } - $url = parent::getFullURL($this->router->pathFor('round.log',[ - 'id' => $round->id, + $url = parent::getFullURL($this->router->pathFor('round.log', [ + 'id' => $round->id, 'file' => $file ])); - $this->breadcrumbs['Logs'] = parent::getFullURL($this->router->pathFor('round.logs',['id'=>$round->id])); - + $this->breadcrumbs['Logs'] = parent::getFullURL($this->router->pathFor('round.logs', ['id' => $round->id])); + $this->breadcrumbs['Parsed Game Logs'] = $url; - return $this->view->render($response, 'rounds/log.tpl',[ - 'round' => $round, + return $this->view->render($response, 'rounds/log.tpl', [ + 'round' => $round, + 'breadcrumbs' => $this->breadcrumbs, + 'ogdata' => $this->ogdata, + 'file' => $logs, + 'filename' => $file, + 'wide' => true + ]); + } + + public function getRoundsWithTestmerge($request, $response, $args) + { + $pr = 0; + $data = new \stdClass; + $data->page = 1; + + if (isset($args['page'])) { + $data->page = filter_var($args['page'], FILTER_VALIDATE_INT); + } + if (isset($args['pr'])) { + $pr = filter_var($args['pr'], FILTER_VALIDATE_INT); + } + + $data->pages = ceil($this->DB->cell("SELECT count(tbl_feedback.id) FROM tbl_feedback WHERE key_name = 'testmerged_prs' AND JSON_CONTAINS(JSON_EXTRACT(tbl_feedback.json, '$.data.*.number'), JSON_QUOTE(?), '$')", $pr) / $this->per_page); + + if ($data->pages == 0) { + return $this->view->render($response, 'base/error.tpl', [ + 'message' => "PR #$pr not found.", + 'code' => 404, + 'linkText' => "Back to all testmerges", + 'link' => parent::getFullURL($this->router->pathFor( + 'stat.collate', + [ + 'stat' => 'testmerged_prs', + 'version' => 2 + ] + )) + ]); + } + + $rounds = $this->DB->run("SELECT $this->columns, + tbl_feedback.json as prdata + FROM tbl_feedback + LEFT JOIN tbl_round ON tbl_feedback.round_id = tbl_round.id + WHERE key_name = 'testmerged_prs' + AND JSON_CONTAINS(JSON_EXTRACT(tbl_feedback.json, '$.data.*.number'), JSON_QUOTE(?), '$') + ORDER BY round_id DESC + LIMIT ?,?", $pr, ($this->page * $this->per_page) - $this->per_page, $this->per_page); + foreach ($rounds as &$round) { + $round = $this->roundModel->parseRound($round); + } + + $data->prdata = json_decode($rounds[0]->prdata, true)['data']; + + $data->last_date = $rounds[0]->initialize_datetime; + $data->first_date = end($rounds)->initialize_datetime; + $data->first_round = $rounds[0]->id; + $data->last_round = end($rounds)->id; + + $data->prdata = current(array_filter($data->prdata, function ($v) use ($pr) { + return $v["number"] == (string) $pr; + })); + + $data->rounds = $rounds; + + + $format = null; + if (isset($request->getQueryParams()['format'])) { + $format = htmlspecialchars($request->getQueryParams()['format']); + } + if ($format === 'json') { + return $response->withJson($data); + } + + $this->breadcrumbs = []; + $this->breadcrumbs['Testmerges'] = $this->router->pathFor( + 'stat.collate', + [ + 'stat' => 'testmerged_prs', + 'version' => 2 + ] + ); + $this->breadcrumbs[$pr] = $this->router->pathFor( + 'stat.testmerge', + [ + 'pr' => $pr + ] + ); + + + $this->ogdata['title'] = "Testmerge Rounds for PR #$pr"; + $this->ogdata['description'] = $data->prdata["title"] . " testmerged in " . count($rounds) . " rounds."; + + return $this->view->render($response, 'stats/testmerge.tpl', [ + 'data' => $data, + 'rounds' => $rounds, + 'round' => $this, + 'wide' => true, + 'pr' => $pr, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata, - 'file' => $logs, - 'filename' => $file, - 'wide' => true + 'ogdata' => $this->ogdata ]); } - public function getRoundsWithStat($request, $response, $args){ - if(isset($args['page'])) { + public function getRoundsWithStat($request, $response, $args) + { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); } - if(isset($args['stat'])) { - $stat = filter_var($args['stat'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + if (isset($args['stat'])) { + $stat = htmlspecialchars($args['stat']); + } + $version = 1; + if (isset($args['version'])) { + $version = filter_var($args['version'], FILTER_VALIDATE_INT); } - $this->pages = ceil($this->DB->cell("SELECT count(tbl_feedback.id) FROM tbl_feedback WHERE key_name = ?", $stat) / $this->per_page); + $this->pages = ceil($this->DB->cell("SELECT count(tbl_feedback.id) FROM tbl_feedback WHERE key_name = ? AND version = ?", $stat, $version) / $this->per_page); $rounds = $this->DB->run("SELECT $this->columns FROM tbl_feedback LEFT JOIN tbl_round ON tbl_feedback.round_id = tbl_round.id WHERE key_name = ? + AND version = ? ORDER BY round_id DESC - LIMIT ?,?", $stat, ($this->page * $this->per_page) - $this->per_page, $this->per_page); - foreach ($rounds as &$round){ + LIMIT ?,?", $stat, $version, ($this->page * $this->per_page) - $this->per_page, $this->per_page); + foreach ($rounds as &$round) { $round = $this->roundModel->parseRound($round); } - return $this->view->render($response, 'stats/rounds.tpl',[ - 'rounds' => $rounds, - 'round' => $this, - 'wide' => true, - 'stat' => $stat, + return $this->view->render($response, 'stats/rounds.tpl', [ + 'rounds' => $rounds, + 'round' => $this, + 'wide' => true, + 'stat' => $stat, + 'version' => $version, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function getRoundsForCkey($ckey){ + public function getRoundsForCkey($ckey) + { $this->pages = ceil($this->DB->cell("SELECT count(tbl_round.id) FROM tbl_connection_log LEFT JOIN tbl_round ON tbl_connection_log.round_id = tbl_round.id WHERE tbl_connection_log.ckey = ? @@ -322,99 +478,106 @@ public function getRoundsForCkey($ckey){ AND tbl_round.shutdown_datetime IS NOT NULL ORDER BY tbl_connection_log.`datetime` DESC LIMIT ?,?", $ckey, ($this->page * $this->per_page) - $this->per_page, $this->per_page); - foreach ($rounds as &$round){ + foreach ($rounds as &$round) { $round = $this->roundModel->parseRound($round); } return $rounds; } - public function getMyRounds($request, $response, $args){ - if(isset($args['page'])) { + public function getMyRounds($request, $response, $args) + { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); } - $this->user = $this->container->get('user'); - $rounds = $this->getRoundsForCkey($this->user->ckey); - return $this->view->render($response, 'me/rounds.tpl',[ - 'rounds' => $rounds, - 'round' => $this, - 'wide' => true, - 'stat' => $stat, + $user = $this->container->get('user'); + $rounds = $this->getRoundsForCkey($user->ckey); + return $this->view->render($response, 'me/rounds.tpl', [ + 'rounds' => $rounds, + 'round' => $this, + 'wide' => true, 'breadcrumbs' => $this->breadcrumbs, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function getPlayerRounds($request, $response, $args){ - if(isset($args['page'])) { + public function getPlayerRounds($request, $response, $args) + { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); } - if(isset($args['ckey'])) { - $ckey = filter_var($args['ckey'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + if (isset($args['ckey'])) { + $ckey = htmlspecialchars($args['ckey']); } $rounds = $this->getRoundsForCkey($ckey); $this->breadcrumbs = [ - $ckey => parent::getFullURL($this->router->pathFor('player.single',[ - 'ckey'=>$ckey + $ckey => parent::getFullURL($this->router->pathFor('player.single', [ + 'ckey' => $ckey ])), - 'Rounds' => parent::getFullURL($this->router->pathFor('player.rounds',[ - 'ckey'=>$ckey + 'Rounds' => parent::getFullURL($this->router->pathFor('player.rounds', [ + 'ckey' => $ckey ])) ]; - return $this->view->render($response, 'player/rounds.tpl',[ - 'rounds' => $rounds, - 'round' => $this, - 'wide' => true, - 'stat' => $stat, + return $this->view->render($response, 'player/rounds.tpl', [ + 'rounds' => $rounds, + 'round' => $this, + 'wide' => true, 'breadcrumbs' => $this->breadcrumbs, - 'ckey' => $ckey, - 'ogdata' => $this->ogdata + 'ckey' => $ckey, + 'ogdata' => $this->ogdata ]); } - public function getActiveRounds($request, $response, $args){ - if(!$this->userCanAccessTGDB){ + public function getActiveRounds($request, $response, $args) + { + $user = $this->container->get('user'); + if (!$user->userCanAccessTGDB) { return false; } $rounds = $this->DB->run("SELECT DISTINCT(*) FROM tbl_round WHERE `end_state` IS NULL LIMIT 0, 4 ORDER BY id DESC"); return $request->withJson($rounds); } - public function winLoss($request, $response, $args){ + public function winLoss($request, $response, $args) + { $p = $request->getQueryParams(); $start = null; $end = null; - if(isset($p['start']) && isset($p['end'])){ - $start = filter_var($p['start'], FILTER_SANITIZE_STRING, - FILTER_FLAG_STRIP_HIGH); - $end = filter_var($p['end'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + if (isset($p['start']) && isset($p['end'])) { + $start = filter_var( + $p['start'], + FILTER_SANITIZE_STRING, + FILTER_FLAG_STRIP_HIGH + ); + $end = htmlspecialchars($p['end']); } - $minmax = $this->DB->row("SELECT + $minmax = $this->DB->rowObj("SELECT min(STR_TO_DATE(R.initialize_datetime, '%Y-%m-%d')) AS min, max(STR_TO_DATE(R.shutdown_datetime, '%Y-%m-%d')) AS max FROM tbl_round AS R WHERE R.shutdown_datetime != '0000-00-00 00:00:00' AND R.initialize_datetime != '0000-00-00 00:00:00'"); - if(!$start) { + if (!$start) { $start = $minmax->min; $end = $minmax->max; } else { - $startDate = new \dateTime($start); + $startDate = new DateTime($start); $start = $startDate->format('Y-m-d'); - $endDate = new \dateTime($end); + $endDate = new DateTime($end); $end = $endDate->format('Y-m-d'); } $data = $this->getWinLossRatios($start, $end); - return $this->view->render($response, 'info/winloss.tpl',[ - 'modes' => $data, - 'start' => $start, - 'end' => $end, - 'min' => $minmax->min, - 'max' => $minmax->max, - 'ogdata' => $this->ogdata - ]); + return $this->view->render($response, 'info/winloss.tpl', [ + 'modes' => $data, + 'start' => $start, + 'end' => $end, + 'min' => $minmax->min, + 'max' => $minmax->max, + 'ogdata' => $this->ogdata + ]); } - public function getWinLossRatios($start = null, $end = null){ + public function getWinLossRatios($start = null, $end = null) + { $data = $this->DB->run("SELECT count(tbl_round.id) AS rounds, tbl_round.game_mode, tbl_round.game_mode_result, @@ -428,9 +591,9 @@ public function getWinLossRatios($start = null, $end = null){ AND tbl_round.shutdown_datetime IS NOT NULL GROUP BY tbl_round.game_mode, tbl_round.game_mode_result ORDER BY tbl_round.game_mode ASC, rounds DESC;", $start, $end); - usort($data, function($a, $b){ - return strcmp($a->game_mode, $b->game_mode); - }); - return $data; + usort($data, function ($a, $b) { + return strcmp($a->game_mode, $b->game_mode); + }); + return $data; } } diff --git a/src/Statbus/Controllers/StatController.php b/src/Statbus/Controllers/StatController.php index 5f2f75d..aca0aea 100755 --- a/src/Statbus/Controllers/StatController.php +++ b/src/Statbus/Controllers/StatController.php @@ -2,38 +2,46 @@ namespace Statbus\Controllers; +use DateTimeImmutable; use Psr\Container\ContainerInterface; use Statbus\Controllers\Controller as Controller; use Statbus\Models\Stat as Stat; -class StatController Extends Controller { +class StatController extends Controller +{ + private Stat $statModel; - public function __construct(ContainerInterface $container) { + public function __construct(ContainerInterface $container) + { parent::__construct($container); $this->statModel = (new Stat()); + + $this->breadcrumbs['All Stats'] = $this->router->pathFor('stat.list'); } - public function getRoundStat($round, $stat, $json = false) { - $stat = $this->DB->row("SELECT * FROM tbl_feedback WHERE round_id = ? AND key_name = ?", $round, $stat); - if(!$stat){ + public function getRoundStat($round, $stat, $json = false) + { + $stat = $this->DB->rowObj("SELECT * FROM tbl_feedback WHERE round_id = ? AND key_name = ?", $round, $stat); + if (!$stat) { return false; } - if($json) { + if ($json) { return $stat->json; } return $this->statModel->parseStat($stat); } - public function getStatsForRound($round, array $stats = null) { - if(is_array($stats)){ + public function getStatsForRound($round, array $stats = null) + { + if (is_array($stats)) { $and = "((key_name = '"; - $and.= implode("') OR (key_name = '", $stats); - $and.= "'))"; + $and .= implode("') OR (key_name = '", $stats); + $and .= "'))"; $stats = $this->DB->run("SELECT * FROM tbl_feedback WHERE $and AND round_id = ?", $round); $tmp = []; - foreach ($stats as &$stat){ + foreach ($stats as &$stat) { $stat = $this->statModel->parseStat($stat); $tmp[$stat->key_name] = $stat; } @@ -41,60 +49,108 @@ public function getStatsForRound($round, array $stats = null) { return $stats; } else { $stats = $this->DB->run("SELECT key_name FROM tbl_feedback WHERE round_id = ? ORDER BY key_name ASC", $round); - if (!$stats) return false; - foreach($stats as $s){ + if (!$stats) + return false; + foreach ($stats as $s) { $tmp[] = $s->key_name; } $stats = array_flip($tmp); return $stats; - } + } } - public function list($request, $response, $args){ + public function list($request, $response, $args) + { $stats = $this->DB->run("SELECT R.key_name, R.key_type, R.version, count(R.round_id) AS rounds FROM tbl_feedback R GROUP BY R.key_name, R.version ORDER BY R.key_name ASC;"); - return $this->view->render($response, 'stats/listing.tpl',[ + + $format = null; + if (isset($request->getQueryParams()['format'])) { + $format = htmlspecialchars($request->getQueryParams()['format']); + } + if ($format === 'json') { + return $response->withJson($stats); + } + + return $this->view->render($response, 'stats/listing.tpl', [ 'stats' => $stats ]); } - public function collate($request, $response, $args){ + public function collate($request, $response, $args) + { $version = 1; - if(isset($args['version'])){ + if (isset($args['version'])) { $version = filter_var($args['version'], FILTER_VALIDATE_INT); } - if(isset($args['stat'])){ - $stat = filter_var($args['stat'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + if (isset($args['stat'])) { + $stat = htmlspecialchars($args['stat']); $p = $request->getQueryParams(); $start = null; $end = null; - if(isset($p['start']) && isset($p['end'])){ - $start = filter_var($p['start'], FILTER_SANITIZE_STRING, - FILTER_FLAG_STRIP_HIGH); - $end = filter_var($p['end'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH); + if (isset($p['start']) && isset($p['end'])) { + $start = htmlspecialchars($p['start']); + $end = htmlspecialchars($p['end']); } } - $minmax = $this->DB->row("SELECT + + $minmax = $this->DB->rowObj("SELECT min(STR_TO_DATE(R.datetime, '%Y-%m-%d')) AS min, - max(STR_TO_DATE(R.datetime, '%Y-%m-%d')) AS max + DATE_ADD(max(STR_TO_DATE(R.datetime, '%Y-%m-%d')), INTERVAL 1 DAY) AS max FROM tbl_feedback AS R WHERE R.key_name = ? AND R.version = ?;", $stat, $version); - if(!$start) { + + if (!$minmax->min) { + return $this->view->render($response, 'base/error.tpl', [ + 'message' => "Stat '$stat' not found.", + 'code' => 404, + 'linkText' => "Back to all stats", + 'link' => parent::getFullURL($this->router->pathFor( + 'stat.list' + )) + ]); + + } + + if (!$start) { + #$start = new DateTimeImmutable($minmax->min); + #$end = new DateTimeImmutable($minmax->max); + #$diff = $start->diff($end)->days; + + #$start = $diff > 60 ? $start->sub(new DateInterval('P30D'))->format('Y-m-d') : $minmax->min; + $start = $minmax->min; $end = $minmax->max; } else { - $startDate = new \dateTime($start); + $startDate = new DateTimeImmutable($start); $start = $startDate->format('Y-m-d'); - $endDate = new \dateTime($end); + $endDate = new DateTimeImmutable($end); $end = $endDate->format('Y-m-d'); } $stat = $this->DB->run("SELECT R.key_name, R.key_type, R.json, R.round_id, R.version, R.datetime FROM tbl_feedback R WHERE R.key_name = ? AND R.version = ? AND R.datetime BETWEEN ? AND ? ORDER BY R.datetime ASC", $stat, $version, $start, $end); $stat = $this->statModel->parseStat($stat, TRUE); - return $this->view->render($response, 'stats/collated.tpl',[ - 'stat' => $stat, + + $format = null; + if (isset($request->getQueryParams()['format'])) { + $format = htmlspecialchars($request->getQueryParams()['format']); + } + if ($format === 'json') { + return $response->withJson($stat); + } + + $this->ogdata['title'] = 'Collated stats for ' . $stat->key_name; + #$this->ogdata['description'] = 'Counting ' . count($stat->data) . ' entries'; + + $this->breadcrumbs[$stat->key_name] = $this->router->pathFor('stat.collate', ['stat' => $stat->key_name, 'version' => $stat->version]); + + return $this->view->render($response, 'stats/collated.tpl', [ + 'stat' => $stat, 'start' => $start, - 'end' => $end, - 'min' => $minmax->min, - 'max' => $minmax->max + 'end' => $end, + 'min' => $minmax->min, + 'max' => $minmax->max, + 'json' => json_encode($stat), + 'breadcrumbs' => $this->breadcrumbs, + 'ogdata' => $this->ogdata ]); } } \ No newline at end of file diff --git a/src/Statbus/Controllers/StatbusController.php b/src/Statbus/Controllers/StatbusController.php index 47abc62..369271d 100755 --- a/src/Statbus/Controllers/StatbusController.php +++ b/src/Statbus/Controllers/StatbusController.php @@ -6,27 +6,32 @@ use Statbus\Controllers\Controller as Controller; use Statbus\Models\Player as Player; use Statbus\Controllers\MessageController as MessageController; +use GuzzleHttp\Exception\ConnectException; -class StatbusController extends Controller { +class StatbusController extends Controller +{ - public function __construct(ContainerInterface $container) { + public function __construct(ContainerInterface $container) + { parent::__construct($container); $this->guzzle = $this->container->get('guzzle'); $this->user = $this->container->get('user'); $this->sb = $this->container->get('settings')['statbus']; } - public function index($request, $response, $args) { - return $this->view->render($response, 'index.tpl',[ + public function index($request, $response, $args) + { + return $this->view->render($response, 'index.tpl', [ 'numbers' => $this->getBigNumbers(), - 'poly' => $this->getPolyLine(), + 'poly' => $this->getPolyLine(), ]); } - public function getBigNumbers(){ + public function getBigNumbers() + { $numbers = new \stdclass; - $numbers->playtime = number_format($this->DB->row("SELECT sum(tbl_role_time.minutes) AS minutes FROM tbl_role_time WHERE tbl_role_time.job = 'Living';")->minutes); + $numbers->playtime = number_format($this->DB->rowObj("SELECT sum(tbl_role_time.minutes) AS minutes FROM tbl_role_time WHERE tbl_role_time.job = 'Living';")->minutes); $numbers->deaths = number_format($this->DB->cell("SELECT count(id) as deaths FROM tbl_death;")); $numbers->rounds = number_format($this->DB->cell("SELECT count(id) as rounds FROM tbl_round;")); $numbers->books = number_format($this->DB->cell("SELECT count(tbl_library.id) FROM tbl_library WHERE tbl_library.content != '' @@ -34,19 +39,21 @@ public function getBigNumbers(){ return $numbers; } - public function doAdminsPlay($request, $response, $args){ + public function doAdminsPlay($request, $response, $args) + { $args = $request->getQueryParams(); $maxRange = 30; - if($this->user->canAccessTGDB){ + if ($this->user && $this->user->canAccessTGDB) { $maxRange = 90; } - if(isset($args['interval'])) { + if (isset($args['interval'])) { $options = array( - 'options'=>array( - 'default'=>20, - 'min_range'=>2, - 'max_range'=>$maxRange - )); + 'options' => array( + 'default' => 20, + 'min_range' => 2, + 'max_range' => $maxRange + ) + ); $interval = filter_var($args['interval'], FILTER_VALIDATE_INT, $options); } else { $interval = 20; @@ -72,43 +79,47 @@ public function doAdminsPlay($request, $response, $args){ $perms = $this->container->get('settings')['statbus']['perm_flags']; $pm = new Player($this->container->get('settings')['statbus']); - foreach ($admins as &$a){ - foreach($perms as $p => $b){ - if ($a->flags & $b){ + foreach ($admins as &$a) { + foreach ($perms as $p => $b) { + if ($a->flags & $b) { $a->permissions[] = $p; } } $a->total = $a->ghost + $a->living; - if(isset($args['json'])) continue; + if (isset($args['json'])) + continue; $a = $pm->parsePlayer($a); } $format = $request->getQueryParam('format'); - if('wiki' === $format) { + if ('wiki' === $format) { $return = ''; - foreach ($admins as $a){ - $return.= "{{Admin
"; - $return.= "|Name=$a->ckey
"; - $return.= "|Rank=$a->rank
"; - $return.= "|Feedback=$a->feedback"; - $return.= "}}
"; + foreach ($admins as $a) { + $return .= "{{StaffMember
"; + $return .= "|Name=$a->ckey
"; + $return .= "|Rank=$a->rank"; + if (isset($a->feedback)) { + $return .= "
|Feedback=$a->feedback"; + } + $return .= "}}
"; } - return $this->view->render($response, 'dump.tpl',[ + return $this->view->render($response, 'dump.tpl', [ 'dump' => $return, 'wide' => true ]); } - return $this->view->render($response, 'info/admins.tpl',[ - 'admins' => $admins, + return $this->view->render($response, 'info/admins.tpl', [ + 'admins' => $admins, 'interval' => $interval, - 'perms' => $perms, - 'wide' => true, + 'perms' => $perms, + 'wide' => true, 'maxRange' => $maxRange, - 'ogdata' => $this->ogdata + 'ogdata' => $this->ogdata ]); } - public function adminLogs($request, $response, $args){ - if(isset($args['page'])) { + public function adminLogs($request, $response, $args) + { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); } $this->pages = ceil($this->DB->cell("SELECT count(tbl_admin_log.id) FROM tbl_admin_log") / $this->per_page); @@ -125,91 +136,136 @@ public function adminLogs($request, $response, $args){ ORDER BY L.datetime DESC LIMIT ?,?", ($this->page * $this->per_page) - $this->per_page, $this->per_page); $pm = new Player($this->container->get('settings')['statbus']); - foreach ($logs as &$l){ + foreach ($logs as &$l) { $l->admin = new \stdclass; $l->admin->ckey = $l->adminckey; $l->admin->rank = $l->adminrank; $l->admin = $pm->parsePlayer($l->admin); $l->class = ''; - $l->icon = 'edit'; - switch($l->operation){ + $l->icon = 'edit'; + switch ($l->operation) { case 'add admin': $l->class = 'success'; - $l->icon = 'user-plus'; - break; + $l->icon = 'user-plus'; + break; case 'remove admin': $l->class = 'danger'; - $l->icon = 'user-times'; - break; + $l->icon = 'user-times'; + break; case 'change admin rank': $l->class = 'info'; - $l->icon = 'user-tag'; - break; + $l->icon = 'user-tag'; + break; case 'add rank': $l->class = 'success'; - $l->icon = 'plus-square'; - break; + $l->icon = 'plus-square'; + break; case 'remove rank': $l->class = 'warning'; - $l->icon = 'minus-square'; - break; + $l->icon = 'minus-square'; + break; case 'change rank flags': $l->class = 'primary'; - $l->icon = 'flag'; - break; + $l->icon = 'flag'; + break; } $l->operation = ucwords($l->operation); } - return $this->view->render($response, 'info/admin_log.tpl',[ - 'logs' => $logs, - 'info' => $this, - 'wide' => true, - 'ogdata' => $this->ogdata + return $this->view->render($response, 'info/admin_log.tpl', [ + 'logs' => $logs, + 'info' => $this, + 'wide' => true, + 'ogdata' => $this->ogdata ]); } - public function tgdbIndex() { + public function tgdbIndex() + { //This method exists solely to scaffold the tgdb index page $memos = (new MessageController($this->container))->getAdminMemos(); - return $this->view->render($this->response, 'tgdb/index.tpl',[ + return $this->view->render($this->response, 'tgdb/index.tpl', [ 'memos' => $memos ]); } - public function getPolyLine() { - if($this->container->get('settings')['statbus']['remote_log_src']){ + public function getPolyLine() + { + if ($this->container->get('settings')['statbus']['remote_log_src']) { try { - $poly = $this->guzzle->request('GET',$this->container->get('settings')['statbus']['remote_log_src'].'/Poly.json'); + $poly = $this->guzzle->request('GET', $this->container->get('settings')['statbus']['remote_log_src'] . '/Poly.json'); $poly = json_decode((string) $poly->getBody(), TRUE); return pick($poly['phrases']); - } - catch (\Guzzle\Http\Exception\ConnectException $e) { - $response = json_encode((string)$e->getResponse()->getBody()); + } catch (ConnectException $e) { + $response = json_encode((string) $e); } } else { return false; } } - public function popGraph(){ + public function popGraph() + { $query = "SELECT FLOOR(AVG(admincount)) AS admins, FLOOR(AVG(playercount)) AS `players`, DATE_FORMAT(`time`, '%Y-%m-%e %H:00:00') as `date`, - count(round_id) AS rounds + count(round_id) AS rounds, + AVG(playercount) OVER(ORDER BY `time` ROWS BETWEEN 30 PRECEDING AND CURRENT ROW) as `averagePlayers` FROM tbl_legacy_population WHERE `time` > CURDATE() - INTERVAL 30 DAY GROUP BY HOUR (`time`), DAY(`TIME`), MONTH(`TIME`), YEAR(`TIME`) ORDER BY `time` DESC;"; $data = $this->DB->run($query); - return $this->view->render($this->response, 'info/heatmap.tpl',[ - 'data' => json_encode($data), - 'wide' => TRUE, - 'ogdata' => $this->ogdata + return $this->view->render($this->response, 'info/heatmap.tpl', [ + 'data' => json_encode($data), + 'wide' => TRUE, + 'ogdata' => $this->ogdata + ]); + } + + public function popRetention() + { + + $results = $this->DB->run('WITH login_log AS + ( + SELECT distinct(ckey), TIMESTAMPDIFF(MONTH, (SELECT MIN(DATETIME) FROM tbl_connection_log), DATETIME) AS login_month, DATETIME as date + FROM tbl_connection_log + GROUP BY 1,2 + ORDER BY 1,2 + ), + time_lapse AS + ( + SELECT ckey, login_month, LAG(login_month, 1) over (PARTITION BY ckey ORDER BY ckey, login_month) AS Lag, date + FROM login_log + ), + time_diff_calc AS + ( + SELECT ckey, login_month, Lag, login_month - Lag AS time_diff, date + FROM time_lapse + ), + player_categorized AS + ( + SELECT ckey, + login_month, + date, + (time_diff = 1 OR NULL) as retained, + (time_diff > 1 OR NULL) as returned, + (time_diff IS NULL OR NULL) as new + FROM time_diff_calc + ) + SELECT login_month as month, COUNT(returned) as returned, COUNT(retained) as retained, COUNT(new) as new, STR_TO_DATE(EXTRACT(YEAR_MONTH FROM date), "%Y%m") as datestamp + FROM player_categorized + GROUP BY 1'); + + return $this->view->render($this->response, 'info/retention.tpl', [ + 'data' => json_encode($results), + 'wide' => TRUE, + 'ogdata' => $this->ogdata ]); } - public function last30Days(){ + public function last30Days() + { $query = "SELECT SUM(`delta`) AS `minutes`, DATE_FORMAT(`datetime`, '%Y-%m-%d %H:00:00') AS `date`, `job` @@ -218,59 +274,62 @@ public function last30Days(){ AND `DATETIME` > CURDATE() - INTERVAL 30 DAY GROUP BY `job`, HOUR(`datetime`), DAY(`DATETIME`), MONTH(`DATETIME`), YEAR(`DATETIME`) ORDER BY `date` ASC;"; - $minutes = $this->DB->run($query); - return $this->view->render($this->response, 'info/30days.tpl',[ - 'minutes' => json_encode($minutes), - 'wide' => TRUE, - 'ogdata' => $this->ogdata - ]); + $minutes = $this->DB->run($query); + return $this->view->render($this->response, 'info/30days.tpl', [ + 'minutes' => json_encode($minutes), + 'wide' => TRUE, + 'ogdata' => $this->ogdata + ]); } - public function submitToAuditLog($action, $text){ + public function submitToAuditLog($action, $text) + { //Check if the audit log exists try { $this->DB->run("SELECT 1 FROM tbl_external_activity LIMIT 1"); - } catch (\PDOException $e){ + } catch (\PDOException $e) { return false; } - $this->DB->insert('tbl_external_activity',[ + $this->DB->insert('tbl_external_activity', [ 'action' => $action, - 'text' => $text, - 'ckey' => ($this->user->ckey) ? $this->user->ckey : null, - 'ip' => ip2long($_SERVER['REMOTE_ADDR']), - 'ogdata' => $this->ogdata + 'text' => $text, + 'ckey' => ($this->user->ckey) ? $this->user->ckey : null, + 'ip' => ip2long($_SERVER['REMOTE_ADDR']), + 'ogdata' => $this->ogdata ]); } - public function electionManager($request, $response, $args) { - if($request->isPost() && $this->sb['election_officer'] === $this->user->ckey){ + public function electionManager($request, $response, $args) + { + if ($request->isPost() && $this->sb['election_officer'] === $this->user->ckey) { $update = $request->getParsedBody()['candidates']; $update = explode(",", $update); - foreach($update as &$u){ + foreach ($update as &$u) { $u = strtolower(preg_replace("/[^a-zA-Z0-9]/", '', $u)); } - try{ - $handle = fopen(ROOTDIR."/tmp/candidates.json", 'w+'); + try { + $handle = fopen(ROOTDIR . "/tmp/candidates.json", 'w+'); fwrite($handle, json_encode($update)); fclose($handle); - } catch(Exception $e){ + } catch (Exception $e) { die($e->getMessage()); } } $args = $request->getQueryParams(); - if(isset($args['interval'])) { + if (isset($args['interval'])) { $options = array( - 'options'=>array( - 'default'=>60, - 'min_range'=>2, - 'max_range'=>180 - )); + 'options' => array( + 'default' => 60, + 'min_range' => 2, + 'max_range' => 180 + ) + ); $interval = filter_var($args['interval'], FILTER_VALIDATE_INT, $options); } else { $interval = 60; } - $list = "('".implode("','",$this->sb['candidates'])."')"; - $candidates = $this->DB->run("SELECT A.ckey, + $list = "('" . implode("','", $this->sb['candidates']) . "')"; + $candidates = $this->DB->run("SELECT A.ckey, A.lastadminrank as rank, (SELECT count(C.id) FROM tbl_connection_log AS C WHERE A.ckey = C.ckey AND C.datetime BETWEEN CURDATE() - INTERVAL ? DAY AND CURDATE()) AS connections, (SELECT sum(G.delta) FROM tbl_role_time_log AS G @@ -284,18 +343,19 @@ public function electionManager($request, $response, $args) { WHERE A.ckey IN $list GROUP BY A.ckey;", $interval, $interval, $interval); $pm = new Player($this->container->get('settings')['statbus']); - foreach ($candidates as &$a){ + foreach ($candidates as &$a) { $a->total = $a->ghost + $a->living; $a = $pm->parsePlayer($a); } - return $this->view->render($response, 'election/candidates.tpl',[ + return $this->view->render($response, 'election/candidates.tpl', [ 'interval' => $interval, 'admins' => $candidates, - 'list' => str_replace(['(',')',"'"], '', $list), + 'list' => str_replace(['(', ')', "'"], '', $list), 'ogdata' => $this->ogdata ]); } - public function mapularity ($request, $response, $args) { + public function mapularity($request, $response, $args) + { $mapularity = $this->DB->run("SELECT date_format(r.initialize_datetime,'%M %Y') as `date`, count(r.id) as rounds, @@ -307,12 +367,12 @@ public function mapularity ($request, $response, $args) { ORDER BY r.initialize_datetime DESC"); $tmp = []; $maps = []; - foreach($mapularity as $row){ + foreach ($mapularity as $row) { $tmp[$row->date][$row->map_name] = $row->rounds; $maps[] = $row->map_name; } $maps = array_unique($maps); - return $this->view->render($response, 'info/mapularity.tpl',[ + return $this->view->render($response, 'info/mapularity.tpl', [ 'maps' => $maps, 'mapularity' => $tmp, 'ogdata' => $this->ogdata diff --git a/src/Statbus/Controllers/TicketController.php b/src/Statbus/Controllers/TicketController.php index 4411ad0..80d1d1d 100755 --- a/src/Statbus/Controllers/TicketController.php +++ b/src/Statbus/Controllers/TicketController.php @@ -2,14 +2,21 @@ namespace Statbus\Controllers; +use ParagonIE\EasyDB\EasyDB; use Psr\Container\ContainerInterface; use Statbus\Controllers\Controller as Controller; use Statbus\Models\Ticket as Ticket; use Statbus\Models\Player as Player; -class TicketController extends Controller { - public function __construct(ContainerInterface $container) { +class TicketController extends Controller +{ + private Ticket $tm; + private Player $pm; + private EasyDB $alt_db; + + public function __construct(ContainerInterface $container) + { parent::__construct($container); $this->settings = $this->container->get('settings')['statbus']; $this->tm = new Ticket($this->settings); @@ -21,7 +28,8 @@ public function __construct(ContainerInterface $container) { $this->permaLink = 'ticket.single'; } - public function getActiveTickets(){ + public function getActiveTickets() + { $this->pages = ceil($this->DB->cell("SELECT count(tbl_ticket.id) FROM tbl_ticket @@ -46,7 +54,7 @@ public function getActiveTickets(){ GROUP BY t.id ORDER BY `timestamp` DESC LIMIT ?, ?;", ($this->page * $this->per_page) - $this->per_page, $this->per_page); - foreach ($tickets as &$t){ + foreach ($tickets as &$t) { $t->sender = new \stdclass; $t->sender->ckey = $t->sender_ckey; $t->sender->rank = $t->s_rank; @@ -62,7 +70,8 @@ public function getActiveTickets(){ return $tickets; } - public function getTicketsForRound(int $round) { + public function getTicketsForRound(int $round) + { $round = filter_var($round, FILTER_VALIDATE_INT); $tickets = $this->DB->run("SELECT t.id, @@ -85,7 +94,7 @@ public function getTicketsForRound(int $round) { WHERE t.round_id = ? AND t.action = 'Ticket Opened' ORDER BY `timestamp` ASC;", $round); - foreach ($tickets as &$t){ + foreach ($tickets as &$t) { $t->sender = new \stdclass; $t->sender->ckey = $t->sender_ckey; $t->sender->rank = $t->s_rank; @@ -101,7 +110,8 @@ public function getTicketsForRound(int $round) { return $tickets; } - public function getSingleTicket(int $round, int $ticket){ + public function getSingleTicket(int $round, int $ticket) + { $round = filter_var($round, FILTER_VALIDATE_INT); $ticket = filter_var($ticket, FILTER_VALIDATE_INT); $tickets = $this->DB->run("SELECT @@ -123,7 +133,7 @@ public function getSingleTicket(int $round, int $ticket){ WHERE t.round_id = ? AND t.ticket = ? ORDER BY `timestamp` ASC;", $round, $ticket); - foreach ($tickets as &$t){ + foreach ($tickets as &$t) { $t->sender = new \stdclass; $t->sender->ckey = $t->sender_ckey; $t->sender->rank = $t->s_rank; @@ -139,7 +149,8 @@ public function getSingleTicket(int $round, int $ticket){ return $tickets; } - public function getTicketsForCkey(string $ckey) { + public function getTicketsForCkey(string $ckey) + { $this->pages = ceil($this->DB->cell("SELECT count(t.id) FROM tbl_ticket t @@ -164,9 +175,9 @@ public function getTicketsForCkey(string $ckey) { GROUP BY t.id ORDER BY `timestamp` DESC LIMIT ?, ?;", $ckey, $ckey, ($this->page * $this->per_page) - $this->per_page, $this->page * $this->per_page); - // var_dump(($this->page * $this->per_page) - $this->per_page); - // var_dump($this->page * $this->per_page); - foreach ($tickets as &$t){ + // var_dump(($this->page * $this->per_page) - $this->per_page); + // var_dump($this->page * $this->per_page); + foreach ($tickets as &$t) { $t->sender = new \stdclass; $t->sender->ckey = $t->sender_ckey; $t->sender->rank = $t->s_rank; @@ -182,48 +193,53 @@ public function getTicketsForCkey(string $ckey) { return $tickets; } - public function index($request, $response, $args) { - if(isset($args['page'])) { + public function index($request, $response, $args) + { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); } - return $this->view->render($this->response, 'tickets/index.tpl',[ - 'tickets' => $this->getActiveTickets(), - 'ticket' => $this, - ]); + return $this->view->render($this->response, 'tickets/index.tpl', [ + 'tickets' => $this->getActiveTickets(), + 'ticket' => $this, + ]); } - public function roundTickets($request, $response, $args){ + public function roundTickets($request, $response, $args) + { $this->path = 'ticket.round'; - return $this->view->render($this->response, 'tickets/round.tpl',[ - 'tickets' => $this->getTicketsForRound($args['round']), - 'round' => $args['round'], - 'ticket' => $this - ]); + return $this->view->render($this->response, 'tickets/round.tpl', [ + 'tickets' => $this->getTicketsForRound($args['round']), + 'round' => $args['round'], + 'ticket' => $this + ]); } - public function single($request, $response, $args){ - return $this->view->render($this->response, 'tickets/single.tpl',[ - 'tickets' => $this->getSingleTicket($args['round'],$args['ticket']), - ]); + public function single($request, $response, $args) + { + return $this->view->render($this->response, 'tickets/single.tpl', [ + 'tickets' => $this->getSingleTicket($args['round'], $args['ticket']), + ]); } - public function myTickets($request, $response, $args) { - if(isset($args['page'])) { + public function myTickets($request, $response, $args) + { + if (isset($args['page'])) { $this->page = filter_var($args['page'], FILTER_VALIDATE_INT); } $user = $this->container->get('user'); $this->path = "me.tickets"; $this->permaLink = "me.tickets.single"; - return $this->view->render($this->response, 'tickets/me.tpl',[ + return $this->view->render($this->response, 'tickets/me.tpl', [ 'tickets' => $this->getTicketsForCkey($user->ckey), 'ticket' => $this, ]); } - public function myTicket($request, $response, $args){ - $this->user = $this->container->get('user'); + public function myTicket($request, $response, $args) + { + $user = $this->container->get('user'); $tickets = $this->getSingleTicket($args['round'], $args['ticket']); - if(!in_array($this->user->ckey, [$tickets[0]->sender_ckey, $tickets[0]->recipient_ckey])) { - return $this->view->render($this->response, 'base/error.tpl',[ + if (!in_array($user->ckey, [$tickets[0]->sender_ckey, $tickets[0]->recipient_ckey])) { + return $this->view->render($this->response, 'base/error.tpl', [ 'message' => 'You do not have permission to view this', 'code' => 403 ]); @@ -231,31 +247,32 @@ public function myTicket($request, $response, $args){ $canPublicize = false; - if(!$tickets[0]->recipient && $this->user->ckey === $tickets[0]->sender_ckey){ + if (!$tickets[0]->recipient && $user->ckey === $tickets[0]->sender_ckey) { $canPublicize = TRUE; //Ahelps sent by anyone regardless of rank } - if($this->user->ckey === $tickets[0]->recipient_ckey) { + if ($user->ckey === $tickets[0]->recipient_ckey) { $canPublicize = TRUE; //Ahelps sent from admin to player } - if('POST' === $this->request->getMethod() && TRUE === $canPublicize){ + if ('POST' === $request->getMethod() && TRUE === $canPublicize) { $this->setTicketStatus($tickets[0]->id); } $status = $this->ticketPublicityStatus($tickets[0]->id); @$status->canPublicize = $canPublicize; - return $this->view->render($this->response, 'tickets/single.me.tpl',[ + return $this->view->render($this->response, 'tickets/single.me.tpl', [ 'tickets' => $tickets, 'status' => $status ]); } - public function publicTicket($request, $response, $args){ + public function publicTicket($request, $response, $args) + { $this->alt_db = $this->container->get('ALT_DB'); $id = $this->getTicketIDFromIdentifier($args['identifier']); $status = $this->ticketPublicityStatus($id); - if($status && 1 !== $status->status){ - return $this->view->render($this->response, 'base/error.tpl',[ + if ($status && 1 !== $status->status) { + return $this->view->render($this->response, 'base/error.tpl', [ 'message' => 'You do not have permission to view this', 'code' => 403 ]); @@ -263,36 +280,42 @@ public function publicTicket($request, $response, $args){ $ticket = $this->getFullTicketFromID($id); $tickets = $this->getSingleTicket($ticket->round_id, $ticket->ticket); - return $this->view->render($this->response, 'tickets/single.me.tpl',[ + return $this->view->render($this->response, 'tickets/single.me.tpl', [ 'tickets' => $tickets, 'status' => $status ]); } - private function getFullTicketFromID($id){ - return($this->DB->row("SELECT round_id, ticket FROM tbl_ticket WHERE id = ?", $id)); + private function getFullTicketFromID($id) + { + return ($this->DB->rowObj("SELECT round_id, ticket FROM tbl_ticket WHERE id = ?", $id)); } - private function getTicketIDFromIdentifier($identifier) { + private function getTicketIDFromIdentifier($identifier) + { return $this->alt_db->cell("SELECT ticket FROM public_tickets WHERE identifier = ?", $identifier); } - private function ticketPublicityStatus($id){ + private function ticketPublicityStatus($id) + { $this->alt_db = $this->container->get('ALT_DB'); - $status = $this->alt_db->row("SELECT * FROM public_tickets WHERE ticket = ?", $id); + $status = $this->alt_db->rowObj("SELECT * FROM public_tickets WHERE ticket = ?", $id); return $status; } - private function setTicketStatus($id){ + private function setTicketStatus($id) + { $status = $this->ticketPublicityStatus($id); - if(!$status){ - $this->alt_db->insert("public_tickets", [ - 'ticket' => $id, - 'status' => 1, - 'identifier' => substr(hash('SHA512',base64_encode(random_bytes(32))),0,16) + if (!$status) { + $this->alt_db->insert( + "public_tickets", + [ + 'ticket' => $id, + 'status' => 1, + 'identifier' => substr(hash('SHA512', base64_encode(random_bytes(32))), 0, 16) ] ); - } else if(1 === $status->status) { + } else if (1 === $status->status) { $this->alt_db->run("UPDATE public_tickets SET `status` = 0 WHERE ticket = ?", $id); } else { $this->alt_db->run("UPDATE public_tickets SET `status` = 1 WHERE ticket = ?", $id); diff --git a/src/Statbus/Controllers/UserController.php b/src/Statbus/Controllers/UserController.php index ce808b8..44d4cee 100755 --- a/src/Statbus/Controllers/UserController.php +++ b/src/Statbus/Controllers/UserController.php @@ -1,48 +1,54 @@ playerModel = new Player($this->container->get('settings')['statbus']); - $this->PC = new PlayerController($this->container); + $this->playerController = new PlayerController($this->container); $this->settings = $this->container->get('settings'); - if(isset($_SESSION['sb']['byond_ckey']) && $this->settings['statbus']['auth']['remote_auth']){ - $this->user = $this->PC->getPlayerByCkey($_SESSION['sb']['byond_ckey']); - } elseif ($this->settings['statbus']['ip_auth']){ - $this->user = $this->PC->getPlayerByIP(ip2long($_SERVER['REMOTE_ADDR'])); - if($this->user->days > $this->settings['statbus']['ip_auth_days']){ + if (isset($_SESSION['sb']['byond_ckey']) && $this->settings['statbus']['auth']['remote_auth']) { + $this->user = $this->playerController->getPlayerByCkey($_SESSION['sb']['byond_ckey']); + } elseif ($this->settings['statbus']['ip_auth']) { + $this->user = $this->playerController->getPlayerByIP(ip2long($_SERVER['REMOTE_ADDR'])); + if ($this->user->days > $this->settings['statbus']['ip_auth_days']) { //Skip admin rank verification. $this->skipRankVerify = true; } } - if($this->user){ + if ($this->user) { $this->verifyAdminRank($this->skipRankVerify); $this->user = $this->playerModel->parsePlayer($this->user); $this->view->getEnvironment()->addGlobal('user', $this->user); } } - public function verifyAdminRank($skip = false) { - if(empty($this->user->ckey) || $skip){ + public function verifyAdminRank($skip = false) + { + if (empty($this->user->ckey) || $skip) { $this->user->rank = 'Player'; return; } - $this->user->rank = $this->DB->row("SELECT tbl_admin.rank, + $this->user->rank = $this->DB->rowObj("SELECT tbl_admin.rank, tbl_admin.feedback, tbl_admin_ranks.flags, tbl_admin_ranks.exclude_flags, @@ -50,71 +56,76 @@ public function verifyAdminRank($skip = false) { FROM tbl_admin LEFT JOIN tbl_admin_ranks ON tbl_admin.rank = tbl_admin_ranks.rank WHERE tbl_admin.ckey = ?", $this->user->ckey); - if(!$this->user->rank){ + if (!$this->user->rank) { $this->user->rank = 'Player'; return; } $perms = $this->container->get('settings')['statbus']['perm_flags']; - foreach($perms as $p => $b){ - if ($this->user->rank->flags & $b){ + foreach ($perms as $p => $b) { + if ($this->user->rank->flags & $b) { $this->user->rank->permissions[] = $p; } } $this->canAccessTGDB(); } - public function fetchUser(){ + public function fetchUser() + { return $this->user; } - public function getCkey(){ + public function getCkey() + { return $this->user->ckey; } - - public function canAccessTGDB(){ - if(empty($this->user->ckey)) return false; - if(is_string($this->user->rank) && 'Player' === $this->user->rank) return false; - if(isset($this->user->rank->permissions) && in_array('BAN', $this->user->rank->permissions)) { + + public function canAccessTGDB() + { + if (empty($this->user->ckey)) + return false; + if (is_string($this->user->rank) && 'Player' === $this->user->rank) + return false; + if (isset($this->user->rank->permissions) && in_array('BAN', $this->user->rank->permissions)) { $this->user->canAccessTGDB = true; return true; } return false; } - public function me($request, $response, $args) { - $lastWords = $this->PC->getLastWords($this->user->ckey); - return $this->view->render($response, 'me/index.tpl',[ + public function me($request, $response, $args) + { + $lastWords = $this->playerController->getLastWords($this->user->ckey); + return $this->view->render($response, 'me/index.tpl', [ 'lastWords' => $lastWords ]); } - public function addFeedback($request, $response, $args) { + public function addFeedback($request, $response, $args) + { $feedback = filter_var($request->getParam('feedback'), FILTER_VALIDATE_URL); - if($feedback){ - try{ - $this->DB->update('tbl_admin',[ + if ($feedback) { + try { + $this->DB->update('tbl_admin', [ 'feedback' => $feedback - ],[ + ], [ 'ckey' => $this->user->ckey ]); (new StatbusController($this->container))->submitToAuditLog('FBL', "Updated feedback link to '$feedback'"); $this->user->rank->feedback = $feedback; - } catch (Exception $e){ - return $this->view->render($response, 'base/error.tpl',[ - 'message' => $e->getMessage(), - 'code' => 500 + } catch (Exception $e) { + return $this->view->render($response, 'base/error.tpl', [ + 'message' => $e->getMessage(), + 'code' => 500 ]); } } - if(FALSE === $request->getAttribute('csrf_status')){ - return $this->view->render($response, 'base/error.tpl',[ - 'message' => "CSRF failure. This action is denied.", - 'code' => 403, - 'link' => $url, - 'linkText' => 'Back' + if (FALSE === $request->getAttribute('csrf_status')) { + return $this->view->render($response, 'base/error.tpl', [ + 'message' => "CSRF failure. This action is denied.", + 'code' => 403, ]); } - return $this->view->render($response, 'tgdb/feedback.tpl',[ + return $this->view->render($response, 'tgdb/feedback.tpl', [ 'feedback' => $this->user->rank->feedback ]); } diff --git a/src/Statbus/Extensions/CsrfExtension.php b/src/Statbus/Extensions/CsrfExtension.php deleted file mode 100755 index 1238afe..0000000 --- a/src/Statbus/Extensions/CsrfExtension.php +++ /dev/null @@ -1,42 +0,0 @@ -csrf = $csrf; - } - - public function getGlobals() - { - // CSRF token name and value - $csrfNameKey = $this->csrf->getTokenNameKey(); - $csrfValueKey = $this->csrf->getTokenValueKey(); - $csrfName = $this->csrf->getTokenName(); - $csrfValue = $this->csrf->getTokenValue(); - - return [ - 'csrf' => [ - 'keys' => [ - 'name' => $csrfNameKey, - 'value' => $csrfValueKey - ], - 'name' => $csrfName, - 'value' => $csrfValue - ] - ]; - } - - public function getName() - { - return 'slim/csrf'; - } -} \ No newline at end of file diff --git a/src/Statbus/Extensions/PrefixedDB.php b/src/Statbus/Extensions/PrefixedDB.php new file mode 100644 index 0000000..443ec61 --- /dev/null +++ b/src/Statbus/Extensions/PrefixedDB.php @@ -0,0 +1,123 @@ +prefix = $prefix; + + $inner = Factory::create($dsn, $username, $password, $options); + + parent::__construct($inner->pdo, $inner->dbEngine, $inner->options); + } + + private function prefixStatement(string $statement): string + { + return str_replace( + 'tbl_', + $this->prefix, + $statement + ); + } + + public function safeQuery( + string $statement, + array $params = [], + int $fetchStyle = EasyDB::DEFAULT_FETCH_STYLE, + bool $returnNumAffected = false, + bool $calledWithVariadicParams = false + ): array|int|object { + return parent::safeQuery($this->prefixStatement($statement), $params, $fetchStyle, $returnNumAffected, $calledWithVariadicParams); + } + + public function single(string $statement, array $params = []): bool|float|int|string|null + { + return parent::single($this->prefixStatement($statement), $params); + } + + public function column(string $statement, array $params = [], int $offset = 0): array|bool + { + return parent::column($this->prefixStatement($statement), $params, $offset); + } + + public function escapeLikeValue(string $value): string + { + return parent::escapeLikeValue($this->prefixStatement($value)); + } + + public function escapeIdentifier(string $string, bool $quote = true): string + { + return parent::escapeIdentifier($this->prefixStatement($string), $quote); + } + + public function delete(string $table, EasyStatement|array $conditions): int + { + return parent::delete($this->prefixStatement($table), $conditions); + } + + public function row(string $statement, ...$params): array + { + /** + * @var array|int $result + */ + $result = $this->safeQuery( + $statement, + $params, + self::DEFAULT_FETCH_STYLE, + false, + true + ); + if (is_array($result)) { + $first = array_shift($result); + if (is_null($result)) { + /* Do not TypeError on empty results */ + return []; + } + if (is_object($first)) { + return get_object_vars($first); + } + return $first; + } + return []; + } + + public function rowObj(string $statement, ...$params): stdClass + { + /** + * @var array|int $result + */ + $result = $this->safeQuery( + $statement, + $params, + self::DEFAULT_FETCH_STYLE, + false, + true + ); + if (is_array($result)) { + $first = array_shift($result); + if (is_null($result)) { + /* Do not TypeError on empty results */ + return new stdClass; + } + if (is_object($first)) { + return $first; + } + return $first; + } + return new stdClass; + } +} \ No newline at end of file diff --git a/src/Statbus/Middleware/UserGuard.php b/src/Statbus/Middleware/UserGuard.php index 3bcc260..4ec0e2d 100755 --- a/src/Statbus/Middleware/UserGuard.php +++ b/src/Statbus/Middleware/UserGuard.php @@ -1,52 +1,61 @@ container = $container; $this->user = $container->get('user'); $this->view = $container->get('view'); - $this->request = $container->get('request'); $this->settings = $container->get('settings'); $this->level = $level; } - public function __invoke($request, $response, $next) { - if(!$this->settings['statbus']['auth']['remote_auth'] && !$this->settings['statbus']['ip_auth']){ - return $this->view->render($response, 'base/error.tpl',[ - 'message' => "No authentication mechanisms specified.", - 'code' => 403 - ]); + public function __invoke(Request $request, Response $response, $next) + { + if (!$this->settings['statbus']['auth']['remote_auth'] && !$this->settings['statbus']['ip_auth']) { + return $this->view->render($response, 'base/error.tpl', [ + 'message' => "No authentication mechanisms specified.", + 'code' => 403 + ]); } - if(!$this->user) { - $_SESSION['return_uri'] = (string) $this->request->getUri(); + if (!$this->user || !$this->user->ckey) { + $_SESSION['return_uri'] = (string) $request->getUri(); $args = null; return (new Auth($this->container))->auth($request, $response, $args); } - switch ($this->level){ + switch ($this->level) { case 1: default: - if (!$this->user) { - return $this->view->render($response, 'base/error.tpl',[ - 'message' => "You must be logged in to access this page", - 'code' => 403 - ]); - } - break; + if (!$this->user->ckey) { + return $this->view->render($response, 'base/error.tpl', [ + 'message' => "You must be logged in to access this page", + 'code' => 403 + ]); + } + break; case 2: if (!$this->user->canAccessTGDB) { - return $this->view->render($response, 'base/error.tpl',[ + return $this->view->render($response, 'base/error.tpl', [ 'message' => "You do not have permission to access this page.", - 'code' => 403 + 'code' => 403 ]); - die(); } $this->view->getEnvironment()->addGlobal('classified', TRUE); - break; + break; } $response = $next($request, $response); return $response; diff --git a/src/Statbus/Models/Death.php b/src/Statbus/Models/Death.php index f30c8ff..175775a 100755 --- a/src/Statbus/Models/Death.php +++ b/src/Statbus/Models/Death.php @@ -2,75 +2,89 @@ namespace Statbus\Models; -class Death { +class Death +{ private $settings; - public function __construct(array $settings){ + public function __construct(array $settings) + { $this->settings = $settings; } - public function parseDeath(&$death) { + public function parseDeath(&$death) + { #$death->server = $this->settings['servers'][array_search($death->ip, array_column($this->settings['servers'], 'ip'))]['name']; $server = array_keys(array_column($this->settings['servers'], 'port'), $death->port); - if(count($server) == 1) $death->server = $this->settings['servers'][$server[0]]['name']; - else $death->server = $this->settings['servers'][array_search($death->ip, array_column($this->settings['servers'], 'ip'))]['name']; + if (count($server) == 1) + $death->server = $this->settings['servers'][$server[0]]['name']; + else + $death->server = $this->settings['servers'][array_search($death->ip, array_column($this->settings['servers'], 'ip'))]['name']; $death->class = ''; - if($death->lakey) $death->class = "murder"; - if($death->suicide) $death->class = "suicide"; + if ($death->lakey) + $death->class = "murder"; + if ($death->suicide) + $death->class = "suicide"; - if($death->special) $death->special = ucwords($death->special); + if ($death->special) + $death->special = ucwords($death->special); $death->vitals = new \stdclass; - $death->vitals->brute = $death->brute; unset($death->brute); - $death->vitals->brain = $death->brain; unset($death->brain); - $death->vitals->fire = $death->fire; unset($death->fire); - $death->vitals->oxy = $death->oxy; unset($death->oxy); - $death->vitals->tox = $death->tox; unset($death->tox); - $death->vitals->clone = $death->clone; unset($death->clone); - $death->vitals->stamina = $death->stamina; unset($death->stamina); - - $death->max = array_search(max((array) $death->vitals),(array) $death->vitals); + $death->vitals->brute = $death->brute; + unset($death->brute); + $death->vitals->brain = $death->brain; + unset($death->brain); + $death->vitals->fire = $death->fire; + unset($death->fire); + $death->vitals->oxy = $death->oxy; + unset($death->oxy); + $death->vitals->tox = $death->tox; + unset($death->tox); + $death->vitals->clone = $death->clone; + unset($death->clone); + $death->vitals->stamina = $death->stamina; + unset($death->stamina); + + $death->max = array_search(max((array) $death->vitals), (array) $death->vitals); $death->cause = "Natural causes"; - switch ($death->max){ + switch ($death->max) { case 'brute': - $death->cause = "Blunt-Force Trauma"; - $death->last_line = "as they were beaten to death"; - break; + $death->cause = "Blunt-Force Trauma"; + $death->last_line = "as they were beaten to death"; + break; case 'brain': - $death->cause = "Crippling Brain Damage"; - $death->last_line = "slurred out as they gave up on life"; - break; + $death->cause = "Crippling Brain Damage"; + $death->last_line = "slurred out as they gave up on life"; + break; case 'fire': - $death->cause = "Severe Burns"; - $death->last_line = "as they cooked alive"; - break; + $death->cause = "Severe Burns"; + $death->last_line = "as they cooked alive"; + break; case 'oxy': - $death->cause = "Suffocation"; - $death->last_line = "with their dying breath"; - break; + $death->cause = "Suffocation"; + $death->last_line = "with their dying breath"; + break; case 'tox': - $death->cause = "Poisoning"; - $death->last_line = "twitching as toxins coursed through their system"; - break; + $death->cause = "Poisoning"; + $death->last_line = "twitching as toxins coursed through their system"; + break; case 'clone': - $death->cause = "Poor Cloning Technique"; - $death->last_line = "scrawled into the floor where they died"; - break; + $death->cause = "Poor Cloning Technique"; + $death->last_line = "scrawled into the floor where they died"; + break; case 'stamina': - $death->cause = "Exhaustion"; - $death->last_line = "whispered in their final moments"; - break; + $death->cause = "Exhaustion"; + $death->last_line = "whispered in their final moments"; + break; } - $death->map_url = str_replace(' ', '', $death->mapname); return $death; } } \ No newline at end of file diff --git a/src/Statbus/Models/Library.php b/src/Statbus/Models/Library.php index 614b7d6..dbb1c4e 100755 --- a/src/Statbus/Models/Library.php +++ b/src/Statbus/Models/Library.php @@ -2,41 +2,46 @@ namespace Statbus\Models; -class Library { +use Parsedown; + +class Library +{ private $settings; public $deaths; - public function __construct(){ - $this->md = new \Parsedown(); + public function __construct() + { + $this->md = new Parsedown(); } - public function parseBook(&$book){ - switch($book->category){ + public function parseBook(&$book) + { + switch ($book->category) { default: case 'Ficton': $book->class = 'success'; - break; + break; case 'Non-Fiction': $book->class = 'info'; - break; + break; case 'Reference': $book->class = 'warning'; - break; + break; case 'Religion': $book->class = 'primary'; - break; + break; case 'Adult': $book->class = 'danger censored'; - break; + break; } - if(isset($book->content)){ + if (isset($book->content)) { $book->content = $this->md->text($book->content); } return $book; diff --git a/src/Statbus/Models/Messages.php b/src/Statbus/Models/Messages.php index f4e801b..0551ede 100755 --- a/src/Statbus/Models/Messages.php +++ b/src/Statbus/Models/Messages.php @@ -2,16 +2,21 @@ namespace Statbus\Models; -class Messages { +use Parsedown; + +class Messages +{ private $settings; - public function __construct(array $settings){ + public function __construct(array $settings) + { $this->settings = $settings; - $this->md = new \Parsedown(); + $this->md = new Parsedown(); } - public function parseMessage(&$message) { + public function parseMessage(&$message) + { $message->text = strip_tags($message->text); $message->text = $this->md->text($message->text); $message->text = str_replace([ @@ -23,54 +28,54 @@ public function parseMessage(&$message) { "'", "
" ], $message->text); - switch ($message->type){ + switch ($message->type) { case 'memo': - $message->icon = 'sticky-note'; + $message->icon = 'sticky-note'; $message->class = 'bg-primary text-white'; - break; + break; case 'message': $message->icon = 'envelope'; $message->class = 'bg-success text-white'; - break; + break; case 'message sent': $message->icon = 'envelope-open'; $message->class = 'border-success'; - break; + break; default: case 'note': $message->icon = 'flag'; $message->class = 'border-warning'; - break; + break; case 'watchlist entry': $message->icon = 'binoculars'; $message->class = 'bg-danger text-white'; - break; + break; } - if(isset($message->severity)){ + if (isset($message->severity)) { switch ($message->severity) { case 'high': $message->severity_class = 'danger'; - break; + break; case 'medium': $message->severity_class = 'warning'; - break; + break; case 'minor': $message->severity_class = 'info'; - break; + break; default: case 'none': $message->severity_class = 'success'; $message->severity = "None"; - break; + break; } $message->severity = ucwords($message->severity); } diff --git a/src/Statbus/Models/Round.php b/src/Statbus/Models/Round.php index 07d6eba..a1ad4d1 100755 --- a/src/Statbus/Models/Round.php +++ b/src/Statbus/Models/Round.php @@ -2,7 +2,10 @@ namespace Statbus\Models; -class Round { +use DateTimeImmutable; + +class Round +{ private $settings; @@ -26,11 +29,13 @@ class Round { public $shutdown_time; public $deaths; - public function __construct(array $settings){ + public function __construct(array $settings) + { $this->settings = $settings; } - public function parseRound(&$round){ + public function parseRound(&$round) + { $round->icons = new \stdclass; $round->icons->mode = 'dice-d6'; @@ -42,10 +47,9 @@ public function parseRound(&$round){ $round = $this->mapStatus($round); #$round->server_data = (object) $this->settings['servers'][array_search($round->port, array_column($this->settings['servers'], 'port'))]; - + $server = array_keys(array_column($this->settings['servers'], 'port'), $round->port); - if(count($server) == 1) $round->server_data = (object) $this->settings['servers'][$server[0]]; - else $round->server_data = (object) $this->settings['servers'][array_search($round->ip, array_column($this->settings['servers'], 'ip'))]; + $round->server_data = (object) $this->settings['servers'][count($server) == 1 ? $server[0] : array_search($round->ip, array_column($this->settings['servers'], 'ip'))]; if (!$round->port) { $round->server = 'Unknown'; @@ -54,38 +58,38 @@ public function parseRound(&$round){ } $round->shuttle = preg_replace("/[^a-zA-Z\d\s:]/", '', $round->shuttle); $round->shuttle = ucwords($round->shuttle); - if('' == $round->shuttle) $round->shuttle = false; + if ('' == $round->shuttle) + $round->shuttle = false; - if($round->commit_hash && isset($this->settings['github'])){ - $round->commit_href = "https://github.com/".$this->settings['github']."/commit/$round->commit_hash"; + if ($round->commit_hash && isset($this->settings['github'])) { + $round->commit_href = "https://github.com/" . $this->settings['github'] . "/commit/$round->commit_hash"; } - $round->commit_hash = substr($round->commit_hash, 0,7); + $round->commit_hash = substr($round->commit_hash, 0, 7); //Remote Log Links $round->logs = FALSE; //No logs by default - if(isset($round->server_data->public_logs)){ + if (isset($round->server_data->public_logs)) { $round->logs = TRUE; - $date = new \DateTime($round->initialize_datetime); + $date = new DateTimeImmutable($round->initialize_datetime); $year = $date->format('Y'); $month = $date->format('m'); $day = $date->format('d'); $round->remote_logs = $round->server_data->public_logs; - $round->remote_logs.= "$year/$month/$day/round-$round->id.zip"; + $round->remote_logs .= "$year/$month/$day/round-$round->id.zip"; $round->remote_logs_dir = str_replace('.zip', '', $round->remote_logs); $round->admin_logs_dir = str_replace($round->server_data->public_logs, $round->server_data->raw_logs, $round->remote_logs_dir); } - - $round->map_url = str_replace(' ', '', $round->map); return $round; } - public function mapStatus(&$round) { - @$round->icons->mode = $this->settings['mode_icons'][$round->mode]; - if ('' === $round->result || 'Undefined' === $round->result){ + public function mapStatus(&$round) + { + @$round->icons->mode = $this->settings['mode_icons'][$round->mode] ?? 'question-circle'; + if ('' === $round->result || 'Undefined' === $round->result) { $round->result = $round->end_state; } - if(strpos($round->result, 'Win - ') !== FALSE){ + if (strpos($round->result, 'Win - ') !== FALSE) { $round->class = 'success'; $round->icons->result = 'check'; } else if (strpos($round->result, 'Loss - ') !== FALSE) { diff --git a/src/Statbus/Models/Stat.php b/src/Statbus/Models/Stat.php index 815e168..6927cfd 100755 --- a/src/Statbus/Models/Stat.php +++ b/src/Statbus/Models/Stat.php @@ -2,19 +2,27 @@ namespace Statbus\Models; -class Stat { +use DateInterval; +use DatePeriod; +use DateTime; +use stdClass; + +class Stat +{ private $filters; - public function __construct(){ - $this->filters = json_decode(file_get_contents(ROOTDIR."/src/conf/stat_filters.json")); + public function __construct() + { + $this->filters = json_decode(file_get_contents(ROOTDIR . "/src/conf/stat_filters.json")); } - public function parseStat(&$stat, $collate = false){ - if (!$collate){ + public function parseStat(&$stat, $collate = false) + { + if (!$collate) { return $this->singleParse($stat); } else { - foreach($stat as &$s){ + foreach ($stat as &$s) { $s = $this->singleParse($s); } $stat = $this->collate($stat); @@ -22,35 +30,36 @@ public function parseStat(&$stat, $collate = false){ } } - public function singleParse(&$stat){ - @$stat->label = $this->filters->{$stat->key_name}->label; - if(isset($stat->label->filter)) { + public function singleParse(&$stat) + { + if (isset($this->filters->{$stat->key_name}->label->filter)) { + $stat->label = $this->filters->{$stat->key_name}->label; $stat->json = str_replace($stat->label->filter, '', $stat->json); } $stat->data = json_decode($stat->json, TRUE)['data']; $stat = $this->specialCases($stat); $stat->output = $stat->data; - switch($stat->key_type){ + switch ($stat->key_type) { case 'associative': - - break; + break; case 'amount': - break; + break; case 'nested tally': - break; + break; case 'tally': $stat->total = array_sum($stat->data); $stat->output = arsort($stat->output); - break; + break; } return $stat; } - public function collate(&$stat){ - $tmp = new \stdclass; + public function collate(&$stat) + { + $tmp = new stdclass; $tmp->collated = TRUE; $tmp->key_name = $stat[0]->key_name; $tmp->key_type = $stat[0]->key_type; @@ -64,63 +73,135 @@ public function collate(&$stat){ $tmp->dates = []; $tmp->js = []; - $a = new \DatePeriod( - new \DateTime($stat[0]->datetime), - new \DateInterval('P1D'), - new \DateTime(end($stat)->datetime) + if (isset($this->filters->{$tmp->key_name}->label)) { + $tmp->label = $this->filters->{$tmp->key_name}->label; + } + + $a = new DatePeriod( + new DateTime($stat[0]->datetime), + new DateInterval('P1D'), + new DateTime(end($stat)->datetime) ); foreach ($a as $key => $value) { - $tmp->dates[$value->format('Y-m-d')] = 0; + $tmp->dates[$value->format('Y-m-d')] = 0; } - switch($tmp->key_type){ - case 'associative': + switch ($tmp->key_type) { + case 'text': + foreach ($stat as $s) { + $tmp->rounds[$s->round_id] = true; - break; + if (!is_array($s->data)) { + $tmp->output[] = $s->data; + continue; + } + + foreach ($s->data as $k) { + $tmp->output[] = $k; + } + } + + $tmp->output = array_unique($tmp->output); + break; + + case 'associative': + $data = []; + foreach ($stat as $s) { + $tmp->rounds[$s->round_id] = count($s->data); + foreach ($s->data as $x => $y) { + $data[] = $y; + } + } + $tmp->data = array_unique($data, SORT_REGULAR); + $tmp->output = $tmp->data; + break; case 'amount': $tmp->output = 0; - foreach($stat as $s){ + foreach ($stat as $s) { $tmp->output += $s->data; $tmp->rounds[$s->round_id] = $s->data; - $tmp->dates[(new \dateTime($s->datetime))->format('Y-m-d')] += $s->data; + $tmp->dates[(new DateTime($s->datetime))->format('Y-m-d')] += $s->data; } - foreach ($tmp->dates as $k => $v){ + foreach ($tmp->dates as $k => $v) { $tmp->js[] = [ 'x' => $k, 'y' => $v ]; } - break; + break; case 'nested tally': - break; + $data = []; + foreach ($stat as $s) { + $tmp->rounds[] = $s->round_id; + foreach ($s->data as $name => $v) { + foreach ($v as $key => $value) { + if ($data[$name][$key]) { + $data[$name][$key] += $value; + continue; + } + + $data[$name][$key] = $value; + } + } + } + + arsort($data, SORT_NUMERIC); + $tmp->data = $data; + $tmp->output = $data; + break; case 'tally': - $data = []; - foreach($stat as $s){ - $tmp->rounds[] = $s->round_id; - $data = array_merge($data, $s->data); - arsort($data); - } - $tmp->output = $data; - break; + $data = []; + foreach ($stat as $s) { + $tmp->rounds[$s->round_id] = $s->data; + foreach ($s->data as $k => $v) { + if ($data[$k]) { + $data[$k] += $v; + continue; + } + + $data[$k] = $v; + $tmp->total += $v; + } + } + arsort($data, SORT_NUMERIC); + $tmp->data = $data; + break; } return $tmp; } - public function specialCases(&$stat){ - switch($stat->key_name){ + public function specialCases(&$stat) + { + switch ($stat->key_name) { case 'commendation': - foreach($stat->data as &$d){ - $d['id'] = strtoupper(substr(hash('sha512', $d['commendee'].$d['reason']), 0,6)); + foreach ($stat->data as &$d) { + $d['id'] = strtoupper(substr(hash('sha512', $d['commendee'] . $d['reason']), 0, 6)); + } + break; + + case 'time_dilation_current': + $stat->chartdata = new stdClass; + foreach ($stat->data as &$d) { + $current = current($d); + $key = key($d); + $d = $current; + $d['datetime'] = $key; + + $stat->chartdata->datetimes[] = $key; + $stat->chartdata->avg[] = $d['avg']; + $stat->chartdata->current[] = $d['current']; + } - break; + $stat->chartdata = json_encode($stat->chartdata); + break; case 'testmerged_prs': $stat->data = array_map("unserialize", array_unique(array_map("serialize", $stat->data))); - break; + break; } return $stat; diff --git a/src/Statbus/Models/Ticket.php b/src/Statbus/Models/Ticket.php index 6efee99..eddd900 100755 --- a/src/Statbus/Models/Ticket.php +++ b/src/Statbus/Models/Ticket.php @@ -4,19 +4,22 @@ use Statbus\Models\Player as Player; -class Ticket { +class Ticket +{ private $settings; - public function __construct(array $settings){ + public function __construct(array $settings) + { $this->settings = $settings; $this->lastDate = null; $this->pm = new Player($settings); } - public function parseTicket(&$ticket){ + public function parseTicket(&$ticket) + { $ticket->icon = "fa-ticket"; - if (!isset($ticket->s_rank)){ + if (!isset($ticket->s_rank)) { $ticket->s_rank = 'player'; $ticket->r_rank = 'player'; } @@ -30,13 +33,13 @@ public function parseTicket(&$ticket){ $ticket->recipient->ckey = $ticket->recipient_ckey; $ticket->recipient->rank = $ticket->r_rank; $ticket->recipient = $this->pm->parsePlayer($ticket->recipient); - - if(($ticket->sender->ckey && $ticket->recipient->ckey) && 'Ticket Opened' === $ticket->action){ + + if (($ticket->sender->ckey && $ticket->recipient->ckey) && 'Ticket Opened' === $ticket->action) { $ticket->bwoink = TRUE; } - if($this->lastDate){ - $interval = date('U',strtotime($ticket->timestamp)) - date('U',strtotime($this->lastDate)); + if ($this->lastDate) { + $interval = date('U', strtotime($ticket->timestamp)) - date('U', strtotime($this->lastDate)); $ticket->interval = date('i:s', $interval); } $ticket->class = 'danger'; @@ -45,7 +48,7 @@ public function parseTicket(&$ticket){ $ticket->message = strip_tags($ticket->message); $ticket->server_data = (object) $this->settings['servers'][array_search($ticket->ip, array_column($this->settings['servers'], 'ip'))]; - + #$server = array_keys(array_column($this->settings['servers'], 'port'), $ticket->port); #if(count($server) == 1) $ticket->server_data = (object) $this->settings['servers'][$server[0]]; #else $ticket->server_data = (object) $this->settings['servers'][array_keys(array_column($this->settings['servers'], 'ip'), long2ip($ticket->ip))]; @@ -57,15 +60,15 @@ public function parseTicket(&$ticket){ default: $ticket->class = "secondary"; $ticket->action_label = "Reply from "; - break; + break; case 'Ticket Opened': - if (null === $ticket->recipient->ckey){ + if (null === $ticket->recipient->ckey) { $ticket->recipient = FALSE; $ticket->class = "primary"; } $ticket->action_label = "Ticket Opened by"; - break; + break; case 'Resolved': $ticket->class = "success"; @@ -74,7 +77,7 @@ public function parseTicket(&$ticket){ $ticket->message = FALSE; $ticket->icon = "thumbs-up"; $ticket->type = "action"; - break; + break; case 'Closed': $ticket->class = "danger"; @@ -83,7 +86,7 @@ public function parseTicket(&$ticket){ $ticket->message = FALSE; $ticket->type = "action"; $ticket->icon = "times-circle"; - break; + break; case 'Rejected': $ticket->class = "danger"; @@ -92,7 +95,7 @@ public function parseTicket(&$ticket){ $ticket->message = FALSE; $ticket->type = "action"; $ticket->icon = "undo"; - break; + break; case 'IC Issue': $ticket->class = "dark"; @@ -101,7 +104,16 @@ public function parseTicket(&$ticket){ $ticket->message = FALSE; $ticket->type = "action"; $ticket->icon = "gavel"; - break; + break; + + case 'Skill Issue': + $ticket->class = "info"; + $ticket->action_label = "Marked as Skill issue by "; + $ticket->recipient = FALSE; + $ticket->message = FALSE; + $ticket->type = "action"; + $ticket->icon = "award"; + break; case 'Disconnected': $ticket->class = "dark"; @@ -111,7 +123,7 @@ public function parseTicket(&$ticket){ $ticket->message = FALSE; $ticket->type = "action"; $ticket->icon = "window-close"; - break; + break; case 'Reconnected': $ticket->class = "info"; @@ -121,60 +133,64 @@ public function parseTicket(&$ticket){ $ticket->message = FALSE; $ticket->type = "action"; $ticket->icon = "network-wired"; - break; + break; } - if(isset($ticket->status)){ - switch ($ticket->status){ + if (isset($ticket->status)) { + switch ($ticket->status) { case 'Ticket Opened': $ticket->status_class = 'info'; $ticket->icon = 'ticket-alt'; - // $ticket->status = "Open"; - break; + break; case 'Reply': $ticket->status_class = 'warning'; $ticket->icon = "reply"; - break; + break; case 'Resolved': $ticket->icon = 'thumbs-up'; $ticket->status_class = 'success'; - break; + break; case 'Closed': $ticket->icon = 'times-circle'; $ticket->status_class = 'danger'; - break; + break; case 'IC Issue': $ticket->status_class = "dark"; $ticket->icon = "gavel"; - break; + break; + + case 'Skill Issue': + $ticket->status_class = "info"; + $ticket->icon = "award"; + break; case 'Disconnected': $ticket->status_class = "dark"; $ticket->icon = "window-close"; - break; + break; case 'Reconnected': $ticket->status_class = "info"; $ticket->icon = "network-wired"; - break; + break; case 'Rejected': $ticket->status_class = "danger"; $ticket->icon = "undo"; - break; + break; default: - $ticket->status_class = 'success'; - // $ticket->status = "Resolved"; - break; + $ticket->status_class = 'danger'; + $ticket->icon = "question-circle"; + break; } } $this->lastDate = $ticket->timestamp; - + return $ticket; } } diff --git a/src/conf/Statbus.php b/src/conf/Statbus.php index 4d119fa..4d2ed18 100755 --- a/src/conf/Statbus.php +++ b/src/conf/Statbus.php @@ -1,131 +1,97 @@ getenv('APP') ?: 'Statbus', - 'UA' => getenv('UA') ?: null, - 'remote_log_src' => getenv('REMOTE_LOGS') ?: null, - 'github' => getenv('GITHUB') ?: null, - 'auth' => [ - 'remote_auth' => getenv('REMOTE_AUTH') ?: false, - 'oauth_start' => 'oauth_create_session.php', - 'token_url' => 'oauth.php', + 'app_name' => $_ENV['APP'] ?: 'Statbus', + 'UA' => $_ENV['UA'] ?? null, + 'remote_log_src' => $_ENV['REMOTE_LOGS'] ?? null, + 'github' => $_ENV['GITHUB'] ?? null, + 'auth' => [ + 'remote_auth' => $_ENV['REMOTE_AUTH'] ?? false, + 'oauth_start' => 'oauth_create_session.php', + 'token_url' => 'oauth.php', 'auth_session' => 'oauth_get_session_info.php' ], - 'ip_auth' => getenv('IP_AUTH') ?: false, - 'ip_auth_days' => getenv('IP_AUTH_DAYS') ?: 10, - 'perm_flags' => [ - 'BUILD' => (1<<0), - 'ADMIN' => (1<<1), - 'BAN' => (1<<2), - 'FUN' => (1<<3), - 'SERVER' => (1<<4), - 'DEBUG' => (1<<5), - 'POSSESS' => (1<<6), - 'PERMISSIONS' => (1<<7), - 'STEALTH' => (1<<8), - 'POLL' => (1<<9), - 'VAREDIT' => (1<<10), - 'SOUND' => (1<<11), - 'SPAWN' => (1<<12), - 'AUTOADMIN' => (1<<13), - 'DBRANKS' => (1<<14) + 'ip_auth' => $_ENV['IP_AUTH'] ?? false, + 'ip_auth_days' => $_ENV['IP_AUTH_DAYS'] ?: 10, + 'perm_flags' => [ + 'BUILD' => (1 << 0), + 'ADMIN' => (1 << 1), + 'BAN' => (1 << 2), + 'FUN' => (1 << 3), + 'SERVER' => (1 << 4), + 'DEBUG' => (1 << 5), + 'POSSESS' => (1 << 6), + 'PERMISSIONS' => (1 << 7), + 'STEALTH' => (1 << 8), + 'POLL' => (1 << 9), + 'VAREDIT' => (1 << 10), + 'SOUND' => (1 << 11), + 'SPAWN' => (1 << 12), + 'AUTOADMIN' => (1 << 13), + 'DBRANKS' => (1 << 14) ], 'ranks' => [ 'Coder' => [ 'backColor' => '#31b626', 'foreColor' => '#FFF', - 'icon' => 'code' + 'icon' => 'code' ], 'Admin' => [ 'backColor' => '#9b59b6', 'foreColor' => '#FFF', - 'icon' => 'asterisk' + 'icon' => 'asterisk' ], ], - 'servers' => [ + 'servers' => [ [ - 'port'=>1234, - 'name'=>'Server' + 'port' => 1234, + 'name' => 'Server' ] ], - 'election_mode' => false, - 'election_officer' => getenv('ELECTION_OFFICER') ?: false, - 'bug_reports' => getenv('BUG_REPORTS') ?: false, + 'election_mode' => false, + 'election_officer' => $_ENV['ELECTION_OFFICER'] ?: false, + 'candidates' => ['marksuckerberg', 'thgvr'], + 'bug_reports' => $_ENV['BUG_REPORTS'] ?: false, 'mode_icons' => [ - 'Abduction'=>'street-view', - 'Ai Malfunction'=>'network-wired', - 'Arching Operation'=>'', - 'Assimilation'=>'brain', - 'Blob'=>'cubes', - 'Changeling'=>'spider', - 'Clockwork Cult'=>'cog', - 'Clown Ops'=>'angry', - 'Cult'=>'book-dead text-danger', - 'Devil'=>'handshake', - 'Devil Agents'=>'handshake', - 'Double Agents'=>'user-ninja', - 'Dynamic Mode'=>'dumpster-fire', - 'Everyone Is The Traitor And Also'=>'', - 'Extended'=>'user-astronaut', - 'Extended Events'=>'', - 'Families'=>'', - 'Gang War'=>'', - 'Gang War No Security'=>'', - 'Hand Of God'=>'', - 'Infiltration'=>'user-tie', - 'Internal Affairs'=>'user-secret', - 'Jeffjeff'=>'', - 'Just Fuck My Shit Up'=>'', - 'Meteor'=>'meteor', - 'Monkey'=>'dizzy', - 'Nuclear Emergency'=>'bomb', - 'Overthrow'=>'frown-open', - 'Ragin\' Mages'=>'magic', - 'Revolution'=>'fist-raised', - 'Rod Madness'=>'slash', - 'Sandbox'=>'grin-stars', - 'Secret Extended'=>'bed', - 'Shadowling'=>'users', - 'Speedy_revolution'=>'', - 'Traitor'=>'skull-crossbones', - 'Traitor+brothers'=>'user-injured', - 'Traitor+changeling'=>'user-astronaut', - 'Very Ragin\' Bullshit Mages'=>'cloud-sun', - 'Vigilante Gang War'=>'', - 'Wizard'=>'hat-wizard' - ], - 'jobs' => [ - 'AI', - 'Assistant', - 'Atmospheric Technician', - 'Bartender', - 'Botanist', - 'Captain', - 'Cargo Technician', - 'Chaplain', - 'Chemist', - 'Chief Engineer', - 'Chief Medical Officer', - 'Clown', - 'Cook', - 'Curator', - 'Cyborg', - 'Detective', - 'Geneticist', - 'Head of Personnel', - 'Head of Security', - 'Janitor', - 'Lawyer', - 'Librarian', - 'Medical Doctor', - 'Mime', - 'Quartermaster', - 'Research Director', - 'Roboticist', - 'Scientist', - 'Security Officer', - 'Shaft Miner', - 'Station Engineer', - 'Virologist', - 'Warden' + 'Abduction' => 'street-view', + 'Ai Malfunction' => 'network-wired', + 'Arching Operation' => '', + 'Assimilation' => 'brain', + 'Blob' => 'cubes', + 'Changeling' => 'spider', + 'Clockwork Cult' => 'cog', + 'Clown Ops' => 'angry', + 'Cult' => 'book-dead text-danger', + 'Devil' => 'handshake', + 'Devil Agents' => 'handshake', + 'Double Agents' => 'user-ninja', + 'Dynamic Mode' => 'dumpster-fire', + 'Everyone Is The Traitor And Also' => '', + 'Extended' => 'user-astronaut', + 'Extended Events' => '', + 'Families' => '', + 'Gang War' => '', + 'Gang War No Security' => '', + 'Hand Of God' => '', + 'Infiltration' => 'user-tie', + 'Internal Affairs' => 'user-secret', + 'Jeffjeff' => '', + 'Just Fuck My Shit Up' => '', + 'Meteor' => 'meteor', + 'Monkey' => 'dizzy', + 'Nuclear Emergency' => 'bomb', + 'Overthrow' => 'frown-open', + 'Ragin\' Mages' => 'magic', + 'Revolution' => 'fist-raised', + 'Rod Madness' => 'slash', + 'Sandbox' => 'grin-stars', + 'Secret Extended' => 'bed', + 'Shadowling' => 'users', + 'Speedy_revolution' => '', + 'Traitor' => 'skull-crossbones', + 'Traitor+brothers' => 'user-injured', + 'Traitor+changeling' => 'user-astronaut', + 'Very Ragin\' Bullshit Mages' => 'cloud-sun', + 'Vigilante Gang War' => '', + 'Wizard' => 'hat-wizard' ] ]; \ No newline at end of file diff --git a/src/conf/stat_filters.json b/src/conf/stat_filters.json index 6a6c4b1..4b36eba 100755 --- a/src/conf/stat_filters.json +++ b/src/conf/stat_filters.json @@ -1,5 +1,5 @@ { - "admin_cookies_spawned":{}, + "admin_cookies_spawned": {}, "admin_secrets_fun_used": { "label": { "key": "Button Pressed", @@ -7,151 +7,159 @@ "subvalue": "Conditions" } }, - "admin_toggle":{ + "admin_toggle": { "label": { "key": "Verb", "value": "Times Used", "subvalue": "Conditions" } }, - "admin_verb":{ + "admin_verb": { "label": { "key": "Verb", "value": "Times Used", "subvalue": "Conditions", - "total":"Total Verbs Used" + "total": "Total Verbs Used" } }, - "ahelp_stats":{ - "label":{ + "ahelp_stats": { + "label": { "key": "Ahelp Status", "value": "Times", "subvalue": "Conditions", - "total":"Total Ahelps" + "total": "Total Ahelps" } }, - "ais_created":{}, - "antagonists":{}, - "arcade_results":{}, - "assembly_made":{ + "ais_created": {}, + "antagonists": {}, + "arcade_results": {}, + "assembly_made": { "label": { "key": "Assembly", "value": "Number Made", "subvalue": "Conditions", - "total":"Total Assemblies Made" + "total": "Total Assemblies Made" } }, - "brother_success":{}, - "byond_build":{}, - "byond_version":{}, - "cargo_imports":{}, - "cell_used":{ + "brother_success": {}, + "byond_build": {}, + "byond_version": {}, + "cargo_imports": {}, + "cell_used": { "label": { "key": "Cell", "value": "Number Used", - "total":"Total Cells Used" + "total": "Total Cells Used" } }, - "changeling_objective":{}, - "changeling_powers":{}, - "changeling_power_purchase":{ + "changeling_objective": {}, + "changeling_powers": {}, + "changeling_power_purchase": { "label": { "key": "Power", "value": "Times Purchased", - "total":"Total Powers Purchased" + "total": "Total Powers Purchased" } }, - "changeling_success":{}, - "chaplain_armor":{}, - "chaplain_weapon":{}, - "chemical_reaction":{ + "changeling_success": {}, + "chaplain_armor": {}, + "chaplain_weapon": {}, + "chemical_reaction": { "label": { "key": "Reagent", "value": "Units created", - "total":"Total Units Created" + "total": "Total Units Created" } }, - "circuit_printed":{}, - "client_byond_version":{ + "circuit_printed": {}, + "client_byond_version": { "label": { "key": "Version", "value": "Times Seen", - "total":"Useless number of clients seen" + "total": "Useless number of clients seen" } }, - "clockcult_scripture_recited":{}, - "colonies_dropped":{}, - "commendation":{}, - "contaminated":{ + "clockcult_scripture_recited": {}, + "colonies_dropped": {}, + "commendation": {}, + "contaminated": { "label": { "key": "Item/Structure", "value": "Number Irridated", - "total":"Total Items/Structures Irridiated" + "total": "Total Items/Structures Irridiated" } }, - "cult_objective":{}, - "cult_runes_scribed":{ + "credits": { + "label": { + "key": "Source", + "value": "Credits", + "total": "Unspent Credits", + "unit": "cr" + } + }, + "cult_objective": {}, + "cult_runes_scribed": { "label": { "key": "Rune", "value": "Number Scribed", - "total":"Total Runes Scribed" + "total": "Total Runes Scribed" } }, - "cyborg_birth":{ - "label":{ - "splain":"I. LIVE. AGAIN." + "cyborg_birth": { + "label": { + "splain": "I. LIVE. AGAIN." } }, - "cyborg_frames_built":{}, - "cyborg_modules":{}, - "dm_version":{}, - "engine_started":{}, - "event_admin_cancelled":{ + "cyborg_frames_built": {}, + "cyborg_modules": {}, + "dm_version": {}, + "engine_started": {}, + "event_admin_cancelled": { "label": { "key": "Event", "value": "Cancellations", - "total":"Total Events Canceled" + "total": "Total Events Canceled" } }, - "event_ran":{ + "event_ran": { "label": { "key": "Event", "value": "Times Ran", - "total":"Total Events Run" + "total": "Total Events Run" } }, - "explosion":{ + "explosion": { "label": { - "splain":"Numbers in red are values without bombcap applied" + "splain": "Numbers in red are values without bombcap applied" } }, - "export_sold_cost":{ + "export_sold_cost": { "label": { "key": "Item", "value": "Value", - "total":"Total Exported" + "total": "Total Exported" } }, - "food_harvested":{}, - "food_made":{}, - "gorillas_created":{}, - "gun_fired":{}, - "handcuffs":{}, - "high_research_level":{}, - "hivelord_core":{}, - "ic_blocked_words":{}, - "immortality_talisman_uses":{}, - "item_deconstructed":{}, - "item_printed":{ + "food_harvested": {}, + "food_made": {}, + "gorillas_created": {}, + "gun_fired": {}, + "handcuffs": {}, + "high_research_level": {}, + "hivelord_core": {}, + "ic_blocked_words": {}, + "immortality_talisman_uses": {}, + "item_deconstructed": {}, + "item_printed": { "label": { "key": "Printer Path", "subvalue": "Printed Item", "value": "Count", - "filter": "/obj/item||/obj/item", - "splain": "/obj/item removed for readability purposes" + "filter": ["/obj/item", "/obj/machinery/rnd/production"], + "splain": "/obj/item and /obj/machinery/rnd/production removed for readability purposes" } }, - "item_used_for_combat":{ + "item_used_for_combat": { "label": { "key": "Weapon", "value": "Times Used", @@ -160,29 +168,29 @@ "splain": "/obj/item removed for readability purposes" } }, - "jaunter":{}, - "job_preferences":{}, - "keycard_auths":{ + "jaunter": {}, + "job_preferences": {}, + "keycard_auths": { "label": { "key": "Protocol", "value": "Times Authorized", "subvalue": "Conditions" } }, - "lazarus_injector":{}, - "mechas_created":{}, - "megafauna_kills":{}, - "megafauna_kills_crusher":{}, - "mining_equipment_bought":{}, - "mining_voucher_redeemed":{}, - "mmis_filled":{}, - "mobs_killed_mining":{}, - "newscaster_channels":{}, - "newscaster_stories":{}, - "newspapers_printed":{}, - "nuclear_challenge_mode":{}, - "object_crafted":{}, - "ore_mined":{ + "lazarus_injector": {}, + "mechas_created": {}, + "megafauna_kills": {}, + "megafauna_kills_crusher": {}, + "mining_equipment_bought": {}, + "mining_voucher_redeemed": {}, + "mmis_filled": {}, + "mobs_killed_mining": {}, + "newscaster_channels": {}, + "newscaster_stories": {}, + "newspapers_printed": {}, + "nuclear_challenge_mode": {}, + "object_crafted": {}, + "ore_mined": { "label": { "key": "Ore", "value": "Units Mined", @@ -190,75 +198,81 @@ "splain": "/obj/item/stack removed for readability purposes" } }, - "pick_used_mining":{}, - "played_url":{}, - "preferences_verb":{}, - "radio_usage":{ + "pick_used_mining": {}, + "played_url": {}, + "preferences_verb": {}, + "radio_usage": { "label": { "key": "Channel", "value": "Messages Transmitted", "total": "Total Messages Transmitted" } }, - "random_seed":{}, - "religion_book":{}, - "religion_deity":{}, - "religion_name":{}, - "roundend_nukedisk":{}, - "round_end_stats":{}, - "science_techweb_unlock":{}, - "science_techweb_unlock_first_thi":{}, - "SDQL query":{ + "random_seed": {}, + "religion_book": {}, + "religion_deity": {}, + "religion_name": {}, + "roundend_nukedisk": {}, + "round_end_stats": {}, + "science_techweb_unlock": {}, + "science_techweb_unlock_first_thi": {}, + "SDQL query": { "label": { "key": "Admin", - "value":"Times Executed", + "value": "Times Executed", "subvalue": "Query", "total": "Times Executed" } }, - "security_level_changes":{}, - "server_tools":{}, - "server_tools_api":{}, - "shuttle_fasttravel":{}, - "shuttle_gib":{}, - "shuttle_manipulator":{}, - "shuttle_purchase":{}, - "shuttle_reason":{}, - "slime_babies_born":{}, - "slime_cores_used":{}, - "slime_core_harvested":{}, - "spacepod_created":{}, - "station_renames":{}, - "surgeries_completed":{}, - "teams":{}, - "testmerged_prs":{}, - "time_dilation_current":{}, - "traitor_objective":{}, - "traitor_random_uplink_items_gott":{}, - "traitor_success":{}, - "traitor_uplink_items_bought":{ + "security_level_changes": {}, + "server_tools": {}, + "server_tools_api": {}, + "ship_purchased": { + "label": { + "key": "Ship Name", + "value": "Times Purchased" + } + }, + "shuttle_fasttravel": {}, + "shuttle_gib": {}, + "shuttle_manipulator": {}, + "shuttle_purchase": {}, + "shuttle_reason": {}, + "slime_babies_born": {}, + "slime_cores_used": {}, + "slime_core_harvested": {}, + "spacepod_created": {}, + "station_renames": {}, + "surgeries_completed": {}, + "teams": {}, + "testmerged_prs": {}, + "time_dilation_current": {}, + "traitor_objective": {}, + "traitor_random_uplink_items_gott": {}, + "traitor_success": {}, + "traitor_uplink_items_bought": { "label": { "key": "Item", - "subvalue":"Cost", + "subvalue": "Cost", "value": "Number Purchased" } }, - "traumas":{}, - "vending_machine_usage":{ + "traumas": {}, + "vending_machine_usage": { "label": { "key": "Vending Machine", - "subvalue":"Item Vended", + "subvalue": "Item Vended", "value": "Number Vended", - "filter":["/obj/machinery/vending/","/obj/item"], - "splain":"/obj/machinery/vending/ and /obj/item removed for readability purposes" + "filter": ["/obj/machinery/vending", "/obj/item"], + "splain": "/obj/machinery/vending and /obj/item removed for readability purposes" } }, - "voice_of_god":{}, - "warp_cube":{}, - "wisp_lantern":{}, - "wizard_objective":{}, - "wizard_spell_improved":{}, - "wizard_spell_learned":{}, - "wizard_success":{}, - "zone_targeted":{} + "voice_of_god": {}, + "warp_cube": {}, + "wisp_lantern": {}, + "wizard_objective": {}, + "wizard_spell_improved": {}, + "wizard_spell_learned": {}, + "wizard_success": {}, + "zone_targeted": {} } diff --git a/src/dependencies.php b/src/dependencies.php index 9169956..0fc01da 100755 --- a/src/dependencies.php +++ b/src/dependencies.php @@ -1,18 +1,26 @@ getContainer(); @@ -35,7 +43,7 @@ //Crsf $container['csrf'] = function ($c) { - $csrf = new \Slim\Csrf\Guard('sb_csrf'); + $csrf = new Guard('sb_csrf'); $csrf->setFailureCallable(function ($request, $response, $next) { $request = $request->withAttribute("csrf_status", false); return $next($request, $response); @@ -47,32 +55,31 @@ $container['view'] = function ($container) { $settings = $container->get('settings')['twig']; - $view = new \Slim\Views\Twig($settings['template_path'], [ - 'debug' => $settings['twig_debug'], - 'cache' => $settings['template_cache'] + $view = new Twig($settings['template_path'], [ + 'debug' => $settings['twig_debug'], + 'cache' => $settings['template_cache'] ]); // Instantiate and add Slim specific extension $router = $container->get('router'); - $uri = \Slim\Http\Uri::createFromEnvironment(new \Slim\Http\Environment($_SERVER)); - $view->addExtension(new \Slim\Views\TwigExtension($router, $uri)); - - $view->addExtension(new Statbus\Extensions\CsrfExtension($container->get('csrf'))); + $uri = Uri::createFromEnvironment(new Environment($_SERVER)); + $view->addExtension(new TwigExtension($router, $uri)); - $view->addExtension(new \Twig_Extension_Debug()); + $view->addExtension(new DebugExtension); + $view->addExtension(new IntlExtension); //Fancy timestamp filter - $twigTimestampFilter = new \Twig_Filter('timestamp', function ($string) { + $twigTimestampFilter = new TwigFilter('timestamp', function ($string) { $string = date('Y-m-d H:i:s', strtotime($string)); $return = ""; - $return.= "