diff --git a/.editorconfig b/.editorconfig index 23cc4257..2ec19b78 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,5 +12,8 @@ insert_final_newline = true [*.md] trim_trailing_whitespace = false -[composer.json] -indent_style = space \ No newline at end of file +[{composer.json,package.json}] +indent_style = space + +[package.json] +indent_size = 2 \ No newline at end of file diff --git a/.github/workflows/phplint.yml b/.github/workflows/phplint.yml new file mode 100644 index 00000000..a9c0d700 --- /dev/null +++ b/.github/workflows/phplint.yml @@ -0,0 +1,13 @@ +name: PHP Linting +on: + pull_request: + branches: [master, develop] + push: + branches: [master] + +jobs: + phplint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: michaelw90/PHP-Lint@master diff --git a/.gitignore b/.gitignore index 3571a757..05318d06 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,19 @@ Thumbs.db .DS_Store .idea -tmp # composer composer.lock vendor +# npm +node_modules + +# created by release.sh releases +tmp + config.local.php -PERSONAL_NOTES # all custom templates templates/* @@ -20,6 +24,10 @@ templates/* images/guilds/* !images/guilds/default.gif +# editor images +images/editor/* +!images/editor/index.html + # cache system/cache/* !system/cache/index.html @@ -35,16 +43,21 @@ system/logs/* system/data/* !system/data/index.html +# php sessions +system/php_sessions/* +!system/php_sessions/index.html + # plugins plugins/* !plugins/.htaccess !plugins/example.json !plugins/account-create-hint.json !plugins/account-create-hint +!plugins/email-confirmed-reward.json +!plugins/email-confirmed-reward !plugins/example-settings-plugin.json !plugins/example-settings-plugin landing -/login.php # system system/functions_custom.php diff --git a/.htaccess b/.htaccess index 9d8bd781..042cb23a 100644 --- a/.htaccess +++ b/.htaccess @@ -9,8 +9,6 @@ RewriteEngine On - # you can put here your myaac root folder - # path relative to web root #RewriteBase /myaac/ RewriteCond %{REQUEST_FILENAME} !-f diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt new file mode 100644 index 00000000..dd7aafdd --- /dev/null +++ b/CONTRIBUTORS.txt @@ -0,0 +1,14 @@ +# automatically exported using this script: +# git log --all --format='%cN <%cE>' | sort -u > contributors +# in no particular order +# cleaned for readability + +Evil Puncker +Fernando Matos +Lee <42119604+Leesneaks@users.noreply.github.com> +caio +slawkens +tobi132 <52947952+tobi132@users.noreply.github.com> +vankk +whiteblXK +xitobuh diff --git a/CREDITS b/CREDITS index 50461327..404de30c 100644 --- a/CREDITS +++ b/CREDITS @@ -1,2 +1,3 @@ * Gesior.pl (2007 - 2008) -* Slawkens (2009 - 2020) +* Slawkens (2009 - 2022) +* Contributors listed in CONTRIBUTORS.txt diff --git a/README.md b/README.md index 4cfe40ce..6fabc570 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# MyAAC +# [MyAAC](https://my-aac.org) [![Build Status Master](https://img.shields.io/travis/slawkens/myaac/master)](https://travis-ci.org/github/slawkens/myaac) [![License: GPL-3.0](https://img.shields.io/github/license/slawkens/myaac)](https://opensource.org/licenses/gpl-license) @@ -11,7 +11,7 @@ MyAAC is a free and open-source Automatic Account Creator (AAC) written in PHP. Official website: https://my-aac.org -### REQUIREMENTS +### Requirements - PHP 5.6 or later - MySQL database @@ -20,7 +20,7 @@ Official website: https://my-aac.org - ZIP PHP Extension - (optional) mod_rewrite to use friendly_urls -### INSTALLATION AND CONFIGURATION +### Installation Just decompress and untar the source (which you should have done by now, if you're reading this), into your webserver's document root. @@ -40,15 +40,40 @@ Official website: https://my-aac.org Visit http://your_domain/install (http://localhost/install) and follow instructions in the browser. -### KNOWN PROBLEMS +### Configuration - - none - +Check *config.php* to get more informations. +Use *config.local.php* for your local configuration changes. -### OTHER NOTES +### Branches + +This repository follows the Git Flow Workflow. +Cheatsheet: [Git-Flow-Cheetsheet](https://danielkummer.github.io/git-flow-cheatsheet) + +That means, we use: +* master branch, for current stable release +* develop branch, for development version (next release) +* feature branches, for features etc. + +### Known Problems + +- Some compatibility issues with some exotical distibutions. + +### Contributing + +Contributions are more than welcome. + +Pull requests should be made to the *develop* branch as that is the working branch, master is for release code. + +Bug fixes to current release should be done to master branch. + +Look: [Contributing](https://github.com/otsoft/myaac/wiki/Contributing) in our wiki. + +### Other Notes If you have a great idea or want contribute to the project - visit our website at https://www.my-aac.org -### LICENSING +### License - This program and all associated files are released under the GNU Public - License, see LICENSE for details. +This program and all associated files are released under the GNU Public License. +See [LICENSE](https://github.com/slawkens/myaac/blob/master/LICENSE) for details. diff --git a/VERSION b/VERSION deleted file mode 100644 index 16d5c109..00000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.9.0-dev \ No newline at end of file diff --git a/admin/index.php b/admin/index.php index c47ac1d6..bb269f3d 100644 --- a/admin/index.php +++ b/admin/index.php @@ -1,9 +1,10 @@ load(); require SYSTEM . 'status.php'; require SYSTEM . 'login.php'; require SYSTEM . 'migrate.php'; -require ADMIN . 'includes/functions.php'; +require __DIR__ . '/includes/functions.php'; $twig->addGlobal('config', $config); $twig->addGlobal('status', $status); +if (ACTION == 'logout') { + require SYSTEM . 'logout.php'; +} + // if we're not logged in - show login box if(!$logged || !admin()) { $page = 'login'; } // include our page -$file = SYSTEM . 'pages/admin/' . $page . '.php'; +$file = __DIR__ . '/pages/' . $page . '.php'; if(!@file_exists($file)) { - $page = '404'; - $file = SYSTEM . 'pages/404.php'; + if (strpos($page, 'plugins/') !== false) { + $file = BASE . $page; + } + else { + $page = '404'; + $file = SYSTEM . 'pages/404.php'; + } } ob_start(); -include($file); +if($hooks->trigger(HOOK_ADMIN_BEFORE_PAGE)) { + require $file; +} $content .= ob_get_contents(); ob_end_clean(); // template $template_path = 'template/'; -require ADMIN . $template_path . 'template.php'; -?> +require __DIR__ . '/' . $template_path . 'template.php'; diff --git a/system/pages/admin/accounts.php b/admin/pages/accounts.php similarity index 98% rename from system/pages/admin/accounts.php rename to admin/pages/accounts.php index 8ef7de58..e481c782 100644 --- a/system/pages/admin/accounts.php +++ b/admin/pages/accounts.php @@ -31,8 +31,8 @@ if ($config['account_country']) { foreach ($config['countries'] as $code => $c) $countries[$code] = $c; } -$web_acc = array("None", "Admin", "Super Admin", "(Admin + Super Admin)"); -$acc_type = array("None", "Normal", "Tutor", "Senior Tutor", "Gamemaster", "God"); +$web_acc = ACCOUNT_WEB_FLAGS; +$acc_type = config('account_types'); ?> @@ -70,7 +70,6 @@ else if (isset($_REQUEST['search'])) { ?>
0) { $account = new OTS_Account(); $account->load($id); @@ -186,8 +185,7 @@ else if (isset($_REQUEST['search'])) { $account->setCustomField('web_lastlogin', $web_lastlogin); if (isset($password)) { - $config_salt_enabled = $db->hasColumn('accounts', 'salt'); - if ($config_salt_enabled) { + if (USE_ACCOUNT_SALT) { $salt = generateRandomString(10, false, true, true); $password = $salt . $password; $account->setCustomField('salt', $salt); @@ -196,7 +194,7 @@ else if (isset($_REQUEST['search'])) { $password = encrypt($password); $account->setPassword($password); - if ($config_salt_enabled) + if (USE_ACCOUNT_SALT) $account->setCustomField('salt', $salt); } diff --git a/system/pages/admin/changelog.php b/admin/pages/changelog.php similarity index 82% rename from system/pages/admin/changelog.php rename to admin/pages/changelog.php index a6e7d01c..4456344d 100644 --- a/system/pages/admin/changelog.php +++ b/admin/pages/changelog.php @@ -17,18 +17,18 @@ if (!hasFlag(FLAG_CONTENT_PAGES) && !superAdmin()) { $title = 'Changelog'; $use_datatable = true; -define('CL_LIMIT', 600); // maximum changelog body length +const CL_LIMIT = 600; // maximum changelog body length ?> orderBy('group_id', POT::ORDER_DESC); $twig->display('admin.changelog.form.html.twig', array( 'action' => $action, - 'cl_link_form' => constant('ADMIN_URL').'?p=changelog&action=' . ($action == 'edit' ? 'edit' : 'add'), - 'cl_id' => isset($id) ? $id : null, - 'body' => isset($body) ? htmlentities($body, ENT_COMPAT, 'UTF-8') : '', - 'create_date' => isset($create_date) ? $create_date : '', - 'player' => isset($player) && $player->isLoaded() ? $player : null, - 'player_id' => isset($player_id) ? $player_id : null, + 'cl_link_form' => constant('ADMIN_URL').'?p=changelog&action=' . ($action == 'edit' ? 'edit' : 'new'), + 'cl_id' => $id ?? null, + 'body' => isset($body) ? escapeHtml($body) : '', + 'create_date' => $create_date ?? '', + 'player_id' => $player_id ?? null, 'account_players' => $account_players, - 'type' => isset($type) ? $type : 0, - 'where' => isset($where) ? $where : 0, + 'type' => $type ?? 0, + 'where' => $where ?? 0, 'log_type' => $log_type, 'log_where' => $log_where, )); diff --git a/system/pages/admin/clmd.php b/admin/pages/clmd.php similarity index 100% rename from system/pages/admin/clmd.php rename to admin/pages/clmd.php diff --git a/system/pages/admin/dashboard.php b/admin/pages/dashboard.php similarity index 94% rename from system/pages/admin/dashboard.php rename to admin/pages/dashboard.php index ba0ba8a0..53380503 100644 --- a/system/pages/admin/dashboard.php +++ b/admin/pages/dashboard.php @@ -47,10 +47,6 @@ $tmp = ''; if (fetchDatabaseConfig('site_closed_message', $tmp)) $closed_message = $tmp; -echo '
'; -$twig->display('admin.dashboard.html.twig', array()); -echo '
'; - $configAdminPanelModules = config('admin_panel_modules'); if (isset($configAdminPanelModules)) { echo '
'; @@ -63,4 +59,4 @@ if (isset($configAdminPanelModules)) { } } echo '
'; -} \ No newline at end of file +} diff --git a/system/pages/admin/data.php b/admin/pages/data.php similarity index 100% rename from system/pages/admin/data.php rename to admin/pages/data.php diff --git a/system/pages/admin/index.html b/admin/pages/index.html similarity index 100% rename from system/pages/admin/index.html rename to admin/pages/index.html diff --git a/admin/pages/login.php b/admin/pages/login.php new file mode 100644 index 00000000..32d8e8b0 --- /dev/null +++ b/admin/pages/login.php @@ -0,0 +1,24 @@ + + * @copyright 2019 MyAAC + * @link https://my-aac.org + */ +defined('MYAAC') or die('Direct access not allowed!'); +$title = 'Login'; + +require PAGES . 'account/login.php'; +if ($logged) { + header('Location: ' . ADMIN_URL); + return; +} + +$twig->display('admin.login.html.twig', [ + 'logout' => (ACTION == 'logout' ? 'You have been logged out!' : ''), + 'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number', + 'account_login_by' => getAccountLoginByLabel(), + 'errors' => $errors ?? '' +]); diff --git a/system/pages/admin/logs.php b/admin/pages/logs.php similarity index 98% rename from system/pages/admin/logs.php rename to admin/pages/logs.php index b02cc8fc..71b57c7f 100644 --- a/system/pages/admin/logs.php +++ b/admin/pages/logs.php @@ -9,6 +9,7 @@ */ defined('MYAAC') or die('Direct access not allowed!'); $title = 'Logs Viewer'; +$use_datatable = true; $files = array(); $aac_path_logs = BASE . 'system/logs/'; diff --git a/system/pages/admin/mailer.php b/admin/pages/mailer.php similarity index 100% rename from system/pages/admin/mailer.php rename to admin/pages/mailer.php diff --git a/admin/pages/mass_account.php b/admin/pages/mass_account.php new file mode 100644 index 00000000..507ef877 --- /dev/null +++ b/admin/pages/mass_account.php @@ -0,0 +1,215 @@ + + * @author Lee + * @copyright 2020 MyAAC + * @link https://my-aac.org + */ +defined('MYAAC') or die('Direct access not allowed!'); + +$title = 'Mass Account Actions'; + +$hasCoinsColumn = $db->hasColumn('accounts', 'coins'); +$hasPointsColumn = $db->hasColumn('accounts', 'premium_points'); +$freePremium = $config['lua']['freePremium']; + +function admin_give_points($points) +{ + global $db, $hasPointsColumn; + + if (!$hasPointsColumn) { + displayMessage('Points not supported.'); + return; + } + + $statement = $db->prepare('UPDATE `accounts` SET `premium_points` = `premium_points` + :points'); + if (!$statement) { + displayMessage('Failed to prepare query statement.'); + return; + } + + if (!$statement->execute([ + 'points' => $points + ])) { + displayMessage('Failed to add points.'); + return; + } + displayMessage($points . ' points added to all accounts.', true); +} + +function admin_give_coins($coins) +{ + global $db, $hasCoinsColumn; + + if (!$hasCoinsColumn) { + displayMessage('Coins not supported.'); + return; + } + + $statement = $db->prepare('UPDATE `accounts` SET `coins` = `coins` + :coins'); + if (!$statement) { + displayMessage('Failed to prepare query statement.'); + return; + } + + if (!$statement->execute([ + 'coins' => $coins + ])) { + displayMessage('Failed to add coins.'); + return; + } + + displayMessage($coins . ' coins added to all accounts.', true); +} + +function query_add_premium($column, $value_query, $condition_query = '1=1', $params = []) +{ + global $db; + + $statement = $db->prepare("UPDATE `accounts` SET `{$column}` = $value_query WHERE $condition_query"); + if (!$statement) { + displayMessage('Failed to prepare query statement.'); + return false; + } + + if (!$statement->execute($params)) { + displayMessage('Failed to add premium days.'); + return false; + } + + return true; +} + +function admin_give_premdays($days) +{ + global $db, $freePremium; + + if ($freePremium) { + displayMessage('Premium days not supported. Free Premium enabled.'); + return; + } + + $value = $days * 86400; + $now = time(); + // othire + if ($db->hasColumn('accounts', 'premend')) { + // append premend + if (query_add_premium('premend', '`premend` + :value', '`premend` > :now', ['value' => $value, 'now' => $now])) { + // set premend + if (query_add_premium('premend', ':value', '`premend` <= :now', ['value' => $now + $value, 'now' => $now])) { + 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; + } + + // tfs 0.x + if ($db->hasColumn('accounts', 'premdays')) { + // append premdays + if (query_add_premium('premdays', '`premdays` + :value', '1=1', ['value' => $days])) { + // append lastday + if (query_add_premium('lastday', '`lastday` + :value', '`lastday` > :now', ['value' => $value, 'now' => $now])) { + // set lastday + if (query_add_premium('lastday', ':value', '`lastday` <= :now', ['value' => $now + $value, 'now' => $now])) { + 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; + } + + // tfs 1.x + if ($db->hasColumn('accounts', 'premium_ends_at')) { + // append premium_ends_at + if (query_add_premium('premium_ends_at', '`premium_ends_at` + :value', '`premium_ends_at` > :now', ['value' => $value, 'now' => $now])) { + // set premium_ends_at + if (query_add_premium('premium_ends_at', ':value', '`premium_ends_at` <= :now', ['value' => $now + $value, 'now' => $now])) { + 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; + } + + displayMessage('Premium Days not supported.'); +} + +if (isset($_POST['action']) && $_POST['action']) { + + $action = $_POST['action']; + + if (preg_match("/[^A-z0-9_\-]/", $action)) { + displayMessage('Invalid action.'); + } else { + $value = isset($_POST['value']) ? intval($_POST['value']) : 0; + + if (!$value) { + displayMessage('Please fill all inputs'); + } else { + switch ($action) { + case 'give-points': + admin_give_points($value); + break; + case 'give-coins': + admin_give_coins($value); + break; + case 'give-premdays': + admin_give_premdays($value); + break; + default: + displayMessage('Action ' . $action . 'not found.'); + } + } + } +} +else { + $twig->display('admin.tools.account.html.twig', array( + 'hasCoinsColumn' => $hasCoinsColumn, + 'hasPointsColumn' => $hasPointsColumn, + 'freePremium' => $freePremium, + )); +} + +function displayMessage($message, $success = false) { + global $twig, $hasCoinsColumn, $hasPointsColumn, $freePremium; + + $success ? success($message): error($message); + + $twig->display('admin.tools.account.html.twig', array( + 'hasCoinsColumn' => $hasCoinsColumn, + 'hasPointsColumn' => $hasPointsColumn, + 'freePremium' => $freePremium, + )); +} diff --git a/admin/pages/mass_teleport.php b/admin/pages/mass_teleport.php new file mode 100644 index 00000000..972975e2 --- /dev/null +++ b/admin/pages/mass_teleport.php @@ -0,0 +1,116 @@ + + * @author Lee + * @copyright 2020 MyAAC + * @link https://my-aac.org + */ +defined('MYAAC') or die('Direct access not allowed!'); + +$title = 'Mass Teleport Actions'; + +function admin_teleport_position($x, $y, $z) { + global $db; + $statement = $db->prepare('UPDATE `players` SET `posx` = :x, `posy` = :y, `posz` = :z'); + if (!$statement) { + displayMessage('Failed to prepare query statement.'); + return; + } + + if (!$statement->execute([ + 'x' => $x, 'y' => $y, 'z' => $z + ])) { + displayMessage('Failed to execute query.'); + return; + } + + displayMessage('Player\'s position updated.', true); +} + +function admin_teleport_town($town_id) { + global $db; + $statement = $db->prepare('UPDATE `players` SET `town_id` = :town_id'); + if (!$statement) { + displayMessage('Failed to prepare query statement.'); + return; + } + + if (!$statement->execute([ + 'town_id' => $town_id + ])) { + displayMessage('Failed to execute query.'); + return; + } + + displayMessage('Player\'s town updated.', true); +} + +if (isset($_POST['action']) && $_POST['action']) { + + $action = $_POST['action']; + + if (preg_match("/[^A-z0-9_\-]/", $action)) { + displayMessage('Invalid action.'); + } else { + + $playersOnline = 0; + if($db->hasTable('players_online')) {// tfs 1.0 + $query = $db->query('SELECT count(*) AS `count` FROM `players_online`'); + } else { + $query = $db->query('SELECT count(*) AS `count` FROM `players` WHERE `players`.`online` > 0'); + } + + $playersOnline = $query->fetch(PDO::FETCH_ASSOC); + if ($playersOnline['count'] > 0) { + displayMessage('Please, close the server before execute this action otherwise players will not be affected.'); + return; + } + + $town_id = isset($_POST['town_id']) ? intval($_POST['town_id']) : null; + $posx = isset($_POST['posx']) ? intval($_POST['posx']) : null; + $posy = isset($_POST['posy']) ? intval($_POST['posy']) : null; + $posz = isset($_POST['posz']) ? intval($_POST['posz']) : null; + $to_temple = $_POST['to_temple'] ?? null; + + switch ($action) { + case 'set-town': + if (!$town_id) { + displayMessage('Please fill all inputs'); + return; + } + + if (!isset($config['towns'][$town_id])) { + displayMessage('Specified town does not exist'); + return; + } + + admin_teleport_town($town_id); + break; + case 'set-position': + if (!$to_temple && ($posx < 0 || $posx > 65535 || $posy < 0 || $posy > 65535|| $posz < 0 || $posz > 16)) { + displayMessage('Invalid Position'); + return; + } + + admin_teleport_position($posx, $posy, $posz); + break; + default: + displayMessage('Action ' . $action . 'not found.'); + } + } + +} +else { + $twig->display('admin.tools.teleport.html.twig', array()); +} + + +function displayMessage($message, $success = false) { + global $twig; + + $success ? success($message): error($message); + $twig->display('admin.tools.teleport.html.twig', array()); +} diff --git a/system/pages/admin/menus.php b/admin/pages/menus.php similarity index 98% rename from system/pages/admin/menus.php rename to admin/pages/menus.php index e6b033ea..20045528 100644 --- a/system/pages/admin/menus.php +++ b/admin/pages/menus.php @@ -93,7 +93,7 @@ if (isset($_REQUEST['template'])) { if (isset($menus[$id])) { foreach ($menus[$id] as $i => $menu): ?> -
  • +
  • diff --git a/system/pages/admin/modules/balance.php b/admin/pages/modules/balance.php similarity index 100% rename from system/pages/admin/modules/balance.php rename to admin/pages/modules/balance.php diff --git a/system/pages/admin/modules/coins.php b/admin/pages/modules/coins.php similarity index 100% rename from system/pages/admin/modules/coins.php rename to admin/pages/modules/coins.php diff --git a/system/pages/admin/modules/created.php b/admin/pages/modules/created.php similarity index 100% rename from system/pages/admin/modules/created.php rename to admin/pages/modules/created.php diff --git a/system/pages/admin/modules/index.html b/admin/pages/modules/index.html similarity index 100% rename from system/pages/admin/modules/index.html rename to admin/pages/modules/index.html diff --git a/system/pages/admin/modules/lastlogin.php b/admin/pages/modules/lastlogin.php similarity index 100% rename from system/pages/admin/modules/lastlogin.php rename to admin/pages/modules/lastlogin.php diff --git a/system/pages/admin/modules/points.php b/admin/pages/modules/points.php similarity index 100% rename from system/pages/admin/modules/points.php rename to admin/pages/modules/points.php diff --git a/system/pages/admin/modules/server_status.php b/admin/pages/modules/server_status.php similarity index 100% rename from system/pages/admin/modules/server_status.php rename to admin/pages/modules/server_status.php diff --git a/system/pages/admin/modules/statistics.php b/admin/pages/modules/statistics.php similarity index 100% rename from system/pages/admin/modules/statistics.php rename to admin/pages/modules/statistics.php diff --git a/system/pages/admin/modules/templates/balance.html.twig b/admin/pages/modules/templates/balance.html.twig similarity index 100% rename from system/pages/admin/modules/templates/balance.html.twig rename to admin/pages/modules/templates/balance.html.twig diff --git a/system/pages/admin/modules/templates/coins.html.twig b/admin/pages/modules/templates/coins.html.twig similarity index 100% rename from system/pages/admin/modules/templates/coins.html.twig rename to admin/pages/modules/templates/coins.html.twig diff --git a/system/pages/admin/modules/templates/created.html.twig b/admin/pages/modules/templates/created.html.twig similarity index 100% rename from system/pages/admin/modules/templates/created.html.twig rename to admin/pages/modules/templates/created.html.twig diff --git a/system/pages/admin/modules/templates/index.html b/admin/pages/modules/templates/index.html similarity index 100% rename from system/pages/admin/modules/templates/index.html rename to admin/pages/modules/templates/index.html diff --git a/system/pages/admin/modules/templates/lastlogin.html.twig b/admin/pages/modules/templates/lastlogin.html.twig similarity index 100% rename from system/pages/admin/modules/templates/lastlogin.html.twig rename to admin/pages/modules/templates/lastlogin.html.twig diff --git a/system/pages/admin/modules/templates/points.html.twig b/admin/pages/modules/templates/points.html.twig similarity index 100% rename from system/pages/admin/modules/templates/points.html.twig rename to admin/pages/modules/templates/points.html.twig diff --git a/system/pages/admin/modules/templates/statistics.html.twig b/admin/pages/modules/templates/statistics.html.twig similarity index 100% rename from system/pages/admin/modules/templates/statistics.html.twig rename to admin/pages/modules/templates/statistics.html.twig diff --git a/system/pages/admin/modules/templates/web_status.twig b/admin/pages/modules/templates/web_status.twig similarity index 100% rename from system/pages/admin/modules/templates/web_status.twig rename to admin/pages/modules/templates/web_status.twig diff --git a/system/pages/admin/modules/web_status.php b/admin/pages/modules/web_status.php similarity index 100% rename from system/pages/admin/modules/web_status.php rename to admin/pages/modules/web_status.php diff --git a/system/pages/admin/news.php b/admin/pages/news.php similarity index 84% rename from system/pages/admin/news.php rename to admin/pages/news.php index 46f30c85..66398b09 100644 --- a/system/pages/admin/news.php +++ b/admin/pages/news.php @@ -23,8 +23,8 @@ if (!hasFlag(FLAG_CONTENT_PAGES) && !superAdmin()) { header('X-XSS-Protection:0'); // some constants, used mainly by database (cannot by modified without schema changes) -define('TITLE_LIMIT', 100); -define('BODY_LIMIT', 65535); // maximum news body length +define('NEWS_TITLE_LIMIT', 100); +define('NEWS_BODY_LIMIT', 65535); // maximum news body length define('ARTICLE_TEXT_LIMIT', 300); define('ARTICLE_IMAGE_LIMIT', 100); @@ -43,12 +43,12 @@ if(!empty($action)) $forum_section = isset($_REQUEST['forum_section']) ? $_REQUEST['forum_section'] : null; $errors = array(); - if($action == 'add') { + if($action == 'new') { if(isset($forum_section) && $forum_section != '-1') { $forum_add = Forum::add_thread($p_title, $body, $forum_section, $player_id, $account_logged->getId(), $errors); } - if(News::add($p_title, $body, $type, $category, $player_id, isset($forum_add) && $forum_add != 0 ? $forum_add : 0, $article_text, $article_image, $errors)) { + if(isset($p_title) && News::add($p_title, $body, $type, $category, $player_id, isset($forum_add) && $forum_add != 0 ? $forum_add : 0, $article_text, $article_image, $errors)) { $p_title = $body = $comments = $article_text = $article_image = ''; $type = $category = $player_id = 0; @@ -115,21 +115,21 @@ if($action == 'edit' || $action == 'new') { $twig->display('admin.news.form.html.twig', array( 'action' => $action, 'news_link' => getLink(PAGE), - 'news_link_form' => '?p=news&action=' . ($action == 'edit' ? 'edit' : 'add'), - 'news_id' => isset($id) ? $id : null, - 'title' => isset($p_title) ? $p_title : '', - 'body' => isset($body) ? htmlentities($body, ENT_COMPAT, 'UTF-8') : '', - 'type' => isset($type) ? $type : null, + 'news_link_form' => '?p=news&action=' . ($action == 'edit' ? 'edit' : 'new'), + 'news_id' => $id ?? null, + 'title' => $p_title ?? '', + 'body' => isset($body) ? escapeHtml($body) : '', + 'type' => $type ?? null, 'player' => isset($player) && $player->isLoaded() ? $player : null, - 'player_id' => isset($player_id) ? $player_id : null, + 'player_id' => $player_id ?? null, 'account_players' => $account_players, - 'category' => isset($category) ? $category : 0, + 'category' => $category ?? 0, 'categories' => $categories, 'forum_boards' => getForumBoards(), - 'forum_section' => isset($forum_section) ? $forum_section : null, - 'comments' => isset($comments) ? $comments : null, - 'article_text' => isset($article_text) ? $article_text : null, - 'article_image' => isset($article_image) ? $article_image : null + 'forum_section' => $forum_section ?? null, + 'comments' => $comments ?? null, + 'article_text' => $article_text ?? null, + 'article_image' => $article_image ?? null )); } diff --git a/system/pages/admin/notepad.php b/admin/pages/notepad.php similarity index 100% rename from system/pages/admin/notepad.php rename to admin/pages/notepad.php diff --git a/system/pages/account.php b/admin/pages/open_source.php similarity index 51% rename from system/pages/account.php rename to admin/pages/open_source.php index 31366187..de6e1e01 100644 --- a/system/pages/account.php +++ b/admin/pages/open_source.php @@ -1,16 +1,14 @@ - * @copyright 2019 MyAAC + * @copyright 2023 MyAAC * @link https://my-aac.org */ defined('MYAAC') or die('Direct access not allowed!'); -if($action == 'confirm_email') { - require_once PAGES . 'account/confirm_email.php'; -} -?> +$title = 'Open Source'; + +$twig->display('admin.open_source.html.twig'); diff --git a/system/pages/admin/pages.php b/admin/pages/pages.php similarity index 58% rename from system/pages/admin/pages.php rename to admin/pages/pages.php index 50ae59f4..8e61224b 100644 --- a/system/pages/admin/pages.php +++ b/admin/pages/pages.php @@ -9,6 +9,7 @@ */ defined('MYAAC') or die('Direct access not allowed!'); $title = 'Pages'; +$use_datatable = true; if (!hasFlag(FLAG_CONTENT_PAGES) && !superAdmin()) { echo 'Access denied.'; @@ -17,13 +18,18 @@ if (!hasFlag(FLAG_CONTENT_PAGES) && !superAdmin()) { header('X-XSS-Protection:0'); -$name = $p_title = ''; +$name = $p_title = null; $groups = new OTS_Groups_List(); $php = false; $enable_tinymce = true; $access = 0; +// some constants, used mainly by database (cannot by modified without schema changes) +define('PAGE_TITLE_LIMIT', 30); +define('PAGE_NAME_LIMIT', 30); +define('PAGE_BODY_LIMIT', 65535); // maximum page body length + if (!empty($action)) { if ($action == 'delete' || $action == 'edit' || $action == 'hide') $id = $_REQUEST['id']; @@ -49,12 +55,13 @@ if (!empty($action)) { $errors = array(); $player_id = 1; - if ($action == 'add') { - if (Pages::add($name, $p_title, $body, $player_id, $php, $enable_tinymce, $access, $errors)) { + if ($action == 'new') { + if (isset($p_title) && Pages::add($name, $p_title, $body, $player_id, $php, $enable_tinymce, $access, $errors)) { $name = $p_title = $body = ''; $player_id = $access = 0; $php = false; $enable_tinymce = true; + success('Added successful.'); } } else if ($action == 'delete') { if (Pages::delete($id, $errors)) @@ -69,15 +76,18 @@ if (!empty($action)) { $enable_tinymce = $_page['enable_tinymce'] == '1'; $access = $_page['access']; } else { - Pages::update($id, $name, $p_title, $body, $player_id, $php, $enable_tinymce, $access); - $action = $name = $p_title = $body = ''; - $player_id = 1; - $access = 0; - $php = false; - $enable_tinymce = true; + if(Pages::update($id, $name, $p_title, $body, $player_id, $php, $enable_tinymce, $access)) { + $action = $name = $p_title = $body = ''; + $player_id = 1; + $access = 0; + $php = false; + $enable_tinymce = true; + success("Updated successful."); + } } } else if ($action == 'hide') { - Pages::toggleHidden($id, $errors); + Pages::toggleHidden($id, $errors, $status); + success(($status == 1 ? 'Show' : 'Hide') . " successful."); } if (!empty($errors)) @@ -105,7 +115,7 @@ $twig->display('admin.pages.form.html.twig', array( 'title' => $p_title, 'php' => $php, 'enable_tinymce' => $enable_tinymce, - 'body' => isset($body) ? htmlentities($body, ENT_COMPAT, 'UTF-8') : '', + 'body' => isset($body) ? escapeHtml($body) : '', 'groups' => $groups->getGroups(), 'access' => $access )); @@ -116,6 +126,44 @@ $twig->display('admin.pages.html.twig', array( class Pages { + static public function verify($name, $title, $body, $player_id, $php, $enable_tinymce, $access, &$errors) + { + if(!isset($title[0]) || !isset($body[0])) { + $errors[] = 'Please fill all inputs.'; + return false; + } + if(strlen($name) > PAGE_NAME_LIMIT) { + $errors[] = 'Page name cannot be longer than ' . PAGE_NAME_LIMIT . ' characters.'; + return false; + } + if(strlen($title) > PAGE_TITLE_LIMIT) { + $errors[] = 'Page title cannot be longer than ' . PAGE_TITLE_LIMIT . ' characters.'; + return false; + } + if(strlen($body) > PAGE_BODY_LIMIT) { + $errors[] = 'Page content cannot be longer than ' . PAGE_BODY_LIMIT . ' characters.'; + return false; + } + if(!isset($player_id) || $player_id == 0) { + $errors[] = 'Player ID is wrong.'; + return false; + } + if(!isset($php) || ($php != 0 && $php != 1)) { + $errors[] = 'Enable PHP is wrong.'; + return false; + } + if(!isset($enable_tinymce) || ($enable_tinymce != 0 && $enable_tinymce != 1)) { + $errors[] = 'Enable TinyMCE is wrong.'; + return false; + } + if(!isset($access) || $access < 0 || $access > PHP_INT_MAX) { + $errors[] = 'Access is wrong.'; + return false; + } + + return true; + } + static public function get($id) { global $db; @@ -128,31 +176,36 @@ class Pages static public function add($name, $title, $body, $player_id, $php, $enable_tinymce, $access, &$errors) { + if(!self::verify($name, $title, $body, $player_id, $php, $enable_tinymce, $access, $errors)) { + return false; + } + global $db; - if (isset($name[0]) && isset($title[0]) && isset($body[0]) && $player_id != 0) { - $query = $db->select(TABLE_PREFIX . 'pages', array('name' => $name)); - if ($query === false) - $db->insert(TABLE_PREFIX . 'pages', - array( - 'name' => $name, - 'title' => $title, - 'body' => $body, - 'player_id' => $player_id, - 'php' => $php ? '1' : '0', - 'enable_tinymce' => $enable_tinymce ? '1' : '0', - 'access' => $access - ) - ); - else - $errors[] = 'Page with this link already exists.'; - } else - $errors[] = 'Please fill all inputs.'; + $query = $db->select(TABLE_PREFIX . 'pages', array('name' => $name)); + if ($query === false) + $db->insert(TABLE_PREFIX . 'pages', + array( + 'name' => $name, + 'title' => $title, + 'body' => $body, + 'player_id' => $player_id, + 'php' => $php ? '1' : '0', + 'enable_tinymce' => $enable_tinymce ? '1' : '0', + 'access' => $access + ) + ); + else + $errors[] = 'Page with this link already exists.'; return !count($errors); } static public function update($id, $name, $title, $body, $player_id, $php, $enable_tinymce, $access) { + if(!self::verify($name, $title, $body, $player_id, $php, $enable_tinymce, $access, $errors)) { + return false; + } + global $db; $db->update(TABLE_PREFIX . 'pages', array( @@ -165,6 +218,8 @@ class Pages 'access' => $access ), array('id' => $id)); + + return true; } static public function delete($id, &$errors) @@ -181,15 +236,18 @@ class Pages return !count($errors); } - static public function toggleHidden($id, &$errors) + static public function toggleHidden($id, &$errors, &$status) { global $db; if (isset($id)) { $query = $db->select(TABLE_PREFIX . 'pages', array('id' => $id)); - if ($query !== false) + if ($query !== false) { $db->update(TABLE_PREFIX . 'pages', array('hidden' => ($query['hidden'] == 1 ? 0 : 1)), array('id' => $id)); - else + $status = $query['hidden']; + } + else { $errors[] = 'Page with id ' . $id . ' does not exists.'; + } } else $errors[] = 'id not set'; diff --git a/system/pages/admin/phpinfo.php b/admin/pages/phpinfo.php similarity index 100% rename from system/pages/admin/phpinfo.php rename to admin/pages/phpinfo.php diff --git a/system/pages/admin/players.php b/admin/pages/players.php similarity index 99% rename from system/pages/admin/players.php rename to admin/pages/players.php index 3b139a79..0ab43f1d 100644 --- a/system/pages/admin/players.php +++ b/admin/pages/players.php @@ -634,9 +634,9 @@ else if (isset($_REQUEST['search'])) { diff --git a/system/pages/admin/plugins.php b/admin/pages/plugins.php similarity index 99% rename from system/pages/admin/plugins.php rename to admin/pages/plugins.php index b106c6e2..65132118 100644 --- a/system/pages/admin/plugins.php +++ b/admin/pages/plugins.php @@ -9,6 +9,7 @@ */ defined('MYAAC') or die('Direct access not allowed!'); $title = 'Plugin manager'; +$use_datatable = true; require_once LIBS . 'plugins.php'; diff --git a/system/pages/admin/reports.php b/admin/pages/reports.php similarity index 98% rename from system/pages/admin/reports.php rename to admin/pages/reports.php index 3c5f466e..9778c9be 100644 --- a/system/pages/admin/reports.php +++ b/admin/pages/reports.php @@ -9,6 +9,7 @@ */ defined('MYAAC') or die('Direct access not allowed!'); $title = 'Report Viewer'; +$use_datatable = true; $files = array(); $server_path_reports = $config['data_path'] . 'reports/'; diff --git a/system/pages/admin/settings.php b/admin/pages/settings.php similarity index 100% rename from system/pages/admin/settings.php rename to admin/pages/settings.php diff --git a/system/pages/admin/statistics.php b/admin/pages/statistics.php similarity index 100% rename from system/pages/admin/statistics.php rename to admin/pages/statistics.php diff --git a/system/pages/admin/tools.php b/admin/pages/tools.php similarity index 69% rename from system/pages/admin/tools.php rename to admin/pages/tools.php index 2b880b46..fa75d750 100644 --- a/system/pages/admin/tools.php +++ b/admin/pages/tools.php @@ -10,18 +10,24 @@ defined('MYAAC') or die('Direct access not allowed!'); $title = 'Tools'; -$tool = $_GET['tool']; -if (!isset($tool)) { +if (!isset($_GET['tool'])) { echo 'Tool not set.'; return; } +$tool = $_GET['tool']; if (preg_match("/[^A-z0-9_\-]/", $tool)) { echo 'Invalid tool.'; return; } -$file = BASE . 'admin/pages/tools/' . $tool . '.php'; -if (!@file_exists($file)) +$file = ADMIN . 'tools/' . $tool . '.php'; + +if (@file_exists($file)) { require $file; + return; +} + +echo 'Tool ' . $tool . ' not found.'; + ?> diff --git a/system/pages/admin/version.php b/admin/pages/version.php similarity index 100% rename from system/pages/admin/version.php rename to admin/pages/version.php diff --git a/system/pages/admin/visitors.php b/admin/pages/visitors.php similarity index 97% rename from system/pages/admin/visitors.php rename to admin/pages/visitors.php index f3d05e8d..e86d647b 100644 --- a/system/pages/admin/visitors.php +++ b/admin/pages/visitors.php @@ -9,6 +9,7 @@ */ defined('MYAAC') or die('Direct access not allowed!'); $title = 'Visitors'; +$use_datatable = true; if (!$config['visitors_counter']): ?> Visitors counter is disabled.
    diff --git a/admin/template/menus.php b/admin/template/menus.php index 46e1bf15..526a528d 100644 --- a/admin/template/menus.php +++ b/admin/template/menus.php @@ -1,49 +1,67 @@ 'Dashboard', 'icon' => 'tachometer-alt', 'link' => 'dashboard'], - ['name' => 'Settings', 'icon' => 'edit', 'link' => 'settings&plugin=core'], - ['name' => 'News', 'icon' => 'newspaper', 'link' => + ['name' => 'Dashboard', 'icon' => 'tachometer-alt', 'order' => 10, 'link' => 'dashboard'], + ['name' => 'Settings', 'icon' => 'edit', 'order' => 19, 'link' => 'settings&plugin=core'], + ['name' => 'News', 'icon' => 'newspaper', 'order' => 20, 'link' => [ - ['name' => 'View', 'link' => 'news'], - ['name' => 'Add news', 'link' => 'news&action=new&type=1'], - ['name' => 'Add ticker', 'link' => 'news&action=new&type=2'], - ['name' => 'Add article', 'link' => 'news&action=new&type=3'], + ['name' => 'View', 'link' => 'news', 'icon' => 'list', 'order' => 10], + ['name' => 'Add news', 'link' => 'news&action=new&type=1', 'icon' => 'plus', 'order' => 20], + ['name' => 'Add ticker', 'link' => 'news&action=new&type=2', 'icon' => 'plus', 'order' => 30], + ['name' => 'Add article', 'link' => 'news&action=new&type=3', 'icon' => 'plus', 'order' => 40], ], ], - ['name' => 'Changelogs', 'icon' => 'newspaper', 'link' => + ['name' => 'Changelogs', 'icon' => 'newspaper', 'order' => 30, 'link' => [ - ['name' => 'View', 'link' => 'changelog'], - ['name' => 'Add', 'link' => 'changelog&action=new'], + ['name' => 'View', 'link' => 'changelog', 'icon' => 'list', 'order' => 10], + ['name' => 'Add', 'link' => 'changelog&action=new', 'icon' => 'plus', 'order' => 20], ], ], - ['name' => 'Mailer', 'icon' => 'envelope', 'link' => 'mailer', 'disabled' => !config('mail_enabled')], - ['name' => 'Pages', 'icon' => 'book', 'link' => + ['name' => 'Mailer', 'icon' => 'envelope', 'order' => 40, 'link' => 'mailer', 'disabled' => !config('mail_enabled')], + ['name' => 'Pages', 'icon' => 'book', 'order' => 50, 'link' => [ - ['name' => 'View', 'link' => 'pages'], - ['name' => 'Add', 'link' => 'pages&action=new'], + ['name' => 'View', 'link' => 'pages', 'icon' => 'list', 'order' => 10], + ['name' => 'Add', 'link' => 'pages&action=new', 'icon' => 'plus', 'order' => 20], ], ], - ['name' => 'Menus', 'icon' => 'list', 'link' => 'menus'], - ['name' => 'Plugins', 'icon' => 'plug', 'link' => 'plugins'], - ['name' => 'Server Data', 'icon' => 'gavel', 'link' => 'data'], - ['name' => 'Editor', 'icon' => 'edit', 'link' => + ['name' => 'Menus', 'icon' => 'list', 'order' => 60, 'link' => 'menus'], + ['name' => 'Plugins', 'icon' => 'plug', 'order' => 70, 'link' => 'plugins'], + ['name' => 'Server Data', 'icon' => 'gavel', 'order' => 80, 'link' => 'data'], + ['name' => 'Editor', 'icon' => 'edit', 'order' => 90, 'link' => [ - ['name' => 'Accounts', 'link' => 'accounts'], - ['name' => 'Players', 'link' => 'players'], + ['name' => 'Accounts', 'link' => 'accounts', 'icon' => 'users', 'order' => 10], + ['name' => 'Players', 'link' => 'players', 'icon' => 'user-astronaut', 'order' => 20], ], ], - ['name' => 'Tools', 'icon' => 'tools', 'link' => + ['name' => 'Tools', 'icon' => 'tools', 'order' => 100, 'link' => [ - ['name' => 'Notepad', 'link' => 'notepad'], - ['name' => 'phpinfo', 'link' => 'phpinfo'], + ['name' => 'Mass Account Actions', 'link' => 'mass_account', 'icon' => 'globe', 'order' => 10], + ['name' => 'Mass Teleport Actions', 'link' => 'mass_teleport', 'icon' => 'globe', 'order' => 20], + ['name' => 'Notepad', 'link' => 'notepad', 'icon' => 'marker', 'order' => 30], + ['name' => 'phpinfo', 'link' => 'phpinfo', 'icon' => 'server', 'order' => 40], ], ], - ['name' => 'Logs', 'icon' => 'bug', 'link' => + ['name' => 'Logs', 'icon' => 'bug', 'order' => 110, 'link' => [ - ['name' => 'Logs', 'link' => 'logs'], - ['name' => 'Reports', 'link' => 'reports'], - ['name' => 'Visitors', 'icon' => 'user', 'link' => 'visitors'], + ['name' => 'Logs', 'link' => 'logs', 'icon' => 'book', 'order' => 10], + ['name' => 'Reports', 'link' => 'reports', 'icon' => 'book', 'order' => 20], + ['name' => 'Visitors', 'link' => 'visitors', 'icon' => 'user', 'order' => 30], ], ], ]; + +$hooks->trigger(HOOK_ADMIN_MENU); + +usort($menus, function ($a, $b) { + return $a['order'] - $b['order']; +}); + +foreach ($menus as $i => $menu) { + if (isset($menu['link']) && is_array($menu['link'])) { + usort($menus[$i]['link'], function ($a, $b) { + return $a['order'] - $b['order']; + }); + } +} + +return $menus; diff --git a/admin/template/template.php b/admin/template/template.php index ff47b671..70ed5174 100644 --- a/admin/template/template.php +++ b/admin/template/template.php @@ -2,6 +2,7 @@ + trigger(HOOK_ADMIN_HEAD_START); ?> <?php echo (isset($title) ? $title . ' - ' : '') . $config['lua']['serverName'];?> @@ -16,8 +17,10 @@ + trigger(HOOK_ADMIN_HEAD_END); ?> +trigger(HOOK_ADMIN_BODY_START); ?>
  • '; + $nav_construct .= '">

    ' . $sub_menu['name'] . '

    '; } ?>
    @@ -175,6 +181,16 @@ echo $content; } ?> +display('admin-bar.html.twig', [ + 'username' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId() + ]); +} +?> @@ -182,5 +198,6 @@ +trigger(HOOK_ADMIN_BODY_END); ?> diff --git a/admin/tools/upload_image.php b/admin/tools/upload_image.php new file mode 100644 index 00000000..5d881f18 --- /dev/null +++ b/admin/tools/upload_image.php @@ -0,0 +1,53 @@ + $returnPathToImage]); +} else { + // Notify editor that the upload failed + header('HTTP/1.1 500 Server Error'); +} + + diff --git a/common.php b/common.php index 847c1232..bcc66be3 100644 --- a/common.php +++ b/common.php @@ -23,69 +23,97 @@ * @copyright 2019 MyAAC * @link https://my-aac.org */ -if (version_compare(phpversion(), '7.1', '<')) die('PHP version 7.1 or higher is required.'); -session_start(); +if (version_compare(phpversion(), '7.2.5', '<')) die('PHP version 7.2.5 or higher is required.'); -define('MYAAC', true); -define('MYAAC_VERSION', '0.9.0-dev'); -define('DATABASE_VERSION', 33); -define('TABLE_PREFIX', 'myaac_'); +const MYAAC = true; +const MYAAC_VERSION = '0.9.0-dev'; +const DATABASE_VERSION = 34; +const TABLE_PREFIX = 'myaac_'; define('START_TIME', microtime(true)); define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX')); define('IS_CLI', in_array(php_sapi_name(), ['cli', 'phpdb'])); // account flags -define('FLAG_ADMIN', 1); -define('FLAG_SUPER_ADMIN', 2); -define('FLAG_CONTENT_PAGES', 4); -define('FLAG_CONTENT_MAILER', 8); -define('FLAG_CONTENT_NEWS', 16); -define('FLAG_CONTENT_FORUM', 32); -define('FLAG_CONTENT_COMMANDS', 64); -define('FLAG_CONTENT_SPELLS', 128); -define('FLAG_CONTENT_MONSTERS', 256); -define('FLAG_CONTENT_GALLERY', 512); -define('FLAG_CONTENT_VIDEOS', 1024); -define('FLAG_CONTENT_FAQ', 2048); -define('FLAG_CONTENT_MENUS', 4096); -define('FLAG_CONTENT_PLAYERS', 8192); +const FLAG_NONE = 0; +const FLAG_ADMIN = 1; +const FLAG_SUPER_ADMIN = 2; +const FLAG_SUPER_BOTH = 3; +const FLAG_CONTENT_PAGES = 4; +const FLAG_CONTENT_MAILER = 8; +const FLAG_CONTENT_NEWS = 16; +const FLAG_CONTENT_FORUM = 32; +const FLAG_CONTENT_COMMANDS = 64; +const FLAG_CONTENT_SPELLS = 128; +const FLAG_CONTENT_MONSTERS = 256; +const FLAG_CONTENT_GALLERY = 512; +const FLAG_CONTENT_VIDEOS = 1024; +const FLAG_CONTENT_FAQ = 2048; +const FLAG_CONTENT_MENUS = 4096; +const FLAG_CONTENT_PLAYERS = 8192; + +// account access types +const ACCOUNT_WEB_FLAGS = [ + FLAG_NONE => 'None', + FLAG_ADMIN =>'Admin', + FLAG_SUPER_ADMIN => 'Super Admin', + FLAG_SUPER_BOTH =>'(Admin + Super Admin)', +]; // news -define('NEWS', 1); -define('TICKER', 2); -define('ARTICLE', 3); +const NEWS = 1; +const TICKER = 2; +const ARTICLE = 3; + +// here you can change location of admin panel +// you need also to rename folder "admin" +// this may improve security +const ADMIN_PANEL_FOLDER = 'admin'; // directories -define('BASE', __DIR__ . '/'); -define('ADMIN', BASE . 'admin/'); -define('SYSTEM', BASE . 'system/'); -define('CACHE', SYSTEM . 'cache/'); -define('LOCALE', SYSTEM . 'locale/'); -define('LIBS', SYSTEM . 'libs/'); -define('LOGS', SYSTEM . 'logs/'); -define('PAGES', SYSTEM . 'pages/'); -define('PLUGINS', BASE . 'plugins/'); -define('TEMPLATES', BASE . 'templates/'); -define('TOOLS', BASE . 'tools/'); -define('VENDOR', BASE . 'vendor/'); +const BASE = __DIR__ . '/'; +const ADMIN = BASE . ADMIN_PANEL_FOLDER . '/'; +const SYSTEM = BASE . 'system/'; +const CACHE = SYSTEM . 'cache/'; +const LOCALE = SYSTEM . 'locale/'; +const LIBS = SYSTEM . 'libs/'; +const LOGS = SYSTEM . 'logs/'; +const PAGES = SYSTEM . 'pages/'; +const PLUGINS = BASE . 'plugins/'; +const TEMPLATES = BASE . 'templates/'; +const TOOLS = BASE . 'tools/'; +const VENDOR = BASE . 'vendor/'; + +// other dirs +const SESSIONS_DIR = SYSTEM . 'php_sessions'; +const GUILD_IMAGES_DIR = 'images/guilds/'; +const EDITOR_IMAGES_DIR = 'images/editor/'; +const GALLERY_DIR = 'images/gallery/'; // menu categories -define('MENU_CATEGORY_NEWS', 1); -define('MENU_CATEGORY_ACCOUNT', 2); -define('MENU_CATEGORY_COMMUNITY', 3); -define('MENU_CATEGORY_FORUM', 4); -define('MENU_CATEGORY_LIBRARY', 5); -define('MENU_CATEGORY_SHOP', 6); +const MENU_CATEGORY_NEWS = 1; +const MENU_CATEGORY_ACCOUNT = 2; +const MENU_CATEGORY_COMMUNITY = 3; +const MENU_CATEGORY_FORUM = 4; +const MENU_CATEGORY_LIBRARY = 5; +const MENU_CATEGORY_SHOP = 6; // otserv versions -define('OTSERV', 1); -define('OTSERV_06', 2); -define('OTSERV_FIRST', OTSERV); -define('OTSERV_LAST', OTSERV_06); -define('TFS_02', 3); -define('TFS_03', 4); -define('TFS_FIRST', TFS_02); -define('TFS_LAST', TFS_03); +const OTSERV = 1; +const OTSERV_06 = 2; +const OTSERV_FIRST = OTSERV; +const OTSERV_LAST = OTSERV_06; +const TFS_02 = 3; +const TFS_03 = 4; +const TFS_FIRST = TFS_02; +const TFS_LAST = TFS_03; + +// other definitions +const ACCOUNT_NUMBER_LENGTH = 8; + +if (!IS_CLI) { + session_save_path(SESSIONS_DIR); + session_start(); +} // basedir $basedir = ''; @@ -94,7 +122,7 @@ $size = count($tmp) - 1; for($i = 1; $i < $size; $i++) $basedir .= '/' . $tmp[$i]; -$basedir = str_replace(array('/admin', '/install'), '', $basedir); +$basedir = str_replace(['/' . ADMIN_PANEL_FOLDER, '/install', '/tools'], '', $basedir); define('BASE_DIR', $basedir); if(!IS_CLI) { diff --git a/composer.json b/composer.json index f5c67e7d..90179c29 100644 --- a/composer.json +++ b/composer.json @@ -1,16 +1,15 @@ { "require": { - "php": ">=7.1", - "ext-dom": "*", - "ext-json": "*", - "ext-gd": "*", + "php": "^7.2.5 || ^8.0", "ext-pdo": "*", "ext-pdo_mysql": "*", + "ext-json": "*", "ext-xml": "*", - "ext-zip": "*", + "ext-dom": "*", "phpmailer/phpmailer": "^6.1", "composer/semver": "^3.2", - "twig/twig": "~1.42.5", - "erusev/parsedown": "^1.7" + "twig/twig": "^2.0", + "erusev/parsedown": "^1.7", + "nikic/fast-route": "^1.3" } } diff --git a/config.php b/config.php index f5c6aa87..3d8954b1 100644 --- a/config.php +++ b/config.php @@ -43,7 +43,7 @@ $config = array( 'database_user' => '', 'database_password' => '', 'database_name' => '', - 'database_log' => false, // should database queries be logged and and saved into system/logs/database.log? + 'database_log' => false, // should database queries be logged and saved into system/logs/database.log? 'database_socket' => '', // set if you want to connect to database through socket (example: /var/run/mysqld/mysqld.sock) 'database_persistent' => false, // use database permanent connection (like server), may speed up your site @@ -69,9 +69,18 @@ $config = array( // account 'account_management' => true, // disable if you're using other method to manage users (fe. tfs account manager) + 'account_login_by_email' => false, // use email instead of Account Name like in latest Tibia + 'account_login_by_email_fallback' => false, // allow also additionally login by Account Name/Number (for users that might forget their email) 'account_create_auto_login' => false, // auto login after creating account? 'account_create_character_create' => true, // allow directly to create character on create account page? - 'account_mail_verify' => false, // force users to confirm their email addresses when registering account + 'account_mail_verify' => false, // force users to confirm their email addresses when registering + 'account_mail_confirmed_reward' => [ // reward users for confirming their E-Mails + // account_mail_verify needs to be enabled too + 'premium_days' => 0, + 'premium_points' => 0, + 'coins' => 0, + 'message' => 'You received %d %s for confirming your E-Mail address.' // example: You received 20 premium points for confirming your E-Mail address. + ], 'account_mail_unique' => true, // email addresses cannot be duplicated? (one account = one email) 'account_mail_block_plus_sign' => true, // block email with '+' signs like test+box@gmail.com (help protect against spamming accounts) 'account_premium_days' => 0, // default premium days on new account @@ -103,18 +112,24 @@ $config = array( 'smtp_secure' => '', // What kind of encryption to use on the SMTP connection. Options: '', 'ssl' (GMail) or 'tls' (Microsoft Outlook) 'smtp_debug' => false, // set true to debug (you will see more info in error.log) - // reCAPTCHA (prevent spam bots) - 'recaptcha_enabled' => false, // enable recaptcha verification code - 'recaptcha_site_key' => '', // get your own site and secret keys at https://www.google.com/recaptcha - 'recaptcha_secret_key' => '', - 'recaptcha_theme' => 'light', // light, dark - // 'generate_new_reckey' => true, // let player generate new recovery key, he will receive e-mail with new rec key (not display on page, hacker can't generate rec key) 'generate_new_reckey_price' => 20, // price for new recovery key 'send_mail_when_change_password' => true, // send e-mail with new password when change password to account 'send_mail_when_generate_reckey' => true, // send e-mail with rec key (key is displayed on page anyway when generate) + // you may need to adjust this for older tfs versions + // by removing Community Manager + 'account_types' => [ + 'None', + 'Normal', + 'Tutor', + 'Senior Tutor', + 'Gamemaster', + 'Community Manager', + 'God', + ], + // genders (aka sex) 'genders' => array( 0 => 'Female', @@ -130,6 +145,8 @@ $config = array( 4 => 'Knight Sample' ), + 'use_character_sample_skills' => false, + // it must show limited number of players after using search in character page 'characters_search_limit' => 15, diff --git a/system/templates/admin.dashboard.html.twig b/images/editor/index.html similarity index 100% rename from system/templates/admin.dashboard.html.twig rename to images/editor/index.html diff --git a/index.php b/index.php index 1c47f2e8..e15abb21 100644 --- a/index.php +++ b/index.php @@ -28,18 +28,22 @@ require_once 'common.php'; require_once SYSTEM . 'functions.php'; $uri = $_SERVER['REQUEST_URI']; +if(false !== strpos($uri, 'index.php')) { + $uri = str_replace_first('/index.php', '', $uri); +} -$tmp = BASE_DIR; -if(!empty($tmp)) - $uri = str_replace(BASE_DIR . '/', '', $uri); -else +if(0 === strpos($uri, '/')) { $uri = str_replace_first('/', '', $uri); +} -$uri = str_replace(array('index.php/', '?'), '', $uri); -define('URI', $uri); +if(preg_match("/^[A-Za-z0-9-_%'+\/]+\.png$/i", $uri)) { + if (!empty(BASE_DIR)) { + $tmp = explode('.', str_replace_first(str_replace_first('/', '', BASE_DIR) . '/', '', $uri)); + } + else { + $tmp = explode('.', $uri); + } -if(preg_match("/^[A-Za-z0-9-_%'+]+\.png$/i", $uri)) { - $tmp = explode('.', $uri); $_REQUEST['name'] = urldecode($tmp[0]); chdir(TOOLS . 'signature'); @@ -47,7 +51,7 @@ if(preg_match("/^[A-Za-z0-9-_%'+]+\.png$/i", $uri)) { exit(); } -if(preg_match("/^(.*)\.(gif|jpg|png|jpeg|tiff|bmp|css|js|less|map|html|php|zip|rar|gz|ttf|woff|ico)$/i", $_SERVER['REQUEST_URI'])) { +if(preg_match("/^(.*)\.(gif|jpg|png|jpeg|tiff|bmp|css|js|less|map|html|zip|rar|gz|ttf|woff|ico)$/i", $_SERVER['REQUEST_URI'])) { http_response_code(404); exit; } @@ -74,110 +78,15 @@ if((!isset($config['installed']) || !$config['installed']) && file_exists(BASE . throw new RuntimeException('Setup detected that install/ directory exists. Please visit this url to start MyAAC Installation.
    Delete install/ directory if you already installed MyAAC.
    Remember to REFRESH this page when you\'re done!'); } -$found = false; -if(empty($uri) || isset($_REQUEST['template'])) { - $_REQUEST['p'] = 'news'; - $found = true; -} -else { - $tmp = strtolower($uri); - if(!preg_match('/[^A-z0-9_\-]/', $uri) && file_exists(SYSTEM . 'pages/' . $tmp . '.php')) { - $_REQUEST['p'] = $uri; - $found = true; - } - else { - $rules = array( - '/^account\/manage\/?$/' => array('subtopic' => 'accountmanagement'), - '/^account\/create\/?$/' => array('subtopic' => 'createaccount'), - '/^account\/lost\/?$/' => array('subtopic' => 'lostaccount'), - '/^account\/logout\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'logout'), - '/^account\/password\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'change_password'), - '/^account\/register\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'register'), - '/^account\/register\/new\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'register_new'), - '/^account\/email\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'change_email'), - '/^account\/info\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'change_info'), - '/^account\/character\/create\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'create_character'), - '/^account\/character\/name\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'change_name'), - '/^account\/character\/sex\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'change_sex'), - '/^account\/character\/delete\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'delete_character'), - '/^account\/character\/comment\/[A-Za-z0-9-_%+\']+\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'change_comment', 'name' => '$3'), - '/^account\/character\/comment\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'change_comment'), - '/^account\/confirm_email\/[A-Za-z0-9-_]+\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'confirm_email', 'v' => '$2'), - '/^bans\/[0-9]+\/?$/' => array('subtopic' => 'bans', 'page' => '$1'), - '/^characters\/[A-Za-z0-9-_%+\']+$/' => array('subtopic' => 'characters', 'name' => '$1'), - '/^changelog\/[0-9]+\/?$/' => array('subtopic' => 'changelog', 'page' => '$1'), - '/^commands\/add\/?$/' => array('subtopic' => 'commands', 'action' => 'add'), - '/^commands\/edit\/?$/' => array('subtopic' => 'commands', 'action' => 'edit'), - '/^creatures\/[A-Za-z0-9-_%+\']+$/' => array('subtopic' => 'creatures', 'creature' => '$1'), - '/^faq\/add\/?$/' => array('subtopic' => 'faq', 'action' => 'add'), - '/^faq\/edit\/?$/' => array('subtopic' => 'faq', 'action' => 'edit'), - '/^forum\/add_board\/?$/' => array('subtopic' => 'forum', 'action' => 'add_board'),# - '/^forum\/edit_board\/?$/' => array('subtopic' => 'forum', 'action' => 'edit_board'), - '/^forum\/board\/[0-9]+\/?$/' => array('subtopic' => 'forum', 'action' => 'show_board', 'id' => '$2'), - '/^forum\/board\/[0-9]+\/[0-9]+\/?$/' => array('subtopic' => 'forum', 'action' => 'show_board', 'id' => '$2', 'page' => '$3'), - '/^forum\/thread\/[0-9]+\/?$/' => array('subtopic' => 'forum', 'action' => 'show_thread', 'id' => '$2'), - '/^forum\/thread\/[0-9]+\/[0-9]+\/?$/' => array('subtopic' => 'forum', 'action' => 'show_thread', 'id' => '$2', 'page' => '$3'), - '/^gallery\/add\/?$/' => array('subtopic' => 'gallery', 'action' => 'add'), - '/^gallery\/edit\/?$/' => array('subtopic' => 'gallery', 'action' => 'edit'), - '/^gallery\/[0-9]+\/?$/' => array('subtopic' => 'gallery', 'image' => '$1'), - '/^gifts\/history\/?$/' => array('subtopic' => 'gifts', 'action' => 'show_history'), - '/^guilds\/[A-Za-z0-9-_%+\']+$/' => array('subtopic' => 'guilds', 'action' => 'show', 'guild' => '$1'), - '/^highscores\/[A-Za-z0-9-_]+\/[A-Za-z0-9-_]+\/[0-9]+\/?$/' => array('subtopic' => 'highscores', 'list' => '$1', 'vocation' => '$2', 'page' => '$3'), - '/^highscores\/[A-Za-z0-9-_]+\/[0-9]+\/?$/' => array('subtopic' => 'highscores', 'list' => '$1', 'page' => '$2'), - '/^highscores\/[A-Za-z0-9-_]+\/[A-Za-z0-9-_]+\/?$/' => array('subtopic' => 'highscores', 'list' => '$1', 'vocation' => '$2'), - '/^highscores\/[A-Za-z0-9-_\']+\/?$/' => array('subtopic' => 'highscores', 'list' => '$1'), - '/^news\/add\/?$/' => array('subtopic' => 'news', 'action' => 'add'), - '/^news\/edit\/?$/' => array('subtopic' => 'news', 'action' => 'edit'), - '/^news\/archive\/?$/' => array('subtopic' => 'newsarchive'), - '/^news\/archive\/[0-9]+\/?$/' => array('subtopic' => 'newsarchive', 'id' => '$2'), - '/^polls\/[0-9]+\/?$/' => array('subtopic' => 'polls', 'id' => '$1'), - '/^spells\/[A-Za-z0-9-_%]+\/[A-Za-z0-9-_]+\/?$/' => array('subtopic' => 'spells', 'vocation' => '$1', 'order' => '$2'), - '/^houses\/view\/?$/' => array('subtopic' => 'houses', 'page' => 'view') - ); - - foreach($rules as $rule => $redirect) { - if (preg_match($rule, $uri)) { - $tmp = explode('/', $uri); - /* @var $redirect array */ - foreach($redirect as $key => $value) { - - if(strpos($value, '$') !== false) { - $value = str_replace('$' . $value[1], $tmp[$value[1]], $value); - } - - $_REQUEST[$key] = $value; - $_GET[$key] = $value; - } - - $found = true; - break; - } - } - } -} - -// define page visited, so it can be used within events system -$page = isset($_REQUEST['subtopic']) ? $_REQUEST['subtopic'] : (isset($_REQUEST['p']) ? $_REQUEST['p'] : ''); -if(empty($page) || !preg_match('/^[A-z0-9\_\-]+$/', $page)) { - $tmp = URI; - if(!empty($tmp)) { - $page = $tmp; - } - else { - if(!$found) - $page = '404'; - else - $page = 'news'; - } -} - -$page = strtolower($page); -define('PAGE', $page); - $template_place_holders = array(); require_once SYSTEM . 'init.php'; +// verify myaac tables exists in database +if(!$db->hasTable('myaac_account_actions')) { + throw new RuntimeException('Seems that the table myaac_account_actions of MyAAC doesn\'t exist in the database. This is a fatal error. You can try to reinstall MyAAC by visiting this url.'); +} + // event system require_once SYSTEM . 'hooks.php'; $hooks = new Hooks(); @@ -189,10 +98,7 @@ require_once SYSTEM . 'status.php'; $twig->addGlobal('config', $config); $twig->addGlobal('status', $status); -// verify myaac tables exists in database -if(!$db->hasTable('myaac_account_actions')) { - throw new RuntimeException('Seems that the table myaac_account_actions of MyAAC doesn\'t exist in the database. This is a fatal error. You can try to reinstall MyAAC by visiting this url.'); -} +require_once SYSTEM . 'router.php'; require SYSTEM . 'migrate.php'; @@ -242,35 +148,6 @@ if($config['visitors_counter']) $visitors = new Visitors($config['visitors_counter_ttl']); } -// page content loading -if(!isset($content[0])) - $content = ''; -$load_it = true; - -// check if site has been closed -$site_closed = false; -if(fetchDatabaseConfig('site_closed', $site_closed)) { - $site_closed = ($site_closed == 1); - if($site_closed) { - if(!admin()) - { - $title = getDatabaseConfig('site_closed_title'); - $content .= '

    ' . getDatabaseConfig('site_closed_message') . '


    '; - $load_it = false; - } - - if(!$logged) - { - ob_start(); - require SYSTEM . 'pages/accountmanagement.php'; - $content .= ob_get_contents(); - ob_end_clean(); - $load_it = false; - } - } -} -define('SITE_CLOSED', $site_closed); - // backward support for gesior if($config['backward_support']) { define('INITIALIZED', true); @@ -279,7 +156,6 @@ if($config['backward_support']) { $layout_name = $template_path; $news_content = ''; $tickers_content = ''; - $subtopic = PAGE; $main_content = ''; $config['access_admin_panel'] = 2; @@ -309,66 +185,14 @@ if($config['backward_support']) { $config['status']['serverStatus_' . $key] = $value; } -if($load_it) -{ - if(SITE_CLOSED && admin()) - $content .= '

    Site is under maintenance (closed mode). Only privileged users can see it.

    '; - - if($config['backward_support']) - require SYSTEM . 'compat_pages.php'; - - $ignore = false; - - $logged_access = 1; - if($logged && $account_logged && $account_logged->isLoaded()) { - $logged_access = $account_logged->getAccess(); - } - - $success = false; - $tmp_content = getCustomPage($page, $success); - if($success) { - $content .= $tmp_content; - if(hasFlag(FLAG_CONTENT_PAGES) || superAdmin()) { - $pageInfo = getCustomPageInfo($page); - $content = $twig->render('admin.pages.links.html.twig', array( - 'page' => array('id' => $pageInfo !== null ? $pageInfo['id'] : 0, 'hidden' => $pageInfo !== null ? $pageInfo['hidden'] : '0') - )) . $content; - } - } else { - $file = $template_path . '/pages/' . $page . '.php'; - if(!@file_exists($file)) - { - $file = SYSTEM . 'pages/' . $page . '.php'; - if(!@file_exists($file)) - { - $page = '404'; - $file = SYSTEM . 'pages/404.php'; - } - } - } - - ob_start(); - if($hooks->trigger(HOOK_BEFORE_PAGE)) { - if(!$ignore) - require $file; - } - - if($config['backward_support'] && isset($main_content[0])) - $content .= $main_content; - - $content .= ob_get_contents(); - ob_end_clean(); - $hooks->trigger(HOOK_AFTER_PAGE); +/** + * @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() + ]); } - -if($config['backward_support']) { - $main_content = $content; - if(!isset($title)) - $title = ucfirst($page); - - $topic = $title; -} - $title_full = (isset($title) ? $title . ' - ' : '') . $config['lua']['serverName']; require $template_path . '/' . $template_index; diff --git a/install/includes/schema.sql b/install/includes/schema.sql index 160461a7..a1f03f42 100644 --- a/install/includes/schema.sql +++ b/install/includes/schema.sql @@ -1,4 +1,4 @@ -SET @myaac_database_version = 32; +SET @myaac_database_version = 33; CREATE TABLE `myaac_account_actions` ( @@ -337,7 +337,7 @@ CREATE TABLE `myaac_spells` CREATE TABLE `myaac_visitors` ( - `ip` VARCHAR(16) NOT NULL, + `ip` VARCHAR(45) NOT NULL, `lastvisit` INT(11) NOT NULL DEFAULT 0, `page` VARCHAR(2048) NOT NULL, UNIQUE (`ip`) diff --git a/install/includes/twig_error.html b/install/includes/twig_error.html index 933a36c1..40daedec 100644 --- a/install/includes/twig_error.html +++ b/install/includes/twig_error.html @@ -2,10 +2,10 @@ We have detected that you don't have access to write to the system/cache directo \ No newline at end of file + diff --git a/install/index.php b/install/index.php index 1bc0e4ba..4931f8c1 100644 --- a/install/index.php +++ b/install/index.php @@ -70,7 +70,7 @@ if($step == 'database') { $key = str_replace('var_', '', $key); - if(in_array($key, array('account', 'password', 'email', 'player_name'))) { + if(in_array($key, array('account', 'password', 'password_confirm', 'email', 'player_name'))) { continue; } @@ -122,6 +122,7 @@ else if($step == 'admin') { else if($step == 'finish') { $email = $_SESSION['var_email']; $password = $_SESSION['var_password']; + $password_confirm = $_SESSION['var_password_confirm']; $player_name = $_SESSION['var_player_name']; // email check @@ -163,6 +164,9 @@ else if($step == 'finish') { else if(!Validator::password($password)) { $errors[] = $locale['step_admin_password_error_format']; } + else if($password != $password_confirm) { + $errors[] = $locale['step_admin_password_confirm_error_not_same']; + } // player name check if(empty($player_name)) { diff --git a/install/steps/3-requirements.php b/install/steps/3-requirements.php index 65fa0c18..659e3036 100644 --- a/install/steps/3-requirements.php +++ b/install/steps/3-requirements.php @@ -7,15 +7,16 @@ $dirs_required = [ 'system/cache', ]; $dirs_optional = [ - 'images/guilds' => $locale['step_requirements_warning_images_guilds'], - 'images/gallery' => $locale['step_requirements_warning_images_gallery'], + GUILD_IMAGES_DIR => $locale['step_requirements_warning_images_guilds'], + GALLERY_DIR => $locale['step_requirements_warning_images_gallery'], ]; $extensions_required = [ - 'json', 'pdo', 'pdo_mysql', 'xml', 'zip' + 'pdo', 'pdo_mysql', 'json', 'xml' ]; $extensions_optional = [ 'gd' => $locale['step_requirements_warning_player_signatures'], + 'zip' => $locale['step_requirements_warning_install_plugins'], ]; /* * diff --git a/install/steps/7-finish.php b/install/steps/7-finish.php index f17a7da2..81ace38f 100644 --- a/install/steps/7-finish.php +++ b/install/steps/7-finish.php @@ -15,8 +15,7 @@ else { $password = $_SESSION['var_password']; - $config_salt_enabled = $db->hasColumn('accounts', 'salt'); - if($config_salt_enabled) + if(USE_ACCOUNT_SALT) { $salt = generateRandomString(10, false, true, true); $password = $salt . $password; @@ -75,7 +74,7 @@ else { $account_used = &$new_account; } - if($config_salt_enabled) + if(USE_ACCOUNT_SALT) $account_used->setCustomField('salt', $salt); $account_used->setCustomField('web_flags', FLAG_ADMIN + FLAG_SUPER_ADMIN); @@ -83,7 +82,7 @@ else { if($db->hasColumn('accounts', 'group_id')) $account_used->setCustomField('group_id', $groups->getHighestId()); if($db->hasColumn('accounts', 'type')) - $account_used->setCustomField('type', 5); + $account_used->setCustomField('type', 6); if(!$player_db->isLoaded()) $player->setAccountId($account_used->getId()); diff --git a/login.php b/login.php new file mode 100644 index 00000000..3ab0b5e9 --- /dev/null +++ b/login.php @@ -0,0 +1,284 @@ +getAttribute('startdate'); + return date_create("{$date}")->format('U'); + } else { + $date = $table1->getAttribute('enddate'); + return date_create("{$date}")->format('U'); + } + } else { + foreach($table1 as $attr) { + if ($attr) { + return $attr->getAttribute($table2); + } + } + } + } + return 'error'; +} + +$request = json_decode(file_get_contents('php://input')); +$action = $request->type ?? ''; + +/** @var OTS_Base_DB $db */ +/** @var array $config */ + +switch ($action) { + case 'cacheinfo': + $playersonline = $db->query("select count(*) from `players_online`")->fetchAll(); + die(json_encode([ + 'playersonline' => (intval($playersonline[0][0])), + 'twitchstreams' => 0, + 'twitchviewer' => 0, + 'gamingyoutubestreams' => 0, + 'gamingyoutubeviewer' => 0 + ])); + + case 'eventschedule': + $eventlist = []; + $file_path = config('server_path') . 'data/XML/events.xml'; + if (!file_exists($file_path)) { + die(json_encode([])); + } + $xml = new DOMDocument; + $xml->load($file_path); + $tmplist = []; + $tableevent = $xml->getElementsByTagName('event'); + + foreach ($tableevent as $event) { + if ($event) { $tmplist = [ + 'colorlight' => parseEvent($event->getElementsByTagName('colors'), false, 'colorlight'), + 'colordark' => parseEvent($event->getElementsByTagName('colors'), false, 'colordark'), + 'description' => parseEvent($event->getElementsByTagName('description'), false, 'description'), + 'displaypriority' => intval(parseEvent($event->getElementsByTagName('details'), false, 'displaypriority')), + 'enddate' => intval(parseEvent($event, true, false)), + 'isseasonal' => getBoolean(intval(parseEvent($event->getElementsByTagName('details'), false, 'isseasonal'))), + 'name' => $event->getAttribute('name'), + 'startdate' => intval(parseEvent($event, true, true)), + 'specialevent' => intval(parseEvent($event->getElementsByTagName('details'), false, 'specialevent')) + ]; + $eventlist[] = $tmplist; } } + die(json_encode(['eventlist' => $eventlist, 'lastupdatetimestamp' => time()])); + + case 'boostedcreature': + $boostDB = $db->query("select * from " . $db->tableName('boosted_creature'))->fetchAll(); + foreach ($boostDB as $Tableboost) { + die(json_encode([ + 'boostedcreature' => true, + 'raceid' => intval($Tableboost['raceid']) + ])); + } + break; + + case 'login': + + $port = $config['lua']['gameProtocolPort']; + + // default world info + $world = [ + 'id' => 0, + 'name' => $config['lua']['serverName'], + 'externaladdress' => $config['lua']['ip'], + 'externalport' => $port, + 'externaladdressprotected' => $config['lua']['ip'], + 'externalportprotected' => $port, + 'externaladdressunprotected' => $config['lua']['ip'], + 'externalportunprotected' => $port, + 'previewstate' => 0, + 'location' => 'BRA', // BRA, EUR, USA + 'anticheatprotection' => false, + 'pvptype' => array_search($config['lua']['worldType'], ['pvp', 'no-pvp', 'pvp-enforced']), + 'istournamentworld' => false, + 'restrictedstore' => false, + 'currenttournamentphase' => 2 + ]; + + $characters = []; + $account = new OTS_Account(); + + $inputEmail = $request->email ?? false; + $inputAccountName = $request->accountname ?? false; + $inputToken = $request->token ?? false; + + if ($inputEmail != false) { // login by email + $account->findByEmail($request->email); + } + else if($inputAccountName != false) { // login by account name + $account->find($inputAccountName); + } + + $current_password = encrypt((USE_ACCOUNT_SALT ? $account->getCustomField('salt') : '') . $request->password); + + if (!$account->isLoaded() || $account->getPassword() != $current_password) { + sendError(($inputEmail != false ? 'Email' : 'Account name') . ' or password is not correct.'); + } + + //log_append('test.log', var_export($account->getCustomField('secret'), true)); + $accountHasSecret = false; + if (fieldExist('secret', 'accounts')) { + $accountSecret = $account->getCustomField('secret'); + if ($accountSecret != null && $accountSecret != '') { + $accountHasSecret = true; + if ($inputToken === false) { + sendError('Submit a valid two-factor authentication token.', 6); + } else { + require_once LIBS . 'rfc6238.php'; + if (TokenAuth6238::verify($accountSecret, $inputToken) !== true) { + sendError('Two-factor authentication failed, token is wrong.', 6); + } + } + } + } + + // common columns + $columns = 'id, name, level, sex, vocation, looktype, lookhead, lookbody, looklegs, lookfeet, lookaddons'; + + if (fieldExist('isreward', 'accounts')) { + $columns .= ', isreward'; + } + + if (fieldExist('istutorial', 'accounts')) { + $columns .= ', istutorial'; + } + + $players = $db->query("select {$columns} from players where account_id = " . $account->getId() . " AND deletion = 0"); + if($players && $players->rowCount() > 0) { + $players = $players->fetchAll(); + + $highestLevelId = 0; + $highestLevel = 0; + foreach ($players as $player) { + if ($player['level'] >= $highestLevel) { + $highestLevel = $player['level']; + $highestLevelId = $player['id']; + } + } + + foreach ($players as $player) { + $characters[] = create_char($player, $highestLevelId); + } + } + + if (fieldExist('premdays', 'accounts') && fieldExist('lastday', 'accounts')) { + $save = false; + $timeNow = time(); + $query = $db->query("select `premdays`, `lastday` from `accounts` where `id` = " . $account->getId()); + if ($query->rowCount() > 0) { + $query = $query->fetch(); + $premDays = (int)$query['premdays']; + $lastDay = (int)$query['lastday']; + $lastLogin = $lastDay; + } else { + sendError("Error while fetching your account data. Please contact admin."); + } + if ($premDays != 0 && $premDays != PHP_INT_MAX) { + if ($lastDay == 0) { + $lastDay = $timeNow; + $save = true; + } else { + $days = (int)(($timeNow - $lastDay) / 86400); + if ($days > 0) { + if ($days >= $premDays) { + $premDays = 0; + $lastDay = 0; + } else { + $premDays -= $days; + $reminder = ($timeNow - $lastDay) % 86400; + $lastDay = $timeNow - $reminder; + } + + $save = true; + } + } + } else if ($lastDay != 0) { + $lastDay = 0; + $save = true; + } + if ($save) { + $db->query("update `accounts` set `premdays` = " . $premDays . ", `lastday` = " . $lastDay . " where `id` = " . $account->getId()); + } + } + + $worlds = [$world]; + $playdata = compact('worlds', 'characters'); + + $sessionKey = ($inputEmail !== false) ? $inputEmail : $inputAccountName; // email or account name + $sessionKey .= "\n" . $request->password; // password + if (!fieldExist('istutorial', 'players')) { + $sessionKey .= "\n"; + } + $sessionKey .= ($accountHasSecret && strlen($accountSecret) > 5) ? $inputToken : ''; + + // this is workaround to distinguish between TFS 1.x and otservbr + // TFS 1.x requires the number in session key + // otservbr requires just login and password + // so we check for istutorial field which is present in otservbr, and not in TFS + if (!fieldExist('istutorial', 'players')) { + $sessionKey .= "\n".floor(time() / 30); + } + + //log_append('slaw.log', $sessionKey); + + $session = [ + 'sessionkey' => $sessionKey, + 'lastlogintime' => 0, + 'ispremium' => $config['lua']['freePremium'] || $account->isPremium(), + 'premiumuntil' => ($account->getPremDays()) > 0 ? (time() + ($account->getPremDays() * 86400)) : 0, + 'status' => 'active', // active, frozen or suspended + 'returnernotification' => false, + 'showrewardnews' => true, + 'isreturner' => true, + 'fpstracking' => false, + 'optiontracking' => false, + 'tournamentticketpurchasestate' => 0, + 'emailcoderequest' => false + ]; + die(json_encode(compact('session', 'playdata'))); + + default: + sendError("Unrecognized event {$action}."); + break; +} + +function create_char($player, $highestLevelId) { + global $config; + return [ + 'worldid' => 0, + 'name' => $player['name'], + 'ismale' => intval($player['sex']) === 1, + 'tutorial' => isset($player['istutorial']) && $player['istutorial'], + 'level' => intval($player['level']), + 'vocation' => $config['vocations'][$player['vocation']], + 'outfitid' => intval($player['looktype']), + 'headcolor' => intval($player['lookhead']), + 'torsocolor' => intval($player['lookbody']), + 'legscolor' => intval($player['looklegs']), + 'detailcolor' => intval($player['lookfeet']), + 'addonsflags' => intval($player['lookaddons']), + 'ishidden' => isset($player['deletion']) && (int)$player['deletion'] === 1, + 'istournamentparticipant' => false, + 'ismaincharacter' => $highestLevelId == $player['id'], + 'dailyrewardstate' => isset($player['isreward']) ? intval($player['isreward']) : 0, + 'remainingdailytournamentplaytime' => 0 + ]; +} diff --git a/nginx-sample.conf b/nginx-sample.conf index f826d21d..5a2deab7 100644 --- a/nginx-sample.conf +++ b/nginx-sample.conf @@ -1,25 +1,29 @@ server { - listen 80; - root /home/otserv/www/public; - index index.php; - server_name your-domain.com; + listen 80; + root /home/otserv/www/public; + index index.php; + server_name your-domain.com; - location / { - try_files $uri $uri/ /index.php; - } + # increase max file upload + client_max_body_size 10M; - location ~ \.php$ { - include snippets/fastcgi-php.conf; - fastcgi_read_timeout 240; - fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; - } + location / { + try_files $uri $uri/ /index.php; + } - location ~ /\.ht { - deny all; - } + location ~ \.php$ { + include snippets/fastcgi-php.conf; + fastcgi_read_timeout 240; + fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; + # for ubuntu 22.04+ it will be php8.1-sock + } - location /system { - deny all; - return 404; - } + location ~ /\.ht { + deny all; + } + + location /system { + deny all; + return 404; + } } diff --git a/plugins/account-create-hint/hint.html.twig b/plugins/account-create-hint/hint.html.twig index d6069d56..5c31dfb1 100644 --- a/plugins/account-create-hint/hint.html.twig +++ b/plugins/account-create-hint/hint.html.twig @@ -1,3 +1,3 @@ To play on {{ config.lua.serverName }} you need an account. -All you have to do to create your new account is to enter an account {% if constant('USE_ACCOUNT_NAME') %}name{% else %}number{% endif %}, password{% if config.recaptcha_enabled %}, confirm reCAPTCHA{% endif %}{% if config.account_country %}, country{% endif %} and your email address. +All you have to do to create your new account is to enter an account {% if constant('USE_ACCOUNT_NAME') %}name{% else %}number{% endif %}, password{% if config.account_country %}, country{% endif %} and your email address. Also you have to agree to the terms presented below. If you have done so, your account {% if constant('USE_ACCOUNT_NAME') %}name{% else %}number{% endif %} will be shown on the following page and your account password will be sent to your email address along with further instructions. If you do not receive the email with your password, please check your spam filter.

    diff --git a/plugins/email-confirmed-reward.json b/plugins/email-confirmed-reward.json new file mode 100644 index 00000000..b1ae4cc1 --- /dev/null +++ b/plugins/email-confirmed-reward.json @@ -0,0 +1,17 @@ +{ + "name": "EMail Confirmed Reward", + "description": "Reward users for confirming their E-Mail.", + "version": "1.0", + "author": "MyAAC Authors", + "contact": "www.my-aac.org", + "hooks": { + "mail-confirmed-reward": { + "type": "EMAIL_CONFIRMED", + "file": "plugins/email-confirmed-reward/reward.php" + } + }, + "uninstall": [ + "plugins/email-confirmed-reward.json", + "plugins/email-confirmed-reward" + ] +} diff --git a/plugins/email-confirmed-reward/reward.php b/plugins/email-confirmed-reward/reward.php new file mode 100644 index 00000000..107ab8d7 --- /dev/null +++ b/plugins/email-confirmed-reward/reward.php @@ -0,0 +1,33 @@ +hasColumn('accounts', 'coins'); +if ($reward['coins'] > 0 && $hasCoinsColumn) { + log_append('email_confirm_error.log', 'accounts.coins column does not exist.'); +} + +if (!isset($account) || !$account->isLoaded()) { + log_append('email_confirm_error.log', 'Account not loaded.'); + return; +} + +if ($reward['premium_points'] > 0) { + $account->setCustomField('premium_points', (int)$account->getCustomField('premium_points') + $reward['premium_points']); + + success(sprintf($reward['message'], $reward['premium_points'], 'premium points')); +} + +if ($reward['coins'] > 0 && $hasCoinsColumn) { + $account->setCustomField('coins', (int)$account->getCustomField('coins') + $reward['coins']); + + success(sprintf($reward['message'], $reward['coins'], 'coins')); +} + +if ($reward['premium_days'] > 0) { + $account->setPremDays($account->getPremDays() + $reward['premium_days']); + $account->save(); + + success(sprintf($reward['message'], $reward['premium_days'], 'premium days')); +} diff --git a/plugins/example.json b/plugins/example.json index b3cf2633..6591a333 100644 --- a/plugins/example.json +++ b/plugins/example.json @@ -6,31 +6,38 @@ "author": "nobody", "contact": "nobody@example.org", "require": { - "myaac": "0.4.3", - "myaac_": ">=0.7,<1.0", // support for defining versions like in composer (since 0.8) - "php": "5.2.0", - "php_": ">5.4,<7.0", // support for defining versions like in composer (since 0.8) + "myaac": "0.9.0", + "myaac_": ">=0.9,<1.0", + "php": "7.4", + "php_": ">7.4,<8.0", "database": "21", - "php-ext": "curl", // php extension needs to be installed (since 0.8) - "ext-curl": ">5.0", // php extension with version specifiec (since 0.8) - "table": "accounts", // table need to exist in database (since 0.8) - "column": "players.online" // column need to exist in database (since 0.8) + "php-ext": "curl", + "ext-curl": ">5.0", + "table": "accounts", + "column": "players.online" }, "install": "plugins/example/install.php", "uninstall": [ "plugins/example.json", "plugins/example-directory", "templates/other-directory" - /*** - this is example of multi line comment - 1. list example - 2. something - ****/ ], "hooks": { "Example Hook": { "type": "BEFORE_PAGE", "file": "plugins/example/before.php" } - } + }, + "routes": { + "First Route": { + "pattern": "/YourAwesomePage/{name:string}/{page:int}", + "file": "plugins/your-plugin/your-awesome-page.php", + "method": "GET", + "priority": "130" + }, + "Redirect Example": { + "redirect_from": "/redirectExample", + "redirect_to": "account/manage" + } + } } diff --git a/release.sh b/release.sh index 6578481b..e720a3a9 100644 --- a/release.sh +++ b/release.sh @@ -13,16 +13,18 @@ fi if [ $1 = "prepare" ]; then # define release version - version=`cat VERSION` + version=`php system/get_version_for_release.php` echo "Preparing to release version $version of the MyAAC Project!" + # make required directories + mkdir -p releases + mkdir -p tmp + # get myaac from git archive git archive --format zip --output tmp/myaac.zip master - # make required directories - mkdir -p releases - mkdir -p tmp && cd tmp + cd tmp/ || exit dir="myaac-$version" if [ -d "$dir" ] ; then @@ -39,9 +41,9 @@ fi if [ $1 = "pack" ]; then # define release version - version=`cat VERSION` + version=`php system/get_version_for_release.php` - cd tmp + cd tmp || exit # tar.gz echo "Creating .tar.gz package.." diff --git a/system/clients.conf.php b/system/clients.conf.php index 4daaec15..b3ec18d5 100644 --- a/system/clients.conf.php +++ b/system/clients.conf.php @@ -76,11 +76,13 @@ $config['clients'] = [ 1096, 1097, 1098, + 1100, 1102, 1140, 1150, 1180, + 1200, 1202, 1215, @@ -89,4 +91,12 @@ $config['clients'] = [ 1240, 1251, 1260, + 1270, + 1280, + 1285, + 1286, + 1290, + 1291, + + 1300, ]; diff --git a/system/compat.php b/system/compat/base.php similarity index 100% rename from system/compat.php rename to system/compat/base.php diff --git a/system/compat/classes.php b/system/compat/classes.php new file mode 100644 index 00000000..051fbdf2 --- /dev/null +++ b/system/compat/classes.php @@ -0,0 +1,15 @@ + + * @copyright 2022 MyAAC + * @link https://my-aac.org + */ +defined('MYAAC') or die('Direct access not allowed!'); + +class Player extends OTS_Player {} +class Guild extends OTS_Guild {} +class GuildRank extends OTS_GuildRank {} +class House extends OTS_House {} diff --git a/system/compat_pages.php b/system/compat/pages.php similarity index 70% rename from system/compat_pages.php rename to system/compat/pages.php index 1830ecd5..3f5d0a0a 100644 --- a/system/compat_pages.php +++ b/system/compat/pages.php @@ -10,6 +10,18 @@ defined('MYAAC') or die('Direct access not allowed!'); switch($page) { + case 'createaccount': + $page = 'account/create'; + break; + + case 'accountmanagement': + $page = 'account/manage'; + break; + + case 'lostaccount': + $page = 'account/lost'; + break; + case 'whoisonline': $page = 'online'; break; @@ -18,6 +30,10 @@ switch($page) $page = 'news'; break; + case 'newsarchive': + $page = 'news/archive'; + break; + case 'tibiarules': $page = 'rules'; break; @@ -37,4 +53,3 @@ switch($page) default: break; } -?> diff --git a/system/exception.php b/system/exception.php index 5c76d66b..3ad1fefe 100644 --- a/system/exception.php +++ b/system/exception.php @@ -39,7 +39,7 @@ function exception_handler($exception) { // we just replace some values manually // cause in case Twig throws exception, we can show it too $content = file_get_contents($template_file); - $content = str_replace(array('{{ BASE_URL }}', '{{ message }}', '{{ backtrace }}', '{{ powered_by }}'), array(BASE_URL, $message, $backtrace_formatted, base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4=')), $content); + $content = str_replace(array('{{ BASE_URL }}', '{{ exceptionClass }}', '{{ message }}', '{{ backtrace }}', '{{ powered_by }}'), array(BASE_URL, get_class($exception), $message, $backtrace_formatted, base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4=')), $content); echo $content; } diff --git a/system/functions.php b/system/functions.php index 3b438705..bd42fae7 100644 --- a/system/functions.php +++ b/system/functions.php @@ -62,20 +62,20 @@ function getFullLink($page, $name, $blank = false) { function getLink($page, $action = null) { $settings = Settings::getInstance(); - return BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : '?') . $page . ($action ? '/' . $action : ''); + return BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : 'index.php/') . $page . ($action ? '/' . $action : ''); } function internalLayoutLink($page, $action = null) {return getLink($page, $action);} function getForumThreadLink($thread_id, $page = NULL) { $settings = Settings::getInstance(); - return BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : '?') . 'forum/thread/' . (int)$thread_id . (isset($page) ? '/' . $page : ''); + return BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : 'index.php/') . 'forum/thread/' . (int)$thread_id . (isset($page) ? '/' . $page : ''); } function getForumBoardLink($board_id, $page = NULL) { $settings = Settings::getInstance(); - return BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : '?') . 'forum/board/' . (int)$board_id . (isset($page) ? '/' . $page : ''); + return BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : 'index.php/') . 'forum/board/' . (int)$board_id . (isset($page) ? '/' . $page : ''); } function getPlayerLink($name, $generate = true) @@ -89,7 +89,7 @@ function getPlayerLink($name, $generate = true) } $settings = Settings::getInstance(); - $url = BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : '?') . 'characters/' . urlencode($name); + $url = BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : 'index.php/') . 'characters/' . urlencode($name); if(!$generate) return $url; return generateLink($url, $name); @@ -98,7 +98,7 @@ function getPlayerLink($name, $generate = true) function getMonsterLink($name, $generate = true) { $settings = Settings::getInstance(); - $url = BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : '?') . 'creatures/' . urlencode($name); + $url = BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : 'index.php/') . 'creatures/' . urlencode($name); if(!$generate) return $url; return generateLink($url, $name); @@ -117,7 +117,7 @@ function getHouseLink($name, $generate = true) } $settings = Settings::getInstance(); - $url = BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : '?') . 'houses/' . urlencode($name); + $url = BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : 'index.php/') . 'houses/' . urlencode($name); if(!$generate) return $url; return generateLink($url, $name); @@ -136,7 +136,7 @@ function getGuildLink($name, $generate = true) } $settings = Settings::getInstance(); - $url = BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : '?') . 'guilds/' . urlencode($name); + $url = BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : 'index.php/') . 'guilds/' . urlencode($name); if(!$generate) return $url; return generateLink($url, $name); @@ -268,6 +268,13 @@ function getForumBoards() return array(); } +// TODO: +// convert forum threads links from just forum/ID +// INTO: forum/thread-name-id, like in XenForo +//function convertForumThreadTitle($title) { +// return str_replace(' ', '-', strtolower($title)); +//} + /** * Retrieves data from myaac database config. * @@ -462,7 +469,7 @@ function tickers() */ function template_place_holder($type) { - global $template_place_holders; + global $twig, $template_place_holders; $ret = ''; if(array_key_exists($type, $template_place_holders) && is_array($template_place_holders[$type])) @@ -471,6 +478,9 @@ function template_place_holder($type) if($type === 'head_start') { $ret .= template_header(); } + elseif ($type === 'body_start') { + $ret .= $twig->render('browsehappy.html.twig'); + } elseif($type === 'body_end') { $ret .= template_ga_code(); } @@ -1034,7 +1044,7 @@ function getTopPlayers($limit = 5) { $deleted = 'deletion'; $is_tfs10 = $db->hasTable('players_online'); - $players = $db->query('SELECT `id`, `name`, `level`, `experience`, `looktype`' . ($db->hasColumn('players', 'lookaddons') ? ', `lookaddons`' : '') . ', `lookhead`, `lookbody`, `looklegs`, `lookfeet`' . ($is_tfs10 ? '' : ', `online`') . ' FROM `players` WHERE `group_id` < ' . config('highscores_groups_hidden') . ' AND `id` NOT IN (' . implode(', ', config('highscores_ids_hidden')) . ') AND `' . $deleted . '` = 0 AND `account_id` != 1 ORDER BY `experience` DESC LIMIT ' . (int)$limit)->fetchAll(); + $players = $db->query('SELECT `id`, `name`, `level`, `vocation`, `experience`, `looktype`' . ($db->hasColumn('players', 'lookaddons') ? ', `lookaddons`' : '') . ', `lookhead`, `lookbody`, `looklegs`, `lookfeet`' . ($is_tfs10 ? '' : ', `online`') . ' FROM `players` WHERE `group_id` < ' . config('highscores_groups_hidden') . ' AND `id` NOT IN (' . implode(', ', config('highscores_ids_hidden')) . ') AND `' . $deleted . '` = 0 AND `account_id` != 1 ORDER BY `experience` DESC LIMIT ' . (int)$limit)->fetchAll(); if($is_tfs10) { foreach($players as &$player) { @@ -1151,6 +1161,12 @@ function clearCache() global $template_name; if ($cache->fetch('template_ini' . $template_name, $tmp)) $cache->delete('template_ini' . $template_name); + + if ($cache->fetch('plugins_hooks', $tmp)) + $cache->delete('plugins_hooks'); + + if ($cache->fetch('plugins_routes', $tmp)) + $cache->delete('plugins_routes'); } deleteDirectory(CACHE . 'signatures', ['index.html'], true); @@ -1158,6 +1174,12 @@ function clearCache() deleteDirectory(CACHE . 'plugins', ['index.html'], true); deleteDirectory(CACHE, ['signatures', 'twig', 'plugins', 'index.html'], true); + // routes cache + $routeCacheFile = CACHE . 'route.cache'; + if (file_exists($routeCacheFile)) { + unlink($routeCacheFile); + } + return true; } @@ -1432,6 +1454,32 @@ function Outfits_loadfromXML() return array('id' => $looktype, 'type' => $type, 'name' => $lookname, 'premium' => $premium, 'unlocked' => $unlocked, 'enabled' => $enabled); } +function Mounts_loadfromXML() +{ + global $config; + $file_path = $config['data_path'] . 'XML/mounts.xml'; + if (!file_exists($file_path)) { return null; } + + $xml = new DOMDocument; + $xml->load($file_path); + + $mounts = null; + foreach ($xml->getElementsByTagName('mount') as $mount) { + $mounts[] = Mount_parseNode($mount); + } + return $mounts; +} + + function Mount_parseNode($node) { + $id = (int)$node->getAttribute('id'); + $clientid = (int)$node->getAttribute('clientid'); + $name = $node->getAttribute('name'); + $speed = (int)$node->getAttribute('speed'); + $premium = $node->getAttribute('premium'); + $type = $node->getAttribute('type'); + return array('id' => $id, 'clientid' => $clientid, 'name' => $name, 'speed' => $speed, 'premium' => $premium, 'type' => $type); +} + function left($str, $length) { return substr($str, 0, $length); } @@ -1473,9 +1521,41 @@ function truncate($string, $length) return $string; } +function getAccountLoginByLabel() +{ + $ret = ''; + if (config('account_login_by_email')) { + $ret = 'Email Address'; + if (config('account_login_by_email_fallback')) { + $ret .= ' or '; + } + } + + if (!config('account_login_by_email') || config('account_login_by_email_fallback')) { + $ret .= 'Account ' . (USE_ACCOUNT_NAME ? 'Name' : 'Number'); + } + + return $ret; +} + +function camelCaseToUnderscore($input) +{ + return ltrim(strtolower(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $input)), '_'); +} + +function removeIfFirstSlash(&$text) { + if(strpos($text, '/') === 0) { + $text = str_replace_first('/', '', $text); + } +}; + +function escapeHtml($html) { + return htmlentities($html, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); +} + // validator functions require_once LIBS . 'validator.php'; -require_once SYSTEM . 'compat.php'; +require_once SYSTEM . 'compat/base.php'; // custom functions require SYSTEM . 'functions_custom.php'; diff --git a/system/get_version_for_release.php b/system/get_version_for_release.php new file mode 100644 index 00000000..c4070d4d --- /dev/null +++ b/system/get_version_for_release.php @@ -0,0 +1,6 @@ +_file)) { - $ret = require BASE . $this->_file; - } + $ret = include BASE . $this->_file; return !isset($ret) || $ret == 1 || $ret; } @@ -120,5 +134,7 @@ class Hooks foreach(Plugins::getHooks() as $hook) { $this->register($hook['name'], $hook['type'], $hook['file']); } + + Plugins::clearWarnings(); } } diff --git a/system/init.php b/system/init.php index 2bdd1261..9175db9b 100644 --- a/system/init.php +++ b/system/init.php @@ -34,6 +34,10 @@ $cache = Cache::getInstance(); // twig require_once SYSTEM . 'twig.php'; +// action, used by many pages +$action = $_REQUEST['action'] ?? ''; +define('ACTION', $action); + // trim values we receive if(isset($_POST)) { @@ -138,6 +142,9 @@ if($settingsItemImagesURL['value'][strlen($settingsItemImagesURL['value']) - 1] } define('USE_ACCOUNT_NAME', $db->hasColumn('accounts', 'name')); +define('USE_ACCOUNT_NUMBER', $db->hasColumn('accounts', 'number')); +define('USE_ACCOUNT_SALT', $db->hasColumn('accounts', 'salt')); + // load vocation names $tmp = ''; if($cache->enabled() && $cache->fetch('vocations', $tmp)) { diff --git a/system/libs/CreateCharacter.php b/system/libs/CreateCharacter.php index 5aa87ad4..09ee5d89 100644 --- a/system/libs/CreateCharacter.php +++ b/system/libs/CreateCharacter.php @@ -12,27 +12,44 @@ class CreateCharacter { /** - * @param string $name - * @param int $sex - * @param int $vocation - * @param int $town - * @param array $errors + * @param $name + * @param $errors * @return bool */ - public function check($name, $sex, &$vocation, &$town, &$errors) { + public function checkName($name, &$errors) + { $minLength = config('character_name_min_length'); $maxLength = config('character_name_max_length'); - if(empty($name)) + if(empty($name)) { $errors['name'] = 'Please enter a name for your character!'; - else if(strlen($name) > $maxLength) - $errors['name'] = 'Name is too long. Max. length '.$maxLength.' letters.'; - else if(strlen($name) < $minLength) - $errors['name'] = 'Name is too short. Min. length '.$minLength.' letters.'; - else { - if(!admin() && !Validator::newCharacterName($name)) { - $errors['name'] = Validator::getLastError(); - } + return false; + } + + if(strlen($name) > $maxLength) { + $errors['name'] = 'Name is too long. Max. length ' . $maxLength . ' letters.'; + return false; + } + + if(strlen($name) < $minLength) { + $errors['name'] = 'Name is too short. Min. length ' . $minLength . ' letters.'; + return false; + } + + $name_length = strlen($name); + if(strspn($name, "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM- '") != $name_length) { + $errors['name'] = 'This name contains invalid letters, words or format. Please use only a-Z, - , \' and space.'; + return false; + } + + if(!preg_match("/[A-z ']/", $name)) { + $errors['name'] = 'Your name contains illegal characters.'; + return false; + } + + if(!admin() && !Validator::newCharacterName($name)) { + $errors['name'] = Validator::getLastError(); + return false; } $player = new OTS_Player(); @@ -42,20 +59,38 @@ class CreateCharacter return false; } - if(empty($sex) && $sex != "0") + return empty($errors); + } + + /** + * @param string $name + * @param int $sex + * @param int $vocation + * @param int $town + * @param array $errors + * @return bool + */ + public function check($name, $sex, &$vocation, &$town, &$errors) + { + $this->checkName($name, $errors); + + if(empty($sex) && $sex != "0") { $errors['sex'] = 'Please select the sex for your character!'; + } if(count(config('character_samples')) > 1) { if(!isset($vocation)) $errors['vocation'] = 'Please select a vocation for your character.'; } - else + else { $vocation = config('character_samples')[0]; + } if(count(config('character_towns')) > 1) { - if(!isset($town)) + if(!isset($town)) { $errors['town'] = 'Please select a town for your character.'; + } } else { $town = config('character_towns')[0]; @@ -158,8 +193,14 @@ class CreateCharacter $player->setManaSpent($char_to_copy->getManaSpent()); $player->setSoul($char_to_copy->getSoul()); - for($skill = POT::SKILL_FIRST; $skill <= POT::SKILL_LAST; $skill++) - $player->setSkill($skill, 10); + for($skill = POT::SKILL_FIRST; $skill <= POT::SKILL_LAST; $skill++) { + $value = 10; + if (config('use_character_sample_skills')) { + $value = $char_to_copy->getSkill($skill); + } + + $player->setSkill($skill, $value); + } $player->setLookBody($char_to_copy->getLookBody()); $player->setLookFeet($char_to_copy->getLookFeet()); @@ -199,16 +240,22 @@ class CreateCharacter if($db->hasTable('player_skills')) { for($i=0; $i<7; $i++) { + $value = 10; + if (config('use_character_sample_skills')) { + $value = $char_to_copy->getSkill($i); + } $skillExists = $db->query('SELECT `skillid` FROM `player_skills` WHERE `player_id` = ' . $player->getId() . ' AND `skillid` = ' . $i); if($skillExists->rowCount() <= 0) { - $db->query('INSERT INTO `player_skills` (`player_id`, `skillid`, `value`, `count`) VALUES ('.$player->getId().', '.$i.', 10, 0)'); + $db->query('INSERT INTO `player_skills` (`player_id`, `skillid`, `value`, `count`) VALUES ('.$player->getId().', '.$i.', ' . $value . ', 0)'); } } } $loaded_items_to_copy = $db->query("SELECT * FROM player_items WHERE player_id = ".$char_to_copy->getId().""); - foreach($loaded_items_to_copy as $save_item) - $db->query("INSERT INTO `player_items` (`player_id` ,`pid` ,`sid` ,`itemtype`, `count`, `attributes`) VALUES ('".$player->getId()."', '".$save_item['pid']."', '".$save_item['sid']."', '".$save_item['itemtype']."', '".$save_item['count']."', '".$save_item['attributes']."');"); + foreach($loaded_items_to_copy as $save_item) { + $blob = $db->quote($save_item['attributes']); + $db->query("INSERT INTO `player_items` (`player_id` ,`pid` ,`sid` ,`itemtype`, `count`, `attributes`) VALUES ('".$player->getId()."', '".$save_item['pid']."', '".$save_item['sid']."', '".$save_item['itemtype']."', '".$save_item['count']."', $blob);"); + } global $twig; $twig->display('success.html.twig', array( diff --git a/system/libs/forum.php b/system/libs/forum.php index 648a9d99..4922a9c3 100644 --- a/system/libs/forum.php +++ b/system/libs/forum.php @@ -11,7 +11,7 @@ defined('MYAAC') or die('Direct access not allowed!'); $configForumTablePrefix = config('forum_table_prefix'); -if(!empty(trim($configForumTablePrefix))) { +if(null !== $configForumTablePrefix && !empty(trim($configForumTablePrefix))) { if(!in_array($configForumTablePrefix, array('myaac_', 'z_'))) { throw new RuntimeException('Invalid value for forum_table_prefix in config.php. Can be only: "myaac_" or "z_".'); } @@ -322,4 +322,4 @@ class Forum return $hasAccess; } } -?> \ No newline at end of file +?> diff --git a/system/libs/news.php b/system/libs/news.php index a9131e02..b0f17158 100644 --- a/system/libs/news.php +++ b/system/libs/news.php @@ -8,12 +8,12 @@ class News $errors[] = 'Please fill all inputs.'; return false; } - if(strlen($title) > TITLE_LIMIT) { - $errors[] = 'News title cannot be longer than ' . TITLE_LIMIT . ' characters.'; + if(strlen($title) > NEWS_TITLE_LIMIT) { + $errors[] = 'News title cannot be longer than ' . NEWS_TITLE_LIMIT . ' characters.'; return false; } - if(strlen($body) > BODY_LIMIT) { - $errors[] = 'News content cannot be longer than ' . BODY_LIMIT . ' characters.'; + if(strlen($body) > NEWS_BODY_LIMIT) { + $errors[] = 'News content cannot be longer than ' . NEWS_BODY_LIMIT . ' characters.'; return false; } if(strlen($article_text) > ARTICLE_TEXT_LIMIT) { @@ -138,4 +138,4 @@ class News } } } -} \ No newline at end of file +} diff --git a/system/libs/plugins.php b/system/libs/plugins.php index c8f3e583..5568f992 100644 --- a/system/libs/plugins.php +++ b/system/libs/plugins.php @@ -71,12 +71,117 @@ class Plugins { } } + public static function getRoutes() + { + $cache = Cache::getInstance(); + if ($cache->enabled()) { + $tmp = ''; + if ($cache->fetch('plugins_routes', $tmp)) { + return unserialize($tmp); + } + } + + $routes = []; + foreach(get_plugins() as $filename) { + $string = file_get_contents(PLUGINS . $filename . '.json'); + $string = self::removeComments($string); + $plugin = json_decode($string, true); + self::$plugin_json = $plugin; + if ($plugin == null) { + self::$warnings[] = 'Cannot load ' . $filename . '.json. File might be not a valid json code.'; + continue; + } + + if(isset($plugin['enabled']) && !getBoolean($plugin['enabled'])) { + self::$warnings[] = 'Skipping ' . $filename . '... The plugin is disabled.'; + continue; + } + + $warningPreTitle = 'Plugin: ' . $filename . ' - '; + + if (isset($plugin['routes'])) { + foreach ($plugin['routes'] as $_name => $info) { + // default method: get + $method = $info['method'] ?? ['GET']; + if ($method !== '*') { + $methods = is_string($method) ? explode(',', $info['method']) : $method; + foreach ($methods as $method) { + if (!in_array($method, ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'])) { + self::$warnings[] = $warningPreTitle . 'Not allowed method ' . $method . '... Disabling this route...'; + } + } + } + else { + $methods = '*'; // all available methods + } + + if (!isset($info['priority'])) { + $info['priority'] = 100; // default priority + } + + if (isset($info['redirect_from'])) { + removeIfFirstSlash($info['redirect_from']); + + $info['pattern'] = $info['redirect_from']; + if (!isset($info['redirect_to'])) { + self::$warnings[] = $warningPreTitle . 'redirect set without "redirect_to".'; + } + else { + removeIfFirstSlash($info['redirect_to']); + $info['file'] = '__redirect__/' . $info['redirect_to']; + } + } + + // replace first occurence of / in pattern if found (will be auto-added later) + removeIfFirstSlash($info['pattern']); + + foreach ($routes as $id => &$route) { + if($route[1] == $info['pattern']) { + if($info['priority'] < $route[3]) { + self::$warnings[] = $warningPreTitle . "Duplicated route with lower priority: {$info['pattern']}. Disabling this route..."; + continue 2; + } + else { + self::$warnings[] = $warningPreTitle . "Duplicated route with lower priority: {$route[1]} ({$route[3]}). Disabling this route..."; + unset($routes[$id]); + } + } + } + + $routes[] = [$methods, $info['pattern'], $info['file'], $info['priority']]; + } + } + } +/* + usort($routes, function ($a, $b) + { + // key 3 is priority + if ($a[3] == $b[3]) { + return 0; + } + + return ($a[3] > $b[3]) ? -1 : 1; + }); +*/ + // cleanup before passing back + // priority is not needed anymore + foreach ($routes as &$route) { + unset($route[3]); + } + + if ($cache->enabled()) { + $cache->set('plugins_routes', serialize($routes), 600); + } + + return $routes; + } + public static function getHooks() { $cache = Cache::getInstance(); if ($cache->enabled()) { $tmp = ''; - if ($cache->fetch('hooks', $tmp)) { + if ($cache->fetch('plugins_hooks', $tmp)) { return unserialize($tmp); } } @@ -96,7 +201,7 @@ class Plugins { } if ($cache->enabled()) { - $cache->set('hooks', serialize($hooks), 600); + $cache->set('plugins_hooks', serialize($hooks), 600); } return $hooks; @@ -259,27 +364,59 @@ class Plugins { } if(in_array($req, array('php-ext', 'php-extension'))) { // require php extension - if(!extension_loaded($version)) { - self::$error = "This plugin requires php extension: " . $version . " to be installed."; + $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') { - if(!$db->hasTable($version)) { - self::$error = "This plugin requires table: " . $version . " to exist in the database."; + $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') { - $tmp = explode('.', $version); - if(count($tmp) == 2) { - if(!$db->hasColumn($tmp[0], $tmp[1])) { - self::$error = "This plugin requires database column: " . $tmp[0] . "." . $tmp[1] . " to exist in database."; - $continue = false; - break; + $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) { @@ -412,6 +549,10 @@ class Plugins { return self::$warnings; } + public static function clearWarnings() { + self::$warnings = []; + } + public static function getError() { return self::$error; } diff --git a/system/libs/pot/OTS_Account.php b/system/libs/pot/OTS_Account.php index beed47dd..89db0d16 100644 --- a/system/libs/pot/OTS_Account.php +++ b/system/libs/pot/OTS_Account.php @@ -101,6 +101,37 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable return $name; } + /** + * @param $email + * @return mixed + * @throws Exception + */ + public function createWithEmail($email = null) + { + // if name is not passed then it will be generated randomly + if( !isset($email) ) + { + throw new Exception(__CLASS__ . ':' . __METHOD__ . ' createWithEmail called without e-mail.'); + } + + // repeats until name is unique + do + { + $name = uniqid(); + + $query = $this->db->query('SELECT `id` FROM `accounts` WHERE `name` = ' . $this->db->quote($name)); + } while($query->rowCount() >= 1); + + // saves blank account info + $this->db->exec('INSERT INTO `accounts` (`name`, `password`, `email`, `created`) VALUES (' . $this->db->quote($name) . ', ' . '\'\', ' . $this->db->quote($email) . ', ' . time() . ')'); + + // reads created account's ID + $this->data['id'] = $this->db->lastInsertId(); + $this->data['name'] = $name; + + // return name of newly created account + return $name; + } /** * Creates new account. * @@ -138,11 +169,32 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable */ public function create($name = NULL, $id = NULL) { - // saves blank account info - $this->db->exec('INSERT INTO `accounts` (' . (isset($id) ? '`id`,' : '') . (isset($name) ? '`name`,' : '') . '`password`, `email`, `created`) VALUES (' . (isset($id) ? $id . ',' : '') . (isset($name) ? $this->db->quote($name) . ',' : '') . ' \'\', \'\',' . time() . ')'); + if(isset($name)) { + $nameOrNumber = 'name'; + $nameOrNumberValue = $name; + } + else { + if (USE_ACCOUNT_NUMBER) { + $nameOrNumber = 'number'; + $nameOrNumberValue = $id; + $id = null; + } + else { + $nameOrNumber = null; + } + } - if(isset($name)) + // 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; + } + else { + if (USE_ACCOUNT_NUMBER) { + $this->data['number'] = $name; + } + } $lastInsertId = $this->db->lastInsertId(); if($lastInsertId != 0) { @@ -179,15 +231,26 @@ 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, $searchOnlyById = false) { if(!$fresh && isset(self::$cache[$id])) { $this->data = self::$cache[$id]; return; } + $numberColumn = 'id'; + $nameOrNumber = ''; + if (!$searchOnlyById) { + if (USE_ACCOUNT_NAME) { + $nameOrNumber = '`name`,'; + } else if (USE_ACCOUNT_NUMBER) { + $nameOrNumber = '`number`,'; + $numberColumn = 'number'; + } + } + // SELECT query on database - $this->data = $this->db->query('SELECT `id`, ' . ($this->db->hasColumn('accounts', 'name') ? '`name`,' : '') . '`password`, `email`, `blocked`, `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(); + $this->data = $this->db->query('SELECT `id`, ' . $nameOrNumber . '`password`, `email`, `blocked`, `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 `' . $numberColumn . '` = ' . (int) $id)->fetch(); self::$cache[$id] = $this->data; } @@ -306,6 +369,15 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable return $this->data['id']; } + public function getNumber() + { + if (isset($this->data['number'])) { + return $this->data['number']; + } + + return $this->data['id']; + } + public function getRLName() { if( !isset($this->data['rlname']) ) @@ -1019,6 +1091,7 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws PDOException On PDO operation error. * @return Iterator List of players. */ + #[\ReturnTypeWillChange] public function getIterator() { return $this->getPlayersList(); @@ -1033,7 +1106,7 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws PDOException On PDO operation error. * @return int Count of players. */ - public function count() + public function count(): int { return $this->getPlayersList()->count(); } diff --git a/system/libs/pot/OTS_Base_DB.php b/system/libs/pot/OTS_Base_DB.php index e6ffa9be..f97745b4 100644 --- a/system/libs/pot/OTS_Base_DB.php +++ b/system/libs/pot/OTS_Base_DB.php @@ -83,34 +83,47 @@ abstract class OTS_Base_DB extends PDO implements IOTS_DB $startTime = microtime(true); } - $ret = parent::query(...$args);; + $ret = parent::query(...$args); if($this->logged) { $totalTime = microtime(true) - $startTime; - $this->log .= round($totalTime, 4) . ' ms - ' . $query . PHP_EOL; + $this->log .= round($totalTime, 4) . ' ms - ' . $args[0] . PHP_EOL; } return $ret; } - public function select($table, $where, $limit = null) + public function select($table, $where = [], $limit = null) { $fields = array_keys($where); $values = array_values($where); - $query = 'SELECT * FROM ' . $this->tableName($table) . ' WHERE ('; + $query = 'SELECT * FROM ' . $this->tableName($table); - $count = count($fields); - for ($i = 0; $i < $count; $i++) - $query.= $this->fieldName($fields[$i]).' = '.$this->quote($values[$i]).' AND '; + if (!empty($where)) { + $query .= ' WHERE ('; + + $count = count($fields); + for ($i = 0; $i < $count; $i++) { + $query .= $this->fieldName($fields[$i]) . ' = ' . $this->quote($values[$i]) . ' AND '; + } + + $query = substr($query, 0, -4); + $query .= ')'; + } - $query = substr($query, 0, -4); if (isset($limit)) - $query .=') LIMIT '.$limit.';'; + $query .=' LIMIT '.$limit.';'; else - $query .=');'; + $query .=';'; $query = $this->query($query); - if($query->rowCount() != 1) return false; - return $query->fetch(); + $rowCount = $query->rowCount(); + if ($rowCount <= 0) return false; + else if ($rowCount == 1) { + return $query->fetch(); + } + + return $query->fetchAll(); + } public function insert($table, $data) diff --git a/system/libs/pot/OTS_Base_List.php b/system/libs/pot/OTS_Base_List.php index c3df55df..4581fdf2 100644 --- a/system/libs/pot/OTS_Base_List.php +++ b/system/libs/pot/OTS_Base_List.php @@ -190,6 +190,7 @@ abstract class OTS_Base_List implements IOTS_DAO, Iterator, Countable * @version 0.1.3 * @return OTS_Base_DAO Current row. */ + #[\ReturnTypeWillChange] public function current() { $id = current($this->rows); @@ -203,7 +204,7 @@ abstract class OTS_Base_List implements IOTS_DAO, Iterator, Countable * * @throws PDOException On PDO operation error. */ - public function rewind() + public function rewind(): void { $this->rows = $this->db->query( $this->getSQL() )->fetchAll(); } @@ -211,7 +212,7 @@ abstract class OTS_Base_List implements IOTS_DAO, Iterator, Countable /** * Moves to next row. */ - public function next() + public function next(): void { next($this->rows); } @@ -221,6 +222,7 @@ abstract class OTS_Base_List implements IOTS_DAO, Iterator, Countable * * @return mixed Array key. */ + #[\ReturnTypeWillChange] public function key() { return key($this->rows); @@ -231,7 +233,7 @@ abstract class OTS_Base_List implements IOTS_DAO, Iterator, Countable * * @return bool Does next row exist. */ - public function valid() + public function valid(): bool { return key($this->rows) !== null; } @@ -243,7 +245,7 @@ abstract class OTS_Base_List implements IOTS_DAO, Iterator, Countable * @return int Number of rows. * @throws PDOException On PDO operation error. */ - public function count() + public function count(): int { return $this->db->query( $this->getSQL(true) )->fetchColumn(); } diff --git a/system/libs/pot/OTS_Container.php b/system/libs/pot/OTS_Container.php index 71340d7c..7b93570f 100644 --- a/system/libs/pot/OTS_Container.php +++ b/system/libs/pot/OTS_Container.php @@ -15,11 +15,11 @@ /** * Container item representation. - * + * *

    * This class represents items that can contain other items. It's {@link OTS_Container::count() count() method} has been overwritten so it now doesn't return count of current item (if it would even be possible for containers) but amount of items within (not recursively). *

    - * + * * @package POT * @version 0.1.3 */ @@ -27,14 +27,14 @@ class OTS_Container extends OTS_Item implements IteratorAggregate { /** * Contained items. - * + * * @var array */ private $items = array(); /** * Adds item to container. - * + * * @param OTS_Item $item Item. */ public function addItem(OTS_Item $item) @@ -44,11 +44,11 @@ class OTS_Container extends OTS_Item implements IteratorAggregate /** * Removes given item from current container. - * + * *

    * Passed item must be exacly instance of item which is stored in container, not it's copy. This method bases on PHP references. *

    - * + * * @param OTS_Item $item Item. * @tutorial POT/Players.pkg#deleting */ @@ -66,14 +66,14 @@ class OTS_Container extends OTS_Item implements IteratorAggregate /** * Number of items inside container. - * + * *

    * OTS_Container implementation of Countable interface differs from {@link OTS_Item OTS_Item} implemention. {@link OTS_Item::count() OTS_Item::count()} returns count of given item, OTS_Container::count() returns number of items inside container. If somehow it would be possible to make container items with more then 1 in one place, you can use {@link OTS_Item::getCount() OTS_Item::getCount()} and {@link OTS_Item::setCount() OTS_Item::setCount()} in code where you are not sure if working with regular item, or container. *

    - * + * * @return int Number of items. */ - public function count() + public function count(): int { return count($items); } @@ -123,7 +123,7 @@ class OTS_Container extends OTS_Item implements IteratorAggregate /** * Returns iterator handle for loops. - * + * * @version 0.1.0 * @since 0.1.0 * @return ArrayIterator Items iterator. @@ -135,7 +135,7 @@ class OTS_Container extends OTS_Item implements IteratorAggregate /** * Clones all contained items. - * + * * @version 0.1.3 * @since 0.1.3 */ diff --git a/system/libs/pot/OTS_DB_PDOQuery.php b/system/libs/pot/OTS_DB_PDOQuery.php index 24ebfa3a..15939173 100644 --- a/system/libs/pot/OTS_DB_PDOQuery.php +++ b/system/libs/pot/OTS_DB_PDOQuery.php @@ -6,7 +6,7 @@ if (PHP_VERSION_ID >= 80000) { /** * @return PDOStatement */ - public function query(?string $query = null, ?int $fetchMode = null, mixed ...$fetchModeArgs) + public function query(?string $query = null, ?int $fetchMode = null, mixed ...$fetchModeArgs): PDOStatement { return $this->doQuery($query, $fetchMode, ...$fetchModeArgs); } diff --git a/system/libs/pot/OTS_Group.php b/system/libs/pot/OTS_Group.php index 819d8b50..19f770f1 100644 --- a/system/libs/pot/OTS_Group.php +++ b/system/libs/pot/OTS_Group.php @@ -538,6 +538,7 @@ class OTS_Group extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws PDOException On PDO operation error. * @return Iterator List of players. */ + #[\ReturnTypeWillChange] public function getIterator() { return $this->getPlayersList(); @@ -552,7 +553,7 @@ class OTS_Group extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws PDOException On PDO operation error. * @return int Count of players. */ - public function count() + public function count(): int { return $this->getPlayersList()->count(); } diff --git a/system/libs/pot/OTS_Groups_List.php b/system/libs/pot/OTS_Groups_List.php index 083615de..868180dd 100644 --- a/system/libs/pot/OTS_Groups_List.php +++ b/system/libs/pot/OTS_Groups_List.php @@ -41,7 +41,7 @@ class OTS_Groups_List implements IteratorAggregate, Countable $info['access'] = $group['name']; $this->groups[$group['id']] = new OTS_Group($info); } - + return; } @@ -50,7 +50,7 @@ class OTS_Groups_List implements IteratorAggregate, Countable global $config; $file = $config['data_path'] . 'XML/groups.xml'; } - + if(!@file_exists($file)) { error('Error: Cannot load groups.xml. More info in system/logs/error.log file.'); log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). It doesnt exist.'); @@ -99,7 +99,7 @@ class OTS_Groups_List implements IteratorAggregate, Countable log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). Error: ' . print_r(error_get_last(), true)); return; } - + // loads groups foreach($groups->getElementsByTagName('group') as $group) { @@ -157,7 +157,7 @@ class OTS_Groups_List implements IteratorAggregate, Countable if($id > $group_id) $group_id = $id; } - + return $group_id; } @@ -196,6 +196,7 @@ class OTS_Groups_List implements IteratorAggregate, Countable * @since 0.1.5 * @return AppendIterator Iterator for all groups. */ + #[\ReturnTypeWillChange] public function getIterator() { $iterator = new AppendIterator(); @@ -210,7 +211,7 @@ class OTS_Groups_List implements IteratorAggregate, Countable * @since 0.1.5 * @return int Amount of all groups. */ - public function count() + public function count(): int { return count($this->groups); } diff --git a/system/libs/pot/OTS_Guild.php b/system/libs/pot/OTS_Guild.php index 27e3e102..98dd47cf 100644 --- a/system/libs/pot/OTS_Guild.php +++ b/system/libs/pot/OTS_Guild.php @@ -709,6 +709,7 @@ class OTS_Guild extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws PDOException On PDO operation error. * @return Iterator List of ranks. */ + #[\ReturnTypeWillChange] public function getIterator() { return $this->getGuildRanksList(); @@ -723,7 +724,7 @@ class OTS_Guild extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws PDOException On PDO operation error. * @return int Count of ranks. */ - public function count() + public function count(): int { return $this->getGuildRanksList()->count(); } diff --git a/system/libs/pot/OTS_GuildRank.php b/system/libs/pot/OTS_GuildRank.php index 24e1478c..a8832faa 100644 --- a/system/libs/pot/OTS_GuildRank.php +++ b/system/libs/pot/OTS_GuildRank.php @@ -396,6 +396,7 @@ class OTS_GuildRank extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws PDOException On PDO operation error. * @return Iterator List of players. */ + #[\ReturnTypeWillChange] public function getIterator() { return $this->getPlayersList(); @@ -410,7 +411,7 @@ class OTS_GuildRank extends OTS_Row_DAO implements IteratorAggregate, Countable * @throws PDOException On PDO operation error. * @return int Count of players. */ - public function count() + public function count(): int { return $this->getPlayersList()->count(); } diff --git a/system/libs/pot/OTS_HousesList.php b/system/libs/pot/OTS_HousesList.php index eda74f45..722e049f 100644 --- a/system/libs/pot/OTS_HousesList.php +++ b/system/libs/pot/OTS_HousesList.php @@ -15,7 +15,7 @@ /** * Wrapper for houses list. - * + * * @package POT * @version 0.1.3 * @tutorial POT/data_directory.pkg#towns.houses @@ -24,14 +24,14 @@ class OTS_HousesList implements IteratorAggregate, Countable, ArrayAccess { /** * List of houses elements. - * + * * @var array */ private $houses = array(); /** * Loads houses information. - * + * * @version 0.1.3 * @param string $path Houses file. * @throws DOMException On DOM operation error. @@ -49,11 +49,11 @@ class OTS_HousesList implements IteratorAggregate, Countable, ArrayAccess /** * Magic PHP5 method. - * + * *

    * Allows object importing from {@link http://www.php.net/manual/en/function.var-export.php var_export()}. *

    - * + * * @param array $properties List of object properties. * @throws DOMException On DOM operation error. */ @@ -72,7 +72,7 @@ class OTS_HousesList implements IteratorAggregate, Countable, ArrayAccess /** * Checks if given house exists on list. - * + * * @version 0.1.3 * @since 0.1.3 * @param string $name Name. @@ -94,7 +94,7 @@ class OTS_HousesList implements IteratorAggregate, Countable, ArrayAccess /** * Returns house information. - * + * * @version 0.1.3 * @param int $id House ID. * @return OTS_House House information wrapper. @@ -112,7 +112,7 @@ class OTS_HousesList implements IteratorAggregate, Countable, ArrayAccess /** * Checks if given house ID exists on list. - * + * * @version 0.1.3 * @since 0.1.3 * @param int $id ID. @@ -125,7 +125,7 @@ class OTS_HousesList implements IteratorAggregate, Countable, ArrayAccess /** * Returns ID of house with given name. - * + * * @version 0.1.3 * @param string $name House name. * @return int House ID. @@ -147,17 +147,17 @@ class OTS_HousesList implements IteratorAggregate, Countable, ArrayAccess /** * Returns amount of houses. - * + * * @return int Count of houses. */ - public function count() + public function count(): int { return count($this->houses); } /** * Returns iterator handle for loops. - * + * * @return ArrayIterator Houses list iterator. */ public function getIterator() @@ -167,7 +167,7 @@ class OTS_HousesList implements IteratorAggregate, Countable, ArrayAccess /** * Checks if given element exists. - * + * * @param string|int $offset Array key. * @return bool True if it's set. */ @@ -185,7 +185,7 @@ class OTS_HousesList implements IteratorAggregate, Countable, ArrayAccess /** * Returns item from given position. - * + * * @version 0.1.3 * @param string|int $offset Array key. * @return OTS_House|int If key is an integer (type-sensitive!) then returns house instance. If it's a string then return associated ID found by house name. @@ -204,7 +204,7 @@ class OTS_HousesList implements IteratorAggregate, Countable, ArrayAccess /** * This method is implemented for ArrayAccess interface. In fact you can't write/append to houses list. Any call to this method will cause {@link E_OTS_ReadOnly E_OTS_ReadOnly} raise. - * + * * @param string|int $offset Array key. * @param mixed $value Field value. * @throws E_OTS_ReadOnly Always - this class is read-only. @@ -216,7 +216,7 @@ class OTS_HousesList implements IteratorAggregate, Countable, ArrayAccess /** * This method is implemented for ArrayAccess interface. In fact you can't write/append to houses list. Any call to this method will cause {@link E_OTS_ReadOnly E_OTS_ReadOnly} raise. - * + * * @param string|int $offset Array key. * @throws E_OTS_ReadOnly Always - this class is read-only. */ @@ -227,11 +227,11 @@ class OTS_HousesList implements IteratorAggregate, Countable, ArrayAccess /** * Returns string representation of object. - * + * *

    * If any display driver is currently loaded then it uses it's method. *

    - * + * * @version 0.1.3 * @since 0.1.3 * @return string String representation of object. diff --git a/system/libs/pot/OTS_Item.php b/system/libs/pot/OTS_Item.php index cbcb2fa9..449bf73c 100644 --- a/system/libs/pot/OTS_Item.php +++ b/system/libs/pot/OTS_Item.php @@ -15,11 +15,11 @@ /** * Single item representation. - * + * *

    * This class represents item that player has. It has no information about item feature, just it's handle in database. To get information about item type and it's features you have to use {@link OTS_ItemType OTS_ItemType class} - you can get it's object by calling {@link OTS_Item::getItemType() getItemType() method}, however you need to have global item types list loaded. *

    - * + * * @package POT * @version 0.1.0 * @property int $count Amount of item. @@ -31,28 +31,28 @@ class OTS_Item implements Countable { /** * Item ID. - * + * * @var int */ private $id; /** * Item count. - * + * * @var int */ private $count = 0; /** * Additional attributes. - * + * * @var string */ private $attributes; /** * Creates item of given ID. - * + * * @param int $id Item ID. */ public function __construct($id) @@ -62,7 +62,7 @@ class OTS_Item implements Countable /** * Returns item type. - * + * * @return int Item ID. */ public function getId() @@ -72,7 +72,7 @@ class OTS_Item implements Countable /** * Returns count of item. - * + * * @return int Count of item. */ public function getCount() @@ -82,7 +82,7 @@ class OTS_Item implements Countable /** * Sets count of item. - * + * * @param int $count Count. */ public function setCount($count) @@ -92,7 +92,7 @@ class OTS_Item implements Countable /** * Returns item custom attributes. - * + * * @return string Attributes. */ public function getAttributes() @@ -102,7 +102,7 @@ class OTS_Item implements Countable /** * Sets item attributes. - * + * * @param string $attributes Item Attributes. */ public function setAttributes($attributes) @@ -112,7 +112,7 @@ class OTS_Item implements Countable /** * Returns type of item. - * + * * @version 0.1.0 * @since 0.1.0 * @return OTS_ItemType Returns item type of item (null if not exists). @@ -125,17 +125,17 @@ class OTS_Item implements Countable /** * Count value for current item. - * + * * @return int Count of item. */ - public function count() + public function count(): int { return $this->count; } /** * Magic PHP5 method. - * + * * @version 0.1.0 * @since 0.1.0 * @param string $name Property name. @@ -166,7 +166,7 @@ class OTS_Item implements Countable /** * Magic PHP5 method. - * + * * @version 0.1.0 * @since 0.1.0 * @param string $name Property name. diff --git a/system/libs/pot/OTS_ItemsList.php b/system/libs/pot/OTS_ItemsList.php index a9dcb2dd..1865117a 100644 --- a/system/libs/pot/OTS_ItemsList.php +++ b/system/libs/pot/OTS_ItemsList.php @@ -7,7 +7,7 @@ /** * Code in this file bases on oryginal OTServ items loading C++ code (itemloader.h, items.cpp, items.h). - * + * * @package POT * @version 0.1.3 * @author Wrzasq @@ -17,7 +17,7 @@ /** * Items list loader. - * + * * @package POT * @version 0.1.3 * @property-read int $otbVersion OTB file version. @@ -88,35 +88,35 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Temple positions. - * + * * @var array */ private $items = array(); /** * OTB version. - * + * * @var int */ private $otbVersion; /** * Client version. - * + * * @var int */ private $clientVersion; /** * Build version. - * + * * @var int */ private $buildVersion; /** * Magic PHP5 method. - * + * *

    * Allows object unserialisation. *

    @@ -129,11 +129,11 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Loads items.xml and items.otb files. - * + * *

    * This method loads both items.xml and items.otb files. Both of them has to be in given directory. *

    - * + * * @version 0.1.3 * @param string $path Path to data/items directory. * @throws E_OTS_FileLoaderError When error occurs during file operation. @@ -191,7 +191,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Parses loaded file. - * + * * @version 0.1.0 * @throws E_OTS_FileLoaderError If file has invalid format. */ @@ -378,7 +378,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Returns OTB file version. - * + * * @return int OTB format version. */ public function getOTBVersion() @@ -388,7 +388,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Returns client version. - * + * * @return int Client version. */ public function getClientVersion() @@ -398,7 +398,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Returns build version. - * + * * @return int Build version. */ public function getBuildVersion() @@ -408,7 +408,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Checks if given item type exists on list. - * + * * @version 0.1.3 * @since 0.1.3 * @param string $name Name. @@ -430,7 +430,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Returns given item type. - * + * * @version 0.1.3 * @param int $id Item type (server) ID. * @return OTS_ItemType Returns item type of given ID. @@ -448,7 +448,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Checks if given type ID exists on list. - * + * * @version 0.1.3 * @since 0.1.3 * @param int $id ID. @@ -461,11 +461,11 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Finds item type by it's name. - * + * *

    * Note: If there are more then one items with same name this function will return first found server ID. It doesn't also mean that it will be the lowest ID - item types are ordered in order that they were loaded from items.xml file. *

    - * + * * @version 0.1.3 * @param string $name Item type name. * @return int Returns item type (server) ID. @@ -497,10 +497,10 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Returns amount of items loaded. - * + * * @return int Count of types. */ - public function count() + public function count(): int { return count($this->items); } @@ -550,7 +550,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Returns iterator handle for loops. - * + * * @version 0.1.0 * @since 0.1.0 * @return ArrayIterator Items list iterator. @@ -562,7 +562,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Checks if given element exists. - * + * * @version 0.1.0 * @since 0.1.0 * @param string|int $offset Array key. @@ -582,7 +582,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Returns item from given position. - * + * * @version 0.1.3 * @since 0.1.0 * @param string|int $offset Array key. @@ -602,7 +602,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * This method is implemented for ArrayAccess interface. In fact you can't write/append to items list. Any call to this method will cause {@link E_OTS_ReadOnly E_OTS_ReadOnly} raise. - * + * * @version 0.1.0 * @since 0.1.0 * @param string|int $offset Array key. @@ -616,7 +616,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * This method is implemented for ArrayAccess interface. In fact you can't write/append to items list. Any call to this method will cause {@link E_OTS_ReadOnly E_OTS_ReadOnly} raise. - * + * * @version 0.1.0 * @since 0.1.0 * @param string|int $offset Array key. @@ -629,7 +629,7 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Magic PHP5 method. - * + * * @version 0.1.0 * @since 0.1.0 * @param string $name Property name. @@ -652,11 +652,11 @@ class OTS_ItemsList extends OTS_FileLoader implements IteratorAggregate, Countab /** * Returns string representation of object. - * + * *

    * If any display driver is currently loaded then it uses it's method. *

    - * + * * @version 0.1.3 * @since 0.1.3 * @return string String representation of object. diff --git a/system/libs/pot/OTS_MonstersList.php b/system/libs/pot/OTS_MonstersList.php index 22ef6df7..bdc6bd06 100644 --- a/system/libs/pot/OTS_MonstersList.php +++ b/system/libs/pot/OTS_MonstersList.php @@ -163,7 +163,7 @@ class OTS_MonstersList implements Iterator, Countable, ArrayAccess * * @return int Count of monsters. */ - public function count() + public function count(): int { return count($this->monsters); } diff --git a/system/libs/pot/OTS_OTBMFile.php b/system/libs/pot/OTS_OTBMFile.php index 3469f858..3183247f 100644 --- a/system/libs/pot/OTS_OTBMFile.php +++ b/system/libs/pot/OTS_OTBMFile.php @@ -7,7 +7,7 @@ /** * Code in this file bases on oryginal OTServ OTBM format loading C++ code (iomapotbm.h, iomapotbm.cpp). - * + * * @package POT * @version 0.1.3 * @author Wrzasq @@ -20,11 +20,11 @@ /** * OTBM format reader. - * + * *

    * POT OTBM file parser is less strict then oryginal OTServ one. For instance it will read waypoints from version 1 OTBM file even that there were no waypoints in that format. *

    - * + * * @package POT * @version 0.1.6 * @property-read OTS_HousesList $housesList Houses list loaded from associated houses file. @@ -95,56 +95,56 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl const OTBM_ATTR_HOUSEDOORID = 14; /** * Amount. - * + * * @version 0.1.6 * @since 0.1.6 */ const OTBM_ATTR_COUNT = 15; /** * Time interval. - * + * * @version 0.1.6 * @since 0.1.6 */ const OTBM_ATTR_DURATION = 16; /** * Metamorphic stage. - * + * * @version 0.1.6 * @since 0.1.6 */ const OTBM_ATTR_DECAYING_STATE = 17; /** * Date of being written. - * + * * @version 0.1.6 * @since 0.1.6 */ const OTBM_ATTR_WRITTENDATE = 18; /** * Sign author. - * + * * @version 0.1.6 * @since 0.1.6 */ const OTBM_ATTR_WRITTENBY = 19; /** * Sleeping player ID. - * + * * @version 0.1.6 * @since 0.1.6 */ const OTBM_ATTR_SLEEPERGUID = 20; /** * Time of sleep started. - * + * * @version 0.1.6 * @since 0.1.6 */ const OTBM_ATTR_SLEEPSTART = 21; /** * Number of charges. - * + * * @version 0.1.6 * @since 0.1.6 */ @@ -208,14 +208,14 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl const OTBM_NODE_HOUSETILE = 14; /** * Waypoints list. - * + * * @version 0.1.6 * @since 0.1.6 */ const OTBM_NODE_WAYPOINTS = 15; /** * Waypoint. - * + * * @version 0.1.6 * @since 0.1.6 */ @@ -223,56 +223,56 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Map width. - * + * * @var int */ private $width; /** * Map height. - * + * * @var int */ private $height; /** * Map description. - * + * * @var string */ private $description = ''; /** * List of towns. - * + * * @var array */ private $towns = array(); /** * Temple positions. - * + * * @var array */ private $temples = array(); /** * Directory path. - * + * * @var string */ private $directory; /** * External houses file. - * + * * @var OTS_HousesList */ private $housesList; /** * List of map tracks. - * + * * @var array * @version 0.1.6 * @since 0.1.6 @@ -281,11 +281,11 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Magic PHP5 method. - * + * *

    * Allows object unserialisation. *

    - * + * * @throws E_OTS_FileLoaderError When error occurs during file operation. */ public function __wakeup() @@ -296,7 +296,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Loads OTBM file content. - * + * * @version 0.1.0 * @param string $file Filename. * @throws E_OTS_FileLoaderError When error occurs during file operation. @@ -316,7 +316,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Parses loaded file. - * + * * @version 0.1.0 * @throws E_OTS_FileLoaderError When error occurs during file operation. * @throws E_OTS_OutOfBuffer When there is read attemp after end of stream. @@ -476,7 +476,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Loads map's houses list. - * + * * @version 0.1.0 * @since 0.1.0 * @return OTS_HousesList Houses from external file. @@ -488,7 +488,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Returns map width. - * + * * @return int Map width. */ public function getWidth() @@ -498,7 +498,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Returns map height. - * + * * @return int Map height. */ public function getHeight() @@ -508,7 +508,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Returns map description. - * + * * @return string Map description. */ public function getDescription() @@ -518,11 +518,11 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Returns map waypoints list. - * + * *

    * Each item of returned array is sub-array with list of waypoints. *

    - * + * * @version 0.1.6 * @since 0.1.6 * @return array List of tracks. @@ -534,7 +534,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Checks if given town ID exists on list. - * + * * @version 0.1.3 * @since 0.1.3 * @param int $id ID. @@ -547,7 +547,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Returns town's ID. - * + * * @version 0.1.3 * @param string $name Town. * @return int ID. @@ -567,7 +567,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Checks if given town name exists on list. - * + * * @version 0.1.3 * @since 0.1.3 * @param string $name Town. @@ -580,7 +580,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Returns name of given town's ID. - * + * * @version 0.1.3 * @param int $id Town ID. * @return string Name. @@ -607,7 +607,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Returns town's temple position. - * + * * @param int $id Town id. * @return OTS_MapCoords|bool Point on map (false if not found). */ @@ -625,12 +625,12 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Returns amount of towns loaded. - * + * * @version 0.0.8 * @since 0.0.8 * @return int Count of towns. */ - public function count() + public function count(): int { return count($this->towns); } @@ -690,7 +690,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Returns iterator handle for loops. - * + * * @version 0.1.0 * @since 0.1.0 * @return ArrayIterator Towns list iterator. @@ -702,7 +702,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Checks if given element exists. - * + * * @version 0.1.0 * @since 0.1.0 * @param string|int $offset Array key. @@ -724,7 +724,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Returns item from given position. - * + * * @version 0.1.0 * @since 0.1.0 * @param string|int $offset Array key. @@ -754,7 +754,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * This method is implemented for ArrayAccess interface. In fact you can't write/append to towns list. Any call to this method will cause {@link E_OTS_ReadOnly E_OTS_ReadOnly} raise. - * + * * @version 0.1.0 * @since 0.1.0 * @param string|int $offset Array key. @@ -768,7 +768,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * This method is implemented for ArrayAccess interface. In fact you can't write/append to towns list. Any call to this method will cause {@link E_OTS_ReadOnly E_OTS_ReadOnly} raise. - * + * * @version 0.1.0 * @since 0.1.0 * @param string|int $offset Array key. @@ -781,7 +781,7 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Magic PHP5 method. - * + * * @version 0.1.0 * @since 0.1.0 * @param string $name Property name. @@ -814,11 +814,11 @@ class OTS_OTBMFile extends OTS_FileLoader implements IteratorAggregate, Countabl /** * Returns string representation of object. - * + * *

    * If any display driver is currently loaded then it uses it's method. *

    - * + * * @version 0.1.3 * @since 0.1.3 * @return string String representation of object. diff --git a/system/libs/pot/OTS_Player.php b/system/libs/pot/OTS_Player.php index 1f6a54c9..b5f48dc6 100644 --- a/system/libs/pot/OTS_Player.php +++ b/system/libs/pot/OTS_Player.php @@ -602,7 +602,7 @@ class OTS_Player extends OTS_Row_DAO } $account = new OTS_Account(); - $account->load($this->data['account_id']); + $account->load($this->data['account_id'], false, true); return $account; } @@ -2489,7 +2489,7 @@ class OTS_Player extends OTS_Row_DAO $value = $this->db->query('SELECT ' . $this->db->fieldName('value') . ' FROM ' . $this->db->tableName('player_storage') . ' WHERE ' . $this->db->fieldName('key') . ' = ' . (int) $key . ' AND ' . $this->db->fieldName('player_id') . ' = ' . $this->data['id'])->fetch(); - if($value !== false) + if($value === false) { return null; } diff --git a/system/libs/pot/OTS_SpellsList.php b/system/libs/pot/OTS_SpellsList.php index c7a91899..8455b7f2 100644 --- a/system/libs/pot/OTS_SpellsList.php +++ b/system/libs/pot/OTS_SpellsList.php @@ -324,7 +324,7 @@ class OTS_SpellsList implements IteratorAggregate, Countable * @since 0.1.5 * @return int Amount of all spells. */ - public function count() + public function count(): int { return count($this->runes) + count($this->instants) + count($this->conjures); } diff --git a/system/libs/pot/OTS_VocationsList.php b/system/libs/pot/OTS_VocationsList.php index 7dc0e32e..36898684 100644 --- a/system/libs/pot/OTS_VocationsList.php +++ b/system/libs/pot/OTS_VocationsList.php @@ -15,7 +15,7 @@ /** * Wrapper for vocations.xml file. - * + * * @package POT * @version 0.1.3 * @example examples/vocations.php vocations.php @@ -25,14 +25,14 @@ class OTS_VocationsList implements IteratorAggregate, Countable, ArrayAccess { /** * List of vocations. - * + * * @var array */ private $vocations = array(); /** * Loads vocations list from given file. - * + * * @param string $file vocations.xml file location. * @throws DOMException On DOM operation error. */ @@ -51,11 +51,11 @@ class OTS_VocationsList implements IteratorAggregate, Countable, ArrayAccess /** * Magic PHP5 method. - * + * *

    * Allows object importing from {@link http://www.php.net/manual/en/function.var-export.php var_export()}. *

    - * + * * @param array $properties List of object properties. * @throws DOMException On DOM operation error. */ @@ -74,7 +74,7 @@ class OTS_VocationsList implements IteratorAggregate, Countable, ArrayAccess /** * Checks if given vocation ID exists on list. - * + * * @version 0.1.3 * @since 0.1.3 * @param int $id ID. @@ -87,7 +87,7 @@ class OTS_VocationsList implements IteratorAggregate, Countable, ArrayAccess /** * Returns vocation's ID. - * + * * @version 0.1.3 * @param string $name Vocation. * @return int ID. @@ -108,7 +108,7 @@ class OTS_VocationsList implements IteratorAggregate, Countable, ArrayAccess /** * Checks if given vocation name exists on list. - * + * * @version 0.1.3 * @since 0.1.3 * @param string $name Vocation. @@ -121,7 +121,7 @@ class OTS_VocationsList implements IteratorAggregate, Countable, ArrayAccess /** * Returns name of given vocation's ID. - * + * * @version 0.1.3 * @param int $id Vocation ID. * @return string Name. @@ -139,17 +139,17 @@ class OTS_VocationsList implements IteratorAggregate, Countable, ArrayAccess /** * Returns amount of vocations loaded. - * + * * @return int Count of vocations. */ - public function count() + public function count(): int { return count($this->vocations); } /** * Returns iterator handle for loops. - * + * * @return ArrayIterator Vocations list iterator. */ public function getIterator() @@ -159,7 +159,7 @@ class OTS_VocationsList implements IteratorAggregate, Countable, ArrayAccess /** * Checks if given element exists. - * + * * @version 0.1.3 * @param string|int $offset Array key. * @return bool True if it's set. @@ -178,7 +178,7 @@ class OTS_VocationsList implements IteratorAggregate, Countable, ArrayAccess /** * Returns item from given position. - * + * * @version 0.1.3 * @param string|int $offset Array key. * @return string|int If key is an integer (type-sensitive!) then returns vocation name. If it's a string then return associated ID. @@ -197,7 +197,7 @@ class OTS_VocationsList implements IteratorAggregate, Countable, ArrayAccess /** * This method is implemented for ArrayAccess interface. In fact you can't write/append to vocations list. Any call to this method will cause {@link E_OTS_ReadOnly E_OTS_ReadOnly} raise. - * + * * @param string|int $offset Array key. * @param mixed $value Field value. * @throws E_OTS_ReadOnly Always - this class is read-only. @@ -209,7 +209,7 @@ class OTS_VocationsList implements IteratorAggregate, Countable, ArrayAccess /** * This method is implemented for ArrayAccess interface. In fact you can't write/append to vocations list. Any call to this method will cause {@link E_OTS_ReadOnly E_OTS_ReadOnly} raise. - * + * * @param string|int $offset Array key. * @throws E_OTS_ReadOnly Always - this class is read-only. */ diff --git a/system/libs/rfc6238.php b/system/libs/rfc6238.php new file mode 100644 index 00000000..579f08f2 --- /dev/null +++ b/system/libs/rfc6238.php @@ -0,0 +1,285 @@ +'0', 'B'=>'1', 'C'=>'2', 'D'=>'3', 'E'=>'4', 'F'=>'5', 'G'=>'6', 'H'=>'7', + 'I'=>'8', 'J'=>'9', 'K'=>'10', 'L'=>'11', 'M'=>'12', 'N'=>'13', 'O'=>'14', 'P'=>'15', + 'Q'=>'16', 'R'=>'17', 'S'=>'18', 'T'=>'19', 'U'=>'20', 'V'=>'21', 'W'=>'22', 'X'=>'23', + 'Y'=>'24', 'Z'=>'25', '2'=>'26', '3'=>'27', '4'=>'28', '5'=>'29', '6'=>'30', '7'=>'31' + ); + + /** + * Use padding false when encoding for urls + * + * @return base32 encoded string + * @author Bryan Ruiz + **/ + public static function encode($input, $padding = true) { + if(empty($input)) return ""; + + $input = str_split($input); + $binaryString = ""; + + for($i = 0; $i < count($input); $i++) { + $binaryString .= str_pad(base_convert(ord($input[$i]), 10, 2), 8, '0', STR_PAD_LEFT); + } + + $fiveBitBinaryArray = str_split($binaryString, 5); + $base32 = ""; + $i=0; + + while($i < count($fiveBitBinaryArray)) { + $base32 .= self::$map[base_convert(str_pad($fiveBitBinaryArray[$i], 5,'0'), 2, 10)]; + $i++; + } + + if($padding && ($x = strlen($binaryString) % 40) != 0) { + if($x == 8) $base32 .= str_repeat(self::$map[32], 6); + else if($x == 16) $base32 .= str_repeat(self::$map[32], 4); + else if($x == 24) $base32 .= str_repeat(self::$map[32], 3); + else if($x == 32) $base32 .= self::$map[32]; + } + + return $base32; + } + + public static function decode($input) { + if(empty($input)) return; + + $paddingCharCount = substr_count($input, self::$map[32]); + $allowedValues = array(6,4,3,1,0); + + if(!in_array($paddingCharCount, $allowedValues)) return false; + + for($i=0; $i<4; $i++){ + if($paddingCharCount == $allowedValues[$i] && + substr($input, -($allowedValues[$i])) != str_repeat(self::$map[32], $allowedValues[$i])) return false; + } + + $input = str_replace('=','', $input); + $input = str_split($input); + $binaryString = ""; + + for($i=0; $i < count($input); $i = $i+8) { + $x = ""; + + if(!in_array($input[$i], self::$map)) return false; + + for($j=0; $j < 8; $j++) { + $x .= str_pad(base_convert(@self::$flippedMap[@$input[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT); + } + + $eightBits = str_split($x, 8); + + for($z = 0; $z < count($eightBits); $z++) { + $binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:""; + } + } + + return $binaryString; + } +} + +// http://www.faqs.org/rfcs/rfc6238.html +// https://github.com/Voronenko/PHPOTP/blob/08cda9cb9c30b7242cf0b3a9100a6244a2874927/code/rfc6238.php +// Local changes: http -> https, consistent indentation, 200x200 -> 300x300 QR image size, PHP end tag +class TokenAuth6238 { + + /** + * verify + * + * @param string $secretkey Secret clue (base 32). + * @return bool True if success, false if failure + */ + public static function verify($secretkey, $code, $rangein30s = 3) { + $key = base32static::decode($secretkey); + $unixtimestamp = time()/30; + + for($i=-($rangein30s); $i<=$rangein30s; $i++) { + $checktime = (int)($unixtimestamp+$i); + $thiskey = self::oath_hotp($key, $checktime); + + if ((int)$code == self::oath_truncate($thiskey,6)) { + return true; + } + + } + return false; + } + + + public static function getTokenCode($secretkey,$rangein30s = 3) { + $result = ""; + $key = base32static::decode($secretkey); + $unixtimestamp = time()/30; + + for($i=-($rangein30s); $i<=$rangein30s; $i++) { + $checktime = (int)($unixtimestamp+$i); + $thiskey = self::oath_hotp($key, $checktime); + $result = $result." # ".self::oath_truncate($thiskey,6); + } + + return $result; + } + + public static function getTokenCodeDebug($secretkey,$rangein30s = 3) { + $result = ""; + print "
    SecretKey: $secretkey
    "; + + $key = base32static::decode($secretkey); + print "Key(base 32 decode): $key
    "; + + $unixtimestamp = time()/30; + print "UnixTimeStamp (time()/30): $unixtimestamp
    "; + + for($i=-($rangein30s); $i<=$rangein30s; $i++) { + $checktime = (int)($unixtimestamp+$i); + print "Calculating oath_hotp from (int)(unixtimestamp +- 30sec offset): $checktime basing on secret key
    "; + + $thiskey = self::oath_hotp($key, $checktime, true); + print "======================================================
    "; + print "CheckTime: $checktime oath_hotp:".$thiskey."
    "; + + $result = $result." # ".self::oath_truncate($thiskey,6,true); + } + + return $result; + } + + public static function getBarCodeUrl($username, $domain, $secretkey, $issuer) { + $url = "https://chart.apis.google.com/chart"; + $url = $url."?chs=300x300&chld=M|0&cht=qr&chl=otpauth://totp/"; + $url = $url.$username . "@" . $domain . "%3Fsecret%3D" . $secretkey . '%26issuer%3D' . rawurlencode($issuer); + return $url; + } + + public static function generateRandomClue($length = 16) { + $b32 = "234567QWERTYUIOPASDFGHJKLZXCVBNM"; + $s = ""; + + for ($i = 0; $i < $length; $i++) + $s .= $b32[rand(0,31)]; + + return $s; + } + + private static function hotp_tobytestream($key) { + $result = array(); + $last = strlen($key); + for ($i = 0; $i < $last; $i = $i + 2) { + $x = $key[$i] + $key[$i + 1]; + $x = strtoupper($x); + $x = hexdec($x); + $result = $result.chr($x); + } + + return $result; + } + + private static function oath_hotp ($key, $counter, $debug=false) { + $result = ""; + $orgcounter = $counter; + $cur_counter = array(0,0,0,0,0,0,0,0); + + if ($debug) { + print "Packing counter $counter (".dechex($counter).")into binary string - pay attention to hex representation of key and binary representation
    "; + } + + for($i=7;$i>=0;$i--) { // C for unsigned char, * for repeating to the end of the input data + $cur_counter[$i] = pack ('C*', $counter); + + if ($debug) { + print $cur_counter[$i]."(".dechex(ord($cur_counter[$i])).")"." from $counter
    "; + } + + $counter = $counter >> 8; + } + + if ($debug) { + foreach ($cur_counter as $char) { + print ord($char) . " "; + } + + print "
    "; + } + + $binary = implode($cur_counter); + + // Pad to 8 characters + str_pad($binary, 8, chr(0), STR_PAD_LEFT); + + if ($debug) { + print "Prior to HMAC calculation pad with zero on the left until 8 characters.
    "; + print "Calculate sha1 HMAC(Hash-based Message Authentication Code http://en.wikipedia.org/wiki/HMAC).
    "; + print "hash_hmac ('sha1', $binary, $key)
    "; + } + + $result = hash_hmac ('sha1', $binary, $key); + + if ($debug) { + print "Result: $result
    "; + } + + return $result; + } + + private static function oath_truncate($hash, $length = 6, $debug=false) { + $result=""; + + // Convert to dec + if($debug) { + print "converting hex hash into characters
    "; + } + + $hashcharacters = str_split($hash,2); + + if($debug) { + print_r($hashcharacters); + print "
    and convert to decimals:
    "; + } + + for ($j=0; $j"; + print "offset:".$offset; + } + + $result = ( + (($hmac_result[$offset+0] & 0x7f) << 24 ) | + (($hmac_result[$offset+1] & 0xff) << 16 ) | + (($hmac_result[$offset+2] & 0xff) << 8 ) | + ($hmac_result[$offset+3] & 0xff) + ) % pow(10,$length); + + return $result; + } +} +?> diff --git a/system/libs/validator.php b/system/libs/validator.php index c69f0039..38db7068 100644 --- a/system/libs/validator.php +++ b/system/libs/validator.php @@ -354,16 +354,6 @@ class Validator } } - if(strspn($name, "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM- '") != $name_length) { - self::$lastError = 'This name contains invalid letters, words or format. Please use only a-Z, - , \' and space.'; - return false; - } - - if(!preg_match("/[A-z ']/", $name)) { - self::$lastError = 'Your name containst illegal characters.'; - return false; - } - return true; } diff --git a/system/locale/de/install.php b/system/locale/de/install.php index 0ed461f6..5cb7342e 100644 --- a/system/locale/de/install.php +++ b/system/locale/de/install.php @@ -99,7 +99,10 @@ $locale['step_admin_account_id_error_same'] = 'Das Passwort darf nicht mit der K $locale['step_admin_password'] = 'Administrator Konto Passwort'; $locale['step_admin_password_desc'] = 'Passwort für Ihr Administratorkonto.'; $locale['step_admin_password_error_empty'] = 'Bitte geben Sie das Passwort für Ihr neues Konto ein.'; -$locale['step_admin_password_error_format'] = 'Ungültiges Passwortformat. Verwenden Sie nur a-Z und Ziffern 0-9. Mindestens 8, maximal 30 Zeichen.'; +$locale['step_admin_password_error_format'] = 'Ungültiges Passwortformat. Mindestens eine Buchstabe und eine Ziffer. Mindestens 8, maximal 30 Zeichen.'; +$locale['step_admin_password_confirm'] = 'Password wiederholen'; +$locale['step_admin_password_confirm_desc'] = 'Passwort für dein Konto wiederholen.'; +$locale['step_admin_password_confirm_error_not_same'] = 'Passwörter sind nicht gleich.'; // finish $locale['step_finish_admin_panel'] = 'Admin Bereich'; diff --git a/system/locale/en/install.php b/system/locale/en/install.php index f9e75756..f856dcbf 100644 --- a/system/locale/en/install.php +++ b/system/locale/en/install.php @@ -41,6 +41,7 @@ $locale['step_requirements_extension'] = '$EXTENSION$ PHP extension'; $locale['step_requirements_warning_images_guilds'] = 'Guild logo upload will not work'; $locale['step_requirements_warning_images_gallery'] = 'Gallery image upload will not work'; $locale['step_requirements_warning_player_signatures'] = 'Player Signatures will not work'; +$locale['step_requirements_warning_install_plugins'] = 'It will be not possible to install plugins'; // config $locale['step_config'] = 'Configuration'; @@ -113,7 +114,10 @@ $locale['step_admin_account_id_error_same'] = 'Password may not be the same as a $locale['step_admin_password'] = 'Admin account password'; $locale['step_admin_password_desc'] = 'Password to your admin account.'; $locale['step_admin_password_error_empty'] = 'Please enter the password for your new account.'; -$locale['step_admin_password_error_format'] = 'Invalid password format. Use only a-Z and numbers 0-9. Minimum 8, maximum 30 characters.'; +$locale['step_admin_password_error_format'] = 'Invalid password format. Minimum one letter and one number. Minimum 8, maximum 30 characters.'; +$locale['step_admin_password_confirm'] = 'Password confirm'; +$locale['step_admin_password_confirm_desc'] = 'Repeat password to your account.'; +$locale['step_admin_password_confirm_error_not_same'] = 'Passwords are not same.'; $locale['step_admin_player_name'] = 'Admin player name'; $locale['step_admin_player_name_desc'] = 'Name of your admin character.'; $locale['step_admin_player_name_error_empty'] = 'Please enter the name of your character.'; diff --git a/system/locale/pl/install.php b/system/locale/pl/install.php index c26a5fc9..1afb2ddd 100644 --- a/system/locale/pl/install.php +++ b/system/locale/pl/install.php @@ -41,6 +41,7 @@ $locale['step_requirements_extension'] = 'Rozszerzenie PHP - $EXTENSION$'; $locale['step_requirements_warning_images_guilds'] = 'Nie będzie możliwości uploadu obrazków gildii'; $locale['step_requirements_warning_images_gallery'] = 'Nie będzie możliwości uploadu obrazków do galerii'; $locale['step_requirements_warning_player_signatures'] = 'Sygnatury graczy nie będą działać'; +$locale['step_requirements_warning_install_plugins'] = 'Nie będzie można instalować rozszerzeń'; // config $locale['step_config'] = 'Konfiguracja'; @@ -112,7 +113,10 @@ $locale['step_admin_account_id_error_same'] = 'Hasło nie może być takie same $locale['step_admin_password'] = 'Hasło Konta Admina'; $locale['step_admin_password_desc'] = 'Hasło do Twojego Konta Admina.'; $locale['step_admin_password_error_empty'] = 'Proszę podać hasło do Twojego nowego konta.'; -$locale['step_admin_password_error_format'] = 'Nieprawidłowy format hasła. Używaj tylko znaków a-Z oraz liczb 0-9. Minimum 8, maksimum 30 znaków.'; +$locale['step_admin_password_error_format'] = 'Nieprawidłowy format hasła. Minimum jeden znak i jedna liczba. Minimum 8, maksimum 30 znaków.'; +$locale['step_admin_password_confirm'] = 'Potwierdź Hasło'; +$locale['step_admin_password_confirm_desc'] = 'Potwierdzenie hasła do Twojego Konta Admina.'; +$locale['step_admin_password_confirm_error_not_same'] = 'Hasła nie są takie same.'; $locale['step_admin_player_name'] = 'Nazwa postaci'; $locale['step_admin_player_name_desc'] = 'Nazwa postaci Konta Admina.'; $locale['step_admin_player_name_error_empty'] = 'Proszę podać nazwę postaci.'; diff --git a/system/locale/pt_br/install.php b/system/locale/pt_br/install.php index 029fd322..e47faede 100644 --- a/system/locale/pt_br/install.php +++ b/system/locale/pt_br/install.php @@ -19,7 +19,7 @@ $locale['not_loaded'] = 'Não carregado'; $locale['loading_spinner'] = 'Por favor aguarde, instalando...'; $locale['importing_spinner'] = 'Por favor, aguarde, importando dados...'; -$locale['please_fill_all'] = 'or favor, preencha todas as entradas!'; +$locale['please_fill_all'] = 'Por favor, preencha todas as entradas!'; $locale['already_installed'] = 'MyAAC já foi instalado. Por favor, apague o diretório install/ . Se você quiser reinstalar o MyAAC - exclua o arquivo config.local.php do diretório principal e atualize a página.'; // welcome diff --git a/system/login.php b/system/login.php index 330efd13..5d55cd18 100644 --- a/system/login.php +++ b/system/login.php @@ -11,11 +11,6 @@ defined('MYAAC') or die('Direct access not allowed!'); $logged = false; $logged_flags = 0; -$action = isset($_REQUEST['action']) ? strtolower($_REQUEST['action']) : ''; -if(!defined('ACTION')) { - define('ACTION', $action); -} - // stay-logged with sessions $current_session = getSession('account'); if($current_session !== false) @@ -33,133 +28,10 @@ if($current_session !== false) } } -if(ACTION === 'logout' && !isset($_REQUEST['account_login'])) { - if(isset($account_logged) && $account_logged->isLoaded()) { - if($hooks->trigger(HOOK_LOGOUT, array('account' => $account_logged, 'password' => getSession('password')))) { - unsetSession('account'); - unsetSession('password'); - unsetSession('remember_me'); - - $logged = false; - unset($account_logged); - - if(isset($_REQUEST['redirect'])) - { - header('Location: ' . urldecode($_REQUEST['redirect'])); - exit; - } - } - } -} -else -{ - // new login with data from form - if(!$logged && isset($_POST['account_login'], $_POST['password_login'])) - { - $login_account = $_POST['account_login']; - $login_password = $_POST['password_login']; - $remember_me = isset($_POST['remember_me']); - if(!empty($login_account) && !empty($login_password)) - { - if($cache->enabled()) - { - $tmp = ''; - if($cache->fetch('failed_logins', $tmp)) - { - $tmp = unserialize($tmp); - $to_remove = array(); - foreach($tmp as $ip => $t) - { - if(time() - $t['last'] >= 5 * 60) - $to_remove[] = $ip; - } - - foreach($to_remove as $ip) - unset($tmp[$ip]); - } - else - $tmp = array(); - - $ip = $_SERVER['REMOTE_ADDR']; - $t = isset($tmp[$ip]) ? $tmp[$ip] : NULL; - } - - $account_logged = new OTS_Account(); - if(USE_ACCOUNT_NAME) - $account_logged->find($login_account); - else - $account_logged->load($login_account, true); - - $config_salt_enabled = $db->hasColumn('accounts', 'salt'); - if($account_logged->isLoaded() && encrypt(($config_salt_enabled ? $account_logged->getCustomField('salt') : '') . $login_password) == $account_logged->getPassword() - && (!isset($t) || $t['attempts'] < 5) - ) - { - setSession('account', $account_logged->getId()); - setSession('password', encrypt(($config_salt_enabled ? $account_logged->getCustomField('salt') : '') . $login_password)); - if($remember_me) { - setSession('remember_me', true); - } - - $logged = true; - $logged_flags = $account_logged->getWebFlags(); - - if(isset($_POST['admin']) && !admin()) { - $errors[] = 'This account has no admin privileges.'; - unsetSession('account'); - unsetSession('password'); - unsetSession('remember_me'); - $logged = false; - } - else { - $account_logged->setCustomField('web_lastlogin', time()); - } - - $hooks->trigger(HOOK_LOGIN, array('account' => $account_logged, 'password' => $login_password, 'remember_me' => $remember_me)); - } - else - { - $hooks->trigger(HOOK_LOGIN_ATTEMPT, array('account' => $login_account, 'password' => $login_password, 'remember_me' => $remember_me)); - - // temporary solution for blocking failed login attempts - if($cache->enabled()) - { - if(isset($t)) - { - $t['attempts']++; - $t['last'] = time(); - - if($t['attempts'] >= 5) - $errors[] = 'A wrong password has been entered 5 times in a row. You are unable to log into your account for the next 5 minutes. Please wait.'; - else - $errors[] = 'Account name or password is not correct.'; - } - else - { - $t = array('attempts' => 1, 'last' => time()); - $errors[] = 'Account name or password is not correct.'; - } - - $tmp[$ip] = $t; - $cache->set('failed_logins', serialize($tmp), 60 * 60); // save for 1 hour - } - else { - $errors[] = 'Account name or password is not correct.'; - } - } - } - else { - $errors[] = 'Please enter your account ' . (USE_ACCOUNT_NAME ? 'name' : 'number') . ' and password.'; - - $hooks->trigger(HOOK_LOGIN_ATTEMPT, array('account' => $login_account, 'password' => $login_password, 'remember_me' => $remember_me)); - } - } - - if($logged) { - $logged_flags = $account_logged->getWebFlags(); - $twig->addGlobal('logged', true); - $twig->addGlobal('account_logged', $account_logged); - } +if($logged) { + $logged_flags = $account_logged->getWebFlags(); + $twig->addGlobal('logged', true); + $twig->addGlobal('account_logged', $account_logged); } setSession('last_visit', time()); diff --git a/system/logout.php b/system/logout.php new file mode 100644 index 00000000..a7f5d8fd --- /dev/null +++ b/system/logout.php @@ -0,0 +1,18 @@ +isLoaded()) { + if($hooks->trigger(HOOK_LOGOUT, ['account_id' => $account_logged->getId()])) { + unsetSession('account'); + unsetSession('password'); + unsetSession('remember_me'); + + $logged = false; + unset($account_logged); + + if(isset($_REQUEST['redirect'])) + { + header('Location: ' . urldecode($_REQUEST['redirect'])); + exit; + } + } +} diff --git a/system/migrations/11.php b/system/migrations/11.php index 91cd92fe..dc335577 100644 --- a/system/migrations/11.php +++ b/system/migrations/11.php @@ -3,12 +3,12 @@ $db->query("RENAME TABLE " . TABLE_PREFIX . "screenshots TO " . TABLE_PREFIX . "gallery, " . TABLE_PREFIX . "movies TO " . TABLE_PREFIX . "videos;"); - + // rename images dir - if(file_exists(BASE . 'images/screenshots') && !file_exists(BASE .'images/gallery')) { - rename(BASE . 'images/screenshots', BASE . 'images/gallery'); + if(file_exists(BASE . 'images/screenshots') && !file_exists(BASE . GALLERY_DIR)) { + rename(BASE . 'images/screenshots', BASE . GALLERY_DIR); } - + // convert old database screenshots images to gallery $query = $db->query('SELECT `id`, `image`, `thumb` FROM `' . TABLE_PREFIX . 'gallery`;'); foreach($query->fetchAll() as $item) { @@ -17,4 +17,4 @@ 'thumb' => str_replace('/screenshots/', '/gallery/', $item['thumb']), ), array('id' => $item['id'])); } -?> \ No newline at end of file +?> diff --git a/system/migrations/33.php b/system/migrations/33.php index 39d48ed4..12fe4c2c 100644 --- a/system/migrations/33.php +++ b/system/migrations/33.php @@ -1,13 +1,6 @@ hasTable(TABLE_PREFIX . 'settings')) { - $db->exec("CREATE TABLE `" . TABLE_PREFIX . "settings` - ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `plugin_name` VARCHAR(255) NOT NULL DEFAULT '', - `key` VARCHAR(255) NOT NULL DEFAULT '', - `value` TEXT NOT NULL, - PRIMARY KEY (`id`), - KEY `key` (`key`) - ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;"); -} +$db->exec('ALTER TABLE `' . TABLE_PREFIX . "visitors` MODIFY `ip` VARCHAR(45) NOT NULL;"); diff --git a/system/migrations/34.php b/system/migrations/34.php new file mode 100644 index 00000000..e69de29b diff --git a/system/pages/405.php b/system/pages/405.php new file mode 100644 index 00000000..3d585f59 --- /dev/null +++ b/system/pages/405.php @@ -0,0 +1,16 @@ + + * @copyright 2021 MyAAC + * @link https://my-aac.org + */ +defined('MYAAC') or die('Direct access not allowed!'); +$title = '405 Method Not Allowed'; + +header('HTTP/1.0 405 Method Not Allowed'); +?> +

    Method not allowed

    +

    The requested method: for URL was not found on this server.

    diff --git a/system/pages/account/base.php b/system/pages/account/base.php new file mode 100644 index 00000000..75b1cc77 --- /dev/null +++ b/system/pages/account/base.php @@ -0,0 +1,29 @@ + + * @copyright 2019 MyAAC + * @link https://my-aac.org + */ +defined('MYAAC') or die('Direct access not allowed!'); + +if(!$logged) +{ + if(!empty($errors)) + $twig->display('error_box.html.twig', array('errors' => $errors)); + + $twig->display('account.login.html.twig', array( + 'redirect' => $_REQUEST['redirect'] ?? null, + 'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number', + 'account_login_by' => getAccountLoginByLabel(), + 'error' => $errors[0] ?? null + )); + + return; +} +else { + $show_form = true; +} diff --git a/system/pages/account/change_comment.php b/system/pages/account/change_comment.php index 43cfd0ca..0f0bc2f1 100644 --- a/system/pages/account/change_comment.php +++ b/system/pages/account/change_comment.php @@ -10,6 +10,13 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +$title = 'Change Comment'; +require __DIR__ . '/base.php'; + +if(!$logged) { + return; +} + $player_name = isset($_REQUEST['name']) ? stripslashes(urldecode($_REQUEST['name'])) : null; $new_comment = isset($_POST['comment']) ? htmlspecialchars(stripslashes(substr($_POST['comment'],0,2000))) : NULL; $new_hideacc = isset($_POST['accountvisible']) ? (int)$_POST['accountvisible'] : NULL; @@ -56,4 +63,4 @@ if($show_form) { )); } } -?> \ No newline at end of file +?> diff --git a/system/pages/account/change_email.php b/system/pages/account/change_email.php index 0bd668a5..2368a284 100644 --- a/system/pages/account/change_email.php +++ b/system/pages/account/change_email.php @@ -10,6 +10,13 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +$title = 'Change E-Mail'; +require __DIR__ . '/base.php'; + +if(!$logged) { + return; +} + $email_new_time = $account_logged->getCustomField("email_new_time"); if($email_new_time > 10) { @@ -29,7 +36,7 @@ if($email_new_time < 10) { $errors[] = 'Please enter password to your account.'; } else { - $post_password = encrypt(($config_salt_enabled ? $account_logged->getCustomField('salt') : '') . $post_password); + $post_password = encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $post_password); if($post_password != $account_logged->getPassword()) { $errors[] = 'Wrong password to account.'; } @@ -159,4 +166,4 @@ if(isset($_POST['emailchangecancel']) && $_POST['emailchangecancel'] == 1) { 'custom_buttons' => $custom_buttons )); } -?> \ No newline at end of file +?> diff --git a/system/pages/account/change_info.php b/system/pages/account/change_info.php index df2a52f6..753f4ce5 100644 --- a/system/pages/account/change_info.php +++ b/system/pages/account/change_info.php @@ -10,6 +10,16 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +$title = 'Change Info'; +require __DIR__ . '/base.php'; + +if(!$logged) { + return; +} + +if($config['account_country']) + require SYSTEM . 'countries.conf.php'; + $show_form = true; $new_rlname = isset($_POST['info_rlname']) ? htmlspecialchars(stripslashes($_POST['info_rlname'])) : NULL; $new_location = isset($_POST['info_location']) ? htmlspecialchars(stripslashes($_POST['info_location'])) : NULL; @@ -53,10 +63,10 @@ if($show_form) { } $twig->display('account.change_info.html.twig', array( - 'countries' => isset($countries) ? $countries : [], + 'countries' => $countries ?? [], 'account_rlname' => $account_rlname, 'account_location' => $account_location, - 'account_country' => isset($account_country) ? $account_country : '' + 'account_country' => $account_country ?? '' )); } -?> \ No newline at end of file +?> diff --git a/system/pages/account/change_name.php b/system/pages/account/change_name.php index 6771760b..ab7bd326 100644 --- a/system/pages/account/change_name.php +++ b/system/pages/account/change_name.php @@ -10,6 +10,13 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +$title = 'Change Name'; +require __DIR__ . '/base.php'; + +if(!$logged) { + return; +} + $player_id = isset($_POST['player_id']) ? (int)$_POST['player_id'] : NULL; $name = isset($_POST['name']) ? stripslashes(ucwords(strtolower($_POST['name']))) : NULL; if((!$config['account_change_character_name'])) @@ -52,6 +59,29 @@ else $old_name = $player->getName(); $player->setName($name); $player->save(); + + if ($db->hasTable('player_deaths') && + $db->hasColumn('player_deaths', 'mostdamage_is_player') && + $db->hasColumn('player_deaths', 'killed_by')) { + + $namesToChange = $db->query('SELECT `player_id`, `time`, `is_player`, `killed_by`, `mostdamage_is_player`, `mostdamage_by` FROM `player_deaths` WHERE (`is_player` = 1 AND `killed_by` = ' . $db->quote($old_name) . ') OR (`mostdamage_is_player` = 1 AND `mostdamage_by` = ' . $db->quote($old_name) . ');'); + + if ($namesToChange->rowCount() > 0) { + foreach ($namesToChange->fetchAll(PDO::FETCH_ASSOC) as $row) { + $changeKey = ''; + if ($row['is_player'] == '1' && $row['killed_by'] == $old_name) { + $changeKey = 'killed_by'; + } else if ($row['mostdamage_is_player'] == '1' && $row['mostdamage_by'] == $old_name) { + $changeKey = 'mostdamage_by'; + } + + if (!empty($changeKey)) { + $db->update('player_deaths', [$changeKey => $name], ['player_id' => $row['player_id'], 'time' => $row['time']]); + } + } + } + } + $account_logged->setCustomField("premium_points", $points - $config['account_change_character_name_points']); $account_logged->logAction('Changed name from ' . $old_name . ' to ' . $player->getName() . '.'); $twig->display('success.html.twig', array( @@ -83,4 +113,4 @@ else } } -?> \ No newline at end of file +?> diff --git a/system/pages/account/change_password.php b/system/pages/account/change_password.php index bd63c46d..9b8c3e8a 100644 --- a/system/pages/account/change_password.php +++ b/system/pages/account/change_password.php @@ -10,9 +10,16 @@ */ defined('MYAAC') or die('Direct access not allowed!'); -$new_password = isset($_POST['newpassword']) ? $_POST['newpassword'] : NULL; -$new_password2 = isset($_POST['newpassword2']) ? $_POST['newpassword2'] : NULL; -$old_password = isset($_POST['oldpassword']) ? $_POST['oldpassword'] : NULL; +$title = 'Change Password'; +require __DIR__ . '/base.php'; + +if(!$logged) { + return; +} + +$new_password = $_POST['newpassword'] ?? NULL; +$new_password2 = $_POST['newpassword2'] ?? NULL; +$old_password = $_POST['oldpassword'] ?? NULL; if(empty($new_password) && empty($new_password2) && empty($old_password)) { $twig->display('account.change_password.html.twig'); } @@ -32,7 +39,7 @@ else } /** @var OTS_Account $account_logged */ - $old_password = encrypt(($config_salt_enabled ? $account_logged->getCustomField('salt') : '') . $old_password); + $old_password = encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $old_password); if($old_password != $account_logged->getPassword()) { $errors[] = "Current password is incorrect!"; } @@ -48,7 +55,7 @@ else { $org_pass = $new_password; - if($config_salt_enabled) + if(USE_ACCOUNT_SALT) { $salt = generateRandomString(10, false, true, true); $new_password = $salt . $new_password; @@ -82,4 +89,4 @@ else } } -?> \ No newline at end of file +?> diff --git a/system/pages/account/change_sex.php b/system/pages/account/change_sex.php index f3eb8299..c67ac608 100644 --- a/system/pages/account/change_sex.php +++ b/system/pages/account/change_sex.php @@ -10,6 +10,13 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +$title = 'Change Sex'; +require __DIR__ . '/base.php'; + +if(!$logged) { + return; +} + $sex_changed = false; $player_id = isset($_POST['player_id']) ? (int)$_POST['player_id'] : NULL; $new_sex = isset($_POST['new_sex']) ? (int)$_POST['new_sex'] : NULL; @@ -85,4 +92,4 @@ else } } -?> \ No newline at end of file +?> diff --git a/system/pages/account/confirm_email.php b/system/pages/account/confirm_email.php index 078ba324..233c5533 100644 --- a/system/pages/account/confirm_email.php +++ b/system/pages/account/confirm_email.php @@ -11,7 +11,7 @@ defined('MYAAC') or die('Direct access not allowed!'); $title = 'Confirm Email'; -$hash = isset($_GET['v']) ? $_GET['v'] : ''; +$hash = $_GET['hash'] ?? ''; if(empty($hash)) { warning('Please enter email hash code.
    If you copied the link, please try again with full link.'); return; @@ -23,6 +23,16 @@ if(!$res->rowCount()) { } else { + $query = $db->query('SELECT id FROM accounts WHERE email_hash = ' . $db->quote($hash) . ' AND email_verified = 0'); + if ($query->rowCount() == 1) { + $query = $query->fetch(PDO::FETCH_ASSOC); + $account = new OTS_Account(); + $account->load($query['id']); + if ($account->isLoaded()) { + $hooks->trigger(HOOK_EMAIL_CONFIRMED, ['account' => $account]); + } + } + $db->update('accounts', array('email_verified' => '1'), array('email_hash' => $hash)); success('You have now verified your e-mail, this will increase the security of your account. Thank you for doing this.'); } diff --git a/system/pages/createaccount.php b/system/pages/account/create.php similarity index 81% rename from system/pages/createaccount.php rename to system/pages/account/create.php index 8af31629..551d6d1e 100644 --- a/system/pages/createaccount.php +++ b/system/pages/account/create.php @@ -34,11 +34,13 @@ $errors = array(); $save = isset($_POST['save']) && $_POST['save'] == 1; if($save) { - if(USE_ACCOUNT_NAME) { - $account_name = $_POST['account']; - } - else { - $account_id = $_POST['account']; + if(!config('account_login_by_email')) { + if(USE_ACCOUNT_NAME) { + $account_name = $_POST['account']; + } + else { + $account_id = $_POST['account']; + } } $email = $_POST['email']; @@ -46,12 +48,14 @@ if($save) $password2 = $_POST['password2']; // account - if(isset($account_id)) { - if(!Validator::accountId($account_id)) + if(!config('account_login_by_email')) { + if (isset($account_id)) { + if (!Validator::accountId($account_id)) { + $errors['account'] = Validator::getLastError(); + } + } else if (!Validator::accountName($account_name)) $errors['account'] = Validator::getLastError(); } - else if(!Validator::accountName($account_name)) - $errors['account'] = Validator::getLastError(); // email if(!Validator::email($email)) @@ -68,19 +72,6 @@ if($save) $errors['country'] = 'Country is invalid.'; } - if($config['recaptcha_enabled']) - { - if(isset($_POST['g-recaptcha-response']) && !empty($_POST['g-recaptcha-response'])) - { - $verifyResponse = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.$config['recaptcha_secret_key'].'&response='.$_POST['g-recaptcha-response']); - $responseData = json_decode($verifyResponse); - if(!$responseData->success) - $errors['verification'] = "Please confirm that you're not a robot."; - } - else - $errors['verification'] = "Please confirm that you're not a robot."; - } - // password if(empty($password)) { $errors['password'] = 'Please enter the password for your new account.'; @@ -93,7 +84,7 @@ if($save) } // check if account name is not equal to password - if(USE_ACCOUNT_NAME && strtoupper($account_name) == strtoupper($password)) { + if(!config('account_login_by_email') && USE_ACCOUNT_NAME && strtoupper($account_name) == strtoupper($password)) { $errors['password'] = 'Password may not be the same as account name.'; } @@ -106,16 +97,28 @@ if($save) } $account_db = new OTS_Account(); - if(USE_ACCOUNT_NAME) - $account_db->find($account_name); - else - $account_db->load($account_id); + if (config('account_login_by_email')) { + $account_db->findByEMail($email); + } + else { + if(USE_ACCOUNT_NAME) { + $account_db->find($account_name); + } + else { + $account_db->load($account_id); + } + } if($account_db->isLoaded()) { - if(USE_ACCOUNT_NAME) - $errors['account'] = 'Account with this name already exist.'; - else - $errors['account'] = 'Account with this id already exist.'; + if (config('account_login_by_email') && !config('account_mail_unique')) { + $errors['account'] = 'Account with this email already exist.'; + } + else if (!config('account_login_by_email')) { + if (USE_ACCOUNT_NAME) + $errors['account'] = 'Account with this name already exist.'; + else + $errors['account'] = 'Account with this id already exist.'; + } } if(!isset($_POST['accept_rules']) || $_POST['accept_rules'] !== 'true') @@ -130,14 +133,17 @@ if($save) 'accept_rules' => isset($_POST['accept_rules']) ? $_POST['accept_rules'] === 'true' : false, ); - if(USE_ACCOUNT_NAME) { - $params['account_name'] = $_POST['account']; - } - else { - $params['account_id'] = $_POST['account']; + if (!config('account_login_by_email')) { + if (USE_ACCOUNT_NAME) { + $params['account_name'] = $_POST['account']; + } else { + $params['account_id'] = $_POST['account']; + } } - $hooks->trigger(HOOK_ACCOUNT_CREATE_AFTER_SUBMIT, $params); + if (!$hooks->trigger(HOOK_ACCOUNT_CREATE_POST, $params)) { + return; + } if(config('account_create_character_create')) { $character_name = isset($_POST['name']) ? stripslashes(ucwords(strtolower($_POST['name']))) : null; @@ -151,13 +157,17 @@ if($save) if(empty($errors)) { $new_account = new OTS_Account(); - if(USE_ACCOUNT_NAME) - $new_account->create($account_name); - else - $new_account->create(NULL, $account_id); + if (config('account_login_by_email')) { + $new_account->createWithEmail($email); + } + else { + if(USE_ACCOUNT_NAME) + $new_account->create($account_name); + else + $new_account->create(NULL, $account_id); + } - $config_salt_enabled = $db->hasColumn('accounts', 'salt'); - if($config_salt_enabled) + if(USE_ACCOUNT_SALT) { $salt = generateRandomString(10, false, true, true); $password = $salt . $password; @@ -168,7 +178,7 @@ if($save) $new_account->unblock(); $new_account->save(); - if($config_salt_enabled) + if(USE_ACCOUNT_SALT) $new_account->setCustomField('salt', $salt); $new_account->setCustomField('created', time()); @@ -183,8 +193,13 @@ if($save) $new_account->setCustomField('premend', time() + $config['account_premium_days'] * 86400); } else { // rest - $new_account->setCustomField('premdays', $config['account_premium_days']); - $new_account->setCustomField('lastday', time()); + if ($db->hasColumn('accounts', 'premium_ends_at')) { // TFS 1.4+ + $new_account->setCustomField('premium_ends_at', time() + $config['account_premium_days'] * (60 * 60 * 24)); + } + else { + $new_account->setCustomField('premdays', $config['account_premium_days']); + $new_account->setCustomField('lastday', time()); + } } } @@ -192,7 +207,11 @@ if($save) $new_account->setCustomField('premium_points', $config['account_premium_points']); } - $tmp_account = (USE_ACCOUNT_NAME ? $account_name : $account_id); + $tmp_account = $email; + if (!config('account_login_by_email')) { + $tmp_account = (USE_ACCOUNT_NAME ? $account_name : $account_id); + } + if($config['mail_enabled'] && $config['account_mail_verify']) { $hash = md5(generateRandomString(16, true, true) . $email); @@ -223,6 +242,14 @@ if($save) } else { + if(config('account_create_character_create')) { + // character creation + $character_created = $createCharacter->doCreate($character_name, $character_sex, $character_vocation, $character_town, $new_account, $errors); + if (!$character_created) { + error('There was an error creating your character. Please create your character later in account management page.'); + } + } + if($config['account_create_auto_login']) { $_POST['account_login'] = USE_ACCOUNT_NAME ? $account_name : $account_id; $_POST['password_login'] = $password2; @@ -265,14 +292,6 @@ if($save) error('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log'); } } - - if(config('account_create_character_create')) { - // character creation - $character_created = $createCharacter->doCreate($character_name, $character_sex, $character_vocation, $character_town, $new_account, $errors); - if (!$character_created) { - error('There was an error creating your character. Please create your character later in account management page.'); - } - } } return; diff --git a/system/pages/account/create_character.php b/system/pages/account/create_character.php index 7c148966..3216a71a 100644 --- a/system/pages/account/create_character.php +++ b/system/pages/account/create_character.php @@ -10,6 +10,13 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +$title = 'Create Character'; +require __DIR__ . '/base.php'; + +if(!$logged) { + return; +} + $character_name = isset($_POST['name']) ? stripslashes($_POST['name']) : null; $character_sex = isset($_POST['sex']) ? (int)$_POST['sex'] : null; $character_vocation = isset($_POST['vocation']) ? (int)$_POST['vocation'] : null; diff --git a/system/pages/account/delete_character.php b/system/pages/account/delete_character.php index 2c2bb341..f5894c77 100644 --- a/system/pages/account/delete_character.php +++ b/system/pages/account/delete_character.php @@ -10,9 +10,16 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +$title = 'Delete Character'; +require __DIR__ . '/base.php'; + +if(!$logged) { + return; +} + $player_name = isset($_POST['delete_name']) ? stripslashes($_POST['delete_name']) : null; $password_verify = isset($_POST['delete_password']) ? $_POST['delete_password'] : null; -$password_verify = encrypt(($config_salt_enabled ? $account_logged->getCustomField('salt') : '') . $password_verify); +$password_verify = encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $password_verify); if(isset($_POST['deletecharactersave']) && $_POST['deletecharactersave'] == 1) { if(empty($player_name) || empty($password_verify)) { $errors[] = 'Character name or/and password is empty. Please fill in form.'; diff --git a/system/pages/account/login.php b/system/pages/account/login.php new file mode 100644 index 00000000..9cc31503 --- /dev/null +++ b/system/pages/account/login.php @@ -0,0 +1,113 @@ +enabled()) + { + $tmp = ''; + if($cache->fetch('failed_logins', $tmp)) + { + $tmp = unserialize($tmp); + $to_remove = array(); + foreach($tmp as $ip => $t) + { + if(time() - $t['last'] >= 5 * 60) + $to_remove[] = $ip; + } + + foreach($to_remove as $ip) + unset($tmp[$ip]); + } + else + $tmp = array(); + + $ip = $_SERVER['REMOTE_ADDR']; + $t = $tmp[$ip] ?? null; + } + + $account_logged = new OTS_Account(); + if (config('account_login_by_email')) { + $account_logged->findByEMail($login_account); + } + + if (!config('account_login_by_email') || config('account_login_by_email_fallback')) { + if(USE_ACCOUNT_NAME) { + $account_logged->find($login_account); + } else { + $account_logged->load($login_account, true); + } + } + + if($account_logged->isLoaded() && encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password) == $account_logged->getPassword() + && (!isset($t) || $t['attempts'] < 5) + ) + { + setSession('account', $account_logged->getNumber()); + setSession('password', encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password)); + if($remember_me) { + setSession('remember_me', true); + } + + $logged = true; + $logged_flags = $account_logged->getWebFlags(); + + if(isset($_POST['admin']) && !admin()) { + $errors[] = 'This account has no admin privileges.'; + unsetSession('account'); + unsetSession('password'); + unsetSession('remember_me'); + $logged = false; + } + else { + $account_logged->setCustomField('web_lastlogin', time()); + } + + $hooks->trigger(HOOK_LOGIN, array('account' => $account_logged, 'password' => $login_password, 'remember_me' => $remember_me)); + } + else + { + $hooks->trigger(HOOK_LOGIN_ATTEMPT, array('account' => $login_account, 'password' => $login_password, 'remember_me' => $remember_me)); + + $errorMessage = getAccountLoginByLabel() . ' or password is not correct.'; + + // temporary solution for blocking failed login attempts + if($cache->enabled()) + { + if(isset($t)) + { + $t['attempts']++; + $t['last'] = time(); + + if($t['attempts'] >= 5) + $errors[] = 'A wrong password has been entered 5 times in a row. You are unable to log into your account for the next 5 minutes. Please wait.'; + else + $errors[] = $errorMessage; + } + else + { + $t = array('attempts' => 1, 'last' => time()); + $errors[] = $errorMessage; + } + + $tmp[$ip] = $t; + $cache->set('failed_logins', serialize($tmp), 60 * 60); // save for 1 hour + } + else { + $errors[] = $errorMessage; + } + } + } + else { + $errors[] = 'Please enter your ' . getAccountLoginByLabel() . ' and password.'; + + $hooks->trigger(HOOK_LOGIN_ATTEMPT, array('account' => $login_account, 'password' => $login_password, 'remember_me' => $remember_me)); + } + + $hooks->trigger(HOOK_ACCOUNT_LOGIN_POST); +} diff --git a/system/pages/account/logout.php b/system/pages/account/logout.php new file mode 100644 index 00000000..55e15585 --- /dev/null +++ b/system/pages/account/logout.php @@ -0,0 +1,22 @@ + + * @copyright 2021 MyAAC + * @link https://my-aac.org + */ +defined('MYAAC') or die('Direct access not allowed!'); + +$title = 'Logout'; + +require __DIR__ . '/base.php'; + +if(!$logged) { + return; +} + +require SYSTEM . 'logout.php'; + +$twig->display('account.logout.html.twig'); diff --git a/system/pages/lostaccount.php b/system/pages/account/lost.php similarity index 98% rename from system/pages/lostaccount.php rename to system/pages/account/lost.php index 57d2355b..f09aa0fa 100644 --- a/system/pages/lostaccount.php +++ b/system/pages/account/lost.php @@ -17,7 +17,6 @@ if(!$config['mail_enabled']) return; } -$config_salt_enabled = $db->hasColumn('accounts', 'salt'); $action_type = isset($_REQUEST['action_type']) ? $_REQUEST['action_type'] : ''; if($action == '') { @@ -111,7 +110,7 @@ elseif($action == 'sendcode') else { $account->setCustomField('email_next', (time() + 60)); - echo '

    An error occorred while sending email! Try again later or contact with admin. For Admin: More info can be found in system/logs/mailer-error.log

    '; + echo '

    An error occurred while sending email! Try again later or contact with admin. For Admin: More info can be found in system/logs/mailer-error.log

    '; } } else @@ -292,7 +291,7 @@ elseif($action == 'step3') $account->setEMail($new_email); $tmp_new_pass = $new_pass; - if($config_salt_enabled) + if(USE_ACCOUNT_SALT) { $salt = generateRandomString(10, false, true, true); $tmp_new_pass = $salt . $new_pass; @@ -301,7 +300,7 @@ elseif($action == 'step3') $account->setPassword(encrypt($tmp_new_pass)); $account->save(); - if($config_salt_enabled) + if(USE_ACCOUNT_SALT) $account->setCustomField('salt', $salt); echo 'Your account name, new password and new e-mail.
    @@ -330,7 +329,7 @@ elseif($action == 'step3') } else { - echo '

    An error occorred while sending email! You will not receive e-mail with this informations. For Admin: More info can be found in system/logs/mailer-error.log

    '; + echo '

    An error occurred while sending email! You will not receive e-mail with this informations. For Admin: More info can be found in system/logs/mailer-error.log

    '; } } else @@ -481,7 +480,7 @@ elseif($action == 'setnewpassword') if(Validator::password($newpassword)) { $tmp_new_pass = $newpassword; - if($config_salt_enabled) + if(USE_ACCOUNT_SALT) { $salt = generateRandomString(10, false, true, true); $tmp_new_pass = $salt . $newpassword; @@ -513,7 +512,7 @@ elseif($action == 'setnewpassword') } else { - echo '

    New password work! An error occorred while sending email! You will not receive e-mail with new password. For Admin: More info can be found in system/logs/mailer-error.log'; + echo '

    New password work! An error occurred while sending email! You will not receive e-mail with new password. For Admin: More info can be found in system/logs/mailer-error.log'; } echo ' diff --git a/system/pages/account/manage.php b/system/pages/account/manage.php new file mode 100644 index 00000000..dead01bc --- /dev/null +++ b/system/pages/account/manage.php @@ -0,0 +1,98 @@ + + * @author Slawkens + * @copyright 2019 MyAAC + * @link https://my-aac.org + */ +defined('MYAAC') or die('Direct access not allowed!'); + +$title = 'Account Management'; +require __DIR__ . '/login.php'; +require __DIR__ . '/base.php'; + +if(!$logged) { + return; +} + +$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()) + $account_status = 'Free Account'; +else + $account_status = '' . ($freePremium ? 'Gratis Premium Account' : 'Premium Account, ' . $account_logged->getPremDays() . ' '.$dayOrDays.' left') . ''; + +$recovery_key = $account_logged->getCustomField('key'); +if(empty($recovery_key)) + $account_registered = 'No'; +else +{ + if($config['generate_new_reckey'] && $config['mail_enabled']) + $account_registered = 'Yes ( Buy new Recovery Key )'; + else + $account_registered = 'Yes'; +} + +$account_created = $account_logged->getCreated(); +$account_email = $account_logged->getEMail(); +$email_new_time = $account_logged->getCustomField("email_new_time"); +if($email_new_time > 1) + $email_new = $account_logged->getCustomField("email_new"); +$account_rlname = $account_logged->getRLName(); +$account_location = $account_logged->getLocation(); +if($account_logged->isBanned()) + if($account_logged->getBanTime() > 0) + $welcome_message = 'Your account is banished until '.date("j F Y, G:i:s", $account_logged->getBanTime()).'!'; + else + $welcome_message = 'Your account is banished FOREVER!'; +else + $welcome_message = 'Welcome to your account!'; + +$email_change = ''; +$email_request = false; +if($email_new_time > 1) +{ + if($email_new_time < time()) + $email_change = '
    (You can accept '.$email_new.' as a new email.)'; + else + { + $email_change = '
    You can accept new e-mail after '.date("j F Y", $email_new_time)."."; + $email_request = true; + } +} + +$actions = array(); +foreach($account_logged->getActionsLog(0, 1000) as $action) { + $actions[] = array('action' => $action['action'], 'date' => $action['date'], 'ip' => $action['ip'] != 0 ? long2ip($action['ip']) : inet_ntop($action['ipv6'])); +} + +$players = array(); +/** @var OTS_Players_List $account_players */ +$account_players = $account_logged->getPlayersList(); +$account_players->orderBy('id'); + +$twig->display('account.management.html.twig', array( + 'welcome_message' => $welcome_message, + 'recovery_key' => $recovery_key, + 'email_change' => $email_change, + 'email_request' => $email_request, + 'email_new_time' => $email_new_time, + 'email_new' => isset($email_new) ? $email_new : '', + 'account' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId(), + 'account_email' => $account_email, + 'account_created' => $account_created, + 'account_status' => $account_status, + 'account_registered' => $account_registered, + 'account_rlname' => $account_rlname, + 'account_location' => $account_location, + 'actions' => $actions, + 'players' => $account_players +)); diff --git a/system/pages/account/redirect.php b/system/pages/account/redirect.php new file mode 100644 index 00000000..8abff630 --- /dev/null +++ b/system/pages/account/redirect.php @@ -0,0 +1,17 @@ + + * @author Slawkens + * @copyright 2019 MyAAC + * @link https://my-aac.org + */ +defined('MYAAC') or die('Direct access not allowed!'); + +$redirect = urldecode($_REQUEST['redirect']); + +$twig->display('account.redirect.html.twig', array( + 'redirect' => $redirect +)); diff --git a/system/pages/account/register.php b/system/pages/account/register.php index bfb69ee1..1d16d905 100644 --- a/system/pages/account/register.php +++ b/system/pages/account/register.php @@ -10,8 +10,15 @@ */ defined('MYAAC') or die('Direct access not allowed!'); -$_POST['reg_password'] = isset($_POST['reg_password']) ? $_POST['reg_password'] : ''; -$reg_password = encrypt(($config_salt_enabled ? $account_logged->getCustomField('salt') : '') . $_POST['reg_password']); +$title = 'Register Account'; +require __DIR__ . '/base.php'; + +if(!$logged) { + return; +} + +$_POST['reg_password'] = $_POST['reg_password'] ?? ''; +$reg_password = encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $_POST['reg_password']); $old_key = $account_logged->getCustomField("key"); if(isset($_POST['registeraccountsave']) && $_POST['registeraccountsave'] == "1") { @@ -22,6 +29,7 @@ if(isset($_POST['registeraccountsave']) && $_POST['registeraccountsave'] == "1") $account_logged->setCustomField("key", $new_rec_key); $account_logged->logAction('Generated recovery key.'); + $message = ''; if($config['mail_enabled'] && $config['send_mail_when_generate_reckey']) { @@ -54,5 +62,3 @@ if($show_form) { //show form $twig->display('account.generate_recovery_key.html.twig'); } - -?> \ No newline at end of file diff --git a/system/pages/account/register_new.php b/system/pages/account/register_new.php index dfe9749f..fb2e8ea3 100644 --- a/system/pages/account/register_new.php +++ b/system/pages/account/register_new.php @@ -10,12 +10,21 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +$title = 'Register Account'; +require __DIR__ . '/base.php'; + +if(!$logged) { + return; +} + if(isset($_POST['reg_password'])) - $reg_password = encrypt(($config_salt_enabled ? $account_logged->getCustomField('salt') : '') . $_POST['reg_password']); + $reg_password = encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $_POST['reg_password']); $reckey = $account_logged->getCustomField('key'); -if((!$config['generate_new_reckey'] || !$config['mail_enabled']) || empty($reckey)) - echo 'You cant get new rec key'; +if((!$config['generate_new_reckey'] || !$config['mail_enabled']) || empty($reckey)) { + $errors[] = 'You cant get new recovery key.'; + $twig->display('error_box.html.twig', array('errors' => $errors)); +} else { $points = $account_logged->getCustomField('premium_points'); @@ -40,7 +49,7 @@ else $message = '
    Your recovery key were send on email address '.$account_logged->getEMail().' for '.$config['generate_new_reckey_price'].' premium points.'; } else - $message = '

    An error occorred while sending email ( '.$account_logged->getEMail().' ) with recovery key! Recovery key not changed. Try again later. For Admin: More info can be found in system/logs/mailer-error.log

    '; + $message = '

    An error occurred while sending email ( '.$account_logged->getEMail().' ) with recovery key! Recovery key not changed. Try again later. For Admin: More info can be found in system/logs/mailer-error.log

    '; $twig->display('success.html.twig', array( 'title' => 'Account Registered', @@ -67,5 +76,3 @@ else )); } } - -?> \ No newline at end of file diff --git a/system/pages/accountmanagement.php b/system/pages/accountmanagement.php deleted file mode 100644 index e3444a73..00000000 --- a/system/pages/accountmanagement.php +++ /dev/null @@ -1,151 +0,0 @@ - - * @author Slawkens - * @copyright 2019 MyAAC - * @link https://my-aac.org - */ -defined('MYAAC') or die('Direct access not allowed!'); -$title = 'Account Management'; - -if($config['account_country']) - require SYSTEM . 'countries.conf.php'; - -$groups = new OTS_Groups_List(); - -$show_form = true; -$config_salt_enabled = $db->hasColumn('accounts', 'salt'); - -if(ACTION == "logout" && !isset($_REQUEST['account_login'])) { - if(!defined('HOOK_LOGOUT_DISPLAY') || HOOK_LOGOUT_DISPLAY) { // plugin will take care of this message - $twig->display('account.logout.html.twig'); - } - - return; -} - -if(!$logged) -{ - if(ACTION == 'confirm_email') { - require PAGES . 'account/' . ACTION . '.php'; - return; - } - - if(!empty($errors)) - $twig->display('error_box.html.twig', array('errors' => $errors)); - - $twig->display('account.login.html.twig', array( - 'redirect' => isset($_REQUEST['redirect']) ? $_REQUEST['redirect'] : null, - 'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number', - 'error' => isset($errors[0]) ? $errors[0] : null - )); - - return; -} - -$errors = array(); - - if(isset($_REQUEST['redirect'])) - { - $redirect = urldecode($_REQUEST['redirect']); - - $twig->display('account.redirect.html.twig', array( - 'redirect' => $redirect - )); - return; - } - - if($action == '') - { - $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()) - $account_status = 'Free Account'; - else - $account_status = '' . ($freePremium ? 'Gratis Premium Account' : 'Premium Account, ' . $account_logged->getPremDays() . ' '.$dayOrDays.' left') . ''; - - $recovery_key = $account_logged->getCustomField('key'); - if(empty($recovery_key)) - $account_registered = 'No'; - else - { - if($config['generate_new_reckey'] && $config['mail_enabled']) - $account_registered = 'Yes ( Buy new Recovery Key )'; - else - $account_registered = 'Yes'; - } - - $account_created = $account_logged->getCreated(); - $account_email = $account_logged->getEMail(); - $email_new_time = $account_logged->getCustomField("email_new_time"); - if($email_new_time > 1) - $email_new = $account_logged->getCustomField("email_new"); - $account_rlname = $account_logged->getRLName(); - $account_location = $account_logged->getLocation(); - if($account_logged->isBanned()) - if($account_logged->getBanTime() > 0) - $welcome_message = 'Your account is banished until '.date("j F Y, G:i:s", $account_logged->getBanTime()).'!'; - else - $welcome_message = 'Your account is banished FOREVER!'; - else - $welcome_message = 'Welcome to your account!'; - - $email_change = ''; - $email_request = false; - if($email_new_time > 1) - { - if($email_new_time < time()) - $email_change = '
    (You can accept '.$email_new.' as a new email.)'; - else - { - $email_change = '
    You can accept new e-mail after '.date("j F Y", $email_new_time)."."; - $email_request = true; - } - } - - $actions = array(); - foreach($account_logged->getActionsLog(0, 1000) as $action) { - $actions[] = array('action' => $action['action'], 'date' => $action['date'], 'ip' => $action['ip'] != 0 ? long2ip($action['ip']) : inet_ntop($action['ipv6'])); - } - - $players = array(); - /** @var OTS_Players_List $account_players */ - $account_players = $account_logged->getPlayersList(); - $account_players->orderBy('id'); - - $twig->display('account.management.html.twig', array( - 'welcome_message' => $welcome_message, - 'recovery_key' => $recovery_key, - 'email_change' => $email_change, - 'email_request' => $email_request, - 'email_new_time' => $email_new_time, - 'email_new' => isset($email_new) ? $email_new : '', - 'account' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId(), - 'account_email' => $account_email, - 'account_created' => $account_created, - 'account_status' => $account_status, - 'account_registered' => $account_registered, - 'account_rlname' => $account_rlname, - 'account_location' => $account_location, - 'actions' => $actions, - 'players' => $account_players - )); - } - else { - if(!ctype_alnum(str_replace(array('-', '_'), '', $action))) { - error('Error: Action contains illegal characters.'); - } - else if(file_exists(PAGES . 'account/' . $action . '.php')) { - require PAGES . 'account/' . $action . '.php'; - } - else { - error('This page does not exists.'); - } - } -?> diff --git a/system/pages/admin/login.php b/system/pages/admin/login.php deleted file mode 100644 index 2c6d4e9f..00000000 --- a/system/pages/admin/login.php +++ /dev/null @@ -1,17 +0,0 @@ - - * @copyright 2019 MyAAC - * @link https://my-aac.org - */ -defined('MYAAC') or die('Direct access not allowed!'); -$title = 'Login'; - -$twig->display('admin.login.html.twig', array( - 'logout' => ($action == 'logout' ? 'You have been logged out!' : ''), - 'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number', - 'errors' => isset($errors)? $errors : '' -)); diff --git a/system/pages/changelog.php b/system/pages/changelog.php index 541f9088..bc65281b 100644 --- a/system/pages/changelog.php +++ b/system/pages/changelog.php @@ -10,8 +10,7 @@ defined('MYAAC') or die('Direct access not allowed!'); $title = 'Changelog'; -$_page = isset($_GET['page']) ? $_GET['page'] : 0; -$id = isset($_GET['id']) ? $_GET['id'] : 0; +$_page = isset($_GET['page']) ? (int)$_GET['page'] : 0; $limit = 30; $offset = $_page * $limit; $next_page = false; @@ -43,4 +42,3 @@ $twig->display('changelog.html.twig', array( 'next_page' => $next_page, 'canEdit' => $canEdit, )); -?> diff --git a/system/pages/characters.php b/system/pages/characters.php index a95ea305..c6c996c2 100644 --- a/system/pages/characters.php +++ b/system/pages/characters.php @@ -47,7 +47,7 @@ if(isset($_REQUEST['name'])) if(empty($name)) { $tmp_link = getPlayerLink($name); - echo 'Here you can get detailed information about a certain player on ' . $config['lua']['serverName'] . '.
    '; + echo 'Here you can get detailed information about a certain player on ' . $config['lua']['serverName'] . '.
    '; echo generate_search_form(true); return; } @@ -82,8 +82,9 @@ if($player->isLoaded() && !$player->isDeleted()) $outfit = $config['outfit_images_url'] . '?id=' . $player->getLookType() . ($db->hasColumn('players', 'lookaddons') ? '&addons=' . $player->getLookAddons() : '') . '&head=' . $player->getLookHead() . '&body=' . $player->getLookBody() . '&legs=' . $player->getLookLegs() . '&feet=' . $player->getLookFeet(); $flag = ''; - if($config['account_country']) + if($config['account_country']) { $flag = getFlagImage($account->getCountry()); + } $player_sex = 'Unknown'; if(isset($config['genders'][$player->getSex()])) @@ -147,9 +148,10 @@ if($player->isLoaded() && !$player->isDeleted()) if($config['characters']['skills']) { if($db->hasColumn('players', 'skill_fist')) {// tfs 1.0+ - $skills_db = $db->query('SELECT `skill_fist`, `skill_club`, `skill_sword`, `skill_axe`, `skill_dist`, `skill_shielding`, `skill_fishing` FROM `players` WHERE `id` = ' . $player->getId())->fetch(); + $skills_db = $db->query('SELECT `maglevel`, `skill_fist`, `skill_club`, `skill_sword`, `skill_axe`, `skill_dist`, `skill_shielding`, `skill_fishing` FROM `players` WHERE `id` = ' . $player->getId())->fetch(); $skill_ids = array( + POT::SKILL_MAGIC => 'maglevel', POT::SKILL_FIST => 'skill_fist', POT::SKILL_CLUB => 'skill_club', POT::SKILL_SWORD => 'skill_sword', @@ -175,8 +177,7 @@ if($player->isLoaded() && !$player->isDeleted()) } $quests_enabled = $config['characters']['quests'] && !empty($config['quests']); - if($quests_enabled) - { + if($quests_enabled) { $quests = $config['quests']; $sql_query_in = ''; $i = 0; @@ -197,10 +198,10 @@ if($player->isLoaded() && !$player->isDeleted()) foreach($quests as &$storage) { $storage = isset($player_storage[$storage]) && $player_storage[$storage] > 0; } + unset($storage); } - if($config['characters']['equipment']) - { + if($config['characters']['equipment']) { global $db; $eq_sql = $db->query('SELECT `pid`, `itemtype` FROM player_items WHERE player_id = '.$player->getId().' AND (`pid` >= 1 and `pid` <= 10)'); $equipment = array(); @@ -284,8 +285,7 @@ WHERE killers.death_id = '".$death['id']."' ORDER BY killers.final_hit DESC, kil $deaths[] = array('time' => $death['date'], 'description' => $description . '.'); } } - } - else { + } else { $mostdamage = ''; if($db->hasColumn('player_deaths', 'mostdamage_by')) $mostdamage = ', `mostdamage_by`, `mostdamage_is_player`, `unjustified`, `mostdamage_unjustified`'; @@ -294,8 +294,7 @@ WHERE killers.death_id = '".$death['id']."' ORDER BY killers.final_hit DESC, kil FROM `player_deaths` WHERE `player_id` = ' . $player->getId() . ' ORDER BY `time` DESC LIMIT 10;')->fetchAll(); - if(count($deaths_db)) - { + if(count($deaths_db)) { $number_of_rows = 0; foreach($deaths_db as $death) { @@ -326,14 +325,12 @@ WHERE killers.death_id = '".$death['id']."' ORDER BY killers.final_hit DESC, kil $frags = array(); $frag_add_content = ''; - if($config['characters']['frags'] && $db->hasTable('killers')) - { + if($config['characters']['frags'] && $db->hasTable('killers')) { //frags list by Xampy $i = 0; $frags_limit = 10; // frags limit to show? // default: 10 $player_frags = $db->query('SELECT `player_deaths`.*, `players`.`name`, `killers`.`unjustified` FROM `player_deaths` LEFT JOIN `killers` ON `killers`.`death_id` = `player_deaths`.`id` LEFT JOIN `player_killers` ON `player_killers`.`kill_id` = `killers`.`id` LEFT JOIN `players` ON `players`.`id` = `player_deaths`.`player_id` WHERE `player_killers`.`player_id` = '.$player->getId().' ORDER BY `date` DESC LIMIT 0,'.$frags_limit.';')->fetchAll(); - if(count($player_frags)) - { + if(count($player_frags)) { $row_count = 0; foreach($player_frags as $frag) { @@ -346,7 +343,7 @@ WHERE killers.death_id = '".$death['id']."' ORDER BY killers.final_hit DESC, kil // signature $settings = Settings::getInstance(); if($config['signature_enabled']) { - $signature_url = BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : '?') . urlencode($player->getName()) . '.png'; + $signature_url = BASE_URL . ($settings['core.friendly_urls']['value'] ? '' : 'index.php/') . urlencode($player->getName()) . '.png'; } $hidden = $player->isHidden(); @@ -417,9 +414,7 @@ WHERE killers.death_id = '".$death['id']."' ORDER BY killers.final_hit DESC, kil 'search_form' => generate_search_form(), 'canEdit' => hasFlag(FLAG_CONTENT_PLAYERS) || superAdmin() )); -} -else -{ +} else { $search_errors[] = 'Character ' . $name . ' does not exist or has been deleted.'; $twig->display('error_box.html.twig', array('errors' => $search_errors)); $search_errors = array(); @@ -433,8 +428,7 @@ else $deleted = 'deletion'; $query = $db->query('SELECT `name`, `level`, `vocation`' . $promotion . ' FROM `players` WHERE `name` LIKE ' . $db->quote('%' . $name . '%') . ' AND ' . $deleted . ' != 1 LIMIT ' . (int)config('characters_search_limit') . ';'); - if($query->rowCount() > 0) - { + if($query->rowCount() > 0) { echo 'Did you mean:
      '; foreach($query as $player) { if(isset($player['promotion'])) { diff --git a/system/pages/creatures.php b/system/pages/creatures.php index c019fbe2..f87b6f28 100644 --- a/system/pages/creatures.php +++ b/system/pages/creatures.php @@ -10,9 +10,10 @@ * @link https://my-aac.org */ defined('MYAAC') or die('Direct access not allowed!'); -$title = "Creatures"; +$title = 'Creatures'; -if (empty($_REQUEST['creature'])) { +if (empty($_REQUEST['name'])) { + // display list of monsters $preview = config('creatures_images_preview'); $creatures = $db->query('SELECT * FROM `' . TABLE_PREFIX . 'monsters` WHERE `hidden` != 1 '.(empty($_REQUEST['boss']) ? '': 'AND `rewardboss` = 1').' ORDER BY name asc')->fetchAll(); @@ -28,55 +29,55 @@ if (empty($_REQUEST['creature'])) { 'preview' => $preview )); -} else { - $creature_name = urldecode(stripslashes(ucwords(strtolower($_REQUEST['creature'])))); - $prep = $db->prepare('SELECT * FROM `' . TABLE_PREFIX . 'monsters` WHERE `hidden` != 1 AND `name` = ? LIMIT 1;'); - $prep->execute([$creature_name]); - $creature = $prep->fetch(); + return; +} - if (isset($creature['name'])) { - function sort_by_chance($a, $b) - { - if ($a['chance'] == $b['chance']) { - return 0; - } - return ($a['chance'] > $b['chance']) ? -1 : 1; +// display monster +$creature_name = urldecode(stripslashes(ucwords(strtolower($_REQUEST['name'])))); +$prep = $db->prepare('SELECT * FROM `' . TABLE_PREFIX . 'monsters` WHERE `hidden` != 1 AND `name` = ? LIMIT 1;'); +$prep->execute([$creature_name]); +$creature = $prep->fetch(); + +if (isset($creature['name'])) { + function sort_by_chance($a, $b) + { + if ($a['chance'] == $b['chance']) { + return 0; } - - $title = $creature['name'] . " - Creatures"; - - $creature['img_link']= getCreatureImgPath($creature_name); - - $voices = json_decode($creature['voices'], true); - $summons = json_decode($creature['summons'], true); - $elements = json_decode($creature['elements'], true); - $immunities = json_decode($creature['immunities'], true); - $loot = json_decode($creature['loot'], true); - usort($loot, 'sort_by_chance'); - - foreach ($loot as &$item) { - $item['name'] = getItemNameById($item['id']); - $item['rarity_chance'] = round($item['chance'] / 1000, 2); - $item['rarity'] = getItemRarity($item['chance']); - $item['tooltip'] = ucfirst($item['name']) . '
      Chance: ' . $item['rarity'] . (config('creatures_loot_percentage') ? ' ('. $item['rarity_chance'] .'%)' : '') . '
      Max count: ' . $item['count']; - } - - $creature['loot'] = isset($loot) ? $loot : null; - $creature['voices'] = isset($voices) ? $voices : null; - $creature['summons'] = isset($summons) ? $summons : null; - $creature['elements'] = isset($elements) ? $elements : null; - $creature['immunities'] = isset($immunities) ? $immunities : null; - - $twig->display('creature.html.twig', array( - 'creature' => $creature, - )); - - } else { - echo "Creature with name " . $creature_name . " doesn't exist."; + return ($a['chance'] > $b['chance']) ? -1 : 1; } -//back button - $twig->display('creatures.back_button.html.twig'); -} -?> + $title = $creature['name'] . " - Creatures"; + $creature['img_link']= getCreatureImgPath($creature_name); + + $voices = json_decode($creature['voices'], true); + $summons = json_decode($creature['summons'], true); + $elements = json_decode($creature['elements'], true); + $immunities = json_decode($creature['immunities'], true); + $loot = json_decode($creature['loot'], true); + usort($loot, 'sort_by_chance'); + + foreach ($loot as &$item) { + $item['name'] = getItemNameById($item['id']); + $item['rarity_chance'] = round($item['chance'] / 1000, 2); + $item['rarity'] = getItemRarity($item['chance']); + $item['tooltip'] = ucfirst($item['name']) . '
      Chance: ' . $item['rarity'] . (config('creatures_loot_percentage') ? ' ('. $item['rarity_chance'] .'%)' : '') . '
      Max count: ' . $item['count']; + } + + $creature['loot'] = isset($loot) ? $loot : null; + $creature['voices'] = isset($voices) ? $voices : null; + $creature['summons'] = isset($summons) ? $summons : null; + $creature['elements'] = isset($elements) ? $elements : null; + $creature['immunities'] = isset($immunities) ? $immunities : null; + + $twig->display('creature.html.twig', array( + 'creature' => $creature, + )); + +} else { + echo "Creature with name " . $creature_name . " doesn't exist."; +} + +// back button +$twig->display('creatures.back_button.html.twig'); diff --git a/system/pages/forum.php b/system/pages/forum.php index 12ec64e0..85456f2e 100644 --- a/system/pages/forum.php +++ b/system/pages/forum.php @@ -5,198 +5,59 @@ * @package MyAAC * @author Gesior * @author Slawkens - * @copyright 2019 MyAAC + * @copyright 2021 MyAAC * @link https://my-aac.org */ -defined('MYAAC') or die('Direct access not allowed!'); -$title = 'Forum'; +defined('MYAAC') or exit; -if(strtolower($config['forum']) != 'site') +require __DIR__ . '/forum/base.php'; +require __DIR__ . '/forum/admin.php'; + +$errors = []; +if(!empty($action)) { - if($config['forum'] != '') - { - header('Location: ' . $config['forum']); - exit; + if(!ctype_alnum(str_replace(array('-', '_'), '', $action))) { + error('Error: Action contains illegal characters.'); } - - echo 'Forum is disabled on this site.'; - return; -} - -if(!$logged) - echo 'You are not logged in. Log in to post on the forum.

      '; - -require_once LIBS . 'forum.php'; - -$canEdit = Forum::isModerator(); -if($canEdit) -{ - $groups = new OTS_Groups_List(); - - if(!empty($action)) - { - if($action == 'delete_board' || $action == 'edit_board' || $action == 'hide_board' || $action == 'moveup_board' || $action == 'movedown_board') - $id = $_REQUEST['id']; - - if(isset($_REQUEST['access'])) - $access = $_REQUEST['access']; - - if(isset($_REQUEST['guild'])) - $guild = $_REQUEST['guild']; - - if(isset($_REQUEST['name'])) - $name = $_REQUEST['name']; - - if(isset($_REQUEST['description'])) - $description = stripslashes($_REQUEST['description']); - - $errors = array(); - - if($action == 'add_board') { - if(Forum::add_board($name, $description, $access, $guild, $errors)) - $action = $name = $description = ''; - } - else if($action == 'delete_board') { - Forum::delete_board($id, $errors); - $action = ''; - } - else if($action == 'edit_board') - { - if(isset($id) && !isset($name)) { - $board = Forum::get_board($id); - $name = $board['name']; - $access = $board['access']; - $guild = $board['guild']; - $description = $board['description']; - } - else { - Forum::update_board($id, $name, $access, $guild, $description); - $action = $name = $description = ''; - $access = $guild = 0; - } - } - else if($action == 'hide_board') { - Forum::toggleHidden_board($id, $errors); - $action = ''; - } - else if($action == 'moveup_board') { - Forum::move_board($id, -1, $errors); - $action = ''; - } - else if($action == 'movedown_board') { - Forum::move_board($id, 1, $errors); - $action = ''; - } - - if(!empty($errors)) { - $twig->display('error_box.html.twig', array('errors' => $errors)); - $action = ''; - } - } - - if(empty($action) || $action == 'edit_board') { - $guilds = $db->query('SELECT `id`, `name` FROM `guilds`')->fetchAll(); - $twig->display('forum.add_board.html.twig', array( - 'link' => getLink('forum', ($action == 'edit_board' ? 'edit_board' : 'add_board')), - 'action' => $action, - 'id' => isset($id) ? $id : null, - 'name' => isset($name) ? $name : null, - 'description' => isset($description) ? $description : null, - 'access' => isset($access) ? $access : 0, - 'guild' => isset($guild) ? $guild : null, - 'groups' => $groups, - 'guilds' => $guilds - )); - - if($action == 'edit_board') - $action = ''; - } -} - -$sections = array(); -foreach(getForumBoards() as $section) -{ - $sections[$section['id']] = array( - 'id' => $section['id'], - 'name' => $section['name'], - 'description' => $section['description'], - 'closed' => $section['closed'] == '1', - 'guild' => $section['guild'], - 'access' => $section['access'] - ); - - if($canEdit) { - $sections[$section['id']]['hidden'] = $section['hidden']; + else if(file_exists(PAGES . 'forum/' . $action . '.php')) { + require PAGES . 'forum/' . $action . '.php'; + return; } else { - $sections[$section['id']]['hidden'] = 0; + error('This page does not exists.'); } } -$number_of_rows = 0; -if(empty($action)) -{ - $info = $db->query("SELECT `section`, COUNT(`id`) AS 'threads', SUM(`replies`) AS 'replies' FROM `" . FORUM_TABLE_PREFIX . "forum` WHERE `first_post` = `id` GROUP BY `section`")->fetchAll(); +$info = $db->query("SELECT `section`, COUNT(`id`) AS 'threads', SUM(`replies`) AS 'replies' FROM `" . FORUM_TABLE_PREFIX . "forum` WHERE `first_post` = `id` GROUP BY `section`")->fetchAll(); - $boards = array(); - foreach($info as $data) - $counters[$data['section']] = array('threads' => $data['threads'], 'posts' => $data['replies'] + $data['threads']); - foreach($sections as $id => $section) - { - $show = true; - if(Forum::hasAccess($id)) { - $last_post = $db->query("SELECT `players`.`name`, `" . FORUM_TABLE_PREFIX . "forum`.`post_date` FROM `players`, `" . FORUM_TABLE_PREFIX . "forum` WHERE `" . FORUM_TABLE_PREFIX . "forum`.`section` = ".(int) $id." AND `players`.`id` = `" . FORUM_TABLE_PREFIX . "forum`.`author_guid` ORDER BY `post_date` DESC LIMIT 1")->fetch(); - $boards[] = array( - 'id' => $id, - 'link' => getForumBoardLink($id), - 'name' => $section['name'], - 'description' => $section['description'], - 'hidden' => $section['hidden'], - 'posts' => isset($counters[$id]['posts']) ? $counters[$id]['posts'] : 0, - 'threads' => isset($counters[$id]['threads']) ? $counters[$id]['threads'] : 0, - 'last_post' => array( - 'name' => isset($last_post['name']) ? $last_post['name'] : null, - 'date' => isset($last_post['post_date']) ? $last_post['post_date'] : null, - 'player_link' => isset($last_post['name']) ? getPlayerLink($last_post['name']) : null, - ) - ); - } +$boards = array(); +foreach($info as $data) + $counters[$data['section']] = array('threads' => $data['threads'], 'posts' => $data['replies'] + $data['threads']); + +foreach($sections as $id => $section) +{ + $show = true; + if(Forum::hasAccess($id)) { + $last_post = $db->query("SELECT `players`.`name`, `" . FORUM_TABLE_PREFIX . "forum`.`post_date` FROM `players`, `" . FORUM_TABLE_PREFIX . "forum` WHERE `" . FORUM_TABLE_PREFIX . "forum`.`section` = ".(int) $id." AND `players`.`id` = `" . FORUM_TABLE_PREFIX . "forum`.`author_guid` ORDER BY `post_date` DESC LIMIT 1")->fetch(); + $boards[] = array( + 'id' => $id, + 'link' => getForumBoardLink($id), + 'name' => $section['name'], + 'description' => $section['description'], + 'hidden' => $section['hidden'], + 'posts' => isset($counters[$id]['posts']) ? $counters[$id]['posts'] : 0, + 'threads' => isset($counters[$id]['threads']) ? $counters[$id]['threads'] : 0, + 'last_post' => array( + 'name' => isset($last_post['name']) ? $last_post['name'] : null, + 'date' => isset($last_post['post_date']) ? $last_post['post_date'] : null, + 'player_link' => isset($last_post['name']) ? getPlayerLink($last_post['name']) : null, + ) + ); } - - $twig->display('forum.boards.html.twig', array( - 'boards' => $boards, - 'canEdit' => $canEdit, - 'last' => count($sections) - )); - - return; } -$errors = array(); -if($action == 'show_board' || $action == 'show_thread') -{ - require PAGES . 'forum/' . $action . '.php'; - return; -} - -if(!$logged) -{ - $extra_url = ''; - if($action == 'new_post' && isset($_GET['thread_id'])) { - $extra_url = '&action=new_post&thread_id=' . $_GET['thread_id']; - } - - header('Location: ' . BASE_URL . '?subtopic=accountmanagement&redirect=' . BASE_URL . urlencode('?subtopic=forum' . $extra_url)); - return; -} - -if(!ctype_alnum(str_replace(array('-', '_'), '', $action))) { - error('Error: Action contains illegal characters.'); -} -else if(file_exists(PAGES . 'forum/' . $action . '.php')) { - require PAGES . 'forum/' . $action . '.php'; -} -else { - error('This page does not exists.'); -} -?> +$twig->display('forum.boards.html.twig', array( + 'boards' => $boards, + 'canEdit' => $canEdit, + 'last' => count($sections) +)); diff --git a/system/pages/forum/admin.php b/system/pages/forum/admin.php new file mode 100644 index 00000000..136c627a --- /dev/null +++ b/system/pages/forum/admin.php @@ -0,0 +1,95 @@ + + * @author Slawkens + * @copyright 2021 MyAAC + * @link https://my-aac.org + */ +defined('MYAAC') or exit('Direct access not allowed!'); + +$canEdit = Forum::isModerator(); +if($canEdit) { + $groups = new OTS_Groups_List(); + + if(!empty($action)) + { + if($action == 'delete_board' || $action == 'edit_board' || $action == 'hide_board' || $action == 'moveup_board' || $action == 'movedown_board') + $id = $_REQUEST['id']; + + if(isset($_REQUEST['access'])) + $access = $_REQUEST['access']; + + if(isset($_REQUEST['guild'])) + $guild = $_REQUEST['guild']; + + if(isset($_REQUEST['name'])) + $name = $_REQUEST['name']; + + if(isset($_REQUEST['description'])) + $description = stripslashes($_REQUEST['description']); + + $errors = array(); + + if($action == 'add_board') { + if(Forum::add_board($name, $description, $access, $guild, $errors)) + $action = $name = $description = ''; + } + else if($action == 'delete_board') { + Forum::delete_board($id, $errors); + $action = ''; + } + else if($action == 'edit_board') + { + if(isset($id) && !isset($name)) { + $board = Forum::get_board($id); + $name = $board['name']; + $access = $board['access']; + $guild = $board['guild']; + $description = $board['description']; + } + else { + Forum::update_board($id, $name, $access, $guild, $description); + $action = $name = $description = ''; + $access = $guild = 0; + } + } + else if($action == 'hide_board') { + Forum::toggleHidden_board($id, $errors); + $action = ''; + } + else if($action == 'moveup_board') { + Forum::move_board($id, -1, $errors); + $action = ''; + } + else if($action == 'movedown_board') { + Forum::move_board($id, 1, $errors); + $action = ''; + } + + if(!empty($errors)) { + $twig->display('error_box.html.twig', array('errors' => $errors)); + $action = ''; + } + } + + if(empty($action) || $action == 'edit_board') { + $guilds = $db->query('SELECT `id`, `name` FROM `guilds`')->fetchAll(); + $twig->display('forum.add_board.html.twig', array( + 'link' => getLink('forum', ($action == 'edit_board' ? 'edit_board' : 'add_board')), + 'action' => $action, + 'id' => isset($id) ? $id : null, + 'name' => isset($name) ? $name : null, + 'description' => isset($description) ? $description : null, + 'access' => isset($access) ? $access : 0, + 'guild' => isset($guild) ? $guild : null, + 'groups' => $groups, + 'guilds' => $guilds + )); + + if($action == 'edit_board') + $action = ''; + } +} diff --git a/system/pages/forum/base.php b/system/pages/forum/base.php new file mode 100644 index 00000000..fa207ca0 --- /dev/null +++ b/system/pages/forum/base.php @@ -0,0 +1,51 @@ + + * @author Slawkens + * @copyright 2021 MyAAC + * @link https://my-aac.org + */ +defined('MYAAC') or die('Direct access not allowed!'); +$title = 'Forum'; + +if(strtolower($config['forum']) != 'site') +{ + if($config['forum'] != '') + { + header('Location: ' . $config['forum']); + exit; + } + + echo 'Forum is disabled on this site.'; + return; +} + +if(!$logged) + echo 'You are not logged in. Log in to post on the forum.

      '; + +require_once LIBS . 'forum.php'; + +$sections = array(); +foreach(getForumBoards() as $section) +{ + $sections[$section['id']] = array( + 'id' => $section['id'], + 'name' => $section['name'], + 'description' => $section['description'], + 'closed' => $section['closed'] == '1', + 'guild' => $section['guild'], + 'access' => $section['access'] + ); + + if($canEdit) { + $sections[$section['id']]['hidden'] = $section['hidden']; + } + else { + $sections[$section['id']]['hidden'] = 0; + } +} + +$number_of_rows = 0; diff --git a/system/pages/forum/edit_post.php b/system/pages/forum/edit_post.php index f522c5e6..45fd675b 100644 --- a/system/pages/forum/edit_post.php +++ b/system/pages/forum/edit_post.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + if(Forum::canPost($account_logged)) { $post_id = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : false; @@ -113,4 +115,4 @@ if(Forum::canPost($account_logged)) else echo "
      Your account is banned, deleted or you don't have any player with level " . $config['forum_level_required'] . " on your account. You can't post."; -?> \ No newline at end of file +?> diff --git a/system/pages/forum/move_thread.php b/system/pages/forum/move_thread.php index 48fb08f0..8fcb98fb 100644 --- a/system/pages/forum/move_thread.php +++ b/system/pages/forum/move_thread.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + if(!Forum::isModerator()) { echo 'You are not logged in or you are not moderator.'; } @@ -61,4 +63,4 @@ else { else echo 'Post with ID ' . $post_id . ' does not exist.'; } -?> \ No newline at end of file +?> diff --git a/system/pages/forum/new_post.php b/system/pages/forum/new_post.php index 436e162d..a3e68dff 100644 --- a/system/pages/forum/new_post.php +++ b/system/pages/forum/new_post.php @@ -10,6 +10,19 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + +if(!$logged) +{ + $extra_url = ''; + if(isset($_GET['thread_id'])) { + $extra_url = '&action=new_post&thread_id=' . $_GET['thread_id']; + } + + header('Location: ' . BASE_URL . '?subtopic=accountmanagement&redirect=' . BASE_URL . urlencode('?subtopic=forum' . $extra_url)); + return; +} + if(Forum::canPost($account_logged)) { $players_from_account = $db->query("SELECT `players`.`name`, `players`.`id` FROM `players` WHERE `players`.`account_id` = ".(int) $account_logged->getId())->fetchAll(); @@ -116,4 +129,4 @@ if(Forum::canPost($account_logged)) else echo "Your account is banned, deleted or you don't have any player with level " . $config['forum_level_required'] . " on your account. You can't post."; -$twig->display('forum.fullscreen.html.twig'); \ No newline at end of file +$twig->display('forum.fullscreen.html.twig'); diff --git a/system/pages/forum/new_thread.php b/system/pages/forum/new_thread.php index 489ea2d3..4db06c63 100644 --- a/system/pages/forum/new_thread.php +++ b/system/pages/forum/new_thread.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + if(Forum::canPost($account_logged)) { $players_from_account = $db->query('SELECT `players`.`name`, `players`.`id` FROM `players` WHERE `players`.`account_id` = '.(int) $account_logged->getId())->fetchAll(); @@ -102,4 +104,4 @@ if(Forum::canPost($account_logged)) else echo 'Your account is banned, deleted or you don\'t have any player with level '.$config['forum_level_required'].' on your account. You can\'t post.'; -?> \ No newline at end of file +?> diff --git a/system/pages/forum/remove_post.php b/system/pages/forum/remove_post.php index caef6a03..bd1e75a4 100644 --- a/system/pages/forum/remove_post.php +++ b/system/pages/forum/remove_post.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + if(Forum::isModerator()) { $id = (int) $_REQUEST['id']; @@ -33,4 +35,4 @@ if(Forum::isModerator()) echo 'Post with ID ' . $id . ' does not exist.'; } else - echo 'You are not logged in or you are not moderator.'; \ No newline at end of file + echo 'You are not logged in or you are not moderator.'; diff --git a/system/pages/forum/show_board.php b/system/pages/forum/show_board.php index 2828f8f0..36251d8e 100644 --- a/system/pages/forum/show_board.php +++ b/system/pages/forum/show_board.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $links_to_pages = ''; $section_id = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : null; @@ -57,8 +59,7 @@ if(isset($last_threads[0])) foreach($last_threads as $thread) { echo ''; - if(Forum::isModerator()) - { + if(Forum::isModerator()) { echo '[MOVE]'; echo '[REMOVE] '; } diff --git a/system/pages/forum/show_thread.php b/system/pages/forum/show_thread.php index bf6cda75..e6c8f926 100644 --- a/system/pages/forum/show_thread.php +++ b/system/pages/forum/show_thread.php @@ -10,9 +10,11 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $links_to_pages = ''; $thread_id = (int) $_REQUEST['id']; -$_page = (int) (isset($_REQUEST['page']) ? $_REQUEST['page'] : 0); +$_page = (int) ($_REQUEST['page'] ?? 0); $thread_starter = $db->query("SELECT `players`.`name`, `" . FORUM_TABLE_PREFIX . "forum`.`post_topic`, `" . FORUM_TABLE_PREFIX . "forum`.`section` FROM `players`, `" . FORUM_TABLE_PREFIX . "forum` WHERE `" . FORUM_TABLE_PREFIX . "forum`.`first_post` = ".(int) $thread_id." AND `" . FORUM_TABLE_PREFIX . "forum`.`id` = `" . FORUM_TABLE_PREFIX . "forum`.`first_post` AND `players`.`id` = `" . FORUM_TABLE_PREFIX . "forum`.`author_guid` LIMIT 1")->fetch(); if(empty($thread_starter['name'])) { diff --git a/system/pages/gallery.php b/system/pages/gallery.php index 58e34598..8cbf3975 100644 --- a/system/pages/gallery.php +++ b/system/pages/gallery.php @@ -145,8 +145,8 @@ class Gallery $pathinfo = pathinfo($image); $extension = strtolower($pathinfo['extension']); - $thumb_filename = 'images/gallery/' . $pathinfo['filename'] . '_thumb.' . $extension; - $filename = 'images/gallery/' . $pathinfo['filename'] . '.' . $extension; + $thumb_filename = GALLERY_DIR . $pathinfo['filename'] . '_thumb.' . $extension; + $filename = GALLERY_DIR . $pathinfo['filename'] . '.' . $extension; if($db->insert(TABLE_PREFIX . 'gallery', array( 'comment' => $comment, 'image' => $filename, 'author' => $author, @@ -172,7 +172,7 @@ class Gallery $pathinfo = pathinfo($image); $extension = strtolower($pathinfo['extension']); - $filename = 'images/gallery/' . $pathinfo['filename'] . '.' . $extension; + $filename = GALLERY_DIR . $pathinfo['filename'] . '.' . $extension; if($db->update(TABLE_PREFIX . 'gallery', array( 'comment' => $comment, @@ -291,7 +291,7 @@ class Gallery { $pathinfo = pathinfo($file); $extension = strtolower($pathinfo['extension']); - $thumb_filename = 'images/gallery/' . $pathinfo['filename'] . '_thumb.' . $extension; + $thumb_filename = GALLERY_DIR . $pathinfo['filename'] . '_thumb.' . $extension; if(!self::resize($file, 170, 110, $thumb_filename, $errors)) return false; diff --git a/system/pages/guilds.php b/system/pages/guilds.php index 0688a4c7..cf834210 100644 --- a/system/pages/guilds.php +++ b/system/pages/guilds.php @@ -11,16 +11,9 @@ defined('MYAAC') or die('Direct access not allowed!'); $title = 'Guilds'; -if($db->hasTable('guild_members')) - define('GUILD_MEMBERS_TABLE', 'guild_members'); -else - define('GUILD_MEMBERS_TABLE', 'guild_membership'); - -define('MOTD_EXISTS', $db->hasColumn('guilds', 'motd')); - //show list of guilds if(empty($action)) { - require PAGES . 'guilds/list_of_guilds.php'; + require PAGES . 'guilds/list.php'; } else { if(!ctype_alnum(str_replace(array('-', '_'), '', $action))) { diff --git a/system/pages/guilds/accept_invite.php b/system/pages/guilds/accept_invite.php index d33d5256..7b013a2c 100644 --- a/system/pages/guilds/accept_invite.php +++ b/system/pages/guilds/accept_invite.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + //set rights in guild $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : null; $name = isset($_REQUEST['name']) ? stripslashes($_REQUEST['name']) : null; @@ -39,13 +41,10 @@ if(isset($_REQUEST['todo']) && $_REQUEST['todo'] == 'save') { $player->find($name); if(!$player->isLoaded()) { $errors[] = 'Player with name '.$name.' doesn\'t exist.'; - } - else - { - $rank_of_player = $player->getRank(); - if($rank_of_player->isLoaded()) { - $errors[] = 'Character with name '.$name.' is already in guild. You must leave guild before you join other guild.'; - } + }else if ($player->getAccountID() != $account_logged->getId()) { + $errors[] = 'Character with name ' . $name. ' is not in your account.'; + }else if ($player->getRank()->isLoaded()){ + $errors[] = 'Character with name '.$name.' is already in guild. You must leave guild before you join other guild.'; } } } @@ -65,7 +64,7 @@ if(isset($_REQUEST['todo']) && $_REQUEST['todo'] == 'save') { } if(!$is_invited) { - $errors[] = 'Character '.$player->getName.' isn\'t invited to guild '.$guild->getName().'.'; + $errors[] = 'Character '.$player->getName() .' isn\'t invited to guild '.$guild->getName().'.'; } } } @@ -123,5 +122,3 @@ else { )); } } - -?> \ No newline at end of file diff --git a/system/pages/guilds/add_rank.php b/system/pages/guilds/add_rank.php index 56b20949..acb65aea 100644 --- a/system/pages/guilds/add_rank.php +++ b/system/pages/guilds/add_rank.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : null; $rank_name = isset($_REQUEST['rank_name']) ? $_REQUEST['rank_name'] : null; if(!Validator::guildName($guild_name)) { @@ -74,4 +76,4 @@ else } } -?> \ No newline at end of file +?> diff --git a/system/pages/guilds/base.php b/system/pages/guilds/base.php new file mode 100644 index 00000000..07fc432d --- /dev/null +++ b/system/pages/guilds/base.php @@ -0,0 +1,17 @@ + + * @copyright 2021 MyAAC + * @link https://my-aac.org + */ +defined('MYAAC') or die('Direct access not allowed!'); + +if($db->hasTable('guild_members')) + define('GUILD_MEMBERS_TABLE', 'guild_members'); +else + define('GUILD_MEMBERS_TABLE', 'guild_membership'); + +define('MOTD_EXISTS', $db->hasColumn('guilds', 'motd')); diff --git a/system/pages/guilds/change_description.php b/system/pages/guilds/change_description.php index 8d803af5..b4e787ee 100644 --- a/system/pages/guilds/change_description.php +++ b/system/pages/guilds/change_description.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : null; if(!Validator::guildName($guild_name)) { $errors[] = Validator::getLastError(); diff --git a/system/pages/guilds/change_logo.php b/system/pages/guilds/change_logo.php index 2430360d..fea3005f 100644 --- a/system/pages/guilds/change_logo.php +++ b/system/pages/guilds/change_logo.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : null; if(!Validator::guildName($guild_name)) { $errors[] = Validator::getLastError(); @@ -44,7 +46,7 @@ if(empty($errors)) { $allowed_ext = array('image/gif', 'image/jpg', 'image/pjpeg', 'image/jpeg', 'image/bmp', 'image/png', 'image/x-png'); $ext_name = array('image/gif' => 'gif', 'image/jpg' => 'jpg', 'image/jpeg' => 'jpg', 'image/pjpeg' => 'jpg', 'image/bmp' => 'bmp', 'image/png' => 'png', 'image/x-png' => 'png'); $save_file_name = str_replace(' ', '_', strtolower($guild->getName())); - $save_path = 'images/guilds/' . $save_file_name; + $save_path = GUILD_IMAGES_DIR . $save_file_name; if(isset($_REQUEST['todo']) && $_REQUEST['todo'] == 'save') { $file = $_FILES['newlogo']; @@ -74,12 +76,12 @@ if(empty($errors)) { { $guild_logo = $guild->getCustomField('logo_name'); $guild_logo = str_replace(array('..', '/', '\\'), array('','',''), $guild->getCustomField('logo_name')); - if(empty($guild_logo) || !file_exists('images/guilds/' . $guild_logo)) { + if(empty($guild_logo) || !file_exists(GUILD_IMAGES_DIR . $guild_logo)) { $guild_logo = "default.gif"; } if($guild_logo != "default.gif" && $guild_logo != $save_file_name.'.'.$extension) { - unlink('images/guilds/' . $guild_logo); + unlink(GUILD_IMAGES_DIR . $guild_logo); } } @@ -94,7 +96,7 @@ if(empty($errors)) { } $guild_logo = $guild->getCustomField('logo_name'); - if(empty($guild_logo) || !file_exists('images/guilds/' . $guild_logo)) { + if(empty($guild_logo) || !file_exists(GUILD_IMAGES_DIR . $guild_logo)) { $guild_logo = "default.gif"; } @@ -122,4 +124,4 @@ if(!empty($errors)) { 'action' => '?subtopic=guilds' )); } -?> \ No newline at end of file +?> diff --git a/system/pages/guilds/change_motd.php b/system/pages/guilds/change_motd.php index 490fea5f..c777548d 100644 --- a/system/pages/guilds/change_motd.php +++ b/system/pages/guilds/change_motd.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + if(!MOTD_EXISTS) return; @@ -74,4 +76,4 @@ if(!empty($errors)) { )); } -?> \ No newline at end of file +?> diff --git a/system/pages/guilds/change_nick.php b/system/pages/guilds/change_nick.php index 5e900a33..bf773124 100644 --- a/system/pages/guilds/change_nick.php +++ b/system/pages/guilds/change_nick.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + if(!$logged) { $errors[] = "You are not logged in. You can't change nick."; $twig->display('error_box.html.twig', array('errors' => $errors)); diff --git a/system/pages/guilds/change_rank.php b/system/pages/guilds/change_rank.php index 7e4fe92c..4341db16 100644 --- a/system/pages/guilds/change_rank.php +++ b/system/pages/guilds/change_rank.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + if(!$logged) { $errors[] = "You are not logged in. You can't change rank."; } @@ -86,7 +88,7 @@ if($guild_vice) else { $player_in_guild = false; - if($guild->getName() === $player_to_change->getRank()->getGuild()->getName() || $guild_leader) + if($guild->getName() === $player_to_change->getRank()->getGuild()->getName()) { $player_in_guild = true; $player_has_lower_rank = false; diff --git a/system/pages/guilds/cleanup_guilds.php b/system/pages/guilds/cleanup_guilds.php index e5e3804e..ebc41ee3 100644 --- a/system/pages/guilds/cleanup_guilds.php +++ b/system/pages/guilds/cleanup_guilds.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + if(!$logged) { echo "You are not logged in."; @@ -64,4 +66,4 @@ else echo "0 guilds found."; $twig->display('guilds.back_button.html.twig'); -?> \ No newline at end of file +?> diff --git a/system/pages/guilds/cleanup_players.php b/system/pages/guilds/cleanup_players.php index b22746b1..30b1e8af 100644 --- a/system/pages/guilds/cleanup_players.php +++ b/system/pages/guilds/cleanup_players.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + if(!$logged) { echo "You are not logged in."; @@ -66,4 +68,4 @@ else echo "0 players found."; $twig->display('guilds.back_button.html.twig'); -?> \ No newline at end of file +?> diff --git a/system/pages/guilds/create.php b/system/pages/guilds/create.php index 12a75d0b..9814ade0 100644 --- a/system/pages/guilds/create.php +++ b/system/pages/guilds/create.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : NULL; $name = isset($_REQUEST['name']) ? stripslashes($_REQUEST['name']) : NULL; $todo = isset($_REQUEST['todo']) ? $_REQUEST['todo'] : NULL; @@ -131,4 +133,4 @@ else { )); } -?> \ No newline at end of file +?> diff --git a/system/pages/guilds/delete_by_admin.php b/system/pages/guilds/delete_by_admin.php index fd9beafb..3e083c8a 100644 --- a/system/pages/guilds/delete_by_admin.php +++ b/system/pages/guilds/delete_by_admin.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : null; if(!Validator::guildName($guild_name)) { $errors[] = Validator::getLastError(); @@ -65,4 +67,4 @@ if(!empty($errors)) { )); } -?> \ No newline at end of file +?> diff --git a/system/pages/guilds/delete_guild.php b/system/pages/guilds/delete_guild.php index cfc87c65..978ac513 100644 --- a/system/pages/guilds/delete_guild.php +++ b/system/pages/guilds/delete_guild.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : null; if(!Validator::guildName($guild_name)) { $errors[] = Validator::getLastError(); @@ -77,4 +79,4 @@ if(!empty($errors)) { )); } -?> \ No newline at end of file +?> diff --git a/system/pages/guilds/delete_invite.php b/system/pages/guilds/delete_invite.php index 8a58f323..1bf5f730 100644 --- a/system/pages/guilds/delete_invite.php +++ b/system/pages/guilds/delete_invite.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : null; $name = stripslashes($_REQUEST['name']); @@ -113,4 +115,4 @@ else 'guild_name' => $guild->getName() )); } -} \ No newline at end of file +} diff --git a/system/pages/guilds/delete_rank.php b/system/pages/guilds/delete_rank.php index 4e52d60e..6a8f642c 100644 --- a/system/pages/guilds/delete_rank.php +++ b/system/pages/guilds/delete_rank.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : null; $rank_to_delete = isset($_REQUEST['rankid']) ? (int) $_REQUEST['rankid'] : null; diff --git a/system/pages/guilds/invite.php b/system/pages/guilds/invite.php index 57eda6cf..09957d2d 100644 --- a/system/pages/guilds/invite.php +++ b/system/pages/guilds/invite.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + //set rights in guild $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : NULL; $name = isset($_REQUEST['name']) ? stripslashes($_REQUEST['name']) : NULL; @@ -122,4 +124,4 @@ if($show) { $twig->display('guilds.back_button.html.twig', array( 'action' => getLink('guilds') . '/' . $guild_name -)); \ No newline at end of file +)); diff --git a/system/pages/guilds/kick_player.php b/system/pages/guilds/kick_player.php index f121ecfc..5ce79187 100644 --- a/system/pages/guilds/kick_player.php +++ b/system/pages/guilds/kick_player.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + //set rights in guild $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : null; $name = isset($_REQUEST['name']) ? stripslashes($_REQUEST['name']) : null; @@ -118,4 +120,4 @@ else )); } } -?> \ No newline at end of file +?> diff --git a/system/pages/guilds/leave_guild.php b/system/pages/guilds/leave.php similarity index 98% rename from system/pages/guilds/leave_guild.php rename to system/pages/guilds/leave.php index 2ce49c8a..892deb89 100644 --- a/system/pages/guilds/leave_guild.php +++ b/system/pages/guilds/leave.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + //set rights in guild $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : NULL; $name = isset($_REQUEST['name']) ? stripslashes($_REQUEST['name']) : NULL; diff --git a/system/pages/guilds/list_of_guilds.php b/system/pages/guilds/list.php similarity index 92% rename from system/pages/guilds/list_of_guilds.php rename to system/pages/guilds/list.php index 1d827c7f..6296db72 100644 --- a/system/pages/guilds/list_of_guilds.php +++ b/system/pages/guilds/list.php @@ -11,6 +11,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $guilds_list = new OTS_Guilds_List(); $guilds_list->orderBy("name"); @@ -19,7 +21,7 @@ if(count($guilds_list) > 0) { foreach ($guilds_list as $guild) { $guild_logo = $guild->getCustomField('logo_name'); - if (empty($guild_logo) || !file_exists('images/guilds/' . $guild_logo)) + if (empty($guild_logo) || !file_exists(GUILD_IMAGES_DIR . $guild_logo)) $guild_logo = "default.gif"; $description = $guild->getCustomField('description'); @@ -36,4 +38,4 @@ $twig->display('guilds.list.html.twig', array( 'guilds' => $guilds, 'logged' => isset($logged) ? $logged : false, 'isAdmin' => admin(), -)); \ No newline at end of file +)); diff --git a/system/pages/guilds/manager.php b/system/pages/guilds/manager.php index c3cabdc8..4d59c22e 100644 --- a/system/pages/guilds/manager.php +++ b/system/pages/guilds/manager.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : null; if(!Validator::guildName($guild_name)) { $errors[] = Validator::getLastError(); @@ -56,5 +58,3 @@ if(empty($errors)) { if(!empty($errors)) { $twig->display('error_box.html.twig', array('errors' => $errors)); } - -?> \ No newline at end of file diff --git a/system/pages/guilds/pass_leadership.php b/system/pages/guilds/pass_leadership.php index ddb3b4a3..462ac9ec 100644 --- a/system/pages/guilds/pass_leadership.php +++ b/system/pages/guilds/pass_leadership.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : NULL; $pass_to = isset($_REQUEST['player']) ? stripslashes($_REQUEST['player']) : NULL; if(!Validator::guildName($guild_name)) { @@ -116,4 +118,4 @@ if(!empty($guild_errors)) { echo '
      ' . $twig->render('buttons.back.html.twig') . '
      '; } -?> \ No newline at end of file +?> diff --git a/system/pages/guilds/save_ranks.php b/system/pages/guilds/save_ranks.php index fc1ed87a..65e047bb 100644 --- a/system/pages/guilds/save_ranks.php +++ b/system/pages/guilds/save_ranks.php @@ -10,6 +10,8 @@ */ defined('MYAAC') or die('Direct access not allowed!'); +require __DIR__ . '/base.php'; + $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : null; if(!Validator::guildName($guild_name)) { $errors[] = Validator::getLastError(); diff --git a/system/pages/guilds/show.php b/system/pages/guilds/show.php index ebd04ce6..dc10480c 100644 --- a/system/pages/guilds/show.php +++ b/system/pages/guilds/show.php @@ -12,6 +12,9 @@ defined('MYAAC') or die('Direct access not allowed!'); $title = 'Guilds'; + +require __DIR__ . '/base.php'; + $guild_name = isset($_REQUEST['guild']) ? urldecode($_REQUEST['guild']) : null; if(!Validator::guildName($guild_name)) $errors[] = Validator::getLastError(); @@ -77,7 +80,7 @@ if($logged) //show guild page $guild_logo = $guild->getCustomField('logo_name'); -if(empty($guild_logo) || !file_exists('images/guilds/' . $guild_logo)) +if(empty($guild_logo) || !file_exists(GUILD_IMAGES_DIR . $guild_logo)) $guild_logo = "default.gif"; $description = $guild->getCustomField('description'); @@ -159,4 +162,4 @@ $twig->display('guilds.view.html.twig', array( 'invited_list' => $invited_list, 'show_accept_invite' => $show_accept_invite, 'useGuildNick' => $useGuildNick -)); \ No newline at end of file +)); diff --git a/system/pages/highscores.php b/system/pages/highscores.php index 4988443b..95647893 100644 --- a/system/pages/highscores.php +++ b/system/pages/highscores.php @@ -15,12 +15,12 @@ $configHighscoresCountryBox = config('highscores_country_box'); if(config('account_country') && $configHighscoresCountryBox) require SYSTEM . 'countries.conf.php'; -$list = isset($_GET['list']) ? $_GET['list'] : 'experience'; -$_page = isset($_GET['page']) ? $_GET['page'] : 1; -$vocation = isset($_GET['vocation']) ? $_GET['vocation'] : 'all'; +$list = $_GET['list'] ?? 'experience'; +$page = $_GET['page'] ?? 1; +$vocation = $_GET['vocation'] ?? 'all'; -if(!is_numeric($_page) || $_page < 1 || $_page > PHP_INT_MAX) { - $_page = 1; +if(!is_numeric($page) || $page < 1 || $page > PHP_INT_MAX) { + $page = 1; } $add_sql = ''; @@ -47,8 +47,8 @@ if($configHighscoresVocationBox && $vocation !== 'all') } } -define('SKILL_FRAGS', -1); -define('SKILL_BALANCE', -2); +const SKILL_FRAGS = -1; +const SKILL_BALANCE = -2; $skill = POT::SKILL__LEVEL; if(is_numeric($list)) @@ -139,7 +139,7 @@ $configHighscoresPerPage = config('highscores_per_page'); $limit = $configHighscoresPerPage + 1; $needReCache = true; -$cacheKey = 'highscores_' . $skill . '_' . $vocation . '_' . $_page . '_' . $configHighscoresPerPage; +$cacheKey = 'highscores_' . $skill . '_' . $vocation . '_' . $page . '_' . $configHighscoresPerPage; $cache = Cache::getInstance(); if ($cache->enabled()) { @@ -150,7 +150,7 @@ if ($cache->enabled()) { } } -$offset = ($_page - 1) * $configHighscoresPerPage; +$offset = ($page - 1) * $configHighscoresPerPage; if (!isset($highscores) || empty($highscores)) { if ($skill >= POT::SKILL_FIRST && $skill <= POT::SKILL_LAST) { // skills if ($db->hasColumn('players', 'skill_fist')) {// tfs 1.0 @@ -280,14 +280,14 @@ foreach($highscores as $id => &$player) //link to previous page if actual page is not first $linkPreviousPage = ''; -if($_page > 1) { - $linkPreviousPage = getLink('highscores') . '/' . $list . ($vocation !== 'all' ? '/' . $vocation : '') . '/' . ($_page - 1); +if($page > 1) { + $linkPreviousPage = getLink('highscores') . '/' . $list . ($vocation !== 'all' ? '/' . $vocation : '') . '/' . ($page - 1); } //link to next page if any result will be on next page $linkNextPage = ''; if($show_link_to_next_page) { - $linkNextPage = getLink('highscores') . '/' . $list . ($vocation !== 'all' ? '/' . $vocation : '') . '/' . ($_page + 1); + $linkNextPage = getLink('highscores') . '/' . $list . ($vocation !== 'all' ? '/' . $vocation : '') . '/' . ($page + 1); } $types = array( diff --git a/system/pages/news.php b/system/pages/news.php index 30e00b54..92d2e7ab 100644 --- a/system/pages/news.php +++ b/system/pages/news.php @@ -64,7 +64,6 @@ if(isset($_GET['archive'])) 'icon' => $categories[$news['category']]['icon_id'], 'author' => $config['news_author'] ? $author : '', 'comments' => $news['comments'] != 0 ? getForumThreadLink($news['comments']) : null, - 'news_date_format' => $config['news_date_format'] )); } else @@ -214,7 +213,6 @@ if(!$news_cached) 'icon' => $categories[$news['category']]['icon_id'], 'author' => $config['news_author'] ? $author : '', 'comments' => $news['comments'] != 0 ? getForumThreadLink($news['comments']) : null, - 'news_date_format' => $config['news_date_format'], 'hidden'=> $news['hidden'] )); } diff --git a/system/pages/newsarchive.php b/system/pages/news/archive.php similarity index 86% rename from system/pages/newsarchive.php rename to system/pages/news/archive.php index aefa0fa9..fbcbe2fd 100644 --- a/system/pages/newsarchive.php +++ b/system/pages/news/archive.php @@ -9,5 +9,4 @@ * @link https://my-aac.org */ $_GET['archive'] = true; -require 'news.php'; -?> +require __DIR__ . '/../news.php'; diff --git a/system/pages/online.php b/system/pages/online.php index a7d505f5..ca629a3b 100644 --- a/system/pages/online.php +++ b/system/pages/online.php @@ -17,7 +17,8 @@ if($config['account_country']) $promotion = ''; if($db->hasColumn('players', 'promotion')) $promotion = '`promotion`,'; -$order = isset($_GET['order']) ? $_GET['order'] : 'name'; + +$order = $_GET['order'] ?? 'name'; if(!in_array($order, array('country', 'name', 'level', 'vocation'))) $order = $db->fieldName('name'); else if($order == 'country') @@ -47,8 +48,9 @@ if($config['online_outfit']) { if($config['online_vocations']) { $vocs = array(); - foreach($config['vocations'] as $id => $name) + foreach($config['vocations'] as $id => $name) { $vocs[$id] = 0; + } } if($db->hasTable('players_online')) // tfs 1.0 @@ -59,8 +61,7 @@ else $players_data = array(); $players = 0; $data = ''; -foreach($playersOnline as $player) -{ +foreach($playersOnline as $player) { $skull = ''; if($config['online_skulls']) { @@ -89,15 +90,14 @@ foreach($playersOnline as $player) 'outfit' => $config['online_outfit'] ? $config['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($config['online_vocations']) + if($config['online_vocations']) { $vocs[($player['vocation'] > $config['vocations_amount'] ? $player['vocation'] - $config['vocations_amount'] : $player['vocation'])]++; + } } $record = ''; -if($players > 0) -{ - if($config['online_record']) - { +if($players > 0) { + if($config['online_record']) { $timestamp = false; if($db->hasTable('server_record')) { $query = @@ -105,15 +105,13 @@ if($players > 0) 'SELECT `record`, `timestamp` FROM `server_record` WHERE `world_id` = ' . (int)$config['lua']['worldId'] . ' ORDER BY `record` DESC LIMIT 1'); $timestamp = true; - } - else if($db->hasTable('server_config')) { // tfs 1.0 + } else if($db->hasTable('server_config')) { // tfs 1.0 $query = $db->query('SELECT `value` as `record` FROM `server_config` WHERE `config` = ' . $db->quote('players_record')); - } - else + } else { $query = NULL; + } - if(isset($query) && $query->rowCount() > 0) - { + if(isset($query) && $query->rowCount() > 0) { $result = $query->fetch(); $record = 'The maximum on this game world was ' . $result['record'] . ' players' . ($timestamp ? ' on ' . date("M d Y, H:i:s", $result['timestamp']) . '.' : '.'); } diff --git a/system/php_sessions/index.html b/system/php_sessions/index.html new file mode 100644 index 00000000..e69de29b diff --git a/system/router.php b/system/router.php new file mode 100644 index 00000000..ff8c7d3d --- /dev/null +++ b/system/router.php @@ -0,0 +1,303 @@ + + * @copyright 2021 MyAAC + * @link https://my-aac.org + */ + +if(!isset($content[0])) + $content = ''; + +// check if site has been closed +$load_it = true; +$site_closed = false; +if(fetchDatabaseConfig('site_closed', $site_closed)) { + $site_closed = ($site_closed == 1); + if($site_closed) { + if(!admin()) + { + $title = getDatabaseConfig('site_closed_title'); + $content .= '

      ' . getDatabaseConfig('site_closed_message') . '


      '; + $load_it = false; + } + + if(!$logged) + { + ob_start(); + require SYSTEM . 'pages/account/manage.php'; + $content .= ob_get_contents(); + ob_end_clean(); + $load_it = false; + } + } +} +define('SITE_CLOSED', $site_closed); + +// Strip query string (?foo=bar) and decode URI +/** @var string $uri */ +if (false !== $pos = strpos($uri, '?')) { + if ($pos !== 1) { + $uri = substr($uri, 0, $pos); + } + else { + $uri = str_replace_first('?', '', $uri); + } +} + +$uri = rawurldecode($uri); +if (BASE_DIR !== '') { + $tmp = str_replace_first('/', '', BASE_DIR); + $uri = str_replace_first($tmp . '/', '', $uri); +} + +define('URI', $uri); + +/** @var boolean $load_it */ +if(!$load_it) { + // ignore warnings in some functions/plugins + // page is not loaded anyways + define('PAGE', ''); + + return; +} + +/** @var string $content */ +if(SITE_CLOSED && admin()) + $content .= '

      Site is under maintenance (closed mode). Only privileged users can see it.

      '; + +$ignore = false; + +/** @var boolean $logged */ +/** @var OTS_Account $account_logged */ +$logged_access = 1; +if($logged && $account_logged && $account_logged->isLoaded()) { + $logged_access = $account_logged->getAccess(); +} + +/** + * Routes loading + */ +$dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r) { + $routes = require SYSTEM . 'routes.php'; + + $isAlreadyDefined = []; + + $routesTmp = []; + foreach(getDatabasePages() as $page) { + $isAlreadyDefined[$page] = true; + $routesTmp[] = ['*', $page, '__database__/' . $page, true]; + } + + Plugins::clearWarnings(); + foreach (Plugins::getRoutes() as $route) { + if(!isset($isAlreadyDefined[$route[1]])) { + $isAlreadyDefined[$route[1]] = true; + $routesTmp[] = [$route[0], $route[1], $route[2]]; + } + } + + foreach ($routes as $route) { + if(!isset($isAlreadyDefined[$route[1]])) { + if (strpos($route[2], '__redirect__') === false && strpos($route[2], '__database__') === false) { + $routesTmp[] = [$route[0], $route[1], 'system/pages/' . $route[2]]; + } + else { + $routesTmp[] = [$route[0], $route[1], $route[2]]; + } + } + } + + //var_dump($routesTmp); + foreach ($routesTmp as $route) { + if ($route[0] === '*') { + $route[0] = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD']; + } + + $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]); + } + + if (config('env') === 'dev') { + foreach(Plugins::getWarnings() as $warning) { + log_append('router.log', $warning); + } + } +}, + [ + 'cacheFile' => CACHE . 'route.cache', + 'cacheDisabled' => config('env') === 'dev', + ] +); + +// Fetch method and URI +$httpMethod = $_SERVER['REQUEST_METHOD']; + +$found = true; + +// old support for pages like /?subtopic=accountmanagement +$page = $_REQUEST['p'] ?? ($_REQUEST['subtopic'] ?? ''); +if(!empty($page) && preg_match('/^[A-z0-9\-]+$/', $page)) { + if (config('backward_support')) { + require SYSTEM . 'compat/pages.php'; + } + + $file = loadPageFromFileSystem($page, $found); + if(!$found) { + $file = false; + } +} +else { + $routeInfo = $dispatcher->dispatch($httpMethod, $uri); + switch ($routeInfo[0]) { + case FastRoute\Dispatcher::NOT_FOUND: + // ... 404 Not Found + //var_dump('not found'); + /** + * Fallback to load page from templates/ or system/pages/ directory + */ + $page = $uri; + if (preg_match('/^[A-z0-9\/\-]+$/', $page)) { + $file = loadPageFromFileSystem($page, $found); + } else { + $found = false; + } + + break; + + case FastRoute\Dispatcher::METHOD_NOT_ALLOWED: + // ... 405 Method Not Allowed + $page = '405'; + $allowedMethods = $routeInfo[1]; + $file = SYSTEM . 'pages/405.php'; + break; + + case FastRoute\Dispatcher::FOUND: + $path = $routeInfo[1]; + $vars = $routeInfo[2]; + + $_REQUEST = array_merge($_REQUEST, $vars); + $_GET = array_merge($_GET, $vars); + + if (strpos($path, '__database__/') !== false) { + $pageName = str_replace('__database__/', '', $path); + + $success = false; + $tmp_content = getCustomPage($pageName, $success); + if ($success) { + $content .= $tmp_content; + if (hasFlag(FLAG_CONTENT_PAGES) || superAdmin()) { + $pageInfo = getCustomPageInfo($pageName); + $content = $twig->render('admin.pages.links.html.twig', array( + 'page' => array('id' => $pageInfo !== null ? $pageInfo['id'] : 0, 'hidden' => $pageInfo !== null ? $pageInfo['hidden'] : '0') + )) . $content; + } + + $page = $pageName; + $file = false; + } + } else if (strpos($path, '__redirect__/') !== false) { + $path = str_replace('__redirect__/', '', $path); + header('Location: ' . BASE_URL . $path); + exit; + } else { + // parse for define PAGE + $tmp = BASE_DIR; + $uri = $_SERVER['REQUEST_URI']; + if (!empty($tmp)) { + $uri = str_replace(BASE_DIR . '/', '', $uri); + } + + if (false !== $pos = strpos($uri, '?')) { + $uri = substr($uri, 0, $pos); + } + if (0 === strpos($uri, '/')) { + $uri = str_replace_first('/', '', $uri); + } + + $page = $uri; + $file = BASE . $path; + } + + unset($tmp, $uri); + break; + } +} + +if (!$found) { + $page = '404'; + $file = SYSTEM . 'pages/404.php'; +} + +define('PAGE', $page); + +ob_start(); +if($hooks->trigger(HOOK_BEFORE_PAGE)) { + if(!$ignore && $file !== false) + require $file; +} + +unset($file); + +if(config('backward_support') && isset($main_content[0])) + $content .= $main_content; + +$content .= ob_get_contents(); +ob_end_clean(); +$hooks->trigger(HOOK_AFTER_PAGE); + +if(!isset($title)) { + $title = ucfirst($page); +} + +if(config('backward_support')) { + $main_content = $content; + $topic = $title; +} + +unset($page); + +function getDatabasePages() { + global $db; + $pages = $db->query('SELECT `name` FROM ' . TABLE_PREFIX . 'pages'); + $ret = []; + + if ($pages->rowCount() < 1) { + return $ret; + } + + foreach($pages->fetchAll() as $page) { + $ret [] = $page['name']; + } + + return $ret; +} + +function loadPageFromFileSystem($page, &$found) { + $file = SYSTEM . 'pages/' . $page . '.php'; + if (!is_file($file)) { + // feature: convert camelCase to snake_case + // so instead of forum/move_thread + // we can write: forum/moveThread + $file = SYSTEM . 'pages/' . camelCaseToUnderscore($page) . '.php'; + if (!is_file($file)) { + // feature: load pages from templates/ dir + global $template_path; + $file = $template_path . '/pages/' . $page . '.php'; + if (!is_file($file)) { + $found = false; + } + } + } + + return $file; +} diff --git a/system/routes.php b/system/routes.php new file mode 100644 index 00000000..a7ddd295 --- /dev/null +++ b/system/routes.php @@ -0,0 +1,58 @@ + + * @copyright 2021 MyAAC + * @link https://my-aac.org + */ + +return [ + ['GET', '', '__redirect__/news'], // redirect empty URL to news + ['GET', 'news/archive/{id:int}[/]', 'news/archive.php'], + + // block access to some files + ['*', 'account/base[/]', '404.php'], // this is to block account/base.php + ['*', 'forum/base[/]', '404.php'], + ['*', 'guilds/base[/]', '404.php'], + + [['GET', 'POST'], 'account/password[/]', 'account/change_password.php'], + [['GET', 'POST'], 'account/register/new[/]', 'account/register_new.php'], + [['GET', 'POST'], 'account/email[/]', 'account/change_email.php'], + [['GET', 'POST'], 'account/info[/]', 'account/change_info.php'], + [['GET', 'POST'], 'account/character/create[/]', 'account/create_character.php'], + [['GET', 'POST'], 'account/character/name[/]', 'account/change_name.php'], + [['GET', 'POST'], 'account/character/sex[/]', 'account/change_sex.php'], + [['GET', 'POST'], 'account/character/delete[/]', 'account/delete_character.php'], + [['GET', 'POST'], 'account/character/comment[/{name:[A-Za-z0-9-_%+\']+}]', 'account/change_comment.php'], + ['GET', 'account/confirm_email/{hash:[A-Za-z0-9-_]+}[/]', 'account/confirm_email.php'], + + ['GET', 'bans/{page:\d+}[/]', 'bans.php'], + [['GET', 'POST'], 'characters[/{name:string]', 'characters.php'], + ['GET', 'changelog[/{page:int}]', 'changelog.php'], + ['GET', 'creatures[/{name:string}]', 'creatures.php'], + + ['GET', 'faq[/{action:string}]', 'faq.php'], + + [['GET', 'POST'], 'forum/{action:string}[/]', 'forum.php'], + ['GET', 'forum/board/{id:int}[/]', 'forum/show_board.php'], + ['GET', 'forum/board/{id:int}/{page:[0-9]+}[/]', 'forum/show_board.php'], + ['GET', 'forum/thread/{id:int}[/]', 'forum/show_thread.php'], + ['GET', 'forum/thread/{id:int}/{page:int}[/]', 'forum/show_thread.php'], + + ['GET', 'gallery/{image:int}[/]', 'gallery.php'], + [['GET', 'POST'], 'gallery/{action:string}[/]', 'gallery.php'], + + [['GET', 'POST'], 'guilds/{guild:string}[/]', 'guilds/show.php'], + + ['GET', 'highscores/{list:alphanum}/{vocation:alphanum}/{page:int}[/]', 'highscores.php'], + ['GET', 'highscores/{list:alphanum}/{page:int}[/]', 'highscores.php'], + ['GET', 'highscores/{list:alphanum}/{vocation:alphanum}[/]', 'highscores.php'], + ['GET', 'highscores/{list:alphanum}[/]', 'highscores.php'], +/* + '/^gifts\/history\/?$/' => array('subtopic' => 'gifts', 'action' => 'show_history'), + '/^polls\/[0-9]+\/?$/' => array('subtopic' => 'polls', 'id' => '$1'), + '/^spells\/[A-Za-z0-9-_%]+\/[A-Za-z0-9-_]+\/?$/' => array('subtopic' => 'spells', 'vocation' => '$1', 'order' => '$2'), + '/^houses\/view\/?$/' => array('subtopic' => 'houses', 'page' => 'view')*/ +]; diff --git a/system/templates/account.change_mail.html.twig b/system/templates/account.change_mail.html.twig index 1965f7a0..1c0d1c12 100644 --- a/system/templates/account.change_mail.html.twig +++ b/system/templates/account.change_mail.html.twig @@ -1,4 +1,4 @@ -Please enter your password and the new email address. Make sure that you enter a valid email address which you have access to. For security reasons, the actual change will be finalised after a waiting period of {{ config.account_mail_change }} days.

      +Please enter your password and the new email address. Make sure that you enter a valid email address which you have access to.
      For security reasons, the actual change will be finalised after a waiting period of {{ config.account_mail_change }} days.

      diff --git a/system/templates/account.create.html.twig b/system/templates/account.create.html.twig index 8d9d57f9..8e885b43 100644 --- a/system/templates/account.create.html.twig +++ b/system/templates/account.create.html.twig @@ -30,15 +30,20 @@ {{ hook('HOOK_ACCOUNT_CREATE_BEFORE_ACCOUNT') }} + {% if not config.account_login_by_email %} + {% endif %} {{ hook('HOOK_ACCOUNT_CREATE_AFTER_ACCOUNT') }} @@ -103,22 +108,6 @@ {{ hook('HOOK_ACCOUNT_CREATE_AFTER_PASSWORDS') }} - - {% if config.recaptcha_enabled %} - - - - - {% if errors.verification is defined %} - - {% endif %} - {% endif %} - - {{ hook('HOOK_ACCOUNT_CREATE_AFTER_RECAPTCHA') }}
      Account {% if constant('USE_ACCOUNT_NAME') %}Name{% else %}Number{% endif %}: + {% if not constant('USE_ACCOUNT_NAME') %} + + {% endif %}
      {% if errors.account is defined %}{{ errors.account }}{% endif %}
      {% if errors.password is defined %}{{ errors.password }}{% endif %}
      - Verification: - -
      -
      {{ errors.verification }}
      @@ -232,7 +221,7 @@ {% if config.character_towns|length > 1 %} - Select your city: + Select your town: @@ -333,4 +322,9 @@
      {{ hook('HOOK_ACCOUNT_CREATE_AFTER_FORM') }} - + + diff --git a/system/templates/account.create.js.html.twig b/system/templates/account.create.js.html.twig index 5dfb4b4b..74bfb8bf 100644 --- a/system/templates/account.create.js.html.twig +++ b/system/templates/account.create.js.html.twig @@ -20,6 +20,9 @@ $('#password2').blur(function() { checkPassword(); }); + $('#SuggestAccountNumber a').click(function (event) { + generateAccountNumber(event); + }); }); function updateFlag() @@ -192,4 +195,18 @@ lastSend = timeNow; } + + function generateAccountNumber(event) + { + event.preventDefault(); + $.getJSON("tools/generate_account_number.php", { uid: Math.random() }, + function(data){ + if(data.hasOwnProperty('success')) { + $('#account_input').val(data.success); + } + } + ); + + setTimeout(checkAccount, 1000); + } diff --git a/system/templates/account.login.html.twig b/system/templates/account.login.html.twig index 2ac4f3f6..2431f1d1 100644 --- a/system/templates/account.login.html.twig +++ b/system/templates/account.login.html.twig @@ -1,4 +1,5 @@ -Please enter your account {{ account|lower }} and your password.
      Create an account if you do not have one yet.

      +{{ hook('HOOK_ACCOUNT_LOGIN_BEFORE_PAGE') }} +Please enter your account {{ account|lower }} and your password.
      Create an account if you do not have one yet.

      {% if redirect is not null %} @@ -22,26 +23,27 @@ Please enter your account {{ account|lower }} and your password.
      + {{ hook('HOOK_ACCOUNT_LOGIN_BEFORE_PASSWORD') }} + {{ hook('HOOK_ACCOUNT_LOGIN_AFTER_ACCOUNT') }} + {{ hook('HOOK_ACCOUNT_LOGIN_AFTER_PASSWORD') }} - {% if error is not null %} - - {% endif %} + {{ hook('HOOK_ACCOUNT_LOGIN_AFTER_REMEMBER_ME') }}
      - Account {{ account }}: + {{ account_login_by }}:
      Password:
      {{ error }}
      @@ -73,4 +75,4 @@ Please enter your account {{ account|lower }} and your password.
      li { + float: right; + margin-right: 15px; +} + +.dropdown { + position: relative; + display: inline-block; +} + +.dropdown-content { + display: none; + position: absolute; + background-color: #343a40; + box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2); + z-index: 1; +} + +.dropdown-content a { + color: #eee; + padding: 12px 16px; + text-decoration: none; + display: block; +} + +.dropdown-content a:hover {color: lightskyblue;} + +.dropdown:hover .dropdown-content {display: block;} + + + + diff --git a/system/templates/admin.changelog.form.html.twig b/system/templates/admin.changelog.form.html.twig index e3ca5dab..99b90f7a 100644 --- a/system/templates/admin.changelog.form.html.twig +++ b/system/templates/admin.changelog.form.html.twig @@ -11,12 +11,12 @@
      - +
      - +
      diff --git a/system/templates/admin.login.html.twig b/system/templates/admin.login.html.twig index c68e2a66..acbcf040 100644 --- a/system/templates/admin.login.html.twig +++ b/system/templates/admin.login.html.twig @@ -18,15 +18,14 @@
      - +
      - +
      - + {{ hook('HOOK_ADMIN_LOGIN_AFTER_ACCOUNT') }}
      @@ -34,6 +33,7 @@
      + {{ hook('HOOK_ADMIN_LOGIN_AFTER_PASSWORD') }}
      @@ -46,6 +46,7 @@
      + {{ hook('HOOK_ADMIN_LOGIN_AFTER_SIGN_IN') }}
      diff --git a/system/templates/admin.mailer.html.twig b/system/templates/admin.mailer.html.twig index 761467d2..eaf32213 100644 --- a/system/templates/admin.mailer.html.twig +++ b/system/templates/admin.mailer.html.twig @@ -1,34 +1,28 @@ - +{{ include('tinymce.html.twig') }} +

      Sending mails may take some time if there are many users in db.

      Mailer
      -
      +
      +
      + +
      - - +