Compare commits

..

2 Commits

Author SHA1 Message Date
dependabot[bot]
c82e537dc7 Bump qs from 6.14.1 to 6.14.2 (#353)
Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.14.2.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.14.2)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-15 01:23:24 +01:00
slawkens
8b10f85bc1 Start v1.8.9-dev 2026-02-04 18:37:03 +01:00
114 changed files with 4671 additions and 2940 deletions

View File

@@ -1,9 +1,9 @@
name: Cypress
on:
pull_request:
branches: [develop]
branches: [main]
push:
branches: [develop]
branches: [main]
jobs:
cypress:
@@ -23,7 +23,7 @@ jobs:
fail-fast: false
matrix:
php-versions: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
ots: ['tfs-1.4', 'canary-3.1.2', 'tfs-0.3'] # TODO: add 'tfs-master' (actually doesn't work cause AAC doesn't support reading .env configuration)
ots: ['tfs-1.4', 'canary-3.1.2'] # TODO: add 'tfs-master' (actually doesn't work cause AAC doesn't support reading .env configuration)
name: Cypress (PHP ${{ matrix.php-versions }}, ${{ matrix.ots }})
steps:
- name: 📌 MySQL Start & init & show db
@@ -58,14 +58,6 @@ jobs:
ref: master
path: ots
- name: Checkout TFS 0.3
uses: actions/checkout@v4
if: matrix.ots == 'tfs-0.3'
with:
repository: otland/tfs-old-svn
ref: 0.3
path: ots
- name: Checkout Canary
uses: actions/checkout@v4
if: matrix.ots == 'canary-3.1.2'
@@ -75,15 +67,9 @@ jobs:
path: ots
- name: Import OTS Schema
if: matrix.ots != 'tfs-0.3'
run: |
mysql -uroot -proot myaac < ots/schema.sql
- name: Import OTS Schema (TFS 0.3)
if: matrix.ots == 'tfs-0.3'
run: |
mysql -uroot -proot myaac < ots/schemas/mysql.sql
- name: Rename config.lua
run: mv ots/config.lua.dist ots/config.lua
@@ -123,33 +109,6 @@ jobs:
regex: false
include: 'ots/config.lua'
- name: Replace mysqlPass (TFS 0.3.6pl1)
uses: jacobtomlinson/gha-find-replace@v3
if: matrix.ots == 'tfs-0.3'
with:
find: 'sqlType = "sqlite"'
replace: 'sqlType = "mysql"'
regex: false
include: 'ots/config.lua'
- name: Replace mysqlPass (TFS 0.3.6pl1)
uses: jacobtomlinson/gha-find-replace@v3
if: matrix.ots == 'tfs-0.3'
with:
find: 'sqlPass = ""'
replace: 'sqlPass = "root"'
regex: false
include: 'ots/config.lua'
- name: Replace mysqlDatabase (Canary)
uses: jacobtomlinson/gha-find-replace@v3
if: matrix.ots == 'tfs-0.3'
with:
find: 'sqlDatabase = "theforgottenserver"'
replace: 'sqlDatabase = "myaac"'
regex: false
include: 'ots/config.lua'
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:

View File

@@ -1,9 +1,9 @@
name: PHP Linting
on:
pull_request:
branches: [develop]
branches: [main]
push:
branches: [develop]
branches: [main]
jobs:
phplint:

View File

@@ -2,9 +2,9 @@ name: "PHPStan"
on:
pull_request:
branches: [develop]
branches: [main]
push:
branches: [develop]
branches: [main]
jobs:
tests:
@@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
php-versions: [ '8.1', '8.2', '8.3', '8.4' ]
steps:
- name: "Checkout"
uses: "actions/checkout@v4"

View File

@@ -1,21 +0,0 @@
## [2.0-dev - x.x.2025]
### Added
* Add an "access" option to Menus (#340)
* Possibility to hide menus for unauthorized users
* Add the possibility to fetch skills, balance and frags in the getTopPlayers function (#347)
### Changed
* Better handling of vocations: (#345)
* Load from vocations.xml (No need to manually set)
* Support for Monk vocation
* Better gallery, loads images from images/gallery folder
* Reworked account action logs to use a single IP column as varchar(45) for both ipv4 and ipv6 (#289)
* Admin Panel: save menu collapse state (https://github.com/slawkens/myaac/commit/55da00520df7463a1d1ca41931df1598e9f2ffeb)
### Internal
* Refactor account/lost pages (#326)
* Refactor OTS_Player to support more distros (#348)
* Refactor PHP cache to store expiration and improve typing (https://github.com/slawkens/myaac/commit/96b8e00f4999f8b4c4c97b54b97d91c6fd7df298)
* Move forum show_board code to Twig (https://github.com/slawkens/myaac/commit/e0e0e467012a5fb9979cc4387af6bad1d4540279)
* Save db cache only if it has changed (https://github.com/slawkens/myaac/commit/11cb1cf97e74f3bccf59360e1efb800a426b3d43)

View File

@@ -9,7 +9,6 @@
*/
use MyAAC\Models\Account as AccountModel;
use MyAAC\Models\AccountAction;
use MyAAC\Models\Player;
defined('MYAAC') or die('Direct access not allowed!');
@@ -183,7 +182,39 @@ else if (isset($_REQUEST['search'])) {
$account->setName($name);
}
if ($hasTypeColumn) {
$account->setCustomField('type', $group);
} elseif ($hasGroupColumn) {
$account->setCustomField('group_id', $group);
}
if ($hasSecretColumn) {
$account->setCustomField('secret', $secret);
}
$account->setCustomField('key', $key);
$account->setEMail($email);
if (HAS_ACCOUNT_COINS) {
$account->setCustomField('coins', $t_coins);
}
if (HAS_ACCOUNT_COINS_TRANSFERABLE || HAS_ACCOUNT_TRANSFERABLE_COINS) {
$account->setCustomField(ACCOUNT_COINS_TRANSFERABLE_COLUMN, $t_coins_transferable);
}
$lastDay = 0;
if($p_days != 0 && $p_days != OTS_Account::GRATIS_PREMIUM_DAYS) {
$lastDay = time();
} else if ($lastDay != 0) {
$lastDay = 0;
}
$account->setPremDays($p_days);
$account->setLastLogin($lastDay);
if ($hasPointsColumn) {
$account->setCustomField('premium_points', $p_points);
}
$account->setRLName($rl_name);
$account->setLocation($rl_loca);
@@ -191,18 +222,9 @@ else if (isset($_REQUEST['search'])) {
$account->setCountry($rl_country);
}
$account->setCustomField('created', $created);
$account->setWebFlags($web_flags);
if (!isCanary()) {
$lastDay = 0;
if($p_days != 0 && $p_days != OTS_Account::GRATIS_PREMIUM_DAYS) {
$lastDay = time();
}
$account->setLastLogin($lastDay);
}
$account->setPremDays($p_days);
$account->setCustomField('web_lastlogin', $web_lastlogin);
if (isset($password)) {
if (USE_ACCOUNT_SALT) {
@@ -216,34 +238,6 @@ else if (isset($_REQUEST['search'])) {
}
$account->save();
if ($hasTypeColumn) {
$account->setCustomField('type', $group);
} elseif ($hasGroupColumn) {
$account->setCustomField('group_id', $group);
}
if ($hasSecretColumn) {
$account->setCustomField('secret', $secret);
}
$account->setCustomField('key', $key);
if (HAS_ACCOUNT_COINS) {
$account->setCustomField('coins', $t_coins);
}
if (HAS_ACCOUNT_COINS_TRANSFERABLE || HAS_ACCOUNT_TRANSFERABLE_COINS) {
$account->setCustomField(ACCOUNT_COINS_TRANSFERABLE_COLUMN, $t_coins_transferable);
}
if ($hasPointsColumn) {
$account->setCustomField('premium_points', $p_points);
}
$account->setCustomField('created', $created);
$account->setCustomField('web_lastlogin', $web_lastlogin);
echo_success('Account saved at: ' . date('G:i'));
}
}
@@ -487,8 +481,9 @@ else if (isset($_REQUEST['search'])) {
</thead>
<tbody>
<?php
$accountActions = AccountAction::where('account_id', $account->getId())->orderByDesc('date')->get();
$accountActions = \MyAAC\Models\AccountAction::where('account_id', $account->getId())->orderByDesc('date')->get();
foreach ($accountActions as $i => $log):
$log->ip = ($log->ip != 0 ? long2ip($log->ip) : inet_ntop($log->ipv6));
?>
<tr>
<td><?php echo $i + 1; ?></td>
@@ -636,7 +631,6 @@ else if (isset($_REQUEST['search'])) {
</div>
</form>
</div>
<?php if (USE_ACCOUNT_NAME): ?>
<div class="col-6 col-lg-12">
<form action="<?php echo $admin_base; ?>" method="post">
<?php csrf(); ?>
@@ -647,7 +641,6 @@ else if (isset($_REQUEST['search'])) {
</div>
</form>
</div>
<?php endif; ?>
<div class="col-6 col-lg-12">
<form action="<?php echo $admin_base; ?>" method="post">
<?php csrf(); ?>

View File

@@ -23,7 +23,6 @@ if (!hasFlag(FLAG_CONTENT_MENUS) && !superAdmin()) {
}
$pluginThemes = Plugins::getThemes();
$groups = new OTS_Groups_List();
if (isset($_POST['template'])) {
$template = $_POST['template'];
@@ -33,8 +32,6 @@ if (isset($_POST['template'])) {
$post_menu_link = $_POST['menu_link'] ?? [];
$post_menu_blank = $_POST['menu_blank'] ?? [];
$post_menu_color = $_POST['menu_color'] ?? [];
$post_menu_access = $_POST['menu_access'] ?? [];
if (count($post_menu) != count($post_menu_link)) {
echo 'Menu count is not equal menu links. Something went wrong when sending form.';
return;
@@ -53,7 +50,6 @@ if (isset($_POST['template'])) {
'link' => $post_menu_link[$category][$i],
'blank' => $post_menu_blank[$category][$i] == 'on' ? 1 : 0,
'color' => str_replace('#', '', $post_menu_color[$category][$i]),
'access' => $post_menu_access[$category][$i],
'category' => $category,
'ordering' => $i
]);
@@ -126,7 +122,7 @@ if (isset($_POST['template'])) {
?>
<?php
$menus = Menu::query()
->select('name', 'link', 'access', 'blank', 'color', 'category', 'ordering')
->select('name', 'link', 'blank', 'color', 'category', 'ordering')
->where('enabled', 1)
->where('template', $template)
->orderBy('ordering')
@@ -155,34 +151,11 @@ if (isset($_POST['template'])) {
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']);
?>
<li class="ui-state-default" id="list-<?php echo $id ?>-<?php echo $i ?>">
<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']); ?>"/>
</label>
<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>
<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']); ?>"/>
<label>Link:</label> <input type="text" name="menu_link[<?php echo $id ?>][]" value="<?php echo $menu['link'] ?>"/>
<input type="hidden" name="menu_blank[<?php echo $id ?>][]" value="0"/>
<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; ?>"/>
<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;
endforeach;

View File

@@ -10,7 +10,6 @@
use MyAAC\Forum;
use MyAAC\Models\Player;
use MyAAC\Server\XML\Vocations;
defined('MYAAC') or die('Direct access not allowed!');
@@ -35,7 +34,6 @@ $skills = array(
$hasBlessingsColumn = $db->hasColumn('players', 'blessings');
$hasBlessingColumn = $db->hasColumn('players', 'blessings1');
$hasLookAddons = $db->hasColumn('players', 'lookaddons');
$hasCapColumn = $db->hasColumn('players', 'cap');
$skull_type = array("None", "Yellow", "Green", "White", "Red", "Black", "Orange");
?>
@@ -168,11 +166,8 @@ else if (isset($_REQUEST['search'])) {
$town = $_POST['town'];
verify_number($town, 'Town', 11);
if ($hasCapColumn) {
$capacity = $_POST['capacity'];
verify_number($capacity, 'Capacity', 11);
}
$capacity = $_POST['capacity'];
verify_number($capacity, 'Capacity', 11);
$sex = $_POST['sex'];
verify_number($sex, 'Sex', 1);
@@ -242,30 +237,7 @@ else if (isset($_REQUEST['search'])) {
$player->setGroup($groups->getGroup($group));
$player->setLevel($level);
$player->setExperience($experience);
if ($db->hasColumn('players', 'promotion')) {
$promotion = 0;
$vocationOriginal = Vocations::getOriginal($vocation);
if ($vocation != $vocationOriginal) {
$tmpId = $vocationOriginal;
while($promoted = Vocations::getPromoted($tmpId)) {
$promotion++;
$tmpId = $promoted;
if ($promoted == $vocation) {
break;
}
}
$vocation = $vocationOriginal;
}
$player->setPromotion($promotion);
}
$player->setVocation($vocation);
$player->setHealth($health);
$player->setHealthMax($health_max);
$player->setMagLevel($magic_level);
@@ -277,20 +249,16 @@ else if (isset($_REQUEST['search'])) {
$player->setLookHead($look_head);
$player->setLookLegs($look_legs);
$player->setLookType($look_type);
if ($hasLookAddons) {
if ($hasLookAddons)
$player->setLookAddons($look_addons);
}
if ($db->hasColumn('players', 'offlinetraining_time'))
$player->setCustomField('offlinetraining_time', $offlinetraining);
$player->setPosX($pos_x);
$player->setPosY($pos_y);
$player->setPosZ($pos_z);
$player->setSoul($soul);
$player->setTownId($town);
if ($hasCapColumn) {
$player->setCap($capacity);
}
$player->setCap($capacity);
$player->setSex($sex);
$player->setLastLogin($lastlogin);
$player->setLastLogout($lastlogout);
@@ -307,11 +275,23 @@ else if (isset($_REQUEST['search'])) {
if ($hasBlessingsColumn)
$player->setBlessings($blessings);
if ($hasBlessingColumn) {
for ($i = 1; $i <= $bless_count; $i++) {
$a = 'blessing' . $i;
$player->setCustomField('blessings' . $i, ${'blessing' . $i} ? '1' : '0');
}
}
$player->setBalance($balance);
if ($db->hasColumn('players', 'stamina'))
$player->setStamina($stamina);
$player->setDeleted($deleted ? '1' : '0');
if ($db->hasColumn('players', 'deletion'))
$player->setCustomField('deletion', $deleted ? '1' : '0');
else
$player->setCustomField('deleted', $deleted ? '1' : '0');
$player->setCustomField('hide', $hide ? '1' : '0');
$player->setCustomField('created', $created);
if (isset($comment))
$player->setCustomField('comment', $comment);
foreach ($_POST['skills'] as $skill => $value) {
$player->setSkill($skill, $value);
@@ -320,24 +300,6 @@ else if (isset($_REQUEST['search'])) {
$player->setSkillTries($skill, $value);
}
$player->save();
if ($db->hasColumn('players', 'offlinetraining_time')) {
$player->setCustomField('offlinetraining_time', $offlinetraining);
}
if ($hasBlessingColumn) {
for ($i = 1; $i <= $bless_count; $i++) {
$a = 'blessing' . $i;
$player->setCustomField('blessings' . $i, ${'blessing' . $i} ? '1' : '0');
}
}
$player->setCustomField('hide', $hide ? '1' : '0');
$player->setCustomField('created', $created);
if (isset($comment)) {
$player->setCustomField('comment', $comment);
}
echo_success('Player saved at: ' . date('G:i'));
$player->load($id);
}
@@ -569,12 +531,10 @@ else if (isset($_REQUEST['search'])) {
</div>
</div>
<div class="form-group row">
<?php if($hasCapColumn): ?>
<div class="col-12 col-sm-12 col-lg-6">
<label for="capacity" class="control-label">Capacity:</label>
<input type="text" class="form-control" id="capacity" name="capacity" autocomplete="off" size="3" maxlength="11" value="<?php echo $player->getCap(); ?>"/>
</div>
<?php endif; ?>
<div class="col-12 col-sm-12 col-lg-6">
<label for="soul" class="control-label">Soul:</label>
<input type="text" class="form-control" id="soul" name="soul" autocomplete="off" size="3" maxlength="10" value="<?php echo $player->getSoul(); ?>"/>

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">
<?php $hooks->trigger(HOOK_ADMIN_HEAD_END); ?>
</head>
<body class="sidebar-mini <?= (session('admin.menu-collapse') ? 'sidebar-collapse' : ''); ?>">
<body class="sidebar-mini ">
<?php $hooks->trigger(HOOK_ADMIN_BODY_START); ?>
<?php if ($logged && admin()) { ?>
<div class="wrapper">
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link sidebar-toggle" data-widget="pushmenu" href="#"><i class="fas fa-bars"></i></a>
<a class="nav-link" data-widget="pushmenu" href="#"><i class="fas fa-bars"></i></a>
</li>
<li class="nav-item d-none d-sm-inline-block">
<a href="<?php echo ADMIN_URL; ?>" class="nav-link">Home</a>
@@ -198,7 +198,6 @@ if ($logged && admin()) {
<script src="<?php echo BASE_URL; ?>tools/js/datatables.bs.min.js"></script>
<?php } ?>
<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); ?>
</body>
</html>

View File

@@ -1,23 +0,0 @@
<?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.');
const MYAAC = true;
const MYAAC_VERSION = '2.0-dev';
const DATABASE_VERSION = 50;
const MYAAC_VERSION = '1.8.9-dev';
const DATABASE_VERSION = 46;
const TABLE_PREFIX = 'myaac_';
define('START_TIME', microtime(true));
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX'));

View File

@@ -19,8 +19,7 @@
"symfony/var-dumper": "^6.4",
"filp/whoops": "^2.15",
"maximebf/debugbar": "1.*",
"guzzlehttp/guzzle": "7.9.3",
"devium/toml": "^1.0"
"guzzlehttp/guzzle": "7.9.3"
},
"require-dev": {
"phpstan/phpstan": "^1.10"

127
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "de1219601202cf55861809fd69c5feb4",
"content-hash": "5317e97a5025ebc2a977214bd3fa964c",
"packages": [
{
"name": "brick/math",
@@ -216,75 +216,6 @@
],
"time": "2024-09-19T14:15:21+00:00"
},
{
"name": "devium/toml",
"version": "1.0.6",
"source": {
"type": "git",
"url": "https://github.com/vanodevium/toml.git",
"reference": "190882f9d92e88f8031285129ba9df501da899a6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vanodevium/toml/zipball/190882f9d92e88f8031285129ba9df501da899a6",
"reference": "190882f9d92e88f8031285129ba9df501da899a6",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": ">=8.1",
"php-ds/php-ds": "^1.5",
"symfony/polyfill-mbstring": "^1.30"
},
"require-dev": {
"laravel/pint": "^1.17.3",
"pestphp/pest": "^2.35.1",
"phpstan/phpstan": "^1.12.2",
"rector/rector": "^1.2.4",
"symfony/var-dumper": "^6.4|^7.1.4"
},
"suggest": {
"ext-ds": "For best performance",
"ext-mbstring": "For best performance"
},
"type": "library",
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Devium\\Toml\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Vano Devium",
"email": "vano@devium.me"
}
],
"description": "A PHP encoder/decoder for TOML compatible with specification 1.0.0",
"keywords": [
"decode",
"encode",
"parser",
"toml"
],
"support": {
"issues": "https://github.com/vanodevium/toml/issues",
"source": "https://github.com/vanodevium/toml/tree/v1.0.6"
},
"funding": [
{
"url": "https://github.com/vanodevium",
"type": "github"
}
],
"time": "2025-04-22T18:10:54+00:00"
},
{
"name": "doctrine/inflector",
"version": "2.0.10",
@@ -1684,62 +1615,6 @@
},
"time": "2021-04-22T21:32:03+00:00"
},
{
"name": "php-ds/php-ds",
"version": "v1.7.0",
"source": {
"type": "git",
"url": "https://github.com/php-ds/polyfill.git",
"reference": "017fb5cdfa52a1f13126c94987b04b884c44f9cd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-ds/polyfill/zipball/017fb5cdfa52a1f13126c94987b04b884c44f9cd",
"reference": "017fb5cdfa52a1f13126c94987b04b884c44f9cd",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": ">=7.4"
},
"provide": {
"ext-ds": "1.5.0"
},
"require-dev": {
"php-ds/tests": "^1.5"
},
"suggest": {
"ext-ds": "to improve performance and reduce memory usage"
},
"type": "library",
"autoload": {
"psr-4": {
"Ds\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rudi Theunissen",
"email": "rudolf.theunissen@gmail.com"
}
],
"description": "Specialized data structures as alternatives to the PHP array",
"keywords": [
"data structures",
"ds",
"php",
"polyfill"
],
"support": {
"issues": "https://github.com/php-ds/polyfill/issues",
"source": "https://github.com/php-ds/polyfill/tree/v1.7.0"
},
"time": "2025-05-18T04:50:53+00:00"
},
{
"name": "phpmailer/phpmailer",
"version": "v6.9.3",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -3,7 +3,6 @@ defined('MYAAC') or die('Direct access not allowed!');
use MyAAC\Models\Changelog;
use MyAAC\Models\Config;
use MyAAC\Models\FAQ;
use MyAAC\Models\ForumBoard;
use MyAAC\Models\Gallery;
use MyAAC\Models\NewsCategory;
@@ -57,10 +56,13 @@ if (NewsCategory::count() === 0) {
}
}
if(FAQ::count() == 0) {
FAQ::create([
'question' => 'What is this?',
'answer' => 'This is website for OTS powered by MyAAC.',
if (Gallery::count() === 0) {
Gallery::create([
'comment' => 'Demon',
'image' => 'images/gallery/demon.jpg',
'thumb' => 'images/gallery/demon_thumb.gif',
'author' => 'MyAAC',
'ordering' => 0,
]);
}

View File

@@ -1,11 +1,11 @@
CREATE TABLE IF NOT EXISTS `myaac_account_actions`
(
`id` int NOT NULL AUTO_INCREMENT,
`account_id` int NOT NULL,
`ip` varchar(45) NOT NULL DEFAULT '',
`ip` int unsigned NOT NULL DEFAULT 0,
`ipv6` binary(16) NOT NULL DEFAULT 0,
`date` int NOT NULL DEFAULT 0,
`action` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
KEY (`account_id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
CREATE TABLE IF NOT EXISTS `myaac_account_emails_verify`
@@ -102,7 +102,6 @@ CREATE TABLE IF NOT EXISTS `myaac_menu`
`template` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`link` varchar(255) NOT NULL,
`access` tinyint NOT NULL DEFAULT 0,
`blank` tinyint NOT NULL DEFAULT 0,
`color` varchar(6) NOT NULL DEFAULT '',
`category` int NOT NULL DEFAULT 1,
@@ -198,6 +197,18 @@ CREATE TABLE IF NOT EXISTS `myaac_pages`
UNIQUE (`name`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
CREATE TABLE IF NOT EXISTS `myaac_gallery`
(
`id` int NOT NULL AUTO_INCREMENT,
`comment` varchar(255) NOT NULL DEFAULT '',
`image` varchar(255) NOT NULL,
`thumb` varchar(255) NOT NULL,
`author` varchar(50) NOT NULL DEFAULT '',
`ordering` int NOT NULL DEFAULT 0,
`hide` tinyint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
CREATE TABLE IF NOT EXISTS `myaac_settings`
(
`id` int NOT NULL AUTO_INCREMENT,

View File

@@ -2,6 +2,8 @@
define('MYAAC_INSTALL', true);
use MyAAC\DataLoader;
use MyAAC\Models\FAQ as ModelsFAQ;
use MyAAC\Plugins;
require_once '../../common.php';
@@ -23,9 +25,34 @@ if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['save
require SYSTEM . 'init.php';
// add player samples
require_once SYSTEM . 'migrations/49.php';
$up();
if ($db->hasTable('players')) {
$deleted = 'deleted';
if ($db->hasColumn('players', 'deletion'))
$deleted = 'deletion';
$time = time();
function insert_sample_if_not_exist($p)
{
global $db, $success, $deleted, $time;
$query = $db->query('SELECT `id` FROM `players` WHERE `name` = ' . $db->quote($p['name']));
if ($query->rowCount() == 0) {
if (!query("INSERT INTO `players` (`id`, `name`, `group_id`, `account_id`, `level`, `vocation`, `health`, `healthmax`, `experience`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `maglevel`, `mana`, `manamax`, `manaspent`, `soul`, `town_id`, `posx`, `posy`, `posz`, `conditions`, `cap`, `sex`, `lastlogin`, `lastip`, `save`, `lastlogout`, `balance`, `$deleted`, `created`, `hide`, `comment`) VALUES (null, " . $db->quote($p['name']) . ", 1, " . getSession('account') . ", " . $p['level'] . ", " . $p['vocation_id'] . ", " . $p['health'] . ", " . $p['healthmax'] . ", " . $p['experience'] . ", 118, 114, 38, 57, " . $p['looktype'] . ", 0, " . $p['mana'] . ", " . $p['manamax'] . ", 0, " . $p['soul'] . ", 1, 1000, 1000, 7, '', " . $p['cap'] . ", 1, " . $time . ", 2130706433, 1, " . $time . ", 0, 0, " . $time . ", 1, '');"))
$success = false;
}
}
$success = true;
insert_sample_if_not_exist(array('name' => 'Rook Sample', 'level' => 1, 'vocation_id' => 0, 'health' => 150, 'healthmax' => 150, 'experience' => 0, 'looktype' => 130, 'mana' => 0, 'manamax' => 0, 'soul' => 100, 'cap' => 400));
insert_sample_if_not_exist(array('name' => 'Sorcerer Sample', 'level' => 8, 'vocation_id' => 1, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Druid Sample', 'level' => 8, 'vocation_id' => 2, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Paladin Sample', 'level' => 8, 'vocation_id' => 3, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 129, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Knight Sample', 'level' => 8, 'vocation_id' => 4, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 131, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
if ($success) {
success($locale['step_database_imported_players']);
}
}
DataLoader::setLocale($locale);
DataLoader::load();
@@ -34,6 +61,10 @@ DataLoader::load();
require_once SYSTEM . 'migrations/17.php';
$up();
// update config.highscores_ids_hidden
require_once SYSTEM . 'migrations/20.php';
$up();
// add z_polls tables
require_once SYSTEM . 'migrations/22.php';
$up();
@@ -52,6 +83,13 @@ $up();
require_once SYSTEM . 'migrations/45.php';
$up();
if(ModelsFAQ::count() == 0) {
ModelsFAQ::create([
'question' => 'What is this?',
'answer' => 'This is website for OTS powered by MyAAC.',
]);
}
$hooks->trigger(HOOK_INSTALL_FINISH);
$db->setClearCacheAfter(true);

6
package-lock.json generated
View File

@@ -1743,9 +1743,9 @@
}
},
"node_modules/qs": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {

View File

@@ -5,6 +5,8 @@ $deprecatedConfig = [
'genders',
'template',
'template_allow_change',
'vocations_amount',
'vocations',
'client',
'session_prefix',
'friendly_urls',

View File

@@ -17,8 +17,6 @@ use MyAAC\Models\Guild;
use MyAAC\Models\House;
use MyAAC\Models\Pages;
use MyAAC\Models\Player;
use MyAAC\Models\PlayerDeath;
use MyAAC\Models\PlayerKillers;
use MyAAC\News;
use MyAAC\Plugins;
use MyAAC\Settings;
@@ -1141,44 +1139,11 @@ function csrfProtect(): void
}
}
function getSkillIdByName(string $name): int|null
{
$skills = [
'level' => POT::SKILL_LEVEL,
'experience' => POT::SKILL_LEVEL,
'magic' => POT::SKILL_MAGIC,
'maglevel' => POT::SKILL_MAGIC,
'balance' => SKILL_BALANCE,
'frags' => SKILL_FRAGS,
'club' => POT::SKILL_CLUB,
'sword' => POT::SKILL_SWORD,
'axe' => POT::SKILL_AXE,
'dist' => POT::SKILL_DIST,
'distance' => POT::SKILL_DIST,
'shield' => POT::SKILL_SHIELD,
'shielding' => POT::SKILL_SHIELD,
'fish' => POT::SKILL_FISH,
'fishing' => POT::SKILL_FISH,
];
return $skills[$name] ?? null;
}
function getTopPlayers($limit = 5, $skill = POT::SKILL_LEVEL)
{
function getTopPlayers($limit = 5, $skill = 'level') {
global $db;
$skillOriginal = $skill;
if (is_string($skill)) {
$skill = getSkillIdByName($skill);
}
if (!is_numeric($skill)) {
throw new RuntimeException("getTopPlayers: Invalid skill: $skillOriginal");
if ($skill === 'level') {
$skill = 'experience';
}
return Cache::remember("top_{$limit}_{$skill}", 2 * 60, function () use ($db, $limit, $skill) {
@@ -1199,64 +1164,15 @@ function getTopPlayers($limit = 5, $skill = POT::SKILL_LEVEL)
$columns[] = 'lookmount';
}
$query = Player::query()
return Player::query()
->select($columns)
->withOnlineStatus()
->notDeleted()
->where('group_id', '<', setting('core.highscores_groups_hidden'))
->whereNotIn('id', setting('core.highscores_ids_hidden'))
->where('account_id', '!=', 1)
->orderByDesc('value');
if ($limit > 0) {
$query->limit($limit);
}
if ($skill >= POT::SKILL_FIRST && $skill <= POT::SKILL_LAST) { // skills
if ($db->hasColumn('players', 'skill_fist')) {// tfs 1.0
$skill_ids = array(
POT::SKILL_FIST => 'skill_fist',
POT::SKILL_CLUB => 'skill_club',
POT::SKILL_SWORD => 'skill_sword',
POT::SKILL_AXE => 'skill_axe',
POT::SKILL_DIST => 'skill_dist',
POT::SKILL_SHIELD => 'skill_shielding',
POT::SKILL_FISH => 'skill_fishing',
);
$query
->addSelect($skill_ids[$skill] . ' as value')
->orderByDesc($skill_ids[$skill] . '_tries');
} else {
$query
->join('player_skills', 'player_skills.player_id', '=', 'players.id')
->where('skillid', $skill)
->addSelect('player_skills.value as value');
}
} else if ($skill == SKILL_FRAGS) // frags
{
if ($db->hasTable('player_killers')) {
$query->addSelect(['value' => PlayerKillers::whereColumn('player_killers.player_id', 'players.id')->selectRaw('COUNT(*)')]);
} else {
$query->addSelect(['value' => PlayerDeath::unjustified()->whereColumn('player_deaths.killed_by', 'players.name')->selectRaw('COUNT(*)')]);
}
} else if ($skill == SKILL_BALANCE) // balance
{
$query
->addSelect('players.balance as value');
} else {
if ($skill == POT::SKILL_MAGIC) {
$query
->addSelect('players.maglevel as value', 'players.maglevel')
->orderByDesc('manaspent');
} else { // level
$query
->addSelect('players.level as value', 'players.experience')
->orderByDesc('experience');
}
}
return $query
->orderByDesc($skill)
->limit($limit)
->get()
->map(function ($e, $i) {
$row = $e->toArray();
@@ -1808,8 +1724,8 @@ function getAccountIdentityColumn(): string
function isCanary(): bool
{
$dataPackDirectory = configLua('dataPackDirectory');
return isset($dataPackDirectory);
$vipSystemEnabled = configLua('vipSystemEnabled');
return isset($vipSystemEnabled);
}
function getStatusUptimeReadable(int $uptime): string

View File

@@ -14,7 +14,6 @@ use MyAAC\CsrfToken;
use MyAAC\Hooks;
use MyAAC\Plugins;
use MyAAC\Models\Town;
use MyAAC\Server\Vocations;
use MyAAC\Settings;
defined('MYAAC') or die('Direct access not allowed!');
@@ -215,5 +214,3 @@ if (count($towns) <= 0) {
config(['towns', $towns]);
unset($towns);
new Vocations();

View File

@@ -12,9 +12,6 @@
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3
*/
use MyAAC\Models\Account as AccountModel;
use MyAAC\Models\AccountAction;
/**
* OTServ account abstraction.
*
@@ -43,11 +40,7 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
*/
private $data = array('email' => '', 'rlname' => '','location' => '', 'country' => '','web_flags' => 0, 'lastday' => 0, 'premdays' => 0, 'created' => 0);
private array $columns = ['password', 'email', 'rlname', 'location', 'country', 'web_flags', 'created'];
private array $optionalColumns = ['name', 'number', 'lastday', 'premdays', 'premium_ends_at', 'premend'];
public static array $cache = [];
public static $cache = array();
const GRATIS_PREMIUM_DAYS = 65535;
/**
@@ -332,50 +325,27 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
*/
public function save()
{
if (!isset($this->data['id'])) {
if( !isset($this->data['id']) )
{
throw new E_OTS_NotLoaded();
}
$defaultValues = [
'premium_ends_at' => 0,
'lastday' => 0,
'premend' => 0,
'premdays' => 0,
];
foreach ($defaultValues as $key => $value) {
if (!isset($this->data[$key])) {
$this->data[$key] = $value;
}
$field = 'lastday';
if($this->db->hasColumn('accounts', 'premend')) { // othire
$field = 'premend';
if(!isset($this->data['premend'])) {
$this->data['premend'] = 0;
}
$columns = $this->columns;
foreach ($this->optionalColumns as $column) {
if ($this->db->hasColumn('accounts', $column)) {
$columns[] = $column;
}
}
else if($this->db->hasColumn('accounts', 'premium_ends_at')) {
$field = 'premium_ends_at';
if(!isset($this->data['premium_ends_at'])) {
$this->data['premium_ends_at'] = 0;
}
}
$values = [];
foreach ($columns as $column) {
$value = $this->data[$column];
$values[$column] = $value;
}
// updates existing player
if( isset($this->data['id']) ) {
AccountModel::where('id', $this->data['id'])->update($values);
}
// creates new player
else {
$values['created'] = time();
$account = AccountModel::create($values);
// ID of new player
$this->data['id'] = $account->id;
}
// UPDATE query on database
$this->db->exec('UPDATE `accounts` SET ' . ($this->db->hasColumn('accounts', 'name') ? '`name` = ' . $this->db->quote($this->data['name']) . ',' : '') . '`password` = ' . $this->db->quote($this->data['password']) . ', `email` = ' . $this->db->quote($this->data['email']) . ', `rlname` = ' . $this->db->quote($this->data['rlname']) . ', `location` = ' . $this->db->quote($this->data['location']) . ', `country` = ' . $this->db->quote($this->data['country']) . ', `web_flags` = ' . (int) $this->data['web_flags'] . ', ' . ($this->db->hasColumn('accounts', 'premdays') ? '`premdays` = ' . (int) $this->data['premdays'] . ',' : '') . '`' . $field . '` = ' . (int) $this->data[$field] . ' WHERE `id` = ' . $this->data['id']);
}
/**
@@ -534,17 +504,11 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
* @since 0.7.5
* @throws E_OTS_NotLoaded If account is not loaded.
*/
public function setPremDays($premdays): void
public function setPremDays($premdays)
{
$this->data['premdays'] = (int) $premdays;
$premiumTimeInSeconds = time() + ($premdays * 24 * 60 * 60);
$this->data['premend'] = $premiumTimeInSeconds;
$this->data['premium_ends_at'] = $premiumTimeInSeconds;
if (isCanary()) {
$this->data['lastday'] = $premiumTimeInSeconds;
}
$this->data['premend'] = time() + ($premdays * 24 * 60 * 60);
$this->data['premium_ends_at'] = time() + ($premdays * 24 * 60 * 60);
}
public function setRLName($name)
@@ -1043,16 +1007,26 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
public function logAction($action)
{
AccountAction::create([
'account_id' => $this->getId(),
'ip' => get_browser_real_ip(),
'date' => time(),
'action' => $action,
]);
$ip = get_browser_real_ip();
if(!str_contains($ip, ":")) {
$ipv6 = '0';
}
else {
$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.

View File

@@ -26,7 +26,6 @@ use MyAAC\Cache\Cache;
*/
class OTS_DB_MySQL extends OTS_Base_DB
{
private bool $hasCacheChanged = false;
private array $has_table_cache = [];
private array $has_column_cache = [];
private array $get_column_info_cache = [];
@@ -165,7 +164,7 @@ class OTS_DB_MySQL extends OTS_Base_DB
$cache->delete('database_columns_info');
$cache->delete('database_checksum');
}
else if ($this->hasCacheChanged) {
else {
$cache->set('database_tables', serialize($this->has_table_cache), 3600);
$cache->set('database_columns', serialize($this->has_column_cache), 3600);
$cache->set('database_columns_info', serialize($this->get_column_info_cache), 3600);
@@ -229,8 +228,6 @@ class OTS_DB_MySQL extends OTS_Base_DB
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);
}
@@ -244,8 +241,6 @@ class OTS_DB_MySQL extends OTS_Base_DB
}
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);
}
@@ -277,14 +272,11 @@ class OTS_DB_MySQL extends OTS_Base_DB
return false;
}
$this->hasCacheChanged = true;
$formatResult = function ($result) {
return [
'field' => $result['Field'],
'type' => $result['Type'],
'null' => strtolower($result['Null']),
'key' => strtolower($result['Key'] ?? ''),
'default' => $result['Default'],
'extra' => $result['Extra'],
];

View File

@@ -8,7 +8,7 @@
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3
*/
use MyAAC\Server\Groups;
use MyAAC\Cache\Cache;
/**
* List of groups.
@@ -47,9 +47,73 @@ class OTS_Groups_List implements IteratorAggregate, Countable
return;
}
$groups = new Groups();
foreach($groups->getGroups() as $id => $info) {
$this->groups[$id] = new OTS_Group($info);
if(!isset($file[0]))
{
global $config;
$file = $config['data_path'] . 'XML/groups.xml';
}
if(!@file_exists($file)) {
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). It doesnt exist.');
return;
}
$cache = Cache::getInstance();
$data = array();
if($cache->enabled())
{
$tmp = '';
if($cache->fetch('groups', $tmp))
$data = unserialize($tmp);
else
{
$groups = new DOMDocument();
if(!@$groups->load($file)) {
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). Error: ' . print_r(error_get_last(), true));
return;
}
// loads groups
foreach( $groups->getElementsByTagName('group') as $group)
{
$data[$group->getAttribute('id')] = array(
'id' => $group->getAttribute('id'),
'name' => $group->getAttribute('name'),
'access' => $group->getAttribute('access')
);
}
$cache->set('groups', serialize($data), 120);
}
foreach($data as $id => $info)
$this->groups[ $id ] = new OTS_Group($info);
}
else
{
// loads DOM document
$groups = new DOMDocument();
if(!@$groups->load($file)) {
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). Error: ' . print_r(error_get_last(), true));
return;
}
// loads groups
foreach($groups->getElementsByTagName('group') as $group)
{
$data[$group->getAttribute('id')] = array(
'id' => $group->getAttribute('id'),
'name' => $group->getAttribute('name'),
'access' => $group->getAttribute('access')
);
$this->groups[ $group->getAttribute('id') ] = new OTS_Group($data[$group->getAttribute('id')]);
//echo $this->getGroup(1)->getName();
}
}
}

View File

@@ -1,6 +1,20 @@
<?php
use MyAAC\Models\Player as PlayerModel;
$__load = array();
/*
'loss_experience' => NULL,
'loss_items' => NULL,
'guild_info' => NULL,
'skull_type' => NULL,
'skull_time' => NULL,
'blessings' => NULL,
'direction' => NULL,
'stamina' => NULL,
'world_id' => NULL,
'online' => NULL,
'deletion' => NULL,
'promotion' => NULL,
'marriage' => NULL
);*/
/**#@+
* @version 0.0.1
@@ -95,10 +109,6 @@ class OTS_Player extends OTS_Row_DAO
POT::SKILL_FISH => array('value' => 0, 'tries' => 0)
);
private array $columns = ['name', 'account_id', 'group_id', 'sex', 'vocation', 'experience', 'level', 'maglevel', 'health', 'healthmax', 'mana', 'manamax', 'manaspent', 'soul', 'lookbody', 'lookfeet', 'lookhead', 'looklegs', 'looktype', 'posx', 'posy', 'posz', 'lastlogin', 'lastlogout', 'lastip', 'town_id', 'balance', 'created', 'comment', 'hide'];
private array $optionalColumns = ['cap', 'skull', 'skull_type', 'skull_time', 'loss_experience', 'loss_mana', 'loss_skills', 'loss_items', 'loss_containers', 'guildnick', 'rank_id', 'promotion', 'direction', 'blessings', 'stamina', 'lookaddons', 'save', 'conditions', 'world_id', 'online', 'deletion', 'deleted', 'marriage'];
private static array $playersOnline;
/**
* Magic PHP5 method.
@@ -123,14 +133,90 @@ class OTS_Player extends OTS_Row_DAO
*/
public function load($id, $fields = null, $load_skills = true)
{
$columns = $this->columns;
foreach ($this->optionalColumns as $column) {
if ($this->db->hasColumn('players', $column)) {
$columns[] = $column;
global $__load;
if(!isset($__load['loss_experience']))
{
$loss = '';
if($this->db->hasColumn('players', 'loss_experience')) {
$loss = ', `loss_experience`, `loss_mana`, `loss_skills`';
}
$__load['loss_experience'] = $loss;
}
if(!isset($__load['loss_items']))
{
$loss_items = '';
if($this->db->hasColumn('players', 'loss_items')) {
$loss_items = ', `loss_items`, `loss_containers`';
}
$__load['loss_items'] = $loss_items;
}
if(!isset($__load['guild_info']))
{
$guild_info = '';
if(!$this->db->hasTable('guild_members') && $this->db->hasColumn('players', 'guildnick')) {
$guild_info = ', `guildnick`, `rank_id`';
}
$__load['guild_info'] = $guild_info;
}
if(!isset($__load['skull_type']))
{
$skull_type = 'skull';
if($this->db->hasColumn('players', 'skull_type')) {
$skull_type = 'skull_type';
}
$__load['skull_type'] = $skull_type;
}
if(!isset($__load['skull_time']))
{
$skull_time = 'skulltime';
if($this->db->hasColumn('players', 'skull_time')) {
$skull_time = 'skull_time';
}
$__load['skull_time'] = $skull_time;
}
if(!isset($__load['blessings'])) {
$__load['blessings'] = $this->db->hasColumn('players', 'blessings');
}
if(!isset($__load['direction'])) {
$__load['direction'] = $this->db->hasColumn('players', 'direction');
}
if(!isset($__load['stamina'])) {
$__load['stamina'] = $this->db->hasColumn('players', 'stamina');
}
if(!isset($__load['world_id'])) {
$__load['world_id'] = $this->db->hasColumn('players', 'world_id');
}
if(!isset($__load['online'])) {
$__load['online'] = $this->db->hasColumn('players', 'online');
}
if(!isset($__load['deletion'])) {
$__load['deletion'] = $this->db->hasColumn('players', 'deletion');
}
if(!isset($__load['promotion'])) {
$__load['promotion'] = $this->db->hasColumn('players', 'promotion');
}
if(!isset($__load['marriage'])) {
$__load['marriage'] = $this->db->hasColumn('players', 'marriage');
}
if(isset($fields)) { // load only what we wish
if(in_array('promotion', $fields)) {
if(!$this->db->hasColumn('players', 'promotion')) {
unset($fields[array_search('promotion', $fields)]);
}
}
if(in_array('deleted', $fields)) {
if($this->db->hasColumn('players', 'deletion')) {
unset($fields[array_search('deleted', $fields)]);
@@ -138,21 +224,21 @@ class OTS_Player extends OTS_Row_DAO
}
}
$columns = [];
foreach ($fields as $field) {
if ($this->db->hasColumn('players', $field)) {
$columns[] = $field;
if(in_array('online', $fields)) {
if(!$this->db->hasColumn('players', 'online')) {
unset($fields[array_search('online', $fields)]);
}
}
$this->data = $this->db->query('SELECT ' . implode(', ', $fields) . ' FROM `players` WHERE `id` = ' . (int)$id)->fetch();
}
else {
// SELECT query on database
$this->data = $this->db->query('SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`' . ($this->db->hasColumn('players', 'lookaddons') ? ', `lookaddons`' : '') . ', `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `save`, `conditions`, `' . $__load['skull_time'] . '` as `skulltime`, `' . $__load['skull_type'] . '` as `skull`' . $__load['guild_info'] . ', `town_id`' . $__load['loss_experience'] . $__load['loss_items'] . ', `balance`' . ($__load['blessings'] ? ', `blessings`' : '') . ($__load['direction'] ? ', `direction`' : '') . ($__load['stamina'] ? ', `stamina`' : '') . ($__load['world_id'] ? ', `world_id`' : '') . ($__load['online'] ? ', `online`' : '') . ', `' . ($__load['deletion'] ? 'deletion' : 'deleted') . '`' . ($__load['promotion'] ? ', `promotion`' : '') . ($__load['marriage'] ? ', `marriage`' : '') . ', `comment`, `created`, `hide` FROM `players` WHERE `id` = ' . (int)$id)->fetch();
}
array_unshift($columns, 'id');
$query = PlayerModel::where('id', $id)->first($columns);
$this->data = $query ? $query->toArray() : [];
// loads skills
if( $this->isLoaded() && $load_skills) {
if( $this->isLoaded() && $load_skills)
{
if($this->db->hasColumn('players', 'skill_fist')) {
$skill_ids = array(
@@ -232,65 +318,153 @@ class OTS_Player extends OTS_Row_DAO
*/
public function save()
{
$defaultValues = [
'cap' => 0,
'skull' => 0,
'skull_type' => 0,
'skull_time' => 0,
'loss_experience' => 100,
'loss_mana' => 100,
'loss_skills' => 100,
'loss_items' => 100,
'loss_containers' => 100,
'guildnick' => '',
'rank_id' => 0,
'promotion' => 0,
'direction' => 0,
'blessings' => 0,
'stamina' => 0,
'lookaddons' => 0,
'save' => 1,
'conditions' => '',
'town_id' => 1,
'world_id' => 1,
'online' => 0,
'deletion' => 0,
'deleted' => 0,
'marriage' => 0,
];
foreach ($defaultValues as $key => $value) {
if (!isset($this->data[$key])) {
$this->data[$key] = $value;
}
$skull_type = 'skull';
if($this->db->hasColumn('players', 'skull_type')) {
$skull_type = 'skull_type';
}
$columns = $this->columns;
foreach ($this->optionalColumns as $column) {
if ($this->db->hasColumn('players', $column)) {
$columns[] = $column;
}
$skull_time = 'skulltime';
if($this->db->hasColumn('players', 'skull_time')) {
$skull_time = 'skull_time';
}
$values = [];
foreach ($columns as $column) {
$value = $this->data[$column];
if(!isset($this->data['loss_experience']))
$this->data['loss_experience'] = 100;
$values[$column] = $value;
}
if(!isset($this->data['loss_mana']))
$this->data['loss_mana'] = 100;
if(!isset($this->data['loss_skills']))
$this->data['loss_skills'] = 100;
if(!isset($this->data['loss_items']))
$this->data['loss_items'] = 10;
if(!isset($this->data['loss_containers']))
$this->data['loss_containers'] = 100;
if(!isset($this->data['guildnick']))
$this->data['guildnick'] = '';
if(!isset($this->data['rank_id']))
$this->data['rank_id'] = 0;
if(!isset($this->data['promotion']))
$this->data['promotion'] = 0;
if(!isset($this->data['direction']))
$this->data['direction'] = 0;
if(!isset($this->data['conditions']))
$this->data['conditions'] = '';
if(!isset($this->data['town_id']))
$this->data['town_id'] = 1;
// updates existing player
if( isset($this->data['id']) ) {
PlayerModel::where('id', $this->data['id'])->update($values);
if( isset($this->data['id']) )
{
$loss = '';
if($this->db->hasColumn('players', 'loss_experience')) {
$loss = ', `loss_experience` = ' . $this->data['loss_experience'] . ', `loss_mana` = ' . $this->data['loss_mana'] . ', `loss_skills` = ' . $this->data['loss_skills'];
}
$loss_items = '';
if($this->db->hasColumn('players', 'loss_items')) {
$loss_items = ', `loss_items` = ' . $this->data['loss_items'] . ', `loss_containers` = ' . $this->data['loss_containers'];
}
$guild_info = '';
if(!$this->db->hasTable('guild_members') && $this->db->hasColumn('players', 'guildnick')) {
$guild_info = ', `guildnick` = ' . $this->db->quote($this->data['guildnick']) . ', ' . $this->db->fieldName('rank_id') . ' = ' . $this->data['rank_id'];
}
$direction = '';
if($this->db->hasColumn('players', 'direction')) {
$direction = ', `direction` = ' . $this->db->quote($this->data['direction']);
}
$blessings = '';
if($this->db->hasColumn('players', 'blessings')) {
$blessings = ', `blessings` = ' . $this->db->quote($this->data['blessings']);
}
$stamina = '';
if($this->db->hasColumn('players', 'stamina')) {
$stamina = ', `stamina` = ' . $this->db->quote($this->data['stamina']);
}
$lookaddons = '';
if($this->db->hasColumn('players', 'lookaddons')) {
$lookaddons = ', `lookaddons` = ' . $this->db->quote($this->data['lookaddons']);
}
// UPDATE query on database
$this->db->query('UPDATE ' . $this->db->tableName('players') . ' SET ' . $this->db->fieldName('name') . ' = ' . $this->db->quote($this->data['name']) . ', ' . $this->db->fieldName('account_id') . ' = ' . $this->data['account_id'] . ', ' . $this->db->fieldName('group_id') . ' = ' . $this->data['group_id'] . ', ' . $this->db->fieldName('sex') . ' = ' . $this->data['sex'] . ', ' . $this->db->fieldName('vocation') . ' = ' . $this->data['vocation'] . ', ' . $this->db->fieldName('experience') . ' = ' . $this->data['experience'] . ', ' . $this->db->fieldName('level') . ' = ' . $this->data['level'] . ', ' . $this->db->fieldName('maglevel') . ' = ' . $this->data['maglevel'] . ', ' . $this->db->fieldName('health') . ' = ' . $this->data['health'] . ', ' . $this->db->fieldName('healthmax') . ' = ' . $this->data['healthmax'] . ', ' . $this->db->fieldName('mana') . ' = ' . $this->data['mana'] . ', ' . $this->db->fieldName('manamax') . ' = ' . $this->data['manamax'] . ', ' . $this->db->fieldName('manaspent') . ' = ' . $this->data['manaspent'] . ', ' . $this->db->fieldName('soul') . ' = ' . $this->data['soul'] . ', ' . $this->db->fieldName('lookbody') . ' = ' . $this->data['lookbody'] . ', ' . $this->db->fieldName('lookfeet') . ' = ' . $this->data['lookfeet'] . ', ' . $this->db->fieldName('lookhead') . ' = ' . $this->data['lookhead'] . ', ' . $this->db->fieldName('looklegs') . ' = ' . $this->data['looklegs'] . ', ' . $this->db->fieldName('looktype') . ' = ' . $this->data['looktype'] . $lookaddons . ', ' . $this->db->fieldName('posx') . ' = ' . $this->data['posx'] . ', ' . $this->db->fieldName('posy') . ' = ' . $this->data['posy'] . ', ' . $this->db->fieldName('posz') . ' = ' . $this->data['posz'] . ', ' . $this->db->fieldName('cap') . ' = ' . $this->data['cap'] . ', ' . $this->db->fieldName('lastlogin') . ' = ' . $this->data['lastlogin'] . ', ' . $this->db->fieldName('lastlogout') . ' = ' . $this->data['lastlogout'] . ', ' . $this->db->fieldName('lastip') . ' = ' . $this->db->quote($this->data['lastip']) . ', ' . $this->db->fieldName('save') . ' = ' . (int) $this->data['save'] . ', ' . $this->db->fieldName('conditions') . ' = ' . $this->db->quote($this->data['conditions']) . ', `' . $skull_time . '` = ' . $this->data['skulltime'] . ', `' . $skull_type . '` = ' . (int) $this->data['skull'] . $guild_info . ', ' . $this->db->fieldName('town_id') . ' = ' . $this->data['town_id'] . $loss . $loss_items . ', ' . $this->db->fieldName('balance') . ' = ' . $this->data['balance'] . $blessings . $stamina . $direction . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id']);
}
// creates new player
else {
$values['created'] = time();
else
{
$loss = '';
$loss_data = '';
if($this->db->hasColumn('players', 'loss_experience')) {
$loss = ', `loss_experience`, `loss_mana`, `loss_skills`';
$loss_data = ', ' . $this->data['loss_experience'] . ', ' . $this->data['loss_mana'] . ', ' . $this->data['loss_skills'];
}
$player = PlayerModel::create($values);
$loss_items = '';
$loss_items_data = '';
if($this->db->hasColumn('players', 'loss_items')) {
$loss_items = ', `loss_items`, `loss_containers`';
$loss_items_data = ', ' . $this->data['loss_items'] . ', ' . $this->data['loss_containers'];
}
// ID of new player
$this->data['id'] = $player->id;
$guild_info = '';
$guild_info_data = '';
if(!$this->db->hasTable('guild_members') && $this->db->hasColumn('players', 'guildnick')) {
$guild_info = ', `guildnick`, `rank_id`';
$guild_info_data = ', ' . $this->db->quote($this->data['guildnick']) . ', ' . $this->data['rank_id'];
}
$promotion = '';
$promotion_data = '';
if($this->db->hasColumn('players', 'promotion')) {
$promotion = ', `promotion`';
$promotion_data = ', ' . $this->data['promotion'];
}
$direction = '';
$direction_data = '';
if($this->db->hasColumn('players', 'direction')) {
$direction = ', `direction`';
$direction_data = ', ' . $this->data['direction'];
}
$blessings = '';
$blessings_data = '';
if($this->db->hasColumn('players', 'blessings')) {
$blessings = ', `blessings`';
$blessings_data = ', ' . $this->data['blessings'];
}
$stamina = '';
$stamina_data = '';
if($this->db->hasColumn('players', 'stamina')) {
$stamina = ', `stamina`';
$stamina_data = ', ' . $this->data['stamina'];
}
$lookaddons = '';
$lookaddons_data = '';
if($this->db->hasColumn('players', 'lookaddons')) {
$lookaddons = ', `lookaddons`';
$lookaddons_data = ', ' . $this->data['lookaddons'];
}
// INSERT query on database
$this->db->query('INSERT INTO `players` (`name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`' . $lookaddons . ', `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `save`, `conditions`, `' . $skull_time . '`, `' . $skull_type . '`' . $guild_info . ', `town_id`' . $loss . $loss_items . ', `balance`' . $blessings . $stamina . $direction . ', `created`' . $promotion . ', `comment`) VALUES (' . $this->db->quote($this->data['name']) . ', ' . $this->data['account_id'] . ', ' . $this->data['group_id'] . ', ' . $this->data['sex'] . ', ' . $this->data['vocation'] . ', ' . $this->data['experience'] . ', ' . $this->data['level'] . ', ' . $this->data['maglevel'] . ', ' . $this->data['health'] . ', ' . $this->data['healthmax'] . ', ' . $this->data['mana'] . ', ' . $this->data['manamax'] . ', ' . $this->data['manaspent'] . ', ' . $this->data['soul'] . ', ' . $this->data['lookbody'] . ', ' . $this->data['lookfeet'] . ', ' . $this->data['lookhead'] . ', ' . $this->data['looklegs'] . ', ' . $this->data['looktype'] . $lookaddons_data . ', ' . $this->data['posx'] . ', ' . $this->data['posy'] . ', ' . $this->data['posz'] . ', ' . $this->data['cap'] . ', ' . $this->data['lastlogin'] . ', ' . $this->data['lastlogout'] . ', ' . $this->data['lastip'] . ', ' . (int) $this->data['save'] . ', ' . $this->db->quote($this->data['conditions']) . ', ' . $this->data['skulltime'] . ', ' . (int) $this->data['skull'] . $guild_info_data . ', ' . $this->data['town_id'] . $loss_data . $loss_items_data . ', ' . $this->data['balance'] . $blessings_data . $stamina_data . $direction_data . ', ' . time() . $promotion_data . ', "")');
// ID of new group
$this->data['id'] = $this->db->lastInsertId();
}
// updates skills - doesn't matter if we have just created character - trigger inserts new skills
@@ -316,7 +490,7 @@ class OTS_Player extends OTS_Row_DAO
$set .= ',';
}
$this->db->query('UPDATE `players` SET ' . $set . ' WHERE `id` = ' . $this->data['id']);
$skills = $this->db->query('UPDATE `players` SET ' . $set . ' WHERE `id` = ' . $this->data['id']);
}
else if($this->db->hasTable('player_skills')) {
foreach($this->skills as $id => $skill)
@@ -574,25 +748,21 @@ class OTS_Player extends OTS_Row_DAO
public function isDeleted()
{
$column = 'deleted';
$field = 'deleted';
if($this->db->hasColumn('players', 'deletion'))
$column = 'deletion';
$field = 'deletion';
if( !isset($this->data[$column]) )
if( !isset($this->data[$field]) )
{
throw new E_OTS_NotLoaded();
}
return $this->data[$column] > 0;
return $this->data[$field] > 0;
}
public function setDeleted($deleted)
{
$column = 'deleted';
if($this->db->hasColumn('players', 'deletion'))
$column = 'deletion';
$this->data[$column] = (int) $deleted;
$this->data['deleted'] = (int) $deleted;
}
public function isOnline()
@@ -682,7 +852,13 @@ class OTS_Player extends OTS_Row_DAO
throw new E_OTS_NotLoaded();
}
return \OTS_Toolbox::getVocationFromPromotion($this->data['vocation'], $this->data['promotion'] ?? 0);
if(isset($this->data['promotion'])) {
global $config;
if((int)$this->data['promotion'] > 0)
return ($this->data['vocation'] + ($this->data['promotion'] * $config['vocations_amount']));
}
return $this->data['vocation'];
}
@@ -1398,7 +1574,12 @@ class OTS_Player extends OTS_Row_DAO
*/
public function getCap()
{
return $this->data['cap'] ?? 0;
if( !isset($this->data['cap']) )
{
throw new E_OTS_NotLoaded();
}
return $this->data['cap'];
}
/**
@@ -1611,12 +1792,12 @@ class OTS_Player extends OTS_Row_DAO
*/
public function getSkullTime()
{
$column = 'skulltime';
if($this->db->hasColumn('players', 'skull_time')) {
$column = 'skull_time';
if( !isset($this->data['skulltime']) )
{
throw new E_OTS_NotLoaded();
}
return $this->data[$column] ?? 0;
return $this->data['skulltime'];
}
/**
@@ -1630,12 +1811,7 @@ class OTS_Player extends OTS_Row_DAO
*/
public function setSkullTime($skulltime)
{
$column = 'skulltime';
if($this->db->hasColumn('players', 'skull_time')) {
$column = 'skull_time';
}
$this->data[$column] = (int) $skulltime;
$this->data['skulltime'] = (int) $skulltime;
}
/**
@@ -3074,10 +3250,6 @@ class OTS_Player extends OTS_Row_DAO
return 0;
}
public function setData(array $data): void{
$this->data = $data;
}
/**
* Magic PHP5 method.
*

View File

@@ -13,8 +13,6 @@
* @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.
*
@@ -112,21 +110,14 @@ class OTS_Toolbox
$list->setFilter($filter);
return $list;
}
public static function getVocationFromPromotion($id, $promotion = 0): int
public static function getVocationName($id, $promotion = 0): string
{
if($promotion > 0) {
for ($i = 0; $i < $promotion; $i++) {
if ($_id = Vocations::getPromoted($id)) {
$id = $_id;
}
}
$id = ($id + ($promotion * config('vocations_amount')));
}
return $id;
}
public static function getVocationName($id, $promotion = 0): string {
return config('vocations')[self::getVocationFromPromotion($id, $promotion)] ?? 'Unknown';
return config('vocations')[$id] ?? 'Unknown';
}
}

View File

@@ -1,6 +1,5 @@
<?php
use MyAAC\Models\Player as PlayerModel;
use MyAAC\Settings;
function updateHighscoresIdsHidden(): void
@@ -11,22 +10,12 @@ function updateHighscoresIdsHidden(): void
return;
}
$players = PlayerModel::where('name', 'Rook Sample')
->orWhere('name', 'Sorcerer Sample')
->orWhere('name', 'Druid Sample')
->orWhere('name', 'Paladin Sample')
->orWhere('name', 'Knight Sample')
->orWhere('name', 'Monk Sample')
->orWhere('name', 'Account Manager')
->orderBy('id')
->select('id')
->get();
$query = $db->query("SELECT `id` FROM `players` WHERE (`name` = " . $db->quote("Rook Sample") . " OR `name` = " . $db->quote("Sorcerer Sample") . " OR `name` = " . $db->quote("Druid Sample") . " OR `name` = " . $db->quote("Paladin Sample") . " OR `name` = " . $db->quote("Knight Sample") . " OR `name` = " . $db->quote("Account Manager") . ") ORDER BY `id`;");
$highscores_ignored_ids = [];
if (count($players) > 0) {
foreach ($players as $result) {
$highscores_ignored_ids[] = $result->id;
}
$highscores_ignored_ids = array();
if ($query->rowCount() > 0) {
foreach ($query->fetchAll() as $result)
$highscores_ignored_ids[] = $result['id'];
} else {
$highscores_ignored_ids[] = 0;
}

View File

@@ -1,42 +0,0 @@
<?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;");
};

View File

@@ -1,16 +0,0 @@
<?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

@@ -1,91 +0,0 @@
<?php
/**
* @var OTS_DB_MySQL $db
*/
use MyAAC\Models\Account as AccountModel;
$time = time();
$accountId = getSession('account') ?? 1;
if (!defined('MYAAC_INSTALL')) {
$accountModel = AccountModel::where('web_flags', 3)->first();
if ($accountModel) {
$accountId = $accountModel->id;
}
}
function insert_sample_if_not_exist($p): void
{
global $time, $accountId;
$player = new OTS_Player();
$player->find($p['name']);
if (!$player->isLoaded()) {
$player->setData([
'name' => $p['name'],
'group_id' => 1,
'account_id' => $accountId,
'level' => $p['level'],
'vocation' => $p['vocation_id'],
'health' => $p['health'],
'healthmax' => $p['healthmax'],
'experience' => $p['experience'],
'lookbody' => 118,
'lookfeet' => 114,
'lookhead' => 38,
'looklegs' => 57,
'looktype' => $p['looktype'],
'maglevel' => 0,
'mana' => $p['mana'],
'manamax' => $p['manamax'],
'manaspent' => 0,
'soul' => $p['soul'],
'town_id' => 1,
'posx' => 1000,
'posy' => 1000,
'posz' => 7,
'conditions' => '',
'cap' => $p['cap'],
'sex' => 1,
'lastlogin' => $time,
'lastip' => 2130706433,
'save' => 1,
'lastlogout' => $time,
'balance' => 0,
'created' => $time,
'hide' => 1,
'comment' => '',
]);
$player->save();
}
}
$up = function () use ($db) {
if (!$db->hasTable('players')) {
return;
}
insert_sample_if_not_exist(['name' => 'Rook Sample', 'level' => 1, 'vocation_id' => 0, 'health' => 150, 'healthmax' => 150, 'experience' => 0, 'looktype' => 130, 'mana' => 0, 'manamax' => 0, 'soul' => 100, 'cap' => 400]);
insert_sample_if_not_exist(['name' => 'Sorcerer Sample', 'level' => 8, 'vocation_id' => 1, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470]);
insert_sample_if_not_exist(['name' => 'Druid Sample', 'level' => 8, 'vocation_id' => 2, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470]);
insert_sample_if_not_exist(['name' => 'Paladin Sample', 'level' => 8, 'vocation_id' => 3, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 129, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470]);
insert_sample_if_not_exist(['name' => 'Knight Sample', 'level' => 8, 'vocation_id' => 4, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 131, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470]);
insert_sample_if_not_exist(['name' => 'Monk Sample', 'level' => 8, 'vocation_id' => 9, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 128, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470]);
if (defined('MYAAC_INSTALL')) {
global $locale;
success($locale['step_database_imported_players']);
}
require_once __DIR__ . '/20.php';
updateHighscoresIdsHidden();
};
$down = function () {
// nothing
};

View File

@@ -1,11 +0,0 @@
CREATE TABLE IF NOT EXISTS `myaac_gallery`
(
`id` int NOT NULL AUTO_INCREMENT,
`comment` varchar(255) NOT NULL DEFAULT '',
`image` varchar(255) NOT NULL,
`thumb` varchar(255) NOT NULL,
`author` varchar(50) NOT NULL DEFAULT '',
`ordering` int NOT NULL DEFAULT 0,
`hide` tinyint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;

View File

@@ -1,16 +0,0 @@
<?php
/**
* @var OTS_DB_MySQL $db
*/
$up = function () use ($db) {
if ($db->hasTable(TABLE_PREFIX . 'gallery')) {
$db->dropTable(TABLE_PREFIX . 'gallery');
}
};
$down = function () use ($db) {
if (!$db->hasTable(TABLE_PREFIX . 'gallery')) {
$db->query(file_get_contents(__DIR__ . '/50-gallery.sql'));
}
};

View File

@@ -1,29 +0,0 @@
<?php
/**
* Example of using getTopPlayers() function
* to display the best players for each skill
*/
defined('MYAAC') or die('Direct access not allowed!');
$skills = [
'magic', 'level',
'balance', 'frags',
POT::SKILL_FIST, POT::SKILL_CLUB,
POT::SKILL_SWORD, POT::SKILL_AXE,
POT::SKILL_DISTANCE, POT::SKILL_SHIELD,
POT::SKILL_FISH
];
foreach ($skills as $skill) {?>
<ul>
<?php
echo '<strong>' . ucwords(is_string($skill) ? $skill : getSkillName($skill)) . '</strong>';
foreach (getTopPlayers(5, $skill) as $player) {?>
<li><?= $player['rank'] . '. ' . $player['name'] . ' - ' . $player['value']; ?></li>
<?php
}
?>
</ul>
<?php
}

View File

@@ -10,7 +10,6 @@
*/
use MyAAC\CreateCharacter;
use MyAAC\Models\AccountAction;
use MyAAC\Models\AccountEmailVerify;
defined('MYAAC') or die('Direct access not allowed!');
@@ -45,16 +44,6 @@ $errors = array();
$save = isset($_POST['save']) && $_POST['save'] == 1;
if($save)
{
$cooldown = setting('core.account_create_ip_block_cooldown');;
if ($cooldown > 0) {
$accountAction = AccountAction::where('ip', get_browser_real_ip())->where('action', 'Account created.')->where('date', '>=', time() - ($cooldown * 60))->first();
if ($accountAction) {
$minute = ($cooldown > 1 ? 'minutes' : 'minute');
$errors['account'] = "You have to wait $cooldown $minute before creating another account.";
}
}
if(!config('account_login_by_email')) {
if(USE_ACCOUNT_NAME) {
$account_name = $_POST['account'];
@@ -151,7 +140,7 @@ if($save)
'country' => $country,
'password' => $password,
'password_confirm' => $password_confirm,
'accept_rules' => isset($_POST['accept_rules']) && $_POST['accept_rules'] === 'true',
'accept_rules' => isset($_POST['accept_rules']) ? $_POST['accept_rules'] === 'true' : false,
);
if (!config('account_login_by_email')) {
@@ -203,21 +192,6 @@ if($save)
$new_account->setPassword(encrypt($password));
$new_account->setEMail($email);
$settingAccountPremiumDays = setting('core.account_premium_days');
if($settingAccountPremiumDays && $settingAccountPremiumDays > 0) {
$new_account->setPremDays($settingAccountPremiumDays);
if (!isCanary()) {
$lastDay = 0;
if($settingAccountPremiumDays != 0 && $settingAccountPremiumDays != OTS_Account::GRATIS_PREMIUM_DAYS) {
$lastDay = time();
}
$new_account->setLastLogin($lastDay);
}
}
$new_account->save();
$hooks->trigger(HOOK_ACCOUNT_CREATE_AFTER_SAVED, ['account' => $new_account]);
@@ -232,6 +206,22 @@ if($save)
$new_account->setCustomField('country', $country);
}
$settingAccountPremiumDays = setting('core.account_premium_days');
if($settingAccountPremiumDays && $settingAccountPremiumDays > 0) {
if($db->hasColumn('accounts', 'premend')) { // othire
$new_account->setCustomField('premend', time() + $settingAccountPremiumDays * 86400);
}
else { // rest
if ($db->hasColumn('accounts', 'premium_ends_at')) { // TFS 1.4+
$new_account->setCustomField('premium_ends_at', time() + $settingAccountPremiumDays * (60 * 60 * 24));
}
else {
$new_account->setCustomField('premdays', $settingAccountPremiumDays);
$new_account->setCustomField('lastday', time());
}
}
}
$accountDefaultPremiumPoints = setting('core.account_premium_points');
if($accountDefaultPremiumPoints > 0) {
$new_account->setCustomField('premium_points', $accountDefaultPremiumPoints);

View File

@@ -9,11 +9,540 @@
* @link https://my-aac.org
*/
defined('MYAAC') or die('Direct access not allowed!');
$title = 'Lost Account';
$title = 'Lost Account Interface';
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>";
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>';
return;
}
$twig->display('account/lost/form.html.twig');
$action_type = isset($_REQUEST['action_type']) ? $_REQUEST['action_type'] : '';
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

@@ -1,18 +0,0 @@
<?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

@@ -1,51 +0,0 @@
<?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

@@ -1,75 +0,0 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
csrfProtect();
require __DIR__ . '/../base.php';
$title = 'Lost Account';
$email = $_POST['email'] ?? '';
$nick = $_POST['nick'] ?? '';
$player = new OTS_Player();
$account = new OTS_Account();
$player->find($nick);
if($player->isLoaded()) {
$account = $player->getAccount();
}
if($account->isLoaded()) {
if($account->getCustomField('email_next') < time()) {
if($account->getEMail() == $email) {
$newCode = generateRandomString(30, true, false, true);
$mailBody = $twig->render('mail.account.lost.code.html.twig', [
'newCode' => $newCode,
'account' => $account,
'nick' => $nick,
]);
$accountEMail = $account->getCustomField('email');
if(_mail($accountEMail, configLua('serverName') . ' - Recover your account', $mailBody)) {
$account->setCustomField('email_code', $newCode);
$account->setCustomField('email_next', (time() + setting('core.mail_lost_account_interval')));
$twig->display('success.html.twig', [
'title' => 'Email has been sent',
'description' => 'Details about steps required to recover your account has been sent to <b>' . $accountEMail . '</b>. You should receive this email within 15 minutes. Please check your inbox/spam directory.',
'custom_buttons' => '',
]);
$twig->display('account.back_button.html.twig', [
'new_line' => true,
'center' => true,
'action' => getLink('news'),
]);
return;
}
$account->setCustomField('email_next', (time() + 60));
error('An error occurred while sending email! Try again later or contact with admin. For Admin: More info can be found in system/logs/mailer-error.log</p>');
}
else {
$errors[] = 'Invalid e-mail to account of character <b>' . escapeHtml($nick) . '</b>. Try again.';
}
}
else {
lostAccountWriteCooldown($nick, (int)$account->getCustomField('email_next'));
}
}
else {
$errors[] = "Player or account of player <b>" . escapeHtml($nick) . "</b> doesn't exist.";
}
if (!empty($errors)) {
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
}
$twig->display('account.back_button.html.twig', [
'new_line' => true,
'center' => true,
'action' => getLink('account/lost/step-1') . '?action=email&nick=' . urlencode($nick),
]);

View File

@@ -1,128 +0,0 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
csrfProtect();
$title = 'Lost Account';
$newPassword = $_POST['password'] ?? '';
$passwordRepeat = $_POST['password_repeat'] ?? '';
$code = $_POST['code'] ?? '';
$character = $_POST['character'] ?? '';
if(empty($code) || empty($character)) {
$errors[] = 'Please enter code from e-mail and name of one character from account.';
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
$twig->display('account/lost/check-code.html.twig', [
'code' => $code,
'character' => $character,
]);
$twig->display('account.back_button.html.twig', [
'new_line' => true,
'center' => true,
'action' => getLink('account/lost/check-code')
]);
return;
}
if (empty($newPassword) || empty($passwordRepeat)) {
$errors[] = 'Please enter both passwords.';
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
$twig->display('account/lost/check-code.finish.html.twig', [
'character' => $character,
'code' => $code,
]);
return;
}
$player = new OTS_Player();
$account = new OTS_Account();
$player->find($character);
if($player->isLoaded()) {
$account = $player->getAccount();
}
$passwordFailed = false;
if($account->isLoaded()) {
if($account->getCustomField('email_code') == $code) {
if ($newPassword == $passwordRepeat) {
if (Validator::password($newPassword)) {
$hooks->trigger(HOOK_ACCOUNT_LOST_EMAIL_SET_NEW_PASSWORD_POST);
if (empty($errors)) {
$tmp_new_pass = $newPassword;
if (USE_ACCOUNT_SALT) {
$salt = generateRandomString(10, false, true, true);
$tmp_new_pass = $salt . $newPassword;
$account->setCustomField('salt', $salt);
}
$account->setPassword(encrypt($tmp_new_pass));
$account->save();
$account->setCustomField('email_code', '');
$mailBody = $twig->render('mail.account.lost.new-password.html.twig', [
'account' => $account,
'newPassword' => $newPassword,
]);
$statusMsg = '';
if (_mail($account->getCustomField('email'), configLua('serverName') . ' - Your new password', $mailBody)) {
$statusMsg = '<br /><small>New password work! Sent e-mail with your password and account name. You should receive this e-mail in 15 minutes. You can login now with new password!';
} else {
$statusMsg = '<br /><p class="error">New password work! An error occurred while sending email! You will not receive e-mail with new password. For Admin: More info can be found in system/logs/mailer-error.log';
}
$twig->display('account/lost/finish.new-password.html.twig', [
'statusMsg' => $statusMsg,
'newPassword' => $newPassword,
]);
}
} else {
$passwordFailed = true;
$errors[] = Validator::getLastError();
}
}
else {
$passwordFailed = true;
$errors[] = 'Passwords are not the same!';
}
}
else {
$errors[] = 'Wrong code to change password.';
}
}
else {
$errors[] = "Account of this character or this character doesn't exist.";
}
if(!empty($errors)) {
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
echo '<br/>';
$template = 'account/lost/check-code.html.twig';
if($passwordFailed) {
$template = 'account/lost/check-code.finish.html.twig';
}
$twig->display($template, [
'code' => $code,
'character' => $character,
]);
}

View File

@@ -1,36 +0,0 @@
<?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

@@ -1,38 +0,0 @@
<?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

@@ -1,49 +0,0 @@
<?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

@@ -1,117 +0,0 @@
<?php
use MyAAC\Models\Account as AccountModel;
defined('MYAAC') or die('Direct access not allowed!');
csrfProtect();
$title = 'Lost Account';
$key = $_POST['key'];
$nick = $_POST['nick'] ?? '';
$newPassword = $_POST['password'] ?? '';
$passwordRepeat = $_POST['password_repeat'] ?? '';
$newEmail = $_POST['email'] ?? '';
$player = new OTS_Player();
$account = new OTS_Account();
$player->find($nick);
if($player->isLoaded()) {
$account = $player->getAccount();
}
if($account->isLoaded()) {
$accountKey = $account->getCustomField('key');
if(!empty($accountKey)) {
if($accountKey == $key) {
if(Validator::password($newPassword)) {
if ($newPassword == $passwordRepeat) {
if (Validator::email($newEmail)) {
$emailExists = AccountModel::where('email', $newEmail)->count() > 0;
if (!$emailExists) {
$hooks->trigger(HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_3_POST);
if (empty($errors)) {
$account->setEMail($newEmail);
$tmp_new_pass = $newPassword;
if (USE_ACCOUNT_SALT) {
$salt = generateRandomString(10, false, true, true);
$tmp_new_pass = $salt . $newPassword;
}
$account->setPassword(encrypt($tmp_new_pass));
$account->save();
if (USE_ACCOUNT_SALT) {
$account->setCustomField('salt', $salt);
}
$statusMsg = '';
if ($account->getCustomField('email_next') < time()) {
$mailBody = $twig->render('mail.account.lost.new-email.html.twig', [
'account' => $account,
'newPassword' => $newPassword,
'newEmail' => $newEmail,
]);
if (_mail($account->getCustomField('email'), configLua('serverName') . ' - New password to your account', $mailBody)) {
$statusMsg = '<br /><small>Sent e-mail with your account name and password to new e-mail. You should receive this e-mail in 15 minutes. You can login now with new password!</small>';
} else {
$statusMsg = '<br /><p class="error">An error occurred while sending email! You will not receive e-mail with this informations. For Admin: More info can be found in system/logs/mailer-error.log</p>';
}
} else {
$statusMsg = '<br /><small>You will not receive e-mail with this informations.</small>';
}
$twig->display('account/lost/finish.new-email.html.twig', [
'statusMsg' => $statusMsg,
'account' => $account,
'newPassword' => $newPassword,
'newEmail' => $newEmail,
]);
return;
}
}
else {
$errors[] = 'This email is already registered!';
}
} else {
$errors[] = Validator::getLastError();
}
}
else {
$errors[] = 'Passwords are not the same!';
}
}
else {
$errors[] = Validator::getLastError();
}
}
else {
$errors[] = 'Wrong recovery key!';
}
}
else {
$errors[] = 'Account of this character has no recovery key!';
}
}
else {
$errors[] = "Player or account of player <b>" . escapeHtml($nick) . "</b> doesn't exist.";
}
if (!empty($errors)) {
$twig->display('error_box.html.twig', [
'errors' => $errors,
]);
}
$twig->display('account.back_button.html.twig', [
'new_line' => true,
'center' => true,
'action' => getLink('account/lost/recovery-key/step-2') . '?nick=' . urlencode($nick) . '&key=' . urlencode($key),
]);

View File

@@ -1,26 +0,0 @@
<?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,8 +96,12 @@ if($email_new_time > 1)
}
}
$actions = $account_logged->getActionsLog(1000);
$actions = array();
foreach($account_logged->getActionsLog(0, 1000) as $action) {
$actions[] = array('action' => $action['action'], 'date' => $action['date'], 'ip' => $action['ip'] != 0 ? long2ip($action['ip']) : inet_ntop($action['ipv6']));
}
$players = array();
/** @var OTS_Players_List $account_players */
$account_players = $account_logged->getPlayersList();
$account_players->orderBy('id');

View File

@@ -452,8 +452,10 @@ WHERE killers.death_id = '".$death['id']."' ORDER BY killers.final_hit DESC, kil
if($query->rowCount() > 0) {
echo 'Did you mean:<ul>';
foreach($query as $player) {
$player['vocation'] = OTS_Toolbox::getVocationFromPromotion($player['vocation'], $player['promotion'] ?? 0);
if(isset($player['promotion'])) {
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 '</ul>';

View File

@@ -42,12 +42,35 @@ for($i = 0; $i < $threads_count['threads_count'] / setting('core.forum_threads_p
$links_to_pages .= '<b>'.($i + 1).' </b>';
}
echo '<a href="' . getLink('forum') . '">Boards</a> >> <b>'.escapeHtml($sections[$section_id]['name']).'</b>';
if($logged && (!$sections[$section_id]['closed'] || Forum::isModerator())) {
echo '<br /><br />
<a href="' . getLink('forum') . '?action=new_thread&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);
$threads = [];
if(count($last_threads) > 0) {
if(isset($last_threads[0])) {
echo '<table width="100%">
<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();
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']);
if(!$player->isLoaded()) {
throw new RuntimeException('Forum error: Player not loaded.');
@@ -56,29 +79,28 @@ if(count($last_threads) > 0) {
$player_account = $player->getAccount();
$canEditForum = $player_account->hasFlag(FLAG_CONTENT_FORUM) || $player_account->isAdmin();
$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']);
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>';
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['player_link'] = getPlayerLink($last_post['name']);
$thread['latest_post'] = $last_post;
if(isset($last_post['name'])) {
echo date('d.m.y H:i:s', $last_post['post_date']) . '<br />by ' . getPlayerLink($last_post['name']);
}
else {
echo 'No posts.';
}
}
else {
echo date('d.m.y H:i:s', $thread['post_date']) . '<br />by ' . getPlayerLink($thread['name']);
}
echo '</td></tr>';
}
$threads[] = $thread;
echo '</table>';
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>';
}
}
$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'],
]);
else {
echo '<h3>No threads in this board.</h3>';
}

View File

@@ -9,25 +9,316 @@
*/
use MyAAC\Cache\Cache;
use MyAAC\Models\Gallery as ModelsGallery;
defined('MYAAC') or die('Direct access not allowed!');
$title = 'Gallery';
const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
$canEdit = hasFlag(FLAG_CONTENT_GALLERY) || superAdmin();
if($canEdit) {
if(function_exists('imagecreatefrompng')) {
if (!empty($action)) {
if ($action == 'delete' || $action == 'edit' || $action == 'hide' || $action == 'moveup' || $action == 'movedown')
$id = $_REQUEST['id'];
$images = Cache::remember('gallery', 5 * 60, function () {
$images = glob(BASE . GALLERY_DIR . '*.*');
if (isset($_REQUEST['comment']))
$comment = stripslashes($_REQUEST['comment']);
$images = array_filter($images, function ($image) {
$ext = pathinfo($image, PATHINFO_EXTENSION);
if (isset($_REQUEST['image']))
$image = $_REQUEST['image'];
return (in_array($ext, ALLOWED_EXTENSIONS) && !str_contains($image, '_thumb'));
});
if (isset($_REQUEST['author']))
$author = $_REQUEST['author'];
return array_map(function ($image) {
return basename($image);
}, $images);
$errors = array();
if ($action == 'add') {
if (Gallery::add($comment, $image, $author, $errors))
$comment = $image = $author = '';
} else if ($action == 'delete') {
Gallery::delete($id, $errors);
} else if ($action == 'edit') {
if (isset($id) && !isset($name)) {
$tmp = Gallery::get($id);
$comment = $tmp['comment'];
$image = $tmp['image'];
$author = $tmp['author'];
} else {
Gallery::update($id, $comment, $image, $author);
$action = $comment = $image = $author = '';
}
} else if ($action == 'hide') {
Gallery::toggleHide($id, $errors);
} else if ($action == 'moveup') {
Gallery::move($id, -1, $errors);
} else if ($action == 'movedown') {
Gallery::move($id, 1, $errors);
}
if (!empty($errors))
$twig->display('error_box.html.twig', array('errors' => $errors));
}
if(!isset($_GET['image'])) {
$twig->display('gallery.form.html.twig', array(
'link' => getLink('gallery/' . ($action == 'edit' ? 'edit' : 'add')),
'action' => $action,
'id' => isset($id) ? $id : null,
'comment' => isset($comment) ? $comment : null,
'image' => isset($image) ? $image : null,
'author' => isset($author) ? $author : null
));
}
}
else
echo 'You cannot edit/add gallery items as it seems your PHP installation doesnt have GD support enabled. Visit <a href="http://be2.php.net/manual/en/image.installation.php">PHP Manual</a> for more info.';
}
if(isset($_GET['image']))
{
$image = $db->query('SELECT * FROM `' . TABLE_PREFIX . 'gallery` WHERE `id` = ' . $db->quote($_GET['image']) . ' ORDER by `ordering` LIMIT 1;');
if($image->rowCount() == 1)
$image = $image->fetch();
else
{
echo 'Image with this id does not exists.';
return;
}
$previous_image = $db->query('SELECT `id` FROM `' . TABLE_PREFIX . 'gallery` WHERE `id` = ' . $db->quote($image['id'] - 1) . ' ORDER by `ordering`;');
if($previous_image->rowCount() == 1)
$previous_image = $previous_image->fetch();
else
$previous_image = NULL;
$next_image = $db->query('SELECT `id` FROM `' . TABLE_PREFIX . 'gallery` WHERE `id` = ' . $db->quote($image['id'] + 1) . ' ORDER by `ordering`;');
if($next_image->rowCount() == 1)
$next_image = $next_image->fetch();
else
$next_image = NULL;
$twig->display('gallery.get.html.twig', array(
'previous' => $previous_image ? $previous_image['id'] : null,
'next' => $next_image ? $next_image['id'] : null,
'image' => $image
));
return;
}
$images = Cache::remember('gallery_' . ($canEdit ? '1' : '0'), 60, function () use ($db, $canEdit) {
return $db->query('SELECT `id`, `comment`, `image`, `author`, `thumb`' .
($canEdit ? ', `hide`, `ordering`' : '') .
' FROM `' . TABLE_PREFIX . 'gallery`' .
(!$canEdit ? ' WHERE `hide` != 1' : '') .
' ORDER BY `ordering`;')->fetchAll(PDO::FETCH_ASSOC);
});
$twig->display('gallery.html.twig', [
$last = count($images);
if(!$last)
{
?>
There are no images added to gallery yet.
<?php
return;
}
$twig->display('gallery.html.twig', array(
'images' => $images,
]);
'last' => $last,
'canEdit' => $canEdit
));
class Gallery
{
static public function add($comment, $image, $author, &$errors)
{
global $db;
if(isset($comment[0]) && isset($image[0]) && isset($author[0]))
{
$query =
$db->query(
'SELECT `ordering`' .
' FROM `' . TABLE_PREFIX . 'gallery`' .
' ORDER BY `ordering`' . ' DESC LIMIT 1'
);
$ordering = 0;
if($query->rowCount() > 0) {
$query = $query->fetch();
$ordering = $query['ordering'] + 1;
}
$pathinfo = pathinfo($image);
$extension = strtolower($pathinfo['extension']);
$thumb_filename = GALLERY_DIR . $pathinfo['filename'] . '_thumb.' . $extension;
$filename = GALLERY_DIR . $pathinfo['filename'] . '.' . $extension;
if($db->insert(TABLE_PREFIX . 'gallery', array(
'comment' => $comment,
'image' => $filename, 'author' => $author,
'thumb' => $thumb_filename,
'ordering' => $ordering))) {
if(self::generateThumb($db->lastInsertId(), $image, $errors))
self::resize($image, 650, 500, $filename, $errors);
}
}
else
$errors[] = 'Please fill all inputs.';
return !count($errors);
}
static public function get($id) {
return ModelsGallery::find($id)->toArray();
}
static public function update($id, $comment, $image, $author) {
$pathinfo = pathinfo($image);
$extension = strtolower($pathinfo['extension']);
$filename = GALLERY_DIR . $pathinfo['filename'] . '.' . $extension;
if(ModelsGallery::where('id', $id)->update([
'comment' => $comment,
'image' => $filename,
'author' => $author
])) {
if(self::generateThumb($id, $image, $errors))
self::resize($image, 650, 500, $filename, $errors);
}
}
static public function delete($id, &$errors)
{
if(isset($id))
{
$row = ModelsGallery::find($id);
if($row)
if (!$row->delete()) {
$errors[] = 'Fail during delete Gallery';
}
else
$errors[] = 'Image with id ' . $id . ' does not exists.';
}
else
$errors[] = 'id not set';
return !count($errors);
}
static public function toggleHide($id, &$errors)
{
if(isset($id))
{
$row = ModelsGallery::find($id);
if($row) {
$row->hide = $row->hide == 1 ? 0 : 1;
if (!$row->save()) {
$errors[] = 'Fail during toggle hide Gallery';
}
} else
$errors[] = 'Image with id ' . $id . ' does not exists.';
}
else
$errors[] = 'id not set';
return !count($errors);
}
static public function move($id, $i, &$errors)
{
global $db;
$query = self::get($id);
if($query !== false)
{
$ordering = $query['ordering'] + $i;
$old_record = $db->select(TABLE_PREFIX . 'gallery', array('ordering' => $ordering));
if($old_record !== false) {
ModelsGallery::where('ordering', $ordering)->update([
'ordering' => $query['ordering'],
]);
}
ModelsGallery::where('id', $id)->update([
'ordering' => $ordering,
]);
}
else
$errors[] = 'Image with id ' . $id . ' does not exists.';
return !count($errors);
}
static public function resize($file, $new_width, $new_height, $new_file, &$errors)
{
$pathinfo = pathinfo($file);
$extension = strtolower($pathinfo['extension']);
switch ($extension)
{
case 'gif': // GIF
$image = imagecreatefromgif($file);
break;
case 'jpg': // JPEG
case 'jpeg':
$image = imagecreatefromjpeg($file);
break;
case 'png': // PNG
$image = imagecreatefrompng($file);
break;
default:
$errors[] = 'Unsupported file format.';
return false;
}
$width = imagesx($image);
$height = imagesy($image);
// create a new temporary image
$tmp_img = imagecreatetruecolor($new_width, $new_height);
// copy and resize old image into new image
imagecopyresized($tmp_img, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
// save thumbnail into a file
switch($extension)
{
case 'gif':
imagegif($tmp_img, $new_file);
break;
case 'jpg':
case 'jpeg':
imagejpeg($tmp_img, $new_file);
break;
case 'png':
imagepng($tmp_img, $new_file);
break;
}
return true;
}
static public function generateThumb($id, $file, &$errors)
{
$pathinfo = pathinfo($file);
$extension = strtolower($pathinfo['extension']);
$thumb_filename = GALLERY_DIR . $pathinfo['filename'] . '_thumb.' . $extension;
if(!self::resize($file, 170, 110, $thumb_filename, $errors))
return false;
if(isset($id))
{
$row = ModelsGallery::find($id);
if($row) {
$row->thumb = $thumb_filename;
$row->save();
} else
$errors[] = 'Image with id ' . $id . ' does not exists.';
}
else
$errors[] = 'id not set';
return !count($errors);
}
}

View File

@@ -13,7 +13,6 @@ use MyAAC\Cache\Cache;
use MyAAC\Models\Player;
use MyAAC\Models\PlayerDeath;
use MyAAC\Models\PlayerKillers;
use MyAAC\Server\Vocations;
defined('MYAAC') or die('Direct access not allowed!');
$title = 'Highscores';
@@ -36,20 +35,24 @@ if(!is_numeric($page) || $page < 1 || $page > PHP_INT_MAX) {
$query = Player::query();
$configVocations = config('vocations');
$configVocationsAmount = config('vocations_amount');
$vocationId = null;
if($vocation !== 'all') {
foreach($configVocations as $id => $name) {
if(strtolower($name) == $vocation) {
$vocationId = $id;
$filterVocations = [$id];
$add_vocs = [$id];
while($tmpVoc = Vocations::getPromoted($id)) {
$id = $tmpVoc;
$filterVocations[] = $tmpVoc;
if ($id !== 0) {
$i = $id + $configVocationsAmount;
while (isset($configVocations[$i])) {
$add_vocs[] = $i;
$i += $configVocationsAmount;
}
}
$query->whereIn('players.vocation', $filterVocations);
$query->whereIn('players.vocation', $add_vocs);
break;
}
}
@@ -322,5 +325,4 @@ $twig->display('highscores.html.twig', [
'page' => $page,
'baseLink' => $baseLink,
'updatedAt' => $updatedAt,
'baseVocations' => Vocations::getBase(true),
]);

View File

@@ -12,7 +12,6 @@
use MyAAC\Cache\Cache;
use MyAAC\Models\ServerConfig;
use MyAAC\Models\ServerRecord;
use MyAAC\Server\Vocations;
defined('MYAAC') or die('Direct access not allowed!');
$title = 'Who is online?';
@@ -57,14 +56,15 @@ $cached = Cache::remember("online_$order", setting('core.online_cache_ttl') * 60
$vocations = array_map(function ($name) {
return 0;
}, config('vocations'));
}, setting('core.vocations'));
if($db->hasTable('players_online')) // tfs 1.0
$playersOnline = $db->query('SELECT `accounts`.`country`, `players`.`name`, `players`.`level`, `players`.`vocation`' . $outfit . ', `' . $skull_time . '` as `skulltime`, `' . $skull_type . '` as `skull` FROM `accounts`, `players`, `players_online` WHERE `players`.`id` = `players_online`.`player_id` AND `accounts`.`id` = `players`.`account_id` ORDER BY ' . $orderSql);
else
$playersOnline = $db->query('SELECT `accounts`.`country`, `players`.`name`, `players`.`level`, `players`.`vocation`' . $outfit . ', ' . $promotion . ' `' . $skull_time . '` as `skulltime`, `' . $skull_type . '` as `skull` FROM `accounts`, `players` WHERE `players`.`online` > 0 AND `accounts`.`id` = `players`.`account_id` ORDER BY ' . $orderSql);
$configVocations = config('vocations');
$settingVocations = setting('core.vocations');
$settingVocationsAmount = setting('core.vocations_amount');
$players = [];
foreach($playersOnline as $player) {
@@ -81,22 +81,22 @@ $cached = Cache::remember("online_$order", setting('core.online_cache_ttl') * 60
}
}
$player['vocation'] = OTS_Toolbox::getVocationFromPromotion($player['vocation'], $player['promotion'] ?? 0);
if(isset($player['promotion'])) {
if((int)$player['promotion'] > 0)
$player['vocation'] += ($player['promotion'] * $settingVocationsAmount);
}
$players[] = array(
'name' => getPlayerLink($player['name']),
'player' => $player,
'level' => $player['level'],
'vocation' => $configVocations[$player['vocation']] ?? 'Unknown',
'vocation' => $settingVocations[$player['vocation']],
'skull' => $skull,
'country_image' => getFlagImage($player['country']),
'outfit' => setting('core.outfit_images_url') . '?id=' . $player['looktype'] . ($outfit_addons ? '&addons=' . $player['lookaddons'] : '') . '&head=' . $player['lookhead'] . '&body=' . $player['lookbody'] . '&legs=' . $player['looklegs'] . '&feet=' . $player['lookfeet'],
);
$originalId = Vocations::getOriginal($player['vocation']);
if ($originalId) {
$vocations[$originalId]++;
}
$vocations[($player['vocation'] > $settingVocationsAmount ? $player['vocation'] - $settingVocationsAmount : $player['vocation'])]++;
}
$record = '';
@@ -142,7 +142,6 @@ $twig->display('online.html.twig', array(
'vocations' => $cached['vocations'],
'vocs' => $cached['vocations'], // deprecated, to be removed
'order' => $order,
'baseVocations' => Vocations::getBase(false),
));
// search bar

View File

@@ -8,7 +8,6 @@
* @link https://my-aac.org
*/
use MyAAC\Models\Menu;
use MyAAC\Models\Pages;
use MyAAC\Plugins;
@@ -332,20 +331,7 @@ else {
}
}
$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) {
if (!$found) {
$page = '404';
$file = SYSTEM . 'pages/404.php';
}

View File

@@ -219,14 +219,7 @@ return [
'cache_engine' => [
'name' => 'Cache Engine',
'type' => 'options',
'options' => [
'auto' => 'Auto',
'file' => 'Files',
'apc' => 'APC',
'apcu' => 'APCu',
'php' => 'PHP',
'disable' => 'Disable',
],
'options' => ['auto' => 'Auto', 'file' => 'Files', 'apc' => 'APC', 'apcu' => 'APCu', 'disable' => 'Disable'],
'desc' => 'Auto is most reasonable. It will detect the best cache engine',
'default' => 'auto',
'is_config' => true,
@@ -341,31 +334,20 @@ return [
},
],
],
/**
* @deprecated
* To be removed in v3.0
*/
'vocations_amount' => [
'hidden' => true,
'name' => 'Vocations Amount',
'type' => 'number',
//'name' => 'Vocations Amount',
//'desc' => 'How many basic vocations your server got (without promotion)',
'desc' => 'How much basic vocations your server got (without promotion)',
'default' => 4,
'callbacks' => [
'get' => function () {
return config('vocations_amount');
},
],
],
'vocations' => [
'hidden' => true,
'name' => 'Vocation Names',
'type' => 'textarea',
//'name' => 'Vocation Names',
//'desc' => 'Separated by comma. Must be in the same order as in vocations.xml, starting with id: 0.',
'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 () {
return config('vocations');
'get' => function ($value) {
return array_map('trim', explode(',', $value));
},
],
],
@@ -1744,18 +1726,6 @@ Sent by MyAAC,<br/>
'account_login_ipban_protection', '=', 'true'
]
],
[
'type' => 'section',
'title' => 'Cooldowns',
],
'account_create_ip_block_cooldown' => [
'name' => 'Create Account IP Block Cooldown',
'type' => 'number',
'desc' => 'Block flooding create account per ip. If you still have a problem with account create spam - then its recommended to install the recaptcha plugin.' .
'<br/><strong>In minutes.</strong> 0 to disable.',
'default' => 10,
],
],
'callbacks' => [
'beforeSave' => function(&$settings, &$values) {

View File

@@ -13,8 +13,8 @@ namespace MyAAC\Cache;
class APC
{
private string $prefix;
private bool $enabled;
private $prefix;
private $enabled;
public function __construct($prefix = '')
{
@@ -22,14 +22,14 @@ class APC
$this->enabled = function_exists('apc_fetch');
}
public function set($key, $var, $ttl = 0): void
public function set($key, $var, $ttl = 0)
{
$key = $this->prefix . $key;
apc_delete($key);
apc_store($key, $var, $ttl);
}
public function get($key): string
public function get($key)
{
$tmp = '';
if ($this->fetch($this->prefix . $key, $tmp)) {
@@ -39,15 +39,18 @@ class APC
return '';
}
public function fetch($key, &$var): bool {
public function fetch($key, &$var)
{
return ($var = apc_fetch($this->prefix . $key)) !== false;
}
public function delete($key): void {
public function delete($key)
{
apc_delete($this->prefix . $key);
}
public function enabled(): bool {
public function enabled()
{
return $this->enabled;
}
}

View File

@@ -13,8 +13,8 @@ namespace MyAAC\Cache;
class APCu
{
private string $prefix;
private bool $enabled;
private $prefix;
private $enabled;
public function __construct($prefix = '')
{
@@ -22,14 +22,14 @@ class APCu
$this->enabled = function_exists('apcu_fetch');
}
public function set($key, $var, $ttl = 0): void
public function set($key, $var, $ttl = 0)
{
$key = $this->prefix . $key;
apcu_delete($key);
apcu_store($key, $var, $ttl);
}
public function get($key): string
public function get($key)
{
$tmp = '';
if ($this->fetch($this->prefix . $key, $tmp)) {
@@ -39,15 +39,18 @@ class APCu
return '';
}
public function fetch($key, &$var): bool {
public function fetch($key, &$var)
{
return ($var = apcu_fetch($this->prefix . $key)) !== false;
}
public function delete($key): void {
public function delete($key)
{
apcu_delete($this->prefix . $key);
}
public function enabled(): bool {
public function enabled()
{
return $this->enabled;
}
}

View File

@@ -47,15 +47,35 @@ class Cache
return self::$instance;
}
self::$instance = match (strtolower($engine)) {
'apc' => new APC($prefix),
'apcu' => new APCu($prefix),
'xcache' => new XCache($prefix),
'file' => new File($prefix, CACHE),
'php' => new PHP($prefix, CACHE),
'auto' => self::generateInstance(self::detect(), $prefix),
default => new self(),
};
switch (strtolower($engine)) {
case 'apc':
self::$instance = new APC($prefix);
break;
case 'apcu':
self::$instance = new APCu($prefix);
break;
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;
}
@@ -63,7 +83,7 @@ class Cache
/**
* @return string
*/
public static function detect(): string
public static function detect()
{
if (function_exists('apc_fetch'))
return 'apc';
@@ -78,7 +98,8 @@ class Cache
/**
* @return bool
*/
public function enabled(): bool {
public function enabled()
{
return false;
}

View File

@@ -12,22 +12,18 @@ namespace MyAAC\Cache;
class File
{
private string $prefix;
private string $dir;
private bool $enabled;
private $prefix;
private $dir;
private $enabled;
public function __construct($prefix = '', $dir = '')
{
$this->prefix = $prefix;
$this->dir = $dir;
ensureFolderExists($this->dir);
ensureIndexExists($this->dir);
$this->enabled = (file_exists($this->dir) && is_dir($this->dir) && is_writable($this->dir));
}
public function set($key, $var, $ttl = 0): void
public function set($key, $var, $ttl = 0)
{
$file = $this->_name($key);
file_put_contents($file, $var);
@@ -39,7 +35,7 @@ class File
touch($file, time() + $ttl);
}
public function get($key): string
public function get($key)
{
$tmp = '';
if ($this->fetch($key, $tmp)) {
@@ -49,7 +45,7 @@ class File
return '';
}
public function fetch($key, &$var): bool
public function fetch($key, &$var)
{
$file = $this->_name($key);
if (!file_exists($file) || filemtime($file) < time()) {
@@ -60,7 +56,7 @@ class File
return true;
}
public function delete($key): void
public function delete($key)
{
$file = $this->_name($key);
if (file_exists($file)) {
@@ -68,11 +64,13 @@ class File
}
}
public function enabled(): bool {
public function enabled()
{
return $this->enabled;
}
private function _name($key): string {
private function _name($key)
{
return sprintf('%s%s%s', $this->dir, $this->prefix, sha1($key));
}
}

View File

@@ -12,37 +12,36 @@ namespace MyAAC\Cache;
class PHP
{
private string $prefix;
private string $dir;
private bool $enabled;
private $prefix;
private $dir;
private $enabled;
public function __construct($prefix = '', $dir = '')
{
$this->prefix = $prefix;
$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);
ensureIndexExists($this->dir);
$this->enabled = (file_exists($this->dir) && is_dir($this->dir) && is_writable($this->dir));
}
// Write to temp file first to ensure atomicity
$tmp = $this->dir . "tmp_$key." . uniqid('', true) . '.tmp';
file_put_contents($tmp, '<?php $var = ' . $var . ';', LOCK_EX);
public function set($key, $var, $ttl = 0): void
{
$var = var_export($var, true);
$file = $this->_name($key);
rename($tmp, $file);
if ($ttl === 0) {
$ttl = 365 * 24 * 60 * 60; // 365 days
}
$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);
touch($file, time() + $ttl);
}
public function get($key)
@@ -55,23 +54,19 @@ class PHP
return '';
}
public function fetch($key, &$var): bool
public function fetch($key, &$var)
{
$file = $this->_name($key);
if (!file_exists($file)) {
if (!file_exists($file) || filemtime($file) < time()) {
return false;
}
$content = include $file;
if (!isset($content) || $content['expires'] < time()) {
return false;
}
$var = $content['var'];
@include $file;
$var = isset($var) ? $var : null;
return true;
}
public function delete($key): void
public function delete($key)
{
$file = $this->_name($key);
if (file_exists($file)) {
@@ -79,11 +74,13 @@ class PHP
}
}
public function enabled(): bool {
public function enabled()
{
return $this->enabled;
}
private function _name($key): string {
private function _name($key)
{
return sprintf('%s%s%s', $this->dir, $this->prefix, sha1($key) . '.php');
}
}

View File

@@ -13,8 +13,8 @@ namespace MyAAC\Cache;
class XCache
{
private string $prefix;
private bool $enabled;
private $prefix;
private $enabled;
public function __construct($prefix = '')
{
@@ -22,14 +22,14 @@ class XCache
$this->enabled = function_exists('xcache_get') && ini_get('xcache.var_size');
}
public function set($key, $var, $ttl = 0): void
public function set($key, $var, $ttl = 0)
{
$key = $this->prefix . $key;
xcache_unset($key);
xcache_set($key, $var, $ttl);
}
public function get($key): string
public function get($key)
{
$tmp = '';
if ($this->fetch($this->prefix . $key, $tmp)) {
@@ -39,7 +39,7 @@ class XCache
return '';
}
public function fetch($key, &$var): bool
public function fetch($key, &$var)
{
$key = $this->prefix . $key;
if (!xcache_isset($key)) {
@@ -50,11 +50,13 @@ class XCache
return true;
}
public function delete($key): void {
public function delete($key)
{
xcache_unset($this->prefix . $key);
}
public function enabled(): bool {
public function enabled()
{
return $this->enabled;
}
}

View File

@@ -149,10 +149,7 @@ class CreateCharacter
if($db->hasColumn('players', 'direction'))
$player->setDirection($playerSample->getDirection());
if($db->hasColumn('players', 'conditions')) {
$player->setConditions($playerSample->getConditions());
}
$player->setConditions($playerSample->getConditions());
$rank = $playerSample->getRank();
if($rank->isLoaded()) {
$player->setRank($playerSample->getRank());
@@ -186,11 +183,7 @@ class CreateCharacter
$player->setLookHead($playerSample->getLookHead());
$player->setLookLegs($playerSample->getLookLegs());
$player->setLookType($playerSample->getLookType());
if($db->hasColumn('players', 'cap')) {
$player->setCap($playerSample->getCap());
}
$player->setCap($playerSample->getCap());
$player->setBalance(0);
$player->setPosX(0);
$player->setPosY(0);

View File

@@ -40,7 +40,7 @@ class DataLoader
{
self::$startTime = microtime(true);
if(Items::load()) {
if(Items::loadFromXML()) {
success(self::$locale['step_database_loaded_items'] . self::getLoadedTime());
}
else {

View File

@@ -16,37 +16,57 @@ use MyAAC\Models\Spell;
class Items
{
public static array $items = [];
private static $error = '';
public static $items;
private static string $error = '';
public static function getError(): string {
return self::$error;
}
public static function load(): bool {
$file_path = config('data_path') . 'items/items.toml';
if (file_exists($file_path)) {
$items = new Server\TOML\Items();
}
elseif (file_exists(config('data_path') . 'items/items.xml')) {
$items = new Server\XML\Items();
}
else {
self::$error = 'Cannot load items.xml or items.toml file. Files not found.';
public static function loadFromXML($show = false)
{
$file_path = config('data_path') . 'items/items.xml';
if (!file_exists($file_path)) {
self::$error = 'Cannot load file ' . $file_path;
return false;
}
if (!$items->load()) {
self::$error = $items->getError();
return false;
$xml = new \DOMDocument;
$xml->load($file_path);
$items = array();
foreach ($xml->getElementsByTagName('item') as $item) {
if ($item->getAttribute('fromid')) {
for ($id = $item->getAttribute('fromid'); $id <= $item->getAttribute('toid'); $id++) {
$tmp = self::parseNode($id, $item, $show);
$items[$tmp['id']] = $tmp['content'];
}
} else {
$tmp = self::parseNode($item->getAttribute('id'), $item, $show);
$items[$tmp['id']] = $tmp['content'];
}
}
$cache_php = new CachePHP(config('cache_prefix'), CACHE . 'persistent/');
$cache_php->set('items', $items, 5 * 365 * 24 * 60 * 60);
return true;
}
public static function init(): void {
if(count(self::$items) > 0) {
public static function parseNode($id, $node, $show = false) {
$name = $node->getAttribute('name');
$article = $node->getAttribute('article');
$plural = $node->getAttribute('plural');
$attributes = array();
foreach($node->getElementsByTagName('attribute') as $attr) {
$attributes[strtolower($attr->getAttribute('key'))] = $attr->getAttribute('value');
}
return array('id' => $id, 'content' => array('article' => $article, 'name' => $name, 'plural' => $plural, 'attributes' => $attributes));
}
public static function getError() {
return self::$error;
}
public static function load() {
if(self::$items) {
return;
}
@@ -55,7 +75,7 @@ class Items
}
public static function get($id) {
self::init();
self::load();
return self::$items[$id] ?? [];
}

View File

@@ -9,6 +9,6 @@ class AccountAction extends Model {
public $timestamps = false;
protected $fillable = ['account_id', 'ip', 'date', 'action'];
protected $fillable = ['account_id', 'ip', 'ipv6', 'date', 'action'];
}

View File

@@ -0,0 +1,18 @@
<?php
namespace MyAAC\Models;
use Illuminate\Database\Eloquent\Model;
class Gallery extends Model {
protected $table = TABLE_PREFIX . 'gallery';
public $timestamps = false;
protected $fillable = [
'comment', 'image', 'thumb',
'author', 'ordering', 'hide',
];
}

View File

@@ -9,6 +9,6 @@ class Menu extends Model {
public $timestamps = false;
protected $fillable = ['template', 'name', 'link', 'access', 'blank', 'color', 'category', 'ordering', 'enabled'];
protected $fillable = ['template', 'name', 'link', 'blank', 'color', 'category', 'ordering', 'enabled'];
}

View File

@@ -23,8 +23,6 @@ class Player extends Model {
public $timestamps = false;
protected $guarded = [];
protected $casts = [
'worldid' => 'integer',
'sex' => 'integer',
@@ -48,8 +46,14 @@ class Player extends Model {
});
}
public function getVocationNameAttribute() {
return \OTS_Toolbox::getVocationName($this->vocation, $this->promotion ?? 0);
public function getVocationNameAttribute()
{
$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()

View File

@@ -39,7 +39,7 @@ class Monsters {
}
$items = array();
Items::init();
Items::load();
foreach((array)Items::$items as $id => $item) {
$items[$item['name']] = $id;
}

View File

@@ -1,29 +0,0 @@
<?php
namespace MyAAC\Server;
use MyAAC\Cache\Cache;
class Groups
{
private static array $groups = [];
public function __construct() {
self::$groups = Cache::remember('groups', 10 * 60, function () {
if (file_exists(config('server_path') . 'config/groups.toml')) {
$groups = new TOML\Groups();
}
else {
$groups = new XML\Groups();
}
$groups->load();
return $groups->getGroups();
});
}
public static function getGroups(): array {
return self::$groups;
}
}

View File

@@ -1,37 +0,0 @@
<?php
namespace MyAAC\Server\TOML;
use Devium\Toml\Toml;
class Groups
{
private array $groups = [];
public function load(): void
{
$file = config('server_path') . 'config/groups.toml';
if(!@file_exists($file)) {
error('Error: Cannot load groups.toml. More info in system/logs/error.log file.');
log_append('error.log', "[OTS_Groups_List.php] Fatal error: Cannot load groups.toml ($file). It doesn't exist.");
return;
}
$toml = file_get_contents($file);
$groups = Toml::decode($toml, asArray: true);
foreach ($groups as $group)
{
$this->groups[$group['id']] = [
'id' => $group['id'],
'name' => $group['name'],
'access' => $group['access'],
];
}
}
public function getGroups(): array {
return $this->groups;
}
}

View File

@@ -1,56 +0,0 @@
<?php
namespace MyAAC\Server\TOML;
use MyAAC\Cache\PHP as CachePHP;
class Items
{
private string $error;
public function getError(): string
{
return $this->error;
}
public function load(): bool
{
$file_path = config('data_path') . 'items/items.toml';
if (!file_exists($file_path)) {
$this->error = 'Cannot load file ' . $file_path;
return false;
}
//$toml = file_get_contents($file_path);
//$items = \Devium\Toml\Toml::decode($toml, asArray: false);
$itemsParser = new ItemsParser();
$itemsParsed = $itemsParser->parse($file_path);
$items = [];
foreach ($itemsParsed as $item) {
$attributes = array_filter($item, function ($key) {
return !in_array($key, ['id', 'article', 'name', 'plural']);
}, ARRAY_FILTER_USE_KEY);
$id = $item['id'] ?? null;
if ($id === null) {
continue;
}
$article = $item['article'] ?? '';
$name = $item['name'] ?? '';
$plural = $item['plural'] ?? '';
$items[$id] = [
'article' => $article,
'name' => $name,
'plural' => $plural,
'attributes' => $attributes,
];
}
$cache_php = new CachePHP(config('cache_prefix'), CACHE . 'persistent/');
$cache_php->set('items', $items, 5 * 365 * 24 * 60 * 60);
return true;
}
}

View File

@@ -1,42 +0,0 @@
<?php
namespace MyAAC\Server\TOML;
class ItemsParser
{
public function parse(string $path): array
{
$ret = [];
$i = 0;
$handle = fopen($path, "r");
if ($handle) {
$parse = '';
while (($line = fgets($handle)) !== false) {
if (str_contains($line, '[[items]]') && $i++ != 0) {
//global $whoopsHandler;
//$whoopsHandler->addDataTable('ini', [$parse]);
$ret[] = parse_ini_string($parse);
$parse = '';
continue;
}
// skip lines like this
// field = {type = "fire", initdamage = 20, ticks = 10000, count = 7, damage = 10}
// as it cannot be parsed by parse_ini_string
if (str_contains($line, 'field =')) {
continue;
}
$parse .= $line;
}
$ret[] = parse_ini_string($parse);
fclose($handle);
}
return $ret;
}
}

View File

@@ -1,42 +0,0 @@
<?php
namespace MyAAC\Server\TOML;
use Devium\Toml\Toml;
class Vocations
{
private array $vocations = [];
private array $vocationsFrom = [];
public function load(): void
{
$tomlVocations = glob(config('data_path') . 'vocations/*.toml');
if (count($tomlVocations) <= 0) {
throw new \RuntimeException('ERROR: Cannot load any .toml vocation from the data/vocations folder.');
}
foreach ($tomlVocations as $file) {
$toml = file_get_contents($file);
$vocations = Toml::decode($toml, asArray: true);
foreach ($vocations as $vocationArray) {
$id = $vocationArray['id'];
$this->vocations[$id] = $vocationArray['name'];
$this->vocationsFrom[$id] = $vocationArray['promotedfrom'];
}
}
ksort($this->vocations, SORT_NUMERIC);
ksort($this->vocationsFrom, SORT_NUMERIC);
}
public function get(): array {
return $this->vocations;
}
public function getFrom(): array {
return $this->vocationsFrom;
}
}

View File

@@ -1,86 +0,0 @@
<?php
namespace MyAAC\Server;
use MyAAC\Cache\Cache;
class Vocations
{
private static array $vocations = [];
private static array $vocationsFrom = [];
public function __construct() {
$cached = Cache::remember('vocations', 10 * 60, function () {
$tomlVocations = glob(config('data_path') . 'vocations/*.toml');
if (count($tomlVocations) > 0) {
$vocations = new TOML\Vocations();
}
else {
$vocations = new XML\Vocations();
}
$vocations->load();
$from = $vocations->getFrom();
$amount = 0;
foreach ($from as $vocId => $fromVocation) {
if ($vocId != 0 && $vocId == $fromVocation) {
$amount++;
}
}
return ['vocations' => $vocations->get(), 'vocationsFrom' => $from, 'amount' => $amount];
});
self::$vocations = $cached['vocations'];
self::$vocationsFrom = $cached['vocationsFrom'];
config(['vocations', self::$vocations]);
config(['vocations_amount', $cached['amount']]);
}
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 {
if (!isset(self::$vocationsFrom[$id])) {
return null;
}
while ($tmpId = self::$vocationsFrom[$id]) {
if ($tmpId == $id) {
break;
}
$id = $tmpId;
}
return $id;
}
public static function getBase($includingRook = true): array {
$vocations = [];
foreach (self::$vocationsFrom as $vocId => $fromVoc) {
if ($vocId == $fromVoc && ($vocId != 0 || $includingRook)) {
$vocations[] = $vocId;
}
}
return $vocations;
}
}

View File

@@ -1,40 +0,0 @@
<?php
namespace MyAAC\Server\XML;
class Groups
{
private array $groups = [];
public function load(): void
{
$file = config('data_path') . 'XML/groups.xml';
if(!@file_exists($file)) {
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
log_append('error.log', "[OTS_Groups_List.php] Fatal error: Cannot load groups.xml ($file). It doesn't exist.");
return;
}
$groups = new \DOMDocument();
if(!@$groups->load($file)) {
error('Error: Cannot load groups.xml. More info in system/logs/error.log file.');
log_append('error.log', '[OTS_Groups_List.php] Fatal error: Cannot load groups.xml (' . $file . '). Error: ' . print_r(error_get_last(), true));
return;
}
// loads groups
foreach( $groups->getElementsByTagName('group') as $group)
{
$this->groups[$group->getAttribute('id')] = [
'id' => $group->getAttribute('id'),
'name' => $group->getAttribute('name'),
'access' => $group->getAttribute('access')
];
}
}
public function getGroups(): array {
return $this->groups;
}
}

View File

@@ -1,57 +0,0 @@
<?php
namespace MyAAC\Server\XML;
use MyAAC\Cache\PHP as CachePHP;
class Items
{
private string $error;
public function getError(): string {
return $this->error;
}
public function load(): bool
{
$file_path = config('data_path') . 'items/items.xml';
if (!file_exists($file_path)) {
$this->error = 'Cannot load file ' . $file_path;
return false;
}
$xml = new \DOMDocument;
$xml->load($file_path);
$items = [];
foreach ($xml->getElementsByTagName('item') as $item) {
if ($item->getAttribute('fromid')) {
for ($id = $item->getAttribute('fromid'); $id <= $item->getAttribute('toid'); $id++) {
$tmp = $this->parseNode($id, $item);
$items[$tmp['id']] = $tmp['content'];
}
} else {
$tmp = $this->parseNode($item->getAttribute('id'), $item);
$items[$tmp['id']] = $tmp['content'];
}
}
$cache_php = new CachePHP(config('cache_prefix'), CACHE . 'persistent/');
$cache_php->set('items', $items, 5 * 365 * 24 * 60 * 60);
return true;
}
public function parseNode($id, $node): array
{
$name = $node->getAttribute('name');
$article = $node->getAttribute('article');
$plural = $node->getAttribute('plural');
$attributes = array();
foreach($node->getElementsByTagName('attribute') as $attr) {
$attributes[strtolower($attr->getAttribute('key'))] = $attr->getAttribute('value');
}
return ['id' => $id, 'content' => ['article' => $article, 'name' => $name, 'plural' => $plural, 'attributes' => $attributes]];
}
}

View File

@@ -1,51 +0,0 @@
<?php
namespace MyAAC\Server\XML;
class Vocations
{
private array $vocations = [];
private array $vocationsFrom = [];
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');
$this->vocations[$id] = $vocation->getAttribute('name');
$fromVocation = (int) $vocation->getAttribute('fromvoc');
$this->vocationsFrom[$id] = $fromVocation;
}
}
public function get(): array {
return $this->vocations;
}
public function getFrom(): array {
return $this->vocationsFrom;
}
public static function getBase($includingRook = true): array {
return \MyAAC\Server\Vocations::getBase($includingRook);
}
public static function getOriginal(int $id): ?int {
return \MyAAC\Server\Vocations::getOriginal($id);
}
}

View File

@@ -152,10 +152,9 @@ class Settings implements \ArrayAccess
}
if ($setting['type'] === 'category') {
$title = strtolower(str_replace(' ', '', $setting['title']));
?>
<li class="nav-item">
<a class="nav-link<?= ($i === 0 ? ' active' : ''); ?>" id="home-tab-<?= $i++; ?>" data-toggle="tab" href="#tab-<?= $title; ?>" type="button"><?= $setting['title']; ?></a>
<a class="nav-link<?= ($i === 0 ? ' active' : ''); ?>" id="home-tab-<?= $i++; ?>" data-toggle="tab" href="#tab-<?= str_replace(' ', '', $setting['title']); ?>" type="button"><?= $setting['title']; ?></a>
</li>
<?php
}
@@ -176,10 +175,8 @@ class Settings implements \ArrayAccess
if ($j++ !== 0) { // close previous category
echo '</tbody></table></div>';
}
$title = strtolower(str_replace(' ', '', $setting['title']));
?>
<div class="tab-pane fade show<?= ($j === 1 ? ' active' : ''); ?>" id="tab-<?= $title; ?>">
<div class="tab-pane fade show<?= ($j === 1 ? ' active' : ''); ?>" id="tab-<?= str_replace(' ', '', $setting['title']); ?>">
<?php
continue;
}

View File

@@ -69,14 +69,6 @@ define('HOOK_ACCOUNT_LOGIN_AFTER_PASSWORD', ++$i);
define('HOOK_ACCOUNT_LOGIN_AFTER_REMEMBER_ME', ++$i);
define('HOOK_ACCOUNT_LOGIN_AFTER_PAGE', ++$i);
define('HOOK_ACCOUNT_LOGIN_POST', ++$i);
define('HOOK_ACCOUNT_LOST_CHECK_CODE_FINISH_AFTER_PASSWORD', ++$i);
define('HOOK_ACCOUNT_LOST_CHECK_CODE_FINISH_AFTER_PASSWORD_REPEAT', ++$i);
define('HOOK_ACCOUNT_LOST_EMAIL_SET_NEW_PASSWORD_POST', ++$i);
define('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_2_AFTER_CHARACTER', ++$i);
define('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_2_AFTER_EMAIL', ++$i);
define('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_2_AFTER_PASSWORD', ++$i);
define('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_2_AFTER_PASSWORD_REPEAT', ++$i);
define('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_3_POST', ++$i);
define('HOOK_ACCOUNT_CREATE_CHARACTER_AFTER', ++$i);
define('HOOK_ACCOUNT_CREATE_CHARACTER_BEFORE_FIRST_TABLE', ++$i);
define('HOOK_ACCOUNT_CREATE_CHARACTER_BEFORE_VOCATIONS', ++$i);

View File

@@ -146,10 +146,10 @@ if($twig_loader) {
function get_template_menus(): array
{
global $template_name, $logged_access;
global $template_name;
$result = Cache::remember('template_menus_' . $template_name, 10 * 60, function () use ($template_name) {
$result = Menu::select(['name', 'link', 'access', 'blank', 'color', 'category'])
$result = Menu::select(['name', 'link', 'blank', 'color', 'category'])
->where('template', $template_name)
->orderBy('category')
->orderBy('ordering')
@@ -163,10 +163,6 @@ function get_template_menus(): array
$menus = [];
foreach($result as $menu) {
if ($menu['access'] > $logged_access) {
continue;
}
if (empty($menu['link'])) {
$menu['link'] = 'news';
}

View File

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

View File

@@ -0,0 +1,36 @@
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

@@ -0,0 +1,10 @@
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

@@ -1,56 +0,0 @@
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"><strong>New password:</strong></label>
</td>
<td>
<input type="password" id="password" name="password" value="" size="40">
</td>
</tr>
{{ hook('HOOK_ACCOUNT_LOST_CHECK_CODE_FINISH_AFTER_PASSWORD') }}
<tr>
<td>
<label for="password_repeat"><strong>Repeat the new password:</strong></label>
</td>
<td>
<input type="password" id="password_repeat" name="password_repeat" value="" size="40">
</td>
</tr>
{{ hook('HOOK_ACCOUNT_LOST_CHECK_CODE_FINISH_AFTER_PASSWORD_REPEAT') }}
</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

@@ -1,33 +0,0 @@
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

@@ -1,54 +0,0 @@
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

@@ -1,58 +0,0 @@
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

@@ -1,30 +0,0 @@
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

@@ -1,43 +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/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

@@ -1,10 +0,0 @@
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

@@ -1,57 +0,0 @@
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">
<strong>Character name:</strong>
</label>
</td>
<td>
<input type=text id="nick" name="nick" value="{{ nick }}" size="40" readonly="readonly">
</td>
</tr>
<tr>
<td>
<label for="key">
<strong>Recovery key:</strong>
</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

@@ -1,90 +0,0 @@
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">
<strong>Please enter new password and e-mail</strong>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<table>
<tr>
<td>
<label for="nick">
<strong>Account of character:</strong>
</label>
</td>
<td>
<input type="text" id="nick" name="nick" value="{{ nick }}" size="40" readonly="readonly">
</td>
</tr>
{{ hook('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_2_AFTER_CHARACTER') }}
<tr>
<td>
<label for="password">
<strong>New password:</strong>
</label>
</td>
<td>
<input type="password" id="password" name="password" value="" size="40">
</td>
</tr>
{{ hook('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_2_AFTER_PASSWORD') }}
<tr>
<td>
<label for="password_repeat">
<strong>Repeat the new password:</strong>
</label>
</td>
<td>
<input type="password" id="password_repeat" name="password_repeat" value="" size="40">
</td>
</tr>
{{ hook('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_2_AFTER_PASSWORD_REPEAT') }}
<tr>
<td>
<label for="email">
<strong>New e-mail address:</strong>
</label>
</td>
<td>
<input type="text" id="email" name="email" value="" size="40">
</td>
</tr>
{{ hook('HOOK_ACCOUNT_LOST_RECOVERY_KEY_STEP_2_AFTER_EMAIL') }}
</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,33 +0,0 @@
<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,8 +2,7 @@
<p class="note">You are editing: {{ template }}<br/><br/>
Hint: You can drag menu items.<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.<br/>
* Guest means everyone will see the link. Player means registered and logged-in user.
Not all templates support blank and colorful links.
</p>
<div class="row text-center">
<div class="col-md-2 col-sm-1"></div>

View File

@@ -14,62 +14,42 @@
colors[{{ cat }}] = '{{ options['default_links_color'] ?? (menuDefaultLinksColor ?? config('menu_default_color')) }}';
{% 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 () {
const $sortable = $(".sortable");
$sortable.sortable();
$sortable.disableSelection();
$(".remove-button").on('click', function () {
confirmRemoveMenuItem(this);
var id = $(this).attr("id");
$('#list-' + id.replace('remove-button-', '')).remove();
});
$(".add-button").on('click', function () {
let cat = $(this).attr("id").replace('add-button-', '');
let id = last_id[cat];
var cat = $(this).attr("id").replace('add-button-', '');
var id = last_id[cat];
last_id[cat]++;
const color = colors[cat];
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);
$('#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 () {
confirmRemoveMenuItem(this);
$('#list-' + $(this).attr("id").replace('remove-button-', '')).remove();
});
initializeSpectrum();
});
$("#menus-form").on('submit', function (e) {
$('.menu-blank-checkbox:not(:checked)').each(function (i, obj) {
$('.blank-checkbox:not(:checked)').each(function (i, obj) {
$(obj).parent().prev().val("off");
});
$('.menu-blank-checkbox:checked').each(function (i, obj) {
$('.blank-checkbox:checked').each(function (i, obj) {
$(obj).parent().prev().val("on");
});
});
});
</script>
<style>
<style type="text/css">
.sortable {
list-style-type: none;
margin: 0;
@@ -80,16 +60,24 @@
.remove-button, .add-button {
cursor: pointer;
}
.ui-sortable-handle {
padding: 10px;
}
.label_menu_name, .label_menu_link {
width: 45%;
}
.menu-name, .menu-link {
width: 100%;
}
</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() {
$(".color-picker").spectrum({
preferredFormat: "hex",
showInput: true,
showPalette: true,
palette: [
['black', 'white', 'blanchedalmond',
'rgb(255, 128, 0);', 'hsv 100 70 50'],
['red', 'yellow', 'green', 'blue', 'violet']
]
});
}
</script>

View File

@@ -83,9 +83,13 @@
<!-- jQuery Form Submit No Refresh + Toastify -->
<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>
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
const noChangesText = "No changes has been made";
$('form')
@@ -165,31 +169,3 @@
{% endif %}
{% endfor %}
</script>
<script>
$(function () {
if ($('.tab-content').length) {
let url = window.location.href;
let activeTab = url.substring(url.indexOf("#") + 1);
let $selector = $('[href="#' + activeTab + '"]')
if ($selector.length) {
$selector.tab('show');
}
}
let url = location.href.replace(/\/$/, "");
$('a[data-toggle="tab"]').on("click", function() {
let newUrl;
const hash = $(this).attr("href");
if(hash === "#home") {
newUrl = url.split("#")[0];
} else {
newUrl = url.split("#")[0] + hash;
}
history.replaceState(null, null, newUrl);
});
});
</script>

View File

@@ -1,57 +0,0 @@
<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

@@ -0,0 +1,33 @@
<form method="post" action="{{ link }}">
{{ csrf() }}
{% if action == 'edit' %}
<input type="hidden" name="id" value="{{ id }}" />
{% endif %}
<table width="100%" border="0" cellspacing="1" cellpadding="4">
<tr>
<td bgcolor="{{ config.vdarkborder }}" class="white"><b>{% if action == 'edit' %}Edit{% else %}Add{% endif %} image</b></td>
</tr>
<tr>
<td bgcolor="{{ config.darkborder }}">
<table border="0" cellpadding="1">
<tr>
<td>Comment:</td>
<td><textarea name="comment" maxlength="255" cols="50" rows="5">{% if comment is not null %}{{ comment }}{% endif %}</textarea></td>
<tr/>
<tr>
<td>Image URL:</td>
<td><input name="image" value="{% if image is not null %}{{ image }}{% endif %}" size="50" maxlength="255"/></td>
<tr/>
<tr>
<td>Author:</td>
<td><input name="author" value="{% if author is not null %}{{ author }}{% endif %}" size="50" maxlength="50"/></td>
<tr/>
<tr>
<td colspan="2" align="center"><input type="submit" value="Submit"/>
</tr>
</table>
</td>
</tr>
</table>
</form>
<br/><br/>

View File

@@ -0,0 +1,15 @@
<div style="position: relative; height: 15px; width: 100%;">
{% if next is not null %}
<a style="float: right;" href="{{ getLink('gallery') ~ '/' ~ next }}" >next <img src="images/arrow_right.gif" width=15 height=11 border=0 ></a>
{% endif %}
{% if previous is not null %}
<a style="position: absolute;" href="{{ getLink('gallery') ~ '/' ~ previous }}"><img src="images/arrow_left.gif" width=15 height=11 border=0 > previous</a>
{% endif %}
<div style="position: absolute; width: 80%; margin-left: 10%; margin-right: 10%; text-align: center;">
<a href="{{ getLink('gallery') }}" ><img src="images/arrow_up.gif" width=11 height=15 border=0 > back</a>
</div>
</div>
<div style="position: relative; text-align: center; top: 20px; ">
<img src="{{ image.image }}" />
<div style="margin-top: 15px; margin-bottom: 35px; ">{{ image.comment }}</div>
</div>

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