Merge branch 'develop' into feature/ots-player-rewrite

This commit is contained in:
slawkens
2026-01-04 13:22:17 +01:00
77 changed files with 853 additions and 3226 deletions

View File

@@ -22,7 +22,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: [ '8.1', '8.2', '8.3', '8.4' ]
php-versions: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
ots: ['tfs-1.4', 'canary-3.1.2'] # TODO: add 'tfs-master' (actually doesn't work cause AAC doesn't support reading .env configuration)
name: Cypress (PHP ${{ matrix.php-versions }}, ${{ matrix.ots }})
steps:

View File

@@ -1,5 +1,46 @@
# Changelog
## [1.8.7 - 04.01.2026]
### Fixed
* Fixed [player/guild/house] bb code in forum (https://github.com/slawkens/myaac/commit/8ec9bf10682c73f1fe40967a106ccda2a5073ed0)
### Changed
* Settings: better responsiveness on mobile (https://github.com/slawkens/myaac/commit/c65d4e4b62ef26fb4e24ecb1d2bcc4556d746adf)
* Signatures: Return 404 when the signature player is not found (https://github.com/slawkens/myaac/commit/7e6480b380799add7a2b1b7ce1d3c1f2b6819ff1)
### Removed
* Remove setting: outfit_images_wrong_looktypes - is obsolete, the bug doesn't exist in the latest outfit images (https://github.com/slawkens/myaac/commit/cc220bedc1f01535eaac23f6961135e2e7a6e310)
## [1.8.6 - 14.12.2025]
### Added
* Added hook for adding custom rules to validate new character name (https://github.com/slawkens/myaac/commit/8e6749c59984631288e8e9803819b2f0ff389761)
### Fixed
* Highscores: Fix ordering by different skills (Adjust order by desc: skill_tries, manaspent, experience) - More exact results (https://github.com/slawkens/myaac/commit/c86257e6dacbad773aa09c0958eeaa106a967f2d)
* Fix exception shown on first install, when there is no vendor - Before it displayed 500 white page, now it display the exception (https://github.com/slawkens/myaac/commit/18a1178e4b93607a350259679e0366cb83fb4126)
* Fix typo $up -> $down, in migration nr 7, was failing due that (https://github.com/slawkens/myaac/commit/fd74f01291d0e9cdb92ee1b95021c9d7b591ad7c)
### Changed
* Ini set html_errors = 0, to show html code in exceptions (https://github.com/slawkens/myaac/commit/9ed06782e67772826d927ad847a077b99df5060d)
## [1.8.5 - 21.11.2025]
### Added
* New Setting: Account Countries Most Popular (https://github.com/slawkens/myaac/commit/946364f59d7cd01472877108ab27ec78fb28307a)
### Changed
* Status: Write to status-error.log if there is connection error (https://github.com/slawkens/myaac/commit/780d4ccef741c1dd45a00bfc121fba9f1a175313)
* Settings: escapeHtml in values (support for html code) (https://github.com/slawkens/myaac/commit/5861efdbe900ccd35309913af0c0a5f3d4cdc1a8)
* News Page: Don't display hidden news for admin - it's confusing (https://github.com/slawkens/myaac/commit/175e97828b9a08ec3080cc8d3fb4eb3f1c08649f)
* Plugins System: Add plugin:remove + plugin:delete as alias for plugin:uninstall + plugin:activate/deactivate (https://github.com/slawkens/myaac/commit/6367054487368c92741bfd1dc7c70c52aea9ee87, https://github.com/slawkens/myaac/commit/baec6c9ebf5c342b3b2f7123427c6ba21dbb93bc)
### Fixed
* Status: Fix $status['uptimeReadable'], was totally wrong (https://github.com/slawkens/myaac/commit/0a6d44bf21417562491aabc93543a2bc3a44b2df)
* Guilds: Detect "deletion" column in guilds show/delete (https://github.com/slawkens/myaac/commit/6775a061bebc9ff449522f0173556d4a7a44fa5e, https://github.com/slawkens/myaac/commit/603d860b56bc7418db09e206f40aa06d0682c00e)
* General: Ensure some cache folders & index.html exists (https://github.com/slawkens/myaac/commit/730a0f29124811f525207c24c06eb0d088fa3434)
## [1.8.4 - 27.10.2025]
### Changed

View File

@@ -1,4 +1,12 @@
## [2.0-dev - x.x.2025]
### Added
* Add an "access" option to Menus (#340)
* Possibility to hide menus for unauthorized users
* Add the possibility to fetch skills in the getTopPlayers function
### Changed
* Reworked account action logs to use single IP column as varchar(45) for both ipv4 and ipv6 (https://github.com/slawkens/myaac/pull/289)
* Better handling of vocations: (#345)
* Load from vocations.xml (No need to manually set)
* Support for Monk vocation
* Reworked account action logs to use a single IP column as varchar(45) for both ipv4 and ipv6 (#289)

View File

@@ -23,6 +23,7 @@ if (!hasFlag(FLAG_CONTENT_MENUS) && !superAdmin()) {
}
$pluginThemes = Plugins::getThemes();
$groups = new OTS_Groups_List();
if (isset($_POST['template'])) {
$template = $_POST['template'];
@@ -32,6 +33,8 @@ if (isset($_POST['template'])) {
$post_menu_link = $_POST['menu_link'] ?? [];
$post_menu_blank = $_POST['menu_blank'] ?? [];
$post_menu_color = $_POST['menu_color'] ?? [];
$post_menu_access = $_POST['menu_access'] ?? [];
if (count($post_menu) != count($post_menu_link)) {
echo 'Menu count is not equal menu links. Something went wrong when sending form.';
return;
@@ -50,6 +53,7 @@ if (isset($_POST['template'])) {
'link' => $post_menu_link[$category][$i],
'blank' => $post_menu_blank[$category][$i] == 'on' ? 1 : 0,
'color' => str_replace('#', '', $post_menu_color[$category][$i]),
'access' => $post_menu_access[$category][$i],
'category' => $category,
'ordering' => $i
]);
@@ -122,7 +126,7 @@ if (isset($_POST['template'])) {
?>
<?php
$menus = Menu::query()
->select('name', 'link', 'blank', 'color', 'category', 'ordering')
->select('name', 'link', 'access', 'blank', 'color', 'category', 'ordering')
->where('enabled', 1)
->where('template', $template)
->orderBy('ordering')
@@ -151,11 +155,34 @@ if (isset($_POST['template'])) {
foreach ($menus[$id] as $menu):
$color = (empty($menu['color']) ? ($cat['default_links_color'] ?? ($config['menu_default_links_color'] ?? ($config['menu_default_color'] ?? '#ffffff'))) : '#' . $menu['color']);
?>
<li class="ui-state-default" id="list-<?php echo $id ?>-<?php echo $i ?>"><label>Name:</label> <input type="text" name="menu[<?php echo $id ?>][]" value="<?php echo escapeHtml($menu['name']); ?>"/>
<label>Link:</label> <input type="text" name="menu_link[<?php echo $id ?>][]" value="<?php echo $menu['link'] ?>"/>
<input type="hidden" name="menu_blank[<?php echo $id ?>][]" value="0"/>
<label><input class="blank-checkbox" type="checkbox" <?php echo($menu['blank'] == 1 ? 'checked' : '') ?>/><span title="Open in New Window">New Window</span></label>
<input class="color-picker" type="text" name="menu_color[<?php echo $id ?>][]" value="<?php echo $color; ?>"/>
<li class="ui-state-default" id="list-<?php echo $id ?>-<?php echo $i ?>">
<label class="label_menu_name">Name: <input type="text" name="menu[<?php echo $id ?>][]" class="form-control menu-name" value="<?php echo escapeHtml($menu['name']); ?>"/>
</label>
<label class="label_menu_link">Link: <input type="text" name="menu_link[<?= $id ?>][]" class="form-control menu-link" value="<?php echo $menu['link'] ?>"/>
</label>
<br/>
<div class="menu-options-row">
<label>Access:
<select name="menu_access[<?= $id ?>][]" class="form-control menu-access">
<option value="0" <?= ($menu['access'] == 0 ? 'selected' : ''); ?>>Guest*</option>
<?php foreach ($groups->getGroups() as $group): ?>
<option value="<?= $group->getId(); ?>" <?= ($menu['access'] == $group->getId() ? 'selected' : ''); ?>><?= ucfirst($group->getName()); ?></option>
<?php endforeach; ?>
</select>
</label>
<label>Color: <input class="menu-color" type="color" name="menu_color[<?php echo $id ?>][]" value="<?php echo $color; ?>"/>
</label>
<input type="hidden" name="menu_blank[<?php echo $id ?>][]" class="menu-blank" value="0"/>
<label><input type="checkbox" class="menu-blank-checkbox" <?php echo($menu['blank'] == 1 ? 'checked' : '') ?>/><span title="Open in New Window">New Window</span></label>
</div>
<a class="remove-button" id="remove-button-<?php echo $id ?>-<?php echo $i ?>"><i class="fas fa-trash"></a></i></li>
<?php $i++; $last_id[$id] = $i;
endforeach;

View File

@@ -19,14 +19,14 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
<?php $hooks->trigger(HOOK_ADMIN_HEAD_END); ?>
</head>
<body class="sidebar-mini ">
<body class="sidebar-mini <?= (session('admin.menu-collapse') ? 'sidebar-collapse' : ''); ?>">
<?php $hooks->trigger(HOOK_ADMIN_BODY_START); ?>
<?php if ($logged && admin()) { ?>
<div class="wrapper">
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" data-widget="pushmenu" href="#"><i class="fas fa-bars"></i></a>
<a class="nav-link sidebar-toggle" data-widget="pushmenu" href="#"><i class="fas fa-bars"></i></a>
</li>
<li class="nav-item d-none d-sm-inline-block">
<a href="<?php echo ADMIN_URL; ?>" class="nav-link">Home</a>
@@ -198,6 +198,7 @@ if ($logged && admin()) {
<script src="<?php echo BASE_URL; ?>tools/js/datatables.bs.min.js"></script>
<?php } ?>
<script src="<?php echo BASE_URL; ?>tools/js/adminlte.min.js"></script>
<?php $twig->display('admin.menu-collapse.html.twig'); ?>
<?php $hooks->trigger(HOOK_ADMIN_BODY_END); ?>
</body>
</html>

View File

@@ -0,0 +1,23 @@
<?php
const MYAAC_ADMIN = true;
const IGNORE_SET_LAST_VISIT = true;
require '../../common.php';
require SYSTEM . 'functions.php';
require SYSTEM . 'init.php';
require SYSTEM . 'login.php';
if(!admin()) {
http_response_code(500);
die('You are not logged in. Probably session expired. Please login again.');
}
if (!isset($_POST['collapse'])) {
http_response_code(500);
die('Something went wrong.');
}
csrfProtect();
setSession('admin.menu-collapse', $_POST['collapse'] == 'true');

View File

@@ -27,7 +27,7 @@ if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is
const MYAAC = true;
const MYAAC_VERSION = '2.0-dev';
const DATABASE_VERSION = 47;
const DATABASE_VERSION = 48;
const TABLE_PREFIX = 'myaac_';
define('START_TIME', microtime(true));
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX'));
@@ -148,16 +148,17 @@ if(!IS_CLI) {
/** @var array $config */
ini_set('log_errors', 1);
if(@$config['env'] === 'dev' || defined('MYAAC_INSTALL')) {
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
}
else {
if(isset($config['env']) && $config['env'] !== 'dev' && !defined('MYAAC_INSTALL')) {
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
}
else {
ini_set('html_errors', 0);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
}
$autoloadFile = VENDOR . 'autoload.php';
if (!is_file($autoloadFile)) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 33 KiB

BIN
images/monk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -102,6 +102,7 @@ CREATE TABLE IF NOT EXISTS `myaac_menu`
`template` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`link` varchar(255) NOT NULL,
`access` tinyint NOT NULL DEFAULT 0,
`blank` tinyint NOT NULL DEFAULT 0,
`color` varchar(6) NOT NULL DEFAULT '',
`category` int NOT NULL DEFAULT 1,

View File

@@ -5,8 +5,6 @@ $deprecatedConfig = [
'genders',
'template',
'template_allow_change',
'vocations_amount',
'vocations',
'client',
'session_prefix',
'friendly_urls',
@@ -21,7 +19,6 @@ $deprecatedConfig = [
'visitors_counter_ttl',
'views_counter',
'outfit_images_url',
'outfit_images_wrong_looktypes',
'item_images_url',
'account_country',
'towns',
@@ -52,6 +49,7 @@ $deprecatedConfig = [
'online_skulls',
'online_outfit',
'online_afk',
'team_style',
'team_display_outfit' => 'team_outfit',
'team_display_status' => 'team_status',
'team_display_world' => 'team_world',

View File

@@ -17,6 +17,8 @@ use MyAAC\Models\Guild;
use MyAAC\Models\House;
use MyAAC\Models\Pages;
use MyAAC\Models\Player;
use MyAAC\Models\PlayerDeath;
use MyAAC\Models\PlayerKillers;
use MyAAC\News;
use MyAAC\Plugins;
use MyAAC\Settings;
@@ -433,16 +435,22 @@ function delete_guild($id)
$rank_list->orderBy('level');
global $db;
$deletedColumn = 'deleted';
if ($db->hasColumn('players', 'deletion')) {
$deletedColumn = 'deletion';
}
/**
* @var OTS_GuildRank $rank_in_guild
*/
foreach($rank_list as $rank_in_guild) {
if($db->hasTable('guild_members'))
$players_with_rank = $db->query('SELECT `players`.`id` as `id`, `guild_members`.`rank_id` as `rank_id` FROM `players`, `guild_members` WHERE `guild_members`.`rank_id` = ' . $rank_in_guild->getId() . ' AND `players`.`id` = `guild_members`.`player_id` ORDER BY `name`;');
$players_with_rank = $db->query('SELECT `players`.`id` as `id`, `guild_members`.`rank_id` as `rank_id` FROM `players`, `guild_members` WHERE `guild_members`.`rank_id` = ' . $rank_in_guild->getId() . ' AND `players`.`id` = `guild_members`.`player_id` AND `' . $deletedColumn . '` = 0 ORDER BY `name`;');
else if($db->hasTable('guild_membership'))
$players_with_rank = $db->query('SELECT `players`.`id` as `id`, `guild_membership`.`rank_id` as `rank_id` FROM `players`, `guild_membership` WHERE `guild_membership`.`rank_id` = ' . $rank_in_guild->getId() . ' AND `players`.`id` = `guild_membership`.`player_id` ORDER BY `name`;');
$players_with_rank = $db->query('SELECT `players`.`id` as `id`, `guild_membership`.`rank_id` as `rank_id` FROM `players`, `guild_membership` WHERE `guild_membership`.`rank_id` = ' . $rank_in_guild->getId() . ' AND `players`.`id` = `guild_membership`.`player_id` AND `' . $deletedColumn . '` = 0 ORDER BY `name`;');
else
$players_with_rank = $db->query('SELECT `id`, `rank_id` FROM `players` WHERE `rank_id` = ' . $rank_in_guild->getId() . ' AND `deleted` = 0;');
$players_with_rank = $db->query('SELECT `id`, `rank_id` FROM `players` WHERE `rank_id` = ' . $rank_in_guild->getId() . ' AND `' . $deletedColumn . '` = 0;');
$players_with_rank_number = $players_with_rank->rowCount();
if($players_with_rank_number > 0) {
@@ -1129,11 +1137,44 @@ function csrfProtect(): void
}
}
function getTopPlayers($limit = 5, $skill = 'level') {
function getSkillIdByName(string $name): int|null
{
$skills = [
'level' => POT::SKILL_LEVEL,
'experience' => POT::SKILL_LEVEL,
'magic' => POT::SKILL_MAGIC,
'maglevel' => POT::SKILL_MAGIC,
'balance' => SKILL_BALANCE,
'frags' => SKILL_FRAGS,
'club' => POT::SKILL_CLUB,
'sword' => POT::SKILL_SWORD,
'axe' => POT::SKILL_AXE,
'dist' => POT::SKILL_DIST,
'distance' => POT::SKILL_DIST,
'shield' => POT::SKILL_SHIELD,
'shielding' => POT::SKILL_SHIELD,
'fish' => POT::SKILL_FISH,
'fishing' => POT::SKILL_FISH,
];
return $skills[$name] ?? null;
}
function getTopPlayers($limit = 5, $skill = POT::SKILL_LEVEL)
{
global $db;
if ($skill === 'level') {
$skill = 'experience';
$skillOriginal = $skill;
if (is_string($skill)) {
$skill = getSkillIdByName($skill);
}
if (!is_numeric($skill)) {
throw new RuntimeException("getTopPlayers: Invalid skill: $skillOriginal");
}
return Cache::remember("top_{$limit}_{$skill}", 2 * 60, function () use ($db, $limit, $skill) {
@@ -1154,15 +1195,64 @@ function getTopPlayers($limit = 5, $skill = 'level') {
$columns[] = 'lookmount';
}
return Player::query()
$query = Player::query()
->select($columns)
->withOnlineStatus()
->notDeleted()
->where('group_id', '<', setting('core.highscores_groups_hidden'))
->whereNotIn('id', setting('core.highscores_ids_hidden'))
->where('account_id', '!=', 1)
->orderByDesc($skill)
->limit($limit)
->orderByDesc('value');
if ($limit > 0) {
$query->limit($limit);
}
if ($skill >= POT::SKILL_FIRST && $skill <= POT::SKILL_LAST) { // skills
if ($db->hasColumn('players', 'skill_fist')) {// tfs 1.0
$skill_ids = array(
POT::SKILL_FIST => 'skill_fist',
POT::SKILL_CLUB => 'skill_club',
POT::SKILL_SWORD => 'skill_sword',
POT::SKILL_AXE => 'skill_axe',
POT::SKILL_DIST => 'skill_dist',
POT::SKILL_SHIELD => 'skill_shielding',
POT::SKILL_FISH => 'skill_fishing',
);
$query
->addSelect($skill_ids[$skill] . ' as value')
->orderByDesc($skill_ids[$skill] . '_tries');
} else {
$query
->join('player_skills', 'player_skills.player_id', '=', 'players.id')
->where('skillid', $skill)
->addSelect('player_skills.value as value');
}
} else if ($skill == SKILL_FRAGS) // frags
{
if ($db->hasTable('player_killers')) {
$query->addSelect(['value' => PlayerKillers::whereColumn('player_killers.player_id', 'players.id')->selectRaw('COUNT(*)')]);
} else {
$query->addSelect(['value' => PlayerDeath::unjustified()->whereColumn('player_deaths.killed_by', 'players.name')->selectRaw('COUNT(*)')]);
}
} else if ($skill == SKILL_BALANCE) // balance
{
$query
->addSelect('players.balance as value');
} else {
if ($skill == POT::SKILL_MAGIC) {
$query
->addSelect('players.maglevel as value', 'players.maglevel')
->orderByDesc('manaspent');
} else { // level
$query
->addSelect('players.level as value', 'players.experience')
->orderByDesc('experience');
}
}
return $query
->get()
->map(function ($e, $i) {
$row = $e->toArray();

View File

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

View File

@@ -26,6 +26,7 @@ use MyAAC\Cache\Cache;
*/
class OTS_DB_MySQL extends OTS_Base_DB
{
private bool $hasCacheChanged = false;
private array $has_table_cache = [];
private array $has_column_cache = [];
private array $get_column_info_cache = [];
@@ -164,7 +165,7 @@ class OTS_DB_MySQL extends OTS_Base_DB
$cache->delete('database_columns_info');
$cache->delete('database_checksum');
}
else {
else if ($this->hasCacheChanged) {
$cache->set('database_tables', serialize($this->has_table_cache), 3600);
$cache->set('database_columns', serialize($this->has_column_cache), 3600);
$cache->set('database_columns_info', serialize($this->get_column_info_cache), 3600);
@@ -228,6 +229,8 @@ class OTS_DB_MySQL extends OTS_Base_DB
private function hasTableInternal($name): bool
{
$this->hasCacheChanged = true;
return ($this->has_table_cache[$name] = $this->query('SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = ' . $this->quote(config('database_name')) . ' AND `TABLE_NAME` = ' . $this->quote($name) . ' LIMIT 1;')->rowCount() > 0);
}
@@ -241,6 +244,8 @@ class OTS_DB_MySQL extends OTS_Base_DB
}
private function hasColumnInternal($table, $column): bool {
$this->hasCacheChanged = true;
return $this->hasTable($table) && ($this->has_column_cache[$table . '.' . $column] = count($this->query('SHOW COLUMNS FROM `' . $table . "` LIKE " . $this->quote($column))->fetchAll()) > 0);
}
@@ -272,6 +277,8 @@ class OTS_DB_MySQL extends OTS_Base_DB
return false;
}
$this->hasCacheChanged = true;
$formatResult = function ($result) {
return [
'field' => $result['Field'],

View File

@@ -673,13 +673,7 @@ class OTS_Player extends OTS_Row_DAO
throw new E_OTS_NotLoaded();
}
if(isset($this->data['promotion'])) {
global $config;
if((int)$this->data['promotion'] > 0)
return ($this->data['vocation'] + ($this->data['promotion'] * $config['vocations_amount']));
}
return $this->data['vocation'];
return \OTS_Toolbox::getVocationFromPromotion($this->data['vocation'], $this->data['promotion'] ?? 0);
}

View File

@@ -13,6 +13,8 @@
* @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3
*/
use MyAAC\Server\XML\Vocations;
/**
* Toolbox for common operations.
*
@@ -110,14 +112,21 @@ class OTS_Toolbox
$list->setFilter($filter);
return $list;
}
public static function getVocationName($id, $promotion = 0): string
public static function getVocationFromPromotion($id, $promotion = 0): int
{
if($promotion > 0) {
$id = ($id + ($promotion * config('vocations_amount')));
for ($i = 0; $i < $promotion; $i++) {
if ($_id = Vocations::getPromoted($id)) {
$id = $_id;
}
}
}
return config('vocations')[$id] ?? 'Unknown';
return $id;
}
public static function getVocationName($id, $promotion = 0): string {
return config('vocations')[self::getVocationFromPromotion($id, $promotion)] ?? 'Unknown';
}
}

16
system/migrations/48.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
/**
* @var OTS_DB_MySQL $db
*/
$up = function () use ($db) {
if (!$db->hasColumn(TABLE_PREFIX . 'menu', 'access')) {
$db->addColumn(TABLE_PREFIX . 'menu', 'access', 'TINYINT NOT NULL DEFAULT 0 AFTER `link`');
}
};
$down = function () use ($db) {
if ($db->hasColumn(TABLE_PREFIX . 'menu', 'access')) {
$db->dropColumn(TABLE_PREFIX . 'menu', 'access');
}
};

View File

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

View File

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

View File

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

View File

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

View File

@@ -452,10 +452,8 @@ WHERE killers.death_id = '".$death['id']."' ORDER BY killers.final_hit DESC, kil
if($query->rowCount() > 0) {
echo 'Did you mean:<ul>';
foreach($query as $player) {
if(isset($player['promotion'])) {
if((int)$player['promotion'] > 0)
$player['vocation'] += ($player['promotion'] * $config['vocations_amount']);
}
$player['vocation'] = OTS_Toolbox::getVocationFromPromotion($player['vocation'], $player['promotion'] ?? 0);
echo '<li>' . getPlayerLink($player['name']) . ' (<small><strong>level ' . $player['level'] . ', ' . $config['vocations'][$player['vocation']] . '</strong></small>)</li>';
}
echo '</ul>';

View File

@@ -42,35 +42,12 @@ for($i = 0; $i < $threads_count['threads_count'] / setting('core.forum_threads_p
$links_to_pages .= '<b>'.($i + 1).' </b>';
}
echo '<a href="' . getLink('forum') . '">Boards</a> >> <b>'.$sections[$section_id]['name'].'</b>';
if($logged && (!$sections[$section_id]['closed'] || Forum::isModerator())) {
echo '<br /><br />
<a href="' . getLink('forum') . '?action=new_thread&section_id='.$section_id.'"><img src="images/forum/topic.gif" border="0" /></a>';
}
echo '<br /><br />Page: '.$links_to_pages.'<br />';
$last_threads = $db->query("SELECT `players`.`id` as `player_id`, `players`.`name`, `" . FORUM_TABLE_PREFIX . "forum`.`first_post`, `" . FORUM_TABLE_PREFIX . "forum`.`post_text`, `" . FORUM_TABLE_PREFIX . "forum`.`post_topic`, `" . FORUM_TABLE_PREFIX . "forum`.`id`, `" . FORUM_TABLE_PREFIX . "forum`.`last_post`, `" . FORUM_TABLE_PREFIX . "forum`.`replies`, `" . FORUM_TABLE_PREFIX . "forum`.`views`, `" . FORUM_TABLE_PREFIX . "forum`.`post_date` FROM `players`, `" . FORUM_TABLE_PREFIX . "forum` WHERE `players`.`id` = `" . FORUM_TABLE_PREFIX . "forum`.`author_guid` AND `" . FORUM_TABLE_PREFIX . "forum`.`section` = ".$section_id." AND `" . FORUM_TABLE_PREFIX . "forum`.`first_post` = `" . FORUM_TABLE_PREFIX . "forum`.`id` ORDER BY `" . FORUM_TABLE_PREFIX . "forum`.`last_post` DESC LIMIT ".setting('core.forum_threads_per_page')." OFFSET ".($_page * setting('core.forum_threads_per_page')))->fetchAll(PDO::FETCH_ASSOC);
if(isset($last_threads[0])) {
echo '<table width="100%">
<tr bgcolor="'.$config['vdarkborder'].'" align="center">
<td class="white">
<span style="font-size: 10px"><b>Thread</b></span></td>
<td><span style="font-size: 10px"><b>Thread Starter</b></span></td>
<td><span style="font-size: 10px"><b>Replies</b></span></td>
<td><span style="font-size: 10px"><b>Views</b></span></td>
<td><span style="font-size: 10px"><b>Last Post</b></span></td>
</tr>';
$threads = [];
if(count($last_threads) > 0) {
$player = new OTS_Player();
foreach($last_threads as $thread) {
echo '<tr bgcolor="' . getStyle($number_of_rows++) . '"><td>';
if(Forum::isModerator()) {
echo '<a href="' . getLink('forum') . '?action=move_thread&id=' . $thread['id'] . '" title="Move Thread"><img src="images/icons/arrow_right.gif"/></a>';
$twig->display('forum.remove_post.html.twig', ['post' => $thread]);
}
$player->load($thread['player_id']);
if(!$player->isLoaded()) {
throw new RuntimeException('Forum error: Player not loaded.');
@@ -79,28 +56,29 @@ if(isset($last_threads[0])) {
$player_account = $player->getAccount();
$canEditForum = $player_account->hasFlag(FLAG_CONTENT_FORUM) || $player_account->isAdmin();
echo '<a href="' . getForumThreadLink($thread['id']) . '">'.htmlspecialchars($thread['post_topic']). '</a><br /><small>'.($canEditForum ? substr(strip_tags($thread['post_text']), 0, 50) : htmlspecialchars(substr($thread['post_text'], 0, 50))).'...</small></td><td>' . getPlayerLink($thread['name']) . '</td><td>'.(int) $thread['replies'].'</td><td>'.(int) $thread['views'].'</td><td>';
$thread['link'] = getForumThreadLink($thread['id']);
$thread['post_shortened'] = ($canEditForum ? substr(strip_tags($thread['post_text']), 0, 50) : htmlspecialchars(substr($thread['post_text'], 0, 50)));
$thread['player'] = $player;
$thread['player_link'] = getPlayerLink($thread['name']);
if($thread['last_post'] > 0) {
$last_post = $db->query("SELECT `players`.`name`, `" . FORUM_TABLE_PREFIX . "forum`.`post_date` FROM `players`, `" . FORUM_TABLE_PREFIX . "forum` WHERE `" . FORUM_TABLE_PREFIX . "forum`.`first_post` = ".(int) $thread['id']." AND `players`.`id` = `" . FORUM_TABLE_PREFIX . "forum`.`author_guid` ORDER BY `post_date` DESC LIMIT 1")->fetch();
if(isset($last_post['name'])) {
echo date('d.m.y H:i:s', $last_post['post_date']) . '<br />by ' . getPlayerLink($last_post['name']);
}
else {
echo 'No posts.';
}
}
else {
echo date('d.m.y H:i:s', $thread['post_date']) . '<br />by ' . getPlayerLink($thread['name']);
}
echo '</td></tr>';
$last_post['player_link'] = getPlayerLink($last_post['name']);
$thread['latest_post'] = $last_post;
}
echo '</table>';
if($logged && (!$sections[$section_id]['closed'] || Forum::isModerator())) {
echo '<br /><a href="' . getLink('forum') . '?action=new_thread&section_id=' . $section_id . '"><img src="images/forum/topic.gif" border="0" /></a>';
$threads[] = $thread;
}
}
else {
echo '<h3>No threads in this board.</h3>';
}
$twig->display('forum.show_board.html.twig', [
'threads' => $threads,
'section_id' => $section_id,
'section_name' => $sections[$section_id]['name'],
'links_to_pages' => $links_to_pages,
'is_moderator' => Forum::isModerator(),
'closed' => $sections[$section_id]['closed'],
]);

View File

@@ -22,7 +22,7 @@ if(!$logged) {
}
$configLuaFreePremium = configLua('freePremium');
$freePremium = (isset($configLuaFreePremium) && getBoolean($configLuaFreePremium)) || ($logged && $account_logged->getPremDays() == OTS_Account::GRATIS_PREMIUM_DAYS);
$freePremium = (isset($configLuaFreePremium) && getBoolean($configLuaFreePremium));
$array_of_player_nig = array();
if(empty($errors))

View File

@@ -91,13 +91,18 @@ $guild_owner = $guild->getOwner();
if($guild_owner->isLoaded())
$guild_owner_name = $guild_owner->getName();
$deletedColumn = 'deleted';
if ($db->hasColumn('players', 'deletion')) {
$deletedColumn = 'deletion';
}
$guild_members = array();
foreach($rank_list as $rank)
{
if($db->hasTable(GUILD_MEMBERS_TABLE))
$players_with_rank = $db->query('SELECT `players`.`id` as `id`, `' . GUILD_MEMBERS_TABLE . '`.`rank_id` as `rank_id` FROM `players`, `' . GUILD_MEMBERS_TABLE . '` WHERE `' . GUILD_MEMBERS_TABLE . '`.`rank_id` = ' . $rank->getId() . ' AND `players`.`id` = `' . GUILD_MEMBERS_TABLE . '`.`player_id` ORDER BY `name`;');
$players_with_rank = $db->query('SELECT `players`.`id` as `id`, `' . GUILD_MEMBERS_TABLE . '`.`rank_id` as `rank_id` FROM `players`, `' . GUILD_MEMBERS_TABLE . '` WHERE `' . GUILD_MEMBERS_TABLE . '`.`rank_id` = ' . $rank->getId() . ' AND `players`.`id` = `' . GUILD_MEMBERS_TABLE . '`.`player_id` AND `' . $deletedColumn . '` = 0 ORDER BY `name`;');
else if($db->hasColumn('players', 'rank_id'))
$players_with_rank = $db->query('SELECT `id`, `rank_id` FROM `players` WHERE `rank_id` = ' . $rank->getId() . ' AND `deleted` = 0;');
$players_with_rank = $db->query('SELECT `id`, `rank_id` FROM `players` WHERE `rank_id` = ' . $rank->getId() . ' AND `' . $deletedColumn . '` = 0;');
$players_with_rank_number = $players_with_rank->rowCount();
if($players_with_rank_number > 0)

View File

@@ -13,6 +13,7 @@ use MyAAC\Cache\Cache;
use MyAAC\Models\Player;
use MyAAC\Models\PlayerDeath;
use MyAAC\Models\PlayerKillers;
use MyAAC\Server\XML\Vocations;
defined('MYAAC') or die('Direct access not allowed!');
$title = 'Highscores';
@@ -35,24 +36,20 @@ if(!is_numeric($page) || $page < 1 || $page > PHP_INT_MAX) {
$query = Player::query();
$configVocations = config('vocations');
$configVocationsAmount = config('vocations_amount');
$vocationId = null;
if($vocation !== 'all') {
foreach($configVocations as $id => $name) {
if(strtolower($name) == $vocation) {
$vocationId = $id;
$add_vocs = [$id];
$filterVocations = [$id];
if ($id !== 0) {
$i = $id + $configVocationsAmount;
while (isset($configVocations[$i])) {
$add_vocs[] = $i;
$i += $configVocationsAmount;
}
while($tmpVoc = Vocations::getPromoted($id)) {
$id = $tmpVoc;
$filterVocations[] = $tmpVoc;
}
$query->whereIn('players.vocation', $add_vocs);
$query->whereIn('players.vocation', $filterVocations);
break;
}
}
@@ -176,7 +173,9 @@ if (empty($highscores)) {
POT::SKILL_FISH => 'skill_fishing',
);
$query->addSelect($skill_ids[$skill] . ' as value');
$query
->addSelect($skill_ids[$skill] . ' as value')
->orderByDesc($skill_ids[$skill] . '_tries');
} else {
$query
->join('player_skills', 'player_skills.player_id', '=', 'players.id')
@@ -198,11 +197,11 @@ if (empty($highscores)) {
if ($skill == POT::SKILL__MAGLEVEL) {
$query
->addSelect('players.maglevel as value', 'players.maglevel')
->orderBy('manaspent');
->orderByDesc('manaspent');
} else { // level
$query
->addSelect('players.level as value', 'players.experience')
->orderBy('experience');
->orderByDesc('experience');
$list = 'experience';
}
}
@@ -247,7 +246,7 @@ foreach($highscores as $id => &$player)
$player['link'] = getPlayerLink($player['name'], false);
$player['flag'] = getFlagImage($player['country']);
$player['outfit'] = '<img style="position:absolute;margin-top:' . (in_array($player['looktype'], setting('core.outfit_images_wrong_looktypes')) ? '-15px;margin-left:5px' : '-45px;margin-left:-25px') . ';" src="' . $player['outfit_url'] . '" alt="" />';
$player['outfit'] = '<img style="position:absolute;margin-top:-50px;margin-left:-30px" src="' . $player['outfit_url'] . '" alt="" />';
if ($skill != POT::SKILL__LEVEL) {
if (isset($lastValue) && $lastValue == $player['value']) {
@@ -323,4 +322,5 @@ $twig->display('highscores.html.twig', [
'page' => $page,
'baseLink' => $baseLink,
'updatedAt' => $updatedAt,
'baseVocations' => Vocations::getBase(true),
]);

View File

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

View File

@@ -8,6 +8,7 @@
* @link https://my-aac.org
*/
use MyAAC\Models\Menu;
use MyAAC\Models\Pages;
use MyAAC\Plugins;
@@ -331,7 +332,20 @@ else {
}
}
if (!$found) {
$tmpPageOriginal = $page;
$pagesWithDynamicPart = ['characters', 'forum', 'highscores', 'monsters'];
foreach ($pagesWithDynamicPart as $_page) {
if (str_contains($page, $_page)) {
$tmpPageOriginal = $_page;
}
}
$themeMenu = Menu::select(['name'])
->where('template', $template_name)
->where('link', $tmpPageOriginal)
->where('access', '>', $logged_access);
if (!$found || $themeMenu->count() >= 1) {
$page = '404';
$file = SYSTEM . 'pages/404.php';
}

View File

@@ -219,7 +219,14 @@ return [
'cache_engine' => [
'name' => 'Cache Engine',
'type' => 'options',
'options' => ['auto' => 'Auto', 'file' => 'Files', 'apc' => 'APC', 'apcu' => 'APCu', 'disable' => 'Disable'],
'options' => [
'auto' => 'Auto',
'file' => 'Files',
'apc' => 'APC',
'apcu' => 'APCu',
'php' => 'PHP',
'disable' => 'Disable',
],
'desc' => 'Auto is most reasonable. It will detect the best cache engine',
'default' => 'auto',
'is_config' => true,
@@ -312,23 +319,6 @@ return [
},
],
],
'vocations_amount' => [
'name' => 'Vocations Amount',
'type' => 'number',
'desc' => 'How much basic vocations your server got (without promotion)',
'default' => 4,
],
'vocations' => [
'name' => 'Vocation Names',
'type' => 'textarea',
'desc' => 'Separated by comma. Must be in the same order as in vocations.xml, starting with id: 0.',
'default' => 'None, Sorcerer, Druid, Paladin, Knight, Master Sorcerer, Elder Druid,Royal Paladin, Elite Knight',
'callbacks' => [
'get' => function ($value) {
return array_map('trim', explode(',', $value));
},
],
],
[
'type' => 'category',
'title' => 'Database',
@@ -1482,17 +1472,6 @@ Sent by MyAAC,<br/>
'desc' => 'Set to animoutfit.php for animated outfit',
'default' => 'https://outfit-images.ots.me/latest/outfit.php',
],
'outfit_images_wrong_looktypes' => [
'name' => 'Outfit Images Wrong Looktypes',
'type' => 'text',
'desc' => 'This looktypes needs to have different margin-top and margin-left because they are wrong positioned',
'default' => '75, 126, 127, 266, 302',
'callbacks' => [
'get' => function ($value) {
return array_map('trim', explode(',', $value));
},
],
],
[
'type' => 'section',
'title' => 'Monster Images'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -48,14 +48,8 @@ class Player extends Model {
});
}
public function getVocationNameAttribute()
{
$vocation = $this->vocation;
if (isset($this->promotion) && $this->promotion > 0) {
$vocation += ($this->promotion * setting('core.vocations_amount'));
}
return config('vocations')[$vocation] ?? 'Unknown';
public function getVocationNameAttribute() {
return \OTS_Toolbox::getVocationName($this->vocation, $this->promotion ?? 0);
}
public function getIsDeletedAttribute()

View File

@@ -0,0 +1,93 @@
<?php
namespace MyAAC\Server\XML;
use MyAAC\Cache\Cache;
class Vocations
{
private static array $vocations;
private static array $vocationsFrom;
public function __construct()
{
$cached = Cache::remember('vocations', 10 * 60, function () {
$this->load();
$from = $this->getFrom();
$amount = 0;
foreach ($from as $vocId => $fromVocation) {
if ($vocId != 0 && $vocId == $fromVocation) {
$amount++;
}
}
return ['vocations' => $this->get(), 'vocationsFrom' => $from, 'amount' => $amount];
});
self::$vocations = $cached['vocations'];
self::$vocationsFrom = $cached['vocationsFrom'];
config(['vocations', self::$vocations]);
config(['vocations_amount', $cached['amount']]);
}
public function load(): void
{
if(!class_exists('DOMDocument')) {
throw new \RuntimeException('Please install PHP xml extension. MyAAC will not work without it.');
}
$vocationsXML = new \DOMDocument();
$file = config('data_path') . 'XML/vocations.xml';
if(!@file_exists($file)) {
$file = config('data_path') . 'vocations.xml';
}
if(!$vocationsXML->load($file)) {
throw new \RuntimeException('ERROR: Cannot load <i>vocations.xml</i> - the file is malformed. Check the file with xml syntax validator.');
}
foreach($vocationsXML->getElementsByTagName('vocation') as $vocation) {
$id = $vocation->getAttribute('id');
self::$vocations[$id] = $vocation->getAttribute('name');
$fromVocation = (int) $vocation->getAttribute('fromvoc');
self::$vocationsFrom[$id] = $fromVocation;
}
}
public static function get(): array {
return self::$vocations;
}
public static function getFrom(): array {
return self::$vocationsFrom;
}
public static function getPromoted(int $id): ?int {
foreach (self::$vocationsFrom as $vocId => $fromVocation) {
if ($id == $fromVocation && $vocId != $id) {
return $vocId;
}
}
return null;
}
public static function getOriginal(int $id): ?int {
return self::$vocationsFrom[$id] ?? null;
}
public static function getBase($includingRook = true): array {
$vocations = [];
foreach (self::$vocationsFrom as $vocId => $fromVoc) {
if ($vocId == $fromVoc && ($vocId != 0 || $includingRook)) {
$vocations[] = $vocId;
}
}
return $vocations;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,8 @@
<p class="note">You are editing: {{ template }}<br/><br/>
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.
Not all templates support blank and colorful links.<br/>
* Guest means everyone will see the link. Player means registered and logged-in user.
</p>
<div class="row text-center">
<div class="col-md-2 col-sm-1"></div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,507 +0,0 @@
/***
Spectrum Colorpicker v1.8.0
https://github.com/bgrins/spectrum
Author: Brian Grinstead
License: MIT
***/
.sp-container {
position:absolute;
top:0;
left:0;
display:inline-block;
*display: inline;
*zoom: 1;
/* https://github.com/bgrins/spectrum/issues/40 */
z-index: 9999994;
overflow: hidden;
}
.sp-container.sp-flat {
position: relative;
}
/* Fix for * { box-sizing: border-box; } */
.sp-container,
.sp-container * {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
/* http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */
.sp-top {
position:relative;
width: 100%;
display:inline-block;
}
.sp-top-inner {
position:absolute;
top:0;
left:0;
bottom:0;
right:0;
}
.sp-color {
position: absolute;
top:0;
left:0;
bottom:0;
right:20%;
}
.sp-hue {
position: absolute;
top:0;
right:0;
bottom:0;
left:84%;
height: 100%;
}
.sp-clear-enabled .sp-hue {
top:33px;
height: 77.5%;
}
.sp-fill {
padding-top: 80%;
}
.sp-sat, .sp-val {
position: absolute;
top:0;
left:0;
right:0;
bottom:0;
}
.sp-alpha-enabled .sp-top {
margin-bottom: 18px;
}
.sp-alpha-enabled .sp-alpha {
display: block;
}
.sp-alpha-handle {
position:absolute;
top:-4px;
bottom: -4px;
width: 6px;
left: 50%;
cursor: pointer;
border: 1px solid black;
background: white;
opacity: .8;
}
.sp-alpha {
display: none;
position: absolute;
bottom: -14px;
right: 0;
left: 0;
height: 8px;
}
.sp-alpha-inner {
border: solid 1px #333;
}
.sp-clear {
display: none;
}
.sp-clear.sp-clear-display {
background-position: center;
}
.sp-clear-enabled .sp-clear {
display: block;
position:absolute;
top:0px;
right:0;
bottom:0;
left:84%;
height: 28px;
}
/* Don't allow text selection */
.sp-container, .sp-replacer, .sp-preview, .sp-dragger, .sp-slider, .sp-alpha, .sp-clear, .sp-alpha-handle, .sp-container.sp-dragging .sp-input, .sp-container button {
-webkit-user-select:none;
-moz-user-select: -moz-none;
-o-user-select:none;
user-select: none;
}
.sp-container.sp-input-disabled .sp-input-container {
display: none;
}
.sp-container.sp-buttons-disabled .sp-button-container {
display: none;
}
.sp-container.sp-palette-buttons-disabled .sp-palette-button-container {
display: none;
}
.sp-palette-only .sp-picker-container {
display: none;
}
.sp-palette-disabled .sp-palette-container {
display: none;
}
.sp-initial-disabled .sp-initial {
display: none;
}
/* Gradients for hue, saturation and value instead of images. Not pretty... but it works */
.sp-sat {
background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#FFF), to(rgba(204, 154, 129, 0)));
background-image: -webkit-linear-gradient(left, #FFF, rgba(204, 154, 129, 0));
background-image: -moz-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
background-image: -o-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
background-image: -ms-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0));
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)";
filter : progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81');
}
.sp-val {
background-image: -webkit-gradient(linear, 0 100%, 0 0, from(#000000), to(rgba(204, 154, 129, 0)));
background-image: -webkit-linear-gradient(bottom, #000000, rgba(204, 154, 129, 0));
background-image: -moz-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
background-image: -o-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
background-image: -ms-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
background-image: linear-gradient(to top, #000, rgba(204, 154, 129, 0));
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)";
filter : progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000');
}
.sp-hue {
background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
background: -ms-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
background: -o-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), color-stop(0.17, #ffff00), color-stop(0.33, #00ff00), color-stop(0.5, #00ffff), color-stop(0.67, #0000ff), color-stop(0.83, #ff00ff), to(#ff0000));
background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
background: linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
}
/* IE filters do not support multiple color stops.
Generate 6 divs, line them up, and do two color gradients for each.
Yes, really.
*/
.sp-1 {
height:17%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00');
}
.sp-2 {
height:16%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00');
}
.sp-3 {
height:17%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff');
}
.sp-4 {
height:17%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff');
}
.sp-5 {
height:16%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff');
}
.sp-6 {
height:17%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000');
}
.sp-hidden {
display: none !important;
}
/* Clearfix hack */
.sp-cf:before, .sp-cf:after { content: ""; display: table; }
.sp-cf:after { clear: both; }
.sp-cf { *zoom: 1; }
/* Mobile devices, make hue slider bigger so it is easier to slide */
@media (max-device-width: 480px) {
.sp-color { right: 40%; }
.sp-hue { left: 63%; }
.sp-fill { padding-top: 60%; }
}
.sp-dragger {
border-radius: 5px;
height: 5px;
width: 5px;
border: 1px solid #fff;
background: #000;
cursor: pointer;
position:absolute;
top:0;
left: 0;
}
.sp-slider {
position: absolute;
top:0;
cursor:pointer;
height: 3px;
left: -1px;
right: -1px;
border: 1px solid #000;
background: white;
opacity: .8;
}
/*
Theme authors:
Here are the basic themeable display options (colors, fonts, global widths).
See http://bgrins.github.io/spectrum/themes/ for instructions.
*/
.sp-container {
border-radius: 0;
background-color: #ECECEC;
border: solid 1px #f0c49B;
padding: 0;
}
.sp-container, .sp-container button, .sp-container input, .sp-color, .sp-hue, .sp-clear {
font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
.sp-top {
margin-bottom: 3px;
}
.sp-color, .sp-hue, .sp-clear {
border: solid 1px #666;
}
/* Input */
.sp-input-container {
float:right;
width: 100px;
margin-bottom: 4px;
}
.sp-initial-disabled .sp-input-container {
width: 100%;
}
.sp-input {
font-size: 12px !important;
border: 1px inset;
padding: 4px 5px;
margin: 0;
width: 100%;
background:transparent;
border-radius: 3px;
color: #222;
}
.sp-input:focus {
border: 1px solid orange;
}
.sp-input.sp-validation-error {
border: 1px solid red;
background: #fdd;
}
.sp-picker-container , .sp-palette-container {
float:left;
position: relative;
padding: 10px;
padding-bottom: 300px;
margin-bottom: -290px;
}
.sp-picker-container {
width: 172px;
border-left: solid 1px #fff;
}
/* Palettes */
.sp-palette-container {
border-right: solid 1px #ccc;
}
.sp-palette-only .sp-palette-container {
border: 0;
}
.sp-palette .sp-thumb-el {
display: block;
position:relative;
float:left;
width: 24px;
height: 15px;
margin: 3px;
cursor: pointer;
border:solid 2px transparent;
}
.sp-palette .sp-thumb-el:hover, .sp-palette .sp-thumb-el.sp-thumb-active {
border-color: orange;
}
.sp-thumb-el {
position:relative;
}
/* Initial */
.sp-initial {
float: left;
border: solid 1px #333;
}
.sp-initial span {
width: 30px;
height: 25px;
border:none;
display:block;
float:left;
margin:0;
}
.sp-initial .sp-clear-display {
background-position: center;
}
/* Buttons */
.sp-palette-button-container,
.sp-button-container {
float: right;
}
/* Replacer (the little preview div that shows up instead of the <input>) */
.sp-replacer {
margin:0;
overflow:hidden;
cursor:pointer;
padding: 4px;
display:inline-block;
*zoom: 1;
*display: inline;
border: solid 1px #91765d;
background: #eee;
color: #333;
vertical-align: middle;
}
.sp-replacer:hover, .sp-replacer.sp-active {
border-color: #F0C49B;
color: #111;
}
.sp-replacer.sp-disabled {
cursor:default;
border-color: silver;
color: silver;
}
.sp-dd {
padding: 2px 0;
height: 16px;
line-height: 16px;
float:left;
font-size:10px;
}
.sp-preview {
position:relative;
width:25px;
height: 20px;
border: solid 1px #222;
margin-right: 5px;
float:left;
z-index: 0;
}
.sp-palette {
*width: 220px;
max-width: 220px;
}
.sp-palette .sp-thumb-el {
width:16px;
height: 16px;
margin:2px 1px;
border: solid 1px #d0d0d0;
}
.sp-container {
padding-bottom:0;
}
/* Buttons: http://hellohappy.org/css3-buttons/ */
.sp-container button {
background-color: #eeeeee;
background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc);
background-image: -moz-linear-gradient(top, #eeeeee, #cccccc);
background-image: -ms-linear-gradient(top, #eeeeee, #cccccc);
background-image: -o-linear-gradient(top, #eeeeee, #cccccc);
background-image: linear-gradient(to bottom, #eeeeee, #cccccc);
border: 1px solid #ccc;
border-bottom: 1px solid #bbb;
border-radius: 3px;
color: #333;
font-size: 14px;
line-height: 1;
padding: 5px 4px;
text-align: center;
text-shadow: 0 1px 0 #eee;
vertical-align: middle;
}
.sp-container button:hover {
background-color: #dddddd;
background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb);
background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb);
background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb);
background-image: -o-linear-gradient(top, #dddddd, #bbbbbb);
background-image: linear-gradient(to bottom, #dddddd, #bbbbbb);
border: 1px solid #bbb;
border-bottom: 1px solid #999;
cursor: pointer;
text-shadow: 0 1px 0 #ddd;
}
.sp-container button:active {
border: 1px solid #aaa;
border-bottom: 1px solid #888;
-webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
-moz-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
-ms-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
-o-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
}
.sp-cancel {
font-size: 11px;
color: #d93f3f !important;
margin:0;
padding:2px;
margin-right: 5px;
vertical-align: middle;
text-decoration:none;
}
.sp-cancel:hover {
color: #d93f3f !important;
text-decoration: underline;
}
.sp-palette span:hover, .sp-palette span.sp-thumb-active {
border-color: #000;
}
.sp-preview, .sp-alpha, .sp-thumb-el {
position:relative;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
}
.sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner {
display:block;
position:absolute;
top:0;left:0;bottom:0;right:0;
}
.sp-palette .sp-thumb-inner {
background-position: 50% 50%;
background-repeat: no-repeat;
}
.sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=);
}
.sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=);
}
.sp-clear-display {
background-repeat:no-repeat;
background-position: center;
background-image: url(data:image/gif;base64,R0lGODlhFAAUAPcAAAAAAJmZmZ2dnZ6enqKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq/Hx8fLy8vT09PX19ff39/j4+Pn5+fr6+vv7+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAUABQAAAihAP9FoPCvoMGDBy08+EdhQAIJCCMybCDAAYUEARBAlFiQQoMABQhKUJBxY0SPICEYHBnggEmDKAuoPMjS5cGYMxHW3IiT478JJA8M/CjTZ0GgLRekNGpwAsYABHIypcAgQMsITDtWJYBR6NSqMico9cqR6tKfY7GeBCuVwlipDNmefAtTrkSzB1RaIAoXodsABiZAEFB06gIBWC1mLVgBa0AAOw==);
}

File diff suppressed because it is too large Load Diff

View File

@@ -35,14 +35,14 @@
if(!isset($_REQUEST['name']))
die('Please enter name as get or post parameter.');
$name = stripslashes(ucwords(strtolower(trim($_REQUEST['name']))));
$player = new OTS_Player();
$player->find($name);
$player->find($_REQUEST['name']);
if(!$player->isLoaded())
{
header('Content-type: image/png');
readfile(SIGNATURES_IMAGES.'nocharacter.png');
//header('Content-type: image/png');
//readfile(SIGNATURES_IMAGES.'nocharacter.png');
http_response_code(404);
exit;
}