Compare commits

...

99 Commits

Author SHA1 Message Date
slawkens
85a52fd715 Merge branch 'develop' into feature/refactor-account-lost 2026-01-04 13:14:33 +01:00
slawkens
7289cce826 Update CHANGELOG-2.x.md 2026-01-04 13:13:14 +01:00
slawkens
af9d4c2aeb Update CHANGELOG-2.x.md 2026-01-04 13:04:01 +01:00
Slawomir Boczek
a66edfad31 Restore vocations.xml loading + support for Monk (#345)
* Restore vocations.xml loading

For better handling of vocations
Monk is supported now

* New images for vocations (+ added Monk)

* Fix online.html.twig cause of merge
2026-01-04 13:00:34 +01:00
slawkens
89a35b5335 Merge branch 'main' into develop 2026-01-04 12:41:11 +01:00
slawkens
3d6e4c074a Release v1.8.7 2026-01-04 12:31:59 +01:00
slawkens
55da00520d Admin Panel: save menu collapse state 2026-01-03 22:04:49 +01:00
slawkens
efef16ee86 Extract script.ajax-setup.html.twig 2026-01-03 22:01:58 +01:00
slawkens
2f0b67f840 Merge branch 'main' into develop 2026-01-03 20:40:56 +01:00
slawkens
c65d4e4b62 Settings: better responsiveness on mobile 2026-01-03 20:40:44 +01:00
slawkens
fec3f3d297 Fix wrong header in admin changelogs 2026-01-03 20:39:49 +01:00
slawkens
7e6480b380 Return 404 when signature player not found
In most cases it was a request for a non existing file
2026-01-03 20:25:12 +01:00
slawkens
7c9c8d2990 More small adjustments to online page 2026-01-03 13:43:32 +01:00
slawkens
ca2e3bb576 Merge branch 'main' into develop 2026-01-03 13:21:22 +01:00
slawkens
2d8d35f5c8 Some adjustments to the online page
Use tables.headline
Use .myaac-table
2026-01-03 13:21:13 +01:00
slawkens
aba50ca0f1 Better delete rank button 2026-01-02 20:07:17 +01:00
slawkens
0110bf6ea2 Account character list: Add [ DELETED ] for deleted characters 2026-01-02 20:06:07 +01:00
slawkens
ba1c63921d Update guilds.leave_guild.html.twig 2026-01-02 20:02:57 +01:00
slawkens
5c68fba82a Add labels to some inputs + some small adjustments 2026-01-02 15:51:10 +01:00
slawkens
18c0212f9a Forum better button styling 2026-01-02 14:04:34 +01:00
slawkens
e0e0e46701 Move forum show_board code to Twig 2026-01-02 13:30:35 +01:00
slawkens
e11676273f Merge branch 'develop' into feature/refactor-account-lost 2026-01-01 13:23:53 +01:00
slawkens
61bcdc0c37 Merge branch 'main' into develop 2026-01-01 13:23:12 +01:00
slawkens
6f87d8b322 Add missing deprecated config.team_style 2026-01-01 11:56:22 +01:00
slawkens
cc220bedc1 Remove setting: outfit_images_wrong_looktypes
Is obsolete, the bug doesn't exist in latest outfit images
2026-01-01 11:55:58 +01:00
slawkens
98be661705 [WIP] Refactor account lost - fixes 2026-01-01 11:37:12 +01:00
slawkens
e425d597a8 [WIP] Refactor account lost 2026-01-01 08:40:48 +01:00
slawkens
85e54e709e Merge branch 'develop' into feature/refactor-account-lost 2026-01-01 08:16:03 +01:00
slawkens
f966dff5a8 Convert switch to match 2025-12-28 15:50:10 +01:00
Slawomir Boczek
402f3bb9b0 [WIP] Add access option to Menus (#340)
* [WIP] Add access option to Menus

Thanks @joelslamospersson for idea

* Add notice about Guest*

* Add access column into schema.sql

* Remove spectrum.js from project

Was used in Menus, replaced by html "color" input

* Block access to page if not required Access by Menus
2025-12-26 12:59:49 +01:00
slawkens
e98de451d8 Merge branch 'main' into develop 2025-12-22 20:04:31 +01:00
slawkens
8ec9bf1068 Fixed [player/guild/house] bb code in forum 2025-12-22 19:59:57 +01:00
slawkens
4fffaf6aff Merge branch 'main' into develop 2025-12-18 14:33:45 +01:00
slawkens
c44c9f9cf4 Add type hints and return types to cache classes 2025-12-18 14:33:07 +01:00
slawkens
ccfd6f1a87 Add PHP to cache engine list in settings 2025-12-18 14:23:25 +01:00
slawkens
96b8e00f49 Refactor PHP cache to store expiration and improve typing
Cache entries now store both the value and expiration timestamp in the file, allowing for more reliable expiration checks. Method signatures have been updated with type hints.
2025-12-18 14:22:42 +01:00
slawkens
11cb1cf97e Save db cache only if it has changed 2025-12-18 11:53:06 +01:00
slawkens
78a3535b6a Start v1.8.7-dev 2025-12-14 13:27:06 +01:00
slawkens
497959fd30 Update CHANGELOG-1.x.md 2025-12-14 13:18:08 +01:00
slawkens
6ba00eea96 Release v1.8.6 2025-12-14 12:38:25 +01:00
slawkens
c5d3d3a25f Merge branch 'main' into develop 2025-12-14 10:21:33 +01:00
slawkens
9ed06782e6 Ini set html_errors = 0, to show html code in exceptions 2025-12-14 10:21:23 +01:00
slawkens
18a1178e4b Fix exception show on first install, when there is no vendor
Before it displayed 500 white page, now it display the exception
2025-12-14 10:20:59 +01:00
slawkens
c86257e6da Highscores: Fix ordering by different skills
Adjust order by desc: skill_tries, manaspent, experience
2025-12-13 21:19:00 +01:00
slawkens
fd74f01291 Fix typo $up -> $down, migration was failing due that 2025-12-09 22:04:21 +01:00
slawkens
3011b969a4 Add php 8.5 to cypress workflow 2025-11-30 17:05:14 +01:00
slawkens
8e6749c599 Hook for adding custom rules to validate new character name 2025-11-24 18:04:09 +01:00
slawkens
549fa862d4 Merge branch 'develop' into feature/refactor-account-lost 2025-11-23 11:02:50 +01:00
slawkens
e1197515f3 Merge branch 'main' into develop 2025-11-23 10:13:00 +01:00
slawkens
ae5df2b704 Start v1.8.6-dev 2025-11-21 18:08:30 +01:00
slawkens
eebfc600cb Detect "deletion" column in guilds show 2025-11-18 00:15:32 +01:00
slawkens
9a99018dce Merge branch 'main' into develop 2025-11-13 20:08:38 +01:00
slawkens
6479546c22 Update CHANGELOG-2.x.md 2025-10-31 16:23:41 +01:00
slawkens
effb23f367 Create CHANGELOG-2.x.md 2025-10-31 15:32:49 +01:00
slawkens
08657c1599 Fix migration 47.php (convert IPs) 2025-10-31 15:25:55 +01:00
slawkens
6500c29799 [WIP] Refactor 2025-10-31 09:42:52 +01:00
slawkens
456b68a88b Merge branch 'main' into feature/refactor-account-lost 2025-10-31 08:25:11 +01:00
slawkens
1379c93439 Create 47.php 2025-10-31 07:00:11 +01:00
slawkens
19b1cfdd34 Merge branch 'main' into develop 2025-10-31 06:56:34 +01:00
slawkens
596dde4077 Merge branch 'main' into feature/refactor-account-lost 2025-09-28 19:15:34 +02:00
slawkens
ac9303402d Merge branch 'main' into feature/refactor-account-lost 2025-09-28 19:14:24 +02:00
slawkens
523210c5b7 Refactor
Add missing password check
Formatting
2025-09-15 20:04:21 +02:00
slawkens
29e2484ad5 Merge branch 'main' into feature/refactor-account-lost 2025-09-14 21:02:11 +02:00
slawkens
9ae07acfc1 Formatting 2025-09-14 21:01:32 +02:00
slawkens
dc6b60d0b6 Merge branch 'main' into feature/refactor-account-lost 2025-09-14 20:50:11 +02:00
slawkens
05b5e703ed Refactor code, better $error messages 2025-09-14 20:49:14 +02:00
slawkens
849944ff20 [WIP] Add csrfProtect() 2025-09-14 20:47:28 +02:00
slawkens
413ad42afa Remove duplicated code - extract lostAccountCooldown function 2025-09-14 20:03:03 +02:00
slawkens
233bf001ce Set $title to 'Lost Account' 2025-09-14 19:49:26 +02:00
slawkens
d2f1f41576 Use myaac-table class for tables 2025-09-14 19:44:30 +02:00
slawkens
2f9ae38c19 Merge branch 'main' into feature/refactor-account-lost 2025-09-14 19:35:26 +02:00
slawkens
b1b536ce68 Update form.html.twig 2025-09-14 19:34:52 +02:00
slawkens
25695a039d [WIP] Refactor account/lost 2025-09-14 17:41:53 +02:00
slawkens
e27d974c46 Merge branch 'main' into feature/refactor-account-lost 2025-09-14 13:02:41 +02:00
slawkens
e719725841 Merge branch 'main' into develop 2025-05-09 13:45:54 +02:00
slawkens
bb3e90110d Merge branch 'main' into develop 2025-05-09 13:14:12 +02:00
slawkens
2f0758e351 Update schema.sql 2025-04-26 06:17:58 +02:00
slawkens
6667c8c364 Merge branch 'main' into develop 2025-04-26 06:17:38 +02:00
slawkens
c13a540878 Merge branch 'main' into develop 2025-04-18 13:58:42 +02:00
slawkens
869ec035d9 Merge branch 'main' into develop 2025-04-04 21:09:12 +02:00
slawkens
9d696d31d8 Merge branch 'main' into develop 2025-04-04 20:08:24 +02:00
slawkens
8cc4caf587 Merge branch 'main' into develop 2025-04-01 07:43:57 +02:00
slawkens
e1d1c7d5db Merge branch 'main' into develop 2025-03-31 22:21:16 +02:00
slawkens
320733c2c1 Merge branch 'main' into develop 2025-03-31 19:51:21 +02:00
slawkens
c1809a98d1 Merge branch 'main' into develop 2025-03-30 07:11:15 +02:00
slawkens
46ed541015 Merge branch 'main' into develop 2025-03-16 20:54:40 +01:00
slawkens
29207361b7 Merge branch 'main' into develop 2025-03-16 12:39:32 +01:00
slawkens
25013ae91b Merge branch 'main' into develop 2025-03-15 23:09:14 +01:00
slawkens
5d630ba9dd Fix the second "Save" button -> addition to previous commit 2025-03-15 22:49:43 +01:00
slawkens
feadf1314d Fix: add possibility to remove all menu items 2025-03-15 22:49:37 +01:00
slawkens
08b8a716d4 Fix the second "Save" button -> addition to previous commit 2025-03-10 13:04:57 +01:00
slawkens
cc26b5c744 Fix: add possibility to remove all menu items 2025-03-10 10:48:19 +01:00
Slawomir Boczek
cb6e9a6a88 Feature/twig hooks filters (#258)
* feat: Hooks filters

* Cleanup
2025-03-09 21:39:37 +01:00
slawkens
4adb0758c5 Set version to 2.0-dev 2025-03-09 21:26:24 +01:00
Slawomir Boczek
7312383f73 Account actions rework on ip (Use single column for IP - VARCHAR(45)) (#289)
* Account actions rework on ip (Use single column for IP - VARCHAR(45))

* No foreach needed here
2025-03-09 21:18:12 +01:00
slawkens
3c1210fefa Nothing important, just better code style 2025-03-03 20:07:54 +01:00
slawkens
67f54eacbc Merge branch 'develop' into feature/refactor-account-lost 2024-09-12 08:24:06 +02:00
slawkens
cde8891b9b Merge branch 'develop' into feature/refactor-account-lost 2024-07-14 05:58:47 +02:00
slawkens
50a8b8169f [WIP] Account Lost refactor 2024-07-10 18:08:21 +02:00
103 changed files with 1701 additions and 3826 deletions

View File

@@ -22,7 +22,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' ]
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'] # 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:

View File

@@ -1,5 +1,30 @@
# Changelog # Changelog
## [1.8.7 - 04.01.2026]
### Fixed
* Fixed [player/guild/house] bb code in forum (https://github.com/slawkens/myaac/commit/8ec9bf10682c73f1fe40967a106ccda2a5073ed0)
### Changed
* Settings: better responsiveness on mobile (https://github.com/slawkens/myaac/commit/c65d4e4b62ef26fb4e24ecb1d2bcc4556d746adf)
* Signatures: Return 404 when the signature player is not found (https://github.com/slawkens/myaac/commit/7e6480b380799add7a2b1b7ce1d3c1f2b6819ff1)
### Removed
* Remove setting: outfit_images_wrong_looktypes - is obsolete, the bug doesn't exist in the latest outfit images (https://github.com/slawkens/myaac/commit/cc220bedc1f01535eaac23f6961135e2e7a6e310)
## [1.8.6 - 14.12.2025]
### Added
* Added hook for adding custom rules to validate new character name (https://github.com/slawkens/myaac/commit/8e6749c59984631288e8e9803819b2f0ff389761)
### Fixed
* Highscores: Fix ordering by different skills (Adjust order by desc: skill_tries, manaspent, experience) - More exact results (https://github.com/slawkens/myaac/commit/c86257e6dacbad773aa09c0958eeaa106a967f2d)
* Fix exception shown on first install, when there is no vendor - Before it displayed 500 white page, now it display the exception (https://github.com/slawkens/myaac/commit/18a1178e4b93607a350259679e0366cb83fb4126)
* Fix typo $up -> $down, in migration nr 7, was failing due that (https://github.com/slawkens/myaac/commit/fd74f01291d0e9cdb92ee1b95021c9d7b591ad7c)
### Changed
* Ini set html_errors = 0, to show html code in exceptions (https://github.com/slawkens/myaac/commit/9ed06782e67772826d927ad847a077b99df5060d)
## [1.8.5 - 21.11.2025] ## [1.8.5 - 21.11.2025]
### Added ### Added

9
CHANGELOG-2.x.md Normal file
View File

@@ -0,0 +1,9 @@
## [2.0-dev - x.x.2025]
### Changed
* Better handling of vocations: (#345)
* Load from vocations.xml (No need to manually set)
* Support for Monk vocation
* Add an access option to Menus (#340)
* Possibility to hide menus for unauthorized users
* Reworked account action logs to use a single IP column as varchar(45) for both ipv4 and ipv6 (#289)

View File

@@ -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!');
@@ -481,9 +482,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>

View File

@@ -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;

View File

@@ -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>

View 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');

View File

@@ -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.5'; const MYAAC_VERSION = '2.0-dev';
const DATABASE_VERSION = 46; const DATABASE_VERSION = 48;
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'));
@@ -148,16 +148,17 @@ if(!IS_CLI) {
/** @var array $config */ /** @var array $config */
ini_set('log_errors', 1); ini_set('log_errors', 1);
if(@$config['env'] === 'dev' || defined('MYAAC_INSTALL')) { if(isset($config['env']) && $config['env'] !== 'dev' && !defined('MYAAC_INSTALL')) {
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
}
else {
ini_set('display_errors', 0); ini_set('display_errors', 0);
ini_set('display_startup_errors', 0); ini_set('display_startup_errors', 0);
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT); error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
} }
else {
ini_set('html_errors', 0);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
}
$autoloadFile = VENDOR . 'autoload.php'; $autoloadFile = VENDOR . 'autoload.php';
if (!is_file($autoloadFile)) { if (!is_file($autoloadFile)) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 33 KiB

BIN
images/monk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -1,11 +1,11 @@
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; ) 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 +102,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,

View File

@@ -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',
@@ -21,7 +19,6 @@ $deprecatedConfig = [
'visitors_counter_ttl', 'visitors_counter_ttl',
'views_counter', 'views_counter',
'outfit_images_url', 'outfit_images_url',
'outfit_images_wrong_looktypes',
'item_images_url', 'item_images_url',
'account_country', 'account_country',
'towns', 'towns',
@@ -52,6 +49,7 @@ $deprecatedConfig = [
'online_skulls', 'online_skulls',
'online_outfit', 'online_outfit',
'online_afk', 'online_afk',
'team_style',
'team_display_outfit' => 'team_outfit', 'team_display_outfit' => 'team_outfit',
'team_display_status' => 'team_status', 'team_display_status' => 'team_status',
'team_display_world' => 'team_world', 'team_display_world' => 'team_world',

View File

@@ -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();

View File

@@ -12,6 +12,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\Models\AccountAction;
/** /**
* OTServ account abstraction. * OTServ account abstraction.
* *
@@ -1007,26 +1009,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($limit) {
} return AccountAction::where('account_id', $this->data['id'])->orderByDesc('date')->limit($limit)->get()->toArray();
public function getActionsLog($limit1, $limit2)
{
$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.

View File

@@ -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'],
]; ];

View File

@@ -852,13 +852,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'];
} }

View File

@@ -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';
} }
} }

42
system/migrations/47.php Normal file
View 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
View 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');
}
};

View File

@@ -9,7 +9,7 @@ $up = function () use ($db) {
} }
}; };
$up = function () use ($db) { $down = function () use ($db) {
if (!$db->hasColumn(TABLE_PREFIX . 'screenshots', 'name')) { if (!$db->hasColumn(TABLE_PREFIX . 'screenshots', 'name')) {
$db->addColumn(TABLE_PREFIX . 'screenshots', 'name', 'VARCHAR(30) NOT NULL'); $db->addColumn(TABLE_PREFIX . 'screenshots', 'name', 'VARCHAR(30) NOT NULL');
} }

View File

@@ -22,7 +22,7 @@ csrfProtect();
$new_password = $_POST['new_password'] ?? null; $new_password = $_POST['new_password'] ?? null;
$new_password_confirm = $_POST['new_password_confirm'] ?? null; $new_password_confirm = $_POST['new_password_confirm'] ?? null;
$old_password = $_POST['old_password'] ?? null; $old_password = $_POST['old_password'] ?? null;
if(empty($new_password) && empty($new_password_confirm) && empty($old_password)) { if(is_null($new_password) && is_null($new_password_confirm) && is_null($old_password)) {
$twig->display('account.change-password.html.twig'); $twig->display('account.change-password.html.twig');
} }
else { else {

View File

@@ -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:&nbsp;<INPUT TYPE=text NAME="nick" VALUE="'.$nick.'" SIZE="40" readonly="readonly"><BR />
Recovery key:&nbsp;&nbsp;&nbsp;&nbsp;<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:&nbsp;&nbsp;<INPUT TYPE=text NAME="nick" VALUE="'.$nick.'" SIZE="40" readonly="readonly"><BR />
New password:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<INPUT id="passor" TYPE=password NAME="passor" VALUE="" SIZE="40"><BR>
Repeat new password:&nbsp;&nbsp;<INPUT id="passor2" TYPE=password NAME="passor" VALUE="" SIZE="40"><BR>
New e-mail address:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>'.$account->getName().'</b><BR>
New password:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>'.$new_pass.'</b><BR>
New e-mail address:&nbsp;<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:&nbsp;<INPUT TYPE=text NAME="code" VALUE="" SIZE="40")><BR />
Character:&nbsp;<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:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<INPUT TYPE=password ID="passor" NAME="passor" VALUE="" SIZE="40")><BR />
Repeat new password:&nbsp;<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:&nbsp;<INPUT TYPE=text NAME="code" VALUE="" SIZE="40")><BR />
Character:&nbsp;<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:&nbsp;<b>'.$newpassword.'</b><BR />
Account name:&nbsp;&nbsp;&nbsp;<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:&nbsp;<INPUT TYPE=text NAME="code" VALUE="" SIZE="40")><BR />
Character:&nbsp;<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>';
}

View 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."]
]);
}

View 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', [
]);
}

View File

@@ -0,0 +1,68 @@
<?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' => '',
]);
}
else {
$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),
]);

View File

@@ -0,0 +1,94 @@
<?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) || empty($newPassword) || empty($passwordRepeat)) {
$errors[] = 'Please enter code from e-mail and name of one character from account. Then press Submit.';
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
$twig->display('account.back_button.html.twig', [
'new_line' => true,
'center' => true,
'action' => getLink('account/lost/check-code')
]);
return;
}
$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 ($newPassword == $passwordRepeat) {
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', '');
$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 {
$error = Validator::getLastError();
}
}
else {
$error = 'Passwords are not the same!';
}
}
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', [
'code' => $code,
'character' => $character,
]);
}

View 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'),
]);

View 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'),
]);

View 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),
]);

View File

@@ -0,0 +1,103 @@
<?php
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)) {
$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[] = 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),
]);

View 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');
}

View File

@@ -96,12 +96,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');

View File

@@ -48,7 +48,7 @@ else
$account_logged->setCustomField('key', $new_rec_key); $account_logged->setCustomField('key', $new_rec_key);
$account_logged->setCustomField(setting('core.donate_column'), $account_logged->getCustomField(setting('core.donate_column')) - setting('core.account_generate_new_reckey_price')); $account_logged->setCustomField(setting('core.donate_column'), $account_logged->getCustomField(setting('core.donate_column')) - setting('core.account_generate_new_reckey_price'));
$account_logged->logAction('Generated new recovery key for ' . setting('core.account_generate_new_reckey_price') . ' premium points.'); $account_logged->logAction('Generated new recovery key for ' . setting('core.account_generate_new_reckey_price') . ' premium points.');
$message = '<br />Your recovery key were send on email address <b>'.$account_logged->getEMail().'</b> for '.setting('core.account_generate_new_reckey_price').' premium points.'; $message = '<br />Your recovery key was sent on email address <b>'.$account_logged->getEMail().'</b> for '.setting('core.account_generate_new_reckey_price').' premium points.';
} }
else else
$message = '<br /><p class="error">An error occurred while sending email ( <b>'.$account_logged->getEMail().'</b> ) with recovery key! Recovery key not changed. Try again later. For Admin: More info can be found in system/logs/mailer-error.log</p>'; $message = '<br /><p class="error">An error occurred while sending email ( <b>'.$account_logged->getEMail().'</b> ) with recovery key! Recovery key not changed. Try again later. For Admin: More info can be found in system/logs/mailer-error.log</p>';

View File

@@ -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>';

View File

@@ -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>'.$sections[$section_id]['name'].'</b>';
if($logged && (!$sections[$section_id]['closed'] || Forum::isModerator())) {
echo '<br /><br />
<a href="' . getLink('forum') . '?action=new_thread&section_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&section_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'],
]);

View File

@@ -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;
} }
} }
@@ -176,7 +173,9 @@ if (empty($highscores)) {
POT::SKILL_FISH => 'skill_fishing', POT::SKILL_FISH => 'skill_fishing',
); );
$query->addSelect($skill_ids[$skill] . ' as value'); $query
->addSelect($skill_ids[$skill] . ' as value')
->orderByDesc($skill_ids[$skill] . '_tries');
} else { } else {
$query $query
->join('player_skills', 'player_skills.player_id', '=', 'players.id') ->join('player_skills', 'player_skills.player_id', '=', 'players.id')
@@ -198,11 +197,11 @@ if (empty($highscores)) {
if ($skill == POT::SKILL__MAGLEVEL) { if ($skill == POT::SKILL__MAGLEVEL) {
$query $query
->addSelect('players.maglevel as value', 'players.maglevel') ->addSelect('players.maglevel as value', 'players.maglevel')
->orderBy('manaspent'); ->orderByDesc('manaspent');
} else { // level } else { // level
$query $query
->addSelect('players.level as value', 'players.experience') ->addSelect('players.level as value', 'players.experience')
->orderBy('experience'); ->orderByDesc('experience');
$list = 'experience'; $list = 'experience';
} }
} }
@@ -247,7 +246,7 @@ foreach($highscores as $id => &$player)
$player['link'] = getPlayerLink($player['name'], false); $player['link'] = getPlayerLink($player['name'], false);
$player['flag'] = getFlagImage($player['country']); $player['flag'] = getFlagImage($player['country']);
$player['outfit'] = '<img style="position:absolute;margin-top:' . (in_array($player['looktype'], setting('core.outfit_images_wrong_looktypes')) ? '-15px;margin-left:5px' : '-45px;margin-left:-25px') . ';" src="' . $player['outfit_url'] . '" alt="" />'; $player['outfit'] = '<img style="position:absolute;margin-top:-50px;margin-left:-30px" src="' . $player['outfit_url'] . '" alt="" />';
if ($skill != POT::SKILL__LEVEL) { if ($skill != POT::SKILL__LEVEL) {
if (isset($lastValue) && $lastValue == $player['value']) { if (isset($lastValue) && $lastValue == $player['value']) {
@@ -323,4 +322,5 @@ $twig->display('highscores.html.twig', [
'page' => $page, 'page' => $page,
'baseLink' => $baseLink, 'baseLink' => $baseLink,
'updatedAt' => $updatedAt, 'updatedAt' => $updatedAt,
'baseVocations' => Vocations::getBase(true),
]); ]);

View File

@@ -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

View File

@@ -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';
} }

View File

@@ -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,
@@ -312,23 +319,6 @@ return [
}, },
], ],
], ],
'vocations_amount' => [
'name' => 'Vocations Amount',
'type' => 'number',
'desc' => 'How much basic vocations your server got (without promotion)',
'default' => 4,
],
'vocations' => [
'name' => 'Vocation Names',
'type' => 'textarea',
'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',
'callbacks' => [
'get' => function ($value) {
return array_map('trim', explode(',', $value));
},
],
],
[ [
'type' => 'category', 'type' => 'category',
'title' => 'Database', 'title' => 'Database',
@@ -1482,17 +1472,6 @@ Sent by MyAAC,<br/>
'desc' => 'Set to animoutfit.php for animated outfit', 'desc' => 'Set to animoutfit.php for animated outfit',
'default' => 'https://outfit-images.ots.me/latest/outfit.php', 'default' => 'https://outfit-images.ots.me/latest/outfit.php',
], ],
'outfit_images_wrong_looktypes' => [
'name' => 'Outfit Images Wrong Looktypes',
'type' => 'text',
'desc' => 'This looktypes needs to have different margin-top and margin-left because they are wrong positioned',
'default' => '75, 126, 127, 266, 302',
'callbacks' => [
'get' => function ($value) {
return array_map('trim', explode(',', $value));
},
],
],
[ [
'type' => 'section', 'type' => 'section',
'title' => 'Monster Images' 'title' => 'Monster Images'

View File

@@ -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;
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -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;
} }

View File

@@ -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));
} }
} }

View File

@@ -12,39 +12,40 @@ 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): string
{ {
$tmp = ''; $tmp = '';
if ($this->fetch($key, $tmp)) { if ($this->fetch($key, $tmp)) {
@@ -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');
} }
} }

View File

@@ -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;
} }
} }

View File

@@ -231,6 +231,7 @@ class Forum
if(!is_int($rows / 2)) { $bgcolor = 'ABED25'; } else { $bgcolor = '23ED25'; } $rows++; if(!is_int($rows / 2)) { $bgcolor = 'ABED25'; } else { $bgcolor = '23ED25'; } $rows++;
$text = str_ireplace('[code]'.$code.'[/code]', '<i>Code:</i><br /><table cellpadding="0" style="background-color: #'.$bgcolor.'; width: 480px; border-style: dotted; border-color: #CCCCCC; border-width: 2px"><tr><td>'.$code.'</td></tr></table>', $text); $text = str_ireplace('[code]'.$code.'[/code]', '<i>Code:</i><br /><table cellpadding="0" style="background-color: #'.$bgcolor.'; width: 480px; border-style: dotted; border-color: #CCCCCC; border-width: 2px"><tr><td>'.$code.'</td></tr></table>', $text);
} }
$rows = 0; $rows = 0;
while(stripos($text, '[quote]') !== false && stripos($text, '[/quote]') !== false ) while(stripos($text, '[quote]') !== false && stripos($text, '[/quote]') !== false )
{ {
@@ -238,11 +239,31 @@ class Forum
if(!is_int($rows / 2)) { $bgcolor = 'AAAAAA'; } else { $bgcolor = 'CCCCCC'; } $rows++; if(!is_int($rows / 2)) { $bgcolor = 'AAAAAA'; } else { $bgcolor = 'CCCCCC'; } $rows++;
$text = str_ireplace('[quote]'.$quote.'[/quote]', '<table cellpadding="0" style="background-color: #'.$bgcolor.'; width: 480px; border-style: dotted; border-color: #007900; border-width: 2px"><tr><td>'.$quote.'</td></tr></table>', $text); $text = str_ireplace('[quote]'.$quote.'[/quote]', '<table cellpadding="0" style="background-color: #'.$bgcolor.'; width: 480px; border-style: dotted; border-color: #007900; border-width: 2px"><tr><td>'.$quote.'</td></tr></table>', $text);
} }
$rows = 0;
while(stripos($text, '[url]') !== false && stripos($text, '[/url]') !== false ) $tagsToParse = [
'url' => function ($str) {
return '<a href="'.$str.'" target="_blank">'.$str.'</a>';
},
'player' => function ($str) {
return generateLink(getPlayerLink($str, false), $str, true);
},
'guild' => function ($str) {
return generateLink(getGuildLink($str, false), $str, true);
},
'house' => function ($str) {
return generateLink(getHouseLink($str, false), $str, true);
}
];
foreach ($tagsToParse as $tag => $callback) {
while(stripos($text, "[$tag]") !== false && stripos($text, "[/$tag]") !== false
&& stripos($text, "[$tag]") < stripos($text, "[/$tag]"))
{ {
$url = substr($text, stripos($text, '[url]')+5, stripos($text, '[/url]') - stripos($text, '[url]') - 5); $length = strlen("[$tag]");
$text = str_ireplace('[url]'.$url.'[/url]', '<a href="'.$url.'" target="_blank">'.$url.'</a>', $text); $substr = substr($text, stripos($text, "[$tag]") + $length, stripos($text, "[/$tag]") - stripos($text, "[$tag]") - $length);
$text = str_ireplace('[' . $tag . ']' . $substr . '[/' . $tag . ']', $callback($substr), $text);
}
} }
$xhtml = false; $xhtml = false;
@@ -252,9 +273,6 @@ class Forum
'#\[u\](.*?)\[/u\]#si' => ($xhtml ? '<span style="text-decoration: underline;">\\1</span>' : '<u>\\1</u>'), '#\[u\](.*?)\[/u\]#si' => ($xhtml ? '<span style="text-decoration: underline;">\\1</span>' : '<u>\\1</u>'),
'#\[s\](.*?)\[/s\]#si' => ($xhtml ? '<strike>\\1</strike>' : '<s>\\1</s>'), '#\[s\](.*?)\[/s\]#si' => ($xhtml ? '<strike>\\1</strike>' : '<s>\\1</s>'),
'#\[guild\](.*?)\[/guild\]#si' => urldecode(generateLink(getGuildLink('$1', false), '$1', true)),
'#\[house\](.*?)\[/house\]#si' => urldecode(generateLink(getHouseLink('$1', false), '$1', true)),
'#\[player\](.*?)\[/player\]#si' => urldecode(generateLink(getPlayerLink('$1', false), '$1', true)),
// TODO: [poll] tag // TODO: [poll] tag
'#\[color=(.*?)\](.*?)\[/color\]#si' => ($xhtml ? '<span style="color: \\1;">\\2</span>' : '<span style="color: \\1">\\2</span>'), '#\[color=(.*?)\](.*?)\[/color\]#si' => ($xhtml ? '<span style="color: \\1;">\\2</span>' : '<span style="color: \\1">\\2</span>'),

View File

@@ -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'];
} }

View File

@@ -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'];
} }

View File

@@ -46,14 +46,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()

View File

@@ -0,0 +1,93 @@
<?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 {
return self::$vocationsFrom[$id] ?? null;
}
public static function getBase($includingRook = true): array {
$vocations = [];
foreach (self::$vocationsFrom as $vocId => $fromVoc) {
if ($vocId == $fromVoc && ($vocId != 0 || $includingRook)) {
$vocations[] = $vocId;
}
}
return $vocations;
}
}

View File

@@ -184,11 +184,11 @@ class Settings implements \ArrayAccess
} }
?> ?>
<h3 id="row_<?= $key ?>" style="text-align: center"><strong><?= $setting['title']; ?></strong></h3> <h3 id="row_<?= $key ?>" style="text-align: center"><strong><?= $setting['title']; ?></strong></h3>
<table class="table table-bordered table-striped"> <table class="table table-bordered table-striped table-responsive d-md-table">
<thead> <thead>
<tr> <tr>
<th style="width: 13%">Name</th> <th style="width: 13%">Name</th>
<th style="width: 30%">Value</th> <th style="width: 30%; min-width: 200px">Value</th>
<th>Description</th> <th>Description</th>
</tr> </tr>
</thead> </thead>

View File

@@ -342,6 +342,16 @@ class Validator
} }
} }
global $hooks;
$params = ['name' => $name, 'error' => ''];
$hooks->triggerFilter(HOOK_FILTER_VALIDATE_CHARACTER_NEW_NAME, $params);
if (!empty($params['error'])) {
self::$lastError = $params['error'];
return false;
}
return true; return true;
} }

View File

@@ -108,6 +108,7 @@ define('HOOK_FILTER_ROUTES', ++$i);
define('HOOK_FILTER_TWIG_DISPLAY', ++$i); define('HOOK_FILTER_TWIG_DISPLAY', ++$i);
define('HOOK_FILTER_TWIG_RENDER', ++$i); define('HOOK_FILTER_TWIG_RENDER', ++$i);
define('HOOK_FILTER_THEME_FOOTER', ++$i); define('HOOK_FILTER_THEME_FOOTER', ++$i);
define('HOOK_FILTER_VALIDATE_CHARACTER_NEW_NAME', ++$i);
const HOOK_FIRST = HOOK_INIT; const HOOK_FIRST = HOOK_INIT;
define('HOOK_LAST', $i); define('HOOK_LAST', $i);

View File

@@ -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';
} }

View File

@@ -1,7 +1,26 @@
{% if new_line is defined and new_line %} {% if new_line is defined and new_line %}
<br/> <br/>
{% endif %} {% endif %}
{% set _center = false %}
{% 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"> <form action="{% if action is not defined %}{{ getLink('account/manage') }}{% else %}{{ action }}{% endif %}" method="post">
{{ csrf() }} {{ csrf() }}
{{ include('buttons.back.html.twig') }} {{ include('buttons.back.html.twig') }}
</form> </form>
{% if _center %}
</td>
</tr>
</tbody>
</table>
{% endif %}

View File

@@ -5,18 +5,18 @@ Please enter your password and the new email address. Make sure that you enter a
<table style="width:100%;"> <table style="width:100%;">
<tr> <tr>
<td class="LabelV" > <td class="LabelV" >
<span>New Email Address:</span> <label for="new_email">New Email Address:</label>
</td> </td>
<td style="width:90%;"> <td style="width:90%;">
<input form="form" name="new_email" value="{% if new_email is defined %}{{ new_email }}{% endif %}" size="30" maxlength="50" autofocus/> <input form="form" id="new_email" name="new_email" value="{% if new_email is defined %}{{ new_email }}{% endif %}" size="30" maxlength="50" autofocus/>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="LabelV"> <td class="LabelV">
<span >Password:</span> <label for="password">Password:</label>
</td> </td>
<td> <td>
<input form="form" type="password" name="password" size="30" maxlength="29"> <input form="form" type="password" id="password" name="password" size="30" maxlength="29">
</td> </td>
</tr> </tr>
</table> </table>

View File

@@ -4,20 +4,26 @@ Here you can tell other players about yourself. This information will be display
{% set content %} {% set content %}
<table style="width: 100%;" > <table style="width: 100%;" >
<tr> <tr>
<td class="LabelV">Real Name:</td> <td class="LabelV">
<label for="info_rlname">Real Name:</label>
</td>
<td style="width:90%;" > <td style="width:90%;" >
<input form="form" name="info_rlname" value="{{ account_rlname }}" size="30" maxlength="50" > <input form="form" id="info_rlname" name="info_rlname" value="{{ account_rlname }}" size="30" maxlength="50" >
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="LabelV" >Location:</td> <td class="LabelV">
<label for="info_location">Location:</label>
</td>
<td> <td>
<input form="form" name="info_location" value="{{ account_location }}" size="30" maxlength="50" > <input form="form" id="info_location" name="info_location" value="{{ account_location }}" size="30" maxlength="50" >
</td> </td>
</tr> </tr>
{% if setting('core.account_country') %} {% if setting('core.account_country') %}
<tr> <tr>
<td class="LabelV">Country:</td> <td class="LabelV">
<label for="account_country">Country:</label>
</td>
<td> <td>
<select form="form" name="info_country" id="account_country"> <select form="form" name="info_country" id="account_country">
{% for code, country in countries %} {% for code, country in countries %}

View File

@@ -6,7 +6,7 @@ Please enter your current password and a new password. For your security, please
<table style="width:100%;"> <table style="width:100%;">
<tr> <tr>
<td class="LabelV"> <td class="LabelV">
<span>Current Password:</span> <label for="old_password">Current Password:</label>
</td> </td>
<td> <td>
<input form="form" type="password" id="old_password" name="old_password" size="30" maxlength="29"> <input form="form" type="password" id="old_password" name="old_password" size="30" maxlength="29">
@@ -17,7 +17,7 @@ Please enter your current password and a new password. For your security, please
<tr> <tr>
<td class="LabelV"> <td class="LabelV">
<span>New Password:</span> <label for="new_password">New Password:</label>
</td> </td>
<td style="width:90%;"> <td style="width:90%;">
<input form="form" type="password" id="new_password" name="new_password" size="30" maxlength="29"> <input form="form" type="password" id="new_password" name="new_password" size="30" maxlength="29">
@@ -28,7 +28,7 @@ Please enter your current password and a new password. For your security, please
<tr> <tr>
<td class="LabelV"> <td class="LabelV">
<span>New Password Again:</span> <label for="new_password_confirm">New Password Again:</label>
</td> </td>
<td> <td>
<input form="form" type="password" id="new_password_confirm" name="new_password_confirm" size="30" maxlength="29"> <input form="form" type="password" id="new_password_confirm" name="new_password_confirm" size="30" maxlength="29">

View File

@@ -1,4 +1,4 @@
To change a name of character select player and choose a new name.<br/> To change the name of a character, select a player and choose a new name.<br/>
<span style="color: red">Change name cost {{ setting('core.account_change_character_name_price') }} {{ setting('core.donate_column') == 'coins' ? 'coins' : 'premium points' }}. You have {{ points }} {{ setting('core.donate_column') == 'coins' ? 'coins' : 'premium points' }}.</span><br/><br/> <span style="color: red">Change name cost {{ setting('core.account_change_character_name_price') }} {{ setting('core.donate_column') == 'coins' ? 'coins' : 'premium points' }}. You have {{ points }} {{ setting('core.donate_column') == 'coins' ? 'coins' : 'premium points' }}.</span><br/><br/>
{% set title = 'Change Name' %} {% set title = 'Change Name' %}
@@ -6,9 +6,11 @@ To change a name of character select player and choose a new name.<br/>
{% set content %} {% set content %}
<table style="width:100%;" > <table style="width:100%;" >
<tr> <tr>
<td class="LabelV" ><span>Character:</span></td> <td class="LabelV">
<label for="player_id">Character:</label>
</td>
<td style="width:90%;" > <td style="width:90%;" >
<select form="form" name="player_id"> <select form="form" id="player_id" name="player_id">
{% for player in account_logged.getPlayersList(false) %} {% for player in account_logged.getPlayersList(false) %}
<option value="{{ player.getId() }}">{{ player.getName() }}</option> <option value="{{ player.getId() }}">{{ player.getName() }}</option>
{% endfor %} {% endfor %}
@@ -16,7 +18,9 @@ To change a name of character select player and choose a new name.<br/>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="LabelV" ><span>New Name:</span></td> <td class="LabelV">
<label for="character_name">New Name:</label>
</td>
<td> <td>
<input form="form" type="text" name="name" id="character_name" size="25" maxlength="25" > <input form="form" type="text" name="name" id="character_name" size="25" maxlength="25" >
<img id="character_indicator" src="images/global/general/{% if not save or errors|length > 0 %}n{% endif %}ok.gif" /> <img id="character_indicator" src="images/global/general/{% if not save or errors|length > 0 %}n{% endif %}ok.gif" />

View File

@@ -6,9 +6,11 @@ To change a sex of character select player and choose a new sex.<br/>
{% set content %} {% set content %}
<table style="width:100%;" > <table style="width:100%;" >
<tr> <tr>
<td class="LabelV" ><span>Character:</span></td> <td class="LabelV">
<label for="player_id">Character:</label>
</td>
<td style="width:90%;" > <td style="width:90%;" >
<select form="form" name="player_id"> <select form="form" id="player_id" name="player_id">
{% for player in players %} {% for player in players %}
<option value="{{ player.getId() }}">{{ player.getName() }}</option> <option value="{{ player.getId() }}">{{ player.getName() }}</option>
{% endfor %} {% endfor %}
@@ -16,9 +18,11 @@ To change a sex of character select player and choose a new sex.<br/>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="LabelV" ><span>New Sex:</span></td> <td class="LabelV">
<label for="new_sex">New Sex:</label>
</td>
<td> <td>
<select form="form" name="new_sex"> <select form="form" id="new_sex" name="new_sex">
{% for id, gender in config.genders %} {% for id, gender in config.genders %}
<option value="{{ id }}"{% if player_sex == id %} selected{% endif %}>{{ gender }}</option> <option value="{{ id }}"{% if player_sex == id %} selected{% endif %}>{{ gender }}</option>
{% endfor %} {% endfor %}

View File

@@ -4,15 +4,19 @@ To delete a character enter the name of the character and your password.<br/><br
{% set content %} {% set content %}
<table style="width:100%;"> <table style="width:100%;">
<tr> <tr>
<td class="LabelV" ><span>Character Name:</span></td> <td class="LabelV">
<label for="delete_name">Character Name:</label>
</td>
<td style="width:90%;"> <td style="width:90%;">
<input form="form" name="delete_name" value="" size="30" maxlength="29"/> <input form="form" id="delete_name" name="delete_name" value="" size="30" maxlength="29"/>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="LabelV" ><span>Password:</span></td> <td class="LabelV">
<label for="delete_password">Password:</label>
</td>
<td> <td>
<input form="form" type="password" name="delete_password" size="30" maxlength="29"/> <input form="form" type="password" id="delete_password" name="delete_password" size="30" maxlength="29"/>
</td> </td>
</tr> </tr>
</table> </table>

View File

@@ -48,7 +48,7 @@
</tr> </tr>
{% if setting('core.mail_enabled') and setting('core.account_mail_verify') %} {% if setting('core.mail_enabled') and setting('core.account_mail_verify') %}
<tr><td></td><td><span><strong>Please use real address!<br/>We will send a link to validate your Email.</strong></span></td></tr> <tr><td></td><td><span><strong>Please use a real address!<br/>We will send a link to validate your Email.</strong></span></td></tr>
{% endif %} {% endif %}
{{ hook('HOOK_ACCOUNT_CREATE_AFTER_EMAIL') }} {{ hook('HOOK_ACCOUNT_CREATE_AFTER_EMAIL') }}

View File

@@ -1,13 +1,15 @@
To generate new recovery key for your account please enter your password.<br/> To generate a new recovery key for your account, please enter your password.<br/>
<span style="color: red"><b>New recovery key cost {{ setting('core.account_generate_new_reckey_price') }} Premium Points</b>.</span> You have {{ points }} premium points. You will receive e-mail with this recovery key. <span style="color: red"><b>New recovery key cost {{ setting('core.account_change_character_name_price') }} {{ setting('core.donate_column') == 'coins' ? 'coins' : 'premium points' }}. You have {{ points }} {{ setting('core.donate_column') == 'coins' ? 'coins' : 'premium points' }}. You will receive an e-mail with this recovery key.
<br/> <br/>
{% set title = 'Generate recovery key' %} {% set title = 'Generate recovery key' %}
{% set background = config('darkborder') %} {% set background = config('darkborder') %}
{% set content %} {% set content %}
<table style="width:100%;"> <table style="width:100%;">
<tr> <tr>
<td class="LabelV"><span>Password:</span></td> <td class="LabelV">
<td><input form="form" type="password" name="reg_password" size="30" maxlength="29" ></td> <label for="reg_password">Password:</label>
</td>
<td><input form="form" type="password" id="reg_password" name="reg_password" size="30" maxlength="29" ></td>
</tr> </tr>
</table> </table>
{% endset %} {% endset %}
@@ -18,7 +20,7 @@ To generate new recovery key for your account please enter your password.<br/>
<td> <td>
<table border="0" cellspacing="0" cellpadding="0"> <table border="0" cellspacing="0" cellpadding="0">
<tr> <tr>
<td style="border:0px;"> <td style="border:0;">
<form id="form" action="{{ getLink('account/register-new') }}" method="post"> <form id="form" action="{{ getLink('account/register-new') }}" method="post">
{{ csrf() }} {{ csrf() }}
<input type="hidden" name="registeraccountsave" value="1"> <input type="hidden" name="registeraccountsave" value="1">
@@ -31,7 +33,7 @@ To generate new recovery key for your account please enter your password.<br/>
<td> <td>
<table border="0" cellspacing="0" cellpadding="0"> <table border="0" cellspacing="0" cellpadding="0">
<tr> <tr>
<td style="border:0px;"> <td style="border:0;">
<form action="{{ getLink('account/manage') }}" method="post"> <form action="{{ getLink('account/manage') }}" method="post">
{{ csrf() }} {{ csrf() }}
{{ include('buttons.back.html.twig') }} {{ include('buttons.back.html.twig') }}

View File

@@ -5,10 +5,10 @@ To generate recovery key for your account please enter your password.<br/><br/>
<table style="width:100%;" > <table style="width:100%;" >
<tr> <tr>
<td class="LabelV"> <td class="LabelV">
<span>Password:</span> <label for="reg_password">Password:</label>
</td> </td>
<td> <td>
<input form="form" type="password" name="reg_password" size="30" maxlength="29" autofocus/> <input form="form" type="password" id="reg_password" name="reg_password" size="30" maxlength="29" autofocus/>
</td> </td>
</tr> </tr>
</table> </table>
@@ -20,7 +20,7 @@ To generate recovery key for your account please enter your password.<br/><br/>
<td> <td>
<table border="0" cellspacing="0" cellpadding="0" > <table border="0" cellspacing="0" cellpadding="0" >
<tr> <tr>
<td style="border:0px;"> <td style="border:0;">
<form id="form" action="{{ getLink('account/register') }}" method="post"> <form id="form" action="{{ getLink('account/register') }}" method="post">
{{ csrf() }} {{ csrf() }}
<input type="hidden" name="registeraccountsave" value="1"/> <input type="hidden" name="registeraccountsave" value="1"/>

View File

@@ -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>

View File

@@ -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>

View File

@@ -179,7 +179,7 @@
{% set i = i + 1 %} {% set i = i + 1 %}
<tr bgcolor="{{ getStyle(i) }}"> <tr bgcolor="{{ getStyle(i) }}">
<td> <td>
<a href="{{ getLink('characters/' ~ player.getName()|urlencode) }}">{{ player.getName() }}</a> <a href="{{ getLink('characters/' ~ player.getName()|urlencode) }}">{{ player.getName() }}</a>{% if player.isDeleted() %}<span style="color: red"><b> [ DELETED ] </b></span>{% endif %}
</td> </td>
<td>{{ player.getLevel() }}</td> <td>{{ player.getLevel() }}</td>
<td>{{ player.getVocationName() }}</td> <td>{{ player.getVocationName() }}</td>

View File

@@ -0,0 +1,54 @@
Please enter new password to your account and repeat to make sure you remember password.<BR>
<form action="{{ getLink('account/lost/email/set-new-password') }}" method="post">
{{ csrf() }}
<input type="hidden" name="character" value="{{ character }}">
<input type="hidden" name="code" value="{{ code }}">
<table class="myaac-table" style="width: 100%;">
<thead>
<tr>
<th class="white"><b>Passwords</b></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<table>
<tr>
<td>
<label for="password">New password:</label>
</td>
<td>
<input type="password" id="password" name="password" value="" size="40">
</td>
</tr>
<tr>
<td>
<label for="password_repeat">Repeat new password:</label>
</td>
<td>
<input type="password" id="password_repeat" name="password_repeat" value="" size="40">
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<br/>
<table style="width: 100%">
<tr>
<td>
<div style="text-align: center">
{% set button_name = 'Submit' %}
{% include('buttons.base.html.twig') %}
</div>
</td>
</tr>
</table>
</form>

View File

@@ -0,0 +1,33 @@
Please enter code from e-mail and name of one character from account. Then press Submit.<br/>
<form action="{{ getLink('account/lost/check-code') }}" method="post">
{{ csrf() }}
<table class="myaac-table" style="width: 100%;">
<thead>
<tr>
<th class="white">
<b>Code & character name</b>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
Your code:&nbsp;<input type="text" name="code" value="{{ code }}" size="40"><br/>
Character:&nbsp;<input type="text" name="character" value="{{ character }}" size="40"><br/>
</td>
</tr>
</tbody>
</table>
<br>
<table style="width: 100%">
<tr>
<td align="center">
{% set button_name = 'Submit' %}
{% include('buttons.base.html.twig') %}
</td>
</tr>
</table>
</form>

View File

@@ -0,0 +1,54 @@
Please enter e-mail to account with this character.<br/>
<form action="{{ getLink('account/lost/email/send-code') }}" method="post">
{{ csrf() }}
<input type=hidden name="character">
<table class="myaac-table" style="width: 100%;">
<thead>
<tr>
<th class="white"><b>Please enter e-mail to account</b></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<table>
<tr>
<td>
<label for="nick">Character:</label>
</td>
<td>
<input type=text id="nick" name="nick" value="{{ nick }}" size="40" readonly="readonly">
</td>
</tr>
<tr>
<td>
<label for="name">E-mail to account:</label>
</td>
<td>
<input type="email" id="name" name="email" value="" size="40">
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<br>
<table style="width: 100%">
<tr>
<td>
<div style="text-align:center">
{% set button_name = 'Submit' %}
{% include('buttons.base.html.twig') %}
</div>
</td>
</tr>
</table>
</form>

View File

@@ -0,0 +1,58 @@
Your account name, new password and new e-mail.<br/>
<table class="myaac-table" style="width: 100%;">
<thead>
<tr>
<th class="white">
<b>Your account name, new password and new e-mail</b>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<table>
<tr>
<td>
Account name:
</td>
<td>
<b>{{ account.getName() }}</b>
</td>
</tr>
<tr>
<td>
New password:
</td>
<td>
<b>{{ newPassword }}</b>
</td>
</tr>
<tr>
<td>
New e-mail address:
</td>
<td>
<b>{{ newEmail }}</b>
</td>
</tr>
</table>
{{ statusMsg|raw }}
</td>
</tr>
</tbody>
</table>
<br>
<table style="width: 100%">
<tr>
<td align="center">
<form action="{{ getLink('account/manage') }}" method="post">
{{ include('buttons.login.html.twig') }}
</form>
</td>
</tr>
</table>

View File

@@ -0,0 +1,30 @@
New password to your account is below. Now you can log in.<BR>
<table class="myaac-table" style="width: 100%;">
<thead>
<tr>
<th class="white"><b>Changed password</b></th>
</tr>
</thead>
<tbody>
<tr>
<td>
New password:&nbsp;<b>{{ newPassword }}</b><br/>
Account name:&nbsp;&nbsp;&nbsp;<i>(Already on your e-mail)</i><br/>
{{ statusMsg|raw }}
</td>
</tr>
</tbody>
</table>
<br/>
<table style="width: 100%">
<tr>
<td align="center">
<form action="{{ getLink('account/manage') }}">
{% set button_name = 'Login' %}
{% include('buttons.base.html.twig') %}
</form>
</td>
</tr>
</table>

View File

@@ -0,0 +1,43 @@
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/step-1') }}" method="post">
{{ csrf() }}
<input type="hidden" name="character" value="">
<table class="myaac-table" style="width: 100%">
<thead>
<tr>
<th class="white"><b>Please enter your character name</b></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="text" name="nick" size="40" autofocus/><br>
</td>
</tr>
</tbody>
</table>
<table style="width: 100%; border-spacing: 1px">
<tr>
<td style="padding: 4px; background: {{ config('vdarkborder') }}" class="white"><b>What do you want?</b></td>
</tr>
<tr>
<td style="padding: 4px; background: {{ config('darkborder') }}">
<input type="radio" name="action" id="action_type_email" value="email">
<label for="action_type_email"> Send me new password and my account name to account e-mail address.</label><br/>
<input type=radio name="action" id="action_type_key" value="recovery-key">
<label for="action_type_key"> I got <b>recovery key</b> and want set new password and e-mail address to my account.</label><br/>
</td>
</tr>
</table>
<br/>
<table style="width: 100%">
<tr>
<td align="center">
{% set button_name = 'Submit' %}
{% include('buttons.base.html.twig') %}
</td>
</tr>
</table>
</form>

View File

@@ -0,0 +1,10 @@
Please select action.<br/>
<table style="width: 100%">
<tr>
<td align="center">
<a href="{{ getLink('account/lost') }}">
{{ include('buttons.back.html.twig') }}
</a>
</td>
</tr>
</table>

View File

@@ -0,0 +1,53 @@
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/recovery-key/step-2') }}" method="post">
{{ csrf() }}
<table class="myaac-table" style="width: 100%;">
<thead>
<tr>
<th class="white">
<b>Please enter your recovery key</b>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<table>
<tr>
<td>
<label for="nick">Character name:</label>
</td>
<td>
<input type=text id="nick" name="nick" value="{{ nick }}" size="40" readonly="readonly">
</td>
</tr>
<tr>
<td>
<label for="key">Recovery key:</label>
</td>
<td>
<input type="text" id="key" name="key" value="{{ key }}" size="40">
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<br>
<table style="width: 100%">
<tr>
<td>
<div style="text-align:center">
{% set button_name = 'Submit' %}
{% include('buttons.base.html.twig') %}
</div>
</td>
</tr>
</table>
</form>

View File

@@ -0,0 +1,71 @@
Set new password and e-mail to your account.<br>
<form action="{{ getLink('account/lost/recovery-key/step-3') }}" method="post">
{{ csrf() }}
<input type="hidden" name="key" VALUE="{{ key }}">
<input type="hidden" name="character" value="">
<table class="myaac-table" style="width: 100%">
<thead>
<tr>
<th class="white">
<b>Please enter new password and e-mail</b>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<table>
<tr>
<td>
<label for="nick">Account of character:</label>
</td>
<td>
<input type="text" id="nick" name="nick" value="{{ nick }}" size="40" readonly="readonly">
</td>
</tr>
<tr>
<td>
<label for="password">New password:</label>
</td>
<td>
<input type="password" id="password" name="password" value="" size="40">
</td>
</tr>
<tr>
<td>
<label for="password_repeat">Repeat new password:</label>
</td>
<td>
<input type="password" id="password_repeat" name="password_repeat" value="" size="40">
</td>
</tr>
<tr>
<td>
<label for="email">New e-mail address:</label>
</td>
<td>
<input type="text" id="email" name="email" value="" size="40">
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<br>
<table style="width: 100%">
<tr>
<td align="center">
{% set button_name = 'Submit' %}
{% include('buttons.base.html.twig') %}
</td>
</tr>
</table>
</form>

View File

@@ -1,6 +1,6 @@
<div class="card card-info card-outline"> <div class="card card-info card-outline">
<div class="card-header"> <div class="card-header">
<h5 class="m-0">News: <h5 class="m-0">Changelogs:
<form method="post" class="float-right"> <form method="post" class="float-right">
{{ csrf() }} {{ csrf() }}
<input type="hidden" name="action" value="new" /> <input type="hidden" name="action" value="new" />

View File

@@ -0,0 +1,33 @@
<link rel="stylesheet" type="text/css" href="{{ constant('BASE_URL') }}tools/css/toastify.min.css">
<script type="text/javascript" src="{{ constant('BASE_URL') }}tools/js/toastify.min.js"></script>
{{ include('script.ajax-setup.html.twig') }}
<script>
$(function () {
/* Store sidebar state */
$('.sidebar-toggle').on('click',function(event) {
event.preventDefault();
const isCollapsed = $('body').hasClass('sidebar-collapse');
$.ajax({
type: 'POST',
url: '{{ constant('ADMIN_URL') }}tools/menu_collapse.php',
data : { collapse: !isCollapsed},
success : function(response) {
},
error : function(response) {
Toastify({
position: 'center',
text: response.responseText,
duration: 3000,
style: {
background: 'red',
},
escapeMarkup: false,
}).showToast();
}
});
});
});
</script>

View File

@@ -2,7 +2,8 @@
<p class="note">You are editing: {{ template }}<br/><br/> <p class="note">You are editing: {{ template }}<br/><br/>
Hint: You can drag menu items.<br/> Hint: You can drag menu items.<br/>
Hint: Add links to external sites using: <b>http://</b> or <b>https://</b> prefix.<br/> Hint: Add links to external sites using: <b>http://</b> or <b>https://</b> prefix.<br/>
Not all templates support blank and colorful links. Not all templates support blank and colorful links.<br/>
* Guest means everyone will see the link. Player means registered and logged-in user.
</p> </p>
<div class="row text-center"> <div class="row text-center">
<div class="col-md-2 col-sm-1"></div> <div class="col-md-2 col-sm-1"></div>

View File

@@ -14,42 +14,62 @@
colors[{{ cat }}] = '{{ options['default_links_color'] ?? (menuDefaultLinksColor ?? config('menu_default_color')) }}'; colors[{{ cat }}] = '{{ options['default_links_color'] ?? (menuDefaultLinksColor ?? config('menu_default_color')) }}';
{% endfor %} {% endfor %}
function confirmRemoveMenuItem(that)
{
let id = $(that).attr("id");
if (confirm('Are you sure, that you want to remove this element?')) {
$('#list-' + id.replace('remove-button-', '')).remove();
}
}
$(function () { $(function () {
const $sortable = $(".sortable"); const $sortable = $(".sortable");
$sortable.sortable(); $sortable.sortable();
$sortable.disableSelection(); $sortable.disableSelection();
$(".remove-button").on('click', function () { $(".remove-button").on('click', function () {
var id = $(this).attr("id"); confirmRemoveMenuItem(this);
$('#list-' + id.replace('remove-button-', '')).remove();
}); });
$(".add-button").on('click', function () { $(".add-button").on('click', function () {
var cat = $(this).attr("id").replace('add-button-', ''); let cat = $(this).attr("id").replace('add-button-', '');
var id = last_id[cat]; let id = last_id[cat];
last_id[cat]++; last_id[cat]++;
const color = colors[cat]; const color = colors[cat];
$('#sortable-' + cat).append('<li class="ui-state-default" id="list-' + cat + '-' + id + '"><label>Name:</label> <input type="text" name="menu[' + cat + '][]" value=""/> <label>Link:</label> <input type="text" name="menu_link[' + cat + '][]" value=""/><input type="hidden" name="menu_blank[' + cat + '][]" value="0" /> <label><input class="blank-checkbox" type="checkbox"/><span title="Open in New Window">New Window</span></label> <input class="color-picker" type="text" name="menu_color[' + cat + '][]" value="#' + color + '" /> <a class="remove-button" id="remove-button-' + cat + '-' + id + '"><i class="fas fa-trash"></i></a></li>'); //add input bo
$('#remove-button-' + cat + '-' + id).on('click', function () {
$('#list-' + $(this).attr("id").replace('remove-button-', '')).remove();
});
initializeSpectrum(); let copy = $('.ui-state-default:first').clone();
copy.attr('id', 'list-' + cat + '-' + id);
copy.find('.menu-name').val('').attr('name', 'menu[' + cat + '][]');
copy.find('.menu-link').val('').attr('name', 'menu_link[' + cat + '][]');
copy.find('.menu-access').val('0').attr('name', 'menu_access[' + cat + '][]');
copy.find('.menu-color').val(color).attr('name', 'menu_color[' + cat + '][]');
copy.find('.menu-blank').attr('name', 'menu_blank[' + cat + '][]');
copy.find('.menu-blank-checkbox').prop('checked', false);
copy.find('.remove-button').attr('id', 'remove-button-' + cat + '-' + id);
$('#sortable-' + cat).append(copy);
$('#remove-button-' + cat + '-' + id).on('click', function () {
confirmRemoveMenuItem(this);
});
}); });
$("#menus-form").on('submit', function (e) { $("#menus-form").on('submit', function (e) {
$('.blank-checkbox:not(:checked)').each(function (i, obj) { $('.menu-blank-checkbox:not(:checked)').each(function (i, obj) {
$(obj).parent().prev().val("off"); $(obj).parent().prev().val("off");
}); });
$('.blank-checkbox:checked').each(function (i, obj) { $('.menu-blank-checkbox:checked').each(function (i, obj) {
$(obj).parent().prev().val("on"); $(obj).parent().prev().val("on");
}); });
}); });
}); });
</script> </script>
<style type="text/css"> <style>
.sortable { .sortable {
list-style-type: none; list-style-type: none;
margin: 0; margin: 0;
@@ -60,24 +80,16 @@
.remove-button, .add-button { .remove-button, .add-button {
cursor: pointer; cursor: pointer;
} }
</style>
<script type="text/javascript" src="{{ constant('BASE_URL') }}tools/js/spectrum.js"></script>
<link type="text/css" rel="stylesheet" href="{{ constant('BASE_URL') }}tools/css/spectrum.css"/>
<script type="text/javascript">
$(function () {
initializeSpectrum();
});
function initializeSpectrum() { .ui-sortable-handle {
$(".color-picker").spectrum({ padding: 10px;
preferredFormat: "hex",
showInput: true,
showPalette: true,
palette: [
['black', 'white', 'blanchedalmond',
'rgb(255, 128, 0);', 'hsv 100 70 50'],
['red', 'yellow', 'green', 'blue', 'violet']
]
});
} }
</script>
.label_menu_name, .label_menu_link {
width: 45%;
}
.menu-name, .menu-link {
width: 100%;
}
</style>

View File

@@ -73,13 +73,9 @@
<!-- jQuery Form Submit No Refresh + Toastify --> <!-- jQuery Form Submit No Refresh + Toastify -->
<link rel="stylesheet" type="text/css" href="{{ constant('BASE_URL') }}tools/css/toastify.min.css"> <link rel="stylesheet" type="text/css" href="{{ constant('BASE_URL') }}tools/css/toastify.min.css">
<script type="text/javascript" src="{{ constant('BASE_URL') }}tools/js/toastify.min.js"></script> <script type="text/javascript" src="{{ constant('BASE_URL') }}tools/js/toastify.min.js"></script>
<script> {{ include('script.ajax-setup.html.twig') }}
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
<script>
const noChangesText = "No changes has been made"; const noChangesText = "No changes has been made";
$('form') $('form')

View File

@@ -17,7 +17,7 @@
{% endif %} {% endif %}
<table border="0" cellspacing="1" cellpadding="4" width="100%"> <table border="0" cellspacing="1" cellpadding="4" width="100%">
{% if config.characters.outfit %} {% if config.characters.outfit %}
<div style="width:64px;height:64px;border:2px solid #F1E0C6; border-radius:50px; padding:13px; margin-top:38px;margin-left:376px;position:absolute;"><img style="margin-left:{% if player.getLookType() in setting('core.outfit_images_wrong_looktypes') %}-0px;margin-top:-0px;width:64px;height:64px;{% else %}-60px;margin-top:-60px;width:128px;height:128px;{% endif %}" src="{{ outfit }}" alt="player outfit"/></div> <div style="width:64px;height:64px;border:2px solid #F1E0C6; border-radius:50px; padding:13px; margin-top:38px;margin-left:376px;position:absolute;"><img style="margin-left:-60px;margin-top:-60px;width:128px;height:128px;" src="{{ outfit }}" alt="player outfit"/></div>
{% endif %} {% endif %}
<tr bgcolor="{{ config.vdarkborder }}"> <tr bgcolor="{{ config.vdarkborder }}">

View File

@@ -43,7 +43,8 @@
</tr> </tr>
</table> </table>
<div style="text-align:center"> <div style="text-align:center">
<input type="submit" value="Post Reply" /> {% set button_name = 'Post Reply' %}
{{ include('buttons.base.html.twig') }}
</div> </div>
</form> </form>

View File

@@ -44,6 +44,7 @@
</tr> </tr>
</table> </table>
<div style="text-align:center"> <div style="text-align:center">
<input type="submit" value="Post Thread" /> {% set button_name = 'Post Thread' %}
{{ include('buttons.base.html.twig') }}
</div> </div>
</form> </form>

View File

@@ -0,0 +1,57 @@
<a href="{{ getLink('forum') }}">Boards</a> >> <b>{{ section_name }}</b>
{% if (logged and (not closed or is_moderator)) %}
<br/><br/>
<a href="{{ getLink('forum') }}?action=new_thread&section_id={{ section_id }}"><img src="images/forum/topic.gif" border="0" /></a>
{% endif %}
{% if threads|length > 0 %}
<br/><br/>Page: {{ links_to_pages|raw }}<br/>
<table width="100%">
<tr bgcolor=" {{ config.vdarkborder }}" align="center">
<td class="white"><span style="font-size: 10px"><b>Thread</b></span></td>
<td class="white"><span style="font-size: 10px"><b>Thread Starter</b></span></td>
<td class="white"><span style="font-size: 10px"><b>Replies</b></span></td>
<td class="white"><span style="font-size: 10px"><b>Views</b></span></td>
<td class="white"><span style="font-size: 10px"><b>Last Post</b></span></td>
</tr>
{% set i = 0 %}
{% for thread in threads %}
<tr bgcolor="{{ getStyle(i) }}">
<td>
{% if is_moderator %}
<a href="{{ getLink('forum') }}?action=move_thread&id={{ thread.id }}" title="Move Thread"><img src="images/icons/arrow_right.gif"/></a>
{{ include('forum.remove_post.html.twig', {post: thread}) }}
{% endif %}
<a href="{{ thread.link }}">{{ thread.post_topic }}</a><br />
<small>{{ thread.post_shortened|raw }}...</small>
</td>
<td>{{ thread.player_link|raw }}</td>
<td>{{ thread.replies }}</td>
<td>{{ thread.views }}</td>
<td>
{% if thread.last_post > 0 %}
{% if thread.latest_post.name is defined %}
{{ thread.latest_post.post_date|date('d.m.y H:i:s') }}<br />by {{ thread.latest_post.player_link|raw }}
{% else %}
No posts.
{% endif %}
{% else %}
{{ thread.post_date|date('d.m.y H:i:s') }}<br />by {{ thread.player_link|raw }}
{% endif %}
</td>
</tr>
{% set i = i + 1 %}
{% endfor %}
</table>
{% else %}
<h3>No threads in this board.</h3>
{% endif %}
{% if(logged and (not closed or is_moderator)) %}
<br /><a href="{{ getLink('forum') }}?action=new_thread&section_id={{ section_id }}"><img src="images/forum/topic.gif" border="0" /></a>
{% endif %}

View File

@@ -24,7 +24,7 @@ Page: {{ links_to_pages|raw }}<br/>
{% set i = i + 1 %} {% set i = i + 1 %}
<td valign="top">{{ post.player_link|raw }}<br/> <td valign="top">{{ post.player_link|raw }}<br/>
{% if post.outfit is defined %} {% if post.outfit is defined %}
<img style="margin-left:{% if post.player.getLookType() in setting('core.outfit_images_wrong_looktypes') %}-0px;margin-top:-0px;width:64px;height:64px;{% else %}-60px;margin-top:-60px;width:128px;height:128px;{% endif %}" src="{{ post.outfit }}" alt="player outfit"/> <img style="margin-left:-60px;margin-top:-60px;width:128px;height:128px;" src="{{ post.outfit }}" alt="player outfit"/>
<br /> <br />
{% endif %} {% endif %}
<span style="font-size: 10px"> <span style="font-size: 10px">

View File

@@ -6,7 +6,7 @@
<td class="white"><b>Leave guild</b></td></tr> <td class="white"><b>Leave guild</b></td></tr>
{% if players|length > 0 %} {% if players|length > 0 %}
<tr bgcolor="{{ config.lightborder }}"> <tr bgcolor="{{ config.lightborder }}">
<td width="100%">Select character to leave guild:</td> <td width="100%">Select a character to leave the guild:</td>
</tr> </tr>
<tr bgcolor="{{ config.darkborder }}"> <tr bgcolor="{{ config.darkborder }}">
<td> <td>

View File

@@ -105,7 +105,8 @@ Here you can change names of ranks, delete and add ranks, pass leadership to oth
<form action="{{ getLink('guilds') }}?guild={{ guild.getName() }}&action=delete_rank" method="post" style="display: inline;"> <form action="{{ getLink('guilds') }}?guild={{ guild.getName() }}&action=delete_rank" method="post" style="display: inline;">
{{ csrf() }} {{ csrf() }}
<input type="hidden" name="rankid" value="{{ rank.getId() }}" /> <input type="hidden" name="rankid" value="{{ rank.getId() }}" />
<input type="image" src="/images/news/delete.png" border="0" alt="Delete" />
<input type="submit" value="Delete" />
</form> </form>
</td> </td>
<td> <td>

View File

@@ -18,7 +18,7 @@
<label for="vocationFilter">Choose a vocation</label> <label for="vocationFilter">Choose a vocation</label>
<select onchange="location = this.value;" id="vocationFilter"> <select onchange="location = this.value;" id="vocationFilter">
<option value="{{ getLink('highscores') }}/{{ list|urlencode }}" class="size_xs">[ALL]</option> <option value="{{ getLink('highscores') }}/{{ list|urlencode }}" class="size_xs">[ALL]</option>
{% for i in 0..config.vocations_amount %} {% for i in baseVocations %}
<option value="{{ getLink('highscores') }}/{{ list|urlencode }}/{{ config.vocations[i]|lower|urlencode }}" class="size_xs" {% if vocationId is not null and vocationId == i %}selected{% endif %}>{{ config.vocations[i]}}</option> <option value="{{ getLink('highscores') }}/{{ list|urlencode }}/{{ config.vocations[i]|lower|urlencode }}" class="size_xs" {% if vocationId is not null and vocationId == i %}selected{% endif %}>{{ config.vocations[i]}}</option>
{% endfor %} {% endfor %}
</select> </select>
@@ -120,7 +120,7 @@
<tr bgcolor="{{ config.lightborder }}"> <tr bgcolor="{{ config.lightborder }}">
<td> <td>
<a href="{{ getLink('highscores') }}/{{ list|urlencode }}" class="size_xs">[ALL]</a><br/> <a href="{{ getLink('highscores') }}/{{ list|urlencode }}" class="size_xs">[ALL]</a><br/>
{% for i in 0..config.vocations_amount %} {% for i in baseVocations %}
<a href="{{ getLink('highscores') }}/{{ list|urlencode }}/{{ config.vocations[i]|lower|urlencode }}" class="size_xs">{{ config.vocations[i]}}</a><br/> <a href="{{ getLink('highscores') }}/{{ list|urlencode }}/{{ config.vocations[i]|lower|urlencode }}" class="size_xs">{{ config.vocations[i]}}</a><br/>
{% endfor %} {% endfor %}
</td> </td>

View File

@@ -0,0 +1,10 @@
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/check-code') }}?code={{ newCode }}&character={{ nick|urlencode }}">{{ getLink('account/lost/check-code') }}?code={{ newCode }}&character={{ nick|urlencode }}</a>
</p>
<p>or open page: <i>{{ getLink('account/lost/check-code') }}</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.

View File

@@ -0,0 +1,7 @@
<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="{{ constant('BASE_URL') }}"><b>{{ config('lua')['serverName'] }}</b></a></p>
<p>Account name: <b>{{ account.getName() }}</b></p>
<p>New password: <b>{{ newPassword }}</b></p>
<p>E-mail: <b>{{ newEmail }}</b> (this e-mail)</p>
<br/>
<p><u>It's automatic e-mail from OTS Lost Account System. Do not reply!</u></p>

View File

@@ -0,0 +1,6 @@
<h3>Your account name and password!</h3>
<p>Changed password to your account in Lost Account Interface on server <a href="{{ constant('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>

View File

@@ -4,55 +4,66 @@
<br/> <br/>
{% endif %} {% endif %}
<br/>
{# vocation statistics #} {# vocation statistics #}
{% if setting('core.online_vocations') %} {% if setting('core.online_vocations') %}
<br/>
{% set title = 'Vocation statistics' %}
{% set tableClass = 'Table3' %}
{% set background = config('darkborder') %}
{% set content %}
{% if setting('core.online_vocations_images') %} {% if setting('core.online_vocations_images') %}
<table width="200" cellspacing="1" cellpadding="0" border="0" align="center"> <table width="200" cellspacing="1" cellpadding="0" border="0" align="center" class="myaac-table">
<tr bgcolor="{{ config.darkborder }}"> <thead>
<td><img src="images/sorcerer.png" /></td> <tr>
<td><img src="images/druid.png" /></td> {% for vocationId in baseVocations %}
<td><img src="images/paladin.png" /></td> <td style="text-align: center;"><strong>{{ config('vocations')[vocationId] }}s</strong></td>
<td><img src="images/knight.png" /></td> {% endfor %}
</tr> </tr>
<tr bgcolor="{{ config.vdarkborder }}"> </thead>
<td class="white" style="text-align: center;"><strong>Sorcerers</strong></td>
<td class="white" style="text-align: center;"><strong>Druids</strong></td> <tr>
<td class="white" style="text-align: center;"><strong>Paladins</strong></td> {% for vocationId in baseVocations %}
<td class="white" style="text-align: center;"><strong>Knights</strong></td> <td><img src="images/{{ config('vocations')[vocationId]|lower }}.png" width="150" height="200"/></td>
{% endfor %}
</tr> </tr>
<tr bgcolor="{{ config.lightborder }}">
<td style="text-align: center;">{{ vocs[1] }}</td> <tr>
<td style="text-align: center;">{{ vocs[2] }}</td> {% for vocationId in baseVocations %}
<td style="text-align: center;">{{ vocs[3] }}</td> <td style="text-align: center;">{{ vocs[vocationId] }}</td>
<td style="text-align: center;">{{ vocs[4] }}</td> {% endfor %}
</tr> </tr>
</table> </table>
<div style="text-align: center;">&nbsp;</div> <div style="text-align: center;">&nbsp;</div>
{% else %} {% else %}
<table border="0" cellspacing="1" cellpadding="4" width="100%"> <table border="0" cellspacing="1" cellpadding="4" width="100%" class="myaac-table">
<tr bgcolor="{{ config.vdarkborder }}"> {% set i = 0 %}
<td class="white" colspan="2"><b>Vocation statistics</b></td> {% for vocationId in baseVocations %}
<tr>
<td width="25%">{{ config.vocations[vocationId] }}</td>
<td width="75%">{{ vocs[vocationId] }}</td>
</tr> </tr>
{% for i in 1..config.vocations_amount %} {% set i = i + 1 %}
<tr bgcolor="{{ getStyle(i) }}">
<td width="25%">{{ config.vocations[i] }}</td>
<td width="75%">{{ vocs[i] }}</td>
</tr>
{% endfor %} {% endfor %}
</table> </table>
<br/> <br/>
{% endif %} {% endif %}
{% endset %}
{% include 'tables.headline.html.twig' %}
{% endif %} {% endif %}
<br/> <br/>
{# show skulls #} {# show skulls #}
{% if setting('core.online_skulls') %} {% if setting('core.online_skulls') %}
<table width="100%" cellspacing="1"> <table width="100%" cellspacing="1" class="myaac-table">
<tr> <tr>
<td style="background: {{ config.darkborder }};" align="center"> <td align="center">
<img src="images/white_skull.gif"/> - 1 - 6 Frags<br/> <img src="images/white_skull.gif"/> - 1 - 6 Frags<br/>
<img src="images/red_skull.gif"/> - 6+ Frags or Red Skull<br/> <img src="images/red_skull.gif"/> - 6+ Frags or Red Skull<br/>
<img src="images/black_skull.gif"/> - 10+ Frags or Black Skull <img src="images/black_skull.gif"/> - 10+ Frags or Black Skull
@@ -125,23 +136,24 @@
{% set title = 'Players Online' %} {% set title = 'Players Online' %}
{% set tableClass = 'Table2' %} {% set tableClass = 'Table2' %}
{% set content %} {% set content %}
<table width="100%"> <table width="100%" class="myaac-table">
<tr class="LabelH" style="position: relative; z-index: 20;">
<tr class="LabelH" style="z-index: 20;">
{% if setting('core.account_country') %} {% if setting('core.account_country') %}
<td width="11px"><a href="{{ getLink('online')}}?order=country_{{ order == 'country_asc' ? 'desc' : 'asc' }}">#&#160;&#160;</a> <td style="width: 6px;"><a href="{{ getLink('online')}}?order=country_{{ order == 'country_asc' ? 'desc' : 'asc' }}">#&#160;&#160;</a>
</td> </td>
{% endif %} {% endif %}
{% if setting('core.online_outfit') %} {% if setting('core.online_outfit') %}
<td><b>Outfit</b></td> <td style="width: 32px;"><b>Outfit</b></td>
{% endif %} {% endif %}
<td style="text-align:left; width:50%">Name&#160;&#160; <td style="text-align:left;">Name&#160;&#160;
<small style="font-weight:normal">[<a href="{{ getLink('online')}}?order=name_{{ order == 'name_asc' ? 'desc' : 'asc' }}">sort</a>]</small> <small style="font-weight:normal">[<a href="{{ getLink('online')}}?order=name_{{ order == 'name_asc' ? 'desc' : 'asc' }}">sort</a>]</small>
<img class="sortarrow" src="images/{{ order == 'name_asc' ? 'order_desc' : (order == 'name_desc' ? 'order_asc' : 'news/blank') }}.gif"/></td> <img class="sortarrow" src="images/{{ order == 'name_asc' ? 'order_desc' : (order == 'name_desc' ? 'order_asc' : 'news/blank') }}.gif"/></td>
<td style="text-align:left;width:30%">Level&#160;&#160; <td style="text-align:left;width:10%">Level&#160;&#160;
<small style="font-weight:normal">[<a href="{{ getLink('online')}}?order=level_{{ order == 'level_asc' ? 'desc' : 'asc' }}">sort</a>]</small> <small style="font-weight:normal">[<a href="{{ getLink('online')}}?order=level_{{ order == 'level_asc' ? 'desc' : 'asc' }}">sort</a>]</small>
<img class="sortarrow" src="images/{{ order == 'level_asc' ? 'order_desc' : (order == 'level_desc' ? 'order_asc' : 'news/blank') }}.gif"/> <img class="sortarrow" src="images/{{ order == 'level_asc' ? 'order_desc' : (order == 'level_desc' ? 'order_asc' : 'news/blank') }}.gif"/>
</td> </td>
<td style="text-align:left;width:50%">Vocation&#160;&#160; <td style="text-align:left;width:20%">Vocation&#160;&#160;
<small style="font-weight:normal">[<a href="{{ getLink('online')}}?order=vocation_{{ order == 'vocation_asc' ? 'desc' : 'asc' }}">sort</a>]</small> <small style="font-weight:normal">[<a href="{{ getLink('online')}}?order=vocation_{{ order == 'vocation_asc' ? 'desc' : 'asc' }}">sort</a>]</small>
<img class="sortarrow" src="images/{{ order == 'vocation_asc' ? 'order_desc' : (order == 'vocation_desc' ? 'order_asc' : 'news/blank') }}.gif"/> <img class="sortarrow" src="images/{{ order == 'vocation_asc' ? 'order_desc' : (order == 'vocation_desc' ? 'order_asc' : 'news/blank') }}.gif"/>
</td> </td>
@@ -151,20 +163,20 @@
{% for player in players %} {% for player in players %}
{% set i = i + 1 %} {% set i = i + 1 %}
<tr style="background: {{ getStyle(i) }}; text-align: right; height: 40px;"> <tr style="text-align: right; height: 40px;">
{% if setting('core.account_country') %} {% if setting('core.account_country') %}
<td>{{ player.country_image|raw }}</td> <td>{{ player.country_image|raw }}</td>
{% endif %} {% endif %}
{% if setting('core.online_outfit') %} {% if setting('core.online_outfit') %}
<td width="5%"><img style="position:absolute;margin-top:-48px;margin-left:-70px;" src="{{ player.outfit }}" alt="player outfit"/></td> <td><img style="position:absolute;margin-top:-48px;margin-left:-70px;" src="{{ player.outfit }}" alt="player outfit"/></td>
{% endif %} {% endif %}
<td style="width:70%; text-align:left"> <td style="text-align:left">
{{ player.name|raw }}{{ player.skull|raw }} {{ player.name|raw }}{{ player.skull|raw }}
</td> </td>
<td style="width:10%">{{ player.level }}</td> <td>{{ player.level }}</td>
<td style="width:20%">{{ player.vocation }}</td> <td>{{ player.vocation }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>

View File

@@ -0,0 +1,7 @@
<script>
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
</script>

View File

@@ -47,7 +47,7 @@
{% if setting('core.team_outfit') %} {% if setting('core.team_outfit') %}
<td> <td>
<img style="position: absolute; margin-top: {% if member.player.looktype in setting('core.outfit_images_wrong_looktypes') %}-16px;margin-left:-0px;{% else %} -45px; margin-left: -30px;{% endif %}" src="{{ member.outfit }}" alt="player outfit"/> <img style="position: absolute; margin-top: -50px; margin-left: -30px;" src="{{ member.outfit }}" alt="player outfit"/>
</td> </td>
{% endif %} {% endif %}
@@ -127,7 +127,7 @@
<tr bgcolor="{{ getStyle(i) }}" style="height: 32px;"> <tr bgcolor="{{ getStyle(i) }}" style="height: 32px;">
{% if setting('core.team_outfit') %} {% if setting('core.team_outfit') %}
<td> <td>
<img style="position: absolute; margin-top: {% if member.player.looktype in setting('core.outfit_images_wrong_looktypes') %}-16px;margin-left:-0px;{% else %} -45px; margin-left: -30px;{% endif %}" src="{{ member.outfit }}" alt="player outfit"/> <img style="position: absolute; margin-top: -50px; margin-left: -30px;" src="{{ member.outfit }}" alt="player outfit"/>
</td> </td>
{% endif %} {% endif %}

View File

@@ -46,7 +46,7 @@
{% for player in topPlayers %} {% for player in topPlayers %}
<div style="text-align:left"><a href="{{ getPlayerLink(player['name'], false) }} " class="topfont {% if player['online'] %}online{% else %}offline{% endif %}"> <div style="text-align:left"><a href="{{ getPlayerLink(player['name'], false) }} " class="topfont {% if player['online'] %}online{% else %}offline{% endif %}">
{% if setting('core.online_outfit') %} {% if setting('core.online_outfit') %}
<img style="position:absolute;margin-top:{% if player.looktype in setting('core.outfit_images_wrong_looktypes') %}-20px;margin-left:-0px;{% else %}-45px;margin-left:-25px;{% endif %}" src="{{ player.outfit }}" alt="player outfit"/> <img style="position:absolute;margin-top:-45px;margin-left:-25px;" src="{{ player.outfit }}" alt="player outfit"/>
{% endif %} {% endif %}
<span style="color: #CCC; margin-left: 40px">{{ player['rank'] }} - </span> <span style="color: #CCC; margin-left: 40px">{{ player['rank'] }} - </span>
{{ player['name'] }} {{ player['name'] }}

Some files were not shown because too many files have changed in this diff Show More