Merge branch 'develop' into feature/twig-hooks-filters

This commit is contained in:
slawkens 2025-03-09 21:24:47 +01:00
commit 9af676a59e
59 changed files with 2732 additions and 2283 deletions

View File

@ -1,9 +1,9 @@
name: Cypress name: Cypress
on: on:
pull_request: pull_request:
branches: [develop] branches: [main]
push: push:
branches: [develop] branches: [main]
jobs: jobs:
cypress: cypress:
@ -35,7 +35,7 @@ jobs:
- name: Checkout MyAAC - name: Checkout MyAAC
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
ref: develop ref: main
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:

View File

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

View File

@ -2,9 +2,9 @@ name: "PHPStan"
on: on:
pull_request: pull_request:
branches: [develop] branches: [main]
push: push:
branches: [develop] branches: [main]
jobs: jobs:
tests: tests:

4
.gitignore vendored
View File

@ -4,6 +4,7 @@ Thumbs.db
# #
/.htaccess /.htaccess
lua
# composer # composer
composer.phar composer.phar
@ -75,6 +76,3 @@ landing
# system # system
system/functions_custom.php system/functions_custom.php
# others/rest
system/pages/downloads.php

View File

@ -1,5 +1,92 @@
# Changelog # Changelog
## [1.2 - 09.02.2025]
### Added
* Twig session(key) function + reworked session functions to accept multi-array like in Laravel (https://github.com/slawkens/myaac/commit/b46ddb43d03ef7e5fc34e555e92e856bdc905691)
* add template_name to twig variables (https://github.com/slawkens/myaac/commit/ae1161d77050bda181802b4496c9de920a7bb1bc)
* add HOOK_INIT, executed just after $hooks are loaded (https://github.com/slawkens/myaac/commit/19686725dc810f63a07f049f82c66cf336d90ca6)
### Changed
* settings: password input hide/show, enable Save button only if changes has been made, save settings in transaction (https://github.com/slawkens/myaac/commit/4fda4f643b60a151179e5dd4f04912fb2618d98f, https://github.com/slawkens/myaac/commit/28fef952f857b79d64bc7495ffa5e1999e68e192, https://github.com/slawkens/myaac/commit/4b6024dc451accadb6c469fa282a9a764c1c0a81)
* rework menus: Different categories can have different colors + Option to reset menus (https://github.com/slawkens/myaac/commit/73de93a561f6b13111e019075724357d8a617249, https://github.com/slawkens/myaac/commit/3da3e62c5b12390d75de9b3320729bcca6e0b458)
### Fixed
* highscores: Fix online status + vocation for TFS 0.x (https://github.com/slawkens/myaac/commit/ea51ad27c38be88d86514cb979bb394fcfbef1f0)
* clear cache button in admin bar needed to be clicked twice until it worked (https://github.com/slawkens/myaac/commit/ea51ad27c38be88d86514cb979bb394fcfbef1f0)
* HOOK_STARTUP location (https://github.com/slawkens/myaac/commit/a73fb1003ee3f812cf182d1834d65f08e6f60d1f)
* if vocation name has more words (https://github.com/slawkens/myaac/commit/9d7fc98e1e0a96b59ecc1a7c39800a64445db364)
### Updated
* Bump twig/twig from 3.18.0 to 3.19.0 (#284)
## [1.1 - 27.01.2025]
### Changed
* adjust mailer settings descriptions to latest gmail (https://github.com/slawkens/myaac/commit/c5d5bb80671db135e6b503f53684771c7272e05d)
* optimize $player->isOnline() function, thanks @gesior (https://github.com/slawkens/myaac/commit/10dd818b139d5e1bb1ca9ec81edfb083ba9316b4)
* make players.comment and guilds.description VARCHAR (https://github.com/slawkens/myaac/commit/a45ceab83a74bee2b89cdb72baceda75e577e3cf)
* add lua/ folder to .gitignore (https://github.com/slawkens/myaac/commit/07012f786b1114cb6ab2f064f82c645b136a375a)
### Fixed
* general fixes in the tibiacom template menus, better support for custom menus
* make functions_custom.php optional (https://github.com/slawkens/myaac/commit/dc2b5afd9980984e2b259c9fc99f2ade46f70a5a)
* error in CLI, where BASE_URL is not defined (https://github.com/slawkens/myaac/commit/4d749b881582f64b5a46196dbbb5ee8097127f03)
* hook ACCOUNT_LOGIN_BEFORE_ACCOUNT location (https://github.com/slawkens/myaac/commit/669c447fca8643ce56d9ef8c1374ec647c780998)
## [1.0.1 - 14.01.2025]
### Fixed
* tibiacom account & news menu links not auto expanding
### Updated (Thanks dependabot)
* twig from ^2.0 to ^3.11
* tinymce from ^6.8.3 to ^7.2.0
* cypress from ^12.12.0 to ^13.17.0
* nesbot/carbon from 2.72.5 to 2.72.6
## [1.0 - 12.01.2025]
First stable release in the v1.0 series.
Minimum PHP 8.1 is required.
Changes since RC.2:
### Added
* feature: migrations up/down. Allows to downgrade/upgrade database to specified version (https://github.com/slawkens/myaac/commit/3f6ff3a3326b0475d28d11ffd7fff51f362d799f)
* new hooks for news management (https://github.com/slawkens/myaac/commit/011a85d8ae34283ded6999882833f9d4797028ec, https://github.com/slawkens/myaac/commit/36bd3eb846e829b45313e10f7568dc4e95841143)
* None Vocation to highscores (can be changed to RookStayer in Admin Panel) (https://github.com/slawkens/myaac/commit/a4a248099521bb5b8b2aa5bd592138debd2f19d5)
* support for button_color (green, red, blue) (https://github.com/slawkens/myaac/commit/d8b6b749ee62e88b6af4a05d3d7557f90b94d94e)
* add $whoopsHandler as variable, can be used by plugins (https://github.com/slawkens/myaac/commit/b0c8cf2ecda23045d725aaf43cfb3852ed766a4b)
* PlayerModel->outfit_url attribute (https://github.com/slawkens/myaac/commit/3b5be1a8db5dceecaa388e2925a5536d13b38881)
* support for selecting plugin themes in Admin menus.php (https://github.com/slawkens/myaac/commit/77a2c1cec343ffe4be5c2c2503ee81bc32a14ca1)
### Changed
* schema: Change character set to utf8mb4 (support for Emojis in Menus/Pages/News/Forum etc.) (https://github.com/slawkens/myaac/commit/27c44f1bdfb6234cf0c9d5b4b491123bb205b08f)
* prefer get_browser_real_ip() over REMOTE_ADDR (https://github.com/slawkens/myaac/commit/941846605c00cee83168d2f916410b8ba8d4b7b9)
* automatically set selected current one on highscores filters (https://github.com/slawkens/myaac/commit/e96227fbe41ae281783b2d49edb169a603601813)
* rewrite towns loading code, removed OTBM loader (was too slow) (https://github.com/slawkens/myaac/commit/c980a0914632e7b27f718464f669a200707d217e)
* allow OTS_Player to be passed as object to getPlayerLink (https://github.com/slawkens/myaac/commit/84d37c5a8f2c4535a41c8aa8264752969d3f3a3d)
* do not clear menus by default on install (https://github.com/slawkens/myaac/commit/12d8faa3eda5e798f97b71e941c035187daad96e)
* display warning in admin panel - plugins - if zip extension is not installed (https://github.com/slawkens/myaac/commit/e3ffe5d9e11d78ab064a370d8541bac351c9bcd9)
* set default_socket_timeout for ipinfo.io checkup to 5 seconds (https://github.com/slawkens/myaac/commit/783d96fc6568a607d3198b832fed3a0dd06c4ebb)
* refactor getTopPlayers function (support for balance) (https://github.com/slawkens/myaac/commit/c769962e39fe8dfb72ecd5be1864e145696be794)
### Fixed
* XSS in forum (https://github.com/slawkens/myaac/commit/c2b7286d20d4b579171540f7a774e8a0995d5e8f, https://github.com/slawkens/myaac/commit/8fb643596f9586005976e7bdb484a541a9d8715e)
* price deducted when changing sex (https://github.com/slawkens/myaac/commit/16671ea40b72dcf74037c359ad572f9eb825edf9)
* move_thread by unauthorized user (https://github.com/slawkens/myaac/commit/d6c40c836a53cb1710f911f77f45f28b54ea1b54, thanks @anyeor)
* TFS 1.4.2 where conditions is NULL (https://github.com/slawkens/myaac/commit/b8396d4c8482e951da538b13f2296123732c4545)
* do not show forum new thread show button if not logged in (https://github.com/slawkens/myaac/commit/507402171ba3b6e7ee184bd7fa73e0d55e0cad7a, @anyeor)
* login if limiter is disabled (https://github.com/slawkens/myaac/commit/a0f1971583f0f790013e2145fb5ac573c59fbdef)
* fixes to installMenus function (https://github.com/slawkens/myaac/commit/a2fadc5945fe0a5e39f740827f6ffbda1bb501e2)
* many PHP exceptions in different places
* fixes to tibiacom menus ActiveSubmenuItem
### Removed
* bugtracker SQL table code as the page has been removed/moved to plugins (https://github.com/slawkens/myaac/commit/5782772b901b05fb814bc718d062f6e2cd71df8c)
## [1.0-RC.2 - 25.10.2024] ## [1.0-RC.2 - 25.10.2024]
Still waiting for your reports about bugs found in this release. We are very close to stable release. Still waiting for your reports about bugs found in this release. We are very close to stable release.
@ -87,7 +174,6 @@ Minimum PHP version for this release is 8.1.
* support for login and create account only by email (configurable) * support for login and create account only by email (configurable)
* with no need for account name * with no need for account name
* Google ReCAPTCHA v3 support (available as plugin) * Google ReCAPTCHA v3 support (available as plugin)
* automatically load towns names from .OTBM file
* support for Account Number * support for Account Number
* suggest account number option * suggest account number option
* many new functions, hooks and configurables * many new functions, hooks and configurables
@ -116,4 +202,4 @@ Minimum PHP version for this release is 8.1.
* change_password email to be more informal * change_password email to be more informal
### Fixed ### Fixed
* hundrets of bug fixes, mostly patched from 0.8, so it makes no sense writing them again here * hundreds of bug fixes, mostly patched from 0.8, so it makes no sense writing them again here

View File

@ -1,3 +1,3 @@
* Gesior.pl (2007 - 2008) * Gesior.pl (2007 - 2008)
* Slawkens (2009 - 2023) * Slawkens (2009 - 2025)
* Contributors listed in CONTRIBUTORS.txt * Contributors listed in CONTRIBUTORS.txt

View File

@ -11,10 +11,10 @@ Official website: https://my-aac.org
[![Closed Issues](https://img.shields.io/github/issues-closed-raw/slawkens/myaac)](https://github.com/slawkens/myaac/issues?q=is%3Aissue+is%3Aclosed) [![Closed Issues](https://img.shields.io/github/issues-closed-raw/slawkens/myaac)](https://github.com/slawkens/myaac/issues?q=is%3Aissue+is%3Aclosed)
| Version | Status | Branch | Requirements | | Version | Status | Branch | Requirements |
|:--------|:-----------------------|:--------|:---------------| |:--------|:-----------------------|:-------|:---------------|
| **1.x** | **Active development** | develop | **PHP >= 8.1** | | **1.x** | **Active development** | master | **PHP >= 8.1** |
| 0.9.x | Not developed anymore | 0.9 | PHP >= 7.2.5 | | 0.9.x | Not developed anymore | 0.9 | PHP >= 7.2.5 |
| 0.8.x | Active support | master | PHP >= 7.2.5 | | 0.8.x | Active support | 0.8 | PHP >= 7.2.5 |
| 0.7.x | End Of Life | 0.7 | PHP >= 5.3.3 | | 0.7.x | End Of Life | 0.7 | PHP >= 5.3.3 |
### Requirements ### Requirements

View File

@ -9,6 +9,7 @@
*/ */
use MyAAC\Models\Account as AccountModel; use MyAAC\Models\Account as AccountModel;
use MyAAC\Models\AccountAction;
use MyAAC\Models\Player; use MyAAC\Models\Player;
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
@ -466,9 +467,8 @@ else if (isset($_REQUEST['search'])) {
</thead> </thead>
<tbody> <tbody>
<?php <?php
$accountActions = \MyAAC\Models\AccountAction::where('account_id', $account->getId())->orderByDesc('date')->get(); $accountActions = AccountAction::where('account_id', $account->getId())->orderByDesc('date')->get();
foreach ($accountActions as $i => $log): foreach ($accountActions as $i => $log):
$log->ip = ($log->ip != 0 ? long2ip($log->ip) : inet_ntop($log->ipv6));
?> ?>
<tr> <tr>
<td><?php echo $i + 1; ?></td> <td><?php echo $i + 1; ?></td>

View File

@ -10,6 +10,7 @@
use MyAAC\Cache\Cache; use MyAAC\Cache\Cache;
use MyAAC\Models\Menu; use MyAAC\Models\Menu;
use MyAAC\Plugins;
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
$title = 'Menus'; $title = 'Menus';
@ -21,6 +22,8 @@ if (!hasFlag(FLAG_CONTENT_MENUS) && !superAdmin()) {
return; return;
} }
$pluginThemes = Plugins::getThemes();
if (isset($_POST['template'])) { if (isset($_POST['template'])) {
$template = $_POST['template']; $template = $_POST['template'];
@ -56,54 +59,67 @@ if (isset($_POST['template'])) {
} }
} }
$cache = Cache::getInstance(); onTemplateMenusChange();
if ($cache->enabled()) {
$cache->delete('template_menus');
}
success('Saved at ' . date('H:i')); success('Saved at ' . date('H:i'));
} }
$file = TEMPLATES . $template . '/config.php'; $path = TEMPLATES . $template;
if (file_exists($file)) {
require_once $file; if (isset($pluginThemes[$template])) {
$path = BASE . $pluginThemes[$template];
}
$path .= '/config.php';
if (file_exists($path)) {
require_once $path;
} else { } else {
echo 'Cannot find template config.php file.'; echo 'Cannot find template config.php file.';
return; return;
} }
if (isset($_GET['reset_colors'])) {
if (isset($config['menu_default_color'])) {
Menu::where('template', $template)->update(['color' => str_replace('#', '', $config['menu_default_color'])]);
success('Colors has been reset.');
}
else {
warning('There is no default color defined, cannot reset colors.');
}
}
if (!isset($config['menu_categories'])) { if (!isset($config['menu_categories'])) {
echo "No menu categories set in template config.php.<br/>This template doesn't support dynamic menus."; echo "No menu categories set in template config.php.<br/>This template doesn't support dynamic menus.";
return; return;
} }
if (isset($_GET['reset_colors'])) {
foreach ($config['menu_categories'] as $id => $options) {
$color = $options['default_links_color'] ?? ($config['menu_default_links_color'] ?? ($config['menu_default_color'] ?? '#ffffff'));
Menu::where('template', $template)->where('category', $id)->update(['color' => str_replace('#', '', $color)]);
}
onTemplateMenusChange();
success('Colors has been reset at ' . date('H:i'));
}
if (isset($_GET['reset_menus'])) {
$configMenus = config('menus');
if (isset($configMenus)) {
Plugins::installMenus($template, config('menus'), true);
onTemplateMenusChange();
success('Menus has been reset at ' . date('H:i'));
}
else {
error("This template don't support reinstalling menus.");
}
}
$title = 'Menus - ' . $template; $title = 'Menus - ' . $template;
$canResetColors = isset($config['menu_default_color']) || isset($config['menu_default_links_color']);
foreach ($config['menu_categories'] as $id => $options) {
if (isset($options['default_links_color'])) {
$canResetColors = true;
}
}
$twig->display('admin.menus.header.html.twig', [
'template' => $template,
'canResetColors' => $canResetColors
]);
?> ?>
<div align="center" class="text-center">
<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.
</p>
<?php if (isset($config['menu_default_color'])) {?>
<form method="post" action="?p=menus&reset_colors" onsubmit="return confirm('Do you really want to reset colors?');">
<?php csrf(); ?>
<input type="hidden" name="template" value="<?php echo $template ?>"/>
<button type="submit" class="btn btn-danger">Reset Colors to default</button>
</form>
<br/>
<?php } ?>
</div>
<?php <?php
$menus = Menu::query() $menus = Menu::query()
->select('name', 'link', 'blank', 'color', 'category', 'ordering') ->select('name', 'link', 'blank', 'color', 'category', 'ordering')
@ -133,12 +149,13 @@ if (isset($_POST['template'])) {
if (isset($menus[$id])) { if (isset($menus[$id])) {
$i = 0; $i = 0;
foreach ($menus[$id] as $menu): foreach ($menus[$id] as $menu):
$color = (empty($menu['color']) ? ($cat['default_links_color'] ?? ($config['menu_default_links_color'] ?? ($config['menu_default_color'] ?? '#ffffff'))) : '#' . $menu['color']);
?> ?>
<li class="ui-state-default" id="list-<?php echo $id ?>-<?php echo $i ?>"><label>Name:</label> <input type="text" name="menu[<?php echo $id ?>][]" value="<?php echo escapeHtml($menu['name']); ?>"/> <li class="ui-state-default" id="list-<?php echo $id ?>-<?php echo $i ?>"><label>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'] ?>"/> <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"/> <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> <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 (empty($menu['color']) ? ($config['menu_default_color'] ?? '#ffffff') : $menu['color']); ?>"/> <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> <a class="remove-button" id="remove-button-<?php echo $id ?>-<?php echo $i ?>"><i class="fas fa-trash"></a></i></li>
<?php $i++; $last_id[$id] = $i; <?php $i++; $last_id[$id] = $i;
endforeach; endforeach;
@ -162,15 +179,19 @@ if (isset($_POST['template'])) {
$twig->display('admin.menus.js.html.twig', array( $twig->display('admin.menus.js.html.twig', array(
'menus' => $menus, 'menus' => $menus,
'last_id' => $last_id, 'last_id' => $last_id,
'menu_default_color' => $config['menu_default_color'] ?? '#ffffff'
)); ));
?> ?>
<?php <?php
} else { } else {
$templates = Menu::select('template')->distinct()->get()->toArray(); $templates = Menu::select('template')->distinct()->get()->toArray();
foreach ($templates as $key => $value) { foreach ($templates as $key => $value) {
$file = TEMPLATES . $value['template'] . '/config.php'; $path = TEMPLATES . $value['template'];
if (!file_exists($file)) {
if (isset($pluginThemes[$value['template']])) {
$path = BASE . $pluginThemes[$value['template']];
}
if (!file_exists($path . '/config.php')) {
unset($templates[$key]); unset($templates[$key]);
} }
} }
@ -179,3 +200,11 @@ if (isset($_POST['template'])) {
'templates' => $templates 'templates' => $templates
)); ));
} }
function onTemplateMenusChange(): void
{
$cache = Cache::getInstance();
if ($cache->enabled()) {
$cache->delete('template_menus');
}
}

View File

@ -26,8 +26,8 @@
if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is required.'); if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is required.');
const MYAAC = true; const MYAAC = true;
const MYAAC_VERSION = '1.0-RC.2'; const MYAAC_VERSION = '1.2';
const DATABASE_VERSION = 42; const DATABASE_VERSION = 44;
const TABLE_PREFIX = 'myaac_'; const TABLE_PREFIX = 'myaac_';
define('START_TIME', microtime(true)); define('START_TIME', microtime(true));
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX')); define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX'));

View File

@ -8,7 +8,7 @@
"ext-dom": "*", "ext-dom": "*",
"phpmailer/phpmailer": "^6.1", "phpmailer/phpmailer": "^6.1",
"composer/semver": "^3.2", "composer/semver": "^3.2",
"twig/twig": "^2.0", "twig/twig": "^3.11",
"erusev/parsedown": "^1.7", "erusev/parsedown": "^1.7",
"nikic/fast-route": "^1.3", "nikic/fast-route": "^1.3",
"matomo/device-detector": "^6.0", "matomo/device-detector": "^6.0",

295
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "27c63d07ab6337cd8bb5b59f2b2e08d8", "content-hash": "be4d1489a53a9cd8eec6bcaa7a096f30",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@ -885,16 +885,16 @@
}, },
{ {
"name": "matomo/device-detector", "name": "matomo/device-detector",
"version": "6.4.1", "version": "6.4.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/matomo-org/device-detector.git", "url": "https://github.com/matomo-org/device-detector.git",
"reference": "0d364e0dd6c177da3c24cd4049178026324fd7ac" "reference": "806e52d214b05ddead1a1d4304c7592f61f95976"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/0d364e0dd6c177da3c24cd4049178026324fd7ac", "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/806e52d214b05ddead1a1d4304c7592f61f95976",
"reference": "0d364e0dd6c177da3c24cd4049178026324fd7ac", "reference": "806e52d214b05ddead1a1d4304c7592f61f95976",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -950,20 +950,20 @@
"source": "https://github.com/matomo-org/matomo", "source": "https://github.com/matomo-org/matomo",
"wiki": "https://dev.matomo.org/" "wiki": "https://dev.matomo.org/"
}, },
"time": "2024-09-24T13:50:04+00:00" "time": "2024-12-16T16:38:01+00:00"
}, },
{ {
"name": "maximebf/debugbar", "name": "maximebf/debugbar",
"version": "v1.23.3", "version": "v1.23.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/maximebf/php-debugbar.git", "url": "https://github.com/php-debugbar/php-debugbar.git",
"reference": "687400043d77943ef95e8417cb44e1673ee57844" "reference": "eeabd61a1f19ba5dcd5ac4585a477130ee03ce25"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/687400043d77943ef95e8417cb44e1673ee57844", "url": "https://api.github.com/repos/php-debugbar/php-debugbar/zipball/eeabd61a1f19ba5dcd5ac4585a477130ee03ce25",
"reference": "687400043d77943ef95e8417cb44e1673ee57844", "reference": "eeabd61a1f19ba5dcd5ac4585a477130ee03ce25",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1015,10 +1015,10 @@
"debugbar" "debugbar"
], ],
"support": { "support": {
"issues": "https://github.com/maximebf/php-debugbar/issues", "issues": "https://github.com/php-debugbar/php-debugbar/issues",
"source": "https://github.com/maximebf/php-debugbar/tree/v1.23.3" "source": "https://github.com/php-debugbar/php-debugbar/tree/v1.23.5"
}, },
"time": "2024-10-29T12:24:25+00:00" "time": "2024-12-15T19:20:42+00:00"
}, },
{ {
"name": "mustangostang/spyc", "name": "mustangostang/spyc",
@ -1076,16 +1076,16 @@
}, },
{ {
"name": "nesbot/carbon", "name": "nesbot/carbon",
"version": "2.72.5", "version": "2.72.6",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/briannesbitt/Carbon.git", "url": "https://github.com/CarbonPHP/carbon.git",
"reference": "afd46589c216118ecd48ff2b95d77596af1e57ed" "reference": "1e9d50601e7035a4c61441a208cb5bed73e108c5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/afd46589c216118ecd48ff2b95d77596af1e57ed", "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/1e9d50601e7035a4c61441a208cb5bed73e108c5",
"reference": "afd46589c216118ecd48ff2b95d77596af1e57ed", "reference": "1e9d50601e7035a4c61441a208cb5bed73e108c5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1105,7 +1105,7 @@
"doctrine/orm": "^2.7 || ^3.0", "doctrine/orm": "^2.7 || ^3.0",
"friendsofphp/php-cs-fixer": "^3.0", "friendsofphp/php-cs-fixer": "^3.0",
"kylekatarnls/multi-tester": "^2.0", "kylekatarnls/multi-tester": "^2.0",
"ondrejmirtes/better-reflection": "*", "ondrejmirtes/better-reflection": "<6",
"phpmd/phpmd": "^2.9", "phpmd/phpmd": "^2.9",
"phpstan/extension-installer": "^1.0", "phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.12.99 || ^1.7.14", "phpstan/phpstan": "^0.12.99 || ^1.7.14",
@ -1118,10 +1118,6 @@
], ],
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": {
"dev-master": "3.x-dev",
"dev-2.x": "2.x-dev"
},
"laravel": { "laravel": {
"providers": [ "providers": [
"Carbon\\Laravel\\ServiceProvider" "Carbon\\Laravel\\ServiceProvider"
@ -1131,6 +1127,10 @@
"includes": [ "includes": [
"extension.neon" "extension.neon"
] ]
},
"branch-alias": {
"dev-2.x": "2.x-dev",
"dev-master": "3.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -1179,7 +1179,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-06-03T19:18:41+00:00" "time": "2024-12-27T09:28:11+00:00"
}, },
{ {
"name": "nikic/fast-route", "name": "nikic/fast-route",
@ -1575,16 +1575,16 @@
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v6.4.15", "version": "v6.4.17",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/console.git", "url": "https://github.com/symfony/console.git",
"reference": "f1fc6f47283e27336e7cebb9e8946c8de7bff9bd" "reference": "799445db3f15768ecc382ac5699e6da0520a0a04"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/f1fc6f47283e27336e7cebb9e8946c8de7bff9bd", "url": "https://api.github.com/repos/symfony/console/zipball/799445db3f15768ecc382ac5699e6da0520a0a04",
"reference": "f1fc6f47283e27336e7cebb9e8946c8de7bff9bd", "reference": "799445db3f15768ecc382ac5699e6da0520a0a04",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1649,7 +1649,7 @@
"terminal" "terminal"
], ],
"support": { "support": {
"source": "https://github.com/symfony/console/tree/v6.4.15" "source": "https://github.com/symfony/console/tree/v6.4.17"
}, },
"funding": [ "funding": [
{ {
@ -1665,7 +1665,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-11-06T14:19:14+00:00" "time": "2024-12-07T12:07:30+00:00"
}, },
{ {
"name": "symfony/deprecation-contracts", "name": "symfony/deprecation-contracts",
@ -1686,12 +1686,12 @@
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": { "branch-alias": {
"dev-main": "3.5-dev" "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
} }
}, },
"autoload": { "autoload": {
@ -1760,8 +1760,8 @@
"type": "library", "type": "library",
"extra": { "extra": {
"thanks": { "thanks": {
"name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill",
"url": "https://github.com/symfony/polyfill" "name": "symfony/polyfill"
} }
}, },
"autoload": { "autoload": {
@ -1836,8 +1836,8 @@
"type": "library", "type": "library",
"extra": { "extra": {
"thanks": { "thanks": {
"name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill",
"url": "https://github.com/symfony/polyfill" "name": "symfony/polyfill"
} }
}, },
"autoload": { "autoload": {
@ -1914,8 +1914,8 @@
"type": "library", "type": "library",
"extra": { "extra": {
"thanks": { "thanks": {
"name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill",
"url": "https://github.com/symfony/polyfill" "name": "symfony/polyfill"
} }
}, },
"autoload": { "autoload": {
@ -1998,8 +1998,8 @@
"type": "library", "type": "library",
"extra": { "extra": {
"thanks": { "thanks": {
"name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill",
"url": "https://github.com/symfony/polyfill" "name": "symfony/polyfill"
} }
}, },
"autoload": { "autoload": {
@ -2052,71 +2052,6 @@
], ],
"time": "2024-09-09T11:45:10+00:00" "time": "2024-09-09T11:45:10+00:00"
}, },
{
"name": "symfony/polyfill-php72",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce",
"reference": "fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"type": "metapackage",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php72/tree/v1.31.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{ {
"name": "symfony/polyfill-php80", "name": "symfony/polyfill-php80",
"version": "v1.31.0", "version": "v1.31.0",
@ -2137,8 +2072,8 @@
"type": "library", "type": "library",
"extra": { "extra": {
"thanks": { "thanks": {
"name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill",
"url": "https://github.com/symfony/polyfill" "name": "symfony/polyfill"
} }
}, },
"autoload": { "autoload": {
@ -2197,6 +2132,82 @@
], ],
"time": "2024-09-09T11:45:10+00:00" "time": "2024-09-09T11:45:10+00:00"
}, },
{
"name": "symfony/polyfill-php81",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php81\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{ {
"name": "symfony/service-contracts", "name": "symfony/service-contracts",
"version": "v3.5.1", "version": "v3.5.1",
@ -2221,12 +2232,12 @@
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": { "branch-alias": {
"dev-main": "3.5-dev" "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
} }
}, },
"autoload": { "autoload": {
@ -2480,12 +2491,12 @@
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": { "branch-alias": {
"dev-main": "3.5-dev" "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
} }
}, },
"autoload": { "autoload": {
@ -2626,38 +2637,38 @@
}, },
{ {
"name": "twig/twig", "name": "twig/twig",
"version": "v2.16.1", "version": "v3.19.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/twigphp/Twig.git", "url": "https://github.com/twigphp/Twig.git",
"reference": "19185947ec75d433a3ac650af32fc05649b95ee1" "reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/19185947ec75d433a3ac650af32fc05649b95ee1", "url": "https://api.github.com/repos/twigphp/Twig/zipball/d4f8c2b86374f08efc859323dbcd95c590f7124e",
"reference": "19185947ec75d433a3ac650af32fc05649b95ee1", "reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.1.3", "php": ">=8.0.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8", "symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3", "symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php72": "^1.8" "symfony/polyfill-php81": "^1.29"
}, },
"require-dev": { "require-dev": {
"psr/container": "^1.0", "phpstan/phpstan": "^2.0",
"symfony/phpunit-bridge": "^5.4.9|^6.3" "psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
}, },
"type": "library", "type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.16-dev"
}
},
"autoload": { "autoload": {
"psr-0": { "files": [
"Twig_": "lib/" "src/Resources/core.php",
}, "src/Resources/debug.php",
"src/Resources/escaper.php",
"src/Resources/string_loader.php"
],
"psr-4": { "psr-4": {
"Twig\\": "src/" "Twig\\": "src/"
} }
@ -2690,7 +2701,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/twigphp/Twig/issues", "issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v2.16.1" "source": "https://github.com/twigphp/Twig/tree/v3.19.0"
}, },
"funding": [ "funding": [
{ {
@ -2702,7 +2713,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-09-09T17:53:56+00:00" "time": "2025-01-29T07:06:14+00:00"
}, },
{ {
"name": "voku/portable-ascii", "name": "voku/portable-ascii",
@ -2840,16 +2851,16 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.12.12", "version": "1.12.15",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0" "reference": "c91d4e8bc056f46cf653656e6f71004b254574d1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c91d4e8bc056f46cf653656e6f71004b254574d1",
"reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0", "reference": "c91d4e8bc056f46cf653656e6f71004b254574d1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2894,7 +2905,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-11-28T22:13:23+00:00" "time": "2025-01-05T16:40:22+00:00"
} }
], ],
"aliases": [], "aliases": [],
@ -2911,5 +2922,5 @@
"ext-dom": "*" "ext-dom": "*"
}, },
"platform-dev": [], "platform-dev": [],
"plugin-api-version": "2.6.0" "plugin-api-version": "2.3.0"
} }

View File

@ -116,9 +116,9 @@ describe('Check Public Pages', () => {
}) })
// library // library
it('Go to creatures page', () => { it('Go to monsters page', () => {
cy.visit({ cy.visit({
url: Cypress.env('URL') + '/creatures', url: Cypress.env('URL') + '/monsters',
method: 'GET', method: 'GET',
}) })
}) })

View File

@ -4,7 +4,7 @@ const YOU_ARE_NOT_LOGGEDIN = 'You are not logged in.';
describe('Check Protected Pages', () => { describe('Check Protected Pages', () => {
// character actions // character actions
it('Go to accouht character creation page', () => { it('Go to account character creation page', () => {
cy.visit({ cy.visit({
url: Cypress.env('URL') + '/account/character/create', url: Cypress.env('URL') + '/account/character/create',
method: 'GET', method: 'GET',
@ -12,7 +12,7 @@ describe('Check Protected Pages', () => {
cy.contains(REQUIRED_LOGIN_MESSAGE) cy.contains(REQUIRED_LOGIN_MESSAGE)
}) })
it('Go to accouht character deletion page', () => { it('Go to account character deletion page', () => {
cy.visit({ cy.visit({
url: Cypress.env('URL') + '/account/character/delete', url: Cypress.env('URL') + '/account/character/delete',
method: 'GET', method: 'GET',
@ -21,7 +21,7 @@ describe('Check Protected Pages', () => {
}) })
// account actions // account actions
it('Go to accouht email change page', () => { it('Go to account email change page', () => {
cy.visit({ cy.visit({
url: Cypress.env('URL') + '/account/email', url: Cypress.env('URL') + '/account/email',
method: 'GET', method: 'GET',
@ -29,7 +29,7 @@ describe('Check Protected Pages', () => {
cy.contains(REQUIRED_LOGIN_MESSAGE) cy.contains(REQUIRED_LOGIN_MESSAGE)
}) })
it('Go to accouht password change page', () => { it('Go to account password change page', () => {
cy.visit({ cy.visit({
url: Cypress.env('URL') + '/account/password', url: Cypress.env('URL') + '/account/password',
method: 'GET', method: 'GET',
@ -37,7 +37,7 @@ describe('Check Protected Pages', () => {
cy.contains(REQUIRED_LOGIN_MESSAGE) cy.contains(REQUIRED_LOGIN_MESSAGE)
}) })
it('Go to accouht info change page', () => { it('Go to account info change page', () => {
cy.visit({ cy.visit({
url: Cypress.env('URL') + '/account/info', url: Cypress.env('URL') + '/account/info',
method: 'GET', method: 'GET',
@ -45,7 +45,7 @@ describe('Check Protected Pages', () => {
cy.contains(REQUIRED_LOGIN_MESSAGE) cy.contains(REQUIRED_LOGIN_MESSAGE)
}) })
it('Go to accouht logout change page', () => { it('Go to account logout change page', () => {
cy.visit({ cy.visit({
url: Cypress.env('URL') + '/account/logout', url: Cypress.env('URL') + '/account/logout',
method: 'GET', method: 'GET',

View File

@ -76,6 +76,8 @@ require_once SYSTEM . 'status.php';
$twig->addGlobal('config', $config); $twig->addGlobal('config', $config);
$twig->addGlobal('status', $status); $twig->addGlobal('status', $status);
$hooks->trigger(HOOK_STARTUP);
// backward support for gesior // backward support for gesior
if(setting('core.backward_support')) { if(setting('core.backward_support')) {
define('INITIALIZED', true); define('INITIALIZED', true);
@ -117,8 +119,6 @@ if(setting('core.backward_support')) {
require_once SYSTEM . 'router.php'; require_once SYSTEM . 'router.php';
$hooks->trigger(HOOK_STARTUP);
// anonymous usage statistics // anonymous usage statistics
// sent only when user agrees // sent only when user agrees
if(setting('core.anonymous_usage_statistics')) { if(setting('core.anonymous_usage_statistics')) {
@ -134,7 +134,7 @@ if(setting('core.anonymous_usage_statistics')) {
if(fetchDatabaseConfig('last_usage_report', $value)) { if(fetchDatabaseConfig('last_usage_report', $value)) {
$should_report = time() > (int)$value + $report_time; $should_report = time() > (int)$value + $report_time;
if($cache->enabled()) { if($cache->enabled()) {
$cache->set('last_usage_report', $value); $cache->set('last_usage_report', $value, 60 * 60);
} }
} }
else { else {
@ -148,7 +148,7 @@ if(setting('core.anonymous_usage_statistics')) {
updateDatabaseConfig('last_usage_report', time()); updateDatabaseConfig('last_usage_report', time());
if($cache->enabled()) { if($cache->enabled()) {
$cache->set('last_usage_report', time()); $cache->set('last_usage_report', time(), 60 * 60);
} }
} }
} }

View File

@ -1,13 +1,13 @@
SET @myaac_database_version = 42; SET @myaac_database_version = 43;
CREATE TABLE `myaac_account_actions` CREATE TABLE `myaac_account_actions`
( (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`account_id` INT(11) NOT NULL, `account_id` INT(11) NOT NULL,
`ip` INT(10) UNSIGNED NOT NULL DEFAULT 0, `ip` VARCHAR(45) NOT NULL DEFAULT '',
`ipv6` BINARY(16) NOT NULL DEFAULT 0,
`date` INT(11) NOT NULL DEFAULT 0, `date` INT(11) NOT NULL DEFAULT 0,
`action` VARCHAR(255) NOT NULL DEFAULT '', `action` VARCHAR(255) NOT NULL DEFAULT '',
KEY (`account_id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4;
CREATE TABLE `myaac_admin_menu` CREATE TABLE `myaac_admin_menu`

View File

@ -1,3 +1,4 @@
<?php defined('MYAAC') or die('Direct access not allowed!'); ?>
<!DOCTYPE html> <!DOCTYPE html>
<html dir="<?php echo $locale['direction']; ?>" lang="<?php echo $locale['lang']; ?>" xml:lang="<?php echo $locale['lang']; ?>"> <html dir="<?php echo $locale['direction']; ?>" lang="<?php echo $locale['lang']; ?>" xml:lang="<?php echo $locale['lang']; ?>">
<head> <head>

View File

@ -156,9 +156,14 @@ if ($db->hasTable('guilds')) {
} }
if (!$db->hasColumn('guilds', 'description')) { if (!$db->hasColumn('guilds', 'description')) {
if (query("ALTER TABLE `guilds` ADD `description` TEXT NOT NULL;")) if (query("ALTER TABLE `guilds` ADD `description` VARCHAR(5000) NOT NULL DEFAULT '';"))
success($locale['step_database_adding_field'] . ' guilds.description...'); success($locale['step_database_adding_field'] . ' guilds.description...');
} }
else {
if (query("ALTER TABLE `guilds` MODIFY `description` VARCHAR(5000) NOT NULL DEFAULT '';")) {
success($locale['step_database_modifying_field'] . ' guilds.description...');
}
}
if ($db->hasColumn('guilds', 'logo_gfx_name')) { if ($db->hasColumn('guilds', 'logo_gfx_name')) {
if (query("ALTER TABLE `guilds` CHANGE `logo_gfx_name` `logo_name` VARCHAR( 255 ) NOT NULL DEFAULT 'default.gif';")) { if (query("ALTER TABLE `guilds` CHANGE `logo_gfx_name` `logo_name` VARCHAR( 255 ) NOT NULL DEFAULT 'default.gif';")) {
@ -197,9 +202,14 @@ if ($db->hasTable('players')) {
} }
if (!$db->hasColumn('players', 'comment')) { if (!$db->hasColumn('players', 'comment')) {
if (query("ALTER TABLE `players` ADD `comment` TEXT NOT NULL;")) if (query("ALTER TABLE `players` ADD `comment` VARCHAR(5000) NOT NULL DEFAULT '';"))
success($locale['step_database_adding_field'] . ' players.comment...'); success($locale['step_database_adding_field'] . ' players.comment...');
} }
else {
if (query("ALTER TABLE `players` MODIFY `comment` VARCHAR(5000) NOT NULL DEFAULT '';")) {
success($locale['step_database_modifying_field'] . ' players.comment...');
}
}
if ($db->hasColumn('players', 'rank_id')) { if ($db->hasColumn('players', 'rank_id')) {
if (query("ALTER TABLE players MODIFY `rank_id` INT(11) NOT NULL DEFAULT 0;")) if (query("ALTER TABLE players MODIFY `rank_id` INT(11) NOT NULL DEFAULT 0;"))

View File

@ -24,7 +24,7 @@ server {
} }
location / { location / {
try_files $uri $uri/ /index.php?$query_string;; try_files $uri $uri/ /index.php?$query_string;
} }
location ~ \.php$ { location ~ \.php$ {

901
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
"postinstall": "node ./npm-post-install.js" "postinstall": "node ./npm-post-install.js"
}, },
"devDependencies": { "devDependencies": {
"cypress": "^12.12.0" "cypress": "^13.17.0"
}, },
"dependencies": { "dependencies": {
"@tinymce/tinymce-jquery": "^2.1.0", "@tinymce/tinymce-jquery": "^2.1.0",
@ -12,6 +12,6 @@
"fs-extra": "^11.2.0", "fs-extra": "^11.2.0",
"jquery": "^3.7.1", "jquery": "^3.7.1",
"jquery-ui": "^1.13.2", "jquery-ui": "^1.13.2",
"tinymce": "^6.8.3" "tinymce": "^7.2.0"
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "create-account-hint", "name": "create-account-hint",
"description": "This plugin display text 'To play on Forgotten you need an account. All you have to do to create your new account is to enter an account name, password, country and your email address. Also you have to agree to the terms presented below. If you have done so, your account name will be shown on the following page and your account password will be sent to your email address along with further instructions. If you do not receive the email with your password, please check your spam filter.' on the create account page. <strong>Be careful when uninstalling this!</strong>", "description": "This plugin display text 'To play on Forgotten you need an account. All you have to do to create your new account is to enter an account name, password, country and your email address. Also you have to agree to the terms presented below. If you have done so, your account name will be shown on the following page and your account password will be sent to your email address along with further instructions. If you do not receive the email with your password, please check your spam filter.' on the create account page.",
"version": "1.0", "version": "1.0",
"author": "slawkens", "author": "slawkens",
"contact": "slawkens@gmail.com", "contact": "slawkens@gmail.com",

View File

@ -9,7 +9,4 @@
*/ */
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
global $twig_loader; $twig->display('account-create-hint/hint.html.twig');
$twig_loader->prependPath(BASE . 'plugins/account-create-hint');
$twig->display('hint.html.twig');

View File

@ -22,7 +22,7 @@ if [ $1 = "prepare" ]; then
mkdir -p tmp mkdir -p tmp
# get myaac from git archive # get myaac from git archive
git archive --format zip --output tmp/myaac.zip develop git archive --format zip --output tmp/myaac.zip main
cd tmp/ || exit cd tmp/ || exit

View File

@ -49,7 +49,7 @@ function warning($message, $return = false) {
return message($message, 'warning', $return); return message($message, 'warning', $return);
} }
function note($message, $return = false) { function note($message, $return = false) {
return info($message, $return); return message($message, 'note', $return);
} }
function info($message, $return = false) { function info($message, $return = false) {
return message($message, 'info', $return); return message($message, 'info', $return);
@ -121,7 +121,7 @@ function getPlayerLink($name, $generate = true, bool $colored = false): string
function getMonsterLink($name, $generate = true): string function getMonsterLink($name, $generate = true): string
{ {
$url = BASE_URL . (setting('core.friendly_urls') ? '' : 'index.php/') . 'monsters/' . urlencode($name); $url = BASE_URL . (setting('core.friendly_urls') ? '' : 'index.php/') . 'monsters?name=' . urlencode($name);
if(!$generate) return $url; if(!$generate) return $url;
return generateLink($url, $name); return generateLink($url, $name);
@ -129,16 +129,14 @@ function getMonsterLink($name, $generate = true): string
function getHouseLink($name, $generate = true): string function getHouseLink($name, $generate = true): string
{ {
if(is_numeric($name)) if(is_numeric($name)) {
{
$house = House::find(intval($name), ['name']); $house = House::find(intval($name), ['name']);
if ($house) { if ($house) {
$name = $house->name; $name = $house->name;
} }
} }
$url = BASE_URL . (setting('core.friendly_urls') ? '' : 'index.php/') . 'houses?name=' . urlencode($name);
$url = BASE_URL . (setting('core.friendly_urls') ? '' : 'index.php/') . 'houses/' . urlencode($name);
if(!$generate) return $url; if(!$generate) return $url;
return generateLink($url, $name); return generateLink($url, $name);
@ -990,31 +988,29 @@ function load_config_lua($filename)
continue; continue;
} }
$tmp_exp = explode('=', $line, 2); $tmp_exp = explode('=', $line, 2);
if(strpos($line, 'dofile') !== false) if(str_contains($line, 'dofile')) {
{
$delimiter = '"'; $delimiter = '"';
if(strpos($line, $delimiter) === false) if(!str_contains($line, $delimiter)) {
$delimiter = "'"; $delimiter = "'";
}
$tmp = explode($delimiter, $line); $tmp = explode($delimiter, $line);
$result = array_merge($result, load_config_lua($config['server_path'] . $tmp[1])); $result = array_merge($result, load_config_lua($config['server_path'] . $tmp[1]));
} }
else if(count($tmp_exp) >= 2) else if(count($tmp_exp) >= 2) {
{
$key = trim($tmp_exp[0]); $key = trim($tmp_exp[0]);
if(0 !== strpos($key, '--')) if(!str_starts_with($key, '--')) {
{
$value = trim($tmp_exp[1]); $value = trim($tmp_exp[1]);
if(strpos($value, '--') !== false) {// found some deep comment if(str_contains($value, '--')) {// found some deep comment
$value = preg_replace('/--.*$/i', '', $value); $value = preg_replace('/--.*$/i', '', $value);
} }
if(is_numeric($value)) if(is_numeric($value))
$result[$key] = (float) $value; $result[$key] = (float) $value;
elseif(in_array(@$value[0], array("'", '"')) && in_array(@$value[strlen($value) - 1], array("'", '"'))) elseif(in_array(@$value[0], array("'", '"')) && in_array(@$value[strlen($value) - 1], array("'", '"')))
$result[$key] = (string) substr(substr($value, 1), 0, -1); $result[$key] = substr(substr($value, 1), 0, -1);
elseif(in_array($value, array('true', 'false'))) elseif(in_array($value, array('true', 'false')))
$result[$key] = ($value === 'true') ? true : false; $result[$key] = $value === 'true';
elseif(@$value[0] === '{') { elseif(@$value[0] === '{') {
// arrays are not supported yet // arrays are not supported yet
// just ignore the error // just ignore the error
@ -1022,7 +1018,7 @@ function load_config_lua($filename)
} }
else else
{ {
foreach($result as $tmp_key => $tmp_value) // load values definied by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull foreach($result as $tmp_key => $tmp_value) // load values defined by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull
$value = str_replace($tmp_key, $tmp_value, $value); $value = str_replace($tmp_key, $tmp_value, $value);
$ret = @eval("return $value;"); $ret = @eval("return $value;");
if((string) $ret == '' && trim($value) !== '""') // = parser error if((string) $ret == '' && trim($value) !== '""') // = parser error
@ -1036,8 +1032,7 @@ function load_config_lua($filename)
} }
} }
$result = array_merge($result, isset($config['lua']) ? $config['lua'] : array()); return array_merge($result, $config['lua'] ?? []);
return $result;
} }
function str_replace_first($search,$replace, $subject) { function str_replace_first($search,$replace, $subject) {
@ -1063,17 +1058,36 @@ function get_browser_real_ip() {
return '0'; return '0';
} }
function setSession($key, $data) { function setSession($key, $value = null): void {
$_SESSION[setting('core.session_prefix') . $key] = $data; if (!is_array($key)) {
$key = [$key => $value];
}
foreach ($key as $arrayKey => $arrayValue) {
if (is_null($arrayValue)) {
unsetSession($arrayKey);
}
else {
$_SESSION[setting('core.session_prefix') . $arrayKey] = $arrayValue;
}
}
} }
function getSession($key) { function getSession($key) {
$key = setting('core.session_prefix') . $key; return $_SESSION[setting('core.session_prefix') . $key] ?? null;
return isset($_SESSION[$key]) ? $_SESSION[$key] : false;
} }
function unsetSession($key) { function unsetSession($key): void {
unset($_SESSION[setting('core.session_prefix') . $key]); unset($_SESSION[setting('core.session_prefix') . $key]);
} }
function session($key): mixed {
if (is_array($key)) {
setSession($key);
return null;
}
return getSession($key);
}
function csrf(bool $return = false): string { function csrf(bool $return = false): string {
return CsrfToken::create($return); return CsrfToken::create($return);
} }
@ -1096,20 +1110,16 @@ function csrfProtect(): void
} }
} }
function getTopPlayers($limit = 5) { function getTopPlayers($limit = 5, $skill = 'level') {
global $db; global $db;
$cache = Cache::getInstance(); if ($skill === 'level') {
if($cache->enabled()) { $skill = 'experience';
$tmp = '';
if($cache->fetch('top_' . $limit . '_level', $tmp)) {
$players = unserialize($tmp);
}
} }
if (!isset($players)) { return Cache::remember("top_{$limit}_{$skill}", 2 * 60, function () use ($db, $limit, $skill) {
$columns = [ $columns = [
'id', 'name', 'level', 'vocation', 'experience', 'id', 'name', 'level', 'vocation', 'experience', 'balance',
'looktype', 'lookhead', 'lookbody', 'looklegs', 'lookfeet' 'looktype', 'lookhead', 'lookbody', 'looklegs', 'lookfeet'
]; ];
@ -1117,36 +1127,27 @@ function getTopPlayers($limit = 5) {
$columns[] = 'lookaddons'; $columns[] = 'lookaddons';
} }
if ($db->hasColumn('players', 'online')) { return Player::query()
$columns[] = 'online';
}
$players = Player::query()
->select($columns) ->select($columns)
->withOnlineStatus() ->withOnlineStatus()
->notDeleted() ->notDeleted()
->where('group_id', '<', setting('core.highscores_groups_hidden')) ->where('group_id', '<', setting('core.highscores_groups_hidden'))
->whereNotIn('id', setting('core.highscores_ids_hidden')) ->whereNotIn('id', setting('core.highscores_ids_hidden'))
->where('account_id', '!=', 1) ->where('account_id', '!=', 1)
->orderByDesc('experience') ->orderByDesc($skill)
->limit($limit) ->limit($limit)
->get() ->get()
->map(function ($e, $i) { ->map(function ($e, $i) {
$row = $e->toArray(); $row = $e->toArray();
$row['online'] = $e->online_status; $row['online'] = $e->online_status;
$row['rank'] = $i + 1; $row['rank'] = $i + 1;
$row['outfit_url'] = $e->outfit_url;
unset($row['online_table']); unset($row['online_table']);
return $row; return $row;
})->toArray(); })->toArray();
});
if($cache->enabled()) {
$cache->set('top_' . $limit . '_level', serialize($players), 120);
}
}
return $players;
} }
function deleteDirectory($dir, $ignore = array(), $contentOnly = false) { function deleteDirectory($dir, $ignore = array(), $contentOnly = false) {
@ -1694,4 +1695,7 @@ function getAccountIdentityColumn(): string
require_once SYSTEM . 'compat/base.php'; require_once SYSTEM . 'compat/base.php';
// custom functions // custom functions
require SYSTEM . 'functions_custom.php'; $customFunctions = SYSTEM . 'functions_custom.php';
if (is_file($customFunctions)) {
require $customFunctions;
}

View File

@ -50,6 +50,7 @@ $cache = Cache::getInstance();
global $hooks; global $hooks;
$hooks = new Hooks(); $hooks = new Hooks();
$hooks->load(); $hooks->load();
$hooks->trigger(HOOK_INIT);
// twig // twig
require_once SYSTEM . 'twig.php'; require_once SYSTEM . 'twig.php';
@ -96,8 +97,8 @@ if($config_lua_reload) {
// cache config // cache config
if($cache->enabled()) { if($cache->enabled()) {
$cache->set('config_lua', serialize($config['lua']), 120); $cache->set('config_lua', serialize($config['lua']), 2 * 60);
$cache->set('server_path', $config['server_path']); $cache->set('server_path', $config['server_path'], 10 * 60);
} }
} }
unset($tmp); unset($tmp);
@ -139,7 +140,7 @@ require_once SYSTEM . 'database.php';
// verify myaac tables exists in database // verify myaac tables exists in database
if(!defined('MYAAC_INSTALL') && !$db->hasTable('myaac_account_actions')) { if(!defined('MYAAC_INSTALL') && !$db->hasTable('myaac_account_actions')) {
throw new RuntimeException('Seems that the table myaac_account_actions of MyAAC doesn\'t exist in the database. This is a fatal error. You can try to reinstall MyAAC by visiting ' . BASE_URL . 'install'); throw new RuntimeException('Seems that the table myaac_account_actions of MyAAC doesn\'t exist in the database. This is a fatal error. You can try to reinstall MyAAC by visiting ' . (IS_CLI ? 'http://your-ip.com/' : BASE_URL) . 'install');
} }
// execute migrations // execute migrations

View File

@ -12,6 +12,8 @@
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3 * @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3
*/ */
use MyAAC\Models\AccountAction;
/** /**
* OTServ account abstraction. * OTServ account abstraction.
* *
@ -1010,26 +1012,16 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
public function logAction($action) public function logAction($action)
{ {
$ip = get_browser_real_ip(); AccountAction::create([
if(strpos($ip, ":") === false) { 'account_id' => $this->getId(),
$ipv6 = '0'; 'ip' => get_browser_real_ip(),
} 'date' => time(),
else { 'action' => $action,
$ipv6 = $ip; ]);
$ip = '';
} }
return $this->db->exec('INSERT INTO `' . TABLE_PREFIX . 'account_actions` (`account_id`, `ip`, `ipv6`, `date`, `action`) VALUES (' . $this->db->quote($this->getId()).', ' . ($ip == '' ? '0' : $this->db->quote(ip2long($ip))) . ', (' . ($ipv6 == '0' ? $this->db->quote('') : $this->db->quote(inet_pton($ipv6))) . '), UNIX_TIMESTAMP(NOW()), ' . $this->db->quote($action).')'); public function getActionsLog($limit) {
} return AccountAction::where('account_id', $this->data['id'])->orderByDesc('date')->limit($limit)->get()->toArray();
public function getActionsLog($limit1, $limit2)
{
$actions = array();
foreach($this->db->query('SELECT `ip`, `ipv6`, `date`, `action` FROM `' . TABLE_PREFIX . 'account_actions` WHERE `account_id` = ' . $this->data['id'] . ' ORDER by `date` DESC LIMIT ' . $limit1 . ', ' . $limit2 . '')->fetchAll() as $a)
$actions[] = array('ip' => $a['ip'], 'ipv6' => $a['ipv6'], 'date' => $a['date'], 'action' => $a['action']);
return $actions;
} }
/** /**
* Returns players iterator. * Returns players iterator.

View File

@ -108,6 +108,8 @@ class OTS_Player extends OTS_Row_DAO
POT::SKILL_SHIELD => array('value' => 0, 'tries' => 0), POT::SKILL_SHIELD => array('value' => 0, 'tries' => 0),
POT::SKILL_FISH => array('value' => 0, 'tries' => 0) POT::SKILL_FISH => array('value' => 0, 'tries' => 0)
); );
private static array $playersOnline;
/** /**
* Magic PHP5 method. * Magic PHP5 method.
* *
@ -765,10 +767,18 @@ class OTS_Player extends OTS_Row_DAO
public function isOnline() public function isOnline()
{ {
if($this->db->hasTable('players_online')) // tfs 1.0 if($this->db->hasTable('players_online')) {// tfs 1.0
{ if (!isset(self::$playersOnline)) {
$query = $this->db->query('SELECT `player_id` FROM `players_online` WHERE `player_id` = ' . $this->data['id']); self::$playersOnline = [];
return $query->rowCount() > 0;
$query = $this->db->query('SELECT `player_id` FROM `players_online`');
foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $item) {
self::$playersOnline[$item['player_id']] = true;
}
}
return isset(self::$playersOnline[$this->data['id']]);
} }
if( !isset($this->data['online']) ) if( !isset($this->data['online']) )

View File

@ -14,12 +14,12 @@ $account_logged = new OTS_Account();
// stay-logged with sessions // stay-logged with sessions
$current_session = getSession('account'); $current_session = getSession('account');
if($current_session !== false) if($current_session)
{ {
$account_logged->load($current_session); $account_logged->load($current_session);
if($account_logged->isLoaded() && $account_logged->getPassword() == getSession('password') if($account_logged->isLoaded() && $account_logged->getPassword() == getSession('password')
//&& (!isset($_SESSION['admin']) || admin()) //&& (!isset($_SESSION['admin']) || admin())
&& (getSession('remember_me') !== false || getSession('last_visit') > time() - 15 * 60)) { // login for 15 minutes if "remember me" is not used && (getSession('remember_me') || getSession('last_visit') > time() - 15 * 60)) { // login for 15 minutes if "remember me" is not used
$logged = true; $logged = true;
} }
else { else {

20
system/migrations/43.php Normal file
View File

@ -0,0 +1,20 @@
<?php
/**
* @var OTS_DB_MySQL $db
*/
// 2025-01-22
// change columns to VARCHAR
$up = function () use ($db) {
$db->query("UPDATE guilds set description = '' WHERE description is NULL;"); // prevent truncate error when column is NULL
$db->modifyColumn('guilds', 'description', "VARCHAR(5000) NOT NULL DEFAULT ''");
$db->query("UPDATE players set comment = '' WHERE comment is NULL;");
$db->modifyColumn('players', 'comment', "VARCHAR(5000) NOT NULL DEFAULT ''");
};
$down = function () use ($db) {
$db->modifyColumn('guilds', 'description', "TEXT NOT NULL");
$db->modifyColumn('players', 'comment', "TEXT NOT NULL");
};

27
system/migrations/44.php Normal file
View File

@ -0,0 +1,27 @@
<?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) {
$db->query("ALTER TABLE `myaac_account_actions` DROP KEY `account_id`;");
$db->query("ALTER TABLE `myaac_account_actions` ADD COLUMN `id` INT(11) 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';");
$db->dropColumn(TABLE_PREFIX . 'account_actions', 'ipv6');
};
$down = function () use ($db) {
$db->query("ALTER TABLE `" . TABLE_PREFIX . "account_actions` DROP `id`;");
$db->query("ALTER TABLE `" . TABLE_PREFIX . "account_actions` ADD KEY (`account_id`);");
$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

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

View File

@ -21,9 +21,9 @@ $settingHighscoresCountryBox = setting('core.highscores_country_box');
if(config('account_country') && $settingHighscoresCountryBox) if(config('account_country') && $settingHighscoresCountryBox)
require SYSTEM . 'countries.conf.php'; require SYSTEM . 'countries.conf.php';
$list = $_GET['list'] ?? 'experience'; $list = urldecode($_GET['list'] ?? 'experience');
$page = $_GET['page'] ?? 1; $page = $_GET['page'] ?? 1;
$vocation = $_GET['vocation'] ?? 'all'; $vocation = urldecode($_GET['vocation'] ?? 'all');
if(!is_numeric($page) || $page < 1 || $page > PHP_INT_MAX) { if(!is_numeric($page) || $page < 1 || $page > PHP_INT_MAX) {
$page = 1; $page = 1;
@ -207,6 +207,7 @@ if (empty($highscores)) {
$tmp = $row->toArray(); $tmp = $row->toArray();
$tmp['online'] = $row->online_status; $tmp['online'] = $row->online_status;
$tmp['vocation'] = $row->vocation_name; $tmp['vocation'] = $row->vocation_name;
$tmp['outfit_url'] = $row->outfit_url; // @phpstan-ignore-line
unset($tmp['online_table']); unset($tmp['online_table']);
return $tmp; return $tmp;
@ -233,14 +234,10 @@ foreach($highscores as $id => &$player)
$player['experience'] = number_format($player['experience']); $player['experience'] = number_format($player['experience']);
} }
if(!$settingHighscoresVocation) {
unset($player['vocation']);
}
$player['link'] = getPlayerLink($player['name'], false); $player['link'] = getPlayerLink($player['name'], false);
$player['flag'] = getFlagImage($player['country']); $player['flag'] = getFlagImage($player['country']);
if($settingHighscoresOutfit) { if($settingHighscoresOutfit) {
$player['outfit'] = '<img style="position:absolute;margin-top:' . (in_array($player['looktype'], setting('core.outfit_images_wrong_looktypes')) ? '-15px;margin-left:5px' : '-45px;margin-left:-25px') . ';" src="' . 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'] . '" alt="" />'; $player['outfit'] = '<img style="position:absolute;margin-top:' . (in_array($player['looktype'], setting('core.outfit_images_wrong_looktypes')) ? '-15px;margin-left:5px' : '-45px;margin-left:-25px') . ';" src="' . $player['outfit_url'] . '" alt="" />';
} }
$player['rank'] = $offset + $i; $player['rank'] = $offset + $i;
} }

View File

@ -30,11 +30,11 @@ $state = '';
$order = ''; $order = '';
$type = ''; $type = '';
if(isset($_GET['page']) && $_GET['page'] == 'view' && isset($_REQUEST['house'])) if(isset($_REQUEST['name']))
{ {
$beds = array("", "one", "two", "three", "fourth", "fifth"); $beds = array("", "one", "two", "three", "fourth", "fifth");
$houseName = $_REQUEST['house']; $houseName = urldecode($_REQUEST['name']);
$houseId = (Validator::number($_REQUEST['house']) ? $_REQUEST['house'] : -1); $houseId = (Validator::number($_REQUEST['name']) ? $_REQUEST['name'] : -1);
$selectHouse = $db->query('SELECT * FROM ' . $db->tableName('houses') . ' WHERE ' . $db->fieldName('name') . ' LIKE ' . $db->quote($houseName) . ' OR `id` = ' . $db->quote($houseId)); $selectHouse = $db->query('SELECT * FROM ' . $db->tableName('houses') . ' WHERE ' . $db->fieldName('name') . ' LIKE ' . $db->quote($houseName) . ' OR `id` = ' . $db->quote($houseId));
$house = array(); $house = array();
@ -187,7 +187,7 @@ if(isset($_POST['town']) && isset($_POST['state']) && isset($_POST['order']) &&
$houseRent = 'Free'; $houseRent = 'Free';
} }
$houses[] = array('owner' => $owner, 'name' => $house['name'], 'size' => ($hasTilesColumn ? $house['tiles'] : $house['size']), 'rent' => $house['rent'], 'rentedBy' => $houseRent); $houses[] = array('owner' => $owner, 'name' => $house['name'], 'size' => ($hasTilesColumn ? $house['tiles'] : $house['size']), 'rent' => $house['rent'], 'rentedBy' => $houseRent, 'link' => getHouseLink($house['name'], false));
} }
$housesSearch = true; $housesSearch = true;

View File

@ -88,7 +88,7 @@ if(isset($_GET['archive']))
foreach($news_DB as $news) foreach($news_DB as $news)
{ {
$newses[] = array( $newses[] = array(
'link' => getLink('news') . '/' . $news['id'], 'link' => getLink('news/archive') . '/' . $news['id'],
'icon_id' => $categories[$news['category']]['icon_id'], 'icon_id' => $categories[$news['category']]['icon_id'],
'title' => stripslashes($news['title']), 'title' => stripslashes($news['title']),
'date' => $news['date'] 'date' => $news['date']

View File

@ -39,10 +39,10 @@ return [
[['GET', 'POST'], 'guilds/{guild:string}', 'guilds/show.php'], [['GET', 'POST'], 'guilds/{guild:string}', 'guilds/show.php'],
['GET', 'highscores/{list:alphanum}/{vocation:alphanum}/{page:int}', 'highscores.php'], ['GET', 'highscores/{list:string}/{vocation:string}/{page:int}', 'highscores.php'],
['GET', 'highscores/{list:alphanum}/{page:int}', 'highscores.php'], ['GET', 'highscores/{list:string}/{page:int}', 'highscores.php'],
['GET', 'highscores/{list:alphanum}/{vocation:alphanum}', 'highscores.php'], ['GET', 'highscores/{list:string}/{vocation:string}', 'highscores.php'],
['GET', 'highscores/{list:alphanum}', 'highscores.php'], ['GET', 'highscores/{list:string}', 'highscores.php'],
/* /*
'/^polls\/[0-9]+\/?$/' => array('subtopic' => 'polls', 'id' => '$1'), '/^polls\/[0-9]+\/?$/' => array('subtopic' => 'polls', 'id' => '$1'),
'/^spells\/[A-Za-z0-9-_%]+\/[A-Za-z0-9-_]+\/?$/' => array('subtopic' => 'spells', 'vocation' => '$1', 'order' => '$2'), '/^spells\/[A-Za-z0-9-_%]+\/[A-Za-z0-9-_]+\/?$/' => array('subtopic' => 'spells', 'vocation' => '$1', 'order' => '$2'),

View File

@ -348,7 +348,7 @@ return [
], ],
'database_password' => [ 'database_password' => [
'name' => 'Database Password', 'name' => 'Database Password',
'type' => 'text', 'type' => 'password',
'default' => '', 'default' => '',
'show_if' => [ 'show_if' => [
'database_overwrite', '=', 'true' 'database_overwrite', '=', 'true'
@ -509,7 +509,7 @@ Sent by MyAAC,<br/>
'smtp_port' => [ 'smtp_port' => [
'name' => 'SMTP Host', 'name' => 'SMTP Host',
'type' => 'number', 'type' => 'number',
'desc' => '25 (default) / 465 (ssl, GMail) / 587 (tls, Microsoft Outlook)', 'desc' => '25 (default) / 587 (tls - GMail, Microsoft Outlook)',
'default' => 25, 'default' => 25,
'show_if' => [ 'show_if' => [
'mail_enabled', '=', 'true' 'mail_enabled', '=', 'true'
@ -536,7 +536,8 @@ Sent by MyAAC,<br/>
'smtp_pass' => [ 'smtp_pass' => [
'name' => 'SMTP Password', 'name' => 'SMTP Password',
'type' => 'password', 'type' => 'password',
'desc' => 'Here your email password to authenticate with SMTP', 'desc' => 'Here your email password to authenticate with SMTP.' . PHP_EOL
. 'For GMail use generated App password - https://myaccount.google.com/apppasswords.',
'default' => '', 'default' => '',
'show_if' => [ 'show_if' => [
'mail_enabled', '=', 'true' 'mail_enabled', '=', 'true'
@ -546,7 +547,8 @@ Sent by MyAAC,<br/>
'name' => 'SMTP Security', 'name' => 'SMTP Security',
'type' => 'options', 'type' => 'options',
'options' => ['None', 'SSL', 'TLS'], 'options' => ['None', 'SSL', 'TLS'],
'desc' => 'What kind of encryption to use on the SMTP connection', 'desc' => 'What kind of encryption to use on the SMTP connection.' . PHP_EOL
. '(Gmail, Outlook - tls).',
'default' => 0, 'default' => 0,
'show_if' => [ 'show_if' => [
'mail_enabled', '=', 'true' 'mail_enabled', '=', 'true'
@ -1401,7 +1403,7 @@ Sent by MyAAC,<br/>
'name' => 'Outfit Images URL', 'name' => 'Outfit Images URL',
'type' => 'text', 'type' => 'text',
'desc' => 'Set to animoutfit.php for animated outfit', 'desc' => 'Set to animoutfit.php for animated outfit',
'default' => 'https://outfit-images.ots.me/outfit.php', 'default' => 'https://outfit-images.ots.me/latest/outfit.php',
], ],
'outfit_images_wrong_looktypes' => [ 'outfit_images_wrong_looktypes' => [
'name' => 'Outfit Images Wrong Looktypes', 'name' => 'Outfit Images Wrong Looktypes',

View File

@ -105,9 +105,6 @@ class CreateCharacter
* @param array $errors * @param array $errors
* @return bool * @return bool
* @throws \E_OTS_NotLoaded * @throws \E_OTS_NotLoaded
* @throws \Twig_Error_Loader
* @throws \Twig_Error_Runtime
* @throws \Twig_Error_Syntax
*/ */
public function doCreate($name, $sex, $vocation, $town, $account, &$errors) public function doCreate($name, $sex, $vocation, $town, $account, &$errors)
{ {

View File

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

View File

@ -8,6 +8,13 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
* @property int $level * @property int $level
* @property int $vocation * @property int $vocation
* @property int $online * @property int $online
* @property int $looktype
* @property int $lookhead
* @property int $lookbody
* @property int $looklegs
* @property int $lookfeet
* @property int $lookaddons
* @property string $outfit_url
* @property hasOne $onlineTable * @property hasOne $onlineTable
*/ */
class Player extends Model { class Player extends Model {
@ -42,8 +49,8 @@ class Player extends Model {
public function getVocationNameAttribute() public function getVocationNameAttribute()
{ {
$vocation = $this->vocation; $vocation = $this->vocation;
if (isset($this->promotion)) { if (isset($this->promotion) && $this->promotion > 0) {
$vocation *= $this->promotion; $vocation += ($this->promotion * setting('core.vocations_amount'));
} }
return config('vocations')[$vocation] ?? 'Unknown'; return config('vocations')[$vocation] ?? 'Unknown';
@ -73,12 +80,22 @@ class Player extends Model {
$query->where($column, 0); $query->where($column, 0);
} }
public function scopeWithOnlineStatus($query) { public function scopeWithOnlineStatus($query)
{
global $db; global $db;
if ($db->hasColumn('players', 'online')) {
$query->addSelect('online');
}
else {
$query->when($db->hasTable('players_online'), function ($query) { $query->when($db->hasTable('players_online'), function ($query) {
$query->with('onlineTable'); $query->with('onlineTable');
}); });
} }
}
public function getOutfitUrlAttribute() {
return setting('core.outfit_images_url') . '?id=' . $this->looktype . (!empty($this->lookaddons) ? '&addons=' . $this->lookaddons : '') . '&head=' . $this->lookhead . '&body=' . $this->lookbody . '&legs=' . $this->looklegs . '&feet=' . $this->lookfeet;
}
public function getOnlineStatusAttribute() public function getOnlineStatusAttribute()
{ {

View File

@ -72,6 +72,11 @@ class Settings implements \ArrayAccess
} }
} }
global $db;
try {
$db->beginTransaction();
$this->errors = []; $this->errors = [];
ModelsSettings::where('name', $pluginName)->delete(); ModelsSettings::where('name', $pluginName)->delete();
foreach ($values as $key => $value) { foreach ($values as $key => $value) {
@ -81,15 +86,18 @@ class Settings implements \ArrayAccess
continue; continue;
} }
try {
ModelsSettings::create([ ModelsSettings::create([
'name' => $pluginName, 'name' => $pluginName,
'key' => $key, 'key' => $key,
'value' => $value 'value' => $value
]); ]);
} catch (\PDOException $error) {
$this->errors[] = 'Error while saving setting (' . $pluginName . ' - ' . $key . '): ' . $error->getMessage();
} }
$db->commit();
} catch (\Exception $error) {
$db->rollBack();
$this->errors[] = 'Error while saving settings (' . $pluginName . ')<br/>' . $error->getMessage();
return false;
} }
$this->clearCache(); $this->clearCache();
@ -247,7 +255,15 @@ class Settings implements \ArrayAccess
$min = $max = $step = ''; $min = $max = $step = '';
} }
if ($setting['type'] === 'password') {
echo '<div class="input-group" id="show-hide-' . $key . '">';
}
echo '<input class="form-control" type="' . $setting['type'] . '" name="settings[' . $key . ']" value="' . ($settingsDb[$key] ?? ($setting['default'] ?? '')) . '" id="' . $key . '"' . $min . $max . $step . '/>'; echo '<input class="form-control" type="' . $setting['type'] . '" name="settings[' . $key . ']" value="' . ($settingsDb[$key] ?? ($setting['default'] ?? '')) . '" id="' . $key . '"' . $min . $max . $step . '/>';
if ($setting['type'] === 'password') {
echo '<div class="input-group-append input-group-text"><a href=""><i class="fas fa-eye-slash" ></i></a></div></div>';
}
} }
else if($setting['type'] === 'textarea') { else if($setting['type'] === 'textarea') {

View File

@ -5,6 +5,7 @@ const SKILL_BALANCE = -2;
$i = 0; $i = 0;
define('HOOK_INIT', ++$i);
define('HOOK_STARTUP', ++$i); define('HOOK_STARTUP', ++$i);
define('HOOK_BEFORE_PAGE', ++$i); define('HOOK_BEFORE_PAGE', ++$i);
define('HOOK_AFTER_PAGE', ++$i); define('HOOK_AFTER_PAGE', ++$i);
@ -101,7 +102,7 @@ define('HOOK_FILTER_TWIG_DISPLAY', ++$i);
define('HOOK_FILTER_TWIG_RENDER', ++$i); define('HOOK_FILTER_TWIG_RENDER', ++$i);
define('HOOK_FILTER_THEME_FOOTER', ++$i); define('HOOK_FILTER_THEME_FOOTER', ++$i);
const HOOK_FIRST = HOOK_STARTUP; const HOOK_FIRST = HOOK_INIT;
define('HOOK_LAST', $i); define('HOOK_LAST', $i);
function is_sub_dir($path = NULL, $parent_folder = BASE): bool|string function is_sub_dir($path = NULL, $parent_folder = BASE): bool|string

View File

@ -41,7 +41,7 @@ if(setting('core.template_allow_change'))
} }
else { else {
$template_session = getSession('template'); $template_session = getSession('template');
if ($template_session !== false) { if ($template_session) {
if (!preg_match("/[^A-z0-9_\-]/", $template_session)) { if (!preg_match("/[^A-z0-9_\-]/", $template_session)) {
$template_name = $template_session; $template_name = $template_session;
} }
@ -95,7 +95,7 @@ else {
unset($file); unset($file);
if ($cache->enabled()) { if ($cache->enabled()) {
$cache->set('template_ini_' . $template_name, serialize($template_ini)); $cache->set('template_ini_' . $template_name, serialize($template_ini), 10 * 60);
} }
} }
} }
@ -133,6 +133,7 @@ if($forumSetting != '')
$template['link_forum'] = "<a href='" . $forumSetting . "' target='_blank'>"; $template['link_forum'] = "<a href='" . $forumSetting . "' target='_blank'>";
} }
$twig->addGlobal('template_name', $template_name);
$twig->addGlobal('template_path', $template_path); $twig->addGlobal('template_path', $template_path);
if($twig_loader) { if($twig_loader) {
$twig_loader->prependPath(BASE . $template_path); $twig_loader->prependPath(BASE . $template_path);
@ -152,17 +153,40 @@ function get_template_menus(): array
return $result->toArray(); return $result->toArray();
}); });
$menus = array(); $configMenuCategories = config('menu_categories');
$configMenuDefaultColor = config('menu_default_links_color') ?? config('menu_default_color');
$menus = [];
foreach($result as $menu) { foreach($result as $menu) {
$link_full = strpos(trim($menu['link']), 'http') === 0 ? $menu['link'] : getLink($menu['link']); if (empty($menu['link'])) {
$menus[$menu['category']][] = array('name' => $menu['name'], 'link' => $menu['link'], 'link_full' => $link_full, 'blank' => $menu['blank'] == 1, 'target_blank' => ($menu['blank'] == 1 ? ' target="blank"' : ''), 'color' => $menu['color']); $menu['link'] = 'news';
} }
$new_menus = array(); $link_full = (str_starts_with(trim($menu['link']), 'http') ? $menu['link'] : getLink($menu['link']));
$target_blank = ($menu['blank'] == 1 ? ' target="blank"' : '');
$color = (empty($menu['color']) ? ($configMenuCategories[$menu['category']]['default_links_color'] ?? ($configMenuDefaultColor ?? '')) : $menu['color']);
$color = str_replace('#', '', $color);
if (in_array('#' . $color, [$configMenuCategories[$menu['category']]['default_links_color'] ?? '', $configMenuDefaultColor])) {
$color = '';
}
$style_color = (empty($color) ? '' : 'style="color: #' . $color . ' !important"');
$menus[$menu['category']][] = [
'name' => $menu['name'],
'link' => $menu['link'], 'link_full' => $link_full,
'blank' => $menu['blank'] == 1, 'target_blank' => $target_blank,
'color' => $color, 'style_color' => $style_color,
];
}
$new_menus = [];
/** /**
* @var array $configMenuCategories * @var array $configMenuCategories
*/ */
$configMenuCategories = config('menu_categories');
if($configMenuCategories === null) { if($configMenuCategories === null) {
return []; return [];
} }

View File

@ -24,21 +24,28 @@ Please enter your account {{ account|lower }} and your password.<br/><a href="{{
<td> <td>
<div class="InnerTableContainer"> <div class="InnerTableContainer">
<table style="width:100%;" > <table style="width:100%;" >
{{ hook('HOOK_ACCOUNT_LOGIN_BEFORE_PASSWORD') }}
{{ hook('HOOK_ACCOUNT_LOGIN_BEFORE_ACCOUNT') }}
<tr> <tr>
<td class="LabelV" > <td class="LabelV" >
<span{% if error is not null %} class="red"{% endif %}>{{ account_login_by }}:</span> <span{% if error is not null %} class="red"{% endif %}>{{ account_login_by }}:</span>
</td> </td>
<td style="width:100%;" ><input type="text" name="account_login" size="30" maxlength="{{ setting('core.account_login_by_email') ? '255' : '30' }}" autofocus/></td> <td style="width:100%;" ><input type="text" name="account_login" size="30" maxlength="{{ setting('core.account_login_by_email') ? '255' : '30' }}" autofocus/></td>
</tr> </tr>
{{ hook('HOOK_ACCOUNT_LOGIN_AFTER_ACCOUNT') }} {{ hook('HOOK_ACCOUNT_LOGIN_AFTER_ACCOUNT') }}
{{ hook('HOOK_ACCOUNT_LOGIN_BEFORE_PASSWORD') }}
<tr> <tr>
<td class="LabelV" > <td class="LabelV" >
<span{% if error is not null %} class="red"{% endif %}>Password:</span> <span{% if error is not null %} class="red"{% endif %}>Password:</span>
</td> </td>
<td><input type="password" name="password_login" size="30" maxlength="29" ></td> <td><input type="password" name="password_login" size="30" maxlength="29" ></td>
</tr> </tr>
{{ hook('HOOK_ACCOUNT_LOGIN_AFTER_PASSWORD') }} {{ hook('HOOK_ACCOUNT_LOGIN_AFTER_PASSWORD') }}
<tr> <tr>
<td class="LabelV" ></td> <td class="LabelV" ></td>
<td><input type="checkbox" id="remember_me" name="remember_me" value="true" /> <td><input type="checkbox" id="remember_me" name="remember_me" value="true" />

View File

@ -110,7 +110,7 @@ html { margin-top: 32px !important; }
<form method="post" action="{{ constant('ADMIN_URL') }}?p=dashboard"> <form method="post" action="{{ constant('ADMIN_URL') }}?p=dashboard">
{{ csrf() }} {{ csrf() }}
<input type="hidden" name="clear_cache" value="1" /> <input type="hidden" name="clear_cache" value="1" />
<a class="ab-item" href="#" onclick="confirm('Are you sure that you want to clear cache?') && $(this).closest('form').submit()" title="Clear Cache">Clear Cache</a> <a class="ab-item" href="javascript:void(0);" onclick="confirm('Are you sure that you want to clear cache?') && $(this).closest('form').submit()" title="Clear Cache">Clear Cache</a>
</form> </form>
</li> </li>
</ul> </ul>

View File

@ -0,0 +1,31 @@
<div align="center" class="text-center">
<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.
</p>
<div class="row text-center">
<div class="col-md-2 col-sm-1"></div>
<div class="col-md-8 col-sm-10">
<div class="row justify-content-center">
{% if config('menus') is not null %}
<form method="post" action="?p=menus&reset_menus" onsubmit="return confirm('Do you really want to reset menus?');">
{{ csrf() }}
<input type="hidden" name="template" value="{{ template }}"/>
<button type="submit" class="btn btn-danger">Reset Menus to default</button>
</form>
<br/>
{% endif %}
{% if canResetColors %}
<form method="post" action="?p=menus&reset_colors" onsubmit="return confirm('Do you really want to reset colors?');">
{{ csrf() }}
<input type="hidden" name="template" value="{{ template }}"/>
<button type="submit" class="btn btn-warning" style="margin-left: 20px">Reset Colors to default</button>
</form>
<br/>
{% endif %}
</div>
</div>
<div class="col-md-2 col-sm-1"></div>
</div>
</div>

View File

@ -1,31 +1,41 @@
<link type="text/css" rel="stylesheet" href="{{ constant('BASE_URL') }}tools/ext/jquery-ui/themes/base/jquery-ui.min.css"> <link type="text/css" rel="stylesheet" href="{{ constant('BASE_URL') }}tools/ext/jquery-ui/themes/base/jquery-ui.min.css">
<script> <script>
var last_id = []; let last_id = [];
let colors = [];
{% for cat, menu in menus %} {% for cat, menu in menus %}
last_id[{{ cat }}] = {{ last_id[cat] }}; last_id[{{ cat }}] = {{ last_id[cat] }};
{% endfor %} {% endfor %}
$(function () {
$(".sortable").sortable();
$(".sortable").disableSelection();
$(".remove-button").click(function () { {% set menuDefaultLinksColor = config('menu_default_links_color') %}
{% for cat, options in config('menu_categories') %}
colors[{{ cat }}] = '{{ options['default_links_color'] ?? (menuDefaultLinksColor ?? config('menu_default_color')) }}';
{% endfor %}
$(function () {
const $sortable = $(".sortable");
$sortable.sortable();
$sortable.disableSelection();
$(".remove-button").on('click', function () {
var id = $(this).attr("id"); var id = $(this).attr("id");
$('#list-' + id.replace('remove-button-', '')).remove(); $('#list-' + id.replace('remove-button-', '')).remove();
}); });
$(".add-button").click(function () { $(".add-button").on('click', function () {
var cat = $(this).attr("id").replace('add-button-', ''); var cat = $(this).attr("id").replace('add-button-', '');
var id = last_id[cat]; var id = last_id[cat];
last_id[cat]++; last_id[cat]++;
$('#sortable-' + cat).append('<li class="ui-state-default" id="list-' + cat + '-' + id + '"><label>Name:</label> <input type="text" name="menu[' + cat + '][]" value=""/> <label>Link:</label> <input type="text" name="menu_link[' + cat + '][]" value=""/><input type="hidden" name="menu_blank[' + cat + '][]" value="0" /> <label><input class="blank-checkbox" type="checkbox"/><span title="Open in New Window">New Window</span></label> <input class="color-picker" type="text" name="menu_color[' + cat + '][]" value="{{ menu_default_color }}" /><a class="remove-button" id="remove-button-' + cat + '-' + id + '"><i class="fas fa-trash"></i></a></li>'); //add input bo const color = colors[cat];
$('#remove-button-' + cat + '-' + id).click(function () { $('#sortable-' + cat).append('<li class="ui-state-default" id="list-' + cat + '-' + id + '"><label>Name:</label> <input type="text" name="menu[' + cat + '][]" value=""/> <label>Link:</label> <input type="text" name="menu_link[' + cat + '][]" value=""/><input type="hidden" name="menu_blank[' + cat + '][]" value="0" /> <label><input class="blank-checkbox" type="checkbox"/><span title="Open in New Window">New Window</span></label> <input class="color-picker" type="text" name="menu_color[' + cat + '][]" value="#' + color + '" /> <a class="remove-button" id="remove-button-' + cat + '-' + id + '"><i class="fas fa-trash"></i></a></li>'); //add input bo
$('#remove-button-' + cat + '-' + id).on('click', function () {
$('#list-' + $(this).attr("id").replace('remove-button-', '')).remove(); $('#list-' + $(this).attr("id").replace('remove-button-', '')).remove();
}); });
initialiceSpectrum(); initializeSpectrum();
}); });
$("#menus-form").submit(function (e) { $("#menus-form").on('submit', function (e) {
$('.blank-checkbox:not(:checked)').each(function (i, obj) { $('.blank-checkbox:not(:checked)').each(function (i, obj) {
$(obj).parent().prev().val("off"); $(obj).parent().prev().val("off");
}); });
@ -52,11 +62,11 @@
<script type="text/javascript" src="{{ constant('BASE_URL') }}tools/js/spectrum.js"></script> <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"/> <link type="text/css" rel="stylesheet" href="{{ constant('BASE_URL') }}tools/css/spectrum.css"/>
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function () { $(function () {
initialiceSpectrum(); initializeSpectrum();
}); });
function initialiceSpectrum() { function initializeSpectrum() {
$(".color-picker").spectrum({ $(".color-picker").spectrum({
preferredFormat: "hex", preferredFormat: "hex",
showInput: true, showInput: true,

View File

@ -80,7 +80,26 @@
} }
}); });
$('#settings').submit(function(e) { const noChangesText = "No changes has been made";
$('form')
.each(function(){
$(this).data('serialized', $(this).serialize())
})
.on('change input', function(){
const disable = $(this).serialize() === $(this).data('serialized');
$(this)
.find('input:submit, button:submit')
.prop('disabled', disable)
.prop('title', disable ? noChangesText : '')
;
})
.find('input:submit, button:submit')
.prop('disabled', true)
.prop('title', noChangesText)
;
$('#settings').on('submit', function(e) {
e.preventDefault(); e.preventDefault();
$.ajax({ $.ajax({
@ -94,6 +113,13 @@
duration: 3000, duration: 3000,
escapeMarkup: false, escapeMarkup: false,
}).showToast(); }).showToast();
let $settings = $('#settings');
$settings.data('serialized', $settings.serialize());
$settings
.find('input:submit, button:submit')
.prop('disabled', true)
.prop('title', noChangesText);
}, },
error : function(response) { error : function(response) {
Toastify({ Toastify({
@ -109,3 +135,27 @@
}); });
}); });
</script> </script>
<script>
{% for key, value in settings %}
{% if value.type == 'password' %}
$(function () {
$('#show-hide-{{ key}} a').on('click', function(event) {
event.preventDefault();
const $showHideIcon = $('#show-hide-{{ key}} i');
const $showHideInput = $('#show-hide-{{ key }} input');
if($showHideInput.attr('type') === 'text'){
$showHideInput.attr('type', 'password');
$showHideIcon.addClass('fa-eye-slash');
$showHideIcon.removeClass('fa-eye');
}else if($showHideInput.attr("type") === 'password'){
$showHideInput.attr('type', 'text');
$showHideIcon.removeClass('fa-eye-slash');
$showHideIcon.addClass('fa-eye');
}
});
});
{% endif %}
{% endfor %}
</script>

View File

@ -79,7 +79,8 @@
</tr> </tr>
{% set showedRank, i = false, 0 %} {% set showedRank, i = false, 0 %}
{% for rank in guild_members if rank.members|length > 0 %} {% for rank in guild_members %}
{% if rank.members|length > 0 %}
{% set rankStyle, i = getStyle(i), i + 1 %} {% set rankStyle, i = getStyle(i), i + 1 %}
{% for player in rank.members %} {% for player in rank.members %}
@ -130,6 +131,7 @@
{% endfor %} {% endfor %}
{% set showedRank = false %} {% set showedRank = false %}
{% endif %}
{% else %} {% else %}
<tr bgcolor="{{ getStyle(i) }}"> <tr bgcolor="{{ getStyle(i) }}">
<td colspan="5">No guild members found.</td> <td colspan="5">No guild members found.</td>
@ -162,7 +164,7 @@
</tr> </tr>
{% set i = 0 %} {% set i = 0 %}
{% for invited_player in invited_list if invited_list|length > 0 %} {% for invited_player in invited_list %}
{% if invited_player.isLoaded() %} {% if invited_player.isLoaded() %}
<tr bgcolor="{{ getStyle(i) }}"> <tr bgcolor="{{ getStyle(i) }}">
{% set i = i + 1 %} {% set i = i + 1 %}

View File

@ -9,19 +9,17 @@
<td> <td>
<label for="skillFilter">Choose a Skill</label> <label for="skillFilter">Choose a Skill</label>
<select onchange="location = this.value;" id="skillFilter"> <select onchange="location = this.value;" id="skillFilter">
{% set i = 0 %}
{% for link, name in types %} {% for link, name in types %}
<option value="{{ getLink('highscores') }}/{{ link }}{% if vocation is not null %}/{{ vocation }}{% endif %}" class="size_xs" {% if list is not null and list == link %}selected{% endif %}>{{ name }}</option> <option value="{{ getLink('highscores') }}/{{ link|urlencode }}{% if vocation is not null %}/{{ vocation|lower|urlencode }}{% endif %}" class="size_xs" {% if list is not null and list == link %}selected{% endif %}>{{ name }}</option>
{% endfor %} {% endfor %}
</select> </select>
</td> </td>
<td> <td>
<label for="vocationFilter">Choose a vocation</label> <label for="vocationFilter">Choose a vocation</label>
<select onchange="location = this.value;" id="vocationFilter"> <select onchange="location = this.value;" id="vocationFilter">
<option value="{{ getLink('highscores') }}/{{ list }}" class="size_xs">[ALL]</option> <option value="{{ getLink('highscores') }}/{{ list|urlencode }}" class="size_xs">[ALL]</option>
{% set i = 0 %}
{% for i in 0..config.vocations_amount %} {% for i in 0..config.vocations_amount %}
<option value="{{ getLink('highscores') }}/{{ list }}/{{ config.vocations[i]|lower }}" class="size_xs" {% if vocationId is not null and vocationId == i %}selected{% endif %}>{{ config.vocations[i]}}</option> <option value="{{ getLink('highscores') }}/{{ list|urlencode }}/{{ config.vocations[i]|lower|urlencode }}" class="size_xs" {% if vocationId is not null and vocationId == i %}selected{% endif %}>{{ config.vocations[i]}}</option>
{% endfor %} {% endfor %}
</select> </select>
</td> </td>
@ -105,7 +103,7 @@
<tr bgcolor="{{ config.lightborder }}"> <tr bgcolor="{{ config.lightborder }}">
<td> <td>
{% for link, name in types %} {% for link, name in types %}
<a href="{{ getLink('highscores') }}/{{ link }}{% if vocation is not null %}/{{ vocation }}{% endif %}" class="size_xs">{{ name }}</a><br/> <a href="{{ getLink('highscores') }}/{{ link|urlencode }}{% if vocation is not null %}/{{ vocation|urlencode }}{% endif %}" class="size_xs">{{ name }}</a><br/>
{% endfor %} {% endfor %}
</td> </td>
</tr> </tr>
@ -118,9 +116,9 @@
</tr> </tr>
<tr bgcolor="{{ config.lightborder }}"> <tr bgcolor="{{ config.lightborder }}">
<td> <td>
<a href="{{ getLink('highscores') }}/{{ list }}" class="size_xs">[ALL]</a><br/> <a href="{{ getLink('highscores') }}/{{ list|urlencode }}" class="size_xs">[ALL]</a><br/>
{% for i in 0..config.vocations_amount %} {% for i in 0..config.vocations_amount %}
<a href="{{ getLink('highscores') }}/{{ list }}/{{ config.vocations[i]|lower }}" class="size_xs">{{ config.vocations[i]}}</a><br/> <a href="{{ getLink('highscores') }}/{{ list|urlencode }}/{{ config.vocations[i]|lower|urlencode }}" class="size_xs">{{ config.vocations[i]}}</a><br/>
{% endfor %} {% endfor %}
</td> </td>
</tr> </tr>

View File

@ -56,10 +56,9 @@ the search criteria and start a new search.
</td> </td>
<td> <td>
<form action="{{ getLink('houses/view') }}" method="post"> <a href="{{ house.link }}">
<input type="hidden" name="house" value="{{ house.name }}">
{{ include('buttons.view.html.twig') }} {{ include('buttons.view.html.twig') }}
</form> </a>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -85,7 +84,8 @@ the search criteria and start a new search.
<tr bgcolor="{{ config.darkborder }}"> <tr bgcolor="{{ config.darkborder }}">
<td valign="top" rowspan="2"> <td valign="top" rowspan="2">
{% set checked = false %} {% set checked = false %}
{% for id, name in config.towns if id > 0 %} {% for id, name in config.towns %}
{% if id > 0 %}
{% if ((townId is empty and name is not empty) or id == townId) and not checked %} {% if ((townId is empty and name is not empty) or id == townId) and not checked %}
{% set variable = "checked" %} {% set variable = "checked" %}
{% set checked = true %} {% set checked = true %}
@ -96,6 +96,7 @@ the search criteria and start a new search.
<input type="radio" name="town" id="town_{{ id }}" value="{{ id }}"{% if variable is defined %} {{ variable }}{% endif %}> <input type="radio" name="town" id="town_{{ id }}" value="{{ id }}"{% if variable is defined %} {{ variable }}{% endif %}>
<label for="town_{{ id }}"> {{ name }}</label> <label for="town_{{ id }}"> {{ name }}</label>
<br> <br>
{% endif %}
{% endfor %} {% endfor %}
</td> </td>

View File

@ -139,6 +139,11 @@ $function = new TwigFunction('csrfToken', function () {
}); });
$twig->addFunction($function); $twig->addFunction($function);
$function = new TwigFunction('session', function ($key) {
return session($key);
});
$twig->addFunction($function);
$filter = new TwigFilter('urlencode', function ($s) { $filter = new TwigFilter('urlencode', function ($s) {
return urlencode($s); return urlencode($s);
}); });

View File

@ -1,5 +1,5 @@
<?php <?php
$config['menu_default_color'] = '#ffffff'; $config['menu_default_links_color'] = '#ffffff';
$config['menu_categories'] = array( $config['menu_categories'] = array(
MENU_CATEGORY_NEWS => array('id' => 'news', 'name' => 'Latest News'), MENU_CATEGORY_NEWS => array('id' => 'news', 'name' => 'Latest News'),
@ -8,3 +8,5 @@ $config['menu_categories'] = array(
MENU_CATEGORY_LIBRARY => array('id' => 'library', 'name' => 'Library'), MENU_CATEGORY_LIBRARY => array('id' => 'library', 'name' => 'Library'),
MENU_CATEGORY_SHOP => array('id' => 'shops', 'name' => 'Shop') MENU_CATEGORY_SHOP => array('id' => 'shops', 'name' => 'Shop')
); );
$config['menus'] = require __DIR__ . '/menus.php';

View File

@ -26,7 +26,7 @@ return [
'Team' => 'team', 'Team' => 'team',
], ],
MENU_CATEGORY_LIBRARY => [ MENU_CATEGORY_LIBRARY => [
'Monsters' => 'creatures', 'Monsters' => 'monsters',
'Spells' => 'spells', 'Spells' => 'spells',
'Server Info' => 'server-info', 'Server Info' => 'server-info',
'Commands' => 'commands', 'Commands' => 'commands',

View File

@ -42,8 +42,6 @@ defined('MYAAC') or die('Direct access not allowed!');
<div id="mainsubmenu"> <div id="mainsubmenu">
<?php <?php
$default_menu_color = "ffffff";
foreach($menus as $category => $menu) { foreach($menus as $category => $menu) {
if(!isset($menus[$category])) { if(!isset($menus[$category])) {
continue; continue;
@ -54,8 +52,8 @@ defined('MYAAC') or die('Direct access not allowed!');
$size = count($menus[$category]); $size = count($menus[$category]);
$i = 0; $i = 0;
foreach($menus[$category] as $menu) { foreach($menus[$category] as $link) {
echo '<a href="' . $menu['link_full'] . '"' . ($menu['blank'] ? ' target="_blank"' : '') . ' style="color: #' . (strlen($menu['color']) == 0 ? $default_menu_color : $menu['color']) . ';">' . $menu['name'] . '</a>'; echo '<a href="' . $link['link_full'] . '" ' . $link['target_blank'] . ' ' . $link['style_color'] . '>' . $link['name'] . '</a>';
if(++$i != $size) { if(++$i != $size) {
echo '<span class="separator"></span>'; echo '<span class="separator"></span>';

View File

@ -1,5 +1,5 @@
<?php <?php
$config['menu_default_color'] = '#ffffff'; $config['menu_default_links_color'] = '#ffffff';
$config['menu_categories'] = array( $config['menu_categories'] = array(
MENU_CATEGORY_NEWS => array('id' => 'news', 'name' => 'Latest News'), MENU_CATEGORY_NEWS => array('id' => 'news', 'name' => 'Latest News'),
@ -9,3 +9,5 @@ $config['menu_categories'] = array(
MENU_CATEGORY_LIBRARY => array('id' => 'library', 'name' => 'Library'), MENU_CATEGORY_LIBRARY => array('id' => 'library', 'name' => 'Library'),
MENU_CATEGORY_SHOP => array('id' => 'shops', 'name' => 'Shop') MENU_CATEGORY_SHOP => array('id' => 'shops', 'name' => 'Shop')
); );
$config['menus'] = require __DIR__ . '/menus.php';

View File

@ -7,7 +7,7 @@ if(strlen($text) > 100) // max limit
putenv('GDFONTPATH=' . __DIR__); putenv('GDFONTPATH=' . __DIR__);
// create image // create image
$image = imagecreatetruecolor(250, 28); $image = imagecreatetruecolor(600, 28);
// make the background transparent // make the background transparent
imagecolortransparent($image, imagecolorallocate($image, 0, 0, 0)); imagecolortransparent($image, imagecolorallocate($image, 0, 0, 0));

View File

@ -35,6 +35,17 @@ if(isset($config['boxes']))
} }
else { else {
$tmp = str_replace('/', '_', PAGE); $tmp = str_replace('/', '_', PAGE);
$exp = explode('/', PAGE);
if(PAGE !== 'account/create' && PAGE !== 'account/lost' && isset($exp[1])) {
if ($exp[0] === 'account') {
$tmp = 'account_manage';
} else if ($exp[0] === 'news' && $exp[1] === 'archive') {
$tmp = 'news_archive';
}
else if (in_array($exp[0], ['characters', 'highscores', 'guilds', 'forum'])) {
$tmp = $exp[0];
}
}
} }
} }
else { else {
@ -107,6 +118,15 @@ if(isset($config['boxes']))
menu[0] = {}; menu[0] = {};
var unloadhelper = false; var unloadhelper = false;
<?php
$menuInitStr = '';
foreach ($config['menu_categories'] as $item) {
if ($item['id'] !== 'shops' || setting('core.gifts_system')) {
$menuInitStr .= $item['id'] . '=' . ($item['id'] === 'news' ? '1' : '0') . '&';
}
}
?>
// load the menu and set the active submenu item by using the variable 'activeSubmenuItem' // load the menu and set the active submenu item by using the variable 'activeSubmenuItem'
function LoadMenu() function LoadMenu()
{ {
@ -114,7 +134,7 @@ if(isset($config['boxes']))
document.getElementById("ActiveSubmenuItemIcon_"+activeSubmenuItem).style.visibility = "visible"; document.getElementById("ActiveSubmenuItemIcon_"+activeSubmenuItem).style.visibility = "visible";
menus = localStorage.getItem('menus'); menus = localStorage.getItem('menus');
if(menus == null || menus.lastIndexOf("&") === -1) { if(menus == null || menus.lastIndexOf("&") === -1) {
menus = "news=1&account=0&community=0&library=0&forum=0<?php if (setting('core.gifts_system')) echo '&shops=0'; ?>&"; menus = "<?= $menuInitStr ?>";
} }
FillMenuArray(); FillMenuArray();
InitializeMenu(); InitializeMenu();
@ -318,10 +338,22 @@ if(isset($config['boxes']))
<?php <?php
$menus = get_template_menus(); $menus = get_template_menus();
$countElements = 0;
foreach($config['menu_categories'] as $id => $cat) {
if (!isset($menus[$id]) || ($id == MENU_CATEGORY_SHOP && !setting('core.gifts_system'))) {
continue;
}
$countElements++;
}
$i = 0;
foreach($config['menu_categories'] as $id => $cat) { foreach($config['menu_categories'] as $id => $cat) {
if(!isset($menus[$id]) || ($id == MENU_CATEGORY_SHOP && !setting('core.gifts_system'))) { if(!isset($menus[$id]) || ($id == MENU_CATEGORY_SHOP && !setting('core.gifts_system'))) {
continue; continue;
} }
$i++;
?> ?>
<div id='<?php echo $cat['id']; ?>' class='menuitem'> <div id='<?php echo $cat['id']; ?>' class='menuitem'>
<span onClick="MenuItemAction('<?php echo $cat['id']; ?>')"> <span onClick="MenuItemAction('<?php echo $cat['id']; ?>')">
@ -340,19 +372,13 @@ foreach($config['menu_categories'] as $id => $cat) {
</span> </span>
<div id='<?php echo $cat['id']; ?>_Submenu' class='Submenu'> <div id='<?php echo $cat['id']; ?>_Submenu' class='Submenu'>
<?php <?php
$default_menu_color = "ffffff";
foreach($menus[$id] as $category => $menu) { foreach($menus[$id] as $category => $menu) {
if (empty($menu['link'])) {
$menu['link'] = 'news';
}
$link_color = '#' . (strlen($menu['color']) == 0 ? $default_menu_color : $menu['color']);
?> ?>
<a href='<?php echo $menu['link_full']; ?>'<?php echo $menu['blank'] ? ' target="_blank"' : ''?>> <a href='<?php echo $menu['link_full']; ?>'<?= $menu['target_blank']?>>
<div id='submenu_<?php echo str_replace('/', '_', $menu['link']); ?>' class='Submenuitem' onMouseOver='MouseOverSubmenuItem(this)' onMouseOut='MouseOutSubmenuItem(this)' style="color: <?php echo $link_color; ?>;"> <div id='submenu_<?php echo str_replace('/', '_', $menu['link']); ?>' class='Submenuitem' onMouseOver='MouseOverSubmenuItem(this)' onMouseOut='MouseOutSubmenuItem(this)' >
<div class='LeftChain' style='background-image:url(<?php echo $template_path; ?>/images/general/chain.gif);'></div> <div class='LeftChain' style='background-image:url(<?php echo $template_path; ?>/images/general/chain.gif);'></div>
<div id='ActiveSubmenuItemIcon_<?php echo str_replace('/', '_', $menu['link']); ?>' class='ActiveSubmenuItemIcon' style='background-image:url(<?php echo $template_path; ?>/images/menu/icon-activesubmenu.gif);'></div> <div id='ActiveSubmenuItemIcon_<?php echo str_replace('/', '_', $menu['link']); ?>' class='ActiveSubmenuItemIcon' style='background-image:url(<?php echo $template_path; ?>/images/menu/icon-activesubmenu.gif);'></div>
<div class='SubmenuitemLabel' style="color: <?php echo $link_color; ?>;"><?php echo $menu['name']; ?></div> <div class='SubmenuitemLabel' <?php echo $menu['style_color']; ?>><?php echo $menu['name']; ?></div>
<div class='RightChain' style='background-image:url(<?php echo $template_path; ?>/images/general/chain.gif);'></div> <div class='RightChain' style='background-image:url(<?php echo $template_path; ?>/images/general/chain.gif);'></div>
</div> </div>
</a> </a>
@ -361,7 +387,7 @@ foreach($config['menu_categories'] as $id => $cat) {
?> ?>
</div> </div>
<?php <?php
if($id == MENU_CATEGORY_SHOP || (!setting('core.gifts_system') && $id == MENU_CATEGORY_SHOP - 1)) { if($id == MENU_CATEGORY_SHOP || (!setting('core.gifts_system') && $i == $countElements)) {
?> ?>
<div id='MenuBottom' style='background-image:url(<?php echo $template_path; ?>/images/general/box-bottom.gif);'></div> <div id='MenuBottom' style='background-image:url(<?php echo $template_path; ?>/images/general/box-bottom.gif);'></div>
<?php <?php

View File

@ -28,7 +28,7 @@ return [
'Forum' => 'forum', 'Forum' => 'forum',
], ],
MENU_CATEGORY_LIBRARY => [ MENU_CATEGORY_LIBRARY => [
'Monsters' => 'creatures', 'Monsters' => 'monsters',
'Spells' => 'spells', 'Spells' => 'spells',
'Commands' => 'commands', 'Commands' => 'commands',
'Exp Stages' => 'exp-stages', 'Exp Stages' => 'exp-stages',