diff --git a/.gitignore b/.gitignore index b198856f..15c069b1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ Thumbs.db # /.htaccess -lua +/lua # composer composer.phar diff --git a/CHANGELOG-1.x.md b/CHANGELOG-1.x.md index 17e2a06e..b8c57ce7 100644 --- a/CHANGELOG-1.x.md +++ b/CHANGELOG-1.x.md @@ -1,5 +1,40 @@ # Changelog +## [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 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/mass_account.php b/admin/pages/mass_account.php index 6b1ccb46..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 */ @@ -19,9 +20,9 @@ $title = 'Mass Account Actions'; csrfProtect(); $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; @@ -37,7 +38,7 @@ function admin_give_points($points) displayMessage($points . ' points added to all accounts.', true); } -function admin_give_coins($coins) +function admin_give_coins($coins): void { if (!HAS_ACCOUNT_COINS) { displayMessage('Coins not supported.'); @@ -52,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; @@ -63,6 +64,7 @@ function admin_give_premdays($days) $value = $days * 86400; $now = time(); + // othire if ($db->hasColumn('accounts', 'premend')) { // append premend @@ -70,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; @@ -92,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; @@ -118,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; @@ -170,7 +160,8 @@ else { )); } -function displayMessage($message, $success = false) { +function displayMessage($message, $success = false): void +{ global $twig, $hasPointsColumn, $freePremium; $success ? success($message): error($message); 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 @@ 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/tools/5-database.php b/install/tools/5-database.php index b9ff587f..6406ee48 100644 --- a/install/tools/5-database.php +++ b/install/tools/5-database.php @@ -30,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 '';")) @@ -102,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/login.php b/login.php index d4d25529..438754e2 100644 --- a/login.php +++ b/login.php @@ -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/system/compat/config.php b/system/compat/config.php index 9d58f4d6..4fc3004a 100644 --- a/system/compat/config.php +++ b/system/compat/config.php @@ -81,6 +81,7 @@ $deprecatedConfig = [ 'account_change_character_name_points' => '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 8c365391..582f9c30 100644 --- a/system/functions.php +++ b/system/functions.php @@ -1142,10 +1142,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() @@ -1632,13 +1640,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); } @@ -1652,7 +1661,7 @@ function getGuildNameById($id) return false; } -function getGuildLogoById($id) +function getGuildLogoById($id): string { $logo = 'default.gif'; @@ -1668,7 +1677,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', [ @@ -1696,6 +1706,12 @@ function getAccountIdentityColumn(): string return 'id'; } +function isCanary(): bool +{ + $vipSystemEnabled = configLua('vipSystemEnabled'); + return isset($vipSystemEnabled); +} + // validator functions require_once SYSTEM . 'compat/base.php'; diff --git a/system/libs/pot/OTS_Account.php b/system/libs/pot/OTS_Account.php index 20c7fe32..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,37 +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(); - } - - return $this->data['lastday']; - } - - public function isPremium() - { - if(isset($this->data['premium_ends_at'])) { - return $this->data['premium_ends_at'] > time(); + { + if( !isset($this->data['lastday']) ) + { + throw new E_OTS_NotLoaded(); } - if(isset($this->data['premend'])) { - return $this->data['premend'] > time(); + return $this->data['lastday']; + } + + 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($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. @@ -501,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. @@ -541,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. @@ -562,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. @@ -582,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. @@ -605,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. * @@ -620,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. @@ -639,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. @@ -662,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. @@ -695,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 @@ -716,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. @@ -752,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; @@ -772,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 @@ -786,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 @@ -809,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 @@ -830,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(); @@ -883,7 +886,7 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable $this->data['banned'] = false; $this->data['banned_time'] = 0; } - } + } /** * Deletes account. @@ -897,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. @@ -917,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() { @@ -982,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) { @@ -1039,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. @@ -1053,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. @@ -1069,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. @@ -1119,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. @@ -1177,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 68f34787..8b9626d9 100644 --- a/system/libs/pot/OTS_DB_MySQL.php +++ b/system/libs/pot/OTS_DB_MySQL.php @@ -120,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); + } } } @@ -156,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); } } @@ -295,7 +302,8 @@ class OTS_DB_MySQL extends OTS_Base_DB return []; } - public function revalidateCache() { + public function revalidateCache(): void + { foreach($this->has_table_cache as $key => $value) { $this->hasTableInternal($key); } @@ -310,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/locale/de/install.php b/system/locale/de/install.php index 927da34b..351f6009 100644 --- a/system/locale/de/install.php +++ b/system/locale/de/install.php @@ -78,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 376e82c3..c623035c 100644 --- a/system/locale/en/install.php +++ b/system/locale/en/install.php @@ -83,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 159a7d8f..a8ea0cf6 100644 --- a/system/locale/pl/install.php +++ b/system/locale/pl/install.php @@ -81,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/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 7a0d3c56..9ffdf6f7 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'; @@ -244,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( 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/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/guilds/create.php b/system/pages/guilds/create.php index ef0117e8..08bc8817 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)) || ($logged && $account_logged->getPremDays() == OTS_Account::GRATIS_PREMIUM_DAYS); + $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/router.php b/system/router.php index 9c9ce4ad..f876a9b2 100644 --- a/system/router.php +++ b/system/router.php @@ -88,8 +88,10 @@ 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]; } @@ -165,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']; } @@ -198,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', @@ -212,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']; } @@ -221,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 { diff --git a/system/src/Cache/Cache.php b/system/src/Cache/Cache.php index 53a84530..1b7d284b 100644 --- a/system/src/Cache/Cache.php +++ b/system/src/Cache/Cache.php @@ -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/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('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 0c8aa325..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) { 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/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 f0bd435f..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,32 +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 { - if(isset($this->premium_ends_at)) { - return $this->premium_ends_at > time(); + 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->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 @@ +Current Password: - + + + {{ hook('HOOK_ACCOUNT_CHANGE_PASSWORD_AFTER_OLD_PASSWORD') }} + New Password: - + + + {{ hook('HOOK_ACCOUNT_CHANGE_PASSWORD_AFTER_NEW_PASSWORD') }} + New Password Again: - + 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/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/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.html.twig b/system/templates/online.html.twig index bb6c786c..6f798740 100644 --- a/system/templates/online.html.twig +++ b/system/templates/online.html.twig @@ -101,7 +101,7 @@ Location Datacenter: - {{ setting('core.online_datacenter') }} (Server date & time: - {{ "now"|date("d/m/Y H:i:s") }}) + {{ setting('core.online_datacenter')|raw }} (Server date & time: - {{ "now"|date("d/m/Y H:i:s") }}) PvP Type: diff --git a/system/twig.php b/system/twig.php index ecc7a957..89f25939 100644 --- a/system/twig.php +++ b/system/twig.php @@ -101,7 +101,9 @@ $twig->addFunction($function); $function = new TwigFunction('hook', function ($context, $hook, array $params = []) { global $hooks; - //note($hook); + if (config('hooks_debug')) { + note($hook); + } if(is_string($hook)) { if (defined($hook)) { diff --git a/templates/tibiacom/index.php b/templates/tibiacom/index.php index 1bfe6164..a6b57841 100644 --- a/templates/tibiacom/index.php +++ b/templates/tibiacom/index.php @@ -27,26 +27,18 @@ if(isset($config['boxes'])) var loginStatus="";