Compare commits
122 Commits
v1.8.8
...
feature/2f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed88f9f401 | ||
|
|
54265f42e9 | ||
|
|
bd87881ad0 | ||
|
|
c2424df7a4 | ||
|
|
4d2ed93b31 | ||
|
|
7471c49793 | ||
|
|
381d5bb884 | ||
|
|
7a113ee72a | ||
|
|
234e17654b | ||
|
|
1da771e3ca | ||
|
|
8d78e7090d | ||
|
|
fd457b2fe8 | ||
|
|
108e83806d | ||
|
|
85e8d4d9af | ||
|
|
9d6287ecbc | ||
|
|
16f4cdf40a | ||
|
|
9fa9ec746c | ||
|
|
3e7ee12676 | ||
|
|
b435a2fba4 | ||
|
|
4d7fe0bd58 | ||
|
|
d91de1005b | ||
|
|
88ea9ceee1 | ||
|
|
a92428287d | ||
|
|
9c8a78a386 | ||
|
|
ff4b15ad1d | ||
|
|
eaa8d9346e | ||
|
|
3e2d4d6686 | ||
|
|
e2c9c2bbe0 | ||
|
|
87509ffe16 | ||
|
|
04b37b4356 | ||
|
|
bf70595095 | ||
|
|
668f00e746 | ||
|
|
bbc8bef008 | ||
|
|
8d7c36e3eb | ||
|
|
1edb4743fe | ||
|
|
5e5fd43233 | ||
|
|
867e3e2c38 | ||
|
|
1975fb8ebe | ||
|
|
a44e2d6ebe | ||
|
|
babd822171 | ||
|
|
21e2eed640 | ||
|
|
2e4a8c3d3d | ||
|
|
9f64d7834f | ||
|
|
7d71bc2fee | ||
|
|
fdd0de8602 | ||
|
|
e3efbdc5a8 | ||
|
|
d4cc47e341 | ||
|
|
5040a93031 | ||
|
|
173b1ace88 | ||
|
|
276aa600e2 | ||
|
|
8103f5e70f | ||
|
|
8632cd3191 | ||
|
|
8b6f160a0f | ||
|
|
c3036e7d49 | ||
|
|
0c4edf625c | ||
|
|
c28dc29391 | ||
|
|
2db4f6a57b | ||
|
|
9bfd0242af | ||
|
|
3ea2b68561 | ||
|
|
dcdaa5ef43 | ||
|
|
7289cce826 | ||
|
|
af9d4c2aeb | ||
|
|
a66edfad31 | ||
|
|
89a35b5335 | ||
|
|
55da00520d | ||
|
|
efef16ee86 | ||
|
|
2f0b67f840 | ||
|
|
ca2e3bb576 | ||
|
|
e0e0e46701 | ||
|
|
61bcdc0c37 | ||
|
|
f966dff5a8 | ||
|
|
402f3bb9b0 | ||
|
|
e98de451d8 | ||
|
|
4fffaf6aff | ||
|
|
c44c9f9cf4 | ||
|
|
ccfd6f1a87 | ||
|
|
96b8e00f49 | ||
|
|
11cb1cf97e | ||
|
|
c5d3d3a25f | ||
|
|
e1197515f3 | ||
|
|
eebfc600cb | ||
|
|
9a99018dce | ||
|
|
6479546c22 | ||
|
|
effb23f367 | ||
|
|
08657c1599 | ||
|
|
1379c93439 | ||
|
|
19b1cfdd34 | ||
|
|
abee4b3962 | ||
|
|
fbdb6890b9 | ||
|
|
041f58ed11 | ||
|
|
03c7dd0002 | ||
|
|
e435062025 | ||
|
|
ecc9bd4042 | ||
|
|
797377e428 | ||
|
|
96b5df9d74 | ||
|
|
b3dfc56c96 | ||
|
|
96d6e04bd2 | ||
|
|
9146eee327 | ||
|
|
3d97fa0719 | ||
|
|
a66cafceab | ||
|
|
e719725841 | ||
|
|
bb3e90110d | ||
|
|
2f0758e351 | ||
|
|
6667c8c364 | ||
|
|
c13a540878 | ||
|
|
869ec035d9 | ||
|
|
9d696d31d8 | ||
|
|
8cc4caf587 | ||
|
|
e1d1c7d5db | ||
|
|
320733c2c1 | ||
|
|
c1809a98d1 | ||
|
|
46ed541015 | ||
|
|
29207361b7 | ||
|
|
25013ae91b | ||
|
|
5d630ba9dd | ||
|
|
feadf1314d | ||
|
|
08b8a716d4 | ||
|
|
cc26b5c744 | ||
|
|
cb6e9a6a88 | ||
|
|
4adb0758c5 | ||
|
|
7312383f73 | ||
|
|
3c1210fefa |
47
.github/workflows/cypress.yml
vendored
@@ -1,9 +1,9 @@
|
|||||||
name: Cypress
|
name: Cypress
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [develop]
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [develop]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cypress:
|
cypress:
|
||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
php-versions: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
|
php-versions: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
|
||||||
ots: ['tfs-1.4', 'canary-3.1.2'] # TODO: add 'tfs-master' (actually doesn't work cause AAC doesn't support reading .env configuration)
|
ots: ['tfs-1.4', 'canary-3.1.2', 'tfs-0.3'] # TODO: add 'tfs-master' (actually doesn't work cause AAC doesn't support reading .env configuration)
|
||||||
name: Cypress (PHP ${{ matrix.php-versions }}, ${{ matrix.ots }})
|
name: Cypress (PHP ${{ matrix.php-versions }}, ${{ matrix.ots }})
|
||||||
steps:
|
steps:
|
||||||
- name: 📌 MySQL Start & init & show db
|
- name: 📌 MySQL Start & init & show db
|
||||||
@@ -58,6 +58,14 @@ jobs:
|
|||||||
ref: master
|
ref: master
|
||||||
path: ots
|
path: ots
|
||||||
|
|
||||||
|
- name: Checkout TFS 0.3
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
if: matrix.ots == 'tfs-0.3'
|
||||||
|
with:
|
||||||
|
repository: otland/tfs-old-svn
|
||||||
|
ref: 0.3
|
||||||
|
path: ots
|
||||||
|
|
||||||
- name: Checkout Canary
|
- name: Checkout Canary
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
if: matrix.ots == 'canary-3.1.2'
|
if: matrix.ots == 'canary-3.1.2'
|
||||||
@@ -67,9 +75,15 @@ jobs:
|
|||||||
path: ots
|
path: ots
|
||||||
|
|
||||||
- name: Import OTS Schema
|
- name: Import OTS Schema
|
||||||
|
if: matrix.ots != 'tfs-0.3'
|
||||||
run: |
|
run: |
|
||||||
mysql -uroot -proot myaac < ots/schema.sql
|
mysql -uroot -proot myaac < ots/schema.sql
|
||||||
|
|
||||||
|
- name: Import OTS Schema (TFS 0.3)
|
||||||
|
if: matrix.ots == 'tfs-0.3'
|
||||||
|
run: |
|
||||||
|
mysql -uroot -proot myaac < ots/schemas/mysql.sql
|
||||||
|
|
||||||
- name: Rename config.lua
|
- name: Rename config.lua
|
||||||
run: mv ots/config.lua.dist ots/config.lua
|
run: mv ots/config.lua.dist ots/config.lua
|
||||||
|
|
||||||
@@ -109,6 +123,33 @@ jobs:
|
|||||||
regex: false
|
regex: false
|
||||||
include: 'ots/config.lua'
|
include: 'ots/config.lua'
|
||||||
|
|
||||||
|
- name: Replace mysqlPass (TFS 0.3.6pl1)
|
||||||
|
uses: jacobtomlinson/gha-find-replace@v3
|
||||||
|
if: matrix.ots == 'tfs-0.3'
|
||||||
|
with:
|
||||||
|
find: 'sqlType = "sqlite"'
|
||||||
|
replace: 'sqlType = "mysql"'
|
||||||
|
regex: false
|
||||||
|
include: 'ots/config.lua'
|
||||||
|
|
||||||
|
- name: Replace mysqlPass (TFS 0.3.6pl1)
|
||||||
|
uses: jacobtomlinson/gha-find-replace@v3
|
||||||
|
if: matrix.ots == 'tfs-0.3'
|
||||||
|
with:
|
||||||
|
find: 'sqlPass = ""'
|
||||||
|
replace: 'sqlPass = "root"'
|
||||||
|
regex: false
|
||||||
|
include: 'ots/config.lua'
|
||||||
|
|
||||||
|
- name: Replace mysqlDatabase (Canary)
|
||||||
|
uses: jacobtomlinson/gha-find-replace@v3
|
||||||
|
if: matrix.ots == 'tfs-0.3'
|
||||||
|
with:
|
||||||
|
find: 'sqlDatabase = "theforgottenserver"'
|
||||||
|
replace: 'sqlDatabase = "myaac"'
|
||||||
|
regex: false
|
||||||
|
include: 'ots/config.lua'
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
4
.github/workflows/phplint.yml
vendored
@@ -1,9 +1,9 @@
|
|||||||
name: PHP Linting
|
name: PHP Linting
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [develop]
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [develop]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
phplint:
|
phplint:
|
||||||
|
|||||||
6
.github/workflows/phpstan.yml
vendored
@@ -2,9 +2,9 @@ name: "PHPStan"
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [develop]
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [develop]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
php-versions: [ '8.1', '8.2', '8.3', '8.4' ]
|
php-versions: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
uses: "actions/checkout@v4"
|
uses: "actions/checkout@v4"
|
||||||
|
|||||||
21
CHANGELOG-2.x.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
## [2.0-dev - x.x.2025]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
* Add an "access" option to Menus (#340)
|
||||||
|
* Possibility to hide menus for unauthorized users
|
||||||
|
* Add the possibility to fetch skills in the getTopPlayers function (#347)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Better handling of vocations: (#345)
|
||||||
|
* Load from vocations.xml (No need to manually set)
|
||||||
|
* Support for Monk vocation
|
||||||
|
* Better gallery, loads images from images/gallery folder
|
||||||
|
* Reworked account action logs to use a single IP column as varchar(45) for both ipv4 and ipv6 (#289)
|
||||||
|
* Admin Panel: save menu collapse state (https://github.com/slawkens/myaac/commit/55da00520df7463a1d1ca41931df1598e9f2ffeb)
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
* Refactor account/lost pages (#326)
|
||||||
|
* Refactor OTS_Player to support more distros (#348)
|
||||||
|
* Refactor PHP cache to store expiration and improve typing (https://github.com/slawkens/myaac/commit/96b8e00f4999f8b4c4c97b54b97d91c6fd7df298)
|
||||||
|
* Move forum show_board code to Twig (https://github.com/slawkens/myaac/commit/e0e0e467012a5fb9979cc4387af6bad1d4540279)
|
||||||
|
* Save db cache only if it has changed (https://github.com/slawkens/myaac/commit/11cb1cf97e74f3bccf59360e1efb800a426b3d43)
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use MyAAC\Models\Account as AccountModel;
|
use MyAAC\Models\Account as AccountModel;
|
||||||
|
use MyAAC\Models\AccountAction;
|
||||||
use MyAAC\Models\Player;
|
use MyAAC\Models\Player;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
@@ -182,39 +183,7 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
$account->setName($name);
|
$account->setName($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($hasTypeColumn) {
|
|
||||||
$account->setCustomField('type', $group);
|
|
||||||
} elseif ($hasGroupColumn) {
|
|
||||||
$account->setCustomField('group_id', $group);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($hasSecretColumn) {
|
|
||||||
$account->setCustomField('secret', $secret);
|
|
||||||
}
|
|
||||||
|
|
||||||
$account->setCustomField('key', $key);
|
|
||||||
$account->setEMail($email);
|
$account->setEMail($email);
|
||||||
|
|
||||||
if (HAS_ACCOUNT_COINS) {
|
|
||||||
$account->setCustomField('coins', $t_coins);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HAS_ACCOUNT_COINS_TRANSFERABLE || HAS_ACCOUNT_TRANSFERABLE_COINS) {
|
|
||||||
$account->setCustomField(ACCOUNT_COINS_TRANSFERABLE_COLUMN, $t_coins_transferable);
|
|
||||||
}
|
|
||||||
|
|
||||||
$lastDay = 0;
|
|
||||||
if($p_days != 0 && $p_days != OTS_Account::GRATIS_PREMIUM_DAYS) {
|
|
||||||
$lastDay = time();
|
|
||||||
} else if ($lastDay != 0) {
|
|
||||||
$lastDay = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$account->setPremDays($p_days);
|
|
||||||
$account->setLastLogin($lastDay);
|
|
||||||
if ($hasPointsColumn) {
|
|
||||||
$account->setCustomField('premium_points', $p_points);
|
|
||||||
}
|
|
||||||
$account->setRLName($rl_name);
|
$account->setRLName($rl_name);
|
||||||
$account->setLocation($rl_loca);
|
$account->setLocation($rl_loca);
|
||||||
|
|
||||||
@@ -222,9 +191,18 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
$account->setCountry($rl_country);
|
$account->setCountry($rl_country);
|
||||||
}
|
}
|
||||||
|
|
||||||
$account->setCustomField('created', $created);
|
|
||||||
$account->setWebFlags($web_flags);
|
$account->setWebFlags($web_flags);
|
||||||
$account->setCustomField('web_lastlogin', $web_lastlogin);
|
|
||||||
|
if (!isCanary()) {
|
||||||
|
$lastDay = 0;
|
||||||
|
if($p_days != 0 && $p_days != OTS_Account::GRATIS_PREMIUM_DAYS) {
|
||||||
|
$lastDay = time();
|
||||||
|
}
|
||||||
|
|
||||||
|
$account->setLastLogin($lastDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
$account->setPremDays($p_days);
|
||||||
|
|
||||||
if (isset($password)) {
|
if (isset($password)) {
|
||||||
if (USE_ACCOUNT_SALT) {
|
if (USE_ACCOUNT_SALT) {
|
||||||
@@ -238,6 +216,34 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$account->save();
|
$account->save();
|
||||||
|
|
||||||
|
if ($hasTypeColumn) {
|
||||||
|
$account->setCustomField('type', $group);
|
||||||
|
} elseif ($hasGroupColumn) {
|
||||||
|
$account->setCustomField('group_id', $group);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasSecretColumn) {
|
||||||
|
$account->setCustomField('secret', $secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
$account->setCustomField('key', $key);
|
||||||
|
|
||||||
|
if (HAS_ACCOUNT_COINS) {
|
||||||
|
$account->setCustomField('coins', $t_coins);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HAS_ACCOUNT_COINS_TRANSFERABLE || HAS_ACCOUNT_TRANSFERABLE_COINS) {
|
||||||
|
$account->setCustomField(ACCOUNT_COINS_TRANSFERABLE_COLUMN, $t_coins_transferable);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasPointsColumn) {
|
||||||
|
$account->setCustomField('premium_points', $p_points);
|
||||||
|
}
|
||||||
|
|
||||||
|
$account->setCustomField('created', $created);
|
||||||
|
$account->setCustomField('web_lastlogin', $web_lastlogin);
|
||||||
|
|
||||||
echo_success('Account saved at: ' . date('G:i'));
|
echo_success('Account saved at: ' . date('G:i'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -481,9 +487,8 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php
|
||||||
$accountActions = \MyAAC\Models\AccountAction::where('account_id', $account->getId())->orderByDesc('date')->get();
|
$accountActions = AccountAction::where('account_id', $account->getId())->orderByDesc('date')->get();
|
||||||
foreach ($accountActions as $i => $log):
|
foreach ($accountActions as $i => $log):
|
||||||
$log->ip = ($log->ip != 0 ? long2ip($log->ip) : inet_ntop($log->ipv6));
|
|
||||||
?>
|
?>
|
||||||
<tr>
|
<tr>
|
||||||
<td><?php echo $i + 1; ?></td>
|
<td><?php echo $i + 1; ?></td>
|
||||||
@@ -631,6 +636,7 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<?php if (USE_ACCOUNT_NAME): ?>
|
||||||
<div class="col-6 col-lg-12">
|
<div class="col-6 col-lg-12">
|
||||||
<form action="<?php echo $admin_base; ?>" method="post">
|
<form action="<?php echo $admin_base; ?>" method="post">
|
||||||
<?php csrf(); ?>
|
<?php csrf(); ?>
|
||||||
@@ -641,6 +647,7 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
<div class="col-6 col-lg-12">
|
<div class="col-6 col-lg-12">
|
||||||
<form action="<?php echo $admin_base; ?>" method="post">
|
<form action="<?php echo $admin_base; ?>" method="post">
|
||||||
<?php csrf(); ?>
|
<?php csrf(); ?>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ if (!hasFlag(FLAG_CONTENT_MENUS) && !superAdmin()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$pluginThemes = Plugins::getThemes();
|
$pluginThemes = Plugins::getThemes();
|
||||||
|
$groups = new OTS_Groups_List();
|
||||||
|
|
||||||
if (isset($_POST['template'])) {
|
if (isset($_POST['template'])) {
|
||||||
$template = $_POST['template'];
|
$template = $_POST['template'];
|
||||||
@@ -32,6 +33,8 @@ if (isset($_POST['template'])) {
|
|||||||
$post_menu_link = $_POST['menu_link'] ?? [];
|
$post_menu_link = $_POST['menu_link'] ?? [];
|
||||||
$post_menu_blank = $_POST['menu_blank'] ?? [];
|
$post_menu_blank = $_POST['menu_blank'] ?? [];
|
||||||
$post_menu_color = $_POST['menu_color'] ?? [];
|
$post_menu_color = $_POST['menu_color'] ?? [];
|
||||||
|
$post_menu_access = $_POST['menu_access'] ?? [];
|
||||||
|
|
||||||
if (count($post_menu) != count($post_menu_link)) {
|
if (count($post_menu) != count($post_menu_link)) {
|
||||||
echo 'Menu count is not equal menu links. Something went wrong when sending form.';
|
echo 'Menu count is not equal menu links. Something went wrong when sending form.';
|
||||||
return;
|
return;
|
||||||
@@ -50,6 +53,7 @@ if (isset($_POST['template'])) {
|
|||||||
'link' => $post_menu_link[$category][$i],
|
'link' => $post_menu_link[$category][$i],
|
||||||
'blank' => $post_menu_blank[$category][$i] == 'on' ? 1 : 0,
|
'blank' => $post_menu_blank[$category][$i] == 'on' ? 1 : 0,
|
||||||
'color' => str_replace('#', '', $post_menu_color[$category][$i]),
|
'color' => str_replace('#', '', $post_menu_color[$category][$i]),
|
||||||
|
'access' => $post_menu_access[$category][$i],
|
||||||
'category' => $category,
|
'category' => $category,
|
||||||
'ordering' => $i
|
'ordering' => $i
|
||||||
]);
|
]);
|
||||||
@@ -122,7 +126,7 @@ if (isset($_POST['template'])) {
|
|||||||
?>
|
?>
|
||||||
<?php
|
<?php
|
||||||
$menus = Menu::query()
|
$menus = Menu::query()
|
||||||
->select('name', 'link', 'blank', 'color', 'category', 'ordering')
|
->select('name', 'link', 'access', 'blank', 'color', 'category', 'ordering')
|
||||||
->where('enabled', 1)
|
->where('enabled', 1)
|
||||||
->where('template', $template)
|
->where('template', $template)
|
||||||
->orderBy('ordering')
|
->orderBy('ordering')
|
||||||
@@ -151,11 +155,34 @@ if (isset($_POST['template'])) {
|
|||||||
foreach ($menus[$id] as $menu):
|
foreach ($menus[$id] as $menu):
|
||||||
$color = (empty($menu['color']) ? ($cat['default_links_color'] ?? ($config['menu_default_links_color'] ?? ($config['menu_default_color'] ?? '#ffffff'))) : '#' . $menu['color']);
|
$color = (empty($menu['color']) ? ($cat['default_links_color'] ?? ($config['menu_default_links_color'] ?? ($config['menu_default_color'] ?? '#ffffff'))) : '#' . $menu['color']);
|
||||||
?>
|
?>
|
||||||
<li class="ui-state-default" id="list-<?php echo $id ?>-<?php echo $i ?>"><label>Name:</label> <input type="text" name="menu[<?php echo $id ?>][]" value="<?php echo escapeHtml($menu['name']); ?>"/>
|
<li class="ui-state-default" id="list-<?php echo $id ?>-<?php echo $i ?>">
|
||||||
<label>Link:</label> <input type="text" name="menu_link[<?php echo $id ?>][]" value="<?php echo $menu['link'] ?>"/>
|
<label class="label_menu_name">Name: <input type="text" name="menu[<?php echo $id ?>][]" class="form-control menu-name" value="<?php echo escapeHtml($menu['name']); ?>"/>
|
||||||
<input type="hidden" name="menu_blank[<?php echo $id ?>][]" value="0"/>
|
</label>
|
||||||
<label><input class="blank-checkbox" type="checkbox" <?php echo($menu['blank'] == 1 ? 'checked' : '') ?>/><span title="Open in New Window">New Window</span></label>
|
|
||||||
<input class="color-picker" type="text" name="menu_color[<?php echo $id ?>][]" value="<?php echo $color; ?>"/>
|
<label class="label_menu_link">Link: <input type="text" name="menu_link[<?= $id ?>][]" class="form-control menu-link" value="<?php echo $menu['link'] ?>"/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<div class="menu-options-row">
|
||||||
|
|
||||||
|
<label>Access:
|
||||||
|
<select name="menu_access[<?= $id ?>][]" class="form-control menu-access">
|
||||||
|
<option value="0" <?= ($menu['access'] == 0 ? 'selected' : ''); ?>>Guest*</option>
|
||||||
|
<?php foreach ($groups->getGroups() as $group): ?>
|
||||||
|
<option value="<?= $group->getId(); ?>" <?= ($menu['access'] == $group->getId() ? 'selected' : ''); ?>><?= ucfirst($group->getName()); ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>Color: <input class="menu-color" type="color" name="menu_color[<?php echo $id ?>][]" value="<?php echo $color; ?>"/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<input type="hidden" name="menu_blank[<?php echo $id ?>][]" class="menu-blank" value="0"/>
|
||||||
|
<label><input type="checkbox" class="menu-blank-checkbox" <?php echo($menu['blank'] == 1 ? 'checked' : '') ?>/><span title="Open in New Window">New Window</span></label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<a class="remove-button" id="remove-button-<?php echo $id ?>-<?php echo $i ?>"><i class="fas fa-trash"></a></i></li>
|
<a class="remove-button" id="remove-button-<?php echo $id ?>-<?php echo $i ?>"><i class="fas fa-trash"></a></i></li>
|
||||||
<?php $i++; $last_id[$id] = $i;
|
<?php $i++; $last_id[$id] = $i;
|
||||||
endforeach;
|
endforeach;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
use MyAAC\Forum;
|
use MyAAC\Forum;
|
||||||
use MyAAC\Models\Player;
|
use MyAAC\Models\Player;
|
||||||
|
use MyAAC\Server\XML\Vocations;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ $skills = array(
|
|||||||
$hasBlessingsColumn = $db->hasColumn('players', 'blessings');
|
$hasBlessingsColumn = $db->hasColumn('players', 'blessings');
|
||||||
$hasBlessingColumn = $db->hasColumn('players', 'blessings1');
|
$hasBlessingColumn = $db->hasColumn('players', 'blessings1');
|
||||||
$hasLookAddons = $db->hasColumn('players', 'lookaddons');
|
$hasLookAddons = $db->hasColumn('players', 'lookaddons');
|
||||||
|
$hasCapColumn = $db->hasColumn('players', 'cap');
|
||||||
|
|
||||||
$skull_type = array("None", "Yellow", "Green", "White", "Red", "Black", "Orange");
|
$skull_type = array("None", "Yellow", "Green", "White", "Red", "Black", "Orange");
|
||||||
?>
|
?>
|
||||||
@@ -166,8 +168,11 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
$town = $_POST['town'];
|
$town = $_POST['town'];
|
||||||
verify_number($town, 'Town', 11);
|
verify_number($town, 'Town', 11);
|
||||||
|
|
||||||
$capacity = $_POST['capacity'];
|
if ($hasCapColumn) {
|
||||||
verify_number($capacity, 'Capacity', 11);
|
$capacity = $_POST['capacity'];
|
||||||
|
verify_number($capacity, 'Capacity', 11);
|
||||||
|
}
|
||||||
|
|
||||||
$sex = $_POST['sex'];
|
$sex = $_POST['sex'];
|
||||||
verify_number($sex, 'Sex', 1);
|
verify_number($sex, 'Sex', 1);
|
||||||
|
|
||||||
@@ -237,7 +242,30 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
$player->setGroup($groups->getGroup($group));
|
$player->setGroup($groups->getGroup($group));
|
||||||
$player->setLevel($level);
|
$player->setLevel($level);
|
||||||
$player->setExperience($experience);
|
$player->setExperience($experience);
|
||||||
|
|
||||||
|
if ($db->hasColumn('players', 'promotion')) {
|
||||||
|
$promotion = 0;
|
||||||
|
|
||||||
|
$vocationOriginal = Vocations::getOriginal($vocation);
|
||||||
|
if ($vocation != $vocationOriginal) {
|
||||||
|
$tmpId = $vocationOriginal;
|
||||||
|
while($promoted = Vocations::getPromoted($tmpId)) {
|
||||||
|
$promotion++;
|
||||||
|
|
||||||
|
$tmpId = $promoted;
|
||||||
|
if ($promoted == $vocation) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$vocation = $vocationOriginal;
|
||||||
|
}
|
||||||
|
|
||||||
|
$player->setPromotion($promotion);
|
||||||
|
}
|
||||||
|
|
||||||
$player->setVocation($vocation);
|
$player->setVocation($vocation);
|
||||||
|
|
||||||
$player->setHealth($health);
|
$player->setHealth($health);
|
||||||
$player->setHealthMax($health_max);
|
$player->setHealthMax($health_max);
|
||||||
$player->setMagLevel($magic_level);
|
$player->setMagLevel($magic_level);
|
||||||
@@ -249,16 +277,20 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
$player->setLookHead($look_head);
|
$player->setLookHead($look_head);
|
||||||
$player->setLookLegs($look_legs);
|
$player->setLookLegs($look_legs);
|
||||||
$player->setLookType($look_type);
|
$player->setLookType($look_type);
|
||||||
if ($hasLookAddons)
|
if ($hasLookAddons) {
|
||||||
$player->setLookAddons($look_addons);
|
$player->setLookAddons($look_addons);
|
||||||
if ($db->hasColumn('players', 'offlinetraining_time'))
|
}
|
||||||
$player->setCustomField('offlinetraining_time', $offlinetraining);
|
|
||||||
$player->setPosX($pos_x);
|
$player->setPosX($pos_x);
|
||||||
$player->setPosY($pos_y);
|
$player->setPosY($pos_y);
|
||||||
$player->setPosZ($pos_z);
|
$player->setPosZ($pos_z);
|
||||||
$player->setSoul($soul);
|
$player->setSoul($soul);
|
||||||
$player->setTownId($town);
|
$player->setTownId($town);
|
||||||
$player->setCap($capacity);
|
|
||||||
|
if ($hasCapColumn) {
|
||||||
|
$player->setCap($capacity);
|
||||||
|
}
|
||||||
|
|
||||||
$player->setSex($sex);
|
$player->setSex($sex);
|
||||||
$player->setLastLogin($lastlogin);
|
$player->setLastLogin($lastlogin);
|
||||||
$player->setLastLogout($lastlogout);
|
$player->setLastLogout($lastlogout);
|
||||||
@@ -275,23 +307,11 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
if ($hasBlessingsColumn)
|
if ($hasBlessingsColumn)
|
||||||
$player->setBlessings($blessings);
|
$player->setBlessings($blessings);
|
||||||
|
|
||||||
if ($hasBlessingColumn) {
|
|
||||||
for ($i = 1; $i <= $bless_count; $i++) {
|
|
||||||
$a = 'blessing' . $i;
|
|
||||||
$player->setCustomField('blessings' . $i, ${'blessing' . $i} ? '1' : '0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$player->setBalance($balance);
|
$player->setBalance($balance);
|
||||||
if ($db->hasColumn('players', 'stamina'))
|
if ($db->hasColumn('players', 'stamina'))
|
||||||
$player->setStamina($stamina);
|
$player->setStamina($stamina);
|
||||||
if ($db->hasColumn('players', 'deletion'))
|
|
||||||
$player->setCustomField('deletion', $deleted ? '1' : '0');
|
$player->setDeleted($deleted ? '1' : '0');
|
||||||
else
|
|
||||||
$player->setCustomField('deleted', $deleted ? '1' : '0');
|
|
||||||
$player->setCustomField('hide', $hide ? '1' : '0');
|
|
||||||
$player->setCustomField('created', $created);
|
|
||||||
if (isset($comment))
|
|
||||||
$player->setCustomField('comment', $comment);
|
|
||||||
|
|
||||||
foreach ($_POST['skills'] as $skill => $value) {
|
foreach ($_POST['skills'] as $skill => $value) {
|
||||||
$player->setSkill($skill, $value);
|
$player->setSkill($skill, $value);
|
||||||
@@ -300,6 +320,24 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
$player->setSkillTries($skill, $value);
|
$player->setSkillTries($skill, $value);
|
||||||
}
|
}
|
||||||
$player->save();
|
$player->save();
|
||||||
|
|
||||||
|
if ($db->hasColumn('players', 'offlinetraining_time')) {
|
||||||
|
$player->setCustomField('offlinetraining_time', $offlinetraining);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasBlessingColumn) {
|
||||||
|
for ($i = 1; $i <= $bless_count; $i++) {
|
||||||
|
$a = 'blessing' . $i;
|
||||||
|
$player->setCustomField('blessings' . $i, ${'blessing' . $i} ? '1' : '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$player->setCustomField('hide', $hide ? '1' : '0');
|
||||||
|
$player->setCustomField('created', $created);
|
||||||
|
if (isset($comment)) {
|
||||||
|
$player->setCustomField('comment', $comment);
|
||||||
|
}
|
||||||
|
|
||||||
echo_success('Player saved at: ' . date('G:i'));
|
echo_success('Player saved at: ' . date('G:i'));
|
||||||
$player->load($id);
|
$player->load($id);
|
||||||
}
|
}
|
||||||
@@ -531,10 +569,12 @@ else if (isset($_REQUEST['search'])) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
|
<?php if($hasCapColumn): ?>
|
||||||
<div class="col-12 col-sm-12 col-lg-6">
|
<div class="col-12 col-sm-12 col-lg-6">
|
||||||
<label for="capacity" class="control-label">Capacity:</label>
|
<label for="capacity" class="control-label">Capacity:</label>
|
||||||
<input type="text" class="form-control" id="capacity" name="capacity" autocomplete="off" size="3" maxlength="11" value="<?php echo $player->getCap(); ?>"/>
|
<input type="text" class="form-control" id="capacity" name="capacity" autocomplete="off" size="3" maxlength="11" value="<?php echo $player->getCap(); ?>"/>
|
||||||
</div>
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
<div class="col-12 col-sm-12 col-lg-6">
|
<div class="col-12 col-sm-12 col-lg-6">
|
||||||
<label for="soul" class="control-label">Soul:</label>
|
<label for="soul" class="control-label">Soul:</label>
|
||||||
<input type="text" class="form-control" id="soul" name="soul" autocomplete="off" size="3" maxlength="10" value="<?php echo $player->getSoul(); ?>"/>
|
<input type="text" class="form-control" id="soul" name="soul" autocomplete="off" size="3" maxlength="10" value="<?php echo $player->getSoul(); ?>"/>
|
||||||
|
|||||||
@@ -19,14 +19,14 @@
|
|||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
|
||||||
<?php $hooks->trigger(HOOK_ADMIN_HEAD_END); ?>
|
<?php $hooks->trigger(HOOK_ADMIN_HEAD_END); ?>
|
||||||
</head>
|
</head>
|
||||||
<body class="sidebar-mini ">
|
<body class="sidebar-mini <?= (session('admin.menu-collapse') ? 'sidebar-collapse' : ''); ?>">
|
||||||
<?php $hooks->trigger(HOOK_ADMIN_BODY_START); ?>
|
<?php $hooks->trigger(HOOK_ADMIN_BODY_START); ?>
|
||||||
<?php if ($logged && admin()) { ?>
|
<?php if ($logged && admin()) { ?>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
|
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" data-widget="pushmenu" href="#"><i class="fas fa-bars"></i></a>
|
<a class="nav-link sidebar-toggle" data-widget="pushmenu" href="#"><i class="fas fa-bars"></i></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item d-none d-sm-inline-block">
|
<li class="nav-item d-none d-sm-inline-block">
|
||||||
<a href="<?php echo ADMIN_URL; ?>" class="nav-link">Home</a>
|
<a href="<?php echo ADMIN_URL; ?>" class="nav-link">Home</a>
|
||||||
@@ -198,6 +198,7 @@ if ($logged && admin()) {
|
|||||||
<script src="<?php echo BASE_URL; ?>tools/js/datatables.bs.min.js"></script>
|
<script src="<?php echo BASE_URL; ?>tools/js/datatables.bs.min.js"></script>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
<script src="<?php echo BASE_URL; ?>tools/js/adminlte.min.js"></script>
|
<script src="<?php echo BASE_URL; ?>tools/js/adminlte.min.js"></script>
|
||||||
|
<?php $twig->display('admin.menu-collapse.html.twig'); ?>
|
||||||
<?php $hooks->trigger(HOOK_ADMIN_BODY_END); ?>
|
<?php $hooks->trigger(HOOK_ADMIN_BODY_END); ?>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
23
admin/tools/menu_collapse.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
const MYAAC_ADMIN = true;
|
||||||
|
const IGNORE_SET_LAST_VISIT = true;
|
||||||
|
|
||||||
|
require '../../common.php';
|
||||||
|
require SYSTEM . 'functions.php';
|
||||||
|
require SYSTEM . 'init.php';
|
||||||
|
require SYSTEM . 'login.php';
|
||||||
|
|
||||||
|
if(!admin()) {
|
||||||
|
http_response_code(500);
|
||||||
|
die('You are not logged in. Probably session expired. Please login again.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_POST['collapse'])) {
|
||||||
|
http_response_code(500);
|
||||||
|
die('Something went wrong.');
|
||||||
|
}
|
||||||
|
|
||||||
|
csrfProtect();
|
||||||
|
|
||||||
|
setSession('admin.menu-collapse', $_POST['collapse'] == 'true');
|
||||||
@@ -26,8 +26,8 @@
|
|||||||
if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is required.');
|
if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is required.');
|
||||||
|
|
||||||
const MYAAC = true;
|
const MYAAC = true;
|
||||||
const MYAAC_VERSION = '1.8.8';
|
const MYAAC_VERSION = '2.0-dev';
|
||||||
const DATABASE_VERSION = 46;
|
const DATABASE_VERSION = 51;
|
||||||
const TABLE_PREFIX = 'myaac_';
|
const TABLE_PREFIX = 'myaac_';
|
||||||
define('START_TIME', microtime(true));
|
define('START_TIME', microtime(true));
|
||||||
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX'));
|
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX'));
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
"symfony/var-dumper": "^6.4",
|
"symfony/var-dumper": "^6.4",
|
||||||
"filp/whoops": "^2.15",
|
"filp/whoops": "^2.15",
|
||||||
"maximebf/debugbar": "1.*",
|
"maximebf/debugbar": "1.*",
|
||||||
"guzzlehttp/guzzle": "7.9.3"
|
"guzzlehttp/guzzle": "7.9.3",
|
||||||
|
"spomky-labs/otphp": "^11.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^1.10"
|
"phpstan/phpstan": "^1.10"
|
||||||
|
|||||||
717
composer.lock
generated
BIN
images/druid.png
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 33 KiB |
BIN
images/monk.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 31 KiB |
@@ -3,6 +3,7 @@ defined('MYAAC') or die('Direct access not allowed!');
|
|||||||
|
|
||||||
use MyAAC\Models\Changelog;
|
use MyAAC\Models\Changelog;
|
||||||
use MyAAC\Models\Config;
|
use MyAAC\Models\Config;
|
||||||
|
use MyAAC\Models\FAQ;
|
||||||
use MyAAC\Models\ForumBoard;
|
use MyAAC\Models\ForumBoard;
|
||||||
use MyAAC\Models\Gallery;
|
use MyAAC\Models\Gallery;
|
||||||
use MyAAC\Models\NewsCategory;
|
use MyAAC\Models\NewsCategory;
|
||||||
@@ -56,13 +57,10 @@ if (NewsCategory::count() === 0) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Gallery::count() === 0) {
|
if(FAQ::count() == 0) {
|
||||||
Gallery::create([
|
FAQ::create([
|
||||||
'comment' => 'Demon',
|
'question' => 'What is this?',
|
||||||
'image' => 'images/gallery/demon.jpg',
|
'answer' => 'This is website for OTS powered by MyAAC.',
|
||||||
'thumb' => 'images/gallery/demon_thumb.gif',
|
|
||||||
'author' => 'MyAAC',
|
|
||||||
'ordering' => 0,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
CREATE TABLE IF NOT EXISTS `myaac_account_actions`
|
CREATE TABLE IF NOT EXISTS `myaac_account_actions`
|
||||||
(
|
(
|
||||||
|
`id` int NOT NULL AUTO_INCREMENT,
|
||||||
`account_id` int NOT NULL,
|
`account_id` int NOT NULL,
|
||||||
`ip` int unsigned NOT NULL DEFAULT 0,
|
`ip` varchar(45) NOT NULL DEFAULT '',
|
||||||
`ipv6` binary(16) NOT NULL DEFAULT 0,
|
|
||||||
`date` int NOT NULL DEFAULT 0,
|
`date` int NOT NULL DEFAULT 0,
|
||||||
`action` varchar(255) NOT NULL DEFAULT '',
|
`action` varchar(255) NOT NULL DEFAULT '',
|
||||||
KEY (`account_id`)
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `myaac_account_email_codes`
|
||||||
|
(
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`account_id` int NOT NULL,
|
||||||
|
`code` varchar(6) NOT NULL,
|
||||||
|
`created_at` int NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `myaac_account_emails_verify`
|
CREATE TABLE IF NOT EXISTS `myaac_account_emails_verify`
|
||||||
@@ -102,6 +111,7 @@ CREATE TABLE IF NOT EXISTS `myaac_menu`
|
|||||||
`template` varchar(255) NOT NULL,
|
`template` varchar(255) NOT NULL,
|
||||||
`name` varchar(255) NOT NULL,
|
`name` varchar(255) NOT NULL,
|
||||||
`link` varchar(255) NOT NULL,
|
`link` varchar(255) NOT NULL,
|
||||||
|
`access` tinyint NOT NULL DEFAULT 0,
|
||||||
`blank` tinyint NOT NULL DEFAULT 0,
|
`blank` tinyint NOT NULL DEFAULT 0,
|
||||||
`color` varchar(6) NOT NULL DEFAULT '',
|
`color` varchar(6) NOT NULL DEFAULT '',
|
||||||
`category` int NOT NULL DEFAULT 1,
|
`category` int NOT NULL DEFAULT 1,
|
||||||
@@ -197,18 +207,6 @@ CREATE TABLE IF NOT EXISTS `myaac_pages`
|
|||||||
UNIQUE (`name`)
|
UNIQUE (`name`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `myaac_gallery`
|
|
||||||
(
|
|
||||||
`id` int NOT NULL AUTO_INCREMENT,
|
|
||||||
`comment` varchar(255) NOT NULL DEFAULT '',
|
|
||||||
`image` varchar(255) NOT NULL,
|
|
||||||
`thumb` varchar(255) NOT NULL,
|
|
||||||
`author` varchar(50) NOT NULL DEFAULT '',
|
|
||||||
`ordering` int NOT NULL DEFAULT 0,
|
|
||||||
`hide` tinyint NOT NULL DEFAULT 0,
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `myaac_settings`
|
CREATE TABLE IF NOT EXISTS `myaac_settings`
|
||||||
(
|
(
|
||||||
`id` int NOT NULL AUTO_INCREMENT,
|
`id` int NOT NULL AUTO_INCREMENT,
|
||||||
|
|||||||
@@ -98,6 +98,16 @@ if(!$db->hasColumn('accounts', 'web_flags')) {
|
|||||||
success($locale['step_database_adding_field'] . ' accounts.web_flags...');
|
success($locale['step_database_adding_field'] . ' accounts.web_flags...');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!$db->hasColumn('accounts', '2fa_type')) {
|
||||||
|
if(query("ALTER TABLE `accounts` ADD `2fa_type` tinyint NOT NULL DEFAULT 0 AFTER `web_flags`;"))
|
||||||
|
success($locale['step_database_adding_field'] . ' accounts.2fa_type...');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$db->hasColumn('accounts', '2fa_secret')) {
|
||||||
|
if(query("ALTER TABLE `accounts` ADD `2fa_secret` varchar(16) NOT NULL DEFAULT '' AFTER `2fa_type`;"))
|
||||||
|
success($locale['step_database_adding_field'] . ' accounts.2fa_secret...');
|
||||||
|
}
|
||||||
|
|
||||||
if(!$db->hasColumn('accounts', 'email_verified')) {
|
if(!$db->hasColumn('accounts', 'email_verified')) {
|
||||||
if(query("ALTER TABLE `accounts` ADD `email_verified` TINYINT(1) NOT NULL DEFAULT 0 AFTER `web_flags`;"))
|
if(query("ALTER TABLE `accounts` ADD `email_verified` TINYINT(1) NOT NULL DEFAULT 0 AFTER `web_flags`;"))
|
||||||
success($locale['step_database_adding_field'] . ' accounts.email_verified...');
|
success($locale['step_database_adding_field'] . ' accounts.email_verified...');
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
define('MYAAC_INSTALL', true);
|
define('MYAAC_INSTALL', true);
|
||||||
|
|
||||||
use MyAAC\DataLoader;
|
use MyAAC\DataLoader;
|
||||||
use MyAAC\Models\FAQ as ModelsFAQ;
|
|
||||||
use MyAAC\Plugins;
|
|
||||||
|
|
||||||
require_once '../../common.php';
|
require_once '../../common.php';
|
||||||
|
|
||||||
@@ -25,34 +23,9 @@ if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['save
|
|||||||
|
|
||||||
require SYSTEM . 'init.php';
|
require SYSTEM . 'init.php';
|
||||||
|
|
||||||
if ($db->hasTable('players')) {
|
// add player samples
|
||||||
$deleted = 'deleted';
|
require_once SYSTEM . 'migrations/49.php';
|
||||||
if ($db->hasColumn('players', 'deletion'))
|
$up();
|
||||||
$deleted = 'deletion';
|
|
||||||
|
|
||||||
$time = time();
|
|
||||||
function insert_sample_if_not_exist($p)
|
|
||||||
{
|
|
||||||
global $db, $success, $deleted, $time;
|
|
||||||
|
|
||||||
$query = $db->query('SELECT `id` FROM `players` WHERE `name` = ' . $db->quote($p['name']));
|
|
||||||
if ($query->rowCount() == 0) {
|
|
||||||
if (!query("INSERT INTO `players` (`id`, `name`, `group_id`, `account_id`, `level`, `vocation`, `health`, `healthmax`, `experience`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `maglevel`, `mana`, `manamax`, `manaspent`, `soul`, `town_id`, `posx`, `posy`, `posz`, `conditions`, `cap`, `sex`, `lastlogin`, `lastip`, `save`, `lastlogout`, `balance`, `$deleted`, `created`, `hide`, `comment`) VALUES (null, " . $db->quote($p['name']) . ", 1, " . getSession('account') . ", " . $p['level'] . ", " . $p['vocation_id'] . ", " . $p['health'] . ", " . $p['healthmax'] . ", " . $p['experience'] . ", 118, 114, 38, 57, " . $p['looktype'] . ", 0, " . $p['mana'] . ", " . $p['manamax'] . ", 0, " . $p['soul'] . ", 1, 1000, 1000, 7, '', " . $p['cap'] . ", 1, " . $time . ", 2130706433, 1, " . $time . ", 0, 0, " . $time . ", 1, '');"))
|
|
||||||
$success = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$success = true;
|
|
||||||
insert_sample_if_not_exist(array('name' => 'Rook Sample', 'level' => 1, 'vocation_id' => 0, 'health' => 150, 'healthmax' => 150, 'experience' => 0, 'looktype' => 130, 'mana' => 0, 'manamax' => 0, 'soul' => 100, 'cap' => 400));
|
|
||||||
insert_sample_if_not_exist(array('name' => 'Sorcerer Sample', 'level' => 8, 'vocation_id' => 1, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
|
|
||||||
insert_sample_if_not_exist(array('name' => 'Druid Sample', 'level' => 8, 'vocation_id' => 2, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
|
|
||||||
insert_sample_if_not_exist(array('name' => 'Paladin Sample', 'level' => 8, 'vocation_id' => 3, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 129, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
|
|
||||||
insert_sample_if_not_exist(array('name' => 'Knight Sample', 'level' => 8, 'vocation_id' => 4, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 131, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
|
|
||||||
|
|
||||||
if ($success) {
|
|
||||||
success($locale['step_database_imported_players']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DataLoader::setLocale($locale);
|
DataLoader::setLocale($locale);
|
||||||
DataLoader::load();
|
DataLoader::load();
|
||||||
@@ -61,10 +34,6 @@ DataLoader::load();
|
|||||||
require_once SYSTEM . 'migrations/17.php';
|
require_once SYSTEM . 'migrations/17.php';
|
||||||
$up();
|
$up();
|
||||||
|
|
||||||
// update config.highscores_ids_hidden
|
|
||||||
require_once SYSTEM . 'migrations/20.php';
|
|
||||||
$up();
|
|
||||||
|
|
||||||
// add z_polls tables
|
// add z_polls tables
|
||||||
require_once SYSTEM . 'migrations/22.php';
|
require_once SYSTEM . 'migrations/22.php';
|
||||||
$up();
|
$up();
|
||||||
@@ -83,13 +52,6 @@ $up();
|
|||||||
require_once SYSTEM . 'migrations/45.php';
|
require_once SYSTEM . 'migrations/45.php';
|
||||||
$up();
|
$up();
|
||||||
|
|
||||||
if(ModelsFAQ::count() == 0) {
|
|
||||||
ModelsFAQ::create([
|
|
||||||
'question' => 'What is this?',
|
|
||||||
'answer' => 'This is website for OTS powered by MyAAC.',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$hooks->trigger(HOOK_INSTALL_FINISH);
|
$hooks->trigger(HOOK_INSTALL_FINISH);
|
||||||
|
|
||||||
$db->setClearCacheAfter(true);
|
$db->setClearCacheAfter(true);
|
||||||
|
|||||||
104
login.php
@@ -5,6 +5,7 @@ use MyAAC\Models\PlayerOnline;
|
|||||||
use MyAAC\Models\Account;
|
use MyAAC\Models\Account;
|
||||||
use MyAAC\Models\Player;
|
use MyAAC\Models\Player;
|
||||||
use MyAAC\RateLimit;
|
use MyAAC\RateLimit;
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
require_once 'common.php';
|
require_once 'common.php';
|
||||||
require_once SYSTEM . 'functions.php';
|
require_once SYSTEM . 'functions.php';
|
||||||
@@ -12,7 +13,7 @@ require_once SYSTEM . 'init.php';
|
|||||||
require_once SYSTEM . 'status.php';
|
require_once SYSTEM . 'status.php';
|
||||||
|
|
||||||
# error function
|
# error function
|
||||||
function sendError($message, $code = 3){
|
function sendError($message, $code = 3) {
|
||||||
$ret = [];
|
$ret = [];
|
||||||
$ret['errorCode'] = $code;
|
$ret['errorCode'] = $code;
|
||||||
$ret['errorMessage'] = $message;
|
$ret['errorMessage'] = $message;
|
||||||
@@ -108,17 +109,18 @@ switch ($action) {
|
|||||||
|
|
||||||
case 'login':
|
case 'login':
|
||||||
|
|
||||||
$port = $config['lua']['gameProtocolPort'];
|
$ip = configLua('ip');
|
||||||
|
$port = configLua('gameProtocolPort');
|
||||||
|
|
||||||
// default world info
|
// default world info
|
||||||
$world = [
|
$world = [
|
||||||
'id' => 0,
|
'id' => 0,
|
||||||
'name' => $config['lua']['serverName'],
|
'name' => $config['lua']['serverName'],
|
||||||
'externaladdress' => $config['lua']['ip'],
|
'externaladdress' => $ip,
|
||||||
'externalport' => $port,
|
'externalport' => $port,
|
||||||
'externaladdressprotected' => $config['lua']['ip'],
|
'externaladdressprotected' => $ip,
|
||||||
'externalportprotected' => $port,
|
'externalportprotected' => $port,
|
||||||
'externaladdressunprotected' => $config['lua']['ip'],
|
'externaladdressunprotected' => $ip,
|
||||||
'externalportunprotected' => $port,
|
'externalportunprotected' => $port,
|
||||||
'previewstate' => 0,
|
'previewstate' => 0,
|
||||||
'location' => 'BRA', // BRA, EUR, USA
|
'location' => 'BRA', // BRA, EUR, USA
|
||||||
@@ -133,13 +135,12 @@ switch ($action) {
|
|||||||
|
|
||||||
$inputEmail = $request->email ?? false;
|
$inputEmail = $request->email ?? false;
|
||||||
$inputAccountName = $request->accountname ?? false;
|
$inputAccountName = $request->accountname ?? false;
|
||||||
$inputToken = $request->token ?? false;
|
|
||||||
|
|
||||||
$account = Account::query();
|
$account = Account::query();
|
||||||
if ($inputEmail != false) { // login by email
|
if ($inputEmail) { // login by email
|
||||||
$account->where('email', $inputEmail);
|
$account->where('email', $inputEmail);
|
||||||
}
|
}
|
||||||
else if($inputAccountName != false) { // login by account name
|
else if($inputAccountName) { // login by account name
|
||||||
$account->where('name', $inputAccountName);
|
$account->where('name', $inputAccountName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,13 +152,14 @@ switch ($action) {
|
|||||||
$limiter->load();
|
$limiter->load();
|
||||||
|
|
||||||
$ban_msg = 'A wrong account, password or secret has been entered ' . setting('core.account_login_attempts_limit') . ' times in a row. You are unable to log into your account for the next ' . setting('core.account_login_ban_time') . ' minutes. Please wait.';
|
$ban_msg = 'A wrong account, password or secret has been entered ' . setting('core.account_login_attempts_limit') . ' times in a row. You are unable to log into your account for the next ' . setting('core.account_login_ban_time') . ' minutes. Please wait.';
|
||||||
|
|
||||||
if (!$account) {
|
if (!$account) {
|
||||||
$limiter->increment($ip);
|
$limiter->increment($ip);
|
||||||
if ($limiter->exceeded($ip)) {
|
if ($limiter->exceeded($ip)) {
|
||||||
sendError($ban_msg);
|
sendError($ban_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendError(($inputEmail != false ? 'Email' : 'Account name') . ' or password is not correct.');
|
sendError(($inputEmail ? 'Email' : 'Account name') . ' or password is not correct.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$current_password = encrypt((USE_ACCOUNT_SALT ? $account->salt : '') . $request->password);
|
$current_password = encrypt((USE_ACCOUNT_SALT ? $account->salt : '') . $request->password);
|
||||||
@@ -167,32 +169,30 @@ switch ($action) {
|
|||||||
sendError($ban_msg);
|
sendError($ban_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendError(($inputEmail != false ? 'Email' : 'Account name') . ' or password is not correct.');
|
sendError(($inputEmail ? 'Email' : 'Account name') . ' or password is not correct.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$accountHasSecret = false;
|
$twoFactorAuth = TwoFactorAuth::getInstance($account->id);
|
||||||
if (fieldExist('secret', 'accounts')) {
|
|
||||||
$accountSecret = $account->secret;
|
|
||||||
if ($accountSecret != null && $accountSecret != '') {
|
|
||||||
$accountHasSecret = true;
|
|
||||||
if ($inputToken === false) {
|
|
||||||
$limiter->increment($ip);
|
|
||||||
if ($limiter->exceeded($ip)) {
|
|
||||||
sendError($ban_msg);
|
|
||||||
}
|
|
||||||
sendError('Submit a valid two-factor authentication token.', 6);
|
|
||||||
} else {
|
|
||||||
require_once LIBS . 'rfc6238.php';
|
|
||||||
if (TokenAuth6238::verify($accountSecret, $inputToken) !== true) {
|
|
||||||
$limiter->increment($ip);
|
|
||||||
if ($limiter->exceeded($ip)) {
|
|
||||||
sendError($ban_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendError('Two-factor authentication failed, token is wrong.', 6);
|
$code = '';
|
||||||
}
|
if ($twoFactorAuth->isActive()) {
|
||||||
}
|
if ($twoFactorAuth->getAuthType() === TwoFactorAuth::TYPE_EMAIL) {
|
||||||
|
$code = $request->emailcode ?? false;
|
||||||
}
|
}
|
||||||
|
else if ($twoFactorAuth->getAuthType() === TwoFactorAuth::TYPE_APP) {
|
||||||
|
$code = $request->token ?? false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
$errorCode = 6;
|
||||||
|
if (!$twoFactorAuth->processClientLogin($code, $error, $errorCode)) {
|
||||||
|
$limiter->increment($ip);
|
||||||
|
if ($limiter->exceeded($ip)) {
|
||||||
|
sendError($ban_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendError($error, $errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
$limiter->reset($ip);
|
$limiter->reset($ip);
|
||||||
@@ -220,46 +220,6 @@ switch ($action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* not needed anymore?
|
|
||||||
if (fieldExist('premdays', 'accounts') && fieldExist('lastday', 'accounts')) {
|
|
||||||
$save = false;
|
|
||||||
$timeNow = time();
|
|
||||||
$premDays = $account->premdays;
|
|
||||||
$lastDay = $account->lastday;
|
|
||||||
$lastLogin = $lastDay;
|
|
||||||
|
|
||||||
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) {
|
|
||||||
$account->premdays = $premDays;
|
|
||||||
$account->lastday = $lastDay;
|
|
||||||
$account->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
$worlds = [$world];
|
$worlds = [$world];
|
||||||
$playdata = compact('worlds', 'characters');
|
$playdata = compact('worlds', 'characters');
|
||||||
|
|
||||||
@@ -268,7 +228,7 @@ switch ($action) {
|
|||||||
if (!fieldExist('istutorial', 'players')) {
|
if (!fieldExist('istutorial', 'players')) {
|
||||||
$sessionKey .= "\n";
|
$sessionKey .= "\n";
|
||||||
}
|
}
|
||||||
$sessionKey .= ($accountHasSecret && strlen($accountSecret) > 5) ? $inputToken : '';
|
$sessionKey .= ($twoFactorAuth->isActive() && strlen($account->{'2fa_secret'}) > 5) ? $account->{'2fa_secret'} : '';
|
||||||
|
|
||||||
// this is workaround to distinguish between TFS 1.x and otservbr
|
// this is workaround to distinguish between TFS 1.x and otservbr
|
||||||
// TFS 1.x requires the number in session key
|
// TFS 1.x requires the number in session key
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ require __DIR__ . '/system/libs/pot/OTS.php';
|
|||||||
$ots = POT::getInstance();
|
$ots = POT::getInstance();
|
||||||
|
|
||||||
require __DIR__ . '/system/libs/pot/InvitesDriver.php';
|
require __DIR__ . '/system/libs/pot/InvitesDriver.php';
|
||||||
require __DIR__ . '/system/libs/rfc6238.php';
|
|
||||||
require __DIR__ . '/common.php';
|
require __DIR__ . '/common.php';
|
||||||
|
|
||||||
const ACTION = '';
|
const ACTION = '';
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ $deprecatedConfig = [
|
|||||||
'genders',
|
'genders',
|
||||||
'template',
|
'template',
|
||||||
'template_allow_change',
|
'template_allow_change',
|
||||||
'vocations_amount',
|
|
||||||
'vocations',
|
|
||||||
'client',
|
'client',
|
||||||
'session_prefix',
|
'session_prefix',
|
||||||
'friendly_urls',
|
'friendly_urls',
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ use MyAAC\Models\Guild;
|
|||||||
use MyAAC\Models\House;
|
use MyAAC\Models\House;
|
||||||
use MyAAC\Models\Pages;
|
use MyAAC\Models\Pages;
|
||||||
use MyAAC\Models\Player;
|
use MyAAC\Models\Player;
|
||||||
|
use MyAAC\Models\PlayerDeath;
|
||||||
|
use MyAAC\Models\PlayerKillers;
|
||||||
use MyAAC\News;
|
use MyAAC\News;
|
||||||
use MyAAC\Plugins;
|
use MyAAC\Plugins;
|
||||||
use MyAAC\Settings;
|
use MyAAC\Settings;
|
||||||
@@ -515,7 +517,12 @@ function template_place_holder($type): string
|
|||||||
$ret .= $debugBarRenderer->renderHead();
|
$ret .= $debugBarRenderer->renderHead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elseif ($type === 'head_end') {
|
||||||
|
$ret .= setting('core.html_head');
|
||||||
|
}
|
||||||
elseif ($type === 'body_start') {
|
elseif ($type === 'body_start') {
|
||||||
|
$ret .= setting('core.html_body');
|
||||||
|
|
||||||
$ret .= $twig->render('browsehappy.html.twig');
|
$ret .= $twig->render('browsehappy.html.twig');
|
||||||
|
|
||||||
if (admin()) {
|
if (admin()) {
|
||||||
@@ -526,6 +533,8 @@ function template_place_holder($type): string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif($type === 'body_end') {
|
elseif($type === 'body_end') {
|
||||||
|
$ret .= setting('core.html_footer');
|
||||||
|
|
||||||
$ret .= template_ga_code();
|
$ret .= template_ga_code();
|
||||||
if (isset($debugBar)) {
|
if (isset($debugBar)) {
|
||||||
$ret .= $debugBarRenderer->render();
|
$ret .= $debugBarRenderer->render();
|
||||||
@@ -1132,11 +1141,44 @@ function csrfProtect(): void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTopPlayers($limit = 5, $skill = 'level') {
|
function getSkillIdByName(string $name): int|null
|
||||||
|
{
|
||||||
|
$skills = [
|
||||||
|
'level' => POT::SKILL_LEVEL,
|
||||||
|
'experience' => POT::SKILL_LEVEL,
|
||||||
|
|
||||||
|
'magic' => POT::SKILL_MAGIC,
|
||||||
|
'maglevel' => POT::SKILL_MAGIC,
|
||||||
|
|
||||||
|
'balance' => SKILL_BALANCE,
|
||||||
|
'frags' => SKILL_FRAGS,
|
||||||
|
|
||||||
|
'club' => POT::SKILL_CLUB,
|
||||||
|
'sword' => POT::SKILL_SWORD,
|
||||||
|
'axe' => POT::SKILL_AXE,
|
||||||
|
'dist' => POT::SKILL_DIST,
|
||||||
|
'distance' => POT::SKILL_DIST,
|
||||||
|
'shield' => POT::SKILL_SHIELD,
|
||||||
|
'shielding' => POT::SKILL_SHIELD,
|
||||||
|
'fish' => POT::SKILL_FISH,
|
||||||
|
'fishing' => POT::SKILL_FISH,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $skills[$name] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTopPlayers($limit = 5, $skill = POT::SKILL_LEVEL)
|
||||||
|
{
|
||||||
global $db;
|
global $db;
|
||||||
|
|
||||||
if ($skill === 'level') {
|
$skillOriginal = $skill;
|
||||||
$skill = 'experience';
|
|
||||||
|
if (is_string($skill)) {
|
||||||
|
$skill = getSkillIdByName($skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_numeric($skill)) {
|
||||||
|
throw new RuntimeException("getTopPlayers: Invalid skill: $skillOriginal");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Cache::remember("top_{$limit}_{$skill}", 2 * 60, function () use ($db, $limit, $skill) {
|
return Cache::remember("top_{$limit}_{$skill}", 2 * 60, function () use ($db, $limit, $skill) {
|
||||||
@@ -1157,15 +1199,64 @@ function getTopPlayers($limit = 5, $skill = 'level') {
|
|||||||
$columns[] = 'lookmount';
|
$columns[] = 'lookmount';
|
||||||
}
|
}
|
||||||
|
|
||||||
return Player::query()
|
$query = Player::query()
|
||||||
->select($columns)
|
->select($columns)
|
||||||
->withOnlineStatus()
|
->withOnlineStatus()
|
||||||
->notDeleted()
|
->notDeleted()
|
||||||
->where('group_id', '<', setting('core.highscores_groups_hidden'))
|
->where('group_id', '<', setting('core.highscores_groups_hidden'))
|
||||||
->whereNotIn('id', setting('core.highscores_ids_hidden'))
|
->whereNotIn('id', setting('core.highscores_ids_hidden'))
|
||||||
->where('account_id', '!=', 1)
|
->where('account_id', '!=', 1)
|
||||||
->orderByDesc($skill)
|
->orderByDesc('value');
|
||||||
->limit($limit)
|
|
||||||
|
if ($limit > 0) {
|
||||||
|
$query->limit($limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($skill >= POT::SKILL_FIRST && $skill <= POT::SKILL_LAST) { // skills
|
||||||
|
if ($db->hasColumn('players', 'skill_fist')) {// tfs 1.0
|
||||||
|
$skill_ids = array(
|
||||||
|
POT::SKILL_FIST => 'skill_fist',
|
||||||
|
POT::SKILL_CLUB => 'skill_club',
|
||||||
|
POT::SKILL_SWORD => 'skill_sword',
|
||||||
|
POT::SKILL_AXE => 'skill_axe',
|
||||||
|
POT::SKILL_DIST => 'skill_dist',
|
||||||
|
POT::SKILL_SHIELD => 'skill_shielding',
|
||||||
|
POT::SKILL_FISH => 'skill_fishing',
|
||||||
|
);
|
||||||
|
|
||||||
|
$query
|
||||||
|
->addSelect($skill_ids[$skill] . ' as value')
|
||||||
|
->orderByDesc($skill_ids[$skill] . '_tries');
|
||||||
|
} else {
|
||||||
|
$query
|
||||||
|
->join('player_skills', 'player_skills.player_id', '=', 'players.id')
|
||||||
|
->where('skillid', $skill)
|
||||||
|
->addSelect('player_skills.value as value');
|
||||||
|
}
|
||||||
|
} else if ($skill == SKILL_FRAGS) // frags
|
||||||
|
{
|
||||||
|
if ($db->hasTable('player_killers')) {
|
||||||
|
$query->addSelect(['value' => PlayerKillers::whereColumn('player_killers.player_id', 'players.id')->selectRaw('COUNT(*)')]);
|
||||||
|
} else {
|
||||||
|
$query->addSelect(['value' => PlayerDeath::unjustified()->whereColumn('player_deaths.killed_by', 'players.name')->selectRaw('COUNT(*)')]);
|
||||||
|
}
|
||||||
|
} else if ($skill == SKILL_BALANCE) // balance
|
||||||
|
{
|
||||||
|
$query
|
||||||
|
->addSelect('players.balance as value');
|
||||||
|
} else {
|
||||||
|
if ($skill == POT::SKILL_MAGIC) {
|
||||||
|
$query
|
||||||
|
->addSelect('players.maglevel as value', 'players.maglevel')
|
||||||
|
->orderByDesc('manaspent');
|
||||||
|
} else { // level
|
||||||
|
$query
|
||||||
|
->addSelect('players.level as value', 'players.experience')
|
||||||
|
->orderByDesc('experience');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query
|
||||||
->get()
|
->get()
|
||||||
->map(function ($e, $i) {
|
->map(function ($e, $i) {
|
||||||
$row = $e->toArray();
|
$row = $e->toArray();
|
||||||
@@ -1717,8 +1808,8 @@ function getAccountIdentityColumn(): string
|
|||||||
|
|
||||||
function isCanary(): bool
|
function isCanary(): bool
|
||||||
{
|
{
|
||||||
$vipSystemEnabled = configLua('vipSystemEnabled');
|
$dataPackDirectory = configLua('dataPackDirectory');
|
||||||
return isset($vipSystemEnabled);
|
return isset($dataPackDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatusUptimeReadable(int $uptime): string
|
function getStatusUptimeReadable(int $uptime): string
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use MyAAC\CsrfToken;
|
|||||||
use MyAAC\Hooks;
|
use MyAAC\Hooks;
|
||||||
use MyAAC\Plugins;
|
use MyAAC\Plugins;
|
||||||
use MyAAC\Models\Town;
|
use MyAAC\Models\Town;
|
||||||
|
use MyAAC\Server\XML\Vocations;
|
||||||
use MyAAC\Settings;
|
use MyAAC\Settings;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
@@ -214,3 +215,5 @@ if (count($towns) <= 0) {
|
|||||||
|
|
||||||
config(['towns', $towns]);
|
config(['towns', $towns]);
|
||||||
unset($towns);
|
unset($towns);
|
||||||
|
|
||||||
|
new Vocations();
|
||||||
|
|||||||
@@ -12,6 +12,9 @@
|
|||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use MyAAC\Models\Account as AccountModel;
|
||||||
|
use MyAAC\Models\AccountAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OTServ account abstraction.
|
* OTServ account abstraction.
|
||||||
*
|
*
|
||||||
@@ -40,7 +43,11 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
|
|||||||
*/
|
*/
|
||||||
private $data = array('email' => '', 'rlname' => '','location' => '', 'country' => '','web_flags' => 0, 'lastday' => 0, 'premdays' => 0, 'created' => 0);
|
private $data = array('email' => '', 'rlname' => '','location' => '', 'country' => '','web_flags' => 0, 'lastday' => 0, 'premdays' => 0, 'created' => 0);
|
||||||
|
|
||||||
public static $cache = array();
|
private array $columns = ['password', 'email', 'rlname', 'location', 'country', 'web_flags', 'created'];
|
||||||
|
|
||||||
|
private array $optionalColumns = ['name', 'number', 'lastday', 'premdays', 'premium_ends_at', 'premend'];
|
||||||
|
|
||||||
|
public static array $cache = [];
|
||||||
|
|
||||||
const GRATIS_PREMIUM_DAYS = 65535;
|
const GRATIS_PREMIUM_DAYS = 65535;
|
||||||
/**
|
/**
|
||||||
@@ -325,27 +332,50 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
|
|||||||
*/
|
*/
|
||||||
public function save()
|
public function save()
|
||||||
{
|
{
|
||||||
if( !isset($this->data['id']) )
|
if (!isset($this->data['id'])) {
|
||||||
{
|
|
||||||
throw new E_OTS_NotLoaded();
|
throw new E_OTS_NotLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
$field = 'lastday';
|
$defaultValues = [
|
||||||
if($this->db->hasColumn('accounts', 'premend')) { // othire
|
'premium_ends_at' => 0,
|
||||||
$field = 'premend';
|
'lastday' => 0,
|
||||||
if(!isset($this->data['premend'])) {
|
'premend' => 0,
|
||||||
$this->data['premend'] = 0;
|
'premdays' => 0,
|
||||||
}
|
];
|
||||||
}
|
|
||||||
else if($this->db->hasColumn('accounts', 'premium_ends_at')) {
|
|
||||||
$field = 'premium_ends_at';
|
|
||||||
if(!isset($this->data['premium_ends_at'])) {
|
|
||||||
$this->data['premium_ends_at'] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UPDATE query on database
|
foreach ($defaultValues as $key => $value) {
|
||||||
$this->db->exec('UPDATE `accounts` SET ' . ($this->db->hasColumn('accounts', 'name') ? '`name` = ' . $this->db->quote($this->data['name']) . ',' : '') . '`password` = ' . $this->db->quote($this->data['password']) . ', `email` = ' . $this->db->quote($this->data['email']) . ', `rlname` = ' . $this->db->quote($this->data['rlname']) . ', `location` = ' . $this->db->quote($this->data['location']) . ', `country` = ' . $this->db->quote($this->data['country']) . ', `web_flags` = ' . (int) $this->data['web_flags'] . ', ' . ($this->db->hasColumn('accounts', 'premdays') ? '`premdays` = ' . (int) $this->data['premdays'] . ',' : '') . '`' . $field . '` = ' . (int) $this->data[$field] . ' WHERE `id` = ' . $this->data['id']);
|
if (!isset($this->data[$key])) {
|
||||||
|
$this->data[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$columns = $this->columns;
|
||||||
|
foreach ($this->optionalColumns as $column) {
|
||||||
|
if ($this->db->hasColumn('accounts', $column)) {
|
||||||
|
$columns[] = $column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$values = [];
|
||||||
|
foreach ($columns as $column) {
|
||||||
|
$value = $this->data[$column];
|
||||||
|
|
||||||
|
$values[$column] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// updates existing player
|
||||||
|
if( isset($this->data['id']) ) {
|
||||||
|
AccountModel::where('id', $this->data['id'])->update($values);
|
||||||
|
}
|
||||||
|
// creates new player
|
||||||
|
else {
|
||||||
|
$values['created'] = time();
|
||||||
|
|
||||||
|
$account = AccountModel::create($values);
|
||||||
|
|
||||||
|
// ID of new player
|
||||||
|
$this->data['id'] = $account->id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -504,11 +534,17 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
|
|||||||
* @since 0.7.5
|
* @since 0.7.5
|
||||||
* @throws E_OTS_NotLoaded If account is not loaded.
|
* @throws E_OTS_NotLoaded If account is not loaded.
|
||||||
*/
|
*/
|
||||||
public function setPremDays($premdays)
|
public function setPremDays($premdays): void
|
||||||
{
|
{
|
||||||
$this->data['premdays'] = (int) $premdays;
|
$this->data['premdays'] = (int) $premdays;
|
||||||
$this->data['premend'] = time() + ($premdays * 24 * 60 * 60);
|
|
||||||
$this->data['premium_ends_at'] = time() + ($premdays * 24 * 60 * 60);
|
$premiumTimeInSeconds = time() + ($premdays * 24 * 60 * 60);
|
||||||
|
$this->data['premend'] = $premiumTimeInSeconds;
|
||||||
|
$this->data['premium_ends_at'] = $premiumTimeInSeconds;
|
||||||
|
|
||||||
|
if (isCanary()) {
|
||||||
|
$this->data['lastday'] = $premiumTimeInSeconds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRLName($name)
|
public function setRLName($name)
|
||||||
@@ -700,17 +736,11 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
|
|||||||
*/
|
*/
|
||||||
public function setCustomField($field, $value)
|
public function setCustomField($field, $value)
|
||||||
{
|
{
|
||||||
if( !isset($this->data['id']) )
|
if( !isset($this->data['id']) ) {
|
||||||
{
|
|
||||||
throw new E_OTS_NotLoaded();
|
throw new E_OTS_NotLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
// quotes value for SQL query
|
AccountModel::where('id', $this->data['id'])->update([$field => $value]);
|
||||||
if(!( is_int($value) || is_float($value) ))
|
|
||||||
{
|
|
||||||
$value = $this->db->quote($value);
|
|
||||||
}
|
|
||||||
$this->db->exec('UPDATE ' . $this->db->tableName('accounts') . ' SET ' . $this->db->fieldName($field) . ' = ' . $value . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1007,26 +1037,16 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
|
|||||||
|
|
||||||
public function logAction($action)
|
public function logAction($action)
|
||||||
{
|
{
|
||||||
$ip = get_browser_real_ip();
|
AccountAction::create([
|
||||||
if(!str_contains($ip, ":")) {
|
'account_id' => $this->getId(),
|
||||||
$ipv6 = '0';
|
'ip' => get_browser_real_ip(),
|
||||||
}
|
'date' => time(),
|
||||||
else {
|
'action' => $action,
|
||||||
$ipv6 = $ip;
|
]);
|
||||||
$ip = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->db->exec('INSERT INTO `' . TABLE_PREFIX . 'account_actions` (`account_id`, `ip`, `ipv6`, `date`, `action`) VALUES (' . $this->db->quote($this->getId()).', ' . ($ip == '' ? '0' : $this->db->quote(ip2long($ip))) . ', (' . ($ipv6 == '0' ? $this->db->quote('') : $this->db->quote(inet_pton($ipv6))) . '), UNIX_TIMESTAMP(NOW()), ' . $this->db->quote($action).')');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getActionsLog($limit1, $limit2)
|
public function getActionsLog($limit) {
|
||||||
{
|
return AccountAction::where('account_id', $this->data['id'])->orderByDesc('date')->limit($limit)->get()->toArray();
|
||||||
$actions = array();
|
|
||||||
|
|
||||||
foreach($this->db->query('SELECT `ip`, `ipv6`, `date`, `action` FROM `' . TABLE_PREFIX . 'account_actions` WHERE `account_id` = ' . $this->data['id'] . ' ORDER by `date` DESC LIMIT ' . $limit1 . ', ' . $limit2 . '')->fetchAll() as $a)
|
|
||||||
$actions[] = array('ip' => $a['ip'], 'ipv6' => $a['ipv6'], 'date' => $a['date'], 'action' => $a['action']);
|
|
||||||
|
|
||||||
return $actions;
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Returns players iterator.
|
* Returns players iterator.
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ use MyAAC\Cache\Cache;
|
|||||||
*/
|
*/
|
||||||
class OTS_DB_MySQL extends OTS_Base_DB
|
class OTS_DB_MySQL extends OTS_Base_DB
|
||||||
{
|
{
|
||||||
|
private bool $hasCacheChanged = false;
|
||||||
private array $has_table_cache = [];
|
private array $has_table_cache = [];
|
||||||
private array $has_column_cache = [];
|
private array $has_column_cache = [];
|
||||||
private array $get_column_info_cache = [];
|
private array $get_column_info_cache = [];
|
||||||
@@ -164,7 +165,7 @@ class OTS_DB_MySQL extends OTS_Base_DB
|
|||||||
$cache->delete('database_columns_info');
|
$cache->delete('database_columns_info');
|
||||||
$cache->delete('database_checksum');
|
$cache->delete('database_checksum');
|
||||||
}
|
}
|
||||||
else {
|
else if ($this->hasCacheChanged) {
|
||||||
$cache->set('database_tables', serialize($this->has_table_cache), 3600);
|
$cache->set('database_tables', serialize($this->has_table_cache), 3600);
|
||||||
$cache->set('database_columns', serialize($this->has_column_cache), 3600);
|
$cache->set('database_columns', serialize($this->has_column_cache), 3600);
|
||||||
$cache->set('database_columns_info', serialize($this->get_column_info_cache), 3600);
|
$cache->set('database_columns_info', serialize($this->get_column_info_cache), 3600);
|
||||||
@@ -228,6 +229,8 @@ class OTS_DB_MySQL extends OTS_Base_DB
|
|||||||
|
|
||||||
private function hasTableInternal($name): bool
|
private function hasTableInternal($name): bool
|
||||||
{
|
{
|
||||||
|
$this->hasCacheChanged = true;
|
||||||
|
|
||||||
return ($this->has_table_cache[$name] = $this->query('SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = ' . $this->quote(config('database_name')) . ' AND `TABLE_NAME` = ' . $this->quote($name) . ' LIMIT 1;')->rowCount() > 0);
|
return ($this->has_table_cache[$name] = $this->query('SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = ' . $this->quote(config('database_name')) . ' AND `TABLE_NAME` = ' . $this->quote($name) . ' LIMIT 1;')->rowCount() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,6 +244,8 @@ class OTS_DB_MySQL extends OTS_Base_DB
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function hasColumnInternal($table, $column): bool {
|
private function hasColumnInternal($table, $column): bool {
|
||||||
|
$this->hasCacheChanged = true;
|
||||||
|
|
||||||
return $this->hasTable($table) && ($this->has_column_cache[$table . '.' . $column] = count($this->query('SHOW COLUMNS FROM `' . $table . "` LIKE " . $this->quote($column))->fetchAll()) > 0);
|
return $this->hasTable($table) && ($this->has_column_cache[$table . '.' . $column] = count($this->query('SHOW COLUMNS FROM `' . $table . "` LIKE " . $this->quote($column))->fetchAll()) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,11 +277,14 @@ class OTS_DB_MySQL extends OTS_Base_DB
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->hasCacheChanged = true;
|
||||||
|
|
||||||
$formatResult = function ($result) {
|
$formatResult = function ($result) {
|
||||||
return [
|
return [
|
||||||
'field' => $result['Field'],
|
'field' => $result['Field'],
|
||||||
'type' => $result['Type'],
|
'type' => $result['Type'],
|
||||||
'null' => strtolower($result['Null']),
|
'null' => strtolower($result['Null']),
|
||||||
|
'key' => strtolower($result['Key'] ?? ''),
|
||||||
'default' => $result['Default'],
|
'default' => $result['Default'],
|
||||||
'extra' => $result['Extra'],
|
'extra' => $result['Extra'],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,20 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
$__load = array();
|
|
||||||
/*
|
use MyAAC\Models\Player as PlayerModel;
|
||||||
'loss_experience' => NULL,
|
|
||||||
'loss_items' => NULL,
|
|
||||||
'guild_info' => NULL,
|
|
||||||
'skull_type' => NULL,
|
|
||||||
'skull_time' => NULL,
|
|
||||||
'blessings' => NULL,
|
|
||||||
'direction' => NULL,
|
|
||||||
'stamina' => NULL,
|
|
||||||
'world_id' => NULL,
|
|
||||||
'online' => NULL,
|
|
||||||
'deletion' => NULL,
|
|
||||||
'promotion' => NULL,
|
|
||||||
'marriage' => NULL
|
|
||||||
);*/
|
|
||||||
|
|
||||||
/**#@+
|
/**#@+
|
||||||
* @version 0.0.1
|
* @version 0.0.1
|
||||||
@@ -109,6 +95,10 @@ class OTS_Player extends OTS_Row_DAO
|
|||||||
POT::SKILL_FISH => array('value' => 0, 'tries' => 0)
|
POT::SKILL_FISH => array('value' => 0, 'tries' => 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private array $columns = ['name', 'account_id', 'group_id', 'sex', 'vocation', 'experience', 'level', 'maglevel', 'health', 'healthmax', 'mana', 'manamax', 'manaspent', 'soul', 'lookbody', 'lookfeet', 'lookhead', 'looklegs', 'looktype', 'posx', 'posy', 'posz', 'lastlogin', 'lastlogout', 'lastip', 'town_id', 'balance', 'created', 'comment', 'hide'];
|
||||||
|
|
||||||
|
private array $optionalColumns = ['cap', 'skull', 'skull_type', 'skull_time', 'loss_experience', 'loss_mana', 'loss_skills', 'loss_items', 'loss_containers', 'guildnick', 'rank_id', 'promotion', 'direction', 'blessings', 'stamina', 'lookaddons', 'save', 'conditions', 'world_id', 'online', 'deletion', 'deleted', 'marriage'];
|
||||||
|
|
||||||
private static array $playersOnline;
|
private static array $playersOnline;
|
||||||
/**
|
/**
|
||||||
* Magic PHP5 method.
|
* Magic PHP5 method.
|
||||||
@@ -133,90 +123,14 @@ class OTS_Player extends OTS_Row_DAO
|
|||||||
*/
|
*/
|
||||||
public function load($id, $fields = null, $load_skills = true)
|
public function load($id, $fields = null, $load_skills = true)
|
||||||
{
|
{
|
||||||
global $__load;
|
$columns = $this->columns;
|
||||||
|
foreach ($this->optionalColumns as $column) {
|
||||||
if(!isset($__load['loss_experience']))
|
if ($this->db->hasColumn('players', $column)) {
|
||||||
{
|
$columns[] = $column;
|
||||||
$loss = '';
|
|
||||||
if($this->db->hasColumn('players', 'loss_experience')) {
|
|
||||||
$loss = ', `loss_experience`, `loss_mana`, `loss_skills`';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$__load['loss_experience'] = $loss;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isset($__load['loss_items']))
|
|
||||||
{
|
|
||||||
$loss_items = '';
|
|
||||||
if($this->db->hasColumn('players', 'loss_items')) {
|
|
||||||
$loss_items = ', `loss_items`, `loss_containers`';
|
|
||||||
}
|
|
||||||
|
|
||||||
$__load['loss_items'] = $loss_items;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isset($__load['guild_info']))
|
|
||||||
{
|
|
||||||
$guild_info = '';
|
|
||||||
if(!$this->db->hasTable('guild_members') && $this->db->hasColumn('players', 'guildnick')) {
|
|
||||||
$guild_info = ', `guildnick`, `rank_id`';
|
|
||||||
}
|
|
||||||
|
|
||||||
$__load['guild_info'] = $guild_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isset($__load['skull_type']))
|
|
||||||
{
|
|
||||||
$skull_type = 'skull';
|
|
||||||
if($this->db->hasColumn('players', 'skull_type')) {
|
|
||||||
$skull_type = 'skull_type';
|
|
||||||
}
|
|
||||||
|
|
||||||
$__load['skull_type'] = $skull_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isset($__load['skull_time']))
|
|
||||||
{
|
|
||||||
$skull_time = 'skulltime';
|
|
||||||
if($this->db->hasColumn('players', 'skull_time')) {
|
|
||||||
$skull_time = 'skull_time';
|
|
||||||
}
|
|
||||||
|
|
||||||
$__load['skull_time'] = $skull_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isset($__load['blessings'])) {
|
|
||||||
$__load['blessings'] = $this->db->hasColumn('players', 'blessings');
|
|
||||||
}
|
|
||||||
if(!isset($__load['direction'])) {
|
|
||||||
$__load['direction'] = $this->db->hasColumn('players', 'direction');
|
|
||||||
}
|
|
||||||
if(!isset($__load['stamina'])) {
|
|
||||||
$__load['stamina'] = $this->db->hasColumn('players', 'stamina');
|
|
||||||
}
|
|
||||||
if(!isset($__load['world_id'])) {
|
|
||||||
$__load['world_id'] = $this->db->hasColumn('players', 'world_id');
|
|
||||||
}
|
|
||||||
if(!isset($__load['online'])) {
|
|
||||||
$__load['online'] = $this->db->hasColumn('players', 'online');
|
|
||||||
}
|
|
||||||
if(!isset($__load['deletion'])) {
|
|
||||||
$__load['deletion'] = $this->db->hasColumn('players', 'deletion');
|
|
||||||
}
|
|
||||||
if(!isset($__load['promotion'])) {
|
|
||||||
$__load['promotion'] = $this->db->hasColumn('players', 'promotion');
|
|
||||||
}
|
|
||||||
if(!isset($__load['marriage'])) {
|
|
||||||
$__load['marriage'] = $this->db->hasColumn('players', 'marriage');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($fields)) { // load only what we wish
|
if(isset($fields)) { // load only what we wish
|
||||||
if(in_array('promotion', $fields)) {
|
|
||||||
if(!$this->db->hasColumn('players', 'promotion')) {
|
|
||||||
unset($fields[array_search('promotion', $fields)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(in_array('deleted', $fields)) {
|
if(in_array('deleted', $fields)) {
|
||||||
if($this->db->hasColumn('players', 'deletion')) {
|
if($this->db->hasColumn('players', 'deletion')) {
|
||||||
unset($fields[array_search('deleted', $fields)]);
|
unset($fields[array_search('deleted', $fields)]);
|
||||||
@@ -224,21 +138,21 @@ class OTS_Player extends OTS_Row_DAO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(in_array('online', $fields)) {
|
$columns = [];
|
||||||
if(!$this->db->hasColumn('players', 'online')) {
|
foreach ($fields as $field) {
|
||||||
unset($fields[array_search('online', $fields)]);
|
if ($this->db->hasColumn('players', $field)) {
|
||||||
|
$columns[] = $field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->data = $this->db->query('SELECT ' . implode(', ', $fields) . ' FROM `players` WHERE `id` = ' . (int)$id)->fetch();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// SELECT query on database
|
|
||||||
$this->data = $this->db->query('SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`' . ($this->db->hasColumn('players', 'lookaddons') ? ', `lookaddons`' : '') . ', `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `save`, `conditions`, `' . $__load['skull_time'] . '` as `skulltime`, `' . $__load['skull_type'] . '` as `skull`' . $__load['guild_info'] . ', `town_id`' . $__load['loss_experience'] . $__load['loss_items'] . ', `balance`' . ($__load['blessings'] ? ', `blessings`' : '') . ($__load['direction'] ? ', `direction`' : '') . ($__load['stamina'] ? ', `stamina`' : '') . ($__load['world_id'] ? ', `world_id`' : '') . ($__load['online'] ? ', `online`' : '') . ', `' . ($__load['deletion'] ? 'deletion' : 'deleted') . '`' . ($__load['promotion'] ? ', `promotion`' : '') . ($__load['marriage'] ? ', `marriage`' : '') . ', `comment`, `created`, `hide` FROM `players` WHERE `id` = ' . (int)$id)->fetch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
array_unshift($columns, 'id');
|
||||||
|
|
||||||
|
$query = PlayerModel::where('id', $id)->first($columns);
|
||||||
|
$this->data = $query ? $query->toArray() : [];
|
||||||
|
|
||||||
// loads skills
|
// loads skills
|
||||||
if( $this->isLoaded() && $load_skills)
|
if( $this->isLoaded() && $load_skills) {
|
||||||
{
|
|
||||||
if($this->db->hasColumn('players', 'skill_fist')) {
|
if($this->db->hasColumn('players', 'skill_fist')) {
|
||||||
|
|
||||||
$skill_ids = array(
|
$skill_ids = array(
|
||||||
@@ -318,153 +232,65 @@ class OTS_Player extends OTS_Row_DAO
|
|||||||
*/
|
*/
|
||||||
public function save()
|
public function save()
|
||||||
{
|
{
|
||||||
$skull_type = 'skull';
|
$defaultValues = [
|
||||||
if($this->db->hasColumn('players', 'skull_type')) {
|
'cap' => 0,
|
||||||
$skull_type = 'skull_type';
|
'skull' => 0,
|
||||||
|
'skull_type' => 0,
|
||||||
|
'skull_time' => 0,
|
||||||
|
'loss_experience' => 100,
|
||||||
|
'loss_mana' => 100,
|
||||||
|
'loss_skills' => 100,
|
||||||
|
'loss_items' => 100,
|
||||||
|
'loss_containers' => 100,
|
||||||
|
'guildnick' => '',
|
||||||
|
'rank_id' => 0,
|
||||||
|
'promotion' => 0,
|
||||||
|
'direction' => 0,
|
||||||
|
'blessings' => 0,
|
||||||
|
'stamina' => 0,
|
||||||
|
'lookaddons' => 0,
|
||||||
|
'save' => 1,
|
||||||
|
'conditions' => '',
|
||||||
|
'town_id' => 1,
|
||||||
|
'world_id' => 1,
|
||||||
|
'online' => 0,
|
||||||
|
'deletion' => 0,
|
||||||
|
'deleted' => 0,
|
||||||
|
'marriage' => 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($defaultValues as $key => $value) {
|
||||||
|
if (!isset($this->data[$key])) {
|
||||||
|
$this->data[$key] = $value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$skull_time = 'skulltime';
|
$columns = $this->columns;
|
||||||
if($this->db->hasColumn('players', 'skull_time')) {
|
foreach ($this->optionalColumns as $column) {
|
||||||
$skull_time = 'skull_time';
|
if ($this->db->hasColumn('players', $column)) {
|
||||||
|
$columns[] = $column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isset($this->data['loss_experience']))
|
$values = [];
|
||||||
$this->data['loss_experience'] = 100;
|
foreach ($columns as $column) {
|
||||||
|
$value = $this->data[$column];
|
||||||
|
|
||||||
if(!isset($this->data['loss_mana']))
|
$values[$column] = $value;
|
||||||
$this->data['loss_mana'] = 100;
|
}
|
||||||
|
|
||||||
if(!isset($this->data['loss_skills']))
|
|
||||||
$this->data['loss_skills'] = 100;
|
|
||||||
|
|
||||||
if(!isset($this->data['loss_items']))
|
|
||||||
$this->data['loss_items'] = 10;
|
|
||||||
|
|
||||||
if(!isset($this->data['loss_containers']))
|
|
||||||
$this->data['loss_containers'] = 100;
|
|
||||||
|
|
||||||
if(!isset($this->data['guildnick']))
|
|
||||||
$this->data['guildnick'] = '';
|
|
||||||
|
|
||||||
if(!isset($this->data['rank_id']))
|
|
||||||
$this->data['rank_id'] = 0;
|
|
||||||
|
|
||||||
if(!isset($this->data['promotion']))
|
|
||||||
$this->data['promotion'] = 0;
|
|
||||||
|
|
||||||
if(!isset($this->data['direction']))
|
|
||||||
$this->data['direction'] = 0;
|
|
||||||
|
|
||||||
if(!isset($this->data['conditions']))
|
|
||||||
$this->data['conditions'] = '';
|
|
||||||
|
|
||||||
if(!isset($this->data['town_id']))
|
|
||||||
$this->data['town_id'] = 1;
|
|
||||||
|
|
||||||
// updates existing player
|
// updates existing player
|
||||||
if( isset($this->data['id']) )
|
if( isset($this->data['id']) ) {
|
||||||
{
|
PlayerModel::where('id', $this->data['id'])->update($values);
|
||||||
$loss = '';
|
|
||||||
if($this->db->hasColumn('players', 'loss_experience')) {
|
|
||||||
$loss = ', `loss_experience` = ' . $this->data['loss_experience'] . ', `loss_mana` = ' . $this->data['loss_mana'] . ', `loss_skills` = ' . $this->data['loss_skills'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$loss_items = '';
|
|
||||||
if($this->db->hasColumn('players', 'loss_items')) {
|
|
||||||
$loss_items = ', `loss_items` = ' . $this->data['loss_items'] . ', `loss_containers` = ' . $this->data['loss_containers'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$guild_info = '';
|
|
||||||
if(!$this->db->hasTable('guild_members') && $this->db->hasColumn('players', 'guildnick')) {
|
|
||||||
$guild_info = ', `guildnick` = ' . $this->db->quote($this->data['guildnick']) . ', ' . $this->db->fieldName('rank_id') . ' = ' . $this->data['rank_id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$direction = '';
|
|
||||||
if($this->db->hasColumn('players', 'direction')) {
|
|
||||||
$direction = ', `direction` = ' . $this->db->quote($this->data['direction']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$blessings = '';
|
|
||||||
if($this->db->hasColumn('players', 'blessings')) {
|
|
||||||
$blessings = ', `blessings` = ' . $this->db->quote($this->data['blessings']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$stamina = '';
|
|
||||||
if($this->db->hasColumn('players', 'stamina')) {
|
|
||||||
$stamina = ', `stamina` = ' . $this->db->quote($this->data['stamina']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$lookaddons = '';
|
|
||||||
if($this->db->hasColumn('players', 'lookaddons')) {
|
|
||||||
$lookaddons = ', `lookaddons` = ' . $this->db->quote($this->data['lookaddons']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// UPDATE query on database
|
|
||||||
$this->db->query('UPDATE ' . $this->db->tableName('players') . ' SET ' . $this->db->fieldName('name') . ' = ' . $this->db->quote($this->data['name']) . ', ' . $this->db->fieldName('account_id') . ' = ' . $this->data['account_id'] . ', ' . $this->db->fieldName('group_id') . ' = ' . $this->data['group_id'] . ', ' . $this->db->fieldName('sex') . ' = ' . $this->data['sex'] . ', ' . $this->db->fieldName('vocation') . ' = ' . $this->data['vocation'] . ', ' . $this->db->fieldName('experience') . ' = ' . $this->data['experience'] . ', ' . $this->db->fieldName('level') . ' = ' . $this->data['level'] . ', ' . $this->db->fieldName('maglevel') . ' = ' . $this->data['maglevel'] . ', ' . $this->db->fieldName('health') . ' = ' . $this->data['health'] . ', ' . $this->db->fieldName('healthmax') . ' = ' . $this->data['healthmax'] . ', ' . $this->db->fieldName('mana') . ' = ' . $this->data['mana'] . ', ' . $this->db->fieldName('manamax') . ' = ' . $this->data['manamax'] . ', ' . $this->db->fieldName('manaspent') . ' = ' . $this->data['manaspent'] . ', ' . $this->db->fieldName('soul') . ' = ' . $this->data['soul'] . ', ' . $this->db->fieldName('lookbody') . ' = ' . $this->data['lookbody'] . ', ' . $this->db->fieldName('lookfeet') . ' = ' . $this->data['lookfeet'] . ', ' . $this->db->fieldName('lookhead') . ' = ' . $this->data['lookhead'] . ', ' . $this->db->fieldName('looklegs') . ' = ' . $this->data['looklegs'] . ', ' . $this->db->fieldName('looktype') . ' = ' . $this->data['looktype'] . $lookaddons . ', ' . $this->db->fieldName('posx') . ' = ' . $this->data['posx'] . ', ' . $this->db->fieldName('posy') . ' = ' . $this->data['posy'] . ', ' . $this->db->fieldName('posz') . ' = ' . $this->data['posz'] . ', ' . $this->db->fieldName('cap') . ' = ' . $this->data['cap'] . ', ' . $this->db->fieldName('lastlogin') . ' = ' . $this->data['lastlogin'] . ', ' . $this->db->fieldName('lastlogout') . ' = ' . $this->data['lastlogout'] . ', ' . $this->db->fieldName('lastip') . ' = ' . $this->db->quote($this->data['lastip']) . ', ' . $this->db->fieldName('save') . ' = ' . (int) $this->data['save'] . ', ' . $this->db->fieldName('conditions') . ' = ' . $this->db->quote($this->data['conditions']) . ', `' . $skull_time . '` = ' . $this->data['skulltime'] . ', `' . $skull_type . '` = ' . (int) $this->data['skull'] . $guild_info . ', ' . $this->db->fieldName('town_id') . ' = ' . $this->data['town_id'] . $loss . $loss_items . ', ' . $this->db->fieldName('balance') . ' = ' . $this->data['balance'] . $blessings . $stamina . $direction . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id']);
|
|
||||||
}
|
}
|
||||||
// creates new player
|
// creates new player
|
||||||
else
|
else {
|
||||||
{
|
$values['created'] = time();
|
||||||
$loss = '';
|
|
||||||
$loss_data = '';
|
|
||||||
if($this->db->hasColumn('players', 'loss_experience')) {
|
|
||||||
$loss = ', `loss_experience`, `loss_mana`, `loss_skills`';
|
|
||||||
$loss_data = ', ' . $this->data['loss_experience'] . ', ' . $this->data['loss_mana'] . ', ' . $this->data['loss_skills'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$loss_items = '';
|
$player = PlayerModel::create($values);
|
||||||
$loss_items_data = '';
|
|
||||||
if($this->db->hasColumn('players', 'loss_items')) {
|
|
||||||
$loss_items = ', `loss_items`, `loss_containers`';
|
|
||||||
$loss_items_data = ', ' . $this->data['loss_items'] . ', ' . $this->data['loss_containers'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$guild_info = '';
|
// ID of new player
|
||||||
$guild_info_data = '';
|
$this->data['id'] = $player->id;
|
||||||
if(!$this->db->hasTable('guild_members') && $this->db->hasColumn('players', 'guildnick')) {
|
|
||||||
$guild_info = ', `guildnick`, `rank_id`';
|
|
||||||
$guild_info_data = ', ' . $this->db->quote($this->data['guildnick']) . ', ' . $this->data['rank_id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$promotion = '';
|
|
||||||
$promotion_data = '';
|
|
||||||
if($this->db->hasColumn('players', 'promotion')) {
|
|
||||||
$promotion = ', `promotion`';
|
|
||||||
$promotion_data = ', ' . $this->data['promotion'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$direction = '';
|
|
||||||
$direction_data = '';
|
|
||||||
if($this->db->hasColumn('players', 'direction')) {
|
|
||||||
$direction = ', `direction`';
|
|
||||||
$direction_data = ', ' . $this->data['direction'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$blessings = '';
|
|
||||||
$blessings_data = '';
|
|
||||||
if($this->db->hasColumn('players', 'blessings')) {
|
|
||||||
$blessings = ', `blessings`';
|
|
||||||
$blessings_data = ', ' . $this->data['blessings'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$stamina = '';
|
|
||||||
$stamina_data = '';
|
|
||||||
if($this->db->hasColumn('players', 'stamina')) {
|
|
||||||
$stamina = ', `stamina`';
|
|
||||||
$stamina_data = ', ' . $this->data['stamina'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$lookaddons = '';
|
|
||||||
$lookaddons_data = '';
|
|
||||||
if($this->db->hasColumn('players', 'lookaddons')) {
|
|
||||||
$lookaddons = ', `lookaddons`';
|
|
||||||
$lookaddons_data = ', ' . $this->data['lookaddons'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// INSERT query on database
|
|
||||||
$this->db->query('INSERT INTO `players` (`name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`' . $lookaddons . ', `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `save`, `conditions`, `' . $skull_time . '`, `' . $skull_type . '`' . $guild_info . ', `town_id`' . $loss . $loss_items . ', `balance`' . $blessings . $stamina . $direction . ', `created`' . $promotion . ', `comment`) VALUES (' . $this->db->quote($this->data['name']) . ', ' . $this->data['account_id'] . ', ' . $this->data['group_id'] . ', ' . $this->data['sex'] . ', ' . $this->data['vocation'] . ', ' . $this->data['experience'] . ', ' . $this->data['level'] . ', ' . $this->data['maglevel'] . ', ' . $this->data['health'] . ', ' . $this->data['healthmax'] . ', ' . $this->data['mana'] . ', ' . $this->data['manamax'] . ', ' . $this->data['manaspent'] . ', ' . $this->data['soul'] . ', ' . $this->data['lookbody'] . ', ' . $this->data['lookfeet'] . ', ' . $this->data['lookhead'] . ', ' . $this->data['looklegs'] . ', ' . $this->data['looktype'] . $lookaddons_data . ', ' . $this->data['posx'] . ', ' . $this->data['posy'] . ', ' . $this->data['posz'] . ', ' . $this->data['cap'] . ', ' . $this->data['lastlogin'] . ', ' . $this->data['lastlogout'] . ', ' . $this->data['lastip'] . ', ' . (int) $this->data['save'] . ', ' . $this->db->quote($this->data['conditions']) . ', ' . $this->data['skulltime'] . ', ' . (int) $this->data['skull'] . $guild_info_data . ', ' . $this->data['town_id'] . $loss_data . $loss_items_data . ', ' . $this->data['balance'] . $blessings_data . $stamina_data . $direction_data . ', ' . time() . $promotion_data . ', "")');
|
|
||||||
// ID of new group
|
|
||||||
$this->data['id'] = $this->db->lastInsertId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updates skills - doesn't matter if we have just created character - trigger inserts new skills
|
// updates skills - doesn't matter if we have just created character - trigger inserts new skills
|
||||||
@@ -490,7 +316,7 @@ class OTS_Player extends OTS_Row_DAO
|
|||||||
$set .= ',';
|
$set .= ',';
|
||||||
}
|
}
|
||||||
|
|
||||||
$skills = $this->db->query('UPDATE `players` SET ' . $set . ' WHERE `id` = ' . $this->data['id']);
|
$this->db->query('UPDATE `players` SET ' . $set . ' WHERE `id` = ' . $this->data['id']);
|
||||||
}
|
}
|
||||||
else if($this->db->hasTable('player_skills')) {
|
else if($this->db->hasTable('player_skills')) {
|
||||||
foreach($this->skills as $id => $skill)
|
foreach($this->skills as $id => $skill)
|
||||||
@@ -748,21 +574,25 @@ class OTS_Player extends OTS_Row_DAO
|
|||||||
|
|
||||||
public function isDeleted()
|
public function isDeleted()
|
||||||
{
|
{
|
||||||
$field = 'deleted';
|
$column = 'deleted';
|
||||||
if($this->db->hasColumn('players', 'deletion'))
|
if($this->db->hasColumn('players', 'deletion'))
|
||||||
$field = 'deletion';
|
$column = 'deletion';
|
||||||
|
|
||||||
if( !isset($this->data[$field]) )
|
if( !isset($this->data[$column]) )
|
||||||
{
|
{
|
||||||
throw new E_OTS_NotLoaded();
|
throw new E_OTS_NotLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->data[$field] > 0;
|
return $this->data[$column] > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setDeleted($deleted)
|
public function setDeleted($deleted)
|
||||||
{
|
{
|
||||||
$this->data['deleted'] = (int) $deleted;
|
$column = 'deleted';
|
||||||
|
if($this->db->hasColumn('players', 'deletion'))
|
||||||
|
$column = 'deletion';
|
||||||
|
|
||||||
|
$this->data[$column] = (int) $deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isOnline()
|
public function isOnline()
|
||||||
@@ -852,13 +682,7 @@ class OTS_Player extends OTS_Row_DAO
|
|||||||
throw new E_OTS_NotLoaded();
|
throw new E_OTS_NotLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($this->data['promotion'])) {
|
return \OTS_Toolbox::getVocationFromPromotion($this->data['vocation'], $this->data['promotion'] ?? 0);
|
||||||
global $config;
|
|
||||||
if((int)$this->data['promotion'] > 0)
|
|
||||||
return ($this->data['vocation'] + ($this->data['promotion'] * $config['vocations_amount']));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->data['vocation'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1574,12 +1398,7 @@ class OTS_Player extends OTS_Row_DAO
|
|||||||
*/
|
*/
|
||||||
public function getCap()
|
public function getCap()
|
||||||
{
|
{
|
||||||
if( !isset($this->data['cap']) )
|
return $this->data['cap'] ?? 0;
|
||||||
{
|
|
||||||
throw new E_OTS_NotLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->data['cap'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1792,12 +1611,12 @@ class OTS_Player extends OTS_Row_DAO
|
|||||||
*/
|
*/
|
||||||
public function getSkullTime()
|
public function getSkullTime()
|
||||||
{
|
{
|
||||||
if( !isset($this->data['skulltime']) )
|
$column = 'skulltime';
|
||||||
{
|
if($this->db->hasColumn('players', 'skull_time')) {
|
||||||
throw new E_OTS_NotLoaded();
|
$column = 'skull_time';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->data['skulltime'];
|
return $this->data[$column] ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1811,7 +1630,12 @@ class OTS_Player extends OTS_Row_DAO
|
|||||||
*/
|
*/
|
||||||
public function setSkullTime($skulltime)
|
public function setSkullTime($skulltime)
|
||||||
{
|
{
|
||||||
$this->data['skulltime'] = (int) $skulltime;
|
$column = 'skulltime';
|
||||||
|
if($this->db->hasColumn('players', 'skull_time')) {
|
||||||
|
$column = 'skull_time';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->data[$column] = (int) $skulltime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3250,6 +3074,10 @@ class OTS_Player extends OTS_Row_DAO
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setData(array $data): void{
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Magic PHP5 method.
|
* Magic PHP5 method.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3
|
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use MyAAC\Server\XML\Vocations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toolbox for common operations.
|
* Toolbox for common operations.
|
||||||
*
|
*
|
||||||
@@ -110,14 +112,21 @@ class OTS_Toolbox
|
|||||||
$list->setFilter($filter);
|
$list->setFilter($filter);
|
||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
public static function getVocationFromPromotion($id, $promotion = 0): int
|
||||||
public static function getVocationName($id, $promotion = 0): string
|
|
||||||
{
|
{
|
||||||
if($promotion > 0) {
|
if($promotion > 0) {
|
||||||
$id = ($id + ($promotion * config('vocations_amount')));
|
for ($i = 0; $i < $promotion; $i++) {
|
||||||
|
if ($_id = Vocations::getPromoted($id)) {
|
||||||
|
$id = $_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return config('vocations')[$id] ?? 'Unknown';
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getVocationName($id, $promotion = 0): string {
|
||||||
|
return config('vocations')[self::getVocationFromPromotion($id, $promotion)] ?? 'Unknown';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,284 +0,0 @@
|
|||||||
<?php
|
|
||||||
/** https://github.com/Voronenko/PHPOTP/blob/08cda9cb9c30b7242cf0b3a9100a6244a2874927/code/base32static.php
|
|
||||||
* Encode in Base32 based on RFC 4648.
|
|
||||||
* Requires 20% more space than base64
|
|
||||||
* Great for case-insensitive filesystems like Windows and URL's (except for = char which can be excluded using the pad option for urls)
|
|
||||||
*
|
|
||||||
* @package default
|
|
||||||
* @author Bryan Ruiz
|
|
||||||
**/
|
|
||||||
class Base32Static {
|
|
||||||
|
|
||||||
private static $map = array(
|
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7
|
|
||||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
|
|
||||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
|
|
||||||
'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
|
|
||||||
'=' // padding character
|
|
||||||
);
|
|
||||||
|
|
||||||
private static $flippedMap = array(
|
|
||||||
'A'=>'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 "<br/>SecretKey: $secretkey <br/>";
|
|
||||||
|
|
||||||
$key = base32static::decode($secretkey);
|
|
||||||
print "Key(base 32 decode): $key <br/>";
|
|
||||||
|
|
||||||
$unixtimestamp = time()/30;
|
|
||||||
print "UnixTimeStamp (time()/30): $unixtimestamp <br/>";
|
|
||||||
|
|
||||||
for($i=-($rangein30s); $i<=$rangein30s; $i++) {
|
|
||||||
$checktime = (int)($unixtimestamp+$i);
|
|
||||||
print "Calculating oath_hotp from (int)(unixtimestamp +- 30sec offset): $checktime basing on secret key<br/>";
|
|
||||||
|
|
||||||
$thiskey = self::oath_hotp($key, $checktime, true);
|
|
||||||
print "======================================================<br/>";
|
|
||||||
print "CheckTime: $checktime oath_hotp:".$thiskey."<br/>";
|
|
||||||
|
|
||||||
$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<br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
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 <br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
$counter = $counter >> 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($debug) {
|
|
||||||
foreach ($cur_counter as $char) {
|
|
||||||
print ord($char) . " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
print "<br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
$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.<br/>";
|
|
||||||
print "Calculate sha1 HMAC(Hash-based Message Authentication Code http://en.wikipedia.org/wiki/HMAC).<br/>";
|
|
||||||
print "hash_hmac ('sha1', $binary, $key)<br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = hash_hmac ('sha1', $binary, $key);
|
|
||||||
|
|
||||||
if ($debug) {
|
|
||||||
print "Result: $result <br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function oath_truncate($hash, $length = 6, $debug=false) {
|
|
||||||
$result="";
|
|
||||||
|
|
||||||
// Convert to dec
|
|
||||||
if($debug) {
|
|
||||||
print "converting hex hash into characters<br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
$hashcharacters = str_split($hash,2);
|
|
||||||
|
|
||||||
if($debug) {
|
|
||||||
print_r($hashcharacters);
|
|
||||||
print "<br/>and convert to decimals:<br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($j=0; $j<count($hashcharacters); $j++) {
|
|
||||||
$hmac_result[]=hexdec($hashcharacters[$j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if($debug) {
|
|
||||||
print_r($hmac_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://php.net/manual/ru/function.hash-hmac.php
|
|
||||||
// adopted from brent at thebrent dot net 21-May-2009 08:17 comment
|
|
||||||
|
|
||||||
$offset = $hmac_result[19] & 0xf;
|
|
||||||
|
|
||||||
if($debug) {
|
|
||||||
print "Calculating offset as 19th element of hmac:".$hmac_result[19]."<br/>";
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use MyAAC\Models\Player as PlayerModel;
|
||||||
use MyAAC\Settings;
|
use MyAAC\Settings;
|
||||||
|
|
||||||
function updateHighscoresIdsHidden(): void
|
function updateHighscoresIdsHidden(): void
|
||||||
@@ -10,12 +11,22 @@ function updateHighscoresIdsHidden(): void
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = $db->query("SELECT `id` FROM `players` WHERE (`name` = " . $db->quote("Rook Sample") . " OR `name` = " . $db->quote("Sorcerer Sample") . " OR `name` = " . $db->quote("Druid Sample") . " OR `name` = " . $db->quote("Paladin Sample") . " OR `name` = " . $db->quote("Knight Sample") . " OR `name` = " . $db->quote("Account Manager") . ") ORDER BY `id`;");
|
$players = PlayerModel::where('name', 'Rook Sample')
|
||||||
|
->orWhere('name', 'Sorcerer Sample')
|
||||||
|
->orWhere('name', 'Druid Sample')
|
||||||
|
->orWhere('name', 'Paladin Sample')
|
||||||
|
->orWhere('name', 'Knight Sample')
|
||||||
|
->orWhere('name', 'Monk Sample')
|
||||||
|
->orWhere('name', 'Account Manager')
|
||||||
|
->orderBy('id')
|
||||||
|
->select('id')
|
||||||
|
->get();
|
||||||
|
|
||||||
$highscores_ignored_ids = array();
|
$highscores_ignored_ids = [];
|
||||||
if ($query->rowCount() > 0) {
|
if (count($players) > 0) {
|
||||||
foreach ($query->fetchAll() as $result)
|
foreach ($players as $result) {
|
||||||
$highscores_ignored_ids[] = $result['id'];
|
$highscores_ignored_ids[] = $result->id;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$highscores_ignored_ids[] = 0;
|
$highscores_ignored_ids[] = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
42
system/migrations/47.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @var OTS_DB_MySQL $db
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 2025-02-27
|
||||||
|
// remove ipv6, change to ip (for both ipv4 + ipv6) as VARCHAR(45)
|
||||||
|
$up = function () use ($db) {
|
||||||
|
$accountActionsInfo = $db->getColumnInfo(TABLE_PREFIX . 'account_actions', 'account_id');
|
||||||
|
if ($accountActionsInfo && is_array($accountActionsInfo) && $accountActionsInfo['key'] == 'pri') {
|
||||||
|
$db->query("ALTER TABLE `myaac_account_actions` DROP KEY `account_id`;");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$db->hasColumn(TABLE_PREFIX . 'account_actions', 'id')) {
|
||||||
|
$db->addColumn(TABLE_PREFIX . 'account_actions', 'id', 'INT NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`id`)');
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->modifyColumn(TABLE_PREFIX . 'account_actions', 'ip', "VARCHAR(45) NOT NULL DEFAULT ''");
|
||||||
|
$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ip` = INET_NTOA(`ip`) WHERE `ip` != '0';");
|
||||||
|
$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ip` = INET6_NTOA(`ipv6`) WHERE `ip` = '0';");
|
||||||
|
|
||||||
|
if ($db->hasColumn(TABLE_PREFIX . 'account_actions', 'ipv6')) {
|
||||||
|
$db->dropColumn(TABLE_PREFIX . 'account_actions', 'ipv6');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$down = function () use ($db) {
|
||||||
|
if ($db->hasColumn(TABLE_PREFIX . 'account_actions', 'id')) {
|
||||||
|
$db->query("ALTER TABLE `" . TABLE_PREFIX . "account_actions` DROP `id`;");
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->query("ALTER TABLE `" . TABLE_PREFIX . "account_actions` ADD KEY (`account_id`);");
|
||||||
|
|
||||||
|
if (!$db->hasColumn(TABLE_PREFIX . 'account_actions', 'ipv6')) {
|
||||||
|
$db->addColumn(TABLE_PREFIX . 'account_actions', 'ipv6', "BINARY(16) NOT NULL DEFAULT 0x00000000000000000000000000000000 AFTER ip");
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ipv6` = INET6_ATON(ip) WHERE NOT IS_IPV4(`ip`);");
|
||||||
|
$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ip` = INET_ATON(`ip`) WHERE IS_IPV4(`ip`);");
|
||||||
|
$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ip` = 0 WHERE `ipv6` != 0x00000000000000000000000000000000;");
|
||||||
|
$db->modifyColumn(TABLE_PREFIX . 'account_actions', 'ip', "INT(11) UNSIGNED NOT NULL DEFAULT 0;");
|
||||||
|
};
|
||||||
16
system/migrations/48.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @var OTS_DB_MySQL $db
|
||||||
|
*/
|
||||||
|
|
||||||
|
$up = function () use ($db) {
|
||||||
|
if (!$db->hasColumn(TABLE_PREFIX . 'menu', 'access')) {
|
||||||
|
$db->addColumn(TABLE_PREFIX . 'menu', 'access', 'TINYINT NOT NULL DEFAULT 0 AFTER `link`');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$down = function () use ($db) {
|
||||||
|
if ($db->hasColumn(TABLE_PREFIX . 'menu', 'access')) {
|
||||||
|
$db->dropColumn(TABLE_PREFIX . 'menu', 'access');
|
||||||
|
}
|
||||||
|
};
|
||||||
91
system/migrations/49.php
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @var OTS_DB_MySQL $db
|
||||||
|
*/
|
||||||
|
|
||||||
|
use MyAAC\Models\Account as AccountModel;
|
||||||
|
|
||||||
|
$time = time();
|
||||||
|
|
||||||
|
$accountId = getSession('account') ?? 1;
|
||||||
|
if (!defined('MYAAC_INSTALL')) {
|
||||||
|
$accountModel = AccountModel::where('web_flags', 3)->first();
|
||||||
|
if ($accountModel) {
|
||||||
|
$accountId = $accountModel->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function insert_sample_if_not_exist($p): void
|
||||||
|
{
|
||||||
|
global $time, $accountId;
|
||||||
|
|
||||||
|
$player = new OTS_Player();
|
||||||
|
$player->find($p['name']);
|
||||||
|
|
||||||
|
if (!$player->isLoaded()) {
|
||||||
|
|
||||||
|
$player->setData([
|
||||||
|
'name' => $p['name'],
|
||||||
|
'group_id' => 1,
|
||||||
|
'account_id' => $accountId,
|
||||||
|
'level' => $p['level'],
|
||||||
|
'vocation' => $p['vocation_id'],
|
||||||
|
'health' => $p['health'],
|
||||||
|
'healthmax' => $p['healthmax'],
|
||||||
|
'experience' => $p['experience'],
|
||||||
|
'lookbody' => 118,
|
||||||
|
'lookfeet' => 114,
|
||||||
|
'lookhead' => 38,
|
||||||
|
'looklegs' => 57,
|
||||||
|
'looktype' => $p['looktype'],
|
||||||
|
'maglevel' => 0,
|
||||||
|
'mana' => $p['mana'],
|
||||||
|
'manamax' => $p['manamax'],
|
||||||
|
'manaspent' => 0,
|
||||||
|
'soul' => $p['soul'],
|
||||||
|
'town_id' => 1,
|
||||||
|
'posx' => 1000,
|
||||||
|
'posy' => 1000,
|
||||||
|
'posz' => 7,
|
||||||
|
'conditions' => '',
|
||||||
|
'cap' => $p['cap'],
|
||||||
|
'sex' => 1,
|
||||||
|
'lastlogin' => $time,
|
||||||
|
'lastip' => 2130706433,
|
||||||
|
'save' => 1,
|
||||||
|
'lastlogout' => $time,
|
||||||
|
'balance' => 0,
|
||||||
|
'created' => $time,
|
||||||
|
'hide' => 1,
|
||||||
|
'comment' => '',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$player->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$up = function () use ($db) {
|
||||||
|
if (!$db->hasTable('players')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
insert_sample_if_not_exist(['name' => 'Rook Sample', 'level' => 1, 'vocation_id' => 0, 'health' => 150, 'healthmax' => 150, 'experience' => 0, 'looktype' => 130, 'mana' => 0, 'manamax' => 0, 'soul' => 100, 'cap' => 400]);
|
||||||
|
insert_sample_if_not_exist(['name' => 'Sorcerer Sample', 'level' => 8, 'vocation_id' => 1, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470]);
|
||||||
|
insert_sample_if_not_exist(['name' => 'Druid Sample', 'level' => 8, 'vocation_id' => 2, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470]);
|
||||||
|
insert_sample_if_not_exist(['name' => 'Paladin Sample', 'level' => 8, 'vocation_id' => 3, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 129, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470]);
|
||||||
|
insert_sample_if_not_exist(['name' => 'Knight Sample', 'level' => 8, 'vocation_id' => 4, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 131, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470]);
|
||||||
|
insert_sample_if_not_exist(['name' => 'Monk Sample', 'level' => 8, 'vocation_id' => 9, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 128, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470]);
|
||||||
|
|
||||||
|
if (defined('MYAAC_INSTALL')) {
|
||||||
|
global $locale;
|
||||||
|
|
||||||
|
success($locale['step_database_imported_players']);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/20.php';
|
||||||
|
updateHighscoresIdsHidden();
|
||||||
|
};
|
||||||
|
|
||||||
|
$down = function () {
|
||||||
|
// nothing
|
||||||
|
};
|
||||||
11
system/migrations/50-gallery.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `myaac_gallery`
|
||||||
|
(
|
||||||
|
`id` int NOT NULL AUTO_INCREMENT,
|
||||||
|
`comment` varchar(255) NOT NULL DEFAULT '',
|
||||||
|
`image` varchar(255) NOT NULL,
|
||||||
|
`thumb` varchar(255) NOT NULL,
|
||||||
|
`author` varchar(50) NOT NULL DEFAULT '',
|
||||||
|
`ordering` int NOT NULL DEFAULT 0,
|
||||||
|
`hide` tinyint NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
||||||
16
system/migrations/50.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @var OTS_DB_MySQL $db
|
||||||
|
*/
|
||||||
|
|
||||||
|
$up = function () use ($db) {
|
||||||
|
if ($db->hasTable(TABLE_PREFIX . 'gallery')) {
|
||||||
|
$db->dropTable(TABLE_PREFIX . 'gallery');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$down = function () use ($db) {
|
||||||
|
if (!$db->hasTable(TABLE_PREFIX . 'gallery')) {
|
||||||
|
$db->query(file_get_contents(__DIR__ . '/50-gallery.sql'));
|
||||||
|
}
|
||||||
|
};
|
||||||
8
system/migrations/51-account_email_codes.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
CREATE TABLE `myaac_account_email_codes`
|
||||||
|
(
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`account_id` int NOT NULL,
|
||||||
|
`code` varchar(6) NOT NULL,
|
||||||
|
`created_at` int NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
|
||||||
36
system/migrations/51.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
// 2fa
|
||||||
|
// add the myaac_account_email_codes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var OTS_DB_MySQL $db
|
||||||
|
*/
|
||||||
|
|
||||||
|
$up = function () use ($db) {
|
||||||
|
if (!$db->hasColumn('accounts', '2fa_type')) {
|
||||||
|
$db->addColumn('accounts', '2fa_type', "tinyint NOT NULL DEFAULT 0 AFTER `web_flags`");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$db->hasColumn('accounts', '2fa_secret')) {
|
||||||
|
$db->addColumn('accounts', '2fa_secret', "varchar(16) NOT NULL DEFAULT '' AFTER `2fa_type`");
|
||||||
|
}
|
||||||
|
|
||||||
|
// add myaac_account_email_codes table
|
||||||
|
if (!$db->hasTable(TABLE_PREFIX . 'account_email_codes')) {
|
||||||
|
$db->exec(file_get_contents(__DIR__ . '/51-account_email_codes.sql'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$down = function () use ($db) {
|
||||||
|
if ($db->hasColumn('accounts', '2fa_type')) {
|
||||||
|
$db->dropColumn('accounts', '2fa_type');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($db->hasColumn('accounts', '2fa_secret')) {
|
||||||
|
$db->dropColumn('accounts', '2fa_secret');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($db->hasTable(TABLE_PREFIX . 'account_email_codes')) {
|
||||||
|
$db->dropTable(TABLE_PREFIX . 'account_email_codes');
|
||||||
|
}
|
||||||
|
};
|
||||||
29
system/pages/#examples/top-5.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Example of using getTopPlayers() function
|
||||||
|
* to display the best players for each skill
|
||||||
|
*/
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
$skills = [
|
||||||
|
'magic', 'level',
|
||||||
|
'balance', 'frags',
|
||||||
|
POT::SKILL_FIST, POT::SKILL_CLUB,
|
||||||
|
POT::SKILL_SWORD, POT::SKILL_AXE,
|
||||||
|
POT::SKILL_DISTANCE, POT::SKILL_SHIELD,
|
||||||
|
POT::SKILL_FISH
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($skills as $skill) {?>
|
||||||
|
<ul>
|
||||||
|
<?php
|
||||||
|
echo '<strong>' . ucwords(is_string($skill) ? $skill : getSkillName($skill)) . '</strong>';
|
||||||
|
foreach (getTopPlayers(5, $skill) as $player) {?>
|
||||||
|
<li><?= $player['rank'] . '. ' . $player['name'] . ' - ' . $player['value']; ?></li>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</ul>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
26
system/pages/account/2fa/app/disable.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
require __DIR__ . '/../base.php';
|
||||||
|
|
||||||
|
if (!isRequestMethod('post')) {
|
||||||
|
error('This page cannot be accessed directly.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$account_logged->isLoaded()) {
|
||||||
|
error('Account not found!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$twoFactorAuth->isActive($twoFactorAuth::TYPE_APP)) {
|
||||||
|
error("Your account does not have Two Factor App Authentication enabled.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$twoFactorAuth->disable();
|
||||||
|
|
||||||
|
$twig->display('success.html.twig', [
|
||||||
|
'title' => 'Disabled',
|
||||||
|
'description' => 'Two Factor App Authentication has been disabled.'
|
||||||
|
]);
|
||||||
105
system/pages/account/2fa/app/enable.php
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
|
require __DIR__ . '/../base.php';
|
||||||
|
|
||||||
|
if ($twoFactorAuth->isActive()) {
|
||||||
|
$errors[] = 'Two-factor authentication is already enabled on your account.';
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$explodeRecoveryKey = explode('-', $account_logged->getCustomField('key'));
|
||||||
|
$newRecoveryKeyFormat = (count($explodeRecoveryKey) == 4);
|
||||||
|
|
||||||
|
if (ACTION == 'request') {
|
||||||
|
|
||||||
|
if ($newRecoveryKeyFormat) {
|
||||||
|
$key = $_POST['key1'] . '-' . $_POST['key2'] . '-' . $_POST['key3'] . '-' . $_POST['key4'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$key = $_POST['key'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$accountKey = $account_logged->getCustomField('key');
|
||||||
|
if (!empty($key) && $key == $accountKey) {
|
||||||
|
$secret = getSession('2fa_secret');
|
||||||
|
if ($secret === null) {
|
||||||
|
$secret = generateRandom2faSecret();
|
||||||
|
setSession('2fa_secret', $secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twoFactorAuth->appDisplayEnable($secret);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (empty($key)) {
|
||||||
|
$errors[] = 'Please enter the recovery key!';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Invalid recovery key!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ACTION == 'link') {
|
||||||
|
$secret = getSession('2fa_secret');
|
||||||
|
|
||||||
|
if ($secret === null) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => ['Secret not set. Go back and try again.']]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$authCode = $_POST['auth-code'] ?? '';
|
||||||
|
if (!empty($authCode)) {
|
||||||
|
$otp = $twoFactorAuth->appInitTOTP($secret);
|
||||||
|
|
||||||
|
if (!$otp->verify($authCode)) {
|
||||||
|
$errors = ['Token is invalid!'];
|
||||||
|
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
|
||||||
|
$twoFactorAuth->appDisplayEnable($secret, $otp, $errors);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($db->hasColumn('accounts', 'secret')) {
|
||||||
|
$account_logged->setCustomField('secret', $secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
$account_logged->setCustomField('2fa_secret', $secret);
|
||||||
|
$twoFactorAuth->enable(TwoFactorAuth::TYPE_APP);
|
||||||
|
|
||||||
|
$twig->display('success.html.twig',
|
||||||
|
[
|
||||||
|
'title' => 'Authenticator App Connected',
|
||||||
|
'description' => 'You successfully connected your Tibia account to an authenticator app.'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors = ['You have to enter the code generated by the authenticator!'];
|
||||||
|
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
$twoFactorAuth->appDisplayEnable($secret, null, $errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account/2fa/app/enable.warning.html.twig',
|
||||||
|
[
|
||||||
|
'newRecoveryKeyFormat' => $newRecoveryKeyFormat,
|
||||||
|
'errors' => $errors,
|
||||||
|
]
|
||||||
|
);
|
||||||
41
system/pages/account/2fa/base.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
|
csrfProtect();
|
||||||
|
|
||||||
|
$title = 'Two Factor Authentication';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var OTS_Account $account_logged
|
||||||
|
*/
|
||||||
|
$code = $_REQUEST['auth-code'] ?? '';
|
||||||
|
|
||||||
|
if (!$account_logged->isLoaded()) {
|
||||||
|
$current_session = getSession('account');
|
||||||
|
if($current_session) {
|
||||||
|
$account_logged = new OTS_Account();
|
||||||
|
$account_logged->load($current_session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$twoFactorAuth = TwoFactorAuth::getInstance($account_logged);
|
||||||
|
$twig->addGlobal('account_logged', $account_logged);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Took from ZnoteAAC
|
||||||
|
* @author Znote
|
||||||
|
*/
|
||||||
|
function generateRandom2faSecret($length = 16): string
|
||||||
|
{
|
||||||
|
$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
||||||
|
$charactersLength = strlen($characters);
|
||||||
|
$randomString = '';
|
||||||
|
|
||||||
|
for ($i = 0; $i < $length; $i++) {
|
||||||
|
$randomString .= $characters[rand(0, $charactersLength - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $randomString;
|
||||||
|
}
|
||||||
34
system/pages/account/2fa/email/disable.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
require __DIR__ . '/../base.php';
|
||||||
|
|
||||||
|
if ((!setting('core.mail_enabled'))) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => ['Account Two-Factor E-Mail Authentication disabled.']]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isRequestMethod('post')) {
|
||||||
|
error('This page cannot be accessed directly.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$account_logged->isLoaded()) {
|
||||||
|
error('Account not found!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$twoFactorAuth->isActive($twoFactorAuth::TYPE_EMAIL)) {
|
||||||
|
error("Your account does not have Two Factor E-Mail Authentication enabled.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$twoFactorAuth->disable();
|
||||||
|
$twoFactorAuth->deleteOldCodes();
|
||||||
|
|
||||||
|
$twig->display('success.html.twig',
|
||||||
|
[
|
||||||
|
'title' => 'Email Code Authentication Disabled',
|
||||||
|
'description' => 'You have successfully <strong>disabled</strong> the <b>Email Code Authentication</b> for your account.'
|
||||||
|
]
|
||||||
|
);
|
||||||
51
system/pages/account/2fa/email/enable.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
require __DIR__ . '/../base.php';
|
||||||
|
|
||||||
|
if ((!setting('core.mail_enabled'))) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => ['Account Two-Factor E-Mail Authentication disabled.']]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($twoFactorAuth->isActive()) {
|
||||||
|
$errors[] = 'Two-factor authentication is already enabled on your account.';
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$twoFactorAuth->hasRecentEmailCode(15 * 60)) {
|
||||||
|
$twoFactorAuth->resendEmailCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['save'])) {
|
||||||
|
if (!empty($code)) {
|
||||||
|
$twoFactorAuth->setAuthGateway(TwoFactorAuth::TYPE_EMAIL);
|
||||||
|
if ($twoFactorAuth->getAuthGateway()->verifyCode($code)) {
|
||||||
|
$serverName = configLua('serverName');
|
||||||
|
|
||||||
|
$twoFactorAuth->enable(TwoFactorAuth::TYPE_EMAIL);
|
||||||
|
$twoFactorAuth->deleteOldCodes();
|
||||||
|
|
||||||
|
$twig->display('success.html.twig', [
|
||||||
|
'title' => 'Email Code Authentication Activated',
|
||||||
|
'description' => sprintf('You have successfully activated <b>email code authentication</b> for your account. This means an <b>email code</b> will be sent to the email address assigned to your account whenever you try to log in to the %s client or the %s website. In order to log in, you will need to enter the <b>most recent email code</b> you have received.', $serverName, $serverName)
|
||||||
|
]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Invalid email code!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account/2fa/email/enable.html.twig', ['wrongCode' => count($errors) > 0]);
|
||||||
32
system/pages/account/2fa/email/resend-code.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
require __DIR__ . '/../base.php';
|
||||||
|
|
||||||
|
if ((!setting('core.mail_enabled'))) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => ['Account Two-Factor E-Mail Authentication disabled.']]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$account_logged->isLoaded()) {
|
||||||
|
error('Account not found!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($twoFactorAuth->isActive($twoFactorAuth::TYPE_APP)) {
|
||||||
|
error('You have to disable the app auth first!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($twoFactorAuth->hasRecentEmailCode(30 * 60)) {
|
||||||
|
$errors = ['Sorry, one email per 30 minutes'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$twoFactorAuth->resendEmailCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account/2fa/email/enable.html.twig');
|
||||||
@@ -17,6 +17,10 @@ if(!$logged)
|
|||||||
if(!empty($errors))
|
if(!empty($errors))
|
||||||
$twig->display('error_box.html.twig', array('errors' => $errors));
|
$twig->display('error_box.html.twig', array('errors' => $errors));
|
||||||
|
|
||||||
|
if (defined('HIDE_LOGIN_BOX') && HIDE_LOGIN_BOX) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$twig->display('account.login.html.twig', array(
|
$twig->display('account.login.html.twig', array(
|
||||||
'redirect' => $_REQUEST['redirect'] ?? null,
|
'redirect' => $_REQUEST['redirect'] ?? null,
|
||||||
'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number',
|
'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number',
|
||||||
@@ -30,3 +34,11 @@ if(!$logged)
|
|||||||
else {
|
else {
|
||||||
$show_form = true;
|
$show_form = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateRecoveryKey(): string
|
||||||
|
{
|
||||||
|
return generateRandomString(5, false, true, true) . '-' .
|
||||||
|
generateRandomString(5, false, true, true) . '-' .
|
||||||
|
generateRandomString(5, false, true, true) . '-' .
|
||||||
|
generateRandomString(5, false, true, true);
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use MyAAC\CreateCharacter;
|
use MyAAC\CreateCharacter;
|
||||||
|
use MyAAC\Models\AccountAction;
|
||||||
use MyAAC\Models\AccountEmailVerify;
|
use MyAAC\Models\AccountEmailVerify;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
@@ -44,6 +45,16 @@ $errors = array();
|
|||||||
$save = isset($_POST['save']) && $_POST['save'] == 1;
|
$save = isset($_POST['save']) && $_POST['save'] == 1;
|
||||||
if($save)
|
if($save)
|
||||||
{
|
{
|
||||||
|
$cooldown = setting('core.account_create_ip_block_cooldown');;
|
||||||
|
if ($cooldown > 0) {
|
||||||
|
$accountAction = AccountAction::where('ip', get_browser_real_ip())->where('action', 'Account created.')->where('date', '>=', time() - ($cooldown * 60))->first();
|
||||||
|
|
||||||
|
if ($accountAction) {
|
||||||
|
$minute = ($cooldown > 1 ? 'minutes' : 'minute');
|
||||||
|
$errors['account'] = "You have to wait $cooldown $minute before creating another account.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(!config('account_login_by_email')) {
|
if(!config('account_login_by_email')) {
|
||||||
if(USE_ACCOUNT_NAME) {
|
if(USE_ACCOUNT_NAME) {
|
||||||
$account_name = $_POST['account'];
|
$account_name = $_POST['account'];
|
||||||
@@ -140,7 +151,7 @@ if($save)
|
|||||||
'country' => $country,
|
'country' => $country,
|
||||||
'password' => $password,
|
'password' => $password,
|
||||||
'password_confirm' => $password_confirm,
|
'password_confirm' => $password_confirm,
|
||||||
'accept_rules' => isset($_POST['accept_rules']) ? $_POST['accept_rules'] === 'true' : false,
|
'accept_rules' => isset($_POST['accept_rules']) && $_POST['accept_rules'] === 'true',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!config('account_login_by_email')) {
|
if (!config('account_login_by_email')) {
|
||||||
@@ -192,6 +203,21 @@ if($save)
|
|||||||
|
|
||||||
$new_account->setPassword(encrypt($password));
|
$new_account->setPassword(encrypt($password));
|
||||||
$new_account->setEMail($email);
|
$new_account->setEMail($email);
|
||||||
|
|
||||||
|
$settingAccountPremiumDays = setting('core.account_premium_days');
|
||||||
|
if($settingAccountPremiumDays && $settingAccountPremiumDays > 0) {
|
||||||
|
$new_account->setPremDays($settingAccountPremiumDays);
|
||||||
|
|
||||||
|
if (!isCanary()) {
|
||||||
|
$lastDay = 0;
|
||||||
|
if($settingAccountPremiumDays != 0 && $settingAccountPremiumDays != OTS_Account::GRATIS_PREMIUM_DAYS) {
|
||||||
|
$lastDay = time();
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_account->setLastLogin($lastDay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$new_account->save();
|
$new_account->save();
|
||||||
|
|
||||||
$hooks->trigger(HOOK_ACCOUNT_CREATE_AFTER_SAVED, ['account' => $new_account]);
|
$hooks->trigger(HOOK_ACCOUNT_CREATE_AFTER_SAVED, ['account' => $new_account]);
|
||||||
@@ -206,22 +232,6 @@ if($save)
|
|||||||
$new_account->setCustomField('country', $country);
|
$new_account->setCustomField('country', $country);
|
||||||
}
|
}
|
||||||
|
|
||||||
$settingAccountPremiumDays = setting('core.account_premium_days');
|
|
||||||
if($settingAccountPremiumDays && $settingAccountPremiumDays > 0) {
|
|
||||||
if($db->hasColumn('accounts', 'premend')) { // othire
|
|
||||||
$new_account->setCustomField('premend', time() + $settingAccountPremiumDays * 86400);
|
|
||||||
}
|
|
||||||
else { // rest
|
|
||||||
if ($db->hasColumn('accounts', 'premium_ends_at')) { // TFS 1.4+
|
|
||||||
$new_account->setCustomField('premium_ends_at', time() + $settingAccountPremiumDays * (60 * 60 * 24));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$new_account->setCustomField('premdays', $settingAccountPremiumDays);
|
|
||||||
$new_account->setCustomField('lastday', time());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$accountDefaultPremiumPoints = setting('core.account_premium_points');
|
$accountDefaultPremiumPoints = setting('core.account_premium_points');
|
||||||
if($accountDefaultPremiumPoints > 0) {
|
if($accountDefaultPremiumPoints > 0) {
|
||||||
$new_account->setCustomField('premium_points', $accountDefaultPremiumPoints);
|
$new_account->setCustomField('premium_points', $accountDefaultPremiumPoints);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use MyAAC\RateLimit;
|
use MyAAC\RateLimit;
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
@@ -52,8 +53,18 @@ if(!empty($login_account) && !empty($login_password))
|
|||||||
$errors[] = 'Your account is not verified. Please verify your email address. If the message is not coming check the SPAM folder in your E-Mail client.<br/>' .
|
$errors[] = 'Your account is not verified. Please verify your email address. If the message is not coming check the SPAM folder in your E-Mail client.<br/>' .
|
||||||
'You can resend the Email here: <a href="' . $link . '">' . $link . '</a>';
|
'You can resend the Email here: <a href="' . $link . '">' . $link . '</a>';
|
||||||
} else {
|
} else {
|
||||||
session_regenerate_id();
|
|
||||||
setSession('account', $account_logged->getId());
|
setSession('account', $account_logged->getId());
|
||||||
|
|
||||||
|
if (!$hooks->trigger(HOOK_ACCOUNT_LOGIN_PRE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$twoFactorAuth = TwoFactorAuth::getInstance($account_logged);
|
||||||
|
if (!$twoFactorAuth->process($login_account, $login_password, $remember_me, $_POST['auth-code'] ?? '')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
session_regenerate_id();
|
||||||
setSession('password', encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password));
|
setSession('password', encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password));
|
||||||
if($remember_me) {
|
if($remember_me) {
|
||||||
setSession('remember_me', true);
|
setSession('remember_me', true);
|
||||||
|
|||||||
@@ -9,540 +9,11 @@
|
|||||||
* @link https://my-aac.org
|
* @link https://my-aac.org
|
||||||
*/
|
*/
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
$title = 'Lost Account Interface';
|
$title = 'Lost Account';
|
||||||
|
|
||||||
if(!setting('core.mail_enabled'))
|
if(!setting('core.mail_enabled')) {
|
||||||
{
|
echo "<b>Account maker is not configured to send e-mails, you can't use Lost Account Interface. Contact with admin to get help.</b>";
|
||||||
echo '<b>Account maker is not configured to send e-mails, you can\'t use Lost Account Interface. Contact with admin to get help.</b>';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$action_type = isset($_REQUEST['action_type']) ? $_REQUEST['action_type'] : '';
|
$twig->display('account/lost/form.html.twig');
|
||||||
if($action == '')
|
|
||||||
{
|
|
||||||
$twig->display('account.lost.form.html.twig');
|
|
||||||
}
|
|
||||||
else if($action == 'step1' && $action_type == '') {
|
|
||||||
$twig->display('account.lost.noaction.html.twig');
|
|
||||||
}
|
|
||||||
elseif($action == 'step1' && $action_type == 'email')
|
|
||||||
{
|
|
||||||
$nick = stripslashes($_REQUEST['nick']);
|
|
||||||
if(Validator::characterName($nick))
|
|
||||||
{
|
|
||||||
$player = new OTS_Player();
|
|
||||||
$account = new OTS_Account();
|
|
||||||
$player->find($nick);
|
|
||||||
if($player->isLoaded())
|
|
||||||
$account = $player->getAccount();
|
|
||||||
|
|
||||||
if($account->isLoaded())
|
|
||||||
{
|
|
||||||
if($account->getCustomField('email_next') < time())
|
|
||||||
echo 'Please enter e-mail to account with this character.<BR>
|
|
||||||
<form action="' . getLink('account/lost') . '?action=sendcode" method=post>
|
|
||||||
<input type=hidden name="character">
|
|
||||||
<table cellspacing=1 cellpadding=4 border=0 width=100%>
|
|
||||||
<TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Please enter e-mail to account</B></TD></TR>
|
|
||||||
<TR><TD BGCOLOR="'.$config['darkborder'].'">
|
|
||||||
Character: <INPUT TYPE=text NAME="nick" VALUE="'.$nick.'" SIZE="40" readonly="readonly"><BR>
|
|
||||||
E-mail to account:<INPUT TYPE=text NAME="email" VALUE="" SIZE="40"><BR>
|
|
||||||
</TD></TR>
|
|
||||||
</TABLE>
|
|
||||||
<BR>
|
|
||||||
<TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
' . $twig->render('buttons.submit.html.twig') . '</div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$insec = (int)$account->getCustomField('email_next') - time();
|
|
||||||
$minutesleft = floor($insec / 60);
|
|
||||||
$secondsleft = $insec - ($minutesleft * 60);
|
|
||||||
$timeleft = $minutesleft.' minutes '.$secondsleft.' seconds';
|
|
||||||
echo 'Account of selected character (<b>'.$nick.'</b>) received e-mail in last '.ceil(setting('core.mail_lost_account_interval') / 60).' minutes. You must wait '.$timeleft.' before you can use Lost Account Interface again.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Player or account of player <b>' . $nick . '</b> doesn\'t exist.';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Invalid player name format. If you have other characters on account try with other name.';
|
|
||||||
echo '<BR /><TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
<a href="' . getLink('account/lost') . '" border="0"><IMG SRC="'.$template_path.'/images/global/buttons/sbutton_back.gif" NAME="Back" ALT="Back" BORDER=0 WIDTH=120 HEIGHT=18></a></div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
}
|
|
||||||
elseif($action == 'sendcode')
|
|
||||||
{
|
|
||||||
$email = $_REQUEST['email'];
|
|
||||||
$nick = stripslashes($_REQUEST['nick']);
|
|
||||||
if(Validator::characterName($nick))
|
|
||||||
{
|
|
||||||
$player = new OTS_Player();
|
|
||||||
$account = new OTS_Account();
|
|
||||||
$player->find($nick);
|
|
||||||
if($player->isLoaded())
|
|
||||||
$account = $player->getAccount();
|
|
||||||
|
|
||||||
if($account->isLoaded())
|
|
||||||
{
|
|
||||||
if($account->getCustomField('email_next') < time())
|
|
||||||
{
|
|
||||||
if($account->getEMail() == $email)
|
|
||||||
{
|
|
||||||
$newcode = generateRandomString(30, true, false, true);
|
|
||||||
$mailBody = '
|
|
||||||
You asked to reset your ' . $config['lua']['serverName'] . ' password.<br/>
|
|
||||||
<p>Account name: '.$account->getName().'</p>
|
|
||||||
<br />
|
|
||||||
To do so, please click this link:
|
|
||||||
<p><a href="' . getLink('account/lost') . '?action=checkcode&code='.$newcode.'&character='.urlencode($nick).'">' . getLink('account/lost') . '?action=checkcode&code='.$newcode.'&character='.urlencode($nick).'</a></p>
|
|
||||||
<p>or open page: <i>' . getLink('account/lost') . '?action=checkcode</i> and in field "code" write <b>'.$newcode.'</b></p>
|
|
||||||
<br/>
|
|
||||||
<p>If you did not request a password change, you may ignore this message and your password will remain unchanged.';
|
|
||||||
|
|
||||||
$account_mail = $account->getCustomField('email');
|
|
||||||
if(_mail($account_mail, $config['lua']['serverName'].' - Recover your account', $mailBody))
|
|
||||||
{
|
|
||||||
$account->setCustomField('email_code', $newcode);
|
|
||||||
$account->setCustomField('email_next', (time() + setting('core.mail_lost_account_interval')));
|
|
||||||
echo '<br />Details about steps required to recover your account has been sent to <b>' . $account_mail . '</b>. You should receive this email within 15 minutes. Please check your inbox/spam directory.';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$account->setCustomField('email_next', (time() + 60));
|
|
||||||
echo '<br /><p class="error">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</p>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Invalid e-mail to account of character <b>'.$nick.'</b>. Try again.';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$insec = (int)$account->getCustomField('email_next') - time();
|
|
||||||
$minutesleft = floor($insec / 60);
|
|
||||||
$secondsleft = $insec - ($minutesleft * 60);
|
|
||||||
$timeleft = $minutesleft.' minutes '.$secondsleft.' seconds';
|
|
||||||
echo 'Account of selected character (<b>'.$nick.'</b>) received e-mail in last '.ceil(setting('core.mail_lost_account_interval') / 60).' minutes. You must wait '.$timeleft.' before you can use Lost Account Interface again.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Player or account of player <b>'.$nick.'</b> doesn\'t exist.';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Invalid player name format. If you have other characters on account try with other name.';
|
|
||||||
echo '<BR /><TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
<a href="' . getLink('account/lost') . '?action=step1&action_type=email&nick='.urlencode($nick).'" border="0"><IMG SRC="'.$template_path.'/images/global/buttons/sbutton_back.gif" NAME="Back" ALT="Back" BORDER=0 WIDTH=120 HEIGHT=18></a></div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
}
|
|
||||||
elseif($action == 'step1' && $action_type == 'reckey')
|
|
||||||
{
|
|
||||||
$nick = stripslashes($_REQUEST['nick']);
|
|
||||||
if(Validator::characterName($nick))
|
|
||||||
{
|
|
||||||
$player = new OTS_Player();
|
|
||||||
$account = new OTS_Account();
|
|
||||||
$player->find($nick);
|
|
||||||
if($player->isLoaded())
|
|
||||||
$account = $player->getAccount();
|
|
||||||
if($account->isLoaded())
|
|
||||||
{
|
|
||||||
$account_key = $account->getCustomField('key');
|
|
||||||
if(!empty($account_key))
|
|
||||||
{
|
|
||||||
echo 'If you enter right recovery key you will see form to set new e-mail and password to account. To this e-mail will be send your new password and account name.<BR>
|
|
||||||
<FORM ACTION="' . getLink('account/lost') . '?action=step2" METHOD=post>
|
|
||||||
<TABLE CELLSPACING=1 CELLPADDING=4 BORDER=0 WIDTH=100%>
|
|
||||||
<TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Please enter your recovery key</B></TD></TR>
|
|
||||||
<TR><TD BGCOLOR="'.$config['darkborder'].'">
|
|
||||||
Character name: <INPUT TYPE=text NAME="nick" VALUE="'.$nick.'" SIZE="40" readonly="readonly"><BR />
|
|
||||||
Recovery key: <INPUT TYPE=text NAME="key" VALUE="" SIZE="40"><BR>
|
|
||||||
</TD></TR>
|
|
||||||
</TABLE>
|
|
||||||
<BR>
|
|
||||||
<TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
' . $twig->render('buttons.submit.html.twig') . '</div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Account of this character has no recovery key!';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Player or account of player <b>'.$nick.'</b> doesn\'t exist.';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Invalid player name format. If you have other characters on account try with other name.';
|
|
||||||
echo '<BR /><TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
<a href="' . getLink('account/lost') . '" border="0"><IMG SRC="'.$template_path.'/images/global/buttons/sbutton_back.gif" NAME="Back" ALT="Back" BORDER=0 WIDTH=120 HEIGHT=18></a></div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
}
|
|
||||||
elseif($action == 'step2')
|
|
||||||
{
|
|
||||||
$rec_key = trim($_REQUEST['key']);
|
|
||||||
$nick = stripslashes($_REQUEST['nick']);
|
|
||||||
if(Validator::characterName($nick))
|
|
||||||
{
|
|
||||||
$player = new OTS_Player();
|
|
||||||
$account = new OTS_Account();
|
|
||||||
$player->find($nick);
|
|
||||||
if($player->isLoaded())
|
|
||||||
$account = $player->getAccount();
|
|
||||||
if($account->isLoaded())
|
|
||||||
{
|
|
||||||
$account_key = $account->getCustomField('key');
|
|
||||||
if(!empty($account_key))
|
|
||||||
{
|
|
||||||
if($account_key == $rec_key)
|
|
||||||
{
|
|
||||||
echo '<script type="text/javascript">
|
|
||||||
function validate_required(field,alerttxt)
|
|
||||||
{
|
|
||||||
with (field)
|
|
||||||
{
|
|
||||||
if (value==null||value==""||value==" ")
|
|
||||||
{alert(alerttxt);return false;}
|
|
||||||
else {return true}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function validate_email(field,alerttxt)
|
|
||||||
{
|
|
||||||
with (field)
|
|
||||||
{
|
|
||||||
apos=value.indexOf("@");
|
|
||||||
dotpos=value.lastIndexOf(".");
|
|
||||||
if (apos<1||dotpos-apos<2)
|
|
||||||
{alert(alerttxt);return false;}
|
|
||||||
else {return true;}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function validate_form(thisform)
|
|
||||||
{
|
|
||||||
with (thisform)
|
|
||||||
{
|
|
||||||
if (validate_required(email,"Please enter your e-mail!")==false)
|
|
||||||
{email.focus();return false;}
|
|
||||||
if (validate_email(email,"Invalid e-mail format!")==false)
|
|
||||||
{email.focus();return false;}
|
|
||||||
if (validate_required(passor,"Please enter password!")==false)
|
|
||||||
{passor.focus();return false;}
|
|
||||||
if (validate_required(passor2,"Please repeat password!")==false)
|
|
||||||
{passor2.focus();return false;}
|
|
||||||
if (passor2.value!=passor.value)
|
|
||||||
{alert(\'Repeated password is not equal to password!\');return false;}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>';
|
|
||||||
echo 'Set new password and e-mail to your account.<BR>
|
|
||||||
<FORM ACTION="' . getLink('account/lost') . '?action=step3" onsubmit="return validate_form(this)" METHOD=post>
|
|
||||||
<INPUT TYPE=hidden NAME="character" VALUE="">
|
|
||||||
<TABLE CELLSPACING=1 CELLPADDING=4 BORDER=0 WIDTH=100%>
|
|
||||||
<TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Please enter new password and e-mail</B></TD></TR>
|
|
||||||
<TR><TD BGCOLOR="'.$config['darkborder'].'">
|
|
||||||
Account of character: <INPUT TYPE=text NAME="nick" VALUE="'.$nick.'" SIZE="40" readonly="readonly"><BR />
|
|
||||||
New password: <INPUT id="passor" TYPE=password NAME="passor" VALUE="" SIZE="40"><BR>
|
|
||||||
Repeat new password: <INPUT id="passor2" TYPE=password NAME="passor" VALUE="" SIZE="40"><BR>
|
|
||||||
New e-mail address: <INPUT id="email" TYPE=text NAME="email" VALUE="" SIZE="40"><BR>
|
|
||||||
<INPUT TYPE=hidden NAME="key" VALUE="'.$rec_key.'">
|
|
||||||
</TD></TR>
|
|
||||||
</TABLE>
|
|
||||||
<BR>
|
|
||||||
<TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
' . $twig->render('buttons.submit.html.twig') . '</div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Wrong recovery key!';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Account of this character has no recovery key!';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Player or account of player <b>'.$nick.'</b> doesn\'t exist.';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Invalid player name format. If you have other characters on account try with other name.';
|
|
||||||
echo '<BR /><TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
<a href="' . getLink('account/lost') . '?action=step1&action_type=reckey&nick='.urlencode($nick).'" border="0"><IMG SRC="'.$template_path.'/images/global/buttons/sbutton_back.gif" NAME="Back" ALT="Back" BORDER=0 WIDTH=120 HEIGHT=18></a></div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
}
|
|
||||||
elseif($action == 'step3')
|
|
||||||
{
|
|
||||||
$rec_key = trim($_REQUEST['key']);
|
|
||||||
$nick = stripslashes($_REQUEST['nick']);
|
|
||||||
$new_pass = trim($_REQUEST['passor']);
|
|
||||||
$new_email = trim($_REQUEST['email']);
|
|
||||||
if(Validator::characterName($nick))
|
|
||||||
{
|
|
||||||
$player = new OTS_Player();
|
|
||||||
$account = new OTS_Account();
|
|
||||||
$player->find($nick);
|
|
||||||
if($player->isLoaded())
|
|
||||||
$account = $player->getAccount();
|
|
||||||
if($account->isLoaded())
|
|
||||||
{
|
|
||||||
$account_key = $account->getCustomField('key');
|
|
||||||
if(!empty($account_key))
|
|
||||||
{
|
|
||||||
if($account_key == $rec_key)
|
|
||||||
{
|
|
||||||
if(Validator::password($new_pass))
|
|
||||||
{
|
|
||||||
if(Validator::email($new_email))
|
|
||||||
{
|
|
||||||
$account->setEMail($new_email);
|
|
||||||
|
|
||||||
$tmp_new_pass = $new_pass;
|
|
||||||
if(USE_ACCOUNT_SALT)
|
|
||||||
{
|
|
||||||
$salt = generateRandomString(10, false, true, true);
|
|
||||||
$tmp_new_pass = $salt . $new_pass;
|
|
||||||
}
|
|
||||||
|
|
||||||
$account->setPassword(encrypt($tmp_new_pass));
|
|
||||||
$account->save();
|
|
||||||
|
|
||||||
if(USE_ACCOUNT_SALT)
|
|
||||||
$account->setCustomField('salt', $salt);
|
|
||||||
|
|
||||||
echo 'Your account name, new password and new e-mail.<BR>
|
|
||||||
<FORM ACTION="' . getLink('account/manage') . '" onsubmit="return validate_form(this)" METHOD=post>
|
|
||||||
<INPUT TYPE=hidden NAME="character" VALUE="">
|
|
||||||
<TABLE CELLSPACING=1 CELLPADDING=4 BORDER=0 WIDTH=100%>
|
|
||||||
<TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Your account name, new password and new e-mail</B></TD></TR>
|
|
||||||
<TR><TD BGCOLOR="'.$config['darkborder'].'">
|
|
||||||
Account name: <b>'.$account->getName().'</b><BR>
|
|
||||||
New password: <b>'.$new_pass.'</b><BR>
|
|
||||||
New e-mail address: <b>'.$new_email.'</b><BR>';
|
|
||||||
if($account->getCustomField('email_next') < time())
|
|
||||||
{
|
|
||||||
$mailBody = '
|
|
||||||
<h3>Your account name and new password!</h3>
|
|
||||||
<p>Changed password and e-mail to your account in Lost Account Interface on server <a href="'.BASE_URL.'"><b>'.$config['lua']['serverName'].'</b></a></p>
|
|
||||||
<p>Account name: <b>'.$account->getName().'</b></p>
|
|
||||||
<p>New password: <b>'.$new_pass.'</b></p>
|
|
||||||
<p>E-mail: <b>'.$new_email.'</b> (this e-mail)</p>
|
|
||||||
<br />
|
|
||||||
<p><u>It\'s automatic e-mail from OTS Lost Account System. Do not reply!</u></p>';
|
|
||||||
|
|
||||||
if(_mail($account->getCustomField('email'), $config['lua']['serverName']." - New password to your account", $mailBody))
|
|
||||||
{
|
|
||||||
echo '<br /><small>Sent e-mail with your account name and password to new e-mail. You should receive this e-mail in 15 minutes. You can login now with new password!</small>';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
echo '<br /><p class="error">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</p>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
echo '<br /><small>You will not receive e-mail with this informations.</small>';
|
|
||||||
}
|
|
||||||
echo '<INPUT TYPE=hidden NAME="account_login" VALUE="'.$account->getId().'">
|
|
||||||
<INPUT TYPE=hidden NAME="password_login" VALUE="'.$new_pass.'">
|
|
||||||
</TD></TR></TABLE><BR>
|
|
||||||
<TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
<INPUT TYPE=image NAME="Login" ALT="Login" SRC="'.$template_path.'/images/global/buttons/sbutton_login.gif" BORDER=0 WIDTH=120 HEIGHT=18></div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo Validator::getLastError();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo Validator::getLastError();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Wrong recovery key!';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Account of this character has no recovery key!';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Player or account of player <b>'.$nick.'</b> doesn\'t exist.';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'Invalid player name format. If you have other characters on account try with other name.';
|
|
||||||
echo '<BR /><TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
<a href="' . getLink('account/lost') . '?action=step1&action_type=reckey&nick='.urlencode($nick).'" border="0"><IMG SRC="'.$template_path.'/images/global/buttons/sbutton_back.gif" NAME="Back" ALT="Back" BORDER=0 WIDTH=120 HEIGHT=18></a></div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
}
|
|
||||||
elseif($action == 'checkcode')
|
|
||||||
{
|
|
||||||
$code = trim($_REQUEST['code']);
|
|
||||||
$character = stripslashes(trim($_REQUEST['character']));
|
|
||||||
if(empty($code) || empty($character))
|
|
||||||
echo 'Please enter code from e-mail and name of one character from account. Then press Submit.<BR>
|
|
||||||
<FORM ACTION="' . getLink('account/lost') . '?action=checkcode" METHOD=post>
|
|
||||||
<TABLE CELLSPACING=1 CELLPADDING=4 BORDER=0 WIDTH=100%>
|
|
||||||
<TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Code & character name</B></TD></TR>
|
|
||||||
<TR><TD BGCOLOR="'.$config['darkborder'].'">
|
|
||||||
Your code: <INPUT TYPE=text NAME="code" VALUE="" SIZE="40")><BR />
|
|
||||||
Character: <INPUT TYPE=text NAME="character" VALUE="" SIZE="40")><BR />
|
|
||||||
</TD></TR>
|
|
||||||
</TABLE>
|
|
||||||
<BR>
|
|
||||||
<TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
' . $twig->render('buttons.submit.html.twig') . '</div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$player = new OTS_Player();
|
|
||||||
$account = new OTS_Account();
|
|
||||||
$player->find($character);
|
|
||||||
if($player->isLoaded())
|
|
||||||
$account = $player->getAccount();
|
|
||||||
if($account->isLoaded())
|
|
||||||
{
|
|
||||||
if($account->getCustomField('email_code') == $code)
|
|
||||||
{
|
|
||||||
echo '<script type="text/javascript">
|
|
||||||
function validate_required(field,alerttxt)
|
|
||||||
{
|
|
||||||
with (field)
|
|
||||||
{
|
|
||||||
if (value==null||value==""||value==" ")
|
|
||||||
{alert(alerttxt);return false;}
|
|
||||||
else {return true}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function validate_form(thisform)
|
|
||||||
{
|
|
||||||
with (thisform)
|
|
||||||
{
|
|
||||||
if (validate_required(passor,"Please enter password!")==false)
|
|
||||||
{passor.focus();return false;}
|
|
||||||
if (validate_required(passor2,"Please repeat password!")==false)
|
|
||||||
{passor2.focus();return false;}
|
|
||||||
if (passor2.value!=passor.value)
|
|
||||||
{alert(\'Repeated password is not equal to password!\');return false;}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
Please enter new password to your account and repeat to make sure you remember password.<BR>
|
|
||||||
<FORM ACTION="' . getLink('account/lost') . '?action=setnewpassword" onsubmit="return validate_form(this)" METHOD=post>
|
|
||||||
<INPUT TYPE=hidden NAME="character" VALUE="'.$character.'">
|
|
||||||
<INPUT TYPE=hidden NAME="code" VALUE="'.$code.'">
|
|
||||||
<TABLE CELLSPACING=1 CELLPADDING=4 BORDER=0 WIDTH=100%>
|
|
||||||
<TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Code & account name</B></TD></TR>
|
|
||||||
<TR><TD BGCOLOR="'.$config['darkborder'].'">
|
|
||||||
New password: <INPUT TYPE=password ID="passor" NAME="passor" VALUE="" SIZE="40")><BR />
|
|
||||||
Repeat new password: <INPUT TYPE=password ID="passor2" NAME="passor2" VALUE="" SIZE="40")><BR />
|
|
||||||
</TD></TR>
|
|
||||||
</TABLE>
|
|
||||||
<BR>
|
|
||||||
<TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
' . $twig->render('buttons.submit.html.twig') . '</div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$error= 'Wrong code to change password.';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$error = 'Account of this character or this character doesn\'t exist.';
|
|
||||||
}
|
|
||||||
if(!empty($error))
|
|
||||||
echo '<span style="color: red"><b>'.$error.'</b></span><br />Please enter code from e-mail and name of one character from account. Then press Submit.<BR>
|
|
||||||
<FORM ACTION="' . getLink('account/lost') . '?action=checkcode" METHOD=post>
|
|
||||||
<TABLE CELLSPACING=1 CELLPADDING=4 BORDER=0 WIDTH=100%>
|
|
||||||
<TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Code & character name</B></TD></TR>
|
|
||||||
<TR><TD BGCOLOR="'.$config['darkborder'].'">
|
|
||||||
Your code: <INPUT TYPE=text NAME="code" VALUE="" SIZE="40")><BR />
|
|
||||||
Character: <INPUT TYPE=text NAME="character" VALUE="" SIZE="40")><BR />
|
|
||||||
</TD></TR>
|
|
||||||
</TABLE>
|
|
||||||
<BR>
|
|
||||||
<TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
' . $twig->render('buttons.submit.html.twig') . '</div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
}
|
|
||||||
elseif($action == 'setnewpassword')
|
|
||||||
{
|
|
||||||
$newpassword = $_REQUEST['passor'];
|
|
||||||
$code = $_REQUEST['code'];
|
|
||||||
$character = stripslashes($_REQUEST['character']);
|
|
||||||
echo '';
|
|
||||||
if(empty($code) || empty($character) || empty($newpassword))
|
|
||||||
echo '<span style="color: red"><b>Error. Try again.</b></span><br />Please enter code from e-mail and name of one character from account. Then press Submit.<BR>
|
|
||||||
<BR><FORM ACTION="' . getLink('account/lost') . '?action=checkcode" METHOD=post>
|
|
||||||
<TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
<INPUT TYPE=image NAME="Back" ALT="Back" SRC="'.$template_path.'/images/global/buttons/sbutton_back.gif" BORDER=0 WIDTH=120 HEIGHT=18></div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$player = new OTS_Player();
|
|
||||||
$account = new OTS_Account();
|
|
||||||
$player->find($character);
|
|
||||||
if($player->isLoaded())
|
|
||||||
$account = $player->getAccount();
|
|
||||||
if($account->isLoaded())
|
|
||||||
{
|
|
||||||
if($account->getCustomField('email_code') == $code)
|
|
||||||
{
|
|
||||||
if(Validator::password($newpassword))
|
|
||||||
{
|
|
||||||
$tmp_new_pass = $newpassword;
|
|
||||||
if(USE_ACCOUNT_SALT)
|
|
||||||
{
|
|
||||||
$salt = generateRandomString(10, false, true, true);
|
|
||||||
$tmp_new_pass = $salt . $newpassword;
|
|
||||||
$account->setCustomField('salt', $salt);
|
|
||||||
}
|
|
||||||
|
|
||||||
$account->setPassword(encrypt($tmp_new_pass ));
|
|
||||||
$account->save();
|
|
||||||
$account->setCustomField('email_code', '');
|
|
||||||
echo 'New password to your account is below. Now you can login.<BR>
|
|
||||||
<INPUT TYPE=hidden NAME="character" VALUE="'.$character.'">
|
|
||||||
<TABLE CELLSPACING=1 CELLPADDING=4 BORDER=0 WIDTH=100%>
|
|
||||||
<TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Changed password</B></TD></TR>
|
|
||||||
<TR><TD BGCOLOR="'.$config['darkborder'].'">
|
|
||||||
New password: <b>'.$newpassword.'</b><BR />
|
|
||||||
Account name: <i>(Already on your e-mail)</i><BR />';
|
|
||||||
|
|
||||||
$mailBody = '
|
|
||||||
<h3>Your account name and password!</h3>
|
|
||||||
<p>Changed password to your account in Lost Account Interface on server <a href="'.BASE_URL.'"><b>'.$config['lua']['serverName'].'</b></a></p>
|
|
||||||
<p>Account name: <b>'.$account->getName().'</b></p>
|
|
||||||
<p>New password: <b>'.$newpassword.'</b></p>
|
|
||||||
<br />
|
|
||||||
<p><u>It\'s automatic e-mail from OTS Lost Account System. Do not reply!</u></p>';
|
|
||||||
|
|
||||||
if(_mail($account->getCustomField('email'), $config['lua']['serverName']." - Your new password", $mailBody))
|
|
||||||
{
|
|
||||||
echo '<br /><small>New password work! Sent e-mail with your password and account name. You should receive this e-mail in 15 minutes. You can login now with new password!';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
echo '<br /><p class="error">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 '</TD></TR>
|
|
||||||
</TABLE>
|
|
||||||
<BR>
|
|
||||||
<TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
<FORM ACTION="' . getLink('account/manage') . '" METHOD=post>
|
|
||||||
<INPUT TYPE=image NAME="Login" ALT="Login" SRC="'.$template_path.'/images/global/buttons/sbutton_login.gif" BORDER=0 WIDTH=120 HEIGHT=18></div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$error= Validator::getLastError();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$error= 'Wrong code to change password.';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$error = 'Account of this character or this character doesn\'t exist.';
|
|
||||||
}
|
|
||||||
if(!empty($error))
|
|
||||||
echo '<span style="color: red"><b>'.$error.'</b></span><br />Please enter code from e-mail and name of one character from account. Then press Submit.<BR>
|
|
||||||
<FORM ACTION="' . getLink('account/lost') . '?action=checkcode" METHOD=post>
|
|
||||||
<TABLE CELLSPACING=1 CELLPADDING=4 BORDER=0 WIDTH=100%>
|
|
||||||
<TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Code & character name</B></TD></TR>
|
|
||||||
<TR><TD BGCOLOR="'.$config['darkborder'].'">
|
|
||||||
Your code: <INPUT TYPE=text NAME="code" VALUE="" SIZE="40")><BR />
|
|
||||||
Character: <INPUT TYPE=text NAME="character" VALUE="" SIZE="40")><BR />
|
|
||||||
</TD></TR>
|
|
||||||
</TABLE>
|
|
||||||
<BR>
|
|
||||||
<TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
|
|
||||||
' . $twig->render('buttons.submit.html.twig') . '</div>
|
|
||||||
</TD></TR></FORM></TABLE></TABLE>';
|
|
||||||
}
|
|
||||||
|
|||||||
18
system/pages/account/lost/base.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
function lostAccountWriteCooldown(string $nick, int $time): void
|
||||||
|
{
|
||||||
|
global $twig;
|
||||||
|
|
||||||
|
$inSec = $time - time();
|
||||||
|
$minutesLeft = floor($inSec / 60);
|
||||||
|
$secondsLeft = $inSec - ($minutesLeft * 60);
|
||||||
|
$timeLeft = "$minutesLeft minutes $secondsLeft seconds";
|
||||||
|
|
||||||
|
$timeRounded = ceil(setting('core.mail_lost_account_interval') / 60);
|
||||||
|
|
||||||
|
$twig->display('error_box.html.twig', [
|
||||||
|
'errors' => ["Account of selected character (<b>" . escapeHtml($nick) . "</b>) received e-mail in last $timeRounded minutes. You must wait $timeLeft before you can use Lost Account Interface again."]
|
||||||
|
]);
|
||||||
|
}
|
||||||
51
system/pages/account/lost/check-code.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
csrfProtect();
|
||||||
|
|
||||||
|
$title = 'Lost Account';
|
||||||
|
|
||||||
|
$code = $_REQUEST['code'] ?? '';
|
||||||
|
$character = $_REQUEST['character'] ?? '';
|
||||||
|
|
||||||
|
if(empty($code) || empty($character)) {
|
||||||
|
$twig->display('account/lost/check-code.html.twig', [
|
||||||
|
'code' => $code,
|
||||||
|
'characters' => $character,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$player = new OTS_Player();
|
||||||
|
$account = new OTS_Account();
|
||||||
|
$player->find($character);
|
||||||
|
if($player->isLoaded()) {
|
||||||
|
$account = $player->getAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($account->isLoaded()) {
|
||||||
|
if($account->getCustomField('email_code') == $code) {
|
||||||
|
$twig->display('account/lost/check-code.finish.html.twig', [
|
||||||
|
'character' => $character,
|
||||||
|
'code' => $code,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$error = 'Wrong code to change password.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$error = "Account of this character or this character doesn't exist.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($error)) {
|
||||||
|
$twig->display('error_box.html.twig', [
|
||||||
|
'errors' => [$error],
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo '<br/>';
|
||||||
|
|
||||||
|
$twig->display('account/lost/check-code.html.twig', [
|
||||||
|
|
||||||
|
]);
|
||||||
|
}
|
||||||
75
system/pages/account/lost/email/send-code.php
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
csrfProtect();
|
||||||
|
|
||||||
|
require __DIR__ . '/../base.php';
|
||||||
|
|
||||||
|
$title = 'Lost Account';
|
||||||
|
|
||||||
|
$email = $_POST['email'] ?? '';
|
||||||
|
$nick = $_POST['nick'] ?? '';
|
||||||
|
|
||||||
|
$player = new OTS_Player();
|
||||||
|
$account = new OTS_Account();
|
||||||
|
$player->find($nick);
|
||||||
|
if($player->isLoaded()) {
|
||||||
|
$account = $player->getAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($account->isLoaded()) {
|
||||||
|
if($account->getCustomField('email_next') < time()) {
|
||||||
|
if($account->getEMail() == $email) {
|
||||||
|
$newCode = generateRandomString(30, true, false, true);
|
||||||
|
$mailBody = $twig->render('mail.account.lost.code.html.twig', [
|
||||||
|
'newCode' => $newCode,
|
||||||
|
'account' => $account,
|
||||||
|
'nick' => $nick,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$accountEMail = $account->getCustomField('email');
|
||||||
|
if(_mail($accountEMail, configLua('serverName') . ' - Recover your account', $mailBody)) {
|
||||||
|
$account->setCustomField('email_code', $newCode);
|
||||||
|
$account->setCustomField('email_next', (time() + setting('core.mail_lost_account_interval')));
|
||||||
|
|
||||||
|
$twig->display('success.html.twig', [
|
||||||
|
'title' => 'Email has been sent',
|
||||||
|
'description' => 'Details about steps required to recover your account has been sent to <b>' . $accountEMail . '</b>. You should receive this email within 15 minutes. Please check your inbox/spam directory.',
|
||||||
|
'custom_buttons' => '',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$twig->display('account.back_button.html.twig', [
|
||||||
|
'new_line' => true,
|
||||||
|
'center' => true,
|
||||||
|
'action' => getLink('news'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$account->setCustomField('email_next', (time() + 60));
|
||||||
|
error('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</p>');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Invalid e-mail to account of character <b>' . escapeHtml($nick) . '</b>. Try again.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lostAccountWriteCooldown($nick, (int)$account->getCustomField('email_next'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = "Player or account of player <b>" . escapeHtml($nick) . "</b> doesn't exist.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$twig->display('error_box.html.twig', [
|
||||||
|
'errors' => $errors,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account.back_button.html.twig', [
|
||||||
|
'new_line' => true,
|
||||||
|
'center' => true,
|
||||||
|
'action' => getLink('account/lost/step-1') . '?action=email&nick=' . urlencode($nick),
|
||||||
|
]);
|
||||||
128
system/pages/account/lost/email/set-new-password.php
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
csrfProtect();
|
||||||
|
|
||||||
|
$title = 'Lost Account';
|
||||||
|
|
||||||
|
$newPassword = $_POST['password'] ?? '';
|
||||||
|
$passwordRepeat = $_POST['password_repeat'] ?? '';
|
||||||
|
$code = $_POST['code'] ?? '';
|
||||||
|
$character = $_POST['character'] ?? '';
|
||||||
|
|
||||||
|
if(empty($code) || empty($character)) {
|
||||||
|
$errors[] = 'Please enter code from e-mail and name of one character from account.';
|
||||||
|
|
||||||
|
$twig->display('error_box.html.twig', [
|
||||||
|
'errors' => $errors,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$twig->display('account/lost/check-code.html.twig', [
|
||||||
|
'code' => $code,
|
||||||
|
'character' => $character,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$twig->display('account.back_button.html.twig', [
|
||||||
|
'new_line' => true,
|
||||||
|
'center' => true,
|
||||||
|
'action' => getLink('account/lost/check-code')
|
||||||
|
]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($newPassword) || empty($passwordRepeat)) {
|
||||||
|
$errors[] = 'Please enter both passwords.';
|
||||||
|
|
||||||
|
$twig->display('error_box.html.twig', [
|
||||||
|
'errors' => $errors,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$twig->display('account/lost/check-code.finish.html.twig', [
|
||||||
|
'character' => $character,
|
||||||
|
'code' => $code,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$player = new OTS_Player();
|
||||||
|
$account = new OTS_Account();
|
||||||
|
$player->find($character);
|
||||||
|
if($player->isLoaded()) {
|
||||||
|
$account = $player->getAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
$passwordFailed = false;
|
||||||
|
|
||||||
|
if($account->isLoaded()) {
|
||||||
|
if($account->getCustomField('email_code') == $code) {
|
||||||
|
if ($newPassword == $passwordRepeat) {
|
||||||
|
if (Validator::password($newPassword)) {
|
||||||
|
|
||||||
|
$hooks->trigger(HOOK_ACCOUNT_LOST_EMAIL_SET_NEW_PASSWORD_POST);
|
||||||
|
|
||||||
|
if (empty($errors)) {
|
||||||
|
$tmp_new_pass = $newPassword;
|
||||||
|
if (USE_ACCOUNT_SALT) {
|
||||||
|
$salt = generateRandomString(10, false, true, true);
|
||||||
|
$tmp_new_pass = $salt . $newPassword;
|
||||||
|
$account->setCustomField('salt', $salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
$account->setPassword(encrypt($tmp_new_pass));
|
||||||
|
$account->save();
|
||||||
|
$account->setCustomField('email_code', '');
|
||||||
|
|
||||||
|
$mailBody = $twig->render('mail.account.lost.new-password.html.twig', [
|
||||||
|
'account' => $account,
|
||||||
|
'newPassword' => $newPassword,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$statusMsg = '';
|
||||||
|
if (_mail($account->getCustomField('email'), configLua('serverName') . ' - Your new password', $mailBody)) {
|
||||||
|
$statusMsg = '<br /><small>New password work! Sent e-mail with your password and account name. You should receive this e-mail in 15 minutes. You can login now with new password!';
|
||||||
|
} else {
|
||||||
|
$statusMsg = '<br /><p class="error">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';
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account/lost/finish.new-password.html.twig', [
|
||||||
|
'statusMsg' => $statusMsg,
|
||||||
|
'newPassword' => $newPassword,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$passwordFailed = true;
|
||||||
|
$errors[] = Validator::getLastError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$passwordFailed = true;
|
||||||
|
$errors[] = 'Passwords are not the same!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Wrong code to change password.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = "Account of this character or this character doesn't exist.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!empty($errors)) {
|
||||||
|
$twig->display('error_box.html.twig', [
|
||||||
|
'errors' => $errors,
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo '<br/>';
|
||||||
|
|
||||||
|
$template = 'account/lost/check-code.html.twig';
|
||||||
|
if($passwordFailed) {
|
||||||
|
$template = 'account/lost/check-code.finish.html.twig';
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display($template, [
|
||||||
|
'code' => $code,
|
||||||
|
'character' => $character,
|
||||||
|
]);
|
||||||
|
}
|
||||||
36
system/pages/account/lost/email/step-1.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
require __DIR__ . '/../base.php';
|
||||||
|
|
||||||
|
csrfProtect();
|
||||||
|
|
||||||
|
$title = 'Lost Account';
|
||||||
|
|
||||||
|
$nick = $_REQUEST['nick'] ?? '';
|
||||||
|
|
||||||
|
if($account->isLoaded()) {
|
||||||
|
if($account->getCustomField('email_next') < time()) {
|
||||||
|
$twig->display('account/lost/email.html.twig', [
|
||||||
|
'nick' => $nick,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lostAccountWriteCooldown($nick, (int)$account->getCustomField('email_next'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = "Player or account of player <b>" . escapeHtml($nick) . "</b> doesn't exist.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$twig->display('error_box.html.twig', [
|
||||||
|
'errors' => $errors,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account.back_button.html.twig', [
|
||||||
|
'new_line' => true,
|
||||||
|
'center' => true,
|
||||||
|
'action' => getLink('account/lost'),
|
||||||
|
]);
|
||||||
38
system/pages/account/lost/recovery-key/step-1.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
csrfProtect();
|
||||||
|
|
||||||
|
$title = 'Lost Account';
|
||||||
|
|
||||||
|
$nick = $_REQUEST['nick'] ?? '';
|
||||||
|
$key = $_REQUEST['key'] ?? '';
|
||||||
|
|
||||||
|
if($account->isLoaded()) {
|
||||||
|
$account_key = $account->getCustomField('key');
|
||||||
|
|
||||||
|
if(!empty($account_key)) {
|
||||||
|
$twig->display('account/lost/recovery-key.step-1.html.twig', [
|
||||||
|
'nick' => $nick,
|
||||||
|
'key' => $key,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Account of this character has no recovery key!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = "Player or account of player <b>" . escapeHtml($nick) . "</b> doesn't exist.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$twig->display('error_box.html.twig', [
|
||||||
|
'errors' => $errors,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account.back_button.html.twig', [
|
||||||
|
'new_line' => true,
|
||||||
|
'center' => true,
|
||||||
|
'action' => getLink('account/lost'),
|
||||||
|
]);
|
||||||
49
system/pages/account/lost/recovery-key/step-2.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
csrfProtect();
|
||||||
|
|
||||||
|
$title = 'Lost Account';
|
||||||
|
|
||||||
|
$key = $_REQUEST['key'] ?? '';
|
||||||
|
$nick = $_REQUEST['nick'] ?? '';
|
||||||
|
|
||||||
|
$player = new OTS_Player();
|
||||||
|
$account = new OTS_Account();
|
||||||
|
$player->find($nick);
|
||||||
|
if($player->isLoaded()) {
|
||||||
|
$account = $player->getAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($account->isLoaded()) {
|
||||||
|
$accountKey = $account->getCustomField('key');
|
||||||
|
if(!empty($accountKey)) {
|
||||||
|
if($accountKey == $key) {
|
||||||
|
$twig->display('account/lost/recovery-key.step-2.html.twig', [
|
||||||
|
'nick' => $nick,
|
||||||
|
'key' => $key,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Wrong recovery key!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Account of this character has no recovery key!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = "Player or account of player <b>" . escapeHtml($nick) . "</b> doesn't exist.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$twig->display('error_box.html.twig', [
|
||||||
|
'errors' => $errors,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account.back_button.html.twig', [
|
||||||
|
'new_line' => true,
|
||||||
|
'center' => true,
|
||||||
|
'action' => getLink('account/lost/step-1') . '?action=recovery-key&nick=' . urlencode($nick) . '&key=' . urlencode($key),
|
||||||
|
]);
|
||||||
117
system/pages/account/lost/recovery-key/step-3.php
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use MyAAC\Models\Account as AccountModel;
|
||||||
|
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
csrfProtect();
|
||||||
|
|
||||||
|
$title = 'Lost Account';
|
||||||
|
|
||||||
|
$key = $_POST['key'];
|
||||||
|
$nick = $_POST['nick'] ?? '';
|
||||||
|
$newPassword = $_POST['password'] ?? '';
|
||||||
|
$passwordRepeat = $_POST['password_repeat'] ?? '';
|
||||||
|
$newEmail = $_POST['email'] ?? '';
|
||||||
|
|
||||||
|
$player = new OTS_Player();
|
||||||
|
$account = new OTS_Account();
|
||||||
|
$player->find($nick);
|
||||||
|
if($player->isLoaded()) {
|
||||||
|
$account = $player->getAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if($account->isLoaded()) {
|
||||||
|
$accountKey = $account->getCustomField('key');
|
||||||
|
|
||||||
|
if(!empty($accountKey)) {
|
||||||
|
if($accountKey == $key) {
|
||||||
|
if(Validator::password($newPassword)) {
|
||||||
|
if ($newPassword == $passwordRepeat) {
|
||||||
|
if (Validator::email($newEmail)) {
|
||||||
|
$emailExists = AccountModel::where('email', $newEmail)->count() > 0;
|
||||||
|
if (!$emailExists) {
|
||||||
|
|
||||||
|
$hooks->trigger(HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_3_POST);
|
||||||
|
|
||||||
|
if (empty($errors)) {
|
||||||
|
$account->setEMail($newEmail);
|
||||||
|
|
||||||
|
$tmp_new_pass = $newPassword;
|
||||||
|
if (USE_ACCOUNT_SALT) {
|
||||||
|
$salt = generateRandomString(10, false, true, true);
|
||||||
|
$tmp_new_pass = $salt . $newPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
$account->setPassword(encrypt($tmp_new_pass));
|
||||||
|
$account->save();
|
||||||
|
|
||||||
|
if (USE_ACCOUNT_SALT) {
|
||||||
|
$account->setCustomField('salt', $salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
$statusMsg = '';
|
||||||
|
if ($account->getCustomField('email_next') < time()) {
|
||||||
|
$mailBody = $twig->render('mail.account.lost.new-email.html.twig', [
|
||||||
|
'account' => $account,
|
||||||
|
'newPassword' => $newPassword,
|
||||||
|
'newEmail' => $newEmail,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (_mail($account->getCustomField('email'), configLua('serverName') . ' - New password to your account', $mailBody)) {
|
||||||
|
$statusMsg = '<br /><small>Sent e-mail with your account name and password to new e-mail. You should receive this e-mail in 15 minutes. You can login now with new password!</small>';
|
||||||
|
} else {
|
||||||
|
$statusMsg = '<br /><p class="error">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</p>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$statusMsg = '<br /><small>You will not receive e-mail with this informations.</small>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account/lost/finish.new-email.html.twig', [
|
||||||
|
'statusMsg' => $statusMsg,
|
||||||
|
'account' => $account,
|
||||||
|
'newPassword' => $newPassword,
|
||||||
|
'newEmail' => $newEmail,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'This email is already registered!';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$errors[] = Validator::getLastError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Passwords are not the same!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = Validator::getLastError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Wrong recovery key!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Account of this character has no recovery key!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = "Player or account of player <b>" . escapeHtml($nick) . "</b> doesn't exist.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$twig->display('error_box.html.twig', [
|
||||||
|
'errors' => $errors,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('account.back_button.html.twig', [
|
||||||
|
'new_line' => true,
|
||||||
|
'center' => true,
|
||||||
|
'action' => getLink('account/lost/recovery-key/step-2') . '?nick=' . urlencode($nick) . '&key=' . urlencode($key),
|
||||||
|
]);
|
||||||
26
system/pages/account/lost/step-1.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
|
csrfProtect();
|
||||||
|
|
||||||
|
$title = 'Lost Account';
|
||||||
|
|
||||||
|
$nick = $_REQUEST['nick'] ?? '';
|
||||||
|
|
||||||
|
$player = new OTS_Player();
|
||||||
|
$account = new OTS_Account();
|
||||||
|
$player->find($nick);
|
||||||
|
if($player->isLoaded()) {
|
||||||
|
$account = $player->getAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ACTION == 'email') {
|
||||||
|
require __DIR__ . '/email/step-1.php';
|
||||||
|
}
|
||||||
|
else if (ACTION == 'recovery-key') {
|
||||||
|
require __DIR__ . '/recovery-key/step-1.php';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$twig->display('account/lost/no-action.html.twig');
|
||||||
|
}
|
||||||
|
|
||||||
@@ -8,6 +8,9 @@
|
|||||||
* @copyright 2019 MyAAC
|
* @copyright 2019 MyAAC
|
||||||
* @link https://my-aac.org
|
* @link https://my-aac.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
|
|
||||||
$title = 'Account Management';
|
$title = 'Account Management';
|
||||||
@@ -96,12 +99,8 @@ if($email_new_time > 1)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$actions = array();
|
$actions = $account_logged->getActionsLog(1000);
|
||||||
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 */
|
/** @var OTS_Players_List $account_players */
|
||||||
$account_players = $account_logged->getPlayersList();
|
$account_players = $account_logged->getPlayersList();
|
||||||
$account_players->orderBy('id');
|
$account_players->orderBy('id');
|
||||||
@@ -120,6 +119,8 @@ $twig->display('account.management.html.twig', array(
|
|||||||
'account_registered' => $account_registered,
|
'account_registered' => $account_registered,
|
||||||
'account_rlname' => $account_rlname,
|
'account_rlname' => $account_rlname,
|
||||||
'account_location' => $account_location,
|
'account_location' => $account_location,
|
||||||
|
'twoFactorViews' => TwoFactorAuth::getInstance($account_logged)->getAccountManageViews(),
|
||||||
|
|
||||||
'actions' => $actions,
|
'actions' => $actions,
|
||||||
'players' => $account_players
|
'players' => $account_players,
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ else
|
|||||||
if($points >= setting('core.account_generate_new_reckey_price'))
|
if($points >= setting('core.account_generate_new_reckey_price'))
|
||||||
{
|
{
|
||||||
$show_form = false;
|
$show_form = false;
|
||||||
$new_rec_key = generateRandomString(10, false, true, true);
|
$new_rec_key = generateRecoveryKey();
|
||||||
|
|
||||||
$mailBody = $twig->render('mail.account.register.html.twig', array(
|
$mailBody = $twig->render('mail.account.register.html.twig', array(
|
||||||
'recovery_key' => $new_rec_key
|
'recovery_key' => $new_rec_key
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ if(isset($_POST['registeraccountsave']) && $_POST['registeraccountsave'] == "1")
|
|||||||
if($reg_password == $account_logged->getPassword()) {
|
if($reg_password == $account_logged->getPassword()) {
|
||||||
if(empty($old_key)) {
|
if(empty($old_key)) {
|
||||||
$show_form = false;
|
$show_form = false;
|
||||||
$new_rec_key = generateRandomString(10, false, true, true);
|
$new_rec_key = generateRecoveryKey();
|
||||||
|
|
||||||
$account_logged->setCustomField("key", $new_rec_key);
|
$account_logged->setCustomField("key", $new_rec_key);
|
||||||
$account_logged->logAction('Generated recovery key.');
|
$account_logged->logAction('Generated recovery key.');
|
||||||
|
|||||||
@@ -452,10 +452,8 @@ WHERE killers.death_id = '".$death['id']."' ORDER BY killers.final_hit DESC, kil
|
|||||||
if($query->rowCount() > 0) {
|
if($query->rowCount() > 0) {
|
||||||
echo 'Did you mean:<ul>';
|
echo 'Did you mean:<ul>';
|
||||||
foreach($query as $player) {
|
foreach($query as $player) {
|
||||||
if(isset($player['promotion'])) {
|
$player['vocation'] = OTS_Toolbox::getVocationFromPromotion($player['vocation'], $player['promotion'] ?? 0);
|
||||||
if((int)$player['promotion'] > 0)
|
|
||||||
$player['vocation'] += ($player['promotion'] * $config['vocations_amount']);
|
|
||||||
}
|
|
||||||
echo '<li>' . getPlayerLink($player['name']) . ' (<small><strong>level ' . $player['level'] . ', ' . $config['vocations'][$player['vocation']] . '</strong></small>)</li>';
|
echo '<li>' . getPlayerLink($player['name']) . ' (<small><strong>level ' . $player['level'] . ', ' . $config['vocations'][$player['vocation']] . '</strong></small>)</li>';
|
||||||
}
|
}
|
||||||
echo '</ul>';
|
echo '</ul>';
|
||||||
|
|||||||
@@ -42,35 +42,12 @@ for($i = 0; $i < $threads_count['threads_count'] / setting('core.forum_threads_p
|
|||||||
$links_to_pages .= '<b>'.($i + 1).' </b>';
|
$links_to_pages .= '<b>'.($i + 1).' </b>';
|
||||||
}
|
}
|
||||||
|
|
||||||
echo '<a href="' . getLink('forum') . '">Boards</a> >> <b>'.escapeHtml($sections[$section_id]['name']).'</b>';
|
|
||||||
|
|
||||||
if($logged && (!$sections[$section_id]['closed'] || Forum::isModerator())) {
|
|
||||||
echo '<br /><br />
|
|
||||||
<a href="' . getLink('forum') . '?action=new_thread§ion_id='.$section_id.'"><img src="images/forum/topic.gif" border="0" /></a>';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo '<br /><br />Page: '.$links_to_pages.'<br />';
|
|
||||||
$last_threads = $db->query("SELECT `players`.`id` as `player_id`, `players`.`name`, `" . FORUM_TABLE_PREFIX . "forum`.`first_post`, `" . FORUM_TABLE_PREFIX . "forum`.`post_text`, `" . FORUM_TABLE_PREFIX . "forum`.`post_topic`, `" . FORUM_TABLE_PREFIX . "forum`.`id`, `" . FORUM_TABLE_PREFIX . "forum`.`last_post`, `" . FORUM_TABLE_PREFIX . "forum`.`replies`, `" . FORUM_TABLE_PREFIX . "forum`.`views`, `" . FORUM_TABLE_PREFIX . "forum`.`post_date` FROM `players`, `" . FORUM_TABLE_PREFIX . "forum` WHERE `players`.`id` = `" . FORUM_TABLE_PREFIX . "forum`.`author_guid` AND `" . FORUM_TABLE_PREFIX . "forum`.`section` = ".$section_id." AND `" . FORUM_TABLE_PREFIX . "forum`.`first_post` = `" . FORUM_TABLE_PREFIX . "forum`.`id` ORDER BY `" . FORUM_TABLE_PREFIX . "forum`.`last_post` DESC LIMIT ".setting('core.forum_threads_per_page')." OFFSET ".($_page * setting('core.forum_threads_per_page')))->fetchAll(PDO::FETCH_ASSOC);
|
$last_threads = $db->query("SELECT `players`.`id` as `player_id`, `players`.`name`, `" . FORUM_TABLE_PREFIX . "forum`.`first_post`, `" . FORUM_TABLE_PREFIX . "forum`.`post_text`, `" . FORUM_TABLE_PREFIX . "forum`.`post_topic`, `" . FORUM_TABLE_PREFIX . "forum`.`id`, `" . FORUM_TABLE_PREFIX . "forum`.`last_post`, `" . FORUM_TABLE_PREFIX . "forum`.`replies`, `" . FORUM_TABLE_PREFIX . "forum`.`views`, `" . FORUM_TABLE_PREFIX . "forum`.`post_date` FROM `players`, `" . FORUM_TABLE_PREFIX . "forum` WHERE `players`.`id` = `" . FORUM_TABLE_PREFIX . "forum`.`author_guid` AND `" . FORUM_TABLE_PREFIX . "forum`.`section` = ".$section_id." AND `" . FORUM_TABLE_PREFIX . "forum`.`first_post` = `" . FORUM_TABLE_PREFIX . "forum`.`id` ORDER BY `" . FORUM_TABLE_PREFIX . "forum`.`last_post` DESC LIMIT ".setting('core.forum_threads_per_page')." OFFSET ".($_page * setting('core.forum_threads_per_page')))->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if(isset($last_threads[0])) {
|
$threads = [];
|
||||||
echo '<table width="100%">
|
if(count($last_threads) > 0) {
|
||||||
<tr bgcolor="'.$config['vdarkborder'].'" align="center">
|
|
||||||
<td class="white">
|
|
||||||
<span style="font-size: 10px"><b>Thread</b></span></td>
|
|
||||||
<td><span style="font-size: 10px"><b>Thread Starter</b></span></td>
|
|
||||||
<td><span style="font-size: 10px"><b>Replies</b></span></td>
|
|
||||||
<td><span style="font-size: 10px"><b>Views</b></span></td>
|
|
||||||
<td><span style="font-size: 10px"><b>Last Post</b></span></td>
|
|
||||||
</tr>';
|
|
||||||
|
|
||||||
$player = new OTS_Player();
|
$player = new OTS_Player();
|
||||||
foreach($last_threads as $thread) {
|
foreach($last_threads as $thread) {
|
||||||
echo '<tr bgcolor="' . getStyle($number_of_rows++) . '"><td>';
|
|
||||||
if(Forum::isModerator()) {
|
|
||||||
echo '<a href="' . getLink('forum') . '?action=move_thread&id=' . $thread['id'] . '" title="Move Thread"><img src="images/icons/arrow_right.gif"/></a>';
|
|
||||||
$twig->display('forum.remove_post.html.twig', ['post' => $thread]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$player->load($thread['player_id']);
|
$player->load($thread['player_id']);
|
||||||
if(!$player->isLoaded()) {
|
if(!$player->isLoaded()) {
|
||||||
throw new RuntimeException('Forum error: Player not loaded.');
|
throw new RuntimeException('Forum error: Player not loaded.');
|
||||||
@@ -79,28 +56,29 @@ if(isset($last_threads[0])) {
|
|||||||
$player_account = $player->getAccount();
|
$player_account = $player->getAccount();
|
||||||
$canEditForum = $player_account->hasFlag(FLAG_CONTENT_FORUM) || $player_account->isAdmin();
|
$canEditForum = $player_account->hasFlag(FLAG_CONTENT_FORUM) || $player_account->isAdmin();
|
||||||
|
|
||||||
echo '<a href="' . getForumThreadLink($thread['id']) . '">'.htmlspecialchars($thread['post_topic']). '</a><br /><small>'.($canEditForum ? substr(strip_tags($thread['post_text']), 0, 50) : htmlspecialchars(substr($thread['post_text'], 0, 50))).'...</small></td><td>' . getPlayerLink($thread['name']) . '</td><td>'.(int) $thread['replies'].'</td><td>'.(int) $thread['views'].'</td><td>';
|
$thread['link'] = getForumThreadLink($thread['id']);
|
||||||
|
|
||||||
|
$thread['post_shortened'] = ($canEditForum ? substr(strip_tags($thread['post_text']), 0, 50) : htmlspecialchars(substr($thread['post_text'], 0, 50)));
|
||||||
|
|
||||||
|
$thread['player'] = $player;
|
||||||
|
$thread['player_link'] = getPlayerLink($thread['name']);
|
||||||
|
|
||||||
if($thread['last_post'] > 0) {
|
if($thread['last_post'] > 0) {
|
||||||
$last_post = $db->query("SELECT `players`.`name`, `" . FORUM_TABLE_PREFIX . "forum`.`post_date` FROM `players`, `" . FORUM_TABLE_PREFIX . "forum` WHERE `" . FORUM_TABLE_PREFIX . "forum`.`first_post` = ".(int) $thread['id']." AND `players`.`id` = `" . FORUM_TABLE_PREFIX . "forum`.`author_guid` ORDER BY `post_date` DESC LIMIT 1")->fetch();
|
$last_post = $db->query("SELECT `players`.`name`, `" . FORUM_TABLE_PREFIX . "forum`.`post_date` FROM `players`, `" . FORUM_TABLE_PREFIX . "forum` WHERE `" . FORUM_TABLE_PREFIX . "forum`.`first_post` = ".(int) $thread['id']." AND `players`.`id` = `" . FORUM_TABLE_PREFIX . "forum`.`author_guid` ORDER BY `post_date` DESC LIMIT 1")->fetch();
|
||||||
|
|
||||||
if(isset($last_post['name'])) {
|
$last_post['player_link'] = getPlayerLink($last_post['name']);
|
||||||
echo date('d.m.y H:i:s', $last_post['post_date']) . '<br />by ' . getPlayerLink($last_post['name']);
|
$thread['latest_post'] = $last_post;
|
||||||
}
|
|
||||||
else {
|
|
||||||
echo 'No posts.';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
echo date('d.m.y H:i:s', $thread['post_date']) . '<br />by ' . getPlayerLink($thread['name']);
|
|
||||||
}
|
|
||||||
echo '</td></tr>';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo '</table>';
|
$threads[] = $thread;
|
||||||
if($logged && (!$sections[$section_id]['closed'] || Forum::isModerator())) {
|
|
||||||
echo '<br /><a href="' . getLink('forum') . '?action=new_thread§ion_id=' . $section_id . '"><img src="images/forum/topic.gif" border="0" /></a>';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
echo '<h3>No threads in this board.</h3>';
|
$twig->display('forum.show_board.html.twig', [
|
||||||
}
|
'threads' => $threads,
|
||||||
|
'section_id' => $section_id,
|
||||||
|
'section_name' => $sections[$section_id]['name'],
|
||||||
|
'links_to_pages' => $links_to_pages,
|
||||||
|
'is_moderator' => Forum::isModerator(),
|
||||||
|
'closed' => $sections[$section_id]['closed'],
|
||||||
|
]);
|
||||||
|
|||||||
@@ -9,316 +9,25 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use MyAAC\Cache\Cache;
|
use MyAAC\Cache\Cache;
|
||||||
use MyAAC\Models\Gallery as ModelsGallery;
|
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
$title = 'Gallery';
|
$title = 'Gallery';
|
||||||
|
|
||||||
$canEdit = hasFlag(FLAG_CONTENT_GALLERY) || superAdmin();
|
const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
||||||
if($canEdit) {
|
|
||||||
if(function_exists('imagecreatefrompng')) {
|
|
||||||
if (!empty($action)) {
|
|
||||||
if ($action == 'delete' || $action == 'edit' || $action == 'hide' || $action == 'moveup' || $action == 'movedown')
|
|
||||||
$id = $_REQUEST['id'];
|
|
||||||
|
|
||||||
if (isset($_REQUEST['comment']))
|
$images = Cache::remember('gallery', 5 * 60, function () {
|
||||||
$comment = stripslashes($_REQUEST['comment']);
|
$images = glob(BASE . GALLERY_DIR . '*.*');
|
||||||
|
|
||||||
if (isset($_REQUEST['image']))
|
$images = array_filter($images, function ($image) {
|
||||||
$image = $_REQUEST['image'];
|
$ext = pathinfo($image, PATHINFO_EXTENSION);
|
||||||
|
|
||||||
if (isset($_REQUEST['author']))
|
return (in_array($ext, ALLOWED_EXTENSIONS) && !str_contains($image, '_thumb'));
|
||||||
$author = $_REQUEST['author'];
|
});
|
||||||
|
|
||||||
$errors = array();
|
return array_map(function ($image) {
|
||||||
|
return basename($image);
|
||||||
if ($action == 'add') {
|
}, $images);
|
||||||
if (Gallery::add($comment, $image, $author, $errors))
|
|
||||||
$comment = $image = $author = '';
|
|
||||||
} else if ($action == 'delete') {
|
|
||||||
Gallery::delete($id, $errors);
|
|
||||||
} else if ($action == 'edit') {
|
|
||||||
if (isset($id) && !isset($name)) {
|
|
||||||
$tmp = Gallery::get($id);
|
|
||||||
$comment = $tmp['comment'];
|
|
||||||
$image = $tmp['image'];
|
|
||||||
$author = $tmp['author'];
|
|
||||||
} else {
|
|
||||||
Gallery::update($id, $comment, $image, $author);
|
|
||||||
$action = $comment = $image = $author = '';
|
|
||||||
}
|
|
||||||
} else if ($action == 'hide') {
|
|
||||||
Gallery::toggleHide($id, $errors);
|
|
||||||
} else if ($action == 'moveup') {
|
|
||||||
Gallery::move($id, -1, $errors);
|
|
||||||
} else if ($action == 'movedown') {
|
|
||||||
Gallery::move($id, 1, $errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($errors))
|
|
||||||
$twig->display('error_box.html.twig', array('errors' => $errors));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isset($_GET['image'])) {
|
|
||||||
$twig->display('gallery.form.html.twig', array(
|
|
||||||
'link' => getLink('gallery/' . ($action == 'edit' ? 'edit' : 'add')),
|
|
||||||
'action' => $action,
|
|
||||||
'id' => isset($id) ? $id : null,
|
|
||||||
'comment' => isset($comment) ? $comment : null,
|
|
||||||
'image' => isset($image) ? $image : null,
|
|
||||||
'author' => isset($author) ? $author : null
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo 'You cannot edit/add gallery items as it seems your PHP installation doesnt have GD support enabled. Visit <a href="http://be2.php.net/manual/en/image.installation.php">PHP Manual</a> for more info.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isset($_GET['image']))
|
|
||||||
{
|
|
||||||
$image = $db->query('SELECT * FROM `' . TABLE_PREFIX . 'gallery` WHERE `id` = ' . $db->quote($_GET['image']) . ' ORDER by `ordering` LIMIT 1;');
|
|
||||||
if($image->rowCount() == 1)
|
|
||||||
$image = $image->fetch();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
echo 'Image with this id does not exists.';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$previous_image = $db->query('SELECT `id` FROM `' . TABLE_PREFIX . 'gallery` WHERE `id` = ' . $db->quote($image['id'] - 1) . ' ORDER by `ordering`;');
|
|
||||||
if($previous_image->rowCount() == 1)
|
|
||||||
$previous_image = $previous_image->fetch();
|
|
||||||
else
|
|
||||||
$previous_image = NULL;
|
|
||||||
|
|
||||||
$next_image = $db->query('SELECT `id` FROM `' . TABLE_PREFIX . 'gallery` WHERE `id` = ' . $db->quote($image['id'] + 1) . ' ORDER by `ordering`;');
|
|
||||||
if($next_image->rowCount() == 1)
|
|
||||||
$next_image = $next_image->fetch();
|
|
||||||
else
|
|
||||||
$next_image = NULL;
|
|
||||||
|
|
||||||
$twig->display('gallery.get.html.twig', array(
|
|
||||||
'previous' => $previous_image ? $previous_image['id'] : null,
|
|
||||||
'next' => $next_image ? $next_image['id'] : null,
|
|
||||||
'image' => $image
|
|
||||||
));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$images = Cache::remember('gallery_' . ($canEdit ? '1' : '0'), 60, function () use ($db, $canEdit) {
|
|
||||||
return $db->query('SELECT `id`, `comment`, `image`, `author`, `thumb`' .
|
|
||||||
($canEdit ? ', `hide`, `ordering`' : '') .
|
|
||||||
' FROM `' . TABLE_PREFIX . 'gallery`' .
|
|
||||||
(!$canEdit ? ' WHERE `hide` != 1' : '') .
|
|
||||||
' ORDER BY `ordering`;')->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$last = count($images);
|
$twig->display('gallery.html.twig', [
|
||||||
if(!$last)
|
|
||||||
{
|
|
||||||
?>
|
|
||||||
There are no images added to gallery yet.
|
|
||||||
<?php
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$twig->display('gallery.html.twig', array(
|
|
||||||
'images' => $images,
|
'images' => $images,
|
||||||
'last' => $last,
|
]);
|
||||||
'canEdit' => $canEdit
|
|
||||||
));
|
|
||||||
|
|
||||||
class Gallery
|
|
||||||
{
|
|
||||||
static public function add($comment, $image, $author, &$errors)
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
if(isset($comment[0]) && isset($image[0]) && isset($author[0]))
|
|
||||||
{
|
|
||||||
$query =
|
|
||||||
$db->query(
|
|
||||||
'SELECT `ordering`' .
|
|
||||||
' FROM `' . TABLE_PREFIX . 'gallery`' .
|
|
||||||
' ORDER BY `ordering`' . ' DESC LIMIT 1'
|
|
||||||
);
|
|
||||||
|
|
||||||
$ordering = 0;
|
|
||||||
if($query->rowCount() > 0) {
|
|
||||||
$query = $query->fetch();
|
|
||||||
$ordering = $query['ordering'] + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$pathinfo = pathinfo($image);
|
|
||||||
$extension = strtolower($pathinfo['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,
|
|
||||||
'thumb' => $thumb_filename,
|
|
||||||
'ordering' => $ordering))) {
|
|
||||||
if(self::generateThumb($db->lastInsertId(), $image, $errors))
|
|
||||||
self::resize($image, 650, 500, $filename, $errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$errors[] = 'Please fill all inputs.';
|
|
||||||
|
|
||||||
return !count($errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
static public function get($id) {
|
|
||||||
return ModelsGallery::find($id)->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
static public function update($id, $comment, $image, $author) {
|
|
||||||
$pathinfo = pathinfo($image);
|
|
||||||
$extension = strtolower($pathinfo['extension']);
|
|
||||||
$filename = GALLERY_DIR . $pathinfo['filename'] . '.' . $extension;
|
|
||||||
|
|
||||||
if(ModelsGallery::where('id', $id)->update([
|
|
||||||
'comment' => $comment,
|
|
||||||
'image' => $filename,
|
|
||||||
'author' => $author
|
|
||||||
])) {
|
|
||||||
if(self::generateThumb($id, $image, $errors))
|
|
||||||
self::resize($image, 650, 500, $filename, $errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static public function delete($id, &$errors)
|
|
||||||
{
|
|
||||||
if(isset($id))
|
|
||||||
{
|
|
||||||
$row = ModelsGallery::find($id);
|
|
||||||
if($row)
|
|
||||||
if (!$row->delete()) {
|
|
||||||
$errors[] = 'Fail during delete Gallery';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$errors[] = 'Image with id ' . $id . ' does not exists.';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$errors[] = 'id not set';
|
|
||||||
|
|
||||||
return !count($errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
static public function toggleHide($id, &$errors)
|
|
||||||
{
|
|
||||||
if(isset($id))
|
|
||||||
{
|
|
||||||
$row = ModelsGallery::find($id);
|
|
||||||
if($row) {
|
|
||||||
$row->hide = $row->hide == 1 ? 0 : 1;
|
|
||||||
if (!$row->save()) {
|
|
||||||
$errors[] = 'Fail during toggle hide Gallery';
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
$errors[] = 'Image with id ' . $id . ' does not exists.';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$errors[] = 'id not set';
|
|
||||||
|
|
||||||
return !count($errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
static public function move($id, $i, &$errors)
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
$query = self::get($id);
|
|
||||||
if($query !== false)
|
|
||||||
{
|
|
||||||
$ordering = $query['ordering'] + $i;
|
|
||||||
$old_record = $db->select(TABLE_PREFIX . 'gallery', array('ordering' => $ordering));
|
|
||||||
if($old_record !== false) {
|
|
||||||
ModelsGallery::where('ordering', $ordering)->update([
|
|
||||||
'ordering' => $query['ordering'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelsGallery::where('id', $id)->update([
|
|
||||||
'ordering' => $ordering,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$errors[] = 'Image with id ' . $id . ' does not exists.';
|
|
||||||
|
|
||||||
return !count($errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
static public function resize($file, $new_width, $new_height, $new_file, &$errors)
|
|
||||||
{
|
|
||||||
$pathinfo = pathinfo($file);
|
|
||||||
$extension = strtolower($pathinfo['extension']);
|
|
||||||
|
|
||||||
switch ($extension)
|
|
||||||
{
|
|
||||||
case 'gif': // GIF
|
|
||||||
$image = imagecreatefromgif($file);
|
|
||||||
break;
|
|
||||||
case 'jpg': // JPEG
|
|
||||||
case 'jpeg':
|
|
||||||
$image = imagecreatefromjpeg($file);
|
|
||||||
break;
|
|
||||||
case 'png': // PNG
|
|
||||||
$image = imagecreatefrompng($file);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$errors[] = 'Unsupported file format.';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$width = imagesx($image);
|
|
||||||
$height = imagesy($image);
|
|
||||||
|
|
||||||
// create a new temporary image
|
|
||||||
$tmp_img = imagecreatetruecolor($new_width, $new_height);
|
|
||||||
|
|
||||||
// copy and resize old image into new image
|
|
||||||
imagecopyresized($tmp_img, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
|
|
||||||
|
|
||||||
// save thumbnail into a file
|
|
||||||
switch($extension)
|
|
||||||
{
|
|
||||||
case 'gif':
|
|
||||||
imagegif($tmp_img, $new_file);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'jpg':
|
|
||||||
case 'jpeg':
|
|
||||||
imagejpeg($tmp_img, $new_file);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'png':
|
|
||||||
imagepng($tmp_img, $new_file);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static public function generateThumb($id, $file, &$errors)
|
|
||||||
{
|
|
||||||
$pathinfo = pathinfo($file);
|
|
||||||
$extension = strtolower($pathinfo['extension']);
|
|
||||||
$thumb_filename = GALLERY_DIR . $pathinfo['filename'] . '_thumb.' . $extension;
|
|
||||||
|
|
||||||
if(!self::resize($file, 170, 110, $thumb_filename, $errors))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(isset($id))
|
|
||||||
{
|
|
||||||
$row = ModelsGallery::find($id);
|
|
||||||
if($row) {
|
|
||||||
$row->thumb = $thumb_filename;
|
|
||||||
$row->save();
|
|
||||||
} else
|
|
||||||
$errors[] = 'Image with id ' . $id . ' does not exists.';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$errors[] = 'id not set';
|
|
||||||
|
|
||||||
return !count($errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use MyAAC\Cache\Cache;
|
|||||||
use MyAAC\Models\Player;
|
use MyAAC\Models\Player;
|
||||||
use MyAAC\Models\PlayerDeath;
|
use MyAAC\Models\PlayerDeath;
|
||||||
use MyAAC\Models\PlayerKillers;
|
use MyAAC\Models\PlayerKillers;
|
||||||
|
use MyAAC\Server\XML\Vocations;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
$title = 'Highscores';
|
$title = 'Highscores';
|
||||||
@@ -35,24 +36,20 @@ if(!is_numeric($page) || $page < 1 || $page > PHP_INT_MAX) {
|
|||||||
$query = Player::query();
|
$query = Player::query();
|
||||||
|
|
||||||
$configVocations = config('vocations');
|
$configVocations = config('vocations');
|
||||||
$configVocationsAmount = config('vocations_amount');
|
|
||||||
|
|
||||||
$vocationId = null;
|
$vocationId = null;
|
||||||
if($vocation !== 'all') {
|
if($vocation !== 'all') {
|
||||||
foreach($configVocations as $id => $name) {
|
foreach($configVocations as $id => $name) {
|
||||||
if(strtolower($name) == $vocation) {
|
if(strtolower($name) == $vocation) {
|
||||||
$vocationId = $id;
|
$vocationId = $id;
|
||||||
$add_vocs = [$id];
|
$filterVocations = [$id];
|
||||||
|
|
||||||
if ($id !== 0) {
|
while($tmpVoc = Vocations::getPromoted($id)) {
|
||||||
$i = $id + $configVocationsAmount;
|
$id = $tmpVoc;
|
||||||
while (isset($configVocations[$i])) {
|
$filterVocations[] = $tmpVoc;
|
||||||
$add_vocs[] = $i;
|
|
||||||
$i += $configVocationsAmount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$query->whereIn('players.vocation', $add_vocs);
|
$query->whereIn('players.vocation', $filterVocations);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,4 +322,5 @@ $twig->display('highscores.html.twig', [
|
|||||||
'page' => $page,
|
'page' => $page,
|
||||||
'baseLink' => $baseLink,
|
'baseLink' => $baseLink,
|
||||||
'updatedAt' => $updatedAt,
|
'updatedAt' => $updatedAt,
|
||||||
|
'baseVocations' => Vocations::getBase(true),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
use MyAAC\Cache\Cache;
|
use MyAAC\Cache\Cache;
|
||||||
use MyAAC\Models\ServerConfig;
|
use MyAAC\Models\ServerConfig;
|
||||||
use MyAAC\Models\ServerRecord;
|
use MyAAC\Models\ServerRecord;
|
||||||
|
use MyAAC\Server\XML\Vocations;
|
||||||
|
|
||||||
defined('MYAAC') or die('Direct access not allowed!');
|
defined('MYAAC') or die('Direct access not allowed!');
|
||||||
$title = 'Who is online?';
|
$title = 'Who is online?';
|
||||||
@@ -56,15 +57,14 @@ $cached = Cache::remember("online_$order", setting('core.online_cache_ttl') * 60
|
|||||||
|
|
||||||
$vocations = array_map(function ($name) {
|
$vocations = array_map(function ($name) {
|
||||||
return 0;
|
return 0;
|
||||||
}, setting('core.vocations'));
|
}, config('vocations'));
|
||||||
|
|
||||||
if($db->hasTable('players_online')) // tfs 1.0
|
if($db->hasTable('players_online')) // tfs 1.0
|
||||||
$playersOnline = $db->query('SELECT `accounts`.`country`, `players`.`name`, `players`.`level`, `players`.`vocation`' . $outfit . ', `' . $skull_time . '` as `skulltime`, `' . $skull_type . '` as `skull` FROM `accounts`, `players`, `players_online` WHERE `players`.`id` = `players_online`.`player_id` AND `accounts`.`id` = `players`.`account_id` ORDER BY ' . $orderSql);
|
$playersOnline = $db->query('SELECT `accounts`.`country`, `players`.`name`, `players`.`level`, `players`.`vocation`' . $outfit . ', `' . $skull_time . '` as `skulltime`, `' . $skull_type . '` as `skull` FROM `accounts`, `players`, `players_online` WHERE `players`.`id` = `players_online`.`player_id` AND `accounts`.`id` = `players`.`account_id` ORDER BY ' . $orderSql);
|
||||||
else
|
else
|
||||||
$playersOnline = $db->query('SELECT `accounts`.`country`, `players`.`name`, `players`.`level`, `players`.`vocation`' . $outfit . ', ' . $promotion . ' `' . $skull_time . '` as `skulltime`, `' . $skull_type . '` as `skull` FROM `accounts`, `players` WHERE `players`.`online` > 0 AND `accounts`.`id` = `players`.`account_id` ORDER BY ' . $orderSql);
|
$playersOnline = $db->query('SELECT `accounts`.`country`, `players`.`name`, `players`.`level`, `players`.`vocation`' . $outfit . ', ' . $promotion . ' `' . $skull_time . '` as `skulltime`, `' . $skull_type . '` as `skull` FROM `accounts`, `players` WHERE `players`.`online` > 0 AND `accounts`.`id` = `players`.`account_id` ORDER BY ' . $orderSql);
|
||||||
|
|
||||||
$settingVocations = setting('core.vocations');
|
$configVocations = config('vocations');
|
||||||
$settingVocationsAmount = setting('core.vocations_amount');
|
|
||||||
|
|
||||||
$players = [];
|
$players = [];
|
||||||
foreach($playersOnline as $player) {
|
foreach($playersOnline as $player) {
|
||||||
@@ -81,22 +81,19 @@ $cached = Cache::remember("online_$order", setting('core.online_cache_ttl') * 60
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isset($player['promotion'])) {
|
$player['vocation'] = OTS_Toolbox::getVocationFromPromotion($player['vocation'], $player['promotion'] ?? 0);
|
||||||
if((int)$player['promotion'] > 0)
|
|
||||||
$player['vocation'] += ($player['promotion'] * $settingVocationsAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
$players[] = array(
|
$players[] = array(
|
||||||
'name' => getPlayerLink($player['name']),
|
'name' => getPlayerLink($player['name']),
|
||||||
'player' => $player,
|
'player' => $player,
|
||||||
'level' => $player['level'],
|
'level' => $player['level'],
|
||||||
'vocation' => $settingVocations[$player['vocation']],
|
'vocation' => $configVocations[$player['vocation']],
|
||||||
'skull' => $skull,
|
'skull' => $skull,
|
||||||
'country_image' => getFlagImage($player['country']),
|
'country_image' => getFlagImage($player['country']),
|
||||||
'outfit' => setting('core.outfit_images_url') . '?id=' . $player['looktype'] . ($outfit_addons ? '&addons=' . $player['lookaddons'] : '') . '&head=' . $player['lookhead'] . '&body=' . $player['lookbody'] . '&legs=' . $player['looklegs'] . '&feet=' . $player['lookfeet'],
|
'outfit' => setting('core.outfit_images_url') . '?id=' . $player['looktype'] . ($outfit_addons ? '&addons=' . $player['lookaddons'] : '') . '&head=' . $player['lookhead'] . '&body=' . $player['lookbody'] . '&legs=' . $player['looklegs'] . '&feet=' . $player['lookfeet'],
|
||||||
);
|
);
|
||||||
|
|
||||||
$vocations[($player['vocation'] > $settingVocationsAmount ? $player['vocation'] - $settingVocationsAmount : $player['vocation'])]++;
|
$vocations[Vocations::getOriginal($player['vocation'])]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
$record = '';
|
$record = '';
|
||||||
@@ -142,6 +139,7 @@ $twig->display('online.html.twig', array(
|
|||||||
'vocations' => $cached['vocations'],
|
'vocations' => $cached['vocations'],
|
||||||
'vocs' => $cached['vocations'], // deprecated, to be removed
|
'vocs' => $cached['vocations'], // deprecated, to be removed
|
||||||
'order' => $order,
|
'order' => $order,
|
||||||
|
'baseVocations' => Vocations::getBase(false),
|
||||||
));
|
));
|
||||||
|
|
||||||
// search bar
|
// search bar
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
* @link https://my-aac.org
|
* @link https://my-aac.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use MyAAC\Models\Menu;
|
||||||
use MyAAC\Models\Pages;
|
use MyAAC\Models\Pages;
|
||||||
use MyAAC\Plugins;
|
use MyAAC\Plugins;
|
||||||
|
|
||||||
@@ -331,7 +332,20 @@ else {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$found) {
|
$tmpPageOriginal = $page;
|
||||||
|
$pagesWithDynamicPart = ['characters', 'forum', 'highscores', 'monsters'];
|
||||||
|
foreach ($pagesWithDynamicPart as $_page) {
|
||||||
|
if (str_contains($page, $_page)) {
|
||||||
|
$tmpPageOriginal = $_page;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$themeMenu = Menu::select(['name'])
|
||||||
|
->where('template', $template_name)
|
||||||
|
->where('link', $tmpPageOriginal)
|
||||||
|
->where('access', '>', $logged_access);
|
||||||
|
|
||||||
|
if (!$found || $themeMenu->count() >= 1) {
|
||||||
$page = '404';
|
$page = '404';
|
||||||
$file = SYSTEM . 'pages/404.php';
|
$file = SYSTEM . 'pages/404.php';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ return [
|
|||||||
'footer' => [
|
'footer' => [
|
||||||
'name' => 'Custom Text',
|
'name' => 'Custom Text',
|
||||||
'type' => 'textarea',
|
'type' => 'textarea',
|
||||||
'desc' => 'Text displayed in the footer.<br/>For example: <i>' . escapeHtml('<br/>') . 'Your Server © 2023. All rights reserved.</i>',
|
'desc' => 'Text displayed in the footer.<br/>For example: <i>' . escapeHtml('<br/>') . 'Your Server © ' . date("Y") . '. All rights reserved.</i>',
|
||||||
'default' => '',
|
'default' => '',
|
||||||
],
|
],
|
||||||
'footer_load_time' => [
|
'footer_load_time' => [
|
||||||
@@ -219,7 +219,14 @@ return [
|
|||||||
'cache_engine' => [
|
'cache_engine' => [
|
||||||
'name' => 'Cache Engine',
|
'name' => 'Cache Engine',
|
||||||
'type' => 'options',
|
'type' => 'options',
|
||||||
'options' => ['auto' => 'Auto', 'file' => 'Files', 'apc' => 'APC', 'apcu' => 'APCu', 'disable' => 'Disable'],
|
'options' => [
|
||||||
|
'auto' => 'Auto',
|
||||||
|
'file' => 'Files',
|
||||||
|
'apc' => 'APC',
|
||||||
|
'apcu' => 'APCu',
|
||||||
|
'php' => 'PHP',
|
||||||
|
'disable' => 'Disable',
|
||||||
|
],
|
||||||
'desc' => 'Auto is most reasonable. It will detect the best cache engine',
|
'desc' => 'Auto is most reasonable. It will detect the best cache engine',
|
||||||
'default' => 'auto',
|
'default' => 'auto',
|
||||||
'is_config' => true,
|
'is_config' => true,
|
||||||
@@ -251,6 +258,28 @@ return [
|
|||||||
'desc' => 'Allow MyAAC to report anonymous usage statistics to developers? The data is sent only once per 30 days and is fully confidential. It won\'t affect the performance of your website',
|
'desc' => 'Allow MyAAC to report anonymous usage statistics to developers? The data is sent only once per 30 days and is fully confidential. It won\'t affect the performance of your website',
|
||||||
'default' => true,
|
'default' => true,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'type' => 'section',
|
||||||
|
'title' => 'Custom HTML',
|
||||||
|
],
|
||||||
|
'html_head' => [
|
||||||
|
'name' => 'HTML Head',
|
||||||
|
'type' => 'textarea',
|
||||||
|
'desc' => escapeHtml('These scripts will be printed in the <head> section. Can be, for example, Google Analytics code.'),
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
'html_body' => [
|
||||||
|
'name' => 'HTML Body',
|
||||||
|
'type' => 'textarea',
|
||||||
|
'desc' => escapeHtml('These scripts will be printed just below the opening <body> tag.'),
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
|
'html_footer' => [
|
||||||
|
'name' => 'HTML Footer',
|
||||||
|
'type' => 'textarea',
|
||||||
|
'desc' => escapeHtml('These scripts will be printed above the closing </body> tag.'),
|
||||||
|
'default' => '',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'type' => 'category',
|
'type' => 'category',
|
||||||
'title' => 'Game',
|
'title' => 'Game',
|
||||||
@@ -312,20 +341,31 @@ return [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* To be removed in v3.0
|
||||||
|
*/
|
||||||
'vocations_amount' => [
|
'vocations_amount' => [
|
||||||
'name' => 'Vocations Amount',
|
'hidden' => true,
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'desc' => 'How much basic vocations your server got (without promotion)',
|
//'name' => 'Vocations Amount',
|
||||||
|
//'desc' => 'How many basic vocations your server got (without promotion)',
|
||||||
'default' => 4,
|
'default' => 4,
|
||||||
|
'callbacks' => [
|
||||||
|
'get' => function () {
|
||||||
|
return config('vocations_amount');
|
||||||
|
},
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'vocations' => [
|
'vocations' => [
|
||||||
'name' => 'Vocation Names',
|
'hidden' => true,
|
||||||
'type' => 'textarea',
|
'type' => 'textarea',
|
||||||
'desc' => 'Separated by comma. Must be in the same order as in vocations.xml, starting with id: 0.',
|
//'name' => 'Vocation Names',
|
||||||
|
//'desc' => 'Separated by comma. Must be in the same order as in vocations.xml, starting with id: 0.',
|
||||||
'default' => 'None, Sorcerer, Druid, Paladin, Knight, Master Sorcerer, Elder Druid,Royal Paladin, Elite Knight',
|
'default' => 'None, Sorcerer, Druid, Paladin, Knight, Master Sorcerer, Elder Druid,Royal Paladin, Elite Knight',
|
||||||
'callbacks' => [
|
'callbacks' => [
|
||||||
'get' => function ($value) {
|
'get' => function () {
|
||||||
return array_map('trim', explode(',', $value));
|
return config('vocations');
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -1704,6 +1744,18 @@ Sent by MyAAC,<br/>
|
|||||||
'account_login_ipban_protection', '=', 'true'
|
'account_login_ipban_protection', '=', 'true'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
|
[
|
||||||
|
'type' => 'section',
|
||||||
|
'title' => 'Cooldowns',
|
||||||
|
],
|
||||||
|
'account_create_ip_block_cooldown' => [
|
||||||
|
'name' => 'Create Account IP Block Cooldown',
|
||||||
|
'type' => 'number',
|
||||||
|
'desc' => 'Block flooding create account per ip. If you still have a problem with account create spam - then its recommended to install the recaptcha plugin.' .
|
||||||
|
'<br/><strong>In minutes.</strong> 0 to disable.',
|
||||||
|
'default' => 10,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'callbacks' => [
|
'callbacks' => [
|
||||||
'beforeSave' => function(&$settings, &$values) {
|
'beforeSave' => function(&$settings, &$values) {
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ namespace MyAAC\Cache;
|
|||||||
|
|
||||||
class APC
|
class APC
|
||||||
{
|
{
|
||||||
private $prefix;
|
private string $prefix;
|
||||||
private $enabled;
|
private bool $enabled;
|
||||||
|
|
||||||
public function __construct($prefix = '')
|
public function __construct($prefix = '')
|
||||||
{
|
{
|
||||||
@@ -22,14 +22,14 @@ class APC
|
|||||||
$this->enabled = function_exists('apc_fetch');
|
$this->enabled = function_exists('apc_fetch');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function set($key, $var, $ttl = 0)
|
public function set($key, $var, $ttl = 0): void
|
||||||
{
|
{
|
||||||
$key = $this->prefix . $key;
|
$key = $this->prefix . $key;
|
||||||
apc_delete($key);
|
apc_delete($key);
|
||||||
apc_store($key, $var, $ttl);
|
apc_store($key, $var, $ttl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get($key)
|
public function get($key): string
|
||||||
{
|
{
|
||||||
$tmp = '';
|
$tmp = '';
|
||||||
if ($this->fetch($this->prefix . $key, $tmp)) {
|
if ($this->fetch($this->prefix . $key, $tmp)) {
|
||||||
@@ -39,18 +39,15 @@ class APC
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fetch($key, &$var)
|
public function fetch($key, &$var): bool {
|
||||||
{
|
|
||||||
return ($var = apc_fetch($this->prefix . $key)) !== false;
|
return ($var = apc_fetch($this->prefix . $key)) !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete($key)
|
public function delete($key): void {
|
||||||
{
|
|
||||||
apc_delete($this->prefix . $key);
|
apc_delete($this->prefix . $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function enabled()
|
public function enabled(): bool {
|
||||||
{
|
|
||||||
return $this->enabled;
|
return $this->enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ namespace MyAAC\Cache;
|
|||||||
|
|
||||||
class APCu
|
class APCu
|
||||||
{
|
{
|
||||||
private $prefix;
|
private string $prefix;
|
||||||
private $enabled;
|
private bool $enabled;
|
||||||
|
|
||||||
public function __construct($prefix = '')
|
public function __construct($prefix = '')
|
||||||
{
|
{
|
||||||
@@ -22,14 +22,14 @@ class APCu
|
|||||||
$this->enabled = function_exists('apcu_fetch');
|
$this->enabled = function_exists('apcu_fetch');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function set($key, $var, $ttl = 0)
|
public function set($key, $var, $ttl = 0): void
|
||||||
{
|
{
|
||||||
$key = $this->prefix . $key;
|
$key = $this->prefix . $key;
|
||||||
apcu_delete($key);
|
apcu_delete($key);
|
||||||
apcu_store($key, $var, $ttl);
|
apcu_store($key, $var, $ttl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get($key)
|
public function get($key): string
|
||||||
{
|
{
|
||||||
$tmp = '';
|
$tmp = '';
|
||||||
if ($this->fetch($this->prefix . $key, $tmp)) {
|
if ($this->fetch($this->prefix . $key, $tmp)) {
|
||||||
@@ -39,18 +39,15 @@ class APCu
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fetch($key, &$var)
|
public function fetch($key, &$var): bool {
|
||||||
{
|
|
||||||
return ($var = apcu_fetch($this->prefix . $key)) !== false;
|
return ($var = apcu_fetch($this->prefix . $key)) !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete($key)
|
public function delete($key): void {
|
||||||
{
|
|
||||||
apcu_delete($this->prefix . $key);
|
apcu_delete($this->prefix . $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function enabled()
|
public function enabled(): bool {
|
||||||
{
|
|
||||||
return $this->enabled;
|
return $this->enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,35 +47,15 @@ class Cache
|
|||||||
return self::$instance;
|
return self::$instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (strtolower($engine)) {
|
self::$instance = match (strtolower($engine)) {
|
||||||
case 'apc':
|
'apc' => new APC($prefix),
|
||||||
self::$instance = new APC($prefix);
|
'apcu' => new APCu($prefix),
|
||||||
break;
|
'xcache' => new XCache($prefix),
|
||||||
|
'file' => new File($prefix, CACHE),
|
||||||
case 'apcu':
|
'php' => new PHP($prefix, CACHE),
|
||||||
self::$instance = new APCu($prefix);
|
'auto' => self::generateInstance(self::detect(), $prefix),
|
||||||
break;
|
default => new self(),
|
||||||
|
};
|
||||||
case 'xcache':
|
|
||||||
self::$instance = new XCache($prefix);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'file':
|
|
||||||
self::$instance = new File($prefix, CACHE);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'php':
|
|
||||||
self::$instance = new PHP($prefix, CACHE);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'auto':
|
|
||||||
self::$instance = self::generateInstance(self::detect(), $prefix);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
self::$instance = new self();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::$instance;
|
return self::$instance;
|
||||||
}
|
}
|
||||||
@@ -83,7 +63,7 @@ class Cache
|
|||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function detect()
|
public static function detect(): string
|
||||||
{
|
{
|
||||||
if (function_exists('apc_fetch'))
|
if (function_exists('apc_fetch'))
|
||||||
return 'apc';
|
return 'apc';
|
||||||
@@ -98,8 +78,7 @@ class Cache
|
|||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function enabled()
|
public function enabled(): bool {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,18 +12,22 @@ namespace MyAAC\Cache;
|
|||||||
|
|
||||||
class File
|
class File
|
||||||
{
|
{
|
||||||
private $prefix;
|
private string $prefix;
|
||||||
private $dir;
|
private string $dir;
|
||||||
private $enabled;
|
private bool $enabled;
|
||||||
|
|
||||||
public function __construct($prefix = '', $dir = '')
|
public function __construct($prefix = '', $dir = '')
|
||||||
{
|
{
|
||||||
$this->prefix = $prefix;
|
$this->prefix = $prefix;
|
||||||
$this->dir = $dir;
|
$this->dir = $dir;
|
||||||
|
|
||||||
|
ensureFolderExists($this->dir);
|
||||||
|
ensureIndexExists($this->dir);
|
||||||
|
|
||||||
$this->enabled = (file_exists($this->dir) && is_dir($this->dir) && is_writable($this->dir));
|
$this->enabled = (file_exists($this->dir) && is_dir($this->dir) && is_writable($this->dir));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function set($key, $var, $ttl = 0)
|
public function set($key, $var, $ttl = 0): void
|
||||||
{
|
{
|
||||||
$file = $this->_name($key);
|
$file = $this->_name($key);
|
||||||
file_put_contents($file, $var);
|
file_put_contents($file, $var);
|
||||||
@@ -35,7 +39,7 @@ class File
|
|||||||
touch($file, time() + $ttl);
|
touch($file, time() + $ttl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get($key)
|
public function get($key): string
|
||||||
{
|
{
|
||||||
$tmp = '';
|
$tmp = '';
|
||||||
if ($this->fetch($key, $tmp)) {
|
if ($this->fetch($key, $tmp)) {
|
||||||
@@ -45,7 +49,7 @@ class File
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fetch($key, &$var)
|
public function fetch($key, &$var): bool
|
||||||
{
|
{
|
||||||
$file = $this->_name($key);
|
$file = $this->_name($key);
|
||||||
if (!file_exists($file) || filemtime($file) < time()) {
|
if (!file_exists($file) || filemtime($file) < time()) {
|
||||||
@@ -56,7 +60,7 @@ class File
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete($key)
|
public function delete($key): void
|
||||||
{
|
{
|
||||||
$file = $this->_name($key);
|
$file = $this->_name($key);
|
||||||
if (file_exists($file)) {
|
if (file_exists($file)) {
|
||||||
@@ -64,13 +68,11 @@ class File
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function enabled()
|
public function enabled(): bool {
|
||||||
{
|
|
||||||
return $this->enabled;
|
return $this->enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function _name($key)
|
private function _name($key): string {
|
||||||
{
|
|
||||||
return sprintf('%s%s%s', $this->dir, $this->prefix, sha1($key));
|
return sprintf('%s%s%s', $this->dir, $this->prefix, sha1($key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,36 +12,37 @@ namespace MyAAC\Cache;
|
|||||||
|
|
||||||
class PHP
|
class PHP
|
||||||
{
|
{
|
||||||
private $prefix;
|
private string $prefix;
|
||||||
private $dir;
|
private string $dir;
|
||||||
private $enabled;
|
private bool $enabled;
|
||||||
|
|
||||||
public function __construct($prefix = '', $dir = '')
|
public function __construct($prefix = '', $dir = '')
|
||||||
{
|
{
|
||||||
$this->prefix = $prefix;
|
$this->prefix = $prefix;
|
||||||
$this->dir = $dir;
|
$this->dir = $dir;
|
||||||
$this->enabled = (file_exists($this->dir) && is_dir($this->dir) && is_writable($this->dir));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function set($key, $var, $ttl = 0)
|
|
||||||
{
|
|
||||||
$var = var_export($var, true);
|
|
||||||
|
|
||||||
ensureFolderExists($this->dir);
|
ensureFolderExists($this->dir);
|
||||||
ensureIndexExists($this->dir);
|
ensureIndexExists($this->dir);
|
||||||
|
|
||||||
// Write to temp file first to ensure atomicity
|
$this->enabled = (file_exists($this->dir) && is_dir($this->dir) && is_writable($this->dir));
|
||||||
$tmp = $this->dir . "tmp_$key." . uniqid('', true) . '.tmp';
|
}
|
||||||
file_put_contents($tmp, '<?php $var = ' . $var . ';', LOCK_EX);
|
|
||||||
|
|
||||||
$file = $this->_name($key);
|
public function set($key, $var, $ttl = 0): void
|
||||||
rename($tmp, $file);
|
{
|
||||||
|
$var = var_export($var, true);
|
||||||
|
|
||||||
if ($ttl === 0) {
|
if ($ttl === 0) {
|
||||||
$ttl = 365 * 24 * 60 * 60; // 365 days
|
$ttl = 365 * 24 * 60 * 60; // 365 days
|
||||||
}
|
}
|
||||||
|
|
||||||
touch($file, time() + $ttl);
|
$expires = time() + $ttl;
|
||||||
|
|
||||||
|
// Write to temp file first to ensure atomicity
|
||||||
|
$tmp = $this->dir . "tmp_$key." . uniqid('', true) . '.tmp';
|
||||||
|
file_put_contents($tmp, "<?php return ['expires' => $expires, 'var' => $var];", LOCK_EX);
|
||||||
|
|
||||||
|
$file = $this->_name($key);
|
||||||
|
rename($tmp, $file);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get($key)
|
public function get($key)
|
||||||
@@ -54,19 +55,23 @@ class PHP
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fetch($key, &$var)
|
public function fetch($key, &$var): bool
|
||||||
{
|
{
|
||||||
$file = $this->_name($key);
|
$file = $this->_name($key);
|
||||||
if (!file_exists($file) || filemtime($file) < time()) {
|
if (!file_exists($file)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include $file;
|
$content = include $file;
|
||||||
$var = isset($var) ? $var : null;
|
if (!isset($content) || $content['expires'] < time()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$var = $content['var'];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete($key)
|
public function delete($key): void
|
||||||
{
|
{
|
||||||
$file = $this->_name($key);
|
$file = $this->_name($key);
|
||||||
if (file_exists($file)) {
|
if (file_exists($file)) {
|
||||||
@@ -74,13 +79,11 @@ class PHP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function enabled()
|
public function enabled(): bool {
|
||||||
{
|
|
||||||
return $this->enabled;
|
return $this->enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function _name($key)
|
private function _name($key): string {
|
||||||
{
|
|
||||||
return sprintf('%s%s%s', $this->dir, $this->prefix, sha1($key) . '.php');
|
return sprintf('%s%s%s', $this->dir, $this->prefix, sha1($key) . '.php');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ namespace MyAAC\Cache;
|
|||||||
|
|
||||||
class XCache
|
class XCache
|
||||||
{
|
{
|
||||||
private $prefix;
|
private string $prefix;
|
||||||
private $enabled;
|
private bool $enabled;
|
||||||
|
|
||||||
public function __construct($prefix = '')
|
public function __construct($prefix = '')
|
||||||
{
|
{
|
||||||
@@ -22,14 +22,14 @@ class XCache
|
|||||||
$this->enabled = function_exists('xcache_get') && ini_get('xcache.var_size');
|
$this->enabled = function_exists('xcache_get') && ini_get('xcache.var_size');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function set($key, $var, $ttl = 0)
|
public function set($key, $var, $ttl = 0): void
|
||||||
{
|
{
|
||||||
$key = $this->prefix . $key;
|
$key = $this->prefix . $key;
|
||||||
xcache_unset($key);
|
xcache_unset($key);
|
||||||
xcache_set($key, $var, $ttl);
|
xcache_set($key, $var, $ttl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get($key)
|
public function get($key): string
|
||||||
{
|
{
|
||||||
$tmp = '';
|
$tmp = '';
|
||||||
if ($this->fetch($this->prefix . $key, $tmp)) {
|
if ($this->fetch($this->prefix . $key, $tmp)) {
|
||||||
@@ -39,7 +39,7 @@ class XCache
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fetch($key, &$var)
|
public function fetch($key, &$var): bool
|
||||||
{
|
{
|
||||||
$key = $this->prefix . $key;
|
$key = $this->prefix . $key;
|
||||||
if (!xcache_isset($key)) {
|
if (!xcache_isset($key)) {
|
||||||
@@ -50,13 +50,11 @@ class XCache
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete($key)
|
public function delete($key): void {
|
||||||
{
|
|
||||||
xcache_unset($this->prefix . $key);
|
xcache_unset($this->prefix . $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function enabled()
|
public function enabled(): bool {
|
||||||
{
|
|
||||||
return $this->enabled;
|
return $this->enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
50
system/src/Commands/GiveAdminCommand.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\Commands;
|
||||||
|
|
||||||
|
use MyAAC\Plugins;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
|
||||||
|
class GiveAdminCommand extends Command
|
||||||
|
{
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
$this->setName('give:admin')
|
||||||
|
->setDescription('This command adds super admin privileges to selected user')
|
||||||
|
->addArgument('account', InputArgument::REQUIRED, 'Account E-Mail, name or id');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
require SYSTEM . 'init.php';
|
||||||
|
|
||||||
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
|
||||||
|
$account = new \OTS_Account();
|
||||||
|
|
||||||
|
$accountParam = $input->getArgument('account');
|
||||||
|
if (str_contains($accountParam, '@')) {
|
||||||
|
$account->findByEMail($accountParam);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (USE_ACCOUNT_NAME || USE_ACCOUNT_NUMBER) {
|
||||||
|
$account->find($accountParam);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$account->load($accountParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$account->isLoaded()) {
|
||||||
|
$io->error('Cannot find account mit supplied parameter: ' . $accountParam);
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$account->setCustomField('web_flags', 3);
|
||||||
|
$io->success('Successfully added admin privileges to ' . $accountParam . ' (E-Mail: ' . $account->getEMail() . ')');
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -149,7 +149,10 @@ class CreateCharacter
|
|||||||
if($db->hasColumn('players', 'direction'))
|
if($db->hasColumn('players', 'direction'))
|
||||||
$player->setDirection($playerSample->getDirection());
|
$player->setDirection($playerSample->getDirection());
|
||||||
|
|
||||||
$player->setConditions($playerSample->getConditions());
|
if($db->hasColumn('players', 'conditions')) {
|
||||||
|
$player->setConditions($playerSample->getConditions());
|
||||||
|
}
|
||||||
|
|
||||||
$rank = $playerSample->getRank();
|
$rank = $playerSample->getRank();
|
||||||
if($rank->isLoaded()) {
|
if($rank->isLoaded()) {
|
||||||
$player->setRank($playerSample->getRank());
|
$player->setRank($playerSample->getRank());
|
||||||
@@ -183,7 +186,11 @@ class CreateCharacter
|
|||||||
$player->setLookHead($playerSample->getLookHead());
|
$player->setLookHead($playerSample->getLookHead());
|
||||||
$player->setLookLegs($playerSample->getLookLegs());
|
$player->setLookLegs($playerSample->getLookLegs());
|
||||||
$player->setLookType($playerSample->getLookType());
|
$player->setLookType($playerSample->getLookType());
|
||||||
$player->setCap($playerSample->getCap());
|
|
||||||
|
if($db->hasColumn('players', 'cap')) {
|
||||||
|
$player->setCap($playerSample->getCap());
|
||||||
|
}
|
||||||
|
|
||||||
$player->setBalance(0);
|
$player->setBalance(0);
|
||||||
$player->setPosX(0);
|
$player->setPosX(0);
|
||||||
$player->setPosY(0);
|
$player->setPosY(0);
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ class AccountAction extends Model {
|
|||||||
|
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
|
|
||||||
protected $fillable = ['account_id', 'ip', 'ipv6', 'date', 'action'];
|
protected $fillable = ['account_id', 'ip', 'date', 'action'];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
18
system/src/Models/AccountBan.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\Models;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class AccountBan extends Model {
|
||||||
|
|
||||||
|
protected $table = TABLE_PREFIX . 'account_bans';
|
||||||
|
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'account_id',
|
||||||
|
'reason', 'banned_at',
|
||||||
|
'expires_at', 'banned_by'
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
14
system/src/Models/AccountEMailCode.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\Models;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class AccountEMailCode extends Model {
|
||||||
|
|
||||||
|
protected $table = TABLE_PREFIX . 'account_email_codes';
|
||||||
|
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $fillable = ['account_id', 'code', 'created_at'];
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MyAAC\Models;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
class Gallery extends Model {
|
|
||||||
|
|
||||||
protected $table = TABLE_PREFIX . 'gallery';
|
|
||||||
|
|
||||||
public $timestamps = false;
|
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'comment', 'image', 'thumb',
|
|
||||||
'author', 'ordering', 'hide',
|
|
||||||
];
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -9,6 +9,6 @@ class Menu extends Model {
|
|||||||
|
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
|
|
||||||
protected $fillable = ['template', 'name', 'link', 'blank', 'color', 'category', 'ordering', 'enabled'];
|
protected $fillable = ['template', 'name', 'link', 'access', 'blank', 'color', 'category', 'ordering', 'enabled'];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ class Player extends Model {
|
|||||||
|
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $guarded = [];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'worldid' => 'integer',
|
'worldid' => 'integer',
|
||||||
'sex' => 'integer',
|
'sex' => 'integer',
|
||||||
@@ -46,14 +48,8 @@ class Player extends Model {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getVocationNameAttribute()
|
public function getVocationNameAttribute() {
|
||||||
{
|
return \OTS_Toolbox::getVocationName($this->vocation, $this->promotion ?? 0);
|
||||||
$vocation = $this->vocation;
|
|
||||||
if (isset($this->promotion) && $this->promotion > 0) {
|
|
||||||
$vocation += ($this->promotion * setting('core.vocations_amount'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return config('vocations')[$vocation] ?? 'Unknown';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getIsDeletedAttribute()
|
public function getIsDeletedAttribute()
|
||||||
|
|||||||
101
system/src/Server/XML/Vocations.php
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\Server\XML;
|
||||||
|
|
||||||
|
use MyAAC\Cache\Cache;
|
||||||
|
|
||||||
|
class Vocations
|
||||||
|
{
|
||||||
|
private static array $vocations;
|
||||||
|
private static array $vocationsFrom;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$cached = Cache::remember('vocations', 10 * 60, function () {
|
||||||
|
$this->load();
|
||||||
|
$from = $this->getFrom();
|
||||||
|
|
||||||
|
$amount = 0;
|
||||||
|
foreach ($from as $vocId => $fromVocation) {
|
||||||
|
if ($vocId != 0 && $vocId == $fromVocation) {
|
||||||
|
$amount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['vocations' => $this->get(), 'vocationsFrom' => $from, 'amount' => $amount];
|
||||||
|
});
|
||||||
|
|
||||||
|
self::$vocations = $cached['vocations'];
|
||||||
|
self::$vocationsFrom = $cached['vocationsFrom'];
|
||||||
|
|
||||||
|
config(['vocations', self::$vocations]);
|
||||||
|
config(['vocations_amount', $cached['amount']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function load(): void
|
||||||
|
{
|
||||||
|
if(!class_exists('DOMDocument')) {
|
||||||
|
throw new \RuntimeException('Please install PHP xml extension. MyAAC will not work without it.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$vocationsXML = new \DOMDocument();
|
||||||
|
$file = config('data_path') . 'XML/vocations.xml';
|
||||||
|
if(!@file_exists($file)) {
|
||||||
|
$file = config('data_path') . 'vocations.xml';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$vocationsXML->load($file)) {
|
||||||
|
throw new \RuntimeException('ERROR: Cannot load <i>vocations.xml</i> - the file is malformed. Check the file with xml syntax validator.');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($vocationsXML->getElementsByTagName('vocation') as $vocation) {
|
||||||
|
$id = $vocation->getAttribute('id');
|
||||||
|
|
||||||
|
self::$vocations[$id] = $vocation->getAttribute('name');
|
||||||
|
|
||||||
|
$fromVocation = (int) $vocation->getAttribute('fromvoc');
|
||||||
|
self::$vocationsFrom[$id] = $fromVocation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get(): array {
|
||||||
|
return self::$vocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getFrom(): array {
|
||||||
|
return self::$vocationsFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPromoted(int $id): ?int {
|
||||||
|
foreach (self::$vocationsFrom as $vocId => $fromVocation) {
|
||||||
|
if ($id == $fromVocation && $vocId != $id) {
|
||||||
|
return $vocId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getOriginal(int $id): ?int {
|
||||||
|
while ($tmpId = self::$vocationsFrom[$id]) {
|
||||||
|
if ($tmpId == $id) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $tmpId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getBase($includingRook = true): array {
|
||||||
|
$vocations = [];
|
||||||
|
foreach (self::$vocationsFrom as $vocId => $fromVoc) {
|
||||||
|
if ($vocId == $fromVoc && ($vocId != 0 || $includingRook)) {
|
||||||
|
$vocations[] = $vocId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $vocations;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -152,9 +152,10 @@ class Settings implements \ArrayAccess
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($setting['type'] === 'category') {
|
if ($setting['type'] === 'category') {
|
||||||
|
$title = strtolower(str_replace(' ', '', $setting['title']));
|
||||||
?>
|
?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link<?= ($i === 0 ? ' active' : ''); ?>" id="home-tab-<?= $i++; ?>" data-toggle="tab" href="#tab-<?= str_replace(' ', '', $setting['title']); ?>" type="button"><?= $setting['title']; ?></a>
|
<a class="nav-link<?= ($i === 0 ? ' active' : ''); ?>" id="home-tab-<?= $i++; ?>" data-toggle="tab" href="#tab-<?= $title; ?>" type="button"><?= $setting['title']; ?></a>
|
||||||
</li>
|
</li>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
@@ -175,8 +176,10 @@ class Settings implements \ArrayAccess
|
|||||||
if ($j++ !== 0) { // close previous category
|
if ($j++ !== 0) { // close previous category
|
||||||
echo '</tbody></table></div>';
|
echo '</tbody></table></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$title = strtolower(str_replace(' ', '', $setting['title']));
|
||||||
?>
|
?>
|
||||||
<div class="tab-pane fade show<?= ($j === 1 ? ' active' : ''); ?>" id="tab-<?= str_replace(' ', '', $setting['title']); ?>">
|
<div class="tab-pane fade show<?= ($j === 1 ? ' active' : ''); ?>" id="tab-<?= $title; ?>">
|
||||||
<?php
|
<?php
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
19
system/src/TwoFactorAuth/Gateway/AppAuthGateway.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\TwoFactorAuth\Gateway;
|
||||||
|
|
||||||
|
use MyAAC\TwoFactorAuth\Interface\AuthGatewayInterface;
|
||||||
|
use OTPHP\TOTP;
|
||||||
|
|
||||||
|
class AppAuthGateway extends BaseAuthGateway implements AuthGatewayInterface
|
||||||
|
{
|
||||||
|
public function verifyCode(string $code): bool
|
||||||
|
{
|
||||||
|
$otp = TOTP::createFromSecret($this->account->getCustomField('secret'));
|
||||||
|
|
||||||
|
$otp->setLabel($this->account->getEmail());
|
||||||
|
$otp->setIssuer(configLua('serverName'));
|
||||||
|
|
||||||
|
return $otp->verify($code);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
system/src/TwoFactorAuth/Gateway/BaseAuthGateway.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\TwoFactorAuth\Gateway;
|
||||||
|
|
||||||
|
class BaseAuthGateway
|
||||||
|
{
|
||||||
|
protected \OTS_Account $account;
|
||||||
|
|
||||||
|
public function __construct(\OTS_Account $account) {
|
||||||
|
$this->account = $account;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
system/src/TwoFactorAuth/Gateway/EmailAuthGateway.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\TwoFactorAuth\Gateway;
|
||||||
|
|
||||||
|
use MyAAC\Models\AccountEMailCode;
|
||||||
|
use MyAAC\TwoFactorAuth\Interface\AuthGatewayInterface;
|
||||||
|
use MyAAC\TwoFactorAuth\TwoFactorAuth;
|
||||||
|
|
||||||
|
class EmailAuthGateway extends BaseAuthGateway implements AuthGatewayInterface
|
||||||
|
{
|
||||||
|
public function verifyCode(string $code): bool
|
||||||
|
{
|
||||||
|
return AccountEMailCode::where('account_id', '=', $this->account->getId())->where('code', $code)->where('created_at', '>', time() - TwoFactorAuth::EMAIL_CODE_VALID_UNTIL)->first() !== null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\TwoFactorAuth\Interface;
|
||||||
|
|
||||||
|
interface AuthGatewayInterface
|
||||||
|
{
|
||||||
|
public function __construct(\OTS_Account $account);
|
||||||
|
public function verifyCode(string $code): bool;
|
||||||
|
}
|
||||||
270
system/src/TwoFactorAuth/TwoFactorAuth.php
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MyAAC\TwoFactorAuth;
|
||||||
|
|
||||||
|
use MyAAC\Models\AccountEMailCode;
|
||||||
|
use MyAAC\TwoFactorAuth\Gateway\AppAuthGateway;
|
||||||
|
use MyAAC\TwoFactorAuth\Gateway\EmailAuthGateway;
|
||||||
|
use OTPHP\TOTP;
|
||||||
|
|
||||||
|
class TwoFactorAuth
|
||||||
|
{
|
||||||
|
const TYPE_NONE = 0;
|
||||||
|
const TYPE_EMAIL = 1;
|
||||||
|
const TYPE_APP = 2;
|
||||||
|
// maybe later
|
||||||
|
//const TYPE_SMS = 3;
|
||||||
|
|
||||||
|
const EMAIL_CODE_VALID_UNTIL = 24 * 60 * 60;
|
||||||
|
|
||||||
|
private static self $instance;
|
||||||
|
|
||||||
|
private \OTS_Account $account;
|
||||||
|
private int $authType;
|
||||||
|
private EmailAuthGateway|AppAuthGateway $authGateway;
|
||||||
|
|
||||||
|
public function __construct(\OTS_Account|int $account) {
|
||||||
|
if (is_int($account)) {
|
||||||
|
$this->account = new \OTS_Account();
|
||||||
|
$this->account->load($account);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->account = $account;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->authType = (int)$this->account->getCustomField('2fa_type');
|
||||||
|
$this->setAuthGateway($this->authType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getInstance($account = null): self
|
||||||
|
{
|
||||||
|
if (!isset(self::$instance)) {
|
||||||
|
self::$instance = new self($account);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process($login_account, $login_password, $remember_me, $code): bool
|
||||||
|
{
|
||||||
|
global $twig;
|
||||||
|
|
||||||
|
if (!$this->isActive()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$view = 'app';
|
||||||
|
if ($this->authType == self::TYPE_EMAIL) {
|
||||||
|
$view = 'email';#
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($code)) {
|
||||||
|
if ($this->authType == self::TYPE_EMAIL) {
|
||||||
|
if (!$this->hasRecentEmailCode(15 * 60)) {
|
||||||
|
$this->resendEmailCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define('HIDE_LOGIN_BOX', true);
|
||||||
|
$twig->display("account/2fa/$view/login.html.twig", [
|
||||||
|
'account_login' => $login_account,
|
||||||
|
'password_login' => $login_password,
|
||||||
|
'remember_me' => $remember_me,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->getAuthGateway()->verifyCode($code)) {
|
||||||
|
if ($this->authType === self::TYPE_EMAIL) {
|
||||||
|
$this->deleteOldCodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setting('core.mail_enabled')) {
|
||||||
|
$mailBody = $twig->render('mail.account.2fa.email-code.wrong-attempt.html.twig');
|
||||||
|
|
||||||
|
if (!_mail($this->account->getEMail(), configLua('serverName') . ' - Failed Two-Factor Authentication Attempt', $mailBody)) {
|
||||||
|
error('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define('HIDE_LOGIN_BOX', true);
|
||||||
|
|
||||||
|
if ($this->authType == self::TYPE_APP) {
|
||||||
|
$errors[] = 'The token is invalid!';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$errors[] = 'Invalid E-Mail code!';
|
||||||
|
}
|
||||||
|
|
||||||
|
$twig->display('error_box.html.twig', ['errors' => $errors]);
|
||||||
|
|
||||||
|
$twig->display("account/2fa/$view/login.html.twig",
|
||||||
|
[
|
||||||
|
'account_login' => $login_account,
|
||||||
|
'password_login' => $login_password,
|
||||||
|
'remember_me' => $remember_me,
|
||||||
|
|
||||||
|
'wrongCode' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processClientLogin($code, string &$error, &$errorCode): bool
|
||||||
|
{
|
||||||
|
if (!$this->isActive()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->authType == self::TYPE_EMAIL) {
|
||||||
|
$errorCode = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($code === false) {
|
||||||
|
$error = 'Submit a valid two-factor authentication token.';
|
||||||
|
|
||||||
|
if ($this->authType == self::TYPE_EMAIL) {
|
||||||
|
if (!$this->hasRecentEmailCode(15 * 60)) {
|
||||||
|
$this->resendEmailCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->getAuthGateway()->verifyCode($code)) {
|
||||||
|
$error = 'Two-factor authentication failed, token is wrong.';
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->authType === self::TYPE_EMAIL) {
|
||||||
|
$this->deleteOldCodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAuthGateway(int $authType): void
|
||||||
|
{
|
||||||
|
if ($authType === self::TYPE_EMAIL) {
|
||||||
|
$this->authGateway = new EmailAuthGateway($this->account);
|
||||||
|
}
|
||||||
|
else if ($authType === self::TYPE_APP) {
|
||||||
|
$this->authGateway = new AppAuthGateway($this->account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAccountManageViews(): array
|
||||||
|
{
|
||||||
|
if ($this->authType == self::TYPE_EMAIL) {
|
||||||
|
$twoFactorView = 'account/2fa/main.protected.html.twig';
|
||||||
|
$twoFactorView2 = 'account/2fa/email/manage.connected.html.twig';
|
||||||
|
}
|
||||||
|
elseif ($this->authType == self::TYPE_APP) {
|
||||||
|
$twoFactorView = 'account/2fa/app/manage.connected.html.twig';
|
||||||
|
$twoFactorView2 = 'account/2fa/main.protected.html.twig';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$twoFactorView = 'account/2fa/app/manage.enable.html.twig';
|
||||||
|
$twoFactorView2 = 'account/2fa/email/manage.enable.html.twig';
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$twoFactorView, $twoFactorView2];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enable(int $type): void {
|
||||||
|
$this->account->setCustomField('2fa_type', $type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function disable(): void
|
||||||
|
{
|
||||||
|
global $db;
|
||||||
|
|
||||||
|
$this->account->setCustomField('2fa_type', self::TYPE_NONE);
|
||||||
|
|
||||||
|
if ($db->hasColumn('accounts', 'secret')) {
|
||||||
|
$this->account->setCustomField('secret', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->account->setCustomField('2fa_secret', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isActive(?int $authType = null): bool {
|
||||||
|
if ($authType !== null) {
|
||||||
|
return $this->authType === $authType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->authType != self::TYPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAuthType(): int {
|
||||||
|
return $this->authType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAuthGateway(): AppAuthGateway|EmailAuthGateway {
|
||||||
|
return $this->authGateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasRecentEmailCode($since = self::EMAIL_CODE_VALID_UNTIL): bool {
|
||||||
|
return AccountEMailCode::where('account_id', '=', $this->account->getId())->where('created_at', '>', time() - $since)->first() !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteOldCodes(): void {
|
||||||
|
AccountEMailCode::where('account_id', '=', $this->account->getId())->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function appInitTOTP(string $secret): TOTP
|
||||||
|
{
|
||||||
|
$otp = TOTP::createFromSecret($secret);
|
||||||
|
|
||||||
|
$otp->setLabel($this->account->getEmail());
|
||||||
|
$otp->setIssuer(configLua('serverName'));
|
||||||
|
|
||||||
|
return $otp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function appDisplayEnable(string $secret, ?TOTP $otp = null, array $errors = []): void
|
||||||
|
{
|
||||||
|
global $twig;
|
||||||
|
|
||||||
|
if ($otp === null) {
|
||||||
|
$otp = $this->appInitTOTP($secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
$grCodeUri = $otp->getQrCodeUri(
|
||||||
|
'https://api.qrserver.com/v1/create-qr-code/?data=[DATA]&size=200x200&ecc=M',
|
||||||
|
'[DATA]'
|
||||||
|
);
|
||||||
|
|
||||||
|
$twig->display('account/2fa/app/enable.html.twig', [
|
||||||
|
'grCodeUri' => $grCodeUri,
|
||||||
|
'secret' => $secret,
|
||||||
|
'errors' => $errors,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resendEmailCode(): void
|
||||||
|
{
|
||||||
|
global $twig;
|
||||||
|
|
||||||
|
$newCode = generateRandomString(6, true, false, true);
|
||||||
|
AccountEMailCode::create([
|
||||||
|
'account_id' => $this->account->getId(),
|
||||||
|
'code' => $newCode,
|
||||||
|
'created_at' => time(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$mailBody = $twig->render('mail.account.2fa.email-code.html.twig', [
|
||||||
|
'code' => $newCode,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!_mail($this->account->getEMail(), configLua('serverName') . ' - Requested Authentication Email Code', $mailBody)) {
|
||||||
|
error('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,6 +69,15 @@ define('HOOK_ACCOUNT_LOGIN_AFTER_PASSWORD', ++$i);
|
|||||||
define('HOOK_ACCOUNT_LOGIN_AFTER_REMEMBER_ME', ++$i);
|
define('HOOK_ACCOUNT_LOGIN_AFTER_REMEMBER_ME', ++$i);
|
||||||
define('HOOK_ACCOUNT_LOGIN_AFTER_PAGE', ++$i);
|
define('HOOK_ACCOUNT_LOGIN_AFTER_PAGE', ++$i);
|
||||||
define('HOOK_ACCOUNT_LOGIN_POST', ++$i);
|
define('HOOK_ACCOUNT_LOGIN_POST', ++$i);
|
||||||
|
define('HOOK_ACCOUNT_LOGIN_PRE', ++$i);
|
||||||
|
define('HOOK_ACCOUNT_LOST_CHECK_CODE_FINISH_AFTER_PASSWORD', ++$i);
|
||||||
|
define('HOOK_ACCOUNT_LOST_CHECK_CODE_FINISH_AFTER_PASSWORD_REPEAT', ++$i);
|
||||||
|
define('HOOK_ACCOUNT_LOST_EMAIL_SET_NEW_PASSWORD_POST', ++$i);
|
||||||
|
define('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_2_AFTER_CHARACTER', ++$i);
|
||||||
|
define('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_2_AFTER_EMAIL', ++$i);
|
||||||
|
define('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_2_AFTER_PASSWORD', ++$i);
|
||||||
|
define('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_2_AFTER_PASSWORD_REPEAT', ++$i);
|
||||||
|
define('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_3_POST', ++$i);
|
||||||
define('HOOK_ACCOUNT_CREATE_CHARACTER_AFTER', ++$i);
|
define('HOOK_ACCOUNT_CREATE_CHARACTER_AFTER', ++$i);
|
||||||
define('HOOK_ACCOUNT_CREATE_CHARACTER_BEFORE_FIRST_TABLE', ++$i);
|
define('HOOK_ACCOUNT_CREATE_CHARACTER_BEFORE_FIRST_TABLE', ++$i);
|
||||||
define('HOOK_ACCOUNT_CREATE_CHARACTER_BEFORE_VOCATIONS', ++$i);
|
define('HOOK_ACCOUNT_CREATE_CHARACTER_BEFORE_VOCATIONS', ++$i);
|
||||||
|
|||||||
@@ -146,10 +146,10 @@ if($twig_loader) {
|
|||||||
|
|
||||||
function get_template_menus(): array
|
function get_template_menus(): array
|
||||||
{
|
{
|
||||||
global $template_name;
|
global $template_name, $logged_access;
|
||||||
|
|
||||||
$result = Cache::remember('template_menus_' . $template_name, 10 * 60, function () use ($template_name) {
|
$result = Cache::remember('template_menus_' . $template_name, 10 * 60, function () use ($template_name) {
|
||||||
$result = Menu::select(['name', 'link', 'blank', 'color', 'category'])
|
$result = Menu::select(['name', 'link', 'access', 'blank', 'color', 'category'])
|
||||||
->where('template', $template_name)
|
->where('template', $template_name)
|
||||||
->orderBy('category')
|
->orderBy('category')
|
||||||
->orderBy('ordering')
|
->orderBy('ordering')
|
||||||
@@ -163,6 +163,10 @@ function get_template_menus(): array
|
|||||||
|
|
||||||
$menus = [];
|
$menus = [];
|
||||||
foreach($result as $menu) {
|
foreach($result as $menu) {
|
||||||
|
if ($menu['access'] > $logged_access) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (empty($menu['link'])) {
|
if (empty($menu['link'])) {
|
||||||
$menu['link'] = 'news';
|
$menu['link'] = 'news';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,26 @@
|
|||||||
{% if new_line is defined and new_line %}
|
{% if new_line is defined and new_line %}
|
||||||
<br/>
|
<br/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form action="{% if action is not defined %}{{ getLink('account/manage') }}{% else %}{{ action }}{% endif %}" method="post">
|
|
||||||
{{ csrf() }}
|
{% set _center = false %}
|
||||||
{{ include('buttons.back.html.twig') }}
|
|
||||||
</form>
|
{% if center is defined and center %}
|
||||||
|
{% set _center = true %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if _center %}
|
||||||
|
<table border="0" cellspacing="1" cellpadding="4" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
{% endif %}
|
||||||
|
<form action="{% if action is not defined %}{{ getLink('account/manage') }}{% else %}{{ action }}{% endif %}" method="post">
|
||||||
|
{{ csrf() }}
|
||||||
|
{{ include('buttons.back.html.twig') }}
|
||||||
|
</form>
|
||||||
|
{% if _center %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
The Lost Account Interface can help you to get back your account name and password. Please enter your character name and select what you want to do.<br/>
|
|
||||||
<form action="{{ getLink('account/lost') }}?action=step1" method="post">
|
|
||||||
{{ csrf() }}
|
|
||||||
<input type="hidden" name="character" value="">
|
|
||||||
<table cellspacing="1" cellpadding="4" border="0" width="100%">
|
|
||||||
<tr>
|
|
||||||
<td bgcolor="{{ config.vdarkborder }}" class="white"><b>Please enter your character name</b></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td bgcolor="{{ config.darkborder }}">
|
|
||||||
<input type="text" name="nick" size="40" autofocus/><br>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<table cellspacing="1" cellpadding="4" border="0" width="100%">
|
|
||||||
<tr>
|
|
||||||
<td bgcolor="{{ config.vdarkborder }}" class="white"><b>What do you want?</b></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td bgcolor="{{ config.darkborder }}">
|
|
||||||
<input type="radio" name="action_type" id="action_type_email" value="email">
|
|
||||||
<label for="action_type_email"> Send me new password and my account name to account e-mail adress.</label><br/>
|
|
||||||
<input type=radio name="action_type" id="action_type_key" value="reckey">
|
|
||||||
<label for="action_type_key"> I got <b>recovery key</b> and want set new password and e-mail adress to my account.</label><br/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<br/>
|
|
||||||
<table cellspacing="0" cellpadding="0" border="0" width="100%">
|
|
||||||
<tr>
|
|
||||||
<td align="center">
|
|
||||||
{{ include('buttons.submit.html.twig') }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</form>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
Please select action.<br/>
|
|
||||||
<table cellspacing="0" cellpadding="0" border="0" width="100%">
|
|
||||||
<tr>
|
|
||||||
<td align="center">
|
|
||||||
<a href="{{ getLink('account/lost') }}" border="0">
|
|
||||||
{{ include('buttons.back.html.twig') }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
@@ -147,6 +147,9 @@
|
|||||||
{% include('buttons.base.html.twig') %}
|
{% include('buttons.base.html.twig') %}
|
||||||
</form>
|
</form>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
{{ include('account/2fa/main.html.twig') }}
|
||||||
|
|
||||||
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }}
|
{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }}
|
||||||
<a name="Account+Logs" ></a>
|
<a name="Account+Logs" ></a>
|
||||||
<h2>Account Logs</h2>
|
<h2>Account Logs</h2>
|
||||||
|
|||||||
76
system/templates/account/2fa/app/enable.html.twig
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
<table style="width:100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="TableContentContainer ">
|
||||||
|
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<ol>
|
||||||
|
<li>Open an authenticator app of your choice (e.g. <a
|
||||||
|
target="_blank"
|
||||||
|
href="https://support.google.com/accounts/answer/1066447"
|
||||||
|
rel="noopener noreferrer">Google Authenticator</a>, <a
|
||||||
|
target="_blank" href="https://www.authy.com/users"
|
||||||
|
rel="noopener noreferrer">Authy</a>). In the app you
|
||||||
|
will be asked either to enter a key manually:<br><b>{{ secret }}</b><br>or
|
||||||
|
to scan the barcode below:<br>
|
||||||
|
<img alt="QR code" style="margin-top: 15px; margin-bottom: 15px;"
|
||||||
|
src="{{ grCodeUri }}">
|
||||||
|
</li>
|
||||||
|
<li><label for="totp">Enter the verification code you have received from the used
|
||||||
|
authenticator app:</label><br>
|
||||||
|
<div style="margin-top: 15px; margin-bottom: 15px;">
|
||||||
|
|
||||||
|
<input form="form" id="auth-code" name="auth-code" maxlength="6" autocomplete="off">
|
||||||
|
{% if errors|length > 0 %}
|
||||||
|
<br/>
|
||||||
|
<div class="FormFieldError">{{ errors[0] }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>Click on "Continue" to connect the authenticator app to your
|
||||||
|
Tibia account.
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<table style="width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr align="center" valign="top">
|
||||||
|
<td>
|
||||||
|
<form id="form" method="post" action="{{ getLink('account/2fa/app/enable') }}">
|
||||||
|
|
||||||
|
<input type="hidden" name="action" value="link">
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_color = 'green' %}
|
||||||
|
{% set button_name = 'Continue' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<form action="{{ getLink('account/manage') }}" method="post" style="padding:0;margin:0;">
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_color = 'blue' %}
|
||||||
|
{% set button_name = 'Cancel' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
101
system/templates/account/2fa/app/enable.warning.html.twig
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
{% set title = 'Warning' %}
|
||||||
|
{% set background = config('darkborder') %}
|
||||||
|
|
||||||
|
{% set content %}
|
||||||
|
<table style="width:100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="red"><b>Please read this warning carefully as it contains important security information! If you skip this message, you might lose your {{ config.lua.serverName }} account!</b></span><br><br>
|
||||||
|
<p>Before you connect your account with an authenticator app, you will be asked to
|
||||||
|
enter your recovery key. If you do not have a valid recovery key, you need to
|
||||||
|
order a new one before you can connect your account with an authenticator.</p>
|
||||||
|
<p>Why?<br>The recovery key is the only way to unlink the authenticator app from
|
||||||
|
your {{ config.lua.serverName }} account in various cases, among others, if:</p>
|
||||||
|
<ul style="list-style-type:square">
|
||||||
|
<li>you lose your device (mobile phone, tablet, etc.) with the authenticator
|
||||||
|
app
|
||||||
|
</li>
|
||||||
|
<li>the device with the authenticator app does not work anymore</li>
|
||||||
|
<li>the device with the authenticator app gets stolen</li>
|
||||||
|
<li>you delete the authenticator app from your device and reinstall it</li>
|
||||||
|
<li>your device is reset for some reason</li>
|
||||||
|
</ul>
|
||||||
|
<p></p>
|
||||||
|
<p>Please note that the authenticator app data is not saved on your device's account
|
||||||
|
(e.g. Google or iTunes sync) even if you have app data backup&synchronisation
|
||||||
|
activated in the settings of your device!</p>
|
||||||
|
<p>In all these scenarios, the recovery key is the only way to get access to your
|
||||||
|
{{ config.lua.serverName }} account. Note that not even customer support will be able to help you in
|
||||||
|
these cases if you do not have a valid recovery key.<br>For this reason, make
|
||||||
|
sure to store your recovery key always in a safe place!</p><br>Do you have a
|
||||||
|
valid recovery key and would like to request the email with the confirmation key to
|
||||||
|
start connecting your {{ config.lua.serverName }} account to an authenticator app?<br><br><b>Enter your
|
||||||
|
recovery key:</b><br/>
|
||||||
|
|
||||||
|
<div style="margin-top: 15px; margin-bottom: 15px;">
|
||||||
|
|
||||||
|
{% if newRecoveryKeyFormat %}
|
||||||
|
|
||||||
|
<input form="form" class="UpperCaseInput" name="key1" value="" size="5" maxlength="5" autocomplete="off"> -
|
||||||
|
<input form="form" class="UpperCaseInput" name="key2" value="" size="5" maxlength="5" autocomplete="off"> - <input form="form" class="UpperCaseInput" name="key3" value="" size="5" maxlength="5" autocomplete="off"> -
|
||||||
|
<input form="form" class="UpperCaseInput" name="key4" value="" size="5" maxlength="5" autocomplete="off">
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<input form="form" class="UpperCaseInput" name="key" value="" autocomplete="off">
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if errors|length > 0 %}
|
||||||
|
<br/>
|
||||||
|
<div class="FormFieldError">{{ errors[0] }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endset %}
|
||||||
|
{% include 'tables.headline.html.twig' %}
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<table style="width:100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr align="center">
|
||||||
|
<td>
|
||||||
|
<form id="form" action="{{ getLink('account/2fa/app/enable') }}" method="post" style="padding:0;margin:0;">
|
||||||
|
|
||||||
|
<input type="hidden" name="action" value="request" />
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_name = 'Request' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<form action="{{ getLink('account/register') }}" method="post" style="padding:0;margin:0;">
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_name = 'Order Recovery Key' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<form action="{{ getLink('account/manage') }}" method="post" style="padding:0;margin:0;">
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_name = 'Cancel Request' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.UpperCaseInput {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
64
system/templates/account/2fa/app/login.html.twig
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
{% set title = 'Enter Authenticator App Token' %}
|
||||||
|
{% set background = config('darkborder') %}
|
||||||
|
|
||||||
|
{% set content %}
|
||||||
|
<table style="width:100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="TableContentContainer ">
|
||||||
|
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Enter the verification code generated by the app:<br>
|
||||||
|
<div style="margin-top: 15px; margin-bottom: 15px;">
|
||||||
|
<div class="LabelV200" style="float:left;">Authenticator App Token:</div>
|
||||||
|
<input form="form" id="auth-code" name="auth-code" maxlength="6" autocomplete="off" required autofocus></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endset %}
|
||||||
|
{% include 'tables.headline.html.twig' %}
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<table style="width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr align="center" valign="top">
|
||||||
|
<td>
|
||||||
|
<form id="form" action="{{ getLink('account/manage') }}" method="post">
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
<input type="hidden" name="account_login" value="{{ account_login ?? '' }}" />
|
||||||
|
<input type="hidden" name="password_login" value="{{ password_login ?? '' }}" />
|
||||||
|
{% if remember_me %}
|
||||||
|
<input type="hidden" name="remember_me" value="true" />
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<input type="hidden" name="step" value="verify">
|
||||||
|
|
||||||
|
{% set button_color = 'green' %}
|
||||||
|
{% set button_name = 'Continue' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<form action="{{ getLink('account/manage') }}" method="post"
|
||||||
|
style="padding:0;margin:0;">
|
||||||
|
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_color = 'blue' %}
|
||||||
|
{% set button_name = 'Cancel' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
25
system/templates/account/2fa/app/manage.connected.html.twig
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="TableContentContainer ">
|
||||||
|
<table class="TableContent" width="100%" style="border:1px solid #faf0d7;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="float: right; width: 135px;">
|
||||||
|
<form action="{{ getLink('account/2fa/app/disable') }}" method="post" style="padding:0;margin:0;">
|
||||||
|
{{ csrf() }}
|
||||||
|
|
||||||
|
{% set button_name = 'Unlink' %}
|
||||||
|
{{ include('buttons.base.html.twig') }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<b>Your Tibia account is <span style="color: green">connected</span> to an authenticator app.</b>
|
||||||
|
<p>If you do not want to use an authenticator app any longer, you can "Unlink" the authenticator
|
||||||
|
App. Note, however, an authenticator app is an important security feature which helps to
|
||||||
|
prevent any unauthorized access to your Tibia account.</p></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||