diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index e93257f1..4dd8e9dd 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -22,7 +22,7 @@ jobs: strategy: fail-fast: false matrix: - php-versions: [ '8.1', '8.2', '8.3', '8.4' ] + php-versions: [ '8.1', '8.2', '8.3', '8.4', '8.5' ] ots: ['tfs-1.4', 'canary-3.1.2'] # TODO: add 'tfs-master' (actually doesn't work cause AAC doesn't support reading .env configuration) name: Cypress (PHP ${{ matrix.php-versions }}, ${{ matrix.ots }}) steps: diff --git a/.gitignore b/.gitignore index b198856f..7da27c57 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ Thumbs.db # /.htaccess -lua +/lua # composer composer.phar @@ -24,6 +24,7 @@ releases tmp config.local.php +config2.local.php # all custom templates templates/* diff --git a/CHANGELOG-1.x.md b/CHANGELOG-1.x.md index 291b5441..0d3777f8 100644 --- a/CHANGELOG-1.x.md +++ b/CHANGELOG-1.x.md @@ -1,5 +1,183 @@ # Changelog +## [1.8.5 - 21.11.2025] + +### Added +* New Setting: Account Countries Most Popular (https://github.com/slawkens/myaac/commit/946364f59d7cd01472877108ab27ec78fb28307a) + +### Changed +* Status: Write to status-error.log if there is connection error (https://github.com/slawkens/myaac/commit/780d4ccef741c1dd45a00bfc121fba9f1a175313) +* Settings: escapeHtml in values (support for html code) (https://github.com/slawkens/myaac/commit/5861efdbe900ccd35309913af0c0a5f3d4cdc1a8) +* News Page: Don't display hidden news for admin - it's confusing (https://github.com/slawkens/myaac/commit/175e97828b9a08ec3080cc8d3fb4eb3f1c08649f) +* Plugins System: Add plugin:remove + plugin:delete as alias for plugin:uninstall + plugin:activate/deactivate (https://github.com/slawkens/myaac/commit/6367054487368c92741bfd1dc7c70c52aea9ee87, https://github.com/slawkens/myaac/commit/baec6c9ebf5c342b3b2f7123427c6ba21dbb93bc) + +### Fixed +* Status: Fix $status['uptimeReadable'], was totally wrong (https://github.com/slawkens/myaac/commit/0a6d44bf21417562491aabc93543a2bc3a44b2df) +* Guilds: Detect "deletion" column in guilds show/delete (https://github.com/slawkens/myaac/commit/6775a061bebc9ff449522f0173556d4a7a44fa5e, https://github.com/slawkens/myaac/commit/603d860b56bc7418db09e206f40aa06d0682c00e) +* General: Ensure some cache folders & index.html exists (https://github.com/slawkens/myaac/commit/730a0f29124811f525207c24c06eb0d088fa3434) + +## [1.8.4 - 27.10.2025] + +### Changed +* Reimport myaac_ tables on every install, this fixes errors when one table is missing or is duplicated (https://github.com/slawkens/myaac/commit/2580edadf84779f09fd395c21f92019b2c762f83) +* Use custom env init on migrate, migrate:run and migrate:to (https://github.com/slawkens/myaac/commit/13ea68cc0c9349380c8e4051d702a6c2c8256f44, https://github.com/slawkens/myaac/commit/07fd034fe4cb0ffdb88667b1e400f414d0c6d06f) + +### Fixed +* Show if there is mysql error on import schema (https://github.com/slawkens/myaac/commit/44110a9496b4385e42c31b75de301037e711b6c3) +* Fix the premium checks, introduced in v1.8.3 (https://github.com/slawkens/myaac/commit/9d92a11fb7cb6d7a1619d79c12faaa0b1c01f980) + +## [1.8.3 - 21.10.2025] + +### Added +* Feature: resend email verify (https://github.com/slawkens/myaac/commit/fe821c58085483e70491dcf76376ad5b96de3fdd) +* New config: hooks_debug (To view where hooks are located in .twig files) (https://github.com/slawkens/myaac/commit/8c3cb0e06f9709c1de3398b48221241e7cbdd310) +* Functions: Add db->getColumnInfo(table, column) (https://github.com/slawkens/myaac/commit/c898fe25efff6793a01d11c26fc153cb23fcb858) +* Plugins: Add option to use ?subtopic=x for plugins pages (https://github.com/slawkens/myaac/commit/97f9d3d6f6c28aef6d824973058d7133f56e09c4) +* getTopPlayers() Function - Add lookmount & promotion (https://github.com/slawkens/myaac/commit/2da0024c68f1cedc38a16ebbc6f52ffa55e65f7a, https://github.com/slawkens/myaac/commit/901df48d134079d648a18f9d82b60182e818ac02) +* New hooks for account/change-password (https://github.com/slawkens/myaac/commit/470555f2687809a0c12491bbb27597e64b8929c1) + +### Changed +* Feature: show vip days in account management (https://github.com/slawkens/myaac/commit/c88b08eb1ec1f560cbfdaaa16b24e3a0f26da7b3, by @andreoam) +* Allow links in error_box.html.twig (https://github.com/slawkens/myaac/commit/9acad15451071639acf7a7d4e81619b0a9742b12) +* Canary - Comment code to update lastday in login.php (https://github.com/slawkens/myaac/commit/38902c30d114fdbce259467f5820f97037b393e9) +* Cache::remember $ttl = -1 = infinite (https://github.com/slawkens/myaac/commit/64acf70d3854182d88aaf0b67f77cea2a254f179) + +### Fixed +* Online - Allow for html code (example - img) in online_datacenter (https://github.com/slawkens/myaac/commit/3bb272ebbbd2eb7769d174b7082061d14a17bd44) +* Guilds - Fix guild create with freePremium enabled (https://github.com/slawkens/myaac/commit/c91bb5d4097647dca2196d3dea87bc90c89181d2) +* Canary - Fix premDays count (https://github.com/slawkens/myaac/commit/3e61692780d4add93b7b0e9f12f7a283bd8f4b7a) +* Template Change: Ignore set last visit for AJAX pages - Fixes template change redirect (https://github.com/slawkens/myaac/commit/89fae38caa7e4f645957fcf1a9330a36358ac04f) +* Admin Panel - Accounts: Fix lastip v6 (TFS master) (https://github.com/slawkens/myaac/commit/f54b1bdd2af4c16c64ddff0e87a6c96bc4cf9eeb) +* Functions - Prevent injection in $db->hasColumn (https://github.com/slawkens/myaac/commit/56bd7ec5ed904666074492f2e4f13e4fce226bee) +* Compat Config: Add missing config: email_lai_sec_interval (https://github.com/slawkens/myaac/commit/2eae44e0755e624a91be68b4d1ec26d01eb4d9a1) + +## [1.8.2 - 26.09.2025] + +### Added +* Routes: Possibility to override routes with plugins pages, like characters.php - No need to define routes in plugin.json anymore (https://github.com/slawkens/myaac/commit/3f24f961b1cdeff5c60387e837ae454448bc5e1b) + +### Changed +* Style: Better look for myaac-table (https://github.com/slawkens/myaac/commit/a6032093b21e5bb3f0e75d2704da87d6dea6469d, https://github.com/slawkens/myaac/commit/5aa9bbf1c8e580d973ec82ac012489f8e7bc437e) + +### Fixed +* Install: Fix when config.local.php cannot be saved (https://github.com/slawkens/myaac/commit/4eab805d26d8c5562b29ed699769919d77dabced) +* Create Account: Fix an exception when email cannot be sent (https://github.com/slawkens/myaac/commit/d0112d1a67e8b854b65ad131f0375b79305df8d3) +* Login Page: Add missing csrf() - fix create account button (https://github.com/slawkens/myaac/commit/3c0cb53e17dd0b85394cfa0fdc9cf9ad8d4551df) +* tibiacom template: Fix account lost menu (https://github.com/slawkens/myaac/commit/ed9beaf2b6ca069e304e569c52e5b9188b58f05c) +* tibiacom template: Fix Menu div wrong tag/closing (#329) (https://github.com/slawkens/myaac/commit/85e7005fd3f0be51466151a3c122b96085fdfe68) +* tibiacom template: Replace firstChild with firstElementChild (Thanks to @un000000) (https://github.com/slawkens/myaac/commit/df7b6e29fb8875da97f431468c81ee99116271d9) + +## [1.8.1 - 05.09.2025] + +### Added +* New Commands: plugin:enable/disable/uninstall {plugin-name} (https://github.com/slawkens/myaac/commit/7a08f91d3fc0897c1ff76089ef3c649a2c6d2003, https://github.com/slawkens/myaac/commit/fec773ba4b740f35c0a3ef92ca8444a4c7d02082) +* Gifts: Added Transferable Coins to the store dropdown menu in the admin area (by @andreoam, #321) (https://github.com/slawkens/myaac/commit/42671c5c199dd9e91c774d8c9d30da9e12f1b695) + +### Changed +* Commands: Allow settings to be changed/reset by plugin name (https://github.com/slawkens/myaac/commit/f8c4332e03e838d285ea0afb4b72b7c23e324d45, https://github.com/slawkens/myaac/commit/4b948e9510f7ba69d00f84d7fdaea8b3bf05b630) +* Templates: Menus should be saved for each template separately (https://github.com/slawkens/myaac/commit/482f4067b2a2e7513d9ba214274a361ffaf123d8) + +### Fixed +* Online: Fix skulls display (#320) (https://github.com/slawkens/myaac/commit/98073a110ae13f9592ec9d2c4d1d1aace87587a9) +* Online: Fix if there is no world_id in the server_record table (https://github.com/slawkens/myaac/commit/b6e1620f14c20eecfc9001a7d86dfb67942985c6) (Reported by @gesior in #318) +* tibiacom: some fixes to menus (https://github.com/slawkens/myaac/commit/20f99903ae80c74ad66c1cf5a5ea8d0b0fc2fd70, https://github.com/slawkens/myaac/commit/11dae90fa94fbbf47447017db5e5847c33d6aadf) +* Guilds: Fix for some servers that don't have guild_invites table (https://github.com/slawkens/myaac/commit/9725a3c2bdb7003f5cb48febb77604c31a9b805b) + +## [1.8 - 02.08.2025] + +### Added +* Templates - Kathrine: Possibility to add custom menu categories (https://github.com/slawkens/myaac/commit/ec11c1402417c25980582467546d1c1e9bb8267f) +* Admin Panel - Accounts Editor: Add Coins Transferable (https://github.com/slawkens/myaac/commit/45d6047031c9c3a0e7e512dc5d15c75629aec5a2, https://github.com/slawkens/myaac/commit/bb097b69ce106500a49686d6f4fe604348eaa310) +* Highscores: + * Revamped: (https://github.com/slawkens/myaac/commit/d8132d4d76e03d5aa0c042be426320655a601392) + * Show real rank, if 2 or more players have the same skill, show them with same rank + * New setting: highscores_online_status + * Additional fields passed to twig: updatedAt, totalResults, page, baseLink + * Add new Setting: Display Skills Box (https://github.com/slawkens/myaac/commit/36ca755243ef1c83f6ac87465b426d4d8d3b0bb9) +* Functions: Add getExperienceForLevel (level) (https://github.com/slawkens/myaac/commit/1566deb84a082176b8c683fda205d828bc38fbcc) +* Commands - cache:clear : Add warning about APCu clear in CLI (https://github.com/slawkens/myaac/commit/83f84172e02e8ea2ccb6dca29bc033e44c35aebc) +* Models - PlayerOnline: Add missing $fillable into model (https://github.com/slawkens/myaac/commit/43415cf35db1c1307f2684c1728693d65065ffff) +* Twig: add cache variable (https://github.com/slawkens/myaac/commit/0efe47ce71c4b364a9e96bc5a55b1655326ae6da) + +### Changed +* pages/online: add cache, resulting in 20x performance boost + * (for an example server with 2k players) (https://github.com/slawkens/myaac/commit/c8363086015cbb6e8786c398c7b9ac3959a26ec4) +* Admin Bar: Move admin bar code into body_start place_holder (https://github.com/slawkens/myaac/commit/f17269e44ce9dd38447bd2e2a8e1bdb065d4161f) +* Cache::remember: $ttl = 0 means no cache (https://github.com/slawkens/myaac/commit/3b47e9df2f4051807c5ff87892f7fa3d348f9c55) +* Templates: Load config.ini with $process_sections set to true (https://github.com/slawkens/myaac/commit/a89f9a84847630eb75b4890fdcc8b7a7bfa6b8ac) +* Twig: Allow for timestamp as integer in the timeago twig function + (https://github.com/slawkens/myaac/commit/34fead906ea13b9f09d7a3c41ed88109d34d386c) + +### Fixed +* Settings: Fixed two exceptions (https://github.com/slawkens/myaac/commit/6e5a4ff8c78ff5373aba091baa66cae029557643, https://github.com/slawkens/myaac/commit/20d69a641c0a933d14889a89da6d32f6a4bc6c7d) +* Models\Account + OTS_Account -> isPremium -> ignore config.freePremium (https://github.com/slawkens/myaac/commit/5271633bdbfbbfed0b1d59c403093ce6fc2b7d20) +* Admin Panel - Mailer: + * Fix send to email link redirecting from accounts page (https://github.com/slawkens/myaac/commit/080cc2781f034c844af658229e495e9a47fd2298) + * Option to send only to verified accounts - only if setting('core.account_mail_verify') enabled (https://github.com/slawkens/myaac/commit/cf7fd20452e863980045bb5d6012ec86c6e8e01f) + +### Internal +* Rewrite to use constants (account transferable coins) (https://github.com/slawkens/myaac/commit/bccf8e056df985bbe1bab5f7ab5492f714d6b62b) +* Refactor to use HAS_ACCOUNT_COINS (https://github.com/slawkens/myaac/commit/caf326a6584a234775ebc6c8000ea02b3fecd160) + +## [1.7.1 - 27.06.2025] + +### Changed +* Rename plugin:install:install to plugin:setup, also add alias to previous command (https://github.com/slawkens/myaac/commit/13d33822b59df349199e885a78a3d6beb0863d0b) + +### Fixed +* Fix commands: setup + cache:clear (https://github.com/slawkens/myaac/commit/0da524fefe93b3028392e9014550eea3324d3a22, https://github.com/slawkens/myaac/commit/fe8281594e989f00280ba1adc734a9198c6b5cc1) +* Fix polls link in tibiacom template (https://github.com/slawkens/myaac/commit/d90fa323d7c77d81768df60feeb1c374b1650a0c) + +## [1.7 - 22.06.2025] + +### Added +* Feature: plugins versions check (#310) +* New hooks: HOOK_ACCOUNT_MANAGE_AFTER_CHARACTERS, HOOK_GUILDS_AFTER_MANAGE_BUTTON (https://github.com/slawkens/myaac/commit/c074a48f245df55646b6705737f667b6a84149b2, https://github.com/slawkens/myaac/commit/e6100a1b72de8695bba1dae9ba4e28bfdce47b10) +* Add OTS_Toolbox::getVocationName(id, promotion) + OTS_Player->isNameLocked() (https://github.com/slawkens/myaac/commit/e222957893c4a1de0dc8dbba55bce1a43418d275, https://github.com/slawkens/myaac/commit/522f6c11d835afd36fd07a07074d96d7e219b488) +* Add missing csrf in more places, causing white page with error about Request (https://github.com/slawkens/myaac/commit/dca904e61d21d856bf809070e7652803a2df0f58, https://github.com/slawkens/myaac/commit/c720ccc451ff90ef40b2a1595468d061ffd7e1e4) + +### Changed +* Revamped online page (https://github.com/slawkens/myaac/commit/9a90e4aae280e607430511c6727d9a714b11f4c5, https://github.com/slawkens/myaac/commit/4767120043b09141870383e249f3729638d53dc2) +* Better $title inventing (https://github.com/slawkens/myaac/commit/0c95bcfd06b68b21512e477646ef7bd3a0d4912b) + +### Fixed +* Use apcu cache clear (https://github.com/slawkens/myaac/commit/b329da52aae9d0e21120a6444d3caf442420ce50, https://github.com/slawkens/myaac/commit/566c2a9151ab6392286f74e26853faa19a1b4f24) +* fix: boostedcreatures for 13.40 (by @GooseWithAKnife) (#307) + +## [1.6.1 - 11.06.2025] + +### Fixed +* Fixed "Request has been cancelled due to security reasons", cause of missing csrf() in twig files (https://github.com/slawkens/myaac/commit/10cd71a6630ffec91b43a26a6d685b66c5836a6a) +* Fix: Ignore duplicated route exception (https://github.com/slawkens/myaac/commit/9d8e9d27bd87167d8d4005942a6af62bfe4c0892) + +### Changed +* Move counter & visitors code before router (In case someone wants to include that info on page) (https://github.com/slawkens/myaac/commit/f78285030708ad3c74ab048711f73bbf3ee5281e) +* Set TinyMCE license key to gpl (Avoid warning message in browser console) (https://github.com/slawkens/myaac/commit/8d29fdb98b92dbc3d2853ef88a185c67036b4a77) + +### Removed +* Remove deprecated TinyMCE plugin - template (https://github.com/slawkens/myaac/commit/309c1fb715b882e67cb673b1544a03befbf64a22) + +## [1.6 - 03.06.2025] + +### Added +* Add new setting/configurable: site_url, prevents domain spoofing (https://github.com/slawkens/myaac/commit/d8a6090be382c35c19117cfef964b594ed02b8d4) +* Add new account coins setting (https://github.com/slawkens/myaac/commit/28886551e86fe562172c4c7f2afb89a2e7672c2e) +* autoload: settings/install/init.php (https://github.com/slawkens/myaac/commit/e5749437074c3b3556628a2aeb5bad2edf97bde0, https://github.com/slawkens/myaac/commit/7d213f479a7e40c6254069b5fc4e578dc32bf8d9, https://github.com/slawkens/myaac/commit/207d6bc69120aba1af2b51808f17e0059b571fed) +* Protect against csrf in more places (accounts & guilds & forums pages) (https://github.com/slawkens/myaac/commit/6eda38603c8ed7e99b92a78a4600b1245377f74d, https://github.com/slawkens/myaac/commit/e776bd52beb3064a9e694efd1b9021ec972ee2f6, https://github.com/slawkens/myaac/commit/84d502bf105f2a789481fba1acc820d236b4de66) +* Added two new hooks for pages loaded from database (custom pages): HOOK_BEFORE_PAGE_CUSTOM, HOOK_AFTER_PAGE_CUSTOM (https://github.com/slawkens/myaac/commit/c961a1ebf837f2ab1734a825ff2c57b4937610c9) +* Add global variables into $hooks->executeFilter (https://github.com/slawkens/myaac/commit/8fdea943768b20193eede99d60313ee84511a0be) +* Add getNPCsCount() to OTS_InfoRespond (https://github.com/slawkens/myaac/commit/7d435ff6433ef1fb2295ee79ed043ee10dc725e9) + +### Fixed +* Allow [] in character name (https://github.com/slawkens/myaac/commit/de6603a51347b9e656c58637ed9971fffdd7cedd) +* Do not allow access to tools/ folder after install (https://github.com/slawkens/myaac/commit/6e0f5913831f8dba69fd2d1505be3e2a303c6324) +* Fix CHANGELOG-1.x.md loading in admin panel (https://github.com/slawkens/myaac/commit/4a30fb495dbfbe1d434e8d52419eaf44fe517aee) +* Fix links not working in admin dashboard modules (https://github.com/slawkens/myaac/commit/be7b27c31aa3bbd6c0289c34d1e61139a3fe015c) +* Fix twig variables: logged + account_logged being not set directly after login (https://github.com/slawkens/myaac/commit/1e9b10d6489c488cadf7f6ed17b42f1ea6c767a8) + +### Changed +* OTS_ServerInfo -> move setTimeout out of class - Possibility to use the class without MyAAC (https://github.com/slawkens/myaac/commit/40d65a6613149fda51bdceb82c807e5301a3388b) + ## [1.5 - 14.05.2025] ### Added diff --git a/aac b/aac index d4ce7b7d..8f213136 100644 --- a/aac +++ b/aac @@ -25,7 +25,9 @@ foreach ($commandsGlob as $item) { } $commandPre = '\\MyAAC\Commands\\'; - $application->add(new ($commandPre . $name)); + if (!trait_exists($class = $commandPre . $name)) { + $application->add(new $class); + } } $pluginCommands = Plugins::getCommands(); diff --git a/admin/pages/accounts.php b/admin/pages/accounts.php index c2154299..2d9c1bbd 100644 --- a/admin/pages/accounts.php +++ b/admin/pages/accounts.php @@ -26,7 +26,6 @@ if (setting('core.account_country')) $nameOrNumberColumn = getAccountIdentityColumn(); $hasSecretColumn = $db->hasColumn('accounts', 'secret'); -$hasCoinsColumn = $db->hasColumn('accounts', 'coins'); $hasPointsColumn = $db->hasColumn('accounts', 'premium_points'); $hasTypeColumn = $db->hasColumn('accounts', 'type'); $hasGroupColumn = $db->hasColumn('accounts', 'group_id'); @@ -136,11 +135,18 @@ else if (isset($_REQUEST['search'])) { if (!Validator::email($email)) $errors['email'] = Validator::getLastError(); - //tibia coins - if ($hasCoinsColumn) { + // tibia coins + if (HAS_ACCOUNT_COINS) { $t_coins = $_POST['t_coins']; verify_number($t_coins, 'Tibia coins', 12); } + + // transferable tibia coins + if (HAS_ACCOUNT_COINS_TRANSFERABLE || HAS_ACCOUNT_TRANSFERABLE_COINS) { + $t_coins_transferable = $_POST['t_coins_transferable']; + verify_number($t_coins_transferable, 'Transferable Tibia coins', 12); + } + // prem days $p_days = (int)$_POST['p_days']; verify_number($p_days, 'Prem days', 11); @@ -185,12 +191,18 @@ else if (isset($_REQUEST['search'])) { if ($hasSecretColumn) { $account->setCustomField('secret', $secret); } + $account->setCustomField('key', $key); $account->setEMail($email); - if ($hasCoinsColumn) { + + if (HAS_ACCOUNT_COINS) { $account->setCustomField('coins', $t_coins); } + if (HAS_ACCOUNT_COINS_TRANSFERABLE || HAS_ACCOUNT_TRANSFERABLE_COINS) { + $account->setCustomField(ACCOUNT_COINS_TRANSFERABLE_COLUMN, $t_coins_transferable); + } + $lastDay = 0; if($p_days != 0 && $p_days != OTS_Account::GRATIS_PREMIUM_DAYS) { $lastDay = time(); @@ -223,9 +235,6 @@ else if (isset($_REQUEST['search'])) { $password = encrypt($password); $account->setPassword($password); - - if (USE_ACCOUNT_SALT) - $account->setCustomField('salt', $salt); } $account->save(); @@ -395,12 +404,18 @@ else if (isset($_REQUEST['search'])) { getEMail() . '">Send Mail)' : ''); ?> - +
+ +
+ + +
+
diff --git a/admin/pages/mailer.php b/admin/pages/mailer.php index 1f8d3188..5ac8f58c 100644 --- a/admin/pages/mailer.php +++ b/admin/pages/mailer.php @@ -25,9 +25,10 @@ if (!setting('core.mail_enabled')) { return; } -$mail_to = isset($_POST['mail_to']) ? stripslashes(trim($_POST['mail_to'])) : null; +$mail_to = isset($_REQUEST['mail_to']) ? stripslashes(trim($_REQUEST['mail_to'])) : null; $mail_subject = isset($_POST['mail_subject']) ? stripslashes($_POST['mail_subject']) : null; $mail_content = isset($_POST['mail_content']) ? stripslashes($_POST['mail_content']) : null; +$mail_verified_only = $_POST['mail_verified_only'] ?? false; if (isset($_POST['submit'])) { if (empty($mail_subject)) { @@ -58,14 +59,14 @@ if (!empty($mail_content) && !empty($mail_subject) && empty($mail_to)) { $success = 0; $failed = 0; - $add = ''; - if (setting('core.account_mail_verify')) { - note('Note: Sending only to users with verified E-Mail.'); - $add = ' AND `email_verified` = 1'; + $query = Account::where('email', '!=', ''); + + if ($mail_verified_only) { + info('Note: Sending only to users with verified E-Mail.'); + $query->where('email_verified', 1); } - $query = Account::where('email', '!=', '')->get(['email']); - foreach ($query as $email) { + foreach ($query->get(['email']) as $email) { if (_mail($email->email, $mail_subject, $mail_content)) { $success++; } @@ -84,5 +85,6 @@ if (!empty($mail_content) && !empty($mail_subject) && empty($mail_to)) { $twig->display('admin.mailer.html.twig', [ 'mail_to' => $mail_to, 'mail_subject' => $mail_subject, - 'mail_content' => $mail_content + 'mail_content' => $mail_content, + 'mail_verified_only' => $mail_verified_only, ]); diff --git a/admin/pages/mass_account.php b/admin/pages/mass_account.php index 46c9bc9d..4ee9f63b 100644 --- a/admin/pages/mass_account.php +++ b/admin/pages/mass_account.php @@ -6,6 +6,7 @@ * @package MyAAC * @author Slawkens * @author Lee + * @author gpedro * @copyright 2020 MyAAC * @link https://my-aac.org */ @@ -18,11 +19,10 @@ $title = 'Mass Account Actions'; csrfProtect(); -$hasCoinsColumn = $db->hasColumn('accounts', 'coins'); $hasPointsColumn = $db->hasColumn('accounts', 'premium_points'); -$freePremium = $config['lua']['freePremium']; +$freePremium = getBoolean(configLua('freePremium')); -function admin_give_points($points) +function admin_give_points($points): void { global $hasPointsColumn; @@ -38,11 +38,9 @@ function admin_give_points($points) displayMessage($points . ' points added to all accounts.', true); } -function admin_give_coins($coins) +function admin_give_coins($coins): void { - global $hasCoinsColumn; - - if (!$hasCoinsColumn) { + if (!HAS_ACCOUNT_COINS) { displayMessage('Coins not supported.'); return; } @@ -55,7 +53,7 @@ function admin_give_coins($coins) displayMessage($coins . ' coins added to all accounts.', true); } -function admin_give_premdays($days) +function admin_give_premdays($days): void { global $db, $freePremium; @@ -66,6 +64,7 @@ function admin_give_premdays($days) $value = $days * 86400; $now = time(); + // othire if ($db->hasColumn('accounts', 'premend')) { // append premend @@ -73,14 +72,11 @@ function admin_give_premdays($days) // set premend if (Account::where('premend', '<=', $now)->update(['premend' => $now + $value])) { displayMessage($days . ' premium days added to all accounts.', true); - return; } else { displayMessage('Failed to execute set query.'); - return; } } else { displayMessage('Failed to execute append query.'); - return; } return; @@ -95,20 +91,14 @@ function admin_give_premdays($days) // set lastday if (Account::where('lastday', '<=', $now)->update(['lastday' => $now + $value])) { displayMessage($days . ' premium days added to all accounts.', true); - return; } else { displayMessage('Failed to execute set query.'); - return; } - - return; } else { displayMessage('Failed to execute append query.'); - return; } } else { displayMessage('Failed to execute set days query.'); - return; } return; @@ -121,14 +111,11 @@ function admin_give_premdays($days) // set premium_ends_at if (Account::where('premium_ends_at', '<=', $now)->update(['premium_ends_at' => $now + $value])) { displayMessage($days . ' premium days added to all accounts.', true); - return; } else { displayMessage('Failed to execute set query.'); - return; } } else { displayMessage('Failed to execute append query.'); - return; } return; @@ -167,19 +154,20 @@ if (!empty(ACTION) && isRequestMethod('post')) { } else { $twig->display('admin.tools.account.html.twig', array( - 'hasCoinsColumn' => $hasCoinsColumn, + 'hasCoinsColumn' => HAS_ACCOUNT_COINS, 'hasPointsColumn' => $hasPointsColumn, 'freePremium' => $freePremium, )); } -function displayMessage($message, $success = false) { - global $twig, $hasCoinsColumn, $hasPointsColumn, $freePremium; +function displayMessage($message, $success = false): void +{ + global $twig, $hasPointsColumn, $freePremium; $success ? success($message): error($message); $twig->display('admin.tools.account.html.twig', array( - 'hasCoinsColumn' => $hasCoinsColumn, + 'hasCoinsColumn' => HAS_ACCOUNT_COINS, 'hasPointsColumn' => $hasPointsColumn, 'freePremium' => $freePremium, )); diff --git a/admin/pages/modules/coins.php b/admin/pages/modules/coins.php index 939ee16c..66ce0186 100644 --- a/admin/pages/modules/coins.php +++ b/admin/pages/modules/coins.php @@ -6,7 +6,7 @@ defined('MYAAC') or die('Direct access not allowed!'); $coins = 0; -if ($db->hasColumn('accounts', 'coins')) { +if (HAS_ACCOUNT_COINS) { $whatToGet = ['id', 'coins']; if (USE_ACCOUNT_NAME) { $whatToGet[] = 'name'; diff --git a/admin/pages/modules/templates/coins.html.twig b/admin/pages/modules/templates/coins.html.twig index 722064f8..ae586ee8 100644 --- a/admin/pages/modules/templates/coins.html.twig +++ b/admin/pages/modules/templates/coins.html.twig @@ -19,7 +19,7 @@ {% set i = i + 1 %} {{ i }} - {{ result.name }} + {{ result.name ?? result.id }} {{ result.coins }} {% endfor %} diff --git a/admin/pages/players.php b/admin/pages/players.php index c44bc012..bd3ab713 100644 --- a/admin/pages/players.php +++ b/admin/pages/players.php @@ -669,11 +669,17 @@ else if (isset($_REQUEST['search'])) {
diff --git a/admin/pages/plugins.php b/admin/pages/plugins.php index db886635..82f8309c 100644 --- a/admin/pages/plugins.php +++ b/admin/pages/plugins.php @@ -51,6 +51,56 @@ else { } else { error('Error while disabling plugin ' . $disable . ': ' . Plugins::getError()); } + } + else if (isset($_GET['check-updates'])) { + $repoUri = $config['admin_plugins_api_uri'] ?? 'https://plugins.my-aac.org/api/'; + success("Fetching latest info from $repoUri.."); + + $adminPlugins = new \MyAAC\Admin\Plugins(); + + $adminPlugins->setApiBaseUri($repoUri); + + try { + $plugins = $adminPlugins->getLatestVersions(); + } + catch (Exception $e) { + error($e->getMessage()); + } + + if (isset($plugins) && count($plugins) > 0) { + $outdated = []; + + foreach (get_plugins(true) as $plugin) { + $string = file_get_contents(BASE . 'plugins/' . $plugin . '.json'); + $plugin_info = json_decode($string, true); + + if (!$plugin_info) { + continue; + } + + $disabled = (str_contains($plugin, 'disabled.')); + $pluginOriginal = ($disabled ? str_replace('disabled.', '', $plugin) : $plugin); + + $info = $plugins[$pluginOriginal] ?? false; + if ($info && version_compare($info['version'], $plugin_info['version'], '>')) { + $outdated[] = [ + 'name' => $pluginOriginal, + 'yourVersion' => $plugin_info['version'], + 'latestVersion' => $info['version'], + 'link' => $info['link'] ?? 'Unknown', + 'download_link' => $info['download_link'] ?? 'Unknown', + ]; + } + } + + if (count($outdated) > 0) { + info('Following updates have been found for your plugins:'); + $twig->display('admin.plugins.outdated.html.twig', ['plugins' => $outdated]); + } + else { + success('All plugins up to date!'); + } + } } else if (isset($_FILES['plugin']['name'])) { $file = $_FILES['plugin']; $filename = $file['name']; diff --git a/admin/pages/visitors.php b/admin/pages/visitors.php index 9e83d339..9fae4d24 100644 --- a/admin/pages/visitors.php +++ b/admin/pages/visitors.php @@ -19,8 +19,7 @@ $use_datatable = true; if (!setting('core.visitors_counter')): ?> Visitors counter is disabled.
- You can enable it by editing this configurable in config.local.php file:
-

$config['visitors_counter'] = true;

+ You can enable it in Settings -> General -> Visitors Counter.
isBot()) { $bot = $dd->getBot(); $message = '(Bot) %s, %s'; - $browser = sprintf($message, $bot['category'], $bot['url'], $bot['name']); + $browser = sprintf($message, $bot['category'] ?? 'Unknown', $bot['url'] ?? '', $bot['name'] ?? 'Unknown name'); } else { $osFamily = OperatingSystem::getOsFamily($dd->getOs('name')); diff --git a/admin/template/menus.php b/admin/template/menus.php index 0bcd7304..6df84745 100644 --- a/admin/template/menus.php +++ b/admin/template/menus.php @@ -60,7 +60,7 @@ usort($menus, function ($a, $b) { foreach ($menus as $i => $menu) { if (isset($menu['link']) && is_array($menu['link'])) { - usort($menus[$i]['link'], function ($a, $b) { + usort($menu['link'], function ($a, $b) { return $a['order'] - $b['order']; }); } diff --git a/admin/tools/phpinfo.php b/admin/tools/phpinfo.php index cd043279..bd09fb5d 100644 --- a/admin/tools/phpinfo.php +++ b/admin/tools/phpinfo.php @@ -1,5 +1,6 @@ 0) { if ($success) { echo 'Saved at ' . date('H:i'); } +else { + echo 'Something unexpected happened - it was impossible to save the settings, please try again later. If problem persists - contact MyAAC developers.'; +} diff --git a/admin/tools/status.php b/admin/tools/status.php index e7a471e7..45597b6f 100644 --- a/admin/tools/status.php +++ b/admin/tools/status.php @@ -1,5 +1,6 @@ =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": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.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": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, { "name": "psr/log", "version": "3.0.2", @@ -1573,6 +2058,50 @@ }, "time": "2021-10-29T13:26:27+00:00" }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "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": "symfony/console", "version": "v6.4.17", @@ -2910,7 +3439,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -2921,6 +3450,6 @@ "ext-xml": "*", "ext-dom": "*" }, - "platform-dev": [], - "plugin-api-version": "2.3.0" + "platform-dev": {}, + "plugin-api-version": "2.6.0" } diff --git a/images/facebook_16x16.png b/images/facebook_16x16.png new file mode 100644 index 00000000..d64c153a Binary files /dev/null and b/images/facebook_16x16.png differ diff --git a/images/instagram_16x16.png b/images/instagram_16x16.png new file mode 100644 index 00000000..62b8ee18 Binary files /dev/null and b/images/instagram_16x16.png differ diff --git a/images/order_asc.gif b/images/order_asc.gif new file mode 100644 index 00000000..c9698a5a Binary files /dev/null and b/images/order_asc.gif differ diff --git a/images/order_desc.gif b/images/order_desc.gif new file mode 100644 index 00000000..4a764bd5 Binary files /dev/null and b/images/order_desc.gif differ diff --git a/images/whatsapp_16x16.png b/images/whatsapp_16x16.png new file mode 100644 index 00000000..a13d2faf Binary files /dev/null and b/images/whatsapp_16x16.png differ diff --git a/index.php b/index.php index bc8a3663..ee8f7a87 100644 --- a/index.php +++ b/index.php @@ -93,6 +93,7 @@ if(setting('core.backward_support')) { if($logged && $account_logged) $group_id_of_acc_logged = $account_logged->getGroupId(); + $config['serverPath'] = $config['server_path']; $config['site'] = &$config; $config['server'] = &$config['lua']; $config['site']['shop_system'] = setting('core.gifts_system'); @@ -117,6 +118,14 @@ if(setting('core.backward_support')) { $config['status']['serverStatus_' . $key] = $value; } +if(setting('core.views_counter')) { + require_once SYSTEM . 'counter.php'; +} + +if(setting('core.visitors_counter')) { + $visitors = new Visitors(setting('core.visitors_counter_ttl')); +} + require_once SYSTEM . 'router.php'; // anonymous usage statistics @@ -153,22 +162,6 @@ if(setting('core.anonymous_usage_statistics')) { } } -if(setting('core.views_counter')) - require_once SYSTEM . 'counter.php'; - -if(setting('core.visitors_counter')) { - $visitors = new Visitors(setting('core.visitors_counter_ttl')); -} - -/** - * @var OTS_Account $account_logged - */ -if ($logged && admin()) { - $content .= $twig->render('admin-bar.html.twig', [ - 'username' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId() - ]); -} - $title_full = (isset($title) ? $title . ' - ' : '') . $config['lua']['serverName']; require $template_path . '/' . $template_index; diff --git a/install/includes/import_base_data.php b/install/includes/import_base_data.php new file mode 100644 index 00000000..45c803ed --- /dev/null +++ b/install/includes/import_base_data.php @@ -0,0 +1,69 @@ + 3, + 'where' => 2, + 'date' => time(), + 'body' => 'MyAAC installed. (:', + 'hide' => 0, + ]); +} + +if (Config::where('name', 'database_version')->count() === 0) { + Config::create([ + 'name' => 'database_version', + 'value' => DATABASE_VERSION, + ]); +} + +if (ForumBoard::count() === 0) { + $forumBoards = [ + ['name' => 'News', 'description' => 'News commenting', 'closed' => 1], + ['name' => 'Trade', 'description' => 'Trade offers.', 'closed' => 0], + ['name' => 'Quests', 'description' => 'Quest making.', 'closed' => 0], + ['name' => 'Pictures', 'description' => 'Your pictures.', 'closed' => 0], + ['name' => 'Bug Report', 'description' => 'Report bugs there.', 'closed' => 0], + ]; + + $i = 0; + foreach ($forumBoards as $forumBoard) { + ForumBoard::create([ + 'name' => $forumBoard['name'], + 'description' => $forumBoard['description'], + 'ordering' => $i++, + 'closed' => $forumBoard['closed'], + ]); + } +} + +if (NewsCategory::count() === 0) { + $newsCategoriesIcons = [ + 0, 1, 2, 3, 4 + ]; + + foreach ($newsCategoriesIcons as $iconId) { + NewsCategory::create([ + 'icon_id' => $iconId, + ]); + } +} + +if (Gallery::count() === 0) { + Gallery::create([ + 'comment' => 'Demon', + 'image' => 'images/gallery/demon.jpg', + 'thumb' => 'images/gallery/demon_thumb.gif', + 'author' => 'MyAAC', + 'ordering' => 0, + ]); +} + +success($locale['step_database_success_import_data']); diff --git a/install/includes/schema.sql b/install/includes/schema.sql index eccaee54..d4bc97d6 100644 --- a/install/includes/schema.sql +++ b/install/includes/schema.sql @@ -1,6 +1,4 @@ -SET @myaac_database_version = 45; - -CREATE TABLE `myaac_account_actions` +CREATE TABLE IF NOT EXISTS `myaac_account_actions` ( `account_id` int NOT NULL, `ip` int unsigned NOT NULL DEFAULT 0, @@ -10,7 +8,16 @@ CREATE TABLE `myaac_account_actions` KEY (`account_id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -CREATE TABLE `myaac_admin_menu` +CREATE TABLE IF NOT EXISTS `myaac_account_emails_verify` +( + `id` int NOT NULL AUTO_INCREMENT, + `account_id` int NOT NULL, + `hash` varchar(32) NOT NULL, + `sent_at` int NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `myaac_admin_menu` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL DEFAULT '', @@ -21,7 +28,7 @@ CREATE TABLE `myaac_admin_menu` PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -CREATE TABLE `myaac_changelog` +CREATE TABLE IF NOT EXISTS `myaac_changelog` ( `id` int NOT NULL AUTO_INCREMENT, `body` varchar(500) NOT NULL DEFAULT '', @@ -33,9 +40,7 @@ CREATE TABLE `myaac_changelog` PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -INSERT INTO `myaac_changelog` (`id`, `type`, `where`, `date`, `body`, `hide`) VALUES (1, 3, 2, UNIX_TIMESTAMP(), 'MyAAC installed. (:', 0); - -CREATE TABLE `myaac_config` +CREATE TABLE IF NOT EXISTS `myaac_config` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(30) NOT NULL, @@ -44,9 +49,7 @@ CREATE TABLE `myaac_config` UNIQUE (`name`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -INSERT INTO `myaac_config` (`name`, `value`) VALUES ('database_version', @myaac_database_version); - -CREATE TABLE `myaac_faq` +CREATE TABLE IF NOT EXISTS `myaac_faq` ( `id` int NOT NULL AUTO_INCREMENT, `question` varchar(255) NOT NULL DEFAULT '', @@ -56,7 +59,7 @@ CREATE TABLE `myaac_faq` PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -CREATE TABLE `myaac_forum_boards` +CREATE TABLE IF NOT EXISTS `myaac_forum_boards` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL, @@ -68,13 +71,8 @@ CREATE TABLE `myaac_forum_boards` `hide` tinyint NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -INSERT INTO `myaac_forum_boards` (`id`, `name`, `description`, `ordering`, `closed`) VALUES (NULL, 'News', 'News commenting', 0, 1); -INSERT INTO `myaac_forum_boards` (`id`, `name`, `description`, `ordering`) VALUES (NULL, 'Trade', 'Trade offers.', 1); -INSERT INTO `myaac_forum_boards` (`id`, `name`, `description`, `ordering`) VALUES (NULL, 'Quests', 'Quest making.', 2); -INSERT INTO `myaac_forum_boards` (`id`, `name`, `description`, `ordering`) VALUES (NULL, 'Pictures', 'Your pictures.', 3); -INSERT INTO `myaac_forum_boards` (`id`, `name`, `description`, `ordering`) VALUES (NULL, 'Bug Report', 'Report bugs there.', 4); -CREATE TABLE `myaac_forum` +CREATE TABLE IF NOT EXISTS `myaac_forum` ( `id` int NOT NULL AUTO_INCREMENT, `first_post` int NOT NULL DEFAULT 0, @@ -98,7 +96,7 @@ CREATE TABLE `myaac_forum` KEY `section` (`section`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -CREATE TABLE `myaac_menu` +CREATE TABLE IF NOT EXISTS `myaac_menu` ( `id` int NOT NULL AUTO_INCREMENT, `template` varchar(255) NOT NULL, @@ -112,7 +110,7 @@ CREATE TABLE `myaac_menu` PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -CREATE TABLE `myaac_monsters` ( +CREATE TABLE IF NOT EXISTS `myaac_monsters` ( `id` int NOT NULL AUTO_INCREMENT, `hide` tinyint NOT NULL DEFAULT 0, `name` varchar(255) NOT NULL, @@ -145,7 +143,7 @@ CREATE TABLE `myaac_monsters` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -CREATE TABLE `myaac_news` +CREATE TABLE IF NOT EXISTS `myaac_news` ( `id` int NOT NULL AUTO_INCREMENT, `title` varchar(100) NOT NULL, @@ -163,7 +161,7 @@ CREATE TABLE `myaac_news` PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -CREATE TABLE `myaac_news_categories` +CREATE TABLE IF NOT EXISTS `myaac_news_categories` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL DEFAULT "", @@ -173,13 +171,7 @@ CREATE TABLE `myaac_news_categories` PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -INSERT INTO `myaac_news_categories` (`id`, `icon_id`) VALUES (NULL, 0); -INSERT INTO `myaac_news_categories` (`id`, `icon_id`) VALUES (NULL, 1); -INSERT INTO `myaac_news_categories` (`id`, `icon_id`) VALUES (NULL, 2); -INSERT INTO `myaac_news_categories` (`id`, `icon_id`) VALUES (NULL, 3); -INSERT INTO `myaac_news_categories` (`id`, `icon_id`) VALUES (NULL, 4); - -CREATE TABLE `myaac_notepad` +CREATE TABLE IF NOT EXISTS `myaac_notepad` ( `id` int NOT NULL AUTO_INCREMENT, `account_id` int NOT NULL, @@ -189,7 +181,7 @@ CREATE TABLE `myaac_notepad` PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -CREATE TABLE `myaac_pages` +CREATE TABLE IF NOT EXISTS `myaac_pages` ( `id` INT NOT NULL AUTO_INCREMENT, `name` varchar(30) NOT NULL, @@ -205,7 +197,7 @@ CREATE TABLE `myaac_pages` UNIQUE (`name`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -CREATE TABLE `myaac_gallery` +CREATE TABLE IF NOT EXISTS `myaac_gallery` ( `id` int NOT NULL AUTO_INCREMENT, `comment` varchar(255) NOT NULL DEFAULT '', @@ -217,9 +209,7 @@ CREATE TABLE `myaac_gallery` PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -INSERT INTO `myaac_gallery` (`id`, `ordering`, `comment`, `image`, `thumb`, `author`) VALUES (NULL, 1, 'Demon', 'images/gallery/demon.jpg', 'images/gallery/demon_thumb.gif', 'MyAAC'); - -CREATE TABLE `myaac_settings` +CREATE TABLE IF NOT EXISTS `myaac_settings` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL DEFAULT '', @@ -229,7 +219,7 @@ CREATE TABLE `myaac_settings` KEY `key` (`key`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -CREATE TABLE `myaac_spells` +CREATE TABLE IF NOT EXISTS `myaac_spells` ( `id` int NOT NULL AUTO_INCREMENT, `spell` varchar(255) NOT NULL DEFAULT '', @@ -252,7 +242,7 @@ CREATE TABLE `myaac_spells` UNIQUE (`name`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -CREATE TABLE `myaac_visitors` +CREATE TABLE IF NOT EXISTS `myaac_visitors` ( `ip` varchar(45) NOT NULL, `lastvisit` int NOT NULL DEFAULT 0, @@ -261,7 +251,7 @@ CREATE TABLE `myaac_visitors` UNIQUE (`ip`) ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; -CREATE TABLE `myaac_weapons` +CREATE TABLE IF NOT EXISTS `myaac_weapons` ( `id` int NOT NULL, `level` int NOT NULL DEFAULT 0, diff --git a/install/steps/4-config.php b/install/steps/4-config.php index 325b97f1..342edd06 100644 --- a/install/steps/4-config.php +++ b/install/steps/4-config.php @@ -10,6 +10,14 @@ foreach($config['clients'] as $client) { $clients[$client] = $client_version; } +if (empty($_SESSION['var_site_url'])) { + //require SYSTEM . 'base.php'; + $serverUrl = 'http' . (isHttps() ? 's' : '') . '://' . $baseHost; + $siteURL = $serverUrl . $baseDir; + + $_SESSION['var_site_url'] = $siteURL; +} + $twig->display('install.config.html.twig', array( 'clients' => $clients, 'timezones' => DateTimeZone::listIdentifiers(), diff --git a/install/steps/5-database.php b/install/steps/5-database.php index cb4f14a3..ecf7f150 100644 --- a/install/steps/5-database.php +++ b/install/steps/5-database.php @@ -42,45 +42,44 @@ if(!$error) { $configToSave['cache_prefix'] = 'myaac_' . generateRandomString(8, true, false, true); $configToSave['database_auto_migrate'] = true; - if(!$error) { - $content = ''; - $saved = Settings::saveConfig($configToSave, BASE . 'config.local.php', $content); - if ($saved) { - success($locale['step_database_config_saved']); - $_SESSION['saved'] = true; + $content = ''; + $saved = Settings::saveConfig($configToSave, BASE . 'config.local.php', $content); + if ($saved || file_exists(BASE . 'config.local.php')) { + success($locale['step_database_config_saved']); + $_SESSION['saved'] = true; - require BASE . 'config.local.php'; - require BASE . 'install/includes/config.php'; + require BASE . 'config.local.php'; + require BASE . 'install/includes/config.php'; - if (!$error) { - require BASE . 'install/includes/database.php'; + if (!$error) { + require BASE . 'install/includes/database.php'; - if (isset($database_error)) { // we failed connect to the database - error($database_error); + if (isset($database_error)) { // we failed connect to the database + error($database_error); + } + else { + if (!$db->hasTable('accounts')) { + $tmp = str_replace('$TABLE$', 'accounts', $locale['step_database_error_table']); + error($tmp); + $error = true; } - else { - if (!$db->hasTable('accounts')) { - $tmp = str_replace('$TABLE$', 'accounts', $locale['step_database_error_table']); - error($tmp); - $error = true; - } - if (!$error) { - $twig->display('install.installer.html.twig', array( - 'url' => 'tools/5-database.php', - 'message' => $locale['loading_spinner'] - )); - } + if (!$error) { + $twig->display('install.installer.html.twig', array( + 'url' => 'tools/5-database.php', + 'message' => $locale['loading_spinner'] + )); } } - } else { - $_SESSION['config_content'] = $content; - unset($_SESSION['saved']); - - $locale['step_database_error_file'] = str_replace('$FILE$', '' . BASE . 'config.php', $locale['step_database_error_file']); - error($locale['step_database_error_file'] . '
- '); } + } else { + $error = true; + $_SESSION['config_content'] = $content; + unset($_SESSION['saved']); + + $locale['step_database_error_file'] = str_replace('$FILE$', '' . BASE . 'config.local.php', $locale['step_database_error_file']); + error($locale['step_database_error_file'] . '
+ '); } } ?> diff --git a/install/steps/7-finish.php b/install/steps/7-finish.php index 0ae302ea..afe63603 100644 --- a/install/steps/7-finish.php +++ b/install/steps/7-finish.php @@ -195,13 +195,4 @@ if(!isset($_SESSION['installed'])) { $_SESSION['installed'] = true; } -foreach($_SESSION as $key => $value) { - if(strpos($key, 'var_') !== false) - unset($_SESSION[$key]); -} -unset($_SESSION['saved']); -if(file_exists(CACHE . 'install.txt')) { - unlink(CACHE . 'install.txt'); -} - $hooks->trigger(HOOK_INSTALL_FINISH_END); diff --git a/install/tools/5-database.php b/install/tools/5-database.php index 395492ee..6406ee48 100644 --- a/install/tools/5-database.php +++ b/install/tools/5-database.php @@ -7,6 +7,11 @@ require SYSTEM . 'functions.php'; require BASE . 'install/includes/functions.php'; require BASE . 'install/includes/locale.php'; +if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['saved'])) { + warning($locale['already_installed']); + return; +} + $error = false; require BASE . 'install/includes/config.php'; @@ -25,26 +30,22 @@ if(!$error) { } } -if($db->hasTable(TABLE_PREFIX . 'account_actions')) { - $locale['step_database_error_table_exist'] = str_replace('$TABLE$', TABLE_PREFIX . 'account_actions', $locale['step_database_error_table_exist']); - warning($locale['step_database_error_table_exist']); -} -else { - // import schema - try { - $locale['step_database_importing'] = str_replace('$DATABASE_NAME$', config('database_name'), $locale['step_database_importing']); - success($locale['step_database_importing']); +// import schema +try { + $locale['step_database_importing'] = str_replace('$DATABASE_NAME$', config('database_name'), $locale['step_database_importing']); + success($locale['step_database_importing']); - $db->query(file_get_contents(BASE . 'install/includes/schema.sql')); + $db->exec(file_get_contents(BASE . 'install/includes/schema.sql')); - $locale['step_database_success_schema'] = str_replace('$PREFIX$', TABLE_PREFIX, $locale['step_database_success_schema']); - success($locale['step_database_success_schema']); - } - catch(PDOException $error_) { - error($locale['step_database_error_schema'] . ' ' . $error_); - return; - } + $locale['step_database_success_schema'] = str_replace('$PREFIX$', TABLE_PREFIX, $locale['step_database_success_schema']); + success($locale['step_database_success_schema']); } +catch(PDOException $error_) { + error($locale['step_database_error_schema'] . ' ' . $error_); + return; +} + +require BASE . 'install/includes/import_base_data.php'; if(!$db->hasColumn('accounts', 'email')) { if(query("ALTER TABLE `accounts` ADD `email` varchar(255) NOT NULL DEFAULT '';")) @@ -97,18 +98,13 @@ if(!$db->hasColumn('accounts', 'web_flags')) { success($locale['step_database_adding_field'] . ' accounts.web_flags...'); } -if(!$db->hasColumn('accounts', 'email_hash')) { - if(query("ALTER TABLE `accounts` ADD `email_hash` VARCHAR(32) NOT NULL DEFAULT '' AFTER `web_flags`;")) - success($locale['step_database_adding_field'] . ' accounts.email_hash...'); -} - if(!$db->hasColumn('accounts', 'email_verified')) { - if(query("ALTER TABLE `accounts` ADD `email_verified` TINYINT(1) NOT NULL DEFAULT 0 AFTER `email_hash`;")) + if(query("ALTER TABLE `accounts` ADD `email_verified` TINYINT(1) NOT NULL DEFAULT 0 AFTER `web_flags`;")) success($locale['step_database_adding_field'] . ' accounts.email_verified...'); } if(!$db->hasColumn('accounts', 'email_new')) { - if(query("ALTER TABLE `accounts` ADD `email_new` VARCHAR(255) NOT NULL DEFAULT '' AFTER `email_hash`;")) + if(query("ALTER TABLE `accounts` ADD `email_new` VARCHAR(255) NOT NULL DEFAULT '' AFTER `email_verified`;")) success($locale['step_database_adding_field'] . ' accounts.email_new...'); } diff --git a/install/tools/7-finish.php b/install/tools/7-finish.php index c1ab5c1d..9c64382b 100644 --- a/install/tools/7-finish.php +++ b/install/tools/7-finish.php @@ -17,11 +17,11 @@ ini_set('max_execution_time', 300); ob_implicit_flush(); header('X-Accel-Buffering: no'); -/* + if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['saved'])) { warning($locale['already_installed']); return; -}*/ +} require SYSTEM . 'init.php'; @@ -94,6 +94,17 @@ $hooks->trigger(HOOK_INSTALL_FINISH); $db->setClearCacheAfter(true); +// cleanup +foreach($_SESSION as $key => $value) { + if(str_contains($key, 'var_')) { + unset($_SESSION[$key]); + } +} +unset($_SESSION['saved']); +if(file_exists(CACHE . 'install.txt')) { + unlink(CACHE . 'install.txt'); +} + $locale['step_finish_desc'] = str_replace('$ADMIN_PANEL$', generateLink(str_replace('tools/', '',ADMIN_URL), $locale['step_finish_admin_panel'], true), $locale['step_finish_desc']); $locale['step_finish_desc'] = str_replace('$HOMEPAGE$', generateLink(str_replace('tools/', '', BASE_URL), $locale['step_finish_homepage'], true), $locale['step_finish_desc']); $locale['step_finish_desc'] = str_replace('$LINK$', generateLink('https://my-aac.org', 'https://my-aac.org', true), $locale['step_finish_desc']); diff --git a/login.php b/login.php index 88d5d88a..438754e2 100644 --- a/login.php +++ b/login.php @@ -88,8 +88,8 @@ switch ($action) { case 'boostedcreature': $clientVersion = (int)setting('core.client'); - // 14.00 and up - if ($clientVersion >= 1400) { + // 13.40 and up + if ($clientVersion >= 1340) { $creatureBoost = $db->query("SELECT * FROM " . $db->tableName('boosted_creature'))->fetchAll(); $bossBoost = $db->query("SELECT * FROM " . $db->tableName('boosted_boss'))->fetchAll(); die(json_encode([ @@ -220,6 +220,8 @@ switch ($action) { } } + /* + * not needed anymore? if (fieldExist('premdays', 'accounts') && fieldExist('lastday', 'accounts')) { $save = false; $timeNow = time(); @@ -256,6 +258,7 @@ switch ($action) { $account->save(); } } + */ $worlds = [$world]; $playdata = compact('worlds', 'characters'); diff --git a/package-lock.json b/package-lock.json index 7cdffe23..3ea74539 100644 --- a/package-lock.json +++ b/package-lock.json @@ -976,15 +976,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -2084,9 +2085,9 @@ "license": "MIT" }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", + "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", "dev": true, "license": "MIT", "engines": { diff --git a/phpstan.neon b/phpstan.neon index 020fa3a6..68e1aa6a 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -28,10 +28,9 @@ parameters: - '#Variable \$guild might not be defined#' - '#Variable \$[a-zA-Z0-9\\_]+ might not be defined#' # Eloquent models + - '#Call to an undefined method [a-zA-Z0-9\\_]+::[a-zA-Z0-9\\_]+\(\)#' - '#Call to an undefined static method [a-zA-Z0-9\\_]+::[a-zA-Z0-9\\_]+\(\)#' - - '#Call to an undefined method object::toArray\(\)#' # system/pages/highscores.php - - '#Call to an undefined method Illuminate\\Database\\Query\\Builder::withOnlineStatus\(\)#' - '#Access to an undefined property Illuminate\\Database\\Eloquent\\Model::\$online_status#' - '#Access to an undefined property Illuminate\\Database\\Eloquent\\Model::\$vocation_name#' - diff --git a/plugins/example.json b/plugins/example.json index 395db41d..fd5e183b 100644 --- a/plugins/example.json +++ b/plugins/example.json @@ -51,5 +51,8 @@ "themes": true, "admin-pages": true, "admin-pages-sub-folders": true, + "settings": true, + "install": true, + "init": false } } diff --git a/system/base.php b/system/base.php new file mode 100644 index 00000000..9cab2925 --- /dev/null +++ b/system/base.php @@ -0,0 +1,21 @@ + 'account_change_character_name_price', 'account_change_character_sex', 'account_change_character_sex_points' => 'account_change_character_name_price', + 'email_lai_sec_interval' => 'mail_lost_account_interval', ]; foreach ($deprecatedConfig as $key => $value) { diff --git a/system/functions.php b/system/functions.php index 6e58ca9d..f11fa382 100644 --- a/system/functions.php +++ b/system/functions.php @@ -433,16 +433,22 @@ function delete_guild($id) $rank_list->orderBy('level'); global $db; + + $deletedColumn = 'deleted'; + if ($db->hasColumn('players', 'deletion')) { + $deletedColumn = 'deletion'; + } + /** * @var OTS_GuildRank $rank_in_guild */ foreach($rank_list as $rank_in_guild) { if($db->hasTable('guild_members')) - $players_with_rank = $db->query('SELECT `players`.`id` as `id`, `guild_members`.`rank_id` as `rank_id` FROM `players`, `guild_members` WHERE `guild_members`.`rank_id` = ' . $rank_in_guild->getId() . ' AND `players`.`id` = `guild_members`.`player_id` ORDER BY `name`;'); + $players_with_rank = $db->query('SELECT `players`.`id` as `id`, `guild_members`.`rank_id` as `rank_id` FROM `players`, `guild_members` WHERE `guild_members`.`rank_id` = ' . $rank_in_guild->getId() . ' AND `players`.`id` = `guild_members`.`player_id` AND `' . $deletedColumn . '` = 0 ORDER BY `name`;'); else if($db->hasTable('guild_membership')) - $players_with_rank = $db->query('SELECT `players`.`id` as `id`, `guild_membership`.`rank_id` as `rank_id` FROM `players`, `guild_membership` WHERE `guild_membership`.`rank_id` = ' . $rank_in_guild->getId() . ' AND `players`.`id` = `guild_membership`.`player_id` ORDER BY `name`;'); + $players_with_rank = $db->query('SELECT `players`.`id` as `id`, `guild_membership`.`rank_id` as `rank_id` FROM `players`, `guild_membership` WHERE `guild_membership`.`rank_id` = ' . $rank_in_guild->getId() . ' AND `players`.`id` = `guild_membership`.`player_id` AND `' . $deletedColumn . '` = 0 ORDER BY `name`;'); else - $players_with_rank = $db->query('SELECT `id`, `rank_id` FROM `players` WHERE `rank_id` = ' . $rank_in_guild->getId() . ' AND `deleted` = 0;'); + $players_with_rank = $db->query('SELECT `id`, `rank_id` FROM `players` WHERE `rank_id` = ' . $rank_in_guild->getId() . ' AND `' . $deletedColumn . '` = 0;'); $players_with_rank_number = $players_with_rank->rowCount(); if($players_with_rank_number > 0) { @@ -512,6 +518,13 @@ function template_place_holder($type): string } elseif ($type === 'body_start') { $ret .= $twig->render('browsehappy.html.twig'); + + if (admin()) { + global $account_logged; + $ret .= $twig->render('admin-bar.html.twig', [ + 'username' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId() + ]); + } } elseif($type === 'body_end') { $ret .= template_ga_code(); @@ -767,6 +780,10 @@ function formatExperience($exp, $color = true) return $ret; } +function getExperienceForLevel($level): float|int { + return ( 50 / 3 ) * pow( $level, 3 ) - ( 100 * pow( $level, 2 ) ) + ( ( 850 / 3 ) * $level ) - 200; +} + function get_locales() { $ret = array(); @@ -982,11 +999,12 @@ function load_config_lua($filename) foreach($lines as $ln => $line) { $line = trim($line); - if(@$line[0] === '{' || @$line[0] === '}') { + if(isset($line[0]) && ($line[0] === '{' || $line[0] === '}')) { // arrays are not supported yet // just ignore the error continue; } + $tmp_exp = explode('=', $line, 2); if(str_contains($line, 'dofile')) { $delimiter = '"'; @@ -1130,10 +1148,18 @@ function getTopPlayers($limit = 5, $skill = 'level') { 'looktype', 'lookhead', 'lookbody', 'looklegs', 'lookfeet' ]; + if ($db->hasColumn('players', 'promotion')) { + $columns[] = 'promotion'; + } + if ($db->hasColumn('players', 'lookaddons')) { $columns[] = 'lookaddons'; } + if ($db->hasColumn('players', 'lookmount')) { + $columns[] = 'lookmount'; + } + return Player::query() ->select($columns) ->withOnlineStatus() @@ -1157,7 +1183,8 @@ function getTopPlayers($limit = 5, $skill = 'level') { }); } -function deleteDirectory($dir, $ignore = array(), $contentOnly = false) { +function deleteDirectory($dir, $ignore = array(), $contentOnly = false): bool +{ if(!file_exists($dir)) { return true; } @@ -1183,6 +1210,21 @@ function deleteDirectory($dir, $ignore = array(), $contentOnly = false) { return rmdir($dir); } +function ensureFolderExists($dir): void +{ + if (!file_exists($dir)) { + mkdir($dir, 0777, true); + } +} + +function ensureIndexExists($dir): void +{ + $dir = rtrim($dir, '/'); + if (!file_exists($file = $dir . '/index.html')) { + touch($file); + } +} + function config($key) { global $config; if (is_array($key)) { @@ -1216,7 +1258,8 @@ function setting($key) return $settings[$key[0]] = $key[1]; } - return $settings[$key]['value']; + $ret = $settings[$key]; + return isset($ret) ? $ret['value'] : null; } function clearCache() @@ -1265,14 +1308,15 @@ function clearCache() $db->setClearCacheAfter(true); } + if (function_exists('apcu_clear_cache')) { + apcu_clear_cache(); + } + deleteDirectory(CACHE . 'signatures', ['index.html'], true); deleteDirectory(CACHE . 'twig', ['index.html'], true); deleteDirectory(CACHE . 'plugins', ['index.html'], true); deleteDirectory(CACHE, ['signatures', 'twig', 'plugins', 'index.html', 'persistent'], true); - // routes cache - clearRouteCache(); - global $hooks; $hooks->trigger(HOOK_CACHE_CLEAR, ['cache' => Cache::getInstance()]); @@ -1618,13 +1662,14 @@ function camelCaseToUnderscore($input) return ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $input)), '_'); } -function removeIfFirstSlash(&$text) { +function removeIfFirstSlash(&$text): void +{ if(strpos($text, '/') === 0) { $text = str_replace_first('/', '', $text); } }; -function escapeHtml($html) { +function escapeHtml($html): string { return htmlspecialchars($html); } @@ -1638,7 +1683,7 @@ function getGuildNameById($id) return false; } -function getGuildLogoById($id) +function getGuildLogoById($id): string { $logo = 'default.gif'; @@ -1654,7 +1699,8 @@ function getGuildLogoById($id) return BASE_URL . GUILD_IMAGES_DIR . $logo; } -function displayErrorBoxWithBackButton($errors, $action = null) { +function displayErrorBoxWithBackButton($errors, $action = null): void +{ global $twig; $twig->display('error_box.html.twig', ['errors' => $errors]); $twig->display('account.back_button.html.twig', [ @@ -1682,6 +1728,49 @@ function getAccountIdentityColumn(): string return 'id'; } +function isCanary(): bool +{ + $vipSystemEnabled = configLua('vipSystemEnabled'); + return isset($vipSystemEnabled); +} + +function getStatusUptimeReadable(int $uptime): string +{ + $fullMinute = 60; + $fullHour = (60 * $fullMinute); + $fullDay = (24 * $fullHour); + $fullMonth = (30 * $fullDay); + $fullYear = (365 * $fullDay); + + // years + $years = floor($uptime / $fullYear); + $y = ($years > 1 ? "$years years, " : ($years == 1 ? 'year, ' : '')); + + $uptime -= $years * $fullYear; + + // months + $months = floor($uptime / $fullMonth); + $m = ($months > 1 ? "$months months, " : ($months == 1 ? 'month, ' : '')); + + $uptime -= $months * $fullMonth; + + // days + $days = floor($uptime / $fullDay); + $d = ($days > 1 ? "$days days, " : ($days == 1 ? 'day, ' : '')); + + $uptime -= $days * $fullDay; + + // hours + $hours = floor($uptime / $fullHour); + + $uptime -= $hours * $fullHour; + + // minutes + $min = floor($uptime / $fullMinute); + + return "{$y}{$m}{$d}{$hours}h {$min}m"; +} + // validator functions require_once SYSTEM . 'compat/base.php'; diff --git a/system/init.php b/system/init.php index 44634265..862b5370 100644 --- a/system/init.php +++ b/system/init.php @@ -12,11 +12,15 @@ use DebugBar\StandardDebugBar; use MyAAC\Cache\Cache; use MyAAC\CsrfToken; use MyAAC\Hooks; +use MyAAC\Plugins; use MyAAC\Models\Town; use MyAAC\Settings; defined('MYAAC') or die('Direct access not allowed!'); +ensureIndexExists(CACHE); +ensureIndexExists(CACHE . 'twig/'); + global $config; if(!isset($config['installed']) || !$config['installed']) { throw new RuntimeException('MyAAC has not been installed yet or there was error during installation. Please install again.'); @@ -46,6 +50,11 @@ if(isset($config['gzip_output']) && $config['gzip_output'] && isset($_SERVER['HT global $cache; $cache = Cache::getInstance(); +// load plugins init.php +foreach (Plugins::getInits() as $init) { + require $init; +} + // event system global $hooks; $hooks = new Hooks(); @@ -138,6 +147,15 @@ $ots = POT::getInstance(); $eloquentConnection = null; require_once SYSTEM . 'database.php'; +define('USE_ACCOUNT_NAME', $db->hasColumn('accounts', 'name')); +define('USE_ACCOUNT_NUMBER', $db->hasColumn('accounts', 'number')); +define('USE_ACCOUNT_SALT', $db->hasColumn('accounts', 'salt')); + +define('HAS_ACCOUNT_COINS', $db->hasColumn('accounts', 'coins')); +define('HAS_ACCOUNT_COINS_TRANSFERABLE', $db->hasColumn('accounts', 'coins_transferable')); +define('HAS_ACCOUNT_TRANSFERABLE_COINS', $db->hasColumn('accounts', 'transferable_coins')); +const ACCOUNT_COINS_TRANSFERABLE_COLUMN = (HAS_ACCOUNT_COINS_TRANSFERABLE ? 'coins_transferable' : 'transferable_coins'); + $twig->addGlobal('logged', false); $twig->addGlobal('account_logged', new \OTS_Account()); @@ -182,10 +200,6 @@ if($settingsItemImagesURL[strlen($settingsItemImagesURL) - 1] !== '/') { setting(['core.item_images_url', $settingsItemImagesURL . '/']); } -define('USE_ACCOUNT_NAME', $db->hasColumn('accounts', 'name')); -define('USE_ACCOUNT_NUMBER', $db->hasColumn('accounts', 'number')); -define('USE_ACCOUNT_SALT', $db->hasColumn('accounts', 'salt')); - $towns = Cache::remember('towns', 10 * 60, function () use ($db) { if ($db->hasTable('towns') && Town::count() > 0) { return Town::orderBy('id', 'ASC')->pluck('name', 'id')->toArray(); diff --git a/system/libs/pot/OTS_Account.php b/system/libs/pot/OTS_Account.php index 8528cf3f..8505128e 100644 --- a/system/libs/pot/OTS_Account.php +++ b/system/libs/pot/OTS_Account.php @@ -38,7 +38,7 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @var array * @version 0.1.5 */ - private $data = array('email' => '', 'rlname' => '','location' => '', 'country' => '','web_flags' => 0, 'lastday' => 0, 'premdays' => 0, 'created' => 0); + private $data = array('email' => '', 'rlname' => '','location' => '', 'country' => '','web_flags' => 0, 'lastday' => 0, 'premdays' => 0, 'created' => 0); public static $cache = array(); @@ -66,39 +66,39 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @example examples/create.php create.php * @tutorial POT/Accounts.pkg#create */ - public function createNamed($name = null) - { - // if name is not passed then it will be generated randomly - if( !isset($name) ) - { - // reads already existing names - foreach( $this->db->query('SELECT ' . $this->db->fieldName('name') . ' FROM ' . $this->db->tableName('accounts') )->fetchAll() as $account) - { - $exist[] = $account['name']; - } + public function createNamed($name = null) + { + // if name is not passed then it will be generated randomly + if( !isset($name) ) + { + // reads already existing names + foreach( $this->db->query('SELECT ' . $this->db->fieldName('name') . ' FROM ' . $this->db->tableName('accounts') )->fetchAll() as $account) + { + $exist[] = $account['name']; + } - // initial name - $name = uniqid(); + // initial name + $name = uniqid(); - // repeats until name is unique - while( in_array($name, $exist) ) - { - $name .= '_'; - } + // repeats until name is unique + while( in_array($name, $exist) ) + { + $name .= '_'; + } - // resets array for account numbers loop - $exist = array(); - } + // resets array for account numbers loop + $exist = array(); + } - // saves blank account info - $this->db->exec('INSERT INTO ' . $this->db->tableName('accounts') . ' (' . $this->db->fieldName('name') . ', ' . $this->db->fieldName('password') . ', ' . $this->db->fieldName('email') . ') VALUES (' . $this->db->quote($name) . ', \'\', \'\')'); + // saves blank account info + $this->db->exec('INSERT INTO ' . $this->db->tableName('accounts') . ' (' . $this->db->fieldName('name') . ', ' . $this->db->fieldName('password') . ', ' . $this->db->fieldName('email') . ') VALUES (' . $this->db->quote($name) . ', \'\', \'\')'); - // reads created account's ID - $this->data['id'] = $this->db->lastInsertId(); + // reads created account's ID + $this->data['id'] = $this->db->lastInsertId(); - // return name of newly created account - return $name; - } + // return name of newly created account + return $name; + } /** * @param $email @@ -166,8 +166,8 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws Exception ON lastInsertId error. * @deprecated 0.1.5 Use createNamed(). */ - public function create($name = NULL, $id = NULL) - { + public function create($name = NULL, $id = NULL) + { if(isset($name)) { $nameOrNumber = 'name'; $nameOrNumberValue = $name; @@ -183,8 +183,8 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable } } - // saves blank account info - $this->db->exec('INSERT INTO `accounts` (' . (isset($id) ? '`id`,' : '') . (isset($nameOrNumber) ? '`' . $nameOrNumber . '`,' : '') . '`password`, `email`, `created`) VALUES (' . (isset($id) ? $id . ',' : '') . (isset($nameOrNumber) ? $this->db->quote($nameOrNumberValue) . ',' : '') . ' \'\', \'\',' . time() . ')'); + // saves blank account info + $this->db->exec('INSERT INTO `accounts` (' . (isset($id) ? '`id`,' : '') . (isset($nameOrNumber) ? '`' . $nameOrNumber . '`,' : '') . '`password`, `email`, `created`) VALUES (' . (isset($id) ? $id . ',' : '') . (isset($nameOrNumber) ? $this->db->quote($nameOrNumberValue) . ',' : '') . ' \'\', \'\',' . time() . ')'); if(isset($name)) { $this->data['name'] = $name; @@ -206,8 +206,8 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable throw new Exception(__CLASS__ . ':' . __METHOD__ . ' unexpected error. Please report to MyAAC Developers.'); } - return $this->data['id']; - } + return $this->data['id']; + } /** * @version 0.0.6 @@ -218,10 +218,10 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @return int Created account number. * @deprecated 0.0.6 There is no more group_id field in database, use create(). */ - public function createEx(OTS_Group $group, $min = 1, $max = 9999999) - { - return $this->create($min, $max); - } + public function createEx(OTS_Group $group, $min = 1, $max = 9999999) + { + return $this->create($min, $max); + } /** * Loads account with given number. @@ -230,8 +230,8 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @param int $id Account number. * @throws PDOException On PDO operation error. */ - public function load($id, $fresh = false) - { + public function load($id, $fresh = false) + { if(!$fresh && isset(self::$cache[$id])) { $this->data = self::$cache[$id]; return; @@ -244,10 +244,10 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable $nameOrNumber = '`number`,'; } - // SELECT query on database + // SELECT query on database $this->data = $this->db->query('SELECT `id`, ' . $nameOrNumber . '`password`, `email`, `rlname`, `location`, `country`, `web_flags`, ' . ($this->db->hasColumn('accounts', 'premdays') ? '`premdays`, ' : '') . ($this->db->hasColumn('accounts', 'lastday') ? '`lastday`, ' : ($this->db->hasColumn('accounts', 'premend') ? '`premend`,' : ($this->db->hasColumn('accounts', 'premium_ends_at') ? '`premium_ends_at`,' : ''))) . '`created` FROM `accounts` WHERE `id` = ' . (int) $id)->fetch(); self::$cache[$id] = $this->data; - } + } /** * Loads account by it's name. @@ -261,22 +261,22 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @param string $name Account's name. * @throws PDOException On PDO operation error. */ - public function find($name) - { + public function find($name) + { $nameOrNumberColumn = 'name'; if (USE_ACCOUNT_NUMBER) { $nameOrNumberColumn = 'number'; } - // finds player's ID - $id = $this->db->query('SELECT `id` FROM `accounts` WHERE `' . $nameOrNumberColumn . '` = ' . $this->db->quote($name) )->fetch(); + // finds player's ID + $id = $this->db->query('SELECT `id` FROM `accounts` WHERE `' . $nameOrNumberColumn . '` = ' . $this->db->quote($name) )->fetch(); - // if anything was found - if( isset($id['id']) ) - { - $this->load($id['id']); - } - } + // if anything was found + if( isset($id['id']) ) + { + $this->load($id['id']); + } + } /** * Loads account by it's e-mail address. @@ -286,27 +286,27 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @param string $email Account's e-mail address. * @throws PDOException On PDO operation error. */ - public function findByEMail($email) - { - // finds player's ID - $id = $this->db->query('SELECT `id` FROM `accounts` WHERE `email` = ' . $this->db->quote($email) )->fetch(); + public function findByEMail($email) + { + // finds player's ID + $id = $this->db->query('SELECT `id` FROM `accounts` WHERE `email` = ' . $this->db->quote($email) )->fetch(); - // if anything was found - if( isset($id['id']) ) - { - $this->load($id['id']); - } - } + // if anything was found + if( isset($id['id']) ) + { + $this->load($id['id']); + } + } /** * Checks if object is loaded. * * @return bool Load state. */ - public function isLoaded() - { - return isset($this->data['id']); - } + public function isLoaded() + { + return isset($this->data['id']); + } /** * Updates account in database. @@ -323,16 +323,16 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws E_OTS_NotLoaded If account doesn't have ID assigned. * @throws PDOException On PDO operation error. */ - public function save() - { - if( !isset($this->data['id']) ) - { - throw new E_OTS_NotLoaded(); - } + public function save() + { + if( !isset($this->data['id']) ) + { + throw new E_OTS_NotLoaded(); + } $field = 'lastday'; if($this->db->hasColumn('accounts', 'premend')) { // othire - $field = 'premend'; + $field = 'premend'; if(!isset($this->data['premend'])) { $this->data['premend'] = 0; } @@ -344,9 +344,9 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable } } - // UPDATE query on database - $this->db->exec('UPDATE `accounts` SET ' . ($this->db->hasColumn('accounts', 'name') ? '`name` = ' . $this->db->quote($this->data['name']) . ',' : '') . '`password` = ' . $this->db->quote($this->data['password']) . ', `email` = ' . $this->db->quote($this->data['email']) . ', `rlname` = ' . $this->db->quote($this->data['rlname']) . ', `location` = ' . $this->db->quote($this->data['location']) . ', `country` = ' . $this->db->quote($this->data['country']) . ', `web_flags` = ' . (int) $this->data['web_flags'] . ', ' . ($this->db->hasColumn('accounts', 'premdays') ? '`premdays` = ' . (int) $this->data['premdays'] . ',' : '') . '`' . $field . '` = ' . (int) $this->data[$field] . ' WHERE `id` = ' . $this->data['id']); - } + // UPDATE query on database + $this->db->exec('UPDATE `accounts` SET ' . ($this->db->hasColumn('accounts', 'name') ? '`name` = ' . $this->db->quote($this->data['name']) . ',' : '') . '`password` = ' . $this->db->quote($this->data['password']) . ', `email` = ' . $this->db->quote($this->data['email']) . ', `rlname` = ' . $this->db->quote($this->data['rlname']) . ', `location` = ' . $this->db->quote($this->data['location']) . ', `country` = ' . $this->db->quote($this->data['country']) . ', `web_flags` = ' . (int) $this->data['web_flags'] . ', ' . ($this->db->hasColumn('accounts', 'premdays') ? '`premdays` = ' . (int) $this->data['premdays'] . ',' : '') . '`' . $field . '` = ' . (int) $this->data[$field] . ' WHERE `id` = ' . $this->data['id']); + } /** * Account number. @@ -359,15 +359,15 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @return int Account number. * @throws E_OTS_NotLoaded If account is not loaded. */ - public function getId() - { - if( !isset($this->data['id']) ) - { - throw new E_OTS_NotLoaded(); - } + public function getId() + { + if( !isset($this->data['id']) ) + { + throw new E_OTS_NotLoaded(); + } - return $this->data['id']; - } + return $this->data['id']; + } public function getNumber() { @@ -378,45 +378,45 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable return $this->data['id']; } - public function getRLName() - { - if( !isset($this->data['rlname']) ) - { - throw new E_OTS_NotLoaded(); - } + public function getRLName() + { + if( !isset($this->data['rlname']) ) + { + throw new E_OTS_NotLoaded(); + } - return $this->data['rlname']; - } + return $this->data['rlname']; + } - public function getLocation() - { - if( !isset($this->data['location']) ) - { - throw new E_OTS_NotLoaded(); - } + public function getLocation() + { + if( !isset($this->data['location']) ) + { + throw new E_OTS_NotLoaded(); + } - return $this->data['location']; - } + return $this->data['location']; + } - public function getCountry() - { - if( !isset($this->data['country']) ) - { - throw new E_OTS_NotLoaded(); - } + public function getCountry() + { + if( !isset($this->data['country']) ) + { + throw new E_OTS_NotLoaded(); + } - return $this->data['country']; - } + return $this->data['country']; + } - public function getWebFlags() - { - if( !isset($this->data['web_flags']) ) - { - throw new E_OTS_NotLoaded(); - } + public function getWebFlags() + { + if( !isset($this->data['web_flags']) ) + { + throw new E_OTS_NotLoaded(); + } - return $this->data['web_flags']; - } + return $this->data['web_flags']; + } public function hasFlag($flag) { @@ -443,10 +443,11 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable throw new E_OTS_NotLoaded(); } - if(isset($this->data['premium_ends_at']) || isset($this->data['premend'])) { - $col = isset($this->data['premium_ends_at']) ? 'premium_ends_at' : 'premend'; - $ret = ceil(($this->data[$col] - time()) / (24 * 60 * 60)); - return max($ret, 0); + if(isset($this->data['premium_ends_at']) || isset($this->data['premend']) || + (isCanary() && isset($this->data['lastday']))) { + $col = (isset($this->data['premium_ends_at']) ? 'premium_ends_at' : (isset($this->data['lastday']) ? 'lastday' : 'premend')); + $ret = ceil(($this->data[$col] - time()) / (24 * 60 * 60)); + return max($ret, 0); } if($this->data['premdays'] == 0) { @@ -462,40 +463,39 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable } public function getLastLogin() - { - if( !isset($this->data['lastday']) ) - { - throw new E_OTS_NotLoaded(); - } + { + if( !isset($this->data['lastday']) ) + { + throw new E_OTS_NotLoaded(); + } - return $this->data['lastday']; - } + return $this->data['lastday']; + } - public function isPremium() - { - global $config; - if(isset($config['lua']['freePremium']) && getBoolean($config['lua']['freePremium'])) return true; + public function isPremium(): bool + { + if(isset($this->data['premium_ends_at']) || isset($this->data['premend']) || + (isCanary() && isset($this->data['lastday']))) { + $col = (isset($this->data['premium_ends_at']) ? 'premium_ends_at' : (isset($this->data['lastday']) ? 'lastday' : 'premend')); + return $this->data[$col] > time(); + } - if(isset($this->data['premium_ends_at'])) { - return $this->data['premium_ends_at'] > time(); - } - - if(isset($this->data['premend'])) { - return $this->data['premend'] > time(); + if($this->data['premdays'] == self::GRATIS_PREMIUM_DAYS){ + return true; } return ($this->data['premdays'] - (date("z", time()) + (365 * (date("Y", time()) - date("Y", $this->data['lastday']))) - date("z", $this->data['lastday'])) > 0); } - public function getCreated() - { - if( !isset($this->data['created']) ) - { - throw new E_OTS_NotLoaded(); - } + public function getCreated() + { + if( !isset($this->data['created']) ) + { + throw new E_OTS_NotLoaded(); + } - return $this->data['created']; - } + return $this->data['created']; + } /** * Name. @@ -504,37 +504,37 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @since 0.7.5 * @throws E_OTS_NotLoaded If account is not loaded. */ - public function setPremDays($premdays) - { + public function setPremDays($premdays) + { $this->data['premdays'] = (int) $premdays; $this->data['premend'] = time() + ($premdays * 24 * 60 * 60); $this->data['premium_ends_at'] = time() + ($premdays * 24 * 60 * 60); - } + } - public function setRLName($name) - { - $this->data['rlname'] = (string) $name; - } + public function setRLName($name) + { + $this->data['rlname'] = (string) $name; + } - public function setLocation($location) - { - $this->data['location'] = (string) $location; - } + public function setLocation($location) + { + $this->data['location'] = (string) $location; + } - public function setCountry($country) - { - $this->data['country'] = (string) $country; - } + public function setCountry($country) + { + $this->data['country'] = (string) $country; + } public function setLastLogin($lastlogin) - { - $this->data['lastday'] = (int) $lastlogin; - } + { + $this->data['lastday'] = (int) $lastlogin; + } - public function setWebFlags($webflags) - { - $this->data['web_flags'] = (int) $webflags; - } + public function setWebFlags($webflags) + { + $this->data['web_flags'] = (int) $webflags; + } /** * Name. @@ -544,15 +544,15 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @return string Name. * @throws E_OTS_NotLoaded If account is not loaded. */ - public function getName() - { - if( !isset($this->data['name']) ) - { - throw new E_OTS_NotLoaded(); - } + public function getName() + { + if( !isset($this->data['name']) ) + { + throw new E_OTS_NotLoaded(); + } - return $this->data['name']; - } + return $this->data['name']; + } /** * Sets account's name. @@ -565,10 +565,10 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @since 0.1.5 * @param string $name Account name. */ - public function setName($name) - { - $this->data['name'] = (string) $name; - } + public function setName($name) + { + $this->data['name'] = (string) $name; + } /** * Account's password. @@ -585,15 +585,15 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @return string Password. * @throws E_OTS_NotLoaded If account is not loaded. */ - public function getPassword() - { - if( !isset($this->data['password']) ) - { - throw new E_OTS_NotLoaded(); - } + public function getPassword() + { + if( !isset($this->data['password']) ) + { + throw new E_OTS_NotLoaded(); + } - return $this->data['password']; - } + return $this->data['password']; + } /** * Sets account's password. @@ -608,10 +608,10 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * * @param string $password Password. */ - public function setPassword($password) - { - $this->data['password'] = (string) $password; - } + public function setPassword($password) + { + $this->data['password'] = (string) $password; + } /** * E-mail address. * @@ -623,15 +623,15 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @return string E-mail. * @throws E_OTS_NotLoaded If account is not loaded. */ - public function getEMail() - { - if( !isset($this->data['email']) ) - { - throw new E_OTS_NotLoaded(); - } + public function getEMail() + { + if( !isset($this->data['email']) ) + { + throw new E_OTS_NotLoaded(); + } - return $this->data['email']; - } + return $this->data['email']; + } /** * Sets account's email. @@ -642,10 +642,10 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * * @param string $email E-mail address. */ - public function setEMail($email) - { - $this->data['email'] = (string) $email; - } + public function setEMail($email) + { + $this->data['email'] = (string) $email; + } /** * Reads custom field. @@ -665,16 +665,16 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws E_OTS_NotLoaded If account is not loaded. * @throws PDOException On PDO operation error. */ - public function getCustomField($field) - { - if( !isset($this->data['id']) ) - { - throw new E_OTS_NotLoaded(); - } + public function getCustomField($field) + { + if( !isset($this->data['id']) ) + { + throw new E_OTS_NotLoaded(); + } - $value = $this->db->query('SELECT ' . $this->db->fieldName($field) . ' FROM ' . $this->db->tableName('accounts') . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id'])->fetch(); - return $value[$field]; - } + $value = $this->db->query('SELECT ' . $this->db->fieldName($field) . ' FROM ' . $this->db->tableName('accounts') . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id'])->fetch(); + return $value[$field]; + } /** * Writes custom field. @@ -698,20 +698,20 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws E_OTS_NotLoaded If account is not loaded. * @throws PDOException On PDO operation error. */ - public function setCustomField($field, $value) - { - if( !isset($this->data['id']) ) - { - throw new E_OTS_NotLoaded(); - } + public function setCustomField($field, $value) + { + if( !isset($this->data['id']) ) + { + throw new E_OTS_NotLoaded(); + } - // quotes value for SQL query - if(!( is_int($value) || is_float($value) )) - { - $value = $this->db->quote($value); - } - $this->db->exec('UPDATE ' . $this->db->tableName('accounts') . ' SET ' . $this->db->fieldName($field) . ' = ' . $value . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id']); - } + // quotes value for SQL query + if(!( is_int($value) || is_float($value) )) + { + $value = $this->db->quote($value); + } + $this->db->exec('UPDATE ' . $this->db->tableName('accounts') . ' SET ' . $this->db->fieldName($field) . ' = ' . $value . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id']); + } /** * @version 0.1.0 @@ -719,25 +719,25 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws E_OTS_NotLoaded If account is not loaded. * @deprecated 0.0.5 Use getPlayersList(). */ - public function getPlayers() - { - if( !isset($this->data['id']) ) - { - throw new E_OTS_NotLoaded(); - } + public function getPlayers() + { + if( !isset($this->data['id']) ) + { + throw new E_OTS_NotLoaded(); + } - $players = array(); + $players = array(); - foreach( $this->db->query('SELECT ' . $this->db->fieldName('id') . ' FROM ' . $this->db->tableName('players') . ' WHERE ' . $this->db->fieldName('account_id') . ' = ' . $this->data['id'])->fetchAll() as $player) - { - // creates new object - $object = new OTS_Player(); - $object->load($player['id']); - $players[] = $object; - } + foreach( $this->db->query('SELECT ' . $this->db->fieldName('id') . ' FROM ' . $this->db->tableName('players') . ' WHERE ' . $this->db->fieldName('account_id') . ' = ' . $this->data['id'])->fetchAll() as $player) + { + // creates new object + $object = new OTS_Player(); + $object->load($player['id']); + $players[] = $object; + } - return $players; - } + return $players; + } /** * List of characters on account. @@ -755,16 +755,16 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @return OTS_Players_List List of players from current account. * @throws E_OTS_NotLoaded If account is not loaded. */ - public function getPlayersList($withDeleted = true) - { - if( !isset($this->data['id']) ) - { - throw new E_OTS_NotLoaded(); - } + public function getPlayersList($withDeleted = true) + { + if( !isset($this->data['id']) ) + { + throw new E_OTS_NotLoaded(); + } - // creates filter - $filter = new OTS_SQLFilter(); - $filter->compareField('account_id', (int) $this->data['id']); + // creates filter + $filter = new OTS_SQLFilter(); + $filter->compareField('account_id', (int) $this->data['id']); if(!$withDeleted) { global $db; @@ -775,12 +775,12 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable } } - // creates list object - $list = new OTS_Players_List(); - $list->setFilter($filter); + // creates list object + $list = new OTS_Players_List(); + $list->setFilter($filter); - return $list; - } + return $list; + } /** * @version 0.1.5 @@ -789,22 +789,22 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws PDOException On PDO operation error. * @deprecated 0.1.5 Use OTS_AccountBan class. */ - public function ban($time = 0) - { - // can't ban nothing - if( !$this->isLoaded() ) - { - throw new E_OTS_NotLoaded(); - } + public function ban($time = 0) + { + // can't ban nothing + if( !$this->isLoaded() ) + { + throw new E_OTS_NotLoaded(); + } - // creates ban entry - $ban = new OTS_AccountBan(); - $ban->setValue($this->data['id']); - $ban->setExpires($time); - $ban->setAdded( time() ); - $ban->activate(); - $ban->save(); - } + // creates ban entry + $ban = new OTS_AccountBan(); + $ban->setValue($this->data['id']); + $ban->setExpires($time); + $ban->setAdded( time() ); + $ban->activate(); + $ban->save(); + } /** * @version 0.1.5 @@ -812,19 +812,19 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws PDOException On PDO operation error. * @deprecated 0.1.5 Use OTS_AccountBan class. */ - public function unban() - { - // can't unban nothing - if( !$this->isLoaded() ) - { - throw new E_OTS_NotLoaded(); - } + public function unban() + { + // can't unban nothing + if( !$this->isLoaded() ) + { + throw new E_OTS_NotLoaded(); + } - // deletes ban entry - $ban = new OTS_AccountBan(); - $ban->find($this->data['id']); - $ban->delete(); - } + // deletes ban entry + $ban = new OTS_AccountBan(); + $ban->find($this->data['id']); + $ban->delete(); + } /** * @version 0.1.5 @@ -833,37 +833,37 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws PDOException On PDO operation error. * @deprecated 0.1.5 Use OTS_AccountBan class. */ - public function isBanned() - { - // nothing can't be banned - if( !$this->isLoaded() ) - { - throw new E_OTS_NotLoaded(); - } + public function isBanned() + { + // nothing can't be banned + if( !$this->isLoaded() ) + { + throw new E_OTS_NotLoaded(); + } if( !isset($this->data['banned']) ) $this->loadBan(); - return ($this->data['banned'] === true); - } + return ($this->data['banned'] === true); + } - public function getBanTime() - { - // nothing can't be banned - if( !$this->isLoaded() ) - { - throw new E_OTS_NotLoaded(); - } + public function getBanTime() + { + // nothing can't be banned + if( !$this->isLoaded() ) + { + throw new E_OTS_NotLoaded(); + } if( !isset($this->data['banned_time']) ) $this->loadBan(); - return $this->data['banned_time']; - } + return $this->data['banned_time']; + } - public function loadBan() - { - // nothing can't be banned - if( !$this->isLoaded() ) - { - throw new E_OTS_NotLoaded(); - } + public function loadBan() + { + // nothing can't be banned + if( !$this->isLoaded() ) + { + throw new E_OTS_NotLoaded(); + } if($this->db->hasTable('account_bans')) { $ban = $this->db->query('SELECT `expires_at` FROM `account_bans` WHERE `account_id` = ' . $this->data['id'] . ' AND (`expires_at` > ' . time() .' OR `expires_at` = -1) ORDER BY `expires_at` DESC')->fetch(); @@ -886,7 +886,7 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable $this->data['banned'] = false; $this->data['banned_time'] = 0; } - } + } /** * Deletes account. @@ -900,19 +900,19 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws E_OTS_NotLoaded If account is not loaded. * @throws PDOException On PDO operation error. */ - public function delete() - { - if( !isset($this->data['id']) ) - { - throw new E_OTS_NotLoaded(); - } + public function delete() + { + if( !isset($this->data['id']) ) + { + throw new E_OTS_NotLoaded(); + } - // deletes row from database - $this->db->exec('DELETE FROM ' . $this->db->tableName('accounts') . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id']); + // deletes row from database + $this->db->exec('DELETE FROM ' . $this->db->tableName('accounts') . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id']); - // resets object handle - unset($this->data['id']); - } + // resets object handle + unset($this->data['id']); + } /** * Checks highest access level of account. @@ -920,10 +920,10 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @return int Access level (highest access level of all characters). * @throws PDOException On PDO operation error. */ - public function getAccess() - { + public function getAccess() + { return $this->getGroupId(); - } + } public function getGroupId() { @@ -985,25 +985,25 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @return int Access level (highest access level of all characters). * @throws PDOException On PDO operation error. */ - public function getGuildAccess(OTS_Guild $guild) - { - // by default - $access = 0; + public function getGuildAccess(OTS_Guild $guild) + { + // by default + $access = 0; - // finds ranks of all characters - foreach($this->getPlayersList(false) as $player) - { - $rank = $player->getRank(); + // finds ranks of all characters + foreach($this->getPlayersList(false) as $player) + { + $rank = $player->getRank(); - // checks if rank's access level is higher then previouls found highest - if( isset($rank) && $rank->isLoaded() && $rank->getGuild()->getId() == $guild->getId() && $rank->getLevel() > $access) - { - $access = $rank->getLevel(); - } - } + // checks if rank's access level is higher then previouls found highest + if( isset($rank) && $rank->isLoaded() && $rank->getGuild()->getId() == $guild->getId() && $rank->getLevel() > $access) + { + $access = $rank->getLevel(); + } + } - return $access; - } + return $access; + } public function logAction($action) { @@ -1042,10 +1042,10 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @return Iterator List of players. */ #[\ReturnTypeWillChange] - public function getIterator() - { - return $this->getPlayersList(); - } + public function getIterator() + { + return $this->getPlayersList(); + } /** * Returns number of player within. @@ -1056,10 +1056,10 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws PDOException On PDO operation error. * @return int Count of players. */ - public function count(): int - { - return $this->getPlayersList()->count(); - } + public function count(): int + { + return $this->getPlayersList()->count(); + } /** * Magic PHP5 method. @@ -1072,44 +1072,44 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws OutOfBoundsException For non-supported properties. * @throws PDOException On PDO operation error. */ - public function __get($name) - { - switch($name) - { - case 'id': - return $this->getId(); + public function __get($name) + { + switch($name) + { + case 'id': + return $this->getId(); - case 'name': - return $this->getName(); + case 'name': + return $this->getName(); - case 'password': - return $this->getPassword(); + case 'password': + return $this->getPassword(); - case 'eMail': - return $this->getEMail(); + case 'eMail': + return $this->getEMail(); - case 'premiumEnd': - return $this->getPremiumEnd(); + case 'premiumEnd': + return $this->getPremiumEnd(); - case 'loaded': - return $this->isLoaded(); + case 'loaded': + return $this->isLoaded(); - case 'playersList': - return $this->getPlayersList(); + case 'playersList': + return $this->getPlayersList(); - case 'deleted': - return $this->isDeleted(); + case 'deleted': + return $this->isDeleted(); - case 'banned': - return $this->isBanned(); + case 'banned': + return $this->isBanned(); - case 'access': - return $this->getAccess(); + case 'access': + return $this->getAccess(); - default: - throw new OutOfBoundsException(); - } - } + default: + throw new OutOfBoundsException(); + } + } /** * Magic PHP5 method. @@ -1122,52 +1122,52 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws OutOfBoundsException For non-supported properties. * @throws PDOException On PDO operation error. */ - public function __set($name, $value) - { - switch($name) - { - case 'name': - $this->setName($name); - break; + public function __set($name, $value) + { + switch($name) + { + case 'name': + $this->setName($name); + break; - case 'password': - $this->setPassword($value); - break; + case 'password': + $this->setPassword($value); + break; - case 'eMail': - $this->setEMail($value); - break; + case 'eMail': + $this->setEMail($value); + break; - case 'premiumEnd': - $this->setPremiumEnd($value); - break; + case 'premiumEnd': + $this->setPremiumEnd($value); + break; - case 'deleted': - if($value) - { - $this->setDeleted(); - } - else - { - $this->unsetDeleted(); - } - break; + case 'deleted': + if($value) + { + $this->setDeleted(); + } + else + { + $this->unsetDeleted(); + } + break; - case 'banned': - if($value) - { - $this->ban(); - } - else - { - $this->unban(); - } - break; + case 'banned': + if($value) + { + $this->ban(); + } + else + { + $this->unban(); + } + break; - default: - throw new OutOfBoundsException(); - } - } + default: + throw new OutOfBoundsException(); + } + } /** * Returns string representation of object. @@ -1180,18 +1180,18 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @since 0.1.0 * @return string String representation of object. */ - public function __toString() - { - $ots = POT::getInstance(); + public function __toString() + { + $ots = POT::getInstance(); - // checks if display driver is loaded - if( $ots->isDisplayDriverLoaded() ) - { - return $ots->getDisplayDriver()->displayAccount($this); - } + // checks if display driver is loaded + if( $ots->isDisplayDriverLoaded() ) + { + return $ots->getDisplayDriver()->displayAccount($this); + } - return $this->getId(); - } + return $this->getId(); + } } /**#@-*/ diff --git a/system/libs/pot/OTS_DB_MySQL.php b/system/libs/pot/OTS_DB_MySQL.php index baa38c76..8b9626d9 100644 --- a/system/libs/pot/OTS_DB_MySQL.php +++ b/system/libs/pot/OTS_DB_MySQL.php @@ -26,10 +26,11 @@ use MyAAC\Cache\Cache; */ class OTS_DB_MySQL extends OTS_Base_DB { - private $has_table_cache = array(); - private $has_column_cache = array(); + private array $has_table_cache = []; + private array $has_column_cache = []; + private array $get_column_info_cache = []; - private $clearCacheAfter = false; + private bool $clearCacheAfter = false; /** * Creates database connection. * @@ -119,6 +120,11 @@ class OTS_DB_MySQL extends OTS_Base_DB if($cache->fetch('database_columns', $tmp) && $tmp) { $this->has_column_cache = unserialize($tmp); } + + $tmp = null; + if($cache->fetch('database_columns_info', $tmp) && $tmp) { + $this->get_column_info_cache = unserialize($tmp); + } } } @@ -155,11 +161,13 @@ class OTS_DB_MySQL extends OTS_Base_DB if ($this->clearCacheAfter) { $cache->delete('database_tables'); $cache->delete('database_columns'); + $cache->delete('database_columns_info'); $cache->delete('database_checksum'); } else { $cache->set('database_tables', serialize($this->has_table_cache), 3600); $cache->set('database_columns', serialize($this->has_column_cache), 3600); + $cache->set('database_columns_info', serialize($this->get_column_info_cache), 3600); $cache->set('database_checksum', serialize(sha1($config['database_host'] . '.' . $config['database_name'])), 3600); } } @@ -209,7 +217,8 @@ class OTS_DB_MySQL extends OTS_Base_DB return $sql; } - public function hasTable($name) { + public function hasTable($name): bool + { if(isset($this->has_table_cache[$name])) { return $this->has_table_cache[$name]; } @@ -217,12 +226,13 @@ class OTS_DB_MySQL extends OTS_Base_DB return $this->hasTableInternal($name); } - private function hasTableInternal($name) { - global $config; - return ($this->has_table_cache[$name] = $this->query('SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = ' . $this->quote($config['database_name']) . ' AND `TABLE_NAME` = ' . $this->quote($name) . ' LIMIT 1;')->rowCount() > 0); + private function hasTableInternal($name): bool + { + return ($this->has_table_cache[$name] = $this->query('SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = ' . $this->quote(config('database_name')) . ' AND `TABLE_NAME` = ' . $this->quote($name) . ' LIMIT 1;')->rowCount() > 0); } - public function hasColumn($table, $column) { + public function hasColumn($table, $column): bool + { if(isset($this->has_column_cache[$table . '.' . $column])) { return $this->has_column_cache[$table . '.' . $column]; } @@ -230,8 +240,8 @@ class OTS_DB_MySQL extends OTS_Base_DB return $this->hasColumnInternal($table, $column); } - private function hasColumnInternal($table, $column) { - return $this->hasTable($table) && ($this->has_column_cache[$table . '.' . $column] = count($this->query('SHOW COLUMNS FROM `' . $table . "` LIKE '" . $column . "'")->fetchAll()) > 0); + private function hasColumnInternal($table, $column): bool { + return $this->hasTable($table) && ($this->has_column_cache[$table . '.' . $column] = count($this->query('SHOW COLUMNS FROM `' . $table . "` LIKE " . $this->quote($column))->fetchAll()) > 0); } public function hasTableAndColumns(string $table, array $columns = []): bool @@ -247,7 +257,53 @@ class OTS_DB_MySQL extends OTS_Base_DB return true; } - public function revalidateCache() { + public function getColumnInfo(string $table, string $column): bool|array + { + if(isset($this->get_column_info_cache[$table . '.' . $column])) { + return $this->get_column_info_cache[$table . '.' . $column]; + } + + return $this->getColumnInfoInternal($table, $column); + } + + private function getColumnInfoInternal(string $table, string $column): bool|array + { + if (!$this->hasTable($table) || !$this->hasColumn($table, $column)) { + return false; + } + + $formatResult = function ($result) { + return [ + 'field' => $result['Field'], + 'type' => $result['Type'], + 'null' => strtolower($result['Null']), + 'default' => $result['Default'], + 'extra' => $result['Extra'], + ]; + }; + + $query = $this->query('SHOW COLUMNS FROM `' . $table . "` LIKE " . $this->quote($column)); + $rowCount = $query->rowCount(); + if ($rowCount > 1) { + $tmp = []; + + $results = $query->fetchAll(PDO::FETCH_ASSOC); + foreach ($results as $result) { + $tmp[] = $formatResult($result); + } + + return ($this->get_column_info_cache[$table . '.' . $column] = $tmp); + } + else if ($rowCount == 1) { + $result = $query->fetch(PDO::FETCH_ASSOC); + return ($this->get_column_info_cache[$table . '.' . $column] = $formatResult($result)); + } + + return []; + } + + public function revalidateCache(): void + { foreach($this->has_table_cache as $key => $value) { $this->hasTableInternal($key); } @@ -262,6 +318,21 @@ class OTS_DB_MySQL extends OTS_Base_DB $this->hasColumnInternal($explode[0], $explode[1]); } } + + foreach($this->get_column_info_cache as $key => $value) { + $explode = explode('.', $key); + if(!isset($this->has_table_cache[$explode[0]])) { // first check if table exist + $this->hasTableInternal($explode[0]); + } + + if($this->has_table_cache[$explode[0]]) { + $this->hasColumnInternal($explode[0], $explode[1]); + } + + if($this->has_table_cache[$explode[0]]) { + $this->getColumnInfoInternal($explode[0], $explode[1]); + } + } } public function setClearCacheAfter($clearCache) diff --git a/system/libs/pot/OTS_Player.php b/system/libs/pot/OTS_Player.php index 7b688d1c..099f5db1 100644 --- a/system/libs/pot/OTS_Player.php +++ b/system/libs/pot/OTS_Player.php @@ -2919,6 +2919,32 @@ class OTS_Player extends OTS_Row_DAO $this->data['banned'] = $ban['active']; $this->data['banned_time'] = $ban['expires']; } + + public function isNameLocked(): bool + { + // nothing can't be banned + if( !$this->isLoaded() ) { + throw new E_OTS_NotLoaded(); + } + + if($this->db->hasTable('player_namelocks')) { + $ban = $this->db->query('SELECT 1 FROM `player_namelocks` WHERE `player_id` = ' . $this->data['id'])->fetch(PDO::FETCH_ASSOC); + return (isset($ban['1'])); + } + else if($this->db->hasTable('bans')) { + if($this->db->hasColumn('bans', 'active')) { + $ban = $this->db->query('SELECT `active`, `expires` FROM `bans` WHERE `type` = 2 AND `active` = 1 AND `value` = ' . $this->data['id'] . ' AND (`expires` > ' . time() .' OR `expires` = -1) ORDER BY `expires` DESC')->fetch(); + return isset($ban['active']); + } + else { // tfs 0.2 + $ban = $this->db->query('SELECT `time` FROM `bans` WHERE `type` = 2 AND `account` = ' . $this->data['account_id'] . ' AND (`time` > ' . time() .' OR `time` = -1) ORDER BY `time` DESC')->fetch(); + + return isset($ban['time']) && ($ban['time'] == -1 || $ban['time'] > 0); + } + } + + return false; + } /** * Deletes player. * @@ -2953,21 +2979,14 @@ class OTS_Player extends OTS_Row_DAO * @return string Player proffesion name. * @throws E_OTS_NotLoaded If player is not loaded or global vocations list is not loaded. */ - public function getVocationName() + public function getVocationName(): string { if( !isset($this->data['vocation']) ) { throw new E_OTS_NotLoaded(); } - global $config; - $voc = $this->getVocation(); - if(!isset($config['vocations'][$voc])) { - return 'Unknown'; - } - - return $config['vocations'][$voc]; - //return POT::getInstance()->getVocationsList()->getVocationName($this->data['vocation']); + return OTS_Toolbox::getVocationName($this->data['vocation'], $this->data['promotion'] ?? 0); } /** diff --git a/system/libs/pot/OTS_ServerInfo.php b/system/libs/pot/OTS_ServerInfo.php index 76ebcaf7..a0ee464f 100644 --- a/system/libs/pot/OTS_ServerInfo.php +++ b/system/libs/pot/OTS_ServerInfo.php @@ -97,6 +97,8 @@ class OTS_ServerInfo return new OTS_Buffer($data); } + log_append('status-error.log', "Cannot connect to {$this->server}:{$this->port} - Error code: $error, message: $message"); + return false; } diff --git a/system/libs/pot/OTS_Toolbox.php b/system/libs/pot/OTS_Toolbox.php index 980e5cb5..575dba6f 100644 --- a/system/libs/pot/OTS_Toolbox.php +++ b/system/libs/pot/OTS_Toolbox.php @@ -15,7 +15,7 @@ /** * Toolbox for common operations. - * + * * @package POT * @version 0.1.5 */ @@ -23,41 +23,41 @@ class OTS_Toolbox { /** * Calculates experience points needed for given level. - * + * * @param int $level Level for which experience should be calculated. * @param int $experience Current experience points. * @return int Experience points for level. */ public static function experienceForLevel($level, $experience = 0) - { - //return 50 * ($level - 1) * ($level * $level - 5 * $level + 12) / 3 - $experience; + { + //return 50 * ($level - 1) * ($level * $level - 5 * $level + 12) / 3 - $experience; $level = $level - 1; return ((50 * $level * $level * $level) - (150 * $level * $level) + (400 * $level)) / 3; - } + } /** * Finds out which level user have basing on his/her experience. - * + * *

* PHP doesn't support complex numbers natively so solving third-level polynomials would be quite hard. Rather then doing this, this method iterates calculating experience for next levels until it finds one which requires enought experience we have. Because of that, for high experience values this function can take relatively long time to be executed. *

- * + * * @param int $experience Current experience points. * @return int Experience level. */ - public static function levelForExperience($experience) - { - // default level - $level = 1; + public static function levelForExperience($experience) + { + // default level + $level = 1; - // until we will find level which requires more experience then we have we will step to next - while( self::experienceForLevel($level + 1) <= $experience) - { - $level++; - } + // until we will find level which requires more experience then we have we will step to next + while( self::experienceForLevel($level + 1) <= $experience) + { + $level++; + } - return $level; - } + return $level; + } /** * @version 0.1.5 @@ -65,25 +65,25 @@ class OTS_Toolbox * @return OTS_Players_List Filtered list. * @deprecated 0.1.5 Use OTS_PlayerBans_List. */ - public static function bannedPlayers() - { - // creates filter - $filter = new OTS_SQLFilter(); - $filter->addFilter( new OTS_SQLField('type', 'bans'), POT::BAN_PLAYER); - $filter->addFilter( new OTS_SQLField('active', 'bans'), 1); - $filter->addFilter( new OTS_SQLField('value', 'bans'), new OTS_SQLField('id', 'players') ); + public static function bannedPlayers() + { + // creates filter + $filter = new OTS_SQLFilter(); + $filter->addFilter( new OTS_SQLField('type', 'bans'), POT::BAN_PLAYER); + $filter->addFilter( new OTS_SQLField('active', 'bans'), 1); + $filter->addFilter( new OTS_SQLField('value', 'bans'), new OTS_SQLField('id', 'players') ); - // selects only active bans - $actives = new OTS_SQLFilter(); - $actives->addFilter( new OTS_SQLField('expires', 'bans'), 0); - $actives->addFilter( new OTS_SQLField('time', 'bans'), time(), OTS_SQLFilter::OPERATOR_GREATER, OTS_SQLFilter::CRITERIUM_OR); - $filter->addFilter($actives); + // selects only active bans + $actives = new OTS_SQLFilter(); + $actives->addFilter( new OTS_SQLField('expires', 'bans'), 0); + $actives->addFilter( new OTS_SQLField('time', 'bans'), time(), OTS_SQLFilter::OPERATOR_GREATER, OTS_SQLFilter::CRITERIUM_OR); + $filter->addFilter($actives); - // creates list and aplies filter - $list = new OTS_Players_List(); - $list->setFilter($filter); - return $list; - } + // creates list and aplies filter + $list = new OTS_Players_List(); + $list->setFilter($filter); + return $list; + } /** * @version 0.1.5 @@ -91,25 +91,34 @@ class OTS_Toolbox * @return OTS_Accounts_List Filtered list. * @deprecated 0.1.5 Use OTS_AccountBans_List. */ - public static function bannedAccounts() - { - // creates filter - $filter = new OTS_SQLFilter(); - $filter->addFilter( new OTS_SQLField('type', 'bans'), POT::BAN_ACCOUNT); - $filter->addFilter( new OTS_SQLField('active', 'bans'), 1); - $filter->addFilter( new OTS_SQLField('value', 'bans'), new OTS_SQLField('id', 'accounts') ); + public static function bannedAccounts() + { + // creates filter + $filter = new OTS_SQLFilter(); + $filter->addFilter( new OTS_SQLField('type', 'bans'), POT::BAN_ACCOUNT); + $filter->addFilter( new OTS_SQLField('active', 'bans'), 1); + $filter->addFilter( new OTS_SQLField('value', 'bans'), new OTS_SQLField('id', 'accounts') ); - // selects only active bans - $actives = new OTS_SQLFilter(); - $actives->addFilter( new OTS_SQLField('expires', 'bans'), 0); - $actives->addFilter( new OTS_SQLField('time', 'bans'), time(), OTS_SQLFilter::OPERATOR_GREATER, OTS_SQLFilter::CRITERIUM_OR); - $filter->addFilter($actives); + // selects only active bans + $actives = new OTS_SQLFilter(); + $actives->addFilter( new OTS_SQLField('expires', 'bans'), 0); + $actives->addFilter( new OTS_SQLField('time', 'bans'), time(), OTS_SQLFilter::OPERATOR_GREATER, OTS_SQLFilter::CRITERIUM_OR); + $filter->addFilter($actives); - // creates list and aplies filter - $list = new OTS_Accounts_List(); - $list->setFilter($filter); - return $list; - } + // creates list and aplies filter + $list = new OTS_Accounts_List(); + $list->setFilter($filter); + return $list; + } + + public static function getVocationName($id, $promotion = 0): string + { + if($promotion > 0) { + $id = ($id + ($promotion * config('vocations_amount'))); + } + + return config('vocations')[$id] ?? 'Unknown'; + } } /**#@-*/ diff --git a/system/locale/de/install.php b/system/locale/de/install.php index ad354609..351f6009 100644 --- a/system/locale/de/install.php +++ b/system/locale/de/install.php @@ -48,6 +48,8 @@ $locale['step_config'] = 'Konfiguration'; $locale['step_config_title'] = 'Grundkonfiguration'; $locale['step_config_server_path'] = 'Serverpfad'; $locale['step_config_server_path_desc'] = 'Pfad zu Ihrem TFS-Hauptverzeichnis, in dem sich die config.lua befinden.'; +$locale['step_config_site_url'] = 'Website URL'; +$locale['step_config_site_url_desc'] = 'Ihre Website-Adresse.'; $locale['step_config_mail_admin'] = 'Admin E-Mail'; $locale['step_config_mail_admin_desc'] = 'Adresse, an die E-Mails aus dem Kontaktformular gesendet werden, z. B. admin@gmail.com'; $locale['step_config_mail_admin_error'] = 'Admin E-Mail ist nicht korrekt.'; @@ -76,6 +78,7 @@ $locale['step_database_error_mysql_connect_3'] = 'MySQL ist nicht richtig konfig $locale['step_database_error_mysql_connect_4'] = 'MySQL-Server läuft nicht.'; $locale['step_database_error_schema'] = 'Fehler beim Importieren des Schemas:'; $locale['step_database_success_schema'] = '$PREFIX$ Tabellen wurden erfolgreich installiert.'; +$locale['step_database_success_import_data'] = 'Import von Daten für Tabellen was erfolgreich.'; $locale['step_database_error_file'] = '$FILE$ konnte nicht geöffnet werden. Bitte kopieren Sie diesen Inhalt und fügen Sie ihn dort ein:'; $locale['step_database_adding_field'] = 'Folgendes Feld wurde hinzugefügt: '; $locale['step_database_modifying_field'] = 'Folgendes Feld wurde geändert: '; diff --git a/system/locale/en/install.php b/system/locale/en/install.php index 0cbd29ad..c623035c 100644 --- a/system/locale/en/install.php +++ b/system/locale/en/install.php @@ -52,6 +52,8 @@ $locale['step_config'] = 'Configuration'; $locale['step_config_title'] = 'Basic configuration'; $locale['step_config_server_path'] = 'Server path'; $locale['step_config_server_path_desc'] = 'Path to your TFS main directory, where you have config.lua located.'; +$locale['step_config_site_url'] = 'Website URL'; +$locale['step_config_site_url_desc'] = 'Your website address.'; $locale['step_config_mail_admin'] = 'Admin Email'; $locale['step_config_mail_admin_desc'] = 'Address where emails from contact form will be delivered, for example admin@gmail.com'; $locale['step_config_mail_admin_error'] = 'Admin Email is not correct.'; @@ -81,6 +83,7 @@ $locale['step_database_error_mysql_connect_3'] = 'MySQL is not configured proper $locale['step_database_error_mysql_connect_4'] = 'MySQL server is not running.'; $locale['step_database_error_schema'] = 'Error while importing schema:'; $locale['step_database_success_schema'] = 'Successfully installed $PREFIX$ tables.'; +$locale['step_database_success_import_data'] = 'Successfully imported base data for tables.'; $locale['step_database_error_file'] = '$FILE$ couldn\'t be opened. Please copy this content and paste there:'; $locale['step_database_adding_field'] = 'Adding field'; $locale['step_database_modifying_field'] = 'Modifying field'; diff --git a/system/locale/pl/install.php b/system/locale/pl/install.php index 5b6725cf..a8ea0cf6 100644 --- a/system/locale/pl/install.php +++ b/system/locale/pl/install.php @@ -52,6 +52,8 @@ $locale['step_config'] = 'Konfiguracja'; $locale['step_config_title'] = 'Podstawowa konfiguracja'; $locale['step_config_server_path'] = 'Ścieżka do serwera'; $locale['step_config_server_path_desc'] = 'Ścieżka do Twojego folderu z TFS, gdzie znajduje się plik config.lua.'; +$locale['step_config_server_url'] = 'Adres strony'; +$locale['step_config_server_url_desc'] = 'Adres tej strony www.'; $locale['step_config_mail_admin'] = 'E-Mail admina'; $locale['step_config_mail_admin_desc'] = 'Na ten adres będą dostarczane E-Maile z formularza kontaktowego, przykładowo admin@gmail.com'; $locale['step_config_mail_admin_error'] = 'E-Mail admina jest niepoprawny.'; @@ -79,7 +81,8 @@ $locale['step_database_error_mysql_connect_2'] = 'Możliwe przyczyny:'; $locale['step_database_error_mysql_connect_3'] = 'MySQL nie jest poprawnie skonfigurowane w config.lua.'; $locale['step_database_error_mysql_connect_4'] = 'Serwer MySQL nie jest uruchomiony.'; $locale['step_database_error_schema'] = 'Błąd podczas importowania struktury bazy danych:'; -$locale['step_database_success_schema'] = 'Pomyślnie zainstalowano tabele $PREFIX$.'; +$locale['step_database_success_schema'] = 'Pomyślnie zaimportowano tabele $PREFIX$.'; +$locale['step_database_success_import_data'] = 'Pomyślnie załadowano bazowe dane dla tabel.'; $locale['step_database_error_file'] = '$FILE$ nie mógł zostać otwarty. Proszę skopiować zawartość pola tekstowego i wkleić do tego pliku:'; $locale['step_database_adding_field'] = 'Dodawanie pola'; $locale['step_database_modifying_field'] = 'Modyfikacja pola'; diff --git a/system/login.php b/system/login.php index 42a96111..335177e7 100644 --- a/system/login.php +++ b/system/login.php @@ -34,8 +34,10 @@ if($logged) { $twig->addGlobal('account_logged', $account_logged); } -setSession('last_visit', time()); -if(defined('PAGE')) { - setSession('last_page', PAGE); +if (!defined('IGNORE_SET_LAST_VISIT') || !IGNORE_SET_LAST_VISIT) { + setSession('last_visit', time()); + if(defined('PAGE')) { + setSession('last_page', PAGE); + } + setSession('last_uri', $_SERVER['REQUEST_URI']); } -setSession('last_uri', $_SERVER['REQUEST_URI']); diff --git a/system/migrate.php b/system/migrate.php index 2199fd73..2bbb3786 100644 --- a/system/migrate.php +++ b/system/migrate.php @@ -9,6 +9,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +global $db; + // database migrations $tmp = ''; if(fetchDatabaseConfig('database_version', $tmp)) { // we got version diff --git a/system/migrations/46-account_emails_verify.sql b/system/migrations/46-account_emails_verify.sql new file mode 100644 index 00000000..13d42b16 --- /dev/null +++ b/system/migrations/46-account_emails_verify.sql @@ -0,0 +1,8 @@ +CREATE TABLE `myaac_account_emails_verify` +( + `id` int NOT NULL AUTO_INCREMENT, + `account_id` int NOT NULL, + `hash` varchar(32) NOT NULL, + `sent_at` int NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; diff --git a/system/migrations/46.php b/system/migrations/46.php new file mode 100644 index 00000000..452527e5 --- /dev/null +++ b/system/migrations/46.php @@ -0,0 +1,24 @@ +hasColumn('accounts', 'email_hash')) { + $db->dropColumn('accounts', 'email_hash'); + } + + if (!$db->hasTable(TABLE_PREFIX . 'account_emails_verify')) { + $db->query(file_get_contents(__DIR__ . '/46-account_emails_verify.sql')); + } +}; + +$down = function () use ($db) { + if (!$db->hasColumn('accounts', 'email_hash')) { + $db->addColumn('accounts', 'email_hash', "varchar(32) NOT NULL DEFAULT ''"); + } + + if ($db->hasTable(TABLE_PREFIX . 'account_emails_verify')) { + $db->dropTable(TABLE_PREFIX . 'account_emails_verify'); + } +}; diff --git a/system/migrations/7.php b/system/migrations/7.php index a96ed7c9..5a6a7925 100644 --- a/system/migrations/7.php +++ b/system/migrations/7.php @@ -9,7 +9,7 @@ $up = function () use ($db) { } }; -$up = function () use ($db) { +$down = function () use ($db) { if (!$db->hasColumn(TABLE_PREFIX . 'screenshots', 'name')) { $db->addColumn(TABLE_PREFIX . 'screenshots', 'name', 'VARCHAR(30) NOT NULL'); } diff --git a/system/pages/404.php b/system/pages/404.php index 90a8921a..60fb66aa 100644 --- a/system/pages/404.php +++ b/system/pages/404.php @@ -8,7 +8,7 @@ * @link https://my-aac.org */ defined('MYAAC') or die('Direct access not allowed!'); -$title = '404 Not Found'; +$title = 'Not Found'; header('HTTP/1.0 404 Not Found'); ?> diff --git a/system/pages/405.php b/system/pages/405.php index 3d585f59..d1eee43a 100644 --- a/system/pages/405.php +++ b/system/pages/405.php @@ -8,7 +8,7 @@ * @link https://my-aac.org */ defined('MYAAC') or die('Direct access not allowed!'); -$title = '405 Method Not Allowed'; +$title = 'Method Not Allowed'; header('HTTP/1.0 405 Method Not Allowed'); ?> diff --git a/system/pages/account/change-email.php b/system/pages/account/change-email.php index 857623f9..6dc8d388 100644 --- a/system/pages/account/change-email.php +++ b/system/pages/account/change-email.php @@ -166,7 +166,7 @@ if(isset($_POST['emailchangecancel']) && $_POST['emailchangecancel'] == 1) { $account_logged->setCustomField("email_new", ""); $account_logged->setCustomField("email_new_time", 0); - $custom_buttons = '
' . $twig->render('buttons.back.html.twig') . '
'; + $custom_buttons = '
' . csrf(true) . '
' . $twig->render('buttons.back.html.twig') . '
'; $twig->display('success.html.twig', array( 'title' => 'Email Address Change Cancelled', diff --git a/system/pages/account/change-password.php b/system/pages/account/change-password.php index 35058d3d..157515b7 100644 --- a/system/pages/account/change-password.php +++ b/system/pages/account/change-password.php @@ -19,18 +19,17 @@ if(!$logged) { csrfProtect(); -$new_password = $_POST['newpassword'] ?? NULL; -$new_password_confirm = $_POST['newpassword_confirm'] ?? NULL; -$old_password = $_POST['oldpassword'] ?? NULL; +$new_password = $_POST['new_password'] ?? null; +$new_password_confirm = $_POST['new_password_confirm'] ?? null; +$old_password = $_POST['old_password'] ?? null; if(empty($new_password) && empty($new_password_confirm) && empty($old_password)) { $twig->display('account.change-password.html.twig'); } -else -{ +else { if(empty($new_password) || empty($new_password_confirm) || empty($old_password)){ $errors[] = 'Please fill in form.'; } - $password_strlen = strlen($new_password); + if($new_password != $new_password_confirm) { $errors[] = 'The new passwords do not match!'; } @@ -41,10 +40,13 @@ else } /** @var OTS_Account $account_logged */ - $old_password = encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $old_password); - if($old_password != $account_logged->getPassword()) { + $old_password_hashed = encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $old_password); + if($old_password_hashed != $account_logged->getPassword()) { $errors[] = 'Current password is incorrect!'; } + else if ($old_password == $new_password) { + $errors[] = 'The old password is same as the new password!'; + } $hooks->trigger(HOOK_ACCOUNT_CHANGE_PASSWORD_POST); } diff --git a/system/pages/account/confirm-email.php b/system/pages/account/confirm-email.php index 615dd942..87183a3c 100644 --- a/system/pages/account/confirm-email.php +++ b/system/pages/account/confirm-email.php @@ -9,6 +9,7 @@ */ use MyAAC\Models\Account; +use MyAAC\Models\AccountEmailVerify; defined('MYAAC') or die('Direct access not allowed!'); @@ -20,16 +21,20 @@ if(empty($hash)) { return; } -if(!Account::where('email_hash', $hash)->exists()) { - note("Your email couldn't be verified. Please contact staff to do it manually."); +// by default link is valid for 30 days +$accountEmailVerify = AccountEmailVerify::where('hash', $hash)->where('sent_at', '>', time() - 30 * 24 * 60 * 60)->first(); +if(!$accountEmailVerify) { + note("Wrong link or link has expired."); } else { - $accountModel = Account::where('email_hash', $hash)->where('email_verified', 0)->first(); + $accountModel = Account::where('id', $accountEmailVerify->account_id)->where('email_verified', 0)->first(); if ($accountModel) { $accountModel->email_verified = 1; $accountModel->save(); + AccountEmailVerify::where('account_id', $accountModel->id)->delete(); + success('You have now verified your e-mail, this will increase the security of your account. Thank you for doing this. You can now log in.'); $account = new OTS_Account(); @@ -39,6 +44,6 @@ else } } else { - error('Link has expired.'); + error('Your account is already verified.'); } } diff --git a/system/pages/account/create.php b/system/pages/account/create.php index 0befa362..731771b7 100644 --- a/system/pages/account/create.php +++ b/system/pages/account/create.php @@ -10,6 +10,7 @@ */ use MyAAC\CreateCharacter; +use MyAAC\Models\AccountEmailVerify; defined('MYAAC') or die('Direct access not allowed!'); $title = 'Create Account'; @@ -221,8 +222,19 @@ if($save) } } - if(setting('core.account_premium_points') && setting('core.account_premium_points') > 0) { - $new_account->setCustomField('premium_points', setting('core.account_premium_points')); + $accountDefaultPremiumPoints = setting('core.account_premium_points'); + if($accountDefaultPremiumPoints > 0) { + $new_account->setCustomField('premium_points', $accountDefaultPremiumPoints); + } + + $accountDefaultCoins = setting('core.account_coins'); + if(HAS_ACCOUNT_COINS && $accountDefaultCoins > 0) { + $new_account->setCustomField('coins', $accountDefaultCoins); + } + + $accountDefaultCoinsTransferable = setting('core.account_coins_transferable'); + if((HAS_ACCOUNT_COINS_TRANSFERABLE || HAS_ACCOUNT_TRANSFERABLE_COINS) && $accountDefaultCoinsTransferable > 0) { + $new_account->setCustomField(ACCOUNT_COINS_TRANSFERABLE_COLUMN, $accountDefaultCoinsTransferable); } $tmp_account = $email; @@ -233,7 +245,12 @@ if($save) if(setting('core.mail_enabled') && setting('core.account_mail_verify')) { $hash = md5(generateRandomString(16, true, true) . $email); - $new_account->setCustomField('email_hash', $hash); + + AccountEmailVerify::create([ + 'account_id' => $new_account->getId(), + 'hash' => $hash, + 'sent_at' => time(), + ]); $verify_url = getLink('account/confirm-email/' . $hash); $body_html = $twig->render('mail.account.verify.html.twig', array( @@ -257,8 +274,10 @@ if($save) } else { - error('An error occorred while sending email! Account not created. Try again. For Admin: More info can be found in system/logs/mailer-error.log'); + error('An error occurred while sending email! Account not created. Try again. For Admin: More info can be found in system/logs/mailer-error.log'); $new_account->delete(); + + return; } } else @@ -348,7 +367,7 @@ if(!empty($errors)) if (setting('core.account_country')) { $countries = array(); - foreach (array('pl', 'se', 'br', 'us', 'gb') as $c) + foreach (setting('core.account_countries_most_popular') ?? [] as $c) $countries[$c] = $config['countries'][$c]; $countries['--'] = '----------'; diff --git a/system/pages/account/login.php b/system/pages/account/login.php index d6771c91..e18dacf4 100644 --- a/system/pages/account/login.php +++ b/system/pages/account/login.php @@ -48,7 +48,9 @@ if(!empty($login_account) && !empty($login_password)) ) { if (setting('core.account_mail_verify') && (int)$account_logged->getCustomField('email_verified') !== 1) { - $errors[] = 'Your account is not verified. Please verify your email address. If the message is not coming check the SPAM folder in your E-Mail client.'; + $link = getLink('account/resend-email-verify'); + $errors[] = 'Your account is not verified. Please verify your email address. If the message is not coming check the SPAM folder in your E-Mail client.
' . + 'You can resend the Email here: ' . $link . ''; } else { session_regenerate_id(); setSession('account', $account_logged->getId()); diff --git a/system/pages/account/manage.php b/system/pages/account/manage.php index 3776a732..b74f7bd8 100644 --- a/system/pages/account/manage.php +++ b/system/pages/account/manage.php @@ -38,15 +38,24 @@ csrfProtect(); $groups = new OTS_Groups_List(); -$freePremium = isset($config['lua']['freePremium']) && getBoolean($config['lua']['freePremium']) || $account_logged->getPremDays() == OTS_Account::GRATIS_PREMIUM_DAYS; -$dayOrDays = $account_logged->getPremDays() == 1 ? 'day' : 'days'; /** * @var OTS_Account $account_logged */ -if(!$account_logged->isPremium()) +$premDays = $account_logged->getPremDays(); + +$freePremium = isset($config['lua']['freePremium']) && getBoolean($config['lua']['freePremium']) || $premDays == OTS_Account::GRATIS_PREMIUM_DAYS; +$dayOrDays = ($premDays == 1 ? 'day' : 'days'); + +$vipSystemEnabled = isset($config['lua']['vipSystemEnabled']) && getBoolean($config['lua']['vipSystemEnabled']); +$premiumLabel = $vipSystemEnabled ? 'VIP' : 'Premium Account'; + +if ($freePremium && !$vipSystemEnabled) { + $account_status = 'Gratis Premium Account'; +} else if(!$account_logged->isPremium()) { $account_status = 'Free Account'; -else - $account_status = '' . ($freePremium ? 'Gratis Premium Account' : 'Premium Account, ' . $account_logged->getPremDays() . ' '.$dayOrDays.' left') . ''; +} else { + $account_status = '' . $premiumLabel . ', ' . $premDays . ' '.$dayOrDays.' left'; +} $recovery_key = $account_logged->getCustomField('key'); if(empty($recovery_key)) diff --git a/system/pages/account/redirect.php b/system/pages/account/redirect.php deleted file mode 100644 index 78bc54d9..00000000 --- a/system/pages/account/redirect.php +++ /dev/null @@ -1,23 +0,0 @@ - - * @author Slawkens - * @copyright 2019 MyAAC - * @link https://my-aac.org - */ -defined('MYAAC') or die('Direct access not allowed!'); - -$redirect = urldecode($_REQUEST['redirect']); - -// should never happen, unless hacker modify the URL -if (!str_contains($redirect, BASE_URL)) { - error('Fatal error: Cannot redirect outside the website.'); - return; -} - -$twig->display('account.redirect.html.twig', array( - 'redirect' => $redirect -)); diff --git a/system/pages/account/resend-email-verify.php b/system/pages/account/resend-email-verify.php new file mode 100644 index 00000000..ffccb548 --- /dev/null +++ b/system/pages/account/resend-email-verify.php @@ -0,0 +1,94 @@ +display('error_box.html.twig', ['errors' => $errors]); + $twig->display('account.back_button.html.twig', [ + 'action' => getLink('account/resend-email-verify'), + ]); +}; + +if (!setting('core.mail_enabled') || !setting('core.account_mail_verify')) { + $errorWithBackButton('Resending email is not possible on this server.'); + return; +} + +$showForm = true; + +if (isset($_POST['submit']) && $_POST['submit'] == '1') { + $email = $_REQUEST['email']; + + if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + $errorWithBackButton('Please enter valid Email.'); + return; + } + + $account = new OTS_Account(); + $account->findByEMail($email); + if ($account->isLoaded()) { + if ($account->getCustomField('email_verified') == '1') { + $errorWithBackButton('This account is already verified! You can log in on the website.'); + return; + } + + $accountEmailVerify = AccountEmailVerify::where('account_id', $account->getId())->orderBy('sent_at', 'DESC')->first(); + if ($accountEmailVerify && time() - $accountEmailVerify->sent_at < 60) { + $errorWithBackButton('Only one Email per minute is allowed. Please try again later.'); + return; + } + + $tmp_account = $email; + if (!config('account_login_by_email')) { + $tmp_account = (USE_ACCOUNT_NAME ? $account->getName() : $account->getId()); + } + + $hash = md5(generateRandomString(16, true, true) . $email); + + AccountEmailVerify::create([ + 'account_id' => $account->getId(), + 'hash' => $hash, + 'sent_at' => time(), + ]); + + $verify_url = getLink('account/confirm-email/' . $hash); + $body_html = $twig->render('mail.account.resend-email-verify.html.twig', array( + 'account' => $tmp_account, + 'verify_url' => generateLink($verify_url, $verify_url, true) + )); + + if (_mail($account->getEMail(), configLua('serverName') . ' - Verify Account', $body_html)) { + $message = "If account with this email exists - you will become an email with verification link."; + $showForm = false; + } else { + $message = "

An error occurred while sending email ({$email} )! Try again later. For Admin: More info can be found in system/logs/mailer-error.log

"; + } + } + else { + $message = "
If account with this email exists - you will become an email with verification link."; + $showForm = false; + } + + $twig->display('success.html.twig', array( + 'title' => 'Verify Email Sent', + 'description' => $message, + )); +} + +//show errors if not empty +if (!empty($errors)) { + $twig->display('error_box.html.twig', ['errors' => $errors]); + $twig->display('account.back_button.html.twig', [ + 'action' => getLink('account/resend-email-verify'), + ]); +} + +if ($showForm) { + $twig->display('account.resend-email-verify.html.twig'); +} diff --git a/system/pages/characters.php b/system/pages/characters.php index 89f98cf0..b88368ff 100644 --- a/system/pages/characters.php +++ b/system/pages/characters.php @@ -202,36 +202,38 @@ if($player->isLoaded() && !$player->isDeleted()) unset($storage); } - if($db->hasTable('player_items') && $db->hasColumn('player_items', 'pid') && $db->hasColumn('player_items', 'sid') && $db->hasColumn('player_items', 'itemtype')) { + if ($db->hasTableAndColumns('player_items', ['pid', 'sid', 'itemtype'])) { $eq_sql = $db->query('SELECT `pid`, `itemtype` FROM player_items WHERE player_id = '.$player->getId().' AND (`pid` >= 1 and `pid` <= 10)'); - $equipment = array(); - foreach($eq_sql as $eq) + $equipment = []; + foreach($eq_sql as $eq) { $equipment[$eq['pid']] = $eq['itemtype']; + } - $empty_slots = array("", "no_helmet", "no_necklace", "no_backpack", "no_armor", "no_handleft", "no_handright", "no_legs", "no_boots", "no_ring", "no_ammo"); - for($i = 0; $i <= 10; $i++) - { + $empty_slots = ["", "no_helmet", "no_necklace", "no_backpack", "no_armor", "no_handleft", "no_handright", "no_legs", "no_boots", "no_ring", "no_ammo"]; + + for($i = 0; $i <= 10; $i++) { if(!isset($equipment[$i]) || $equipment[$i] == 0) $equipment[$i] = $empty_slots[$i]; } - for($i = 1; $i < 11; $i++) - { - if(Validator::number($equipment[$i])) + for($i = 1; $i < 11; $i++) { + if(Validator::number($equipment[$i])) { $equipment[$i] = getItemImage($equipment[$i]); - else + } + else { $equipment[$i] = ' ' . $equipment[$i] . ''; + } } - - $skulls = array( - 1 => 'yellow_skull', - 2 => 'green_skull', - 3 => 'white_skull', - 4 => 'red_skull', - 5 => 'black_skull' - ); } + $skulls = [ + 1 => 'yellow_skull', + 2 => 'green_skull', + 3 => 'white_skull', + 4 => 'red_skull', + 5 => 'black_skull', + ]; + $dead_add_content = ''; $deaths = array(); if($db->hasTable('killers')) { diff --git a/system/pages/guilds/create.php b/system/pages/guilds/create.php index ef0117e8..47ae690b 100644 --- a/system/pages/guilds/create.php +++ b/system/pages/guilds/create.php @@ -21,6 +21,9 @@ if(!$logged) { $errors[] = 'You are not logged in. You can\'t create guild.'; } +$configLuaFreePremium = configLua('freePremium'); +$freePremium = (isset($configLuaFreePremium) && getBoolean($configLuaFreePremium)); + $array_of_player_nig = array(); if(empty($errors)) { @@ -31,7 +34,7 @@ if(empty($errors)) if(!$player_rank->isLoaded()) { if($player->getLevel() >= setting('core.guild_need_level')) { - if(!setting('core.guild_need_premium') || $account_logged->isPremium()) { + if(!setting('core.guild_need_premium') || $account_logged->isPremium() || $freePremium) { $array_of_player_nig[] = $player->getName(); } } @@ -95,7 +98,7 @@ if($todo == 'save') if($player->getLevel() < setting('core.guild_need_level')) { $errors[] = 'Character '.$name.' has too low level. To create guild you need character with level ' . setting('core.guild_need_level') . '.'; } - if(setting('core.guild_need_premium') && !$account_logged->isPremium()) { + if(setting('core.guild_need_premium') && !$account_logged->isPremium() && !$freePremium) { $errors[] = 'Character '.$name.' is on FREE account. To create guild you need PREMIUM account.'; } } diff --git a/system/pages/guilds/invite.php b/system/pages/guilds/invite.php index 9d6c9c06..0984fd0a 100644 --- a/system/pages/guilds/invite.php +++ b/system/pages/guilds/invite.php @@ -23,6 +23,12 @@ if(!Validator::guildName($guild_name)) { $errors[] = Validator::getLastError(); } +if (!$db->hasTableAndColumns('guild_invites', ['player_id'])) { + $errors[] = "Guild invite is not possible on this website."; + $twig->display('error_box.html.twig', ['errors' => $errors]); + return; +} + if(empty($errors)) { $guild = new OTS_Guild(); $guild->find($guild_name); @@ -58,7 +64,7 @@ if(empty($errors)) { } } -if(!$guild_vice) { +if(empty($errors) && !$guild_vice) { $errors[] = 'You are not a leader or vice leader of guild '.$guild_name.'.'.$level_in_guild; } @@ -84,6 +90,7 @@ if(isset($_POST['todo']) && $_POST['todo'] == 'save') { } } } + if(empty($errors)) { include(SYSTEM . 'libs/pot/InvitesDriver.php'); new InvitesDriver($guild); @@ -104,6 +111,7 @@ if(!empty($errors)) { else { if(isset($_POST['todo']) && $_POST['todo'] == 'save') { $guild->invite($player); + $twig->display('success.html.twig', array( 'title' => 'Invite player', 'description' => 'Player with name ' . $player->getName() . ' has been invited to your guild.', diff --git a/system/pages/guilds/list.php b/system/pages/guilds/list.php index 26d77d3a..94c49854 100644 --- a/system/pages/guilds/list.php +++ b/system/pages/guilds/list.php @@ -36,10 +36,9 @@ if(count($guilds_list) > 0) { $guildName = $guild->getName(); $guilds[] = array('name' => $guildName, 'logo' => $guild_logo, 'link' => getGuildLink($guildName, false), 'description' => $description); } -}; +} $twig->display('guilds.list.html.twig', array( 'guilds' => $guilds, - 'logged' => $logged ?? false, 'isAdmin' => admin(), )); diff --git a/system/pages/guilds/show.php b/system/pages/guilds/show.php index 6fd07a91..527f29d7 100644 --- a/system/pages/guilds/show.php +++ b/system/pages/guilds/show.php @@ -91,13 +91,18 @@ $guild_owner = $guild->getOwner(); if($guild_owner->isLoaded()) $guild_owner_name = $guild_owner->getName(); +$deletedColumn = 'deleted'; +if ($db->hasColumn('players', 'deletion')) { + $deletedColumn = 'deletion'; +} + $guild_members = array(); foreach($rank_list as $rank) { if($db->hasTable(GUILD_MEMBERS_TABLE)) - $players_with_rank = $db->query('SELECT `players`.`id` as `id`, `' . GUILD_MEMBERS_TABLE . '`.`rank_id` as `rank_id` FROM `players`, `' . GUILD_MEMBERS_TABLE . '` WHERE `' . GUILD_MEMBERS_TABLE . '`.`rank_id` = ' . $rank->getId() . ' AND `players`.`id` = `' . GUILD_MEMBERS_TABLE . '`.`player_id` ORDER BY `name`;'); + $players_with_rank = $db->query('SELECT `players`.`id` as `id`, `' . GUILD_MEMBERS_TABLE . '`.`rank_id` as `rank_id` FROM `players`, `' . GUILD_MEMBERS_TABLE . '` WHERE `' . GUILD_MEMBERS_TABLE . '`.`rank_id` = ' . $rank->getId() . ' AND `players`.`id` = `' . GUILD_MEMBERS_TABLE . '`.`player_id` AND `' . $deletedColumn . '` = 0 ORDER BY `name`;'); else if($db->hasColumn('players', 'rank_id')) - $players_with_rank = $db->query('SELECT `id`, `rank_id` FROM `players` WHERE `rank_id` = ' . $rank->getId() . ' AND `deleted` = 0;'); + $players_with_rank = $db->query('SELECT `id`, `rank_id` FROM `players` WHERE `rank_id` = ' . $rank->getId() . ' AND `' . $deletedColumn . '` = 0;'); $players_with_rank_number = $players_with_rank->rowCount(); if($players_with_rank_number > 0) @@ -121,25 +126,28 @@ foreach($rank_list as $rank) } } -include(SYSTEM . 'libs/pot/InvitesDriver.php'); -new InvitesDriver($guild); -$invited_list = $guild->listInvites(); +$invited_list = []; $show_accept_invite = 0; -if($logged && count($invited_list) > 0) -{ - foreach($invited_list as $invited_player) - { - if(count($account_players) > 0) - { - foreach($account_players as $player_from_acc) - { - if($player_from_acc->isLoaded() && $invited_player->isLoaded() && $player_from_acc->getName() == $invited_player->getName()) - $show_accept_invite++; + +if ($db->hasTableAndColumns('guild_invites', ['player_id'])) { + include(SYSTEM . 'libs/pot/InvitesDriver.php'); + new InvitesDriver($guild); + $invited_list = $guild->listInvites(); + + if($logged && count($invited_list) > 0) { + foreach($invited_list as $invited_player) { + if(count($account_players) > 0) { + foreach($account_players as $player_from_acc) { + if($player_from_acc->isLoaded() && $invited_player->isLoaded() && $player_from_acc->getName() == $invited_player->getName()) { + $show_accept_invite++; + } + } } } } } + $useGuildNick = $db->hasTable('guild_members') || $db->hasTable('guild_membership') || $db->hasColumn('players', 'guildnick'); $twig->display('guilds.view.html.twig', array( diff --git a/system/pages/highscores.php b/system/pages/highscores.php index 6deb842c..79d4feee 100644 --- a/system/pages/highscores.php +++ b/system/pages/highscores.php @@ -123,16 +123,10 @@ if($db->hasColumn('players', 'promotion')) $promotion = ',players.promotion'; $outfit_addons = false; -$outfit = ''; - -$settingHighscoresOutfit = setting('core.highscores_outfit'); - -if($settingHighscoresOutfit) { - $outfit = ', lookbody, lookfeet, lookhead, looklegs, looktype'; - if($db->hasColumn('players', 'lookaddons')) { - $outfit .= ', lookaddons'; - $outfit_addons = true; - } +$outfit = ', lookbody, lookfeet, lookhead, looklegs, looktype'; +if($db->hasColumn('players', 'lookaddons')) { + $outfit .= ', lookaddons'; + $outfit_addons = true; } $configHighscoresPerPage = setting('core.highscores_per_page'); @@ -146,17 +140,24 @@ $cache = Cache::getInstance(); if ($cache->enabled() && $highscoresTTL > 0) { $tmp = ''; if ($cache->fetch($cacheKey, $tmp)) { - $highscores = unserialize($tmp); + $data = unserialize($tmp); + $totalResults = $data['totalResults']; + $highscores = $data['highscores']; + $updatedAt = $data['updatedAt']; $needReCache = false; } } $offset = ($page - 1) * $configHighscoresPerPage; -$query->join('accounts', 'accounts.id', '=', 'players.account_id') - ->withOnlineStatus() +$query->withOnlineStatus() ->whereNotIn('players.id', setting('core.highscores_ids_hidden')) ->notDeleted() - ->where('players.group_id', '<', setting('core.highscores_groups_hidden')) + ->where('players.group_id', '<', setting('core.highscores_groups_hidden')); + +$totalResultsQuery = clone $query; + +$query + ->join('accounts', 'accounts.id', '=', 'players.account_id') ->limit($limit) ->offset($offset) ->selectRaw('accounts.country, players.id, players.name, players.account_id, players.level, players.vocation' . $outfit . $promotion) @@ -175,7 +176,9 @@ if (empty($highscores)) { POT::SKILL_FISH => 'skill_fishing', ); - $query->addSelect($skill_ids[$skill] . ' as value'); + $query + ->addSelect($skill_ids[$skill] . ' as value') + ->orderByDesc($skill_ids[$skill] . '_tries'); } else { $query ->join('player_skills', 'player_skills.player_id', '=', 'players.id') @@ -197,11 +200,11 @@ if (empty($highscores)) { if ($skill == POT::SKILL__MAGLEVEL) { $query ->addSelect('players.maglevel as value', 'players.maglevel') - ->orderBy('manaspent'); + ->orderByDesc('manaspent'); } else { // level $query ->addSelect('players.level as value', 'players.experience') - ->orderBy('experience'); + ->orderByDesc('experience'); $list = 'experience'; } } @@ -215,17 +218,24 @@ if (empty($highscores)) { return $tmp; })->toArray(); + + $updatedAt = time(); + $totalResults = $totalResultsQuery->count(); } if ($highscoresTTL > 0 && $cache->enabled() && $needReCache) { - $cache->set($cacheKey, serialize($highscores), $highscoresTTL * 60); + $cache->set($cacheKey, serialize( + [ + 'totalResults' => $totalResults, + 'highscores' => $highscores, + 'updatedAt' => $updatedAt, + ] + ), $highscoresTTL * 60); } $show_link_to_next_page = false; $i = 0; -$settingHighscoresVocation = setting('core.highscores_vocation'); - foreach($highscores as $id => &$player) { if(++$i <= $configHighscoresPerPage) @@ -239,10 +249,22 @@ foreach($highscores as $id => &$player) $player['link'] = getPlayerLink($player['name'], false); $player['flag'] = getFlagImage($player['country']); - if($settingHighscoresOutfit) { - $player['outfit'] = ''; + $player['outfit'] = ''; + + if ($skill != POT::SKILL__LEVEL) { + if (isset($lastValue) && $lastValue == $player['value']) { + $player['rank'] = $lastRank; + } + else { + $player['rank'] = $offset + $i; + } + + $lastRank = $player['rank'] ; + $lastValue = $player['value']; + } + else { + $player['rank'] = $offset + $i; } - $player['rank'] = $offset + $i; } else { unset($highscores[$id]); @@ -263,6 +285,8 @@ if($show_link_to_next_page) { $linkNextPage = getLink('highscores') . '/' . $list . ($vocation !== 'all' ? '/' . $vocation : '') . '/' . ($page + 1); } +$baseLink = getLink('highscores') . '/' . $list . ($vocation !== 'all' ? '/' . $vocation : '') . '/'; + $types = array( 'experience' => 'Experience', 'magic' => 'Magic', @@ -297,4 +321,8 @@ $twig->display('highscores.html.twig', [ 'types' => $types, 'linkPreviousPage' => $linkPreviousPage, 'linkNextPage' => $linkNextPage, + 'totalResults' => $totalResults, + 'page' => $page, + 'baseLink' => $baseLink, + 'updatedAt' => $updatedAt, ]); diff --git a/system/pages/news.php b/system/pages/news.php index dc8c700e..eb7ccd2f 100644 --- a/system/pages/news.php +++ b/system/pages/news.php @@ -122,7 +122,7 @@ if(!$news_cached) ); } - $tickers_db = $db->query('SELECT * FROM `' . TABLE_PREFIX . 'news` WHERE `type` = ' . TICKER .($canEdit ? '' : ' AND `hide` != 1') .' ORDER BY `date` DESC LIMIT ' . setting('core.news_ticker_limit')); + $tickers_db = $db->query('SELECT * FROM `' . TABLE_PREFIX . 'news` WHERE `type` = ' . TICKER . ' AND `hide` != 1 ORDER BY `date` DESC LIMIT ' . setting('core.news_ticker_limit')); $tickers_content = ''; if($tickers_db->rowCount() > 0) { @@ -142,7 +142,8 @@ if(!$news_cached) if($cache->enabled() && !$canEdit) $cache->set('news_' . $template_name . '_' . TICKER, $tickers_content, 60 * 60); - $featured_article_db =$db->query('SELECT `id`, `title`, `article_text`, `article_image`, `hide` FROM `' . TABLE_PREFIX . 'news` WHERE `type` = ' . ARTICLE . ($canEdit ? '' : ' AND `hide` != 1') .' ORDER BY `date` DESC LIMIT 1'); + $featured_article_db =$db->query('SELECT `id`, `title`, `article_text`, `article_image`, `hide` FROM `' . TABLE_PREFIX . 'news` WHERE `type` = ' . ARTICLE . ' AND `hide` != 1 ORDER BY `date` DESC LIMIT 1'); + $article = ''; if($featured_article_db->rowCount() > 0) { $article = $featured_article_db->fetch(); @@ -175,7 +176,7 @@ else { if(!$news_cached) { ob_start(); - $newses = $db->query('SELECT * FROM ' . $db->tableName(TABLE_PREFIX . 'news') . ' WHERE type = ' . NEWS . ($canEdit ? '' : ' AND hide != 1') . ' ORDER BY date' . ' DESC LIMIT ' . setting('core.news_limit')); + $newses = $db->query('SELECT * FROM ' . $db->tableName(TABLE_PREFIX . 'news') . ' WHERE type = ' . NEWS . ' AND hide != 1 ORDER BY date' . ' DESC LIMIT ' . setting('core.news_limit')); if($newses->rowCount() > 0) { foreach($newses as $news) diff --git a/system/pages/online.php b/system/pages/online.php index 4f0baa7d..21f2ee0d 100644 --- a/system/pages/online.php +++ b/system/pages/online.php @@ -9,123 +9,140 @@ * @link https://my-aac.org */ +use MyAAC\Cache\Cache; use MyAAC\Models\ServerConfig; use MyAAC\Models\ServerRecord; defined('MYAAC') or die('Direct access not allowed!'); $title = 'Who is online?'; -if (setting('core.account_country')) +if (setting('core.account_country')) { require SYSTEM . 'countries.conf.php'; +} $promotion = ''; -if($db->hasColumn('players', 'promotion')) +if($db->hasColumn('players', 'promotion')) { $promotion = '`promotion`,'; - -$order = $_GET['order'] ?? 'name'; -if(!in_array($order, array('country', 'name', 'level', 'vocation'))) - $order = $db->fieldName('name'); -else if($order == 'country') - $order = $db->tableName('accounts') . '.' . $db->fieldName('country'); -else if($order == 'vocation') - $order = $promotion . 'vocation ASC'; - -$skull_type = 'skull'; -if($db->hasColumn('players', 'skull_type')) { - $skull_type = 'skull_type'; } -$skull_time = 'skulltime'; -if($db->hasColumn('players', 'skull_time')) { - $skull_time = 'skull_time'; +$order = $_GET['order'] ?? 'name_asc'; +if(!in_array($order, ['country_asc', 'country_desc', 'name_asc', 'name_desc', 'level_asc', 'level_desc', 'vocation_asc', 'vocation_desc'])) { + $order = 'name_asc'; +} +else if($order == 'vocation_asc' || $order == 'vocation_desc') { + $order = $promotion . 'vocation_' . (str_contains($order, 'asc') ? 'asc' : 'desc'); } -$outfit_addons = false; -$outfit = ''; -if (setting('core.online_outfit')) { +$cached = Cache::remember("online_$order", setting('core.online_cache_ttl') * 60, function() use($db, $promotion, $order) { + $orderExplode = explode('_', $order); + $orderSql = $orderExplode[0] . ' ' . $orderExplode[1]; + + $skull_type = 'skull'; + if($db->hasColumn('players', 'skull_type')) { + $skull_type = 'skull_type'; + } + + $skull_time = 'skulltime'; + if($db->hasColumn('players', 'skull_time')) { + $skull_time = 'skull_time'; + } + + $outfit_addons = false; $outfit = ', lookbody, lookfeet, lookhead, looklegs, looktype'; if($db->hasColumn('players', 'lookaddons')) { $outfit .= ', lookaddons'; $outfit_addons = true; } -} -$vocs = []; -if (setting('core.online_vocations')) { - foreach($config['vocations'] as $id => $name) { - $vocs[$id] = 0; - } -} + $vocations = array_map(function ($name) { + return 0; + }, setting('core.vocations')); -if($db->hasTable('players_online')) // tfs 1.0 - $playersOnline = $db->query('SELECT `accounts`.`country`, `players`.`name`, `players`.`level`, `players`.`vocation`' . $outfit . ', `' . $skull_time . '` as `skulltime`, `' . $skull_type . '` as `skull` FROM `accounts`, `players`, `players_online` WHERE `players`.`id` = `players_online`.`player_id` AND `accounts`.`id` = `players`.`account_id` ORDER BY ' . $order); -else - $playersOnline = $db->query('SELECT `accounts`.`country`, `players`.`name`, `players`.`level`, `players`.`vocation`' . $outfit . ', ' . $promotion . ' `' . $skull_time . '` as `skulltime`, `' . $skull_type . '` as `skull` FROM `accounts`, `players` WHERE `players`.`online` > 0 AND `accounts`.`id` = `players`.`account_id` ORDER BY ' . $order); + if($db->hasTable('players_online')) // tfs 1.0 + $playersOnline = $db->query('SELECT `accounts`.`country`, `players`.`name`, `players`.`level`, `players`.`vocation`' . $outfit . ', `' . $skull_time . '` as `skulltime`, `' . $skull_type . '` as `skull` FROM `accounts`, `players`, `players_online` WHERE `players`.`id` = `players_online`.`player_id` AND `accounts`.`id` = `players`.`account_id` ORDER BY ' . $orderSql); + else + $playersOnline = $db->query('SELECT `accounts`.`country`, `players`.`name`, `players`.`level`, `players`.`vocation`' . $outfit . ', ' . $promotion . ' `' . $skull_time . '` as `skulltime`, `' . $skull_type . '` as `skull` FROM `accounts`, `players` WHERE `players`.`online` > 0 AND `accounts`.`id` = `players`.`account_id` ORDER BY ' . $orderSql); -$players_data = array(); -$players = 0; -$data = ''; -foreach($playersOnline as $player) { - $skull = ''; - if (setting('core.online_skulls')) - { - if($player['skulltime'] > 0) - { - if($player['skull'] == 3) + $settingVocations = setting('core.vocations'); + $settingVocationsAmount = setting('core.vocations_amount'); + + $players = []; + foreach($playersOnline as $player) { + $skull = ''; + if($player['skulltime'] > 0) { + if($player['skull'] == 3) { $skull = ' '; - elseif($player['skull'] == 4) + } + elseif($player['skull'] == 4) { $skull = ' '; - elseif($player['skull'] == 5) + } + elseif($player['skull'] == 5) { $skull = ' '; - } - } - - if(isset($player['promotion'])) { - if((int)$player['promotion'] > 0) - $player['vocation'] += ($player['promotion'] * $config['vocations_amount']); - } - - $players_data[] = array( - 'name' => getPlayerLink($player['name']), - 'player' => $player, - 'level' => $player['level'], - 'vocation' => $config['vocations'][$player['vocation']], - 'country_image' => setting('core.account_country') ? getFlagImage($player['country']) : null, - 'outfit' => setting('core.online_outfit') ? setting('core.outfit_images_url') . '?id=' . $player['looktype'] . ($outfit_addons ? '&addons=' . $player['lookaddons'] : '') . '&head=' . $player['lookhead'] . '&body=' . $player['lookbody'] . '&legs=' . $player['looklegs'] . '&feet=' . $player['lookfeet'] : null - ); - - if (setting('core.online_vocations')) { - $vocs[($player['vocation'] > $config['vocations_amount'] ? $player['vocation'] - $config['vocations_amount'] : $player['vocation'])]++; - } -} - -$record = ''; -if(count($players_data) > 0) { - if( setting('core.online_record')) { - $result = null; - $timestamp = false; - if($db->hasTable('server_record')) { - $timestamp = true; - $result = ServerRecord::where('world_id', $config['lua']['worldId'])->orderByDesc('record')->first()->toArray(); - } else if($db->hasTable('server_config')) { // tfs 1.0 - $row = ServerConfig::where('config', 'players_record')->first(); - if ($row) { - $result = ['record' => $row->value]; } } - if($result) { - $record = 'The maximum on this game world was ' . $result['record'] . ' players' . ($timestamp ? ' on ' . date("M d Y, H:i:s", $result['timestamp']) . '.' : '.'); + if(isset($player['promotion'])) { + if((int)$player['promotion'] > 0) + $player['vocation'] += ($player['promotion'] * $settingVocationsAmount); + } + + $players[] = array( + 'name' => getPlayerLink($player['name']), + 'player' => $player, + 'level' => $player['level'], + 'vocation' => $settingVocations[$player['vocation']], + 'skull' => $skull, + 'country_image' => getFlagImage($player['country']), + 'outfit' => setting('core.outfit_images_url') . '?id=' . $player['looktype'] . ($outfit_addons ? '&addons=' . $player['lookaddons'] : '') . '&head=' . $player['lookhead'] . '&body=' . $player['lookbody'] . '&legs=' . $player['looklegs'] . '&feet=' . $player['lookfeet'], + ); + + $vocations[($player['vocation'] > $settingVocationsAmount ? $player['vocation'] - $settingVocationsAmount : $player['vocation'])]++; + } + + $record = ''; + if(count($players) > 0) { + if( setting('core.online_record')) { + $result = null; + $timestamp = false; + if($db->hasTable('server_record')) { + $timestamp = $db->hasColumn('server_record', 'timestamp'); + $serverRecordQuery = ServerRecord::query(); + + if ($db->hasColumn('server_record', 'world_id')) { + $serverRecordQuery->where('world_id', configLua('worldId')); + } + + $result = $serverRecordQuery->orderByDesc('record')->first(); + if ($result) { + $result = $result->toArray(); + } + } else if($db->hasTable('server_config')) { // tfs 1.0 + $row = ServerConfig::where('config', 'players_record')->first(); + if ($row) { + $result = ['record' => $row->value]; + } + } + + if($result) { + $record = $result['record'] . ' player' . ($result['record'] > 1 ? 's' : '') . ($timestamp ? ' (on ' . date("M d Y, H:i:s", $result['timestamp']) . ')' : ''); + } } } -} + + return [ + 'players' => $players, + 'record' => $record, + 'vocations' => $vocations, + ]; +}); $twig->display('online.html.twig', array( - 'players' => $players_data, - 'record' => $record, - 'vocs' => $vocs, + 'players' => $cached['players'], + 'record' => $cached['record'], + 'vocations' => $cached['vocations'], + 'vocs' => $cached['vocations'], // deprecated, to be removed + 'order' => $order, )); -//search bar -$twig->display('online.form.html.twig'); -?> +// search bar +$twig->display('characters.form.html.twig'); diff --git a/system/router.php b/system/router.php index b87a843d..f876a9b2 100644 --- a/system/router.php +++ b/system/router.php @@ -88,25 +88,38 @@ if($logged && $account_logged && $account_logged->isLoaded()) { /** * Routes loading */ +$routesFinal = []; $dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r) { - $routesFinal = []; + global $cache, $routesFinal; + foreach(getDatabasePages() as $page) { $routesFinal[] = ['*', $page, '__database__/' . $page, 100]; } + $routes = require SYSTEM . 'routes.php'; Plugins::clearWarnings(); - foreach (Plugins::getRoutes() as $route) { - $routesFinal[] = [$route[0], $route[1], $route[2], $route[3] ?? 1000]; + + foreach (Plugins::getRoutes() as $pluginRoute) { + + $routesFinal[] = [$pluginRoute[0], $pluginRoute[1], $pluginRoute[2], $pluginRoute[3] ?? 1000]; + + // Possibility to override routes with plugins pages, like characters.php + foreach ($routes as &$route) { + if (str_contains($pluginRoute[2], 'pages/' . $route[2])) { + $route[2] = $pluginRoute[2]; + } + } /* echo '
';
-		var_dump($route[1], $route[3], $route[2]);
+		var_dump($pluginRoute[1], $pluginRoute[3], $pluginRoute[2]);
 		echo '/
';
 */
 	}
 
-	$routes = require SYSTEM . 'routes.php';
 	foreach ($routes as $route) {
-		if (!str_contains($route[2], '__redirect__') && !str_contains($route[2], '__database__')) {
+		if (!str_contains($route[2], '__redirect__') && !str_contains($route[2], '__database__')
+			&& !str_contains($route[2], 'plugins/')
+		) {
 			if (!is_file(BASE . 'system/pages/' . $route[2])) {
 				continue;
 			}
@@ -129,14 +142,14 @@ $dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r)
 		return ($a[3] < $b[3]) ? -1 : 1;
 	});
 
+	$aliases = [
+		[':int', ':string', ':alphanum'],
+		[':\d+', ':[A-Za-z0-9-_%+\' ]+', ':[A-Za-z0-9]+'],
+	];
+
 	// remove duplicates
 	// if same route pattern, but different priority
-	$routesFinal = array_filter($routesFinal, function ($a) {
-		$aliases = [
-			[':int', ':string', ':alphanum'],
-			[':\d+', ':[A-Za-z0-9-_%+\' ]+', ':[A-Za-z0-9]+'],
-		];
-
+	$routesFinal = array_filter($routesFinal, function ($a) use ($aliases) {
 		// apply aliases
 		$a[1] = str_replace($aliases[0], $aliases[1], $a[1]);
 
@@ -154,7 +167,7 @@ $dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r)
 	echo '
'; die; */ - foreach ($routesFinal as $route) { + foreach ($routesFinal as &$route) { if ($route[0] === '*') { $route[0] = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD']; } @@ -171,15 +184,15 @@ $dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r) $route[0] = array_map($toUpperCase, $route[0]); } - $aliases = [ - [':int', ':string', ':alphanum'], - [':\d+', ':[A-Za-z0-9-_%+\' ]+', ':[A-Za-z0-9]+'], - ]; - // apply aliases $route[1] = str_replace($aliases[0], $aliases[1], $route[1]); - $r->addRoute($route[0], $route[1], $route[2]); + try { + $r->addRoute($route[0], $route[1], $route[2]); + } + catch (\Exception $e) { + // duplicated route, just ignore + } } if (config('env') === 'dev') { @@ -187,6 +200,10 @@ $dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r) log_append('router.log', $warning); } } + + if ($cache->enabled()) { + $cache->set('routes_final', serialize($routesFinal), 10 * 365 * 24 * 60 * 60); // 10 years / infinite + } }, [ 'cacheFile' => CACHE . 'route.cache', @@ -201,7 +218,7 @@ $found = true; // old support for pages like /?subtopic=accountmanagement $page = $_REQUEST['p'] ?? ($_REQUEST['subtopic'] ?? ''); -if(!empty($page) && preg_match('/^[A-z0-9\-]+$/', $page)) { +if(!empty($page) && preg_match('/^[A-z0-9\/\-]+$/', $page)) { if (isset($_REQUEST['p'])) { // some plugins may require this $_REQUEST['subtopic'] = $_REQUEST['p']; } @@ -210,9 +227,26 @@ if(!empty($page) && preg_match('/^[A-z0-9\-]+$/', $page)) { require SYSTEM . 'compat/pages.php'; } - $file = loadPageFromFileSystem($page, $found); - if(!$found) { - $file = false; + $foundRoute = false; + + $tmp = null; + if ($cache->enabled() && $cache->fetch('routes_final', $tmp)) { + $routesFinal = unserialize($tmp); + } + + foreach ($routesFinal as $route) { + if ($page === $route[1]) { + $file = $route[2]; + $foundRoute = true; + break; + } + } + + if (!$foundRoute) { + $file = loadPageFromFileSystem($page, $found); + if(!$found) { + $file = false; + } } } else { @@ -252,7 +286,7 @@ else { $success = false; $tmp_content = getCustomPage($pageName, $success); - if ($success) { + if ($success && $hooks->trigger(HOOK_BEFORE_PAGE_CUSTOM)) { $content .= $tmp_content; if (hasFlag(FLAG_CONTENT_PAGES) || superAdmin()) { $pageInfo = getCustomPageInfo($pageName); @@ -260,6 +294,8 @@ else { ) . $content; } + $hooks->trigger(HOOK_AFTER_PAGE_CUSTOM); + $page = $pageName; $file = false; } @@ -324,7 +360,9 @@ if (isset($_REQUEST['_page_only'])) { if(!isset($title)) { $title = str_replace('index.php/', '', $page); - $title = ucfirst($title); + $title = str_replace(['_', '-', '/'], ' ', $page); + + $title = ucwords($title); } if(setting('core.backward_support')) { diff --git a/system/routes.php b/system/routes.php index 13cee37f..b4af29f0 100644 --- a/system/routes.php +++ b/system/routes.php @@ -22,11 +22,11 @@ return [ ['GET', 'account/confirm-email/{hash:alphanum}', 'account/confirm-email.php'], ['GET', 'bans/{page:int}', 'bans.php'], - [['GET', 'POST'], 'characters[/{name:string}]', 'characters.php'], - ['GET', 'changelog[/{page:int}]', 'changelog.php'], - [['GET', 'POST'], 'monsters[/{name:string}]', 'monsters.php'], + [['GET', 'POST'], 'characters/{name:[A-Za-z0-9-_%+\' \[\]]+}', 'characters.php'], + ['GET', 'changelog/{page:int}', 'changelog.php'], + [['GET', 'POST'], 'monsters/{name:string}', 'monsters.php'], - [['GET', 'POST'], 'faq[/{action:string}]', 'faq.php'], + [['GET', 'POST'], 'faq/{action:string}', 'faq.php'], [['GET', 'POST'], 'forum/{action:string}', 'forum.php'], ['GET', 'forum/board/{id:int}', 'forum/show_board.php'], diff --git a/system/settings.php b/system/settings.php index d0a705a0..96f24a8c 100644 --- a/system/settings.php +++ b/system/settings.php @@ -19,6 +19,24 @@ $templates = Cache::remember('templates', 5 * 60, function () { }); $defaultTemplate = in_array('kathrine', $templates) ? 'kathrine' : $templates[0]; +global $db; + +if (!IS_CLI) { + require SYSTEM . 'base.php'; + + $serverUrl = 'http' . (isHttps() ? 's' : '') . '://' . $baseHost; + $siteURL = $serverUrl . $baseDir; +} + +$donateColumnOptions = [ + 'premium_points' => 'Premium Points', + 'coins' => 'Coins', +]; + +if (defined('HAS_ACCOUNT_COINS_TRANSFERABLE') && (HAS_ACCOUNT_COINS_TRANSFERABLE || HAS_ACCOUNT_TRANSFERABLE_COINS)) { + $donateColumnOptions[ACCOUNT_COINS_TRANSFERABLE_COLUMN] = 'Coins Transferable'; +} + return [ 'name' => 'MyAAC', 'settings' => [ @@ -30,6 +48,13 @@ return [ 'type' => 'section', 'title' => 'General' ], + 'site_url' => [ + 'name' => 'Website URL', + 'type' => 'text', + 'desc' => 'Website address of this MyAAC instance', + 'default' => IS_CLI ? '' : $siteURL, + 'is_config' => true, + ], 'env' => [ 'name' => 'App Environment', 'type' => 'options', @@ -674,6 +699,20 @@ Sent by MyAAC,
'desc' => 'Default premium points on new account', 'default' => 0, ], + 'account_coins' => [ + 'name' => 'Default Account Coins', + 'type' => 'number', + 'desc' => 'Default coins on new account', + 'hidden' => ($db && !HAS_ACCOUNT_COINS), + 'default' => 0, + ], + 'account_coins_transferable' => [ + 'name' => 'Default Account Transferable Coins', + 'type' => 'number', + 'desc' => 'Default transferable coins on new account', + 'hidden' => ($db && !HAS_ACCOUNT_COINS_TRANSFERABLE && !HAS_ACCOUNT_TRANSFERABLE_COINS), + 'default' => 0, + ], 'account_mail_change' => [ 'name' => 'Account Mail Change Days', 'type' => 'number', @@ -698,6 +737,18 @@ Sent by MyAAC,
'desc' => 'should country of user be automatically recognized by his IP? This makes an external API call to http://ipinfo.io', 'default' => true, ], + 'account_countries_most_popular' => [ + 'name' => 'Account Countries Most Popular', + 'type' => 'text', + 'desc' => 'Those countries will be display at the top of the list on the create account page. The short codes of countries can be found in file system/countries.conf.php', + 'default' => 'pl,se,br,us,gb', + 'callbacks' => [ + 'get' => function ($value) { + $tmp = array_map('trim', explode(',', $value)); + return array_filter($tmp, function ($v) {return !empty($v); }); + }, + ], + ], 'characters_per_account' => [ 'name' => 'Characters per Account', 'type' => 'number', @@ -1039,6 +1090,12 @@ Sent by MyAAC,
'desc' => 'How often to update highscores from database in minutes. Too low may slow down your website.
0 to disable.', 'default' => 15, ], + 'highscores_skills_box' => [ + 'name' => 'Display Skills Box', + 'type' => 'boolean', + 'desc' => 'show "Choose a skill" box on the highscores (allowing peoples to sort highscores by skill)?', + 'default' => true, + ], 'highscores_vocation_box' => [ 'name' => 'Display Vocation Box', 'type' => 'boolean', @@ -1051,6 +1108,12 @@ Sent by MyAAC,
'desc' => 'Show player vocation under his nickname?', 'default' => true, ], + 'highscores_online_status' => [ + 'name' => 'Display Online Status', + 'type' => 'boolean', + 'desc' => 'Show player status as red (offline) or green (online)', + 'default' => false, + ], 'highscores_frags' => [ 'name' => 'Display Top Frags', 'type' => 'boolean', @@ -1205,6 +1268,14 @@ Sent by MyAAC,
'type' => 'section', 'title' => 'Online Page' ], + 'online_cache_ttl' => [ + 'name' => 'Online Cache TTL (in minutes)', + 'type' => 'number', + 'min' => 0, + 'desc' => 'How often to update online list from database in minutes. Too low may slow down your website.' . PHP_EOL . + '0 to disable.', + 'default' => 15, + ], 'online_record' => [ 'name' => 'Display Players Record', 'type' => 'boolean', @@ -1241,6 +1312,12 @@ Sent by MyAAC,
'desc' => '', 'default' => false, ], + 'online_datacenter' => [ + 'name' => 'Data Center', + 'type' => 'text', + 'desc' => 'Server Location, will be shown on online page', + 'default' => 'Poland - Warsaw', + ], [ 'type' => 'section', 'title' => 'Team Page' @@ -1542,13 +1619,14 @@ Sent by MyAAC,
'name' => 'Donate Column', 'type' => 'options', 'desc' => 'What to give to player after donation - what column in accounts table to use.', - 'options' => ['premium_points' => 'Premium Points', 'coins' => 'Coins'], + 'options' => $donateColumnOptions, 'default' => 'premium_points', 'callbacks' => [ 'beforeSave' => function($key, $value, &$errorMessage) { global $db; - if ($value == 'coins' && !$db->hasColumn('accounts', 'coins')) { - $errorMessage = "Shop: Donate Column: Cannot set column to coins, because it doesn't exist in database."; + + if (!$db->hasColumn('accounts', $value)) { + $errorMessage = "Shop: Donate Column: Cannot set column to $value, because it doesn't exist in database."; return false; } return true; diff --git a/system/src/Admin/Plugins.php b/system/src/Admin/Plugins.php new file mode 100644 index 00000000..f7b781f2 --- /dev/null +++ b/system/src/Admin/Plugins.php @@ -0,0 +1,49 @@ + $this->api_base_uri, + // You can set any number of default request options. + 'timeout' => 3.0, + ]); + + $plugins = get_plugins(true); + foreach ($plugins as &$plugin) { + if (str_contains($plugin, 'disabled.')) { + $plugin = str_replace('disabled.', '', $plugin); + } + } + + try { + $response = $client->get('get-latest-versions', [ + 'json' => ['plugins' => $plugins], + ]); + } + catch (\Exception $e) { + error('API Error. Please try again later.'); + return []; + } + + $statusCode = $response->getStatusCode(); + if ($statusCode != 200) { + throw new \Exception('Error getting info from plugins repository. Please try again later.'); + } + + $data = $response->getBody(); + return json_decode($data, true); + } + + public function setApiBaseUri(string $uri): void { + $this->api_base_uri = $uri; + } +} diff --git a/system/src/Cache/Cache.php b/system/src/Cache/Cache.php index 0a369202..1b7d284b 100644 --- a/system/src/Cache/Cache.php +++ b/system/src/Cache/Cache.php @@ -106,7 +106,7 @@ class Cache public static function remember($key, $ttl, $callback) { $cache = self::getInstance(); - if (!$cache->enabled()) { + if (!$cache->enabled() || $ttl == 0) { return $callback(); } @@ -115,6 +115,11 @@ class Cache return unserialize($value); } + // -1 for infinite cache + if ($ttl == -1) { + $ttl = 10 * 365 * 24 * 60 * 60; // 10 years should be enough + } + $value = $callback(); $cache->set($key, serialize($value), $ttl); return $value; diff --git a/system/src/Cache/PHP.php b/system/src/Cache/PHP.php index e42bd928..0b13db07 100644 --- a/system/src/Cache/PHP.php +++ b/system/src/Cache/PHP.php @@ -27,6 +27,9 @@ class PHP { $var = var_export($var, true); + ensureFolderExists($this->dir); + ensureIndexExists($this->dir); + // Write to temp file first to ensure atomicity $tmp = $this->dir . "tmp_$key." . uniqid('', true) . '.tmp'; file_put_contents($tmp, 'load(); - $hooks->trigger(HOOK_INIT); + require SYSTEM . 'init.php'; $io = new SymfonyStyle($input, $output); @@ -29,6 +27,13 @@ class CacheClearCommand extends Command return Command::FAILURE; } + $cacheEngine = config('cache_engine') == 'auto' ? + Cache::detect() : config('cache_engine'); + + if (config('env') !== 'dev' && $cacheEngine == 'apcu') { + $io->warning('APCu cache cannot be cleared in CLI. Please visit the Admin Panel and clear there.'); + } + $io->success('Cache cleared'); return Command::SUCCESS; } diff --git a/system/src/Commands/Env.php b/system/src/Commands/Env.php new file mode 100644 index 00000000..e3130806 --- /dev/null +++ b/system/src/Commands/Env.php @@ -0,0 +1,33 @@ +setName('mail:send') + $this->setName('email:send') + ->setAliases(['mail:send']) ->setDescription('This command sends E-Mail to single user. Message can be provided as follows: ' . PHP_EOL - . ' echo "Hello World" | php sa email:send --subject="This is the subject" test@test.com') + . ' echo "Hello World" | php aac email:send --subject="This is the subject" test@test.com') ->addArgument('recipient', InputArgument::REQUIRED, 'Email, Account Name, Account id or Player Name') ->addOption('subject', 's', InputOption::VALUE_REQUIRED, 'Subject'); } diff --git a/system/src/Commands/MigrateCommand.php b/system/src/Commands/MigrateCommand.php index a8a21016..fe110190 100644 --- a/system/src/Commands/MigrateCommand.php +++ b/system/src/Commands/MigrateCommand.php @@ -9,6 +9,8 @@ use Symfony\Component\Console\Style\SymfonyStyle; class MigrateCommand extends Command { + use Env; + protected function configure(): void { $this->setName('migrate') @@ -17,9 +19,19 @@ class MigrateCommand extends Command protected function execute(InputInterface $input, OutputInterface $output): int { - require SYSTEM . 'init.php'; + $this->init(); $io = new SymfonyStyle($input, $output); + + $tmp = ''; + if(fetchDatabaseConfig('database_version', $tmp)) { // we got version + $tmp = (int)$tmp; + if ($tmp >= DATABASE_VERSION) { + $io->success('Already on latest version.'); + return Command::SUCCESS; + } + } + require SYSTEM . 'migrate.php'; $io->success('Migrated to latest version (' . DATABASE_VERSION . ')'); diff --git a/system/src/Commands/MigrateRunCommand.php b/system/src/Commands/MigrateRunCommand.php index 3bdb1452..2ea06938 100644 --- a/system/src/Commands/MigrateRunCommand.php +++ b/system/src/Commands/MigrateRunCommand.php @@ -10,6 +10,8 @@ use Symfony\Component\Console\Style\SymfonyStyle; class MigrateRunCommand extends Command { + use Env; + protected function configure(): void { $this->setName('migrate:run') @@ -23,12 +25,12 @@ class MigrateRunCommand extends Command protected function execute(InputInterface $input, OutputInterface $output): int { - require SYSTEM . 'init.php'; - $io = new SymfonyStyle($input, $output); $ids = $input->getArgument('id'); + $this->init(); + // pre-check // in case one of the migrations doesn't exist - we won't execute any of them foreach ($ids as $id) { @@ -45,6 +47,22 @@ class MigrateRunCommand extends Command $down = $input->getOption('down') ?? false; + /** + * Sort according to $down option. + * Do we really want it? + * Or should we use order provided by user, + * even when it's not sorted correctly? + * Leaving it for consideration. + */ + /* + if ($down) { + rsort($ids); + } + else { + sort($ids); + } + */ + foreach ($ids as $id) { $this->executeMigration($id, $io, !$down); } diff --git a/system/src/Commands/MigrateToCommand.php b/system/src/Commands/MigrateToCommand.php index b82012ed..72be5730 100644 --- a/system/src/Commands/MigrateToCommand.php +++ b/system/src/Commands/MigrateToCommand.php @@ -11,6 +11,8 @@ use Symfony\Component\Console\Style\SymfonyStyle; class MigrateToCommand extends Command { + use Env; + protected function configure(): void { $this->setName('migrate:to') @@ -32,7 +34,7 @@ class MigrateToCommand extends Command return Command::FAILURE; } - $this->initEnv(); + $this->init(); $currentVersion = Config::where('name', 'database_version')->first()->value; if ($currentVersion > $versionDest) { @@ -80,29 +82,4 @@ class MigrateToCommand extends Command updateDatabaseConfig('database_version', ($_up ? $id : $id - 1)); } - - private function initEnv() - { - global $config; - if (!isset($config['installed']) || !$config['installed']) { - throw new \RuntimeException('MyAAC has not been installed yet or there was error during installation. Please install again.'); - } - - if(empty($config['server_path'])) { - throw new \RuntimeException('Server Path has been not set. Go to config.php and set it.'); - } - - // take care of trailing slash at the end - if($config['server_path'][strlen($config['server_path']) - 1] !== '/') - $config['server_path'] .= '/'; - - $config['lua'] = load_config_lua($config['server_path'] . 'config.lua'); - - // POT - require_once SYSTEM . 'libs/pot/OTS.php'; - $ots = POT::getInstance(); - $eloquentConnection = null; - - require_once SYSTEM . 'database.php'; - } } diff --git a/system/src/Commands/PluginDisableCommand.php b/system/src/Commands/PluginDisableCommand.php new file mode 100644 index 00000000..36c3b766 --- /dev/null +++ b/system/src/Commands/PluginDisableCommand.php @@ -0,0 +1,37 @@ +setName('plugin:disable') + ->setAliases(['plugin:deactivate']) + ->setDescription('This command disables plugin') + ->addArgument('plugin-name', InputArgument::REQUIRED, 'Plugin that you want to disable'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + require SYSTEM . 'init.php'; + + $io = new SymfonyStyle($input, $output); + + $pluginName = $input->getArgument('plugin-name'); + + if (!Plugins::disable($pluginName)) { + $io->error('Error while disabling plugin ' . $pluginName . ': ' . Plugins::getError()); + return 2; + } + + $io->success('Successfully disabled plugin ' . $pluginName); + return Command::SUCCESS; + } +} diff --git a/system/src/Commands/PluginEnableCommand.php b/system/src/Commands/PluginEnableCommand.php new file mode 100644 index 00000000..8d5d0be5 --- /dev/null +++ b/system/src/Commands/PluginEnableCommand.php @@ -0,0 +1,37 @@ +setName('plugin:enable') + ->setAliases(['plugin:activate']) + ->setDescription('This command enables plugin') + ->addArgument('plugin-name', InputArgument::REQUIRED, 'Plugin that you want to enable'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + require SYSTEM . 'init.php'; + + $io = new SymfonyStyle($input, $output); + + $pluginName = $input->getArgument('plugin-name'); + + if (!Plugins::enable($pluginName)) { + $io->error('Error while enabling plugin ' . $pluginName . ': ' . Plugins::getError()); + return 2; + } + + $io->success('Successfully enabled plugin ' . $pluginName); + return Command::SUCCESS; + } +} diff --git a/system/src/Commands/PluginInstallInstallCommand.php b/system/src/Commands/PluginSetupCommand.php similarity index 89% rename from system/src/Commands/PluginInstallInstallCommand.php rename to system/src/Commands/PluginSetupCommand.php index fe1b4f14..8923360c 100644 --- a/system/src/Commands/PluginInstallInstallCommand.php +++ b/system/src/Commands/PluginSetupCommand.php @@ -8,11 +8,12 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -class PluginInstallInstallCommand extends Command +class PluginSetupCommand extends Command { protected function configure(): void { - $this->setName('plugin:install:install') + $this->setName('plugin:setup') + ->setAliases(['plugin:install:install']) ->setDescription('This command executes the "install" part of the plugin') ->addArgument('plugin', InputArgument::REQUIRED, 'Plugin name'); } diff --git a/system/src/Commands/PluginUninstallCommand.php b/system/src/Commands/PluginUninstallCommand.php new file mode 100644 index 00000000..fb5982d4 --- /dev/null +++ b/system/src/Commands/PluginUninstallCommand.php @@ -0,0 +1,41 @@ +setName('plugin:uninstall') + ->setAliases(['plugin:remove', 'plugin:delete']) + ->setDescription('This command uninstalls plugin') + ->addArgument('plugin-name', InputArgument::REQUIRED, 'Plugin that you want to uninstall'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + require SYSTEM . 'init.php'; + + $io = new SymfonyStyle($input, $output); + + $pluginName = $input->getArgument('plugin-name'); + + if (!Plugins::uninstall($pluginName)) { + $io->error('Error while uninstalling plugin ' . $pluginName . ': ' . Plugins::getError()); + return 2; + } + + foreach(Plugins::getWarnings() as $warning) { + $io->warning($warning); + } + + $io->success('Successfully uninstalled plugin ' . $pluginName); + return Command::SUCCESS; + } +} diff --git a/system/src/Commands/SettingsResetCommand.php b/system/src/Commands/SettingsResetCommand.php index 78748b00..3e4d793c 100644 --- a/system/src/Commands/SettingsResetCommand.php +++ b/system/src/Commands/SettingsResetCommand.php @@ -3,6 +3,7 @@ namespace MyAAC\Commands; use MyAAC\Models\Settings as SettingsModel; +use MyAAC\Plugins; use MyAAC\Settings; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -34,7 +35,14 @@ class SettingsResetCommand extends Command return Command::FAILURE; } - if (!$name) { + // find by plugin name + foreach (Plugins::getAllPluginsSettings() as $key => $setting) { + if ($setting['pluginFilename'] === $name) { + $name = $key; + } + } + + if (empty($name)) { SettingsModel::truncate(); } else { diff --git a/system/src/Commands/SettingsSetCommand.php b/system/src/Commands/SettingsSetCommand.php index 4ad33227..eac0ecc5 100644 --- a/system/src/Commands/SettingsSetCommand.php +++ b/system/src/Commands/SettingsSetCommand.php @@ -3,6 +3,7 @@ namespace MyAAC\Commands; use MyAAC\Models\Settings as SettingsModel; +use MyAAC\Plugins; use MyAAC\Settings; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -17,7 +18,7 @@ class SettingsSetCommand extends Command ->setDescription('Updates the setting specified by argument in database') ->addArgument('key', InputArgument::REQUIRED, - 'Setting name/key' + 'Setting key in format name.key' ) ->addArgument('value', InputArgument::REQUIRED, @@ -34,6 +35,18 @@ class SettingsSetCommand extends Command $key = $input->getArgument('key'); $value = $input->getArgument('value'); + // format settings_name.key + // example: core.template + $explode = explode('.', $key); + + // find by plugin name + foreach (Plugins::getAllPluginsSettings() as $_key => $setting) { + if ($setting['pluginFilename'] === $explode[0]) { + $explode[0] = $_key; + $key = implode('.', $explode); + } + } + $settings = Settings::getInstance(); $settings->clearCache(); $settings->load(); @@ -44,10 +57,6 @@ class SettingsSetCommand extends Command return Command::FAILURE; } - // format plugin_name.key - // example: core.template - $explode = explode('.', $key); - $settings->updateInDatabase($explode[0], $explode[1], $value); $settings->clearCache(); diff --git a/system/src/Hook.php b/system/src/Hook.php index 161b2ffc..6acdf6ef 100644 --- a/system/src/Hook.php +++ b/system/src/Hook.php @@ -38,6 +38,8 @@ class Hook } public function executeFilter(&$args) { + global $db, $config, $template_path, $ots, $content, $twig; + return include BASE . $this->_file; } diff --git a/system/src/Items.php b/system/src/Items.php index 977aa602..0f92b005 100644 --- a/system/src/Items.php +++ b/system/src/Items.php @@ -76,10 +76,11 @@ class Items public static function get($id) { self::load(); - return isset(self::$items[$id]) ? self::$items[$id] : []; + return self::$items[$id] ?? []; } - public static function getDescription($id, $count = 1) { + public static function getDescription($id, $count = 1): string + { $item = self::get($id); $attr = $item['attributes']; @@ -112,15 +113,15 @@ class Items $s .= 'an item of type ' . $item['id']; if(isset($attr['type']) && strtolower($attr['type']) == 'rune') { - $item = Spell::where('item_id', $id)->first(); - if($item) { - if($item->level > 0 && $item->maglevel > 0) { - $s .= '. ' . ($count > 1 ? "They" : "It") . ' can only be used by '; + $spell = Spell::where('item_id', $id)->first(); + if($spell) { + if($spell->level > 0 && $spell->maglevel > 0) { + $s .= '. ' . ($count > 1 ? 'They' : 'It') . ' can only be used by '; } $configVocations = config('vocations'); - if(!empty(trim($item->vocations))) { - $vocations = json_decode($item->vocations); + if(!empty(trim($spell->vocations))) { + $vocations = json_decode($spell->vocations); if(count($vocations) > 0) { foreach($vocations as $voc => $show) { $vocations[$configVocations[$voc]] = $show; @@ -133,8 +134,39 @@ class Items $s .= ' with'; + if ($spell->level > 0) { + $s .= ' level ' . $spell->level; + } + + if ($spell->maglevel > 0) { + if ($spell->level > 0) { + $s .= ' and'; + } + + $s .= ' magic level ' . $spell->maglevel; + } + + $s .= ' or higher'; } } + + if (!empty($item['weaponType'])) { + if ($item['weaponType'] == 'distance' && isset($item['ammoType'])) { + $s .= ' (Range:' . $item['range']; + } + + if (isset($item['attack']) && $item['attack'] != 0) { + $s .= ', Atk ' . ($item['attack'] > 0 ? '+' . $item['attack'] : '-' . $item['attack']); + } + + if (isset($item['hitChance']) && $item['hitChance'] != -1) { + $s .= ', Hit% ' . ($item['hitChance'] > 0 ? '+' . $item['hitChance'] : '-' . $item['hitChance']); + } + elseif ($item['weaponType'] != 'ammo') { + + } + } + return $s; } } diff --git a/system/src/Models/Account.php b/system/src/Models/Account.php index b11c2a48..4b44b153 100644 --- a/system/src/Models/Account.php +++ b/system/src/Models/Account.php @@ -5,11 +5,15 @@ namespace MyAAC\Models; use Illuminate\Database\Eloquent\Model; /** + * @property integer $premium_ends_at + * @property integer $premend * @property integer $lastday * @property integer $premdays */ class Account extends Model { + const GRATIS_PREMIUM_DAYS = 65535; + protected $table = 'accounts'; public $timestamps = false; @@ -33,35 +37,35 @@ class Account extends Model { public function getPremiumDaysAttribute() { - if(isset($this->premium_ends_at) || isset($this->premend)) { - $col = isset($this->premium_ends_at) ? 'premium_ends_at' : 'premend'; - $ret = ceil(($this->{$col}- time()) / (24 * 60 * 60)); - return $ret > 0 ? $ret : 0; + if(isset($this->premium_ends_at) || isset($this->premend) || + (isCanary() && isset($this->lastday))) { + $col = (isset($this->premium_ends_at) ? 'premium_ends_at' : (isset($this->lastday) ? 'lastday' : 'premend')); + $ret = ceil(($this->{$col} - time()) / (24 * 60 * 60)); + return max($ret, 0); } if($this->premdays == 0) { return 0; } - if($this->premdays == 65535){ - return 65535; + if($this->premdays == self::GRATIS_PREMIUM_DAYS){ + return self::GRATIS_PREMIUM_DAYS; } $ret = ceil($this->premdays - ((int)date("z", time()) + (365 * (date("Y", time()) - date("Y", $this->lastday))) - date("z", $this->lastday))); return max($ret, 0); } - public function getIsPremiumAttribute() + public function getIsPremiumAttribute(): bool { - global $config; - if(isset($config['lua']['freePremium']) && getBoolean($config['lua']['freePremium'])) return true; + if(isset($this->premium_ends_at) || isset($this->premend) || + (isCanary() && isset($this->lastday))) { + $col = (isset($this->premium_ends_at) ? 'premium_ends_at' : (isset($this->lastday) ? 'lastday' : 'premend')); + return $this->{$col} > time(); + } - if(isset($this->premium_ends_at)) { - return $this->premium_ends_at > time(); - } - - if(isset($this->premend)) { - return $this->premend > time(); + if($this->premdays == self::GRATIS_PREMIUM_DAYS){ + return true; } return ($this->premdays - (date("z", time()) + (365 * (date("Y", time()) - date("Y", $this->lastday))) - date("z", $this->lastday)) > 0); diff --git a/system/src/Models/AccountEmailVerify.php b/system/src/Models/AccountEmailVerify.php new file mode 100644 index 00000000..8773750f --- /dev/null +++ b/system/src/Models/AccountEmailVerify.php @@ -0,0 +1,15 @@ +where('hide', '!=', 1); } + + public function player() { + return $this->belongsTo(Player::class); + } } diff --git a/system/src/Models/ForumBoard.php b/system/src/Models/ForumBoard.php new file mode 100644 index 00000000..ee03fde9 --- /dev/null +++ b/system/src/Models/ForumBoard.php @@ -0,0 +1,16 @@ +belongsTo(Player::class); diff --git a/system/src/Plugins.php b/system/src/Plugins.php index 438fe32e..c6651679 100644 --- a/system/src/Plugins.php +++ b/system/src/Plugins.php @@ -11,6 +11,25 @@ class Plugins { private static $error = null; private static $plugin_json = []; + public static function getInits() + { + return Cache::remember('plugins_inits', 10 * 60, function () { + $inits = []; + foreach(self::getAllPluginsJson() as $plugin) { + if (!self::getAutoLoadOption($plugin, 'init', false)) { + continue; + } + + $pluginInits = glob(PLUGINS . $plugin['filename'] . '/init.php'); + foreach ($pluginInits as $path) { + $inits[] = $path; + } + } + + return $inits; + }); + } + public static function getAdminPages() { return Cache::remember('plugins_admin_pages', 10 * 60, function () { @@ -494,6 +513,9 @@ class Plugins { return false; } + ensureFolderExists($cachePlugins = CACHE . 'plugins'); + ensureIndexExists($cachePlugins); + self::$error = 'There was a problem with extracting zip archive.'; $file_name = $plugin_temp_dir . $json_file; if(!file_exists($file_name)) { @@ -513,193 +535,192 @@ class Plugins { self::$plugin_json = $plugin_json; if ($plugin_json == null) { self::$warnings[] = 'Cannot load ' . $file_name . '. File might be not a valid json code.'; + return false; } - else { - $continue = true; - if(!isset($plugin_json['name']) || empty(trim($plugin_json['name']))) { - self::$error = 'Plugin "name" tag is not set.'; + $continue = true; + + if(!isset($plugin_json['name']) || empty(trim($plugin_json['name']))) { + self::$error = 'Plugin "name" tag is not set.'; + return false; + } + + if(!isset($plugin_json['version']) || empty(trim($plugin_json['version']))) { + self::$warnings[] = 'Plugin "version" tag is not set.'; + } + + if(isset($plugin_json['require'])) { + $require = $plugin_json['require']; + + $myaac_satisfied = true; + if(isset($require['myaac_'])) { + $require_myaac = $require['myaac_']; + if(!Semver::satisfies(MYAAC_VERSION, $require_myaac)) { + $myaac_satisfied = false; + } + } + else if(isset($require['myaac'])) { + $require_myaac = $require['myaac']; + if(version_compare(MYAAC_VERSION, $require_myaac, '<')) { + $myaac_satisfied = false; + } + } + + if(!$myaac_satisfied) { + self::$error = "Your AAC version doesn't meet the requirement of this plugin. Required version is: " . $require_myaac . ", and you're using version " . MYAAC_VERSION . "."; return false; } - if(!isset($plugin_json['version']) || empty(trim($plugin_json['version']))) { - self::$warnings[] = 'Plugin "version" tag is not set.'; + $php_satisfied = true; + if(isset($require['php_'])) { + $require_php = $require['php_']; + if(!Semver::satisfies(phpversion(), $require_php)) { + $php_satisfied = false; + } + } + else if(isset($require['php'])) { + $require_php = $require['php']; + if(version_compare(phpversion(), $require_php, '<')) { + $php_satisfied = false; + } } - if(isset($plugin_json['require'])) { - $require = $plugin_json['require']; + if(!$php_satisfied) { + self::$error = "Your PHP version doesn't meet the requirement of this plugin. Required version is: " . $require_php . ", and you're using version " . phpversion() . "."; + $continue = false; + } - $myaac_satified = true; - if(isset($require['myaac_'])) { - $require_myaac = $require['myaac_']; - if(!Semver::satisfies(MYAAC_VERSION, $require_myaac)) { - $myaac_satified = false; + $database_satisfied = true; + if(isset($require['database_'])) { + $require_database = $require['database_']; + if(!Semver::satisfies(DATABASE_VERSION, $require_database)) { + $database_satisfied = false; + } + } + else if(isset($require['database'])) { + $require_database = $require['database']; + if(version_compare(DATABASE_VERSION, $require_database, '<')) { + $database_satisfied = false; + } + } + + if(!$database_satisfied) { + self::$error = "Your database version doesn't meet the requirement of this plugin. Required version is: " . $require_database . ", and you're using version " . DATABASE_VERSION . "."; + $continue = false; + } + + if($continue) { + foreach($require as $req => $version) { + $req = strtolower(trim($req)); + $version = trim($version); + + if(in_array($req, array('myaac', 'myaac_', 'php', 'php_', 'database', 'database_'))) { + continue; } - } - else if(isset($require['myaac'])) { - $require_myaac = $require['myaac']; - if(version_compare(MYAAC_VERSION, $require_myaac, '<')) { - $myaac_satified = false; - } - } - if(!$myaac_satified) { - self::$error = "Your AAC version doesn't meet the requirement of this plugin. Required version is: " . $require_myaac . ", and you're using version " . MYAAC_VERSION . "."; - return false; - } + if(in_array($req, array('php-ext', 'php-extension'))) { // require php extension + $tmpDisplayError = false; + $explode = explode(',', $version); - $php_satisfied = true; - if(isset($require['php_'])) { - $require_php = $require['php_']; - if(!Semver::satisfies(phpversion(), $require_php)) { - $php_satisfied = false; - } - } - else if(isset($require['php'])) { - $require_php = $require['php']; - if(version_compare(phpversion(), $require_php, '<')) { - $php_satisfied = false; - } - } - - if(!$php_satisfied) { - self::$error = "Your PHP version doesn't meet the requirement of this plugin. Required version is: " . $require_php . ", and you're using version " . phpversion() . "."; - $continue = false; - } - - $database_satisfied = true; - if(isset($require['database_'])) { - $require_database = $require['database_']; - if(!Semver::satisfies(DATABASE_VERSION, $require_database)) { - $database_satisfied = false; - } - } - else if(isset($require['database'])) { - $require_database = $require['database']; - if(version_compare(DATABASE_VERSION, $require_database, '<')) { - $database_satisfied = false; - } - } - - if(!$database_satisfied) { - self::$error = "Your database version doesn't meet the requirement of this plugin. Required version is: " . $require_database . ", and you're using version " . DATABASE_VERSION . "."; - $continue = false; - } - - if($continue) { - foreach($require as $req => $version) { - $req = strtolower(trim($req)); - $version = trim($version); - - if(in_array($req, array('myaac', 'myaac_', 'php', 'php_', 'database', 'database_'))) { - continue; + foreach ($explode as $item) { + if(!extension_loaded($item)) { + $errors[] = "This plugin requires php extension: " . $item . " to be installed."; + $tmpDisplayError = true; + } } - if(in_array($req, array('php-ext', 'php-extension'))) { // require php extension - $tmpDisplayError = false; - $explode = explode(',', $version); - - foreach ($explode as $item) { - if(!extension_loaded($item)) { - $errors[] = "This plugin requires php extension: " . $item . " to be installed."; - $tmpDisplayError = true; - } - } - - if ($tmpDisplayError) { - self::$error = implode('
', $errors); - $continue = false; - break; - } - } - else if($req == 'table') { - $tmpDisplayError = false; - $explode = explode(',', $version); - foreach ($explode as $item) { - if(!$db->hasTable($item)) { - $errors[] = "This plugin requires table: " . $item . " to exist in the database."; - $tmpDisplayError = true; - } - } - - if ($tmpDisplayError) { - self::$error = implode('
', $errors); - $continue = false; - break; - } - } - else if($req == 'column') { - $tmpDisplayError = false; - $explode = explode(',', $version); - foreach ($explode as $item) { - $tmp = explode('.', $item); - - if(count($tmp) == 2) { - if(!$db->hasColumn($tmp[0], $tmp[1])) { - $errors[] = "This plugin requires database column: " . $tmp[0] . "." . $tmp[1] . " to exist in database."; - $tmpDisplayError = true; - } - } - else { - self::$warnings[] = "Invalid plugin require column: " . $item; - } - } - - if ($tmpDisplayError) { - self::$error = implode('
', $errors); - $continue = false; - break; - } - } - else if(strpos($req, 'ext-') !== false) { - $tmp = explode('-', $req); - if(count($tmp) == 2) { - if(!extension_loaded($tmp[1]) || !Semver::satisfies(phpversion($tmp[1]), $version)) { - self::$error = "This plugin requires php extension: " . $tmp[1] . ", version " . $version . " to be installed."; - $continue = false; - break; - } - } - } - else if(!self::is_installed($req, $version)) { - self::$error = "This plugin requires another plugin to run correctly. The another plugin is: " . $req . ", with version " . $version . "."; + if ($tmpDisplayError) { + self::$error = implode('
', $errors); $continue = false; break; } } - } - } + else if($req == 'table') { + $tmpDisplayError = false; + $explode = explode(',', $version); + foreach ($explode as $item) { + if(!$db->hasTable($item)) { + $errors[] = "This plugin requires table: " . $item . " to exist in the database."; + $tmpDisplayError = true; + } + } - if($continue) { - if(!$zip->extractTo(BASE)) { // "Real" Install - self::$error = 'There was a problem with extracting zip archive to base directory.'; - $zip->close(); - return false; - } - - $install = $plugin_json['install'] ?? ''; - if (self::getAutoLoadOption($plugin_json, 'install', true) && is_file(PLUGINS . $pluginFilename . '/install.php')) { - $install = 'plugins/' . $pluginFilename . '/install.php'; - } - - if (!empty($install)) { - if (file_exists(BASE . $install)) { - $db->revalidateCache(); - require BASE . $install; - $db->revalidateCache(); + if ($tmpDisplayError) { + self::$error = implode('
', $errors); + $continue = false; + break; + } } - else { - self::$warnings[] = 'Cannot load install script. Your plugin might be not working correctly.'; + else if($req == 'column') { + $tmpDisplayError = false; + $explode = explode(',', $version); + foreach ($explode as $item) { + $tmp = explode('.', $item); + + if(count($tmp) == 2) { + if(!$db->hasColumn($tmp[0], $tmp[1])) { + $errors[] = "This plugin requires database column: " . $tmp[0] . "." . $tmp[1] . " to exist in database."; + $tmpDisplayError = true; + } + } + else { + self::$warnings[] = "Invalid plugin require column: " . $item; + } + } + + if ($tmpDisplayError) { + self::$error = implode('
', $errors); + $continue = false; + break; + } + } + else if(strpos($req, 'ext-') !== false) { + $tmp = explode('-', $req); + if(count($tmp) == 2) { + if(!extension_loaded($tmp[1]) || !Semver::satisfies(phpversion($tmp[1]), $version)) { + self::$error = "This plugin requires php extension: " . $tmp[1] . ", version " . $version . " to be installed."; + $continue = false; + break; + } + } + } + else if(!self::is_installed($req, $version)) { + self::$error = "This plugin requires another plugin to run correctly. The another plugin is: " . $req . ", with version " . $version . "."; + $continue = false; + break; } } - - clearCache(); - - return true; } } - return false; + if(!$continue) { + return false; + } + + if(!$zip->extractTo(BASE)) { // "Real" Install + self::$error = 'There was a problem with extracting zip archive to base directory.'; + $zip->close(); + return false; + } + + $install = $plugin_json['install'] ?? ''; + if (self::getAutoLoadOption($plugin_json, 'install', true) && is_file(PLUGINS . $pluginFilename . '/install.php')) { + $install = 'plugins/' . $pluginFilename . '/install.php'; + } + + if (!empty($install)) { + if (file_exists(BASE . $install)) { + $db->revalidateCache(); + require BASE . $install; + $db->revalidateCache(); + } + else { + self::$warnings[] = 'Cannot load install script. Your plugin might be not working correctly.'; + } + } + + clearCache(); + return true; } public static function isEnabled($pluginFileName): bool @@ -762,15 +783,20 @@ class Plugins { return false; } - if(!isset($plugin_json['install'])) { - self::$error = "Plugin doesn't have install options defined. Skipping..."; + $install = $plugin_json['install'] ?? ''; + if (self::getAutoLoadOption($plugin_json, 'install', true) && is_file(PLUGINS . $plugin_name . '/install.php')) { + $install = 'plugins/' . $plugin_name . '/install.php'; + } + + if (empty($install)) { + self::$error = "This plugin doesn't seem to have install script defined."; return false; } global $db; - if (file_exists(BASE . $plugin_json['install'])) { + if (file_exists(BASE . $install)) { $db->revalidateCache(); - require BASE . $plugin_json['install']; + require BASE . $install; $db->revalidateCache(); } else { diff --git a/system/src/Settings.php b/system/src/Settings.php index ca4192ad..fea52d04 100644 --- a/system/src/Settings.php +++ b/system/src/Settings.php @@ -7,16 +7,13 @@ use MyAAC\Models\Settings as ModelsSettings; class Settings implements \ArrayAccess { - static private $instance; - private $settingsFile = []; - private $settingsDatabase = []; - private $cache = []; - private $valuesAsked = []; - private $errors = []; + static private ?Settings $instance = null; + private array $settingsFile = []; + private array $settingsDatabase = []; + private array $cache = []; + private array $valuesAsked = []; + private array $errors = []; - /** - * @return Settings - */ public static function getInstance(): Settings { if (!self::$instance) { @@ -26,28 +23,21 @@ class Settings implements \ArrayAccess return self::$instance; } - public function load() + public function load(): void { - $cache = Cache::getInstance(); - if ($cache->enabled()) { - $tmp = ''; - if ($cache->fetch('settings', $tmp)) { - $this->settingsDatabase = unserialize($tmp); - return; + $this->settingsDatabase = Cache::remember('settings', 10 * 60, function () { + $settingsDatabase = []; + + $settings = ModelsSettings::all(); + foreach ($settings as $setting) { + $settingsDatabase[$setting->name][$setting->key] = $setting->value; } - } - $settings = ModelsSettings::all(); - foreach ($settings as $setting) { - $this->settingsDatabase[$setting->name][$setting->key] = $setting->value; - } - - if ($cache->enabled()) { - $cache->set('settings', serialize($this->settingsDatabase), 600); - } + return $settingsDatabase; + }); } - public function save($pluginName, $values) + public function save($pluginName, $values): bool { $this->loadPlugin($pluginName); @@ -104,7 +94,7 @@ class Settings implements \ArrayAccess return true; } - public function updateInDatabase($pluginName, $key, $value) + public function updateInDatabase($pluginName, $key, $value): void { if (ModelsSettings::where(['name' => $pluginName, 'key' => $key])->exists()) { ModelsSettings::where(['name' => $pluginName, 'key' => $key])->update(['value' => $value]); @@ -117,7 +107,7 @@ class Settings implements \ArrayAccess $this->clearCache(); } - public function deleteFromDatabase($pluginName, $key = null) + public function deleteFromDatabase($pluginName, $key = null): void { if (!isset($key)) { ModelsSettings::where('name', $pluginName)->delete(); @@ -217,7 +207,7 @@ class Settings implements \ArrayAccess if (isset($setting['hidden']) && $setting['hidden']) { $value = ''; if ($setting['type'] === 'boolean') { - $value = ($setting['default'] ? 'true' : 'false'); + $value = (getBoolean($setting['default']) ? 'true' : 'false'); } else if (in_array($setting['type'], ['text', 'number', 'float', 'double', 'email', 'password', 'textarea'])) { $value = $setting['default']; @@ -230,12 +220,7 @@ class Settings implements \ArrayAccess } else if ($setting['type'] === 'boolean') { if(isset($settingsDb[$key])) { - if($settingsDb[$key] === 'true') { - $value = true; - } - else { - $value = false; - } + $value = getBoolean($settingsDb[$key]); } else { $value = ($setting['default'] ?? false); @@ -263,7 +248,7 @@ class Settings implements \ArrayAccess echo '
'; } - echo ''; + echo ''; if ($setting['type'] === 'password') { echo '
'; @@ -281,7 +266,7 @@ class Settings implements \ArrayAccess if ($rows < 2) { $rows = 2; // always min 2 rows for textarea } - echo ''; + echo ''; } else if ($setting['type'] === 'options') { @@ -383,7 +368,7 @@ class Settings implements \ArrayAccess } #[\ReturnTypeWillChange] - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { if (is_null($offset)) { throw new \RuntimeException("Settings: You cannot set empty offset with value: $value!"); @@ -423,7 +408,7 @@ class Settings implements \ArrayAccess } #[\ReturnTypeWillChange] - public function offsetUnset($offset) + public function offsetUnset($offset): void { $this->loadPlugin($offset); @@ -455,7 +440,7 @@ class Settings implements \ArrayAccess * @return array|mixed */ #[\ReturnTypeWillChange] - public function offsetGet($offset) + public function offsetGet($offset): mixed { // try cache hit if(isset($this->cache[$offset])) { @@ -472,24 +457,22 @@ class Settings implements \ArrayAccess if (!isset($this->settingsFile[$pluginKeyName]['settings'])) { throw new \RuntimeException('Unknown plugin settings: ' . $pluginKeyName); } + return $this->settingsFile[$pluginKeyName]['settings']; } - $ret = []; - if(isset($this->settingsFile[$pluginKeyName]['settings'][$key])) { - $ret = $this->settingsFile[$pluginKeyName]['settings'][$key]; + if (!isset($this->settingsFile[$pluginKeyName]['settings'][$key])) { + return null; } + $ret = $this->settingsFile[$pluginKeyName]['settings'][$key]; + if(isset($this->settingsDatabase[$pluginKeyName][$key])) { $value = $this->settingsDatabase[$pluginKeyName][$key]; $ret['value'] = $value; } else { - if (!isset($this->settingsFile[$pluginKeyName]['settings'][$key])) { - return null; - } - $ret['value'] = $this->settingsFile[$pluginKeyName]['settings'][$key]['default']; } @@ -523,7 +506,7 @@ class Settings implements \ArrayAccess return $ret; } - private function updateValuesAsked($offset) + private function updateValuesAsked($offset): void { $pluginKeyName = $offset; if (strpos($offset, '.')) { @@ -539,7 +522,7 @@ class Settings implements \ArrayAccess } } - private function loadPlugin($offset) + private function loadPlugin($offset): void { $this->updateValuesAsked($offset); @@ -568,7 +551,7 @@ class Settings implements \ArrayAccess } } - public static function saveConfig($config, $filename, &$content = '') + public static function saveConfig($config, $filename, &$content = ''): bool|int { $content = " $name, 'error' => '']; + $hooks->triggerFilter(HOOK_FILTER_VALIDATE_CHARACTER_NEW_NAME, $params); + + if (!empty($params['error'])) { + self::$lastError = $params['error']; + return false; + } + return true; } diff --git a/system/src/global.php b/system/src/global.php index e4dfec58..03a977b3 100644 --- a/system/src/global.php +++ b/system/src/global.php @@ -8,7 +8,9 @@ $i = 0; define('HOOK_INIT', ++$i); define('HOOK_STARTUP', ++$i); define('HOOK_BEFORE_PAGE', ++$i); +define('HOOK_BEFORE_PAGE_CUSTOM', ++$i); define('HOOK_AFTER_PAGE', ++$i); +define('HOOK_AFTER_PAGE_CUSTOM', ++$i); define('HOOK_FINISH', ++$i); define('HOOK_TIBIACOM_ARTICLE', ++$i); define('HOOK_TIBIACOM_BORDER_3', ++$i); @@ -26,6 +28,8 @@ define('HOOK_CHARACTERS_AFTER_CHARACTERS', ++$i); define('HOOK_LOGIN', ++$i); define('HOOK_LOGIN_ATTEMPT', ++$i); define('HOOK_LOGOUT', ++$i); +define('HOOK_ACCOUNT_CHANGE_PASSWORD_AFTER_OLD_PASSWORD', ++$i); +define('HOOK_ACCOUNT_CHANGE_PASSWORD_AFTER_NEW_PASSWORD', ++$i); define('HOOK_ACCOUNT_CHANGE_PASSWORD_POST', ++$i); define('HOOK_ACCOUNT_CREATE_BEFORE_FORM', ++$i); define('HOOK_ACCOUNT_CREATE_BEFORE_BOXES', ++$i); @@ -52,6 +56,7 @@ define('HOOK_ACCOUNT_MANAGE_BEFORE_GENERAL_INFORMATION', ++$i); define('HOOK_ACCOUNT_MANAGE_BEFORE_PUBLIC_INFORMATION', ++$i); define('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS', ++$i); define('HOOK_ACCOUNT_MANAGE_BEFORE_CHARACTERS', ++$i); +define('HOOK_ACCOUNT_MANAGE_AFTER_CHARACTERS', ++$i); define('HOOK_ACCOUNT_LOGIN_BEFORE_PAGE', ++$i); define('HOOK_ACCOUNT_LOGIN_BEFORE_ACCOUNT', ++$i); define('HOOK_ACCOUNT_LOGIN_AFTER_ACCOUNT', ++$i); @@ -90,6 +95,7 @@ define('HOOK_EMAIL_CONFIRMED', ++$i); define('HOOK_GUILDS_BEFORE_GUILD_HEADER', ++$i); define('HOOK_GUILDS_AFTER_GUILD_HEADER', ++$i); define('HOOK_GUILDS_AFTER_GUILD_INFORMATION', ++$i); +define('HOOK_GUILDS_AFTER_MANAGE_BUTTON', ++$i); define('HOOK_GUILDS_AFTER_GUILD_MEMBERS', ++$i); define('HOOK_GUILDS_AFTER_INVITED_CHARACTERS', ++$i); define('HOOK_TWIG', ++$i); @@ -103,6 +109,7 @@ define('HOOK_FILTER_TWIG_DISPLAY', ++$i); define('HOOK_FILTER_TWIG_RENDER', ++$i); define('HOOK_FILTER_THEME_FOOTER', ++$i); define('HOOK_FILTER_POT', ++$i); +define('HOOK_FILTER_VALIDATE_CHARACTER_NEW_NAME', ++$i); const HOOK_FIRST = HOOK_INIT; define('HOOK_LAST', $i); diff --git a/system/status.php b/system/status.php index 8e8225dd..b69e7acc 100644 --- a/system/status.php +++ b/system/status.php @@ -145,13 +145,7 @@ function updateStatus() { } $uptime = $status['uptime'] = $serverStatus->getUptime(); - $m = date('m', $uptime); - $m = $m > 1 ? "$m months, " : ($m == 1 ? 'month, ' : ''); - $d = date('d', $uptime); - $d = $d > 1 ? "$d days, " : ($d == 1 ? 'day, ' : ''); - $h = date('H', $uptime); - $min = date('i', $uptime); - $status['uptimeReadable'] = "{$m}{$d}{$h}h {$min}m"; + $status['uptimeReadable'] = getStatusUptimeReadable($uptime); $status['monsters'] = $serverStatus->getMonstersCount(); $status['motd'] = $serverStatus->getMOTD(); diff --git a/system/template.php b/system/template.php index f051e640..30cb5535 100644 --- a/system/template.php +++ b/system/template.php @@ -91,7 +91,7 @@ else { $file = BASE . $template_path . '/layout_config.ini'; } - $template_ini = parse_ini_file($file); + $template_ini = parse_ini_file($file, true); unset($file); if ($cache->enabled()) { @@ -148,7 +148,7 @@ function get_template_menus(): array { global $template_name; - $result = Cache::remember('template_menus', 10 * 60, function () use ($template_name) { + $result = Cache::remember('template_menus_' . $template_name, 10 * 60, function () use ($template_name) { $result = Menu::select(['name', 'link', 'blank', 'color', 'category']) ->where('template', $template_name) ->orderBy('category') diff --git a/system/templates/account.change-email.html.twig b/system/templates/account.change-email.html.twig index ea627cc7..ab2a8c06 100644 --- a/system/templates/account.change-email.html.twig +++ b/system/templates/account.change-email.html.twig @@ -28,7 +28,7 @@ Please enter your password and the new email address. Make sure that you enter a - diff --git a/system/templates/account.change-password.html.twig b/system/templates/account.change-password.html.twig index df101323..03f82d9b 100644 --- a/system/templates/account.change-password.html.twig +++ b/system/templates/account.change-password.html.twig @@ -9,23 +9,29 @@ Please enter your current password and a new password. For your security, please Current Password: + + {{ hook('HOOK_ACCOUNT_CHANGE_PASSWORD_AFTER_OLD_PASSWORD') }} + + + {{ hook('HOOK_ACCOUNT_CHANGE_PASSWORD_AFTER_NEW_PASSWORD') }} +
+
{{ csrf() }} @@ -40,14 +40,14 @@ Please enter your password and the new email address. Make sure that you enter a
- - {{ csrf() }} - - + - - + + +
+
+ + {{ csrf() }} {{ include('buttons.back.html.twig') }} -
- +
New Password: - +
New Password Again: - +
diff --git a/system/templates/account.characters.change-comment.html.twig b/system/templates/account.characters.change-comment.html.twig index 8bb94c80..ab8fb384 100644 --- a/system/templates/account.characters.change-comment.html.twig +++ b/system/templates/account.characters.change-comment.html.twig @@ -88,7 +88,7 @@ If you do not want to specify a certain field, just leave it blank.

- + +
+ {{ include('buttons.submit.html.twig') }} @@ -99,15 +99,15 @@ If you do not want to specify a certain field, just leave it blank.

- - {{ csrf() }} - - + - - -
+
+ + {{ csrf() }} {{ include('buttons.back.html.twig') }} -
+ +
diff --git a/system/templates/account.characters.delete.html.twig b/system/templates/account.characters.delete.html.twig index 8593f7ed..b946d221 100644 --- a/system/templates/account.characters.delete.html.twig +++ b/system/templates/account.characters.delete.html.twig @@ -24,7 +24,7 @@ To delete a character enter the name of the character and your password.

- diff --git a/system/templates/account.generate_recovery_key.html.twig b/system/templates/account.generate_recovery_key.html.twig index c34132f0..526d4659 100644 --- a/system/templates/account.generate_recovery_key.html.twig +++ b/system/templates/account.generate_recovery_key.html.twig @@ -32,14 +32,14 @@ To generate recovery key for your account please enter your password.

diff --git a/system/templates/account.management.html.twig b/system/templates/account.management.html.twig index 2db1e57c..03e2c2a6 100644 --- a/system/templates/account.management.html.twig +++ b/system/templates/account.management.html.twig @@ -228,5 +228,7 @@
+
{{ csrf() }} @@ -36,14 +36,14 @@ To delete a character enter the name of the character and your password.

- - {{ csrf() }} - - + - - + + +
+
+ + {{ csrf() }} {{ include('buttons.back.html.twig') }} -
- - {{ csrf() }} - - + - - + + +
+
+ + {{ csrf() }} {{ include('buttons.back.html.twig') }} -
+
+ {{ hook('HOOK_ACCOUNT_MANAGE_AFTER_CHARACTERS') }}
diff --git a/system/templates/account.resend-email-verify.html.twig b/system/templates/account.resend-email-verify.html.twig new file mode 100644 index 00000000..7dd9cc98 --- /dev/null +++ b/system/templates/account.resend-email-verify.html.twig @@ -0,0 +1,45 @@ +Please enter your account Email address.

+{% set title = 'Resend Email' %} +{% set background = config('darkborder') %} +{% set content %} + + + + + +
+ + + +
+{% endset %} +{% include 'tables.headline.html.twig' %} +
+ + + + + +
+ + + + +
+
+ {{ csrf() }} + + {{ include('buttons.submit.html.twig') }} +
+
+
+ + + + +
+
+ {{ include('buttons.back.html.twig') }} +
+
+
diff --git a/system/templates/admin.mailer.html.twig b/system/templates/admin.mailer.html.twig index 0b77ccb3..1dd2eb30 100644 --- a/system/templates/admin.mailer.html.twig +++ b/system/templates/admin.mailer.html.twig @@ -16,6 +16,13 @@ + {% if setting('core.account_mail_verify') %} +
+ + +
+ {% endif %} +
diff --git a/system/templates/admin.plugins.form.html.twig b/system/templates/admin.plugins.form.html.twig index aec82a8a..f8eddd41 100644 --- a/system/templates/admin.plugins.form.html.twig +++ b/system/templates/admin.plugins.form.html.twig @@ -1,7 +1,9 @@
-
Install plugin
+
Install plugin + Check for updates +
{{ csrf() }} diff --git a/system/templates/admin.plugins.outdated.html.twig b/system/templates/admin.plugins.outdated.html.twig new file mode 100644 index 00000000..95421edc --- /dev/null +++ b/system/templates/admin.plugins.outdated.html.twig @@ -0,0 +1,18 @@ + + + + + + + + + + {% for plugin in plugins %} + + + + + + + {% endfor %} +
Plugin NameYour VersionLatest VersionDownload Link
{{ plugin.name }}{{ plugin.yourVersion }}{{ plugin.latestVersion }}{{ plugin.download_link }}
diff --git a/system/templates/characters.form.html.twig b/system/templates/characters.form.html.twig index d731b4e2..0fd85608 100644 --- a/system/templates/characters.form.html.twig +++ b/system/templates/characters.form.html.twig @@ -1,17 +1,23 @@ - - - - - - -
Search Character
- - - - - -
Name: - {{ include('buttons.submit.html.twig') }} -
-
-
\ No newline at end of file +
+
+ {% set title = 'Search Character' %} + {% set tableClass = 'Table1' %} + {% set background = config('darkborder') %} + {% set content %} + + + + + + +
+ Character Name: + + + + {% set button_name = 'Submit' %} + {{ include('buttons.base.html.twig') }} +
+ {% endset %} + {{ include('tables.headline.html.twig') }} +
diff --git a/system/templates/characters.html.twig b/system/templates/characters.html.twig index 2e09bd26..75e15b68 100644 --- a/system/templates/characters.html.twig +++ b/system/templates/characters.html.twig @@ -9,7 +9,7 @@
- {{ hook(constant('HOOK_CHARACTERS_BEFORE_INFORMATIONS')) }} + {{ hook('HOOK_CHARACTERS_BEFORE_INFORMATIONS') }} {% if canEdit %} Edit @@ -153,11 +153,11 @@ {% if account.isPremium() %}Premium Account{% else %}Free Account{% endif %}
- {{ hook(constant('HOOK_CHARACTERS_AFTER_INFORMATIONS')) }} + {{ hook('HOOK_CHARACTERS_AFTER_INFORMATIONS') }}
- {{ hook(constant('HOOK_CHARACTERS_BEFORE_SKILLS')) }} + {{ hook('HOOK_CHARACTERS_BEFORE_SKILLS') }} {% if config.characters.skills %} @@ -179,7 +179,7 @@ {% endif %} - {{ hook(constant('HOOK_CHARACTERS_AFTER_SKILLS')) }} + {{ hook('HOOK_CHARACTERS_AFTER_SKILLS') }} {% if quests_enabled %} @@ -201,7 +201,7 @@ {% endif %} - {{ hook(constant('HOOK_CHARACTERS_AFTER_QUESTS')) }} + {{ hook('HOOK_CHARACTERS_AFTER_QUESTS') }} {% if config.characters.equipment %} @@ -239,11 +239,11 @@ {% endif %} - {{ hook(constant('HOOK_CHARACTERS_AFTER_EQUIPMENT')) }} + {{ hook('HOOK_CHARACTERS_AFTER_EQUIPMENT') }}
- {{ hook(constant('HOOK_CHARACTERS_BEFORE_DEATHS')) }} + {{ hook('HOOK_CHARACTERS_BEFORE_DEATHS') }} {% if deaths|length > 0 %} @@ -283,7 +283,7 @@ {% endif %} - {{ hook(constant('HOOK_CHARACTERS_BEFORE_SIGNATURE')) }} + {{ hook('HOOK_CHARACTERS_BEFORE_SIGNATURE') }} {% if setting('core.signature_enabled') %} @@ -327,7 +327,7 @@ {% endif %} - {{ hook(constant('HOOK_CHARACTERS_AFTER_SIGNATURE')) }} + {{ hook('HOOK_CHARACTERS_AFTER_SIGNATURE') }} {% if not player.isHidden() %} {% set rows = 0 %} @@ -377,7 +377,7 @@ - {{ hook(constant('HOOK_CHARACTERS_AFTER_ACCOUNT')) }} + {{ hook('HOOK_CHARACTERS_AFTER_ACCOUNT') }}

@@ -421,7 +421,7 @@
{% endif %} - {{ hook(constant('HOOK_CHARACTERS_AFTER_CHARACTERS')) }} + {{ hook('HOOK_CHARACTERS_AFTER_CHARACTERS') }} {% if canEdit %} Edit diff --git a/system/templates/error_box.html.twig b/system/templates/error_box.html.twig index e6ed4992..ba9a2263 100644 --- a/system/templates/error_box.html.twig +++ b/system/templates/error_box.html.twig @@ -9,7 +9,7 @@
The Following Errors Have Occurred:
{% for error in errors %} -
  • {{ error|striptags('')|raw }}
  • +
  • {{ error|striptags('')|raw }}
  • {% endfor %}
    @@ -17,4 +17,4 @@
    -
    \ No newline at end of file +
    diff --git a/system/templates/guilds.view.html.twig b/system/templates/guilds.view.html.twig index a25d9570..33dc1d3d 100644 --- a/system/templates/guilds.view.html.twig +++ b/system/templates/guilds.view.html.twig @@ -49,6 +49,7 @@ {% include('buttons.base.html.twig') %}
    {% endif %} + {{ hook('HOOK_GUILDS_AFTER_MANAGE_BUTTON') }} @@ -61,6 +62,7 @@ {{ hook('HOOK_GUILDS_AFTER_GUILD_INFORMATION') }} {% set title = 'Guild Members' %} + {% set background = config('lightborder') %} {% set content %} @@ -151,6 +153,7 @@ {{ hook('HOOK_GUILDS_AFTER_GUILD_MEMBERS') }} {% set title = 'Invited Characters' %} + {% set background = config('lightborder') %} {% set content %}
    @@ -232,14 +235,16 @@ {% endif %} {% if isVice %} - - {{ csrf() }} - - + {% if db.hasTableAndColumns('guild_invites', ['player_id']) %} + + {{ csrf() }} + + + {% endif %} {{ csrf() }} diff --git a/system/templates/highscores.html.twig b/system/templates/highscores.html.twig index 86599b90..88bf579b 100644 --- a/system/templates/highscores.html.twig +++ b/system/templates/highscores.html.twig @@ -66,7 +66,7 @@
    - {% set button_name = 'Invite Character' %} - {% set button_image = '_sbutton_invitecharacter' %} - {% include('buttons.base.html.twig') %} -
    + {% set button_name = 'Invite Character' %} + {% set button_image = '_sbutton_invitecharacter' %} + {% include('buttons.base.html.twig') %} +
    - {{ player.name }} + {{ player.name }} {% if setting('core.highscores_vocation') %}
    {{ player.vocation }} @@ -94,8 +94,10 @@ {% endif %}
    + {% if setting('core.highscores_skills_box') or setting('core.highscores_vocation_box') %} + {% if setting('core.highscores_skills_box') %} @@ -109,7 +111,8 @@
    Choose a skill

    - {% if config.highscores_vocation_box %} + {% endif %} + {% if setting('core.highscores_vocation_box') %} @@ -126,5 +129,6 @@ {% endif %} + {% endif %}
    Choose a vocation
    diff --git a/system/templates/install.config.html.twig b/system/templates/install.config.html.twig index ab4fc361..8c0aa50a 100644 --- a/system/templates/install.config.html.twig +++ b/system/templates/install.config.html.twig @@ -9,7 +9,7 @@ - {% for value in ['server_path'] %} + {% for value in ['site_url', 'server_path'] %}
    diff --git a/system/templates/mail.account.resend-email-verify.html.twig b/system/templates/mail.account.resend-email-verify.html.twig new file mode 100644 index 00000000..75bdab8b --- /dev/null +++ b/system/templates/mail.account.resend-email-verify.html.twig @@ -0,0 +1,7 @@ +Hello {{ account }}!
    +
    +You requested to resend the verify Email on {{ config.lua.serverName }}!
    +
    + +To verify your email address please click the link below:
    +{{ verify_url|raw }} diff --git a/system/templates/online.form.html.twig b/system/templates/online.form.html.twig deleted file mode 100644 index 82c6f7b2..00000000 --- a/system/templates/online.form.html.twig +++ /dev/null @@ -1,25 +0,0 @@ -
    - - - - - - - - -
    - Search Character -
    - - - - - - -
    Name: - - - {{ include('buttons.submit.html.twig') }} -
    -
    - \ No newline at end of file diff --git a/system/templates/online.html.twig b/system/templates/online.html.twig index 0210d80f..6f798740 100644 --- a/system/templates/online.html.twig +++ b/system/templates/online.html.twig @@ -1,39 +1,13 @@ - - - - -{% if players|length == 0 %} -
    Server Status
    Currently no one is playing on {{ config.lua.serverName }}.
    -{% else %} - - - {% if not status.online %} - Server is offline.
    - {% else %} - {% if setting('core.online_afk') %} - {% set players_count = players|length %} - {% set afk = players_count - status.players %} - {% if afk < 0 %} - {% set players_count = players_count + afk|abs %} - {% set afk = 0 %} - {% endif %} - Currently there are {{ status.players }} active and {{ afk }} AFK players.
    - Total number of players: {{ players_count }}.
    - {% else %} - Currently {{ players|length }} players are online.
    - {% endif %} - {% endif %} - {% if setting('core.online_record') %} - {{ record }} - {% endif %} - - - +{% set onlineTTL = setting('core.online_cache_ttl') %} +{% if onlineTTL > 0 and cache.enabled() %} +*Note: Online List is updated every {{ onlineTTL > 1 ? ' ' ~ onlineTTL : '' }} minute{{ onlineTTL > 1 ? 's' : '' }}.
    - {# vocation statistics #} - {% if setting('core.online_vocations') %} +{% endif %} + +{# vocation statistics #} +{% if setting('core.online_vocations') %}
    - {% if setting('core.online_vocations_images') %} + {% if setting('core.online_vocations_images') %} @@ -69,11 +43,13 @@ {% endfor %}

    - {% endif %} {% endif %} +{% endif %} - {# show skulls #} - {% if setting('core.online_skulls') %} +
    + +{# show skulls #} +{% if setting('core.online_skulls') %}
    @@ -83,34 +59,114 @@
    +{% endif %} + +
    + +{% set title = 'World Information' %} +{% set tableClass = 'Table3' %} +{% set background = config('darkborder') %} +{% set content %} + + + + + + + + + + + {% if setting('core.online_record') and record|length > 0 %} + + + + {% endif %} -
    Status:{% if not status.online %}Offline{% else %}Online{% endif %}
    Players Online: + {% if setting('core.online_afk') %} + {% set players_count = players|length %} + {% set afk = players_count - status.players %} + {% if afk < 0 %} + {% set players_count = players_count + afk|abs %} + {% set afk = 0 %} + {% endif %} + Currently there are {{ status.players }} active and {{ afk }} AFK players.
    + Total number of players: {{ players_count }}.
    + {% else %} + {{ players|length }} + {% endif %} +
    Online Record: + {{ record }} +
    - + + + + + + + + +
    Location Datacenter:{{ setting('core.online_datacenter')|raw }} (Server date & time: - {{ "now"|date("d/m/Y H:i:s") }})
    PvP Type: + {% set worldType = config('lua')['worldType']|lower %} + {% if worldType in ['pvp','2','normal','open','openpvp'] %} + Open PvP + {% elseif worldType in ['no-pvp','nopvp','non-pvp','nonpvp','1','safe','optional','optionalpvp'] %} + Optional PvP + {% elseif worldType in ['pvp-enforced','pvpenforced','pvp-enfo','pvpenfo','pvpe','enforced','enfo','3','war','hardcore','hardcorepvp'] %} + Hardcore PvP + {% endif %} +
    +{% endset %} +{% include 'tables.headline.html.twig' %} +
    +
    + +{% set title = 'Players Online' %} +{% set tableClass = 'Table2' %} +{% set content %} + + {% if setting('core.account_country') %} - + {% endif %} {% if setting('core.online_outfit') %} - + {% endif %} - - - + + + + {% set i = 0 %} {% for player in players %} {% set i = i + 1 %} - - {% if setting('core.account_country') %} - - {% endif %} - {% if setting('core.online_outfit') %} - - {% endif %} - - - + + + {% if setting('core.account_country') %} + + {% endif %} + + {% if setting('core.online_outfit') %} + + {% endif %} + + + + {% endfor %}
    ##   + OutfitOutfitNameLevelVocationName   + [sort] + Level   + [sort] + + Vocation   + [sort] + +
    {{ player.country_image|raw }}player outfit{{ player.name|raw }}{{ player.skull }}{{ player.level }}{{ player.vocation }}
    {{ player.country_image|raw }}player outfit + {{ player.name|raw }}{{ player.skull|raw }} + {{ player.level }}{{ player.vocation }}
    -{% endif %} +{% endset %} +{{ include('tables.headline.html.twig') }} diff --git a/system/templates/success.html.twig b/system/templates/success.html.twig index 5d9a9b8b..3c69dad6 100644 --- a/system/templates/success.html.twig +++ b/system/templates/success.html.twig @@ -18,13 +18,14 @@ {% else %}
    - - - + - - + + +
    +
    + + {{ csrf() }} {{ include('buttons.back.html.twig') }} -
    {% endif %} diff --git a/system/templates/tables.style.html.twig b/system/templates/tables.style.html.twig index 479db426..8aa6305b 100644 --- a/system/templates/tables.style.html.twig +++ b/system/templates/tables.style.html.twig @@ -1,6 +1,9 @@ diff --git a/system/templates/tinymce.html.twig b/system/templates/tinymce.html.twig index effea162..00f1638a 100644 --- a/system/templates/tinymce.html.twig +++ b/system/templates/tinymce.html.twig @@ -8,7 +8,7 @@ selector: "#editor", content_css: '{{ constant('ADMIN_URL') }}template/style.css', theme: "silver", - plugins: 'preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help code emoticons', + plugins: 'preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help code emoticons', toolbar1: 'formatselect | bold italic strikethrough forecolor backcolor | emoticons link | alignleft aligncenter alignright alignjustify | numlist bullist outdent indent | removeformat code', resize: 'both', image_advtab: true, @@ -23,6 +23,8 @@ {title: 'Colored Table', value: 'myaac-table'}, ], + license_key: 'gpl', + setup: function (ed) { ed.on('NodeChange', function (e) { if (ed.getContent() !== lastContent) { diff --git a/system/twig.php b/system/twig.php index f59230e3..89f25939 100644 --- a/system/twig.php +++ b/system/twig.php @@ -36,7 +36,11 @@ $twig->addExtension(new MyAAC\Twig\Extension\TypeCastingExtension()); $filter = new TwigFilter('timeago', function ($datetime) { - $time = time() - strtotime($datetime); + if (!is_int($datetime)) { + $datetime = strtotime($datetime); + } + + $time = time() - $datetime; $units = array ( 31536000 => 'year', @@ -97,6 +101,10 @@ $twig->addFunction($function); $function = new TwigFunction('hook', function ($context, $hook, array $params = []) { global $hooks; + if (config('hooks_debug')) { + note($hook); + } + if(is_string($hook)) { if (defined($hook)) { $hook = constant($hook); @@ -152,3 +160,5 @@ $twig->addFilter($filter); unset($function, $filter); $hooks->trigger(HOOK_TWIG, ['twig' => $twig, 'twig_loader' => $twig_loader]); + +$twig->addGlobal('cache', $cache); diff --git a/templates/kathrine/config.php b/templates/kathrine/config.php index 63847317..52835ff4 100644 --- a/templates/kathrine/config.php +++ b/templates/kathrine/config.php @@ -1,12 +1,18 @@ array('id' => 'news', 'name' => 'Latest News'), - MENU_CATEGORY_ACCOUNT => array('id' => 'account', 'name' => 'Account'), - MENU_CATEGORY_COMMUNITY => array('id' => 'community', 'name' => 'Community'), - MENU_CATEGORY_LIBRARY => array('id' => 'library', 'name' => 'Library'), - MENU_CATEGORY_SHOP => array('id' => 'shops', 'name' => 'Shop') -); +// max 7 menus for kathrine +$config['menu_categories'] = [ + MENU_CATEGORY_NEWS => ['id' => 'news', 'name' => 'Latest News'], + // you can add custom menu by uncommenting this + // after doing it, go to admin panel -> Menus and add your entries for this category + // tip: you can move it up/down to show it on specific position + //7 => array('id' => 'testing', 'name' => 'Test Menu 1'), + //8 => array('id' => 'testing2', 'name' => 'Test Menu 2'), + MENU_CATEGORY_ACCOUNT => ['id' => 'account', 'name' => 'Account'], + MENU_CATEGORY_COMMUNITY => ['id' => 'community', 'name' => 'Community'], + MENU_CATEGORY_LIBRARY => ['id' => 'library', 'name' => 'Library'], + MENU_CATEGORY_SHOP => ['id' => 'shops', 'name' => 'Shop'] +]; $config['menus'] = require __DIR__ . '/menus.php'; diff --git a/templates/kathrine/javascript.php b/templates/kathrine/javascript.php index a092b7c2..32fcf53d 100644 --- a/templates/kathrine/javascript.php +++ b/templates/kathrine/javascript.php @@ -1,42 +1,40 @@ -var category = ' $info) { + $templatePages = get_template_pages($id); + + if ($id == MENU_CATEGORY_ACCOUNT) { + $templatePages = array_merge($templatePages, ['account']); + } + + if (in_array($tmp[0], $templatePages)) { + echo $info['id']; + break; + } } ?>'; diff --git a/templates/kathrine/menu.js.html.twig b/templates/kathrine/menu.js.html.twig index adf7ceb0..ae8ad3bf 100644 --- a/templates/kathrine/menu.js.html.twig +++ b/templates/kathrine/menu.js.html.twig @@ -1,10 +1,10 @@ var list = new Array(); {% set i = 0 %} -{% for cat in categories %} - {% if cat.id != 'shops' or setting('core.gifts_system') %} - list[{{ i }}] = '{{ cat.id }}'; +{% for id, cat in config('menu_categories') %} + {% if (cat.id != 'shops' or setting('core.gifts_system')) and menus[id]|length > 0 %} + list[{{ i }}] = '{{ cat.id }}'; + {% set i = i + 1 %} {% endif %} -{% set i = i + 1 %} {% endfor %} function initMenu() diff --git a/templates/kathrine/style.css b/templates/kathrine/style.css index a021b1d9..cc212685 100644 --- a/templates/kathrine/style.css +++ b/templates/kathrine/style.css @@ -27,11 +27,13 @@ body #tabs { - width: 580px; + width: 99%; height: 32px; background: url('images/tabs-bg.png') no-repeat; - float: left; padding-left: 200px; + position: relative; + display: inline-flex; + right: 0; } #tabs .tab @@ -453,6 +455,27 @@ a:hover white-space: nowrap; vertical-align: top; } +.LabelV120 { + font-weight: bold; + padding-right: 10px; + white-space: nowrap; + vertical-align: top; + width: 120px; +} +.LabelV150 { + font-weight: bold; + padding-right: 10px; + white-space: nowrap; + vertical-align: top; + width: 150px; +} +.LabelV200 { + font-weight: bold; + padding-right: 10px; + white-space: nowrap; + vertical-align: top; + width: 200px; +} .LabelH { font-weight: bold; padding-right: 10px; diff --git a/templates/kathrine/template.php b/templates/kathrine/template.php index af03b3c9..ffa2195a 100644 --- a/templates/kathrine/template.php +++ b/templates/kathrine/template.php @@ -8,7 +8,9 @@ defined('MYAAC') or die('Direct access not allowed!'); @@ -28,11 +30,24 @@ defined('MYAAC') or die('Direct access not allowed!'); + + 6) { + $tabsStyle .= 'padding-left: 4px;'; + $tabsStyle .= 'padding-right: 12px;'; + } + elseif ($menusCount > 5) { + $tabsStyle .= 'padding-left: 90px;'; + } + ?> + -
    +
    $cat) { - if($id != MENU_CATEGORY_SHOP || $config['gifts_system']) { ?> + if (($id != MENU_CATEGORY_SHOP || $config['gifts_system']) && isset($menus[$id])) { ?> {% apply spaceless %}
    + {{ csrf() }}
    diff --git a/templates/tibiacom/account.management.html.twig b/templates/tibiacom/account.management.html.twig index 5125f3ed..649eba36 100644 --- a/templates/tibiacom/account.management.html.twig +++ b/templates/tibiacom/account.management.html.twig @@ -11,13 +11,14 @@ - - - + - - + + +
    +
    + + {{ csrf() }} {{ include('buttons.logout.html.twig') }} -
    @@ -59,13 +60,14 @@
    - - - + - - + + +
    +
    + + {{ csrf() }} {{ include('buttons.register_account.html.twig') }} -
    @@ -94,13 +96,14 @@
    - - - + - - + + +
    +
    + + {{ csrf() }} {{ include('buttons.edit.html.twig') }} -
    @@ -177,26 +180,29 @@ - - - + - - + + +
    +
    + + {{ csrf() }} {{ include('buttons.change_password.html.twig') }} -
    - - - + - - + + +
    +
    + + {{ csrf() }} + {{ include('buttons.change_email.html.twig') }} -
    @@ -204,13 +210,14 @@ {% if recovery_key is empty %} - - - + - - + + +
    +
    + + {{ csrf() }} {{ include('buttons.register_account.html.twig') }} -
    {% endif %} @@ -258,13 +265,14 @@ - - - + - - + + +
    +
    + + {{ csrf() }} {{ include('buttons.edit.html.twig') }} -
    @@ -398,8 +406,9 @@ - @@ -410,8 +419,9 @@
    +
    + {{ csrf() }} {{ include('buttons.create_character.html.twig') }}
    - @@ -423,8 +433,9 @@
    +
    + {{ csrf() }} {{ include('buttons.change_name.html.twig') }}
    - @@ -436,8 +447,9 @@ diff --git a/tools/basic.js b/tools/basic.js index 8fa21fa9..c5f84618 100644 --- a/tools/basic.js +++ b/tools/basic.js @@ -1,11 +1,11 @@ function MouseOverBigButton(source) { - if (source?.firstChild?.style) { - source.firstChild.style.visibility = "visible"; + if (source?.firstElementChild?.style) { + source.firstElementChild.style.visibility = "visible"; } } function MouseOutBigButton(source) { - if (source?.firstChild?.style) { - source.firstChild.style.visibility = "hidden"; + if (source?.firstElementChild?.style) { + source.firstElementChild.style.visibility = "hidden"; } } function BigButtonAction(path) { diff --git a/tools/generate_account_number.php b/tools/generate_account_number.php index a53ba9a3..643bb2c2 100644 --- a/tools/generate_account_number.php +++ b/tools/generate_account_number.php @@ -9,6 +9,8 @@ * @link https://my-aac.org */ +const IGNORE_SET_LAST_VISIT = true; + // we need some functions require '../common.php'; require SYSTEM . 'functions.php'; diff --git a/tools/signature/index.php b/tools/signature/index.php index f196781e..c791831b 100644 --- a/tools/signature/index.php +++ b/tools/signature/index.php @@ -53,6 +53,9 @@ exit; } + ensureFolderExists(SIGNATURES_CACHE); + ensureIndexExists(SIGNATURES_CACHE); + $cached = SIGNATURES_CACHE.$player->getId() . '.png'; if(file_exists($cached) && (time() < (filemtime($cached) + (60 * setting('core.signature_cache_time'))))) { diff --git a/tools/status.php b/tools/status.php index ae0ee49d..9e9c8a29 100644 --- a/tools/status.php +++ b/tools/status.php @@ -1,4 +1,7 @@
    +
    + {{ csrf() }} {{ include('buttons.change_sex.html.twig') }}
    - @@ -451,4 +463,7 @@
    +
    + {{ csrf() }} {{ include('buttons.delete_character.html.twig') }}
    {% endset %} {% include 'tables.headline.html.twig' %} -

    +
    +{{ hook('HOOK_ACCOUNT_MANAGE_AFTER_CHARACTERS') }} + +
    diff --git a/templates/tibiacom/basic.css b/templates/tibiacom/basic.css index 4633c5ed..a4f0fc24 100644 --- a/templates/tibiacom/basic.css +++ b/templates/tibiacom/basic.css @@ -1446,6 +1446,27 @@ img { white-space: nowrap; vertical-align: top; } +.LabelV120 { + font-weight: bold; + padding-right: 10px; + white-space: nowrap; + vertical-align: top; + width: 120px; +} +.LabelV150 { + font-weight: bold; + padding-right: 10px; + white-space: nowrap; + vertical-align: top; + width: 150px; +} +.LabelV200 { + font-weight: bold; + padding-right: 10px; + white-space: nowrap; + vertical-align: top; + width: 200px; +} .LabelH { font-weight: bold; padding-right: 10px; diff --git a/templates/tibiacom/boxes/templates/poll.html.twig b/templates/tibiacom/boxes/templates/poll.html.twig index 0b643ee5..25f10865 100644 --- a/templates/tibiacom/boxes/templates/poll.html.twig +++ b/templates/tibiacom/boxes/templates/poll.html.twig @@ -1,6 +1,6 @@
    {{ poll.question }}
    -
    +
    diff --git a/templates/tibiacom/index.php b/templates/tibiacom/index.php index 6d0d1e63..a6b57841 100644 --- a/templates/tibiacom/index.php +++ b/templates/tibiacom/index.php @@ -27,24 +27,18 @@ if(isset($config['boxes'])) var loginStatus=""; "; } else { window.location = ""; } } function LoginstatusTextAction(source) { - if(loginStatus == "false") { + if(loginStatus === "false") { window.location = ""; } else { window.location = ""; @@ -164,6 +158,10 @@ if(isset($config['boxes'])) function InitializeMenu() { for(menuItemName in menu[0]) { + if (!document.getElementById(menuItemName+"_Submenu")) { + continue; + } + if(menu[0][menuItemName] == "0") { document.getElementById(menuItemName+"_Submenu").style.visibility = "hidden"; document.getElementById(menuItemName+"_Submenu").style.display = "none"; @@ -222,11 +220,11 @@ if(isset($config['boxes'])) // mouse-over effects of menubuttons and submenuitems function MouseOverMenuItem(source) { - source.firstChild.style.visibility = "visible"; + source.firstElementChild.style.visibility = "visible"; } function MouseOutMenuItem(source) { - source.firstChild.style.visibility = "hidden"; + source.firstElementChild.style.visibility = "hidden"; } function MouseOverSubmenuItem(source) { @@ -332,7 +330,7 @@ if(isset($config['boxes']))
    - + $cat) { + @@ -454,7 +453,7 @@ foreach($config['menu_categories'] as $id => $cat) { foreach($config['boxes'] as $box) { /** @var string $template_name */ - $file = TEMPLATES . $template_name . '/boxes/' . $box . '.php'; + $file = __DIR__ . '/boxes/' . $box . '.php'; if(file_exists($file)) { include($file); ?>
    + {{ csrf() }} {{ include('buttons.back.html.twig') }}