From 07486762dc3932e662a6fe0245d48ab6d49c2094 Mon Sep 17 00:00:00 2001
From: slawkens <slawkens@gmail.com>
Date: Tue, 14 Feb 2023 16:03:22 +0100
Subject: [PATCH] Add categories in tabs, move more settings, revert back
 getPluginSettings

Categories and sections are now not numbered
Remove example settings plugin
---
 admin/pages/settings.php                     |  64 +++-
 config.php                                   |  59 ----
 plugins/example-settings-plugin.json         |  11 -
 plugins/example-settings-plugin/settings.php |  36 --
 plugins/example.json                         |   3 +-
 system/libs/plugins.php                      |  22 +-
 system/settings.php                          | 327 ++++++++++++++++++-
 7 files changed, 396 insertions(+), 126 deletions(-)
 delete mode 100644 plugins/example-settings-plugin.json
 delete mode 100644 plugins/example-settings-plugin/settings.php

diff --git a/admin/pages/settings.php b/admin/pages/settings.php
index 53e5779e..e5eae974 100644
--- a/admin/pages/settings.php
+++ b/admin/pages/settings.php
@@ -11,14 +11,20 @@ defined('MYAAC') or die('Direct access not allowed!');
 $title = 'Settings';
 
 require_once SYSTEM . 'clients.conf.php';
-if (!isset($_GET['plugin']) || empty($_GET['plugin'])) {
-	error('Please select plugin name from left Panel.');
+if (empty($_GET['plugin'])) {
+	error('Please select plugin from left Panel.');
 	return;
 }
 
 $plugin = $_GET['plugin'];
 
 if($plugin != 'core') {
+	$pluginSettings = Plugins::getPluginSettings($plugin);
+	if (!$pluginSettings) {
+		error('This plugin does not exist or does not have options defined.');
+		return;
+	}
+
 	$settingsFilePath = PLUGINS . $plugin . '/settings.php';
 }
 else {
@@ -58,7 +64,7 @@ if (isset($_POST['save'])) {
 	success('Saved at ' . date('H:i'));
 }
 
-$title = ($plugin == 'core' ? 'MyAAC Settings' : 'Plugin Settings - ' . $plugin);
+$title = ($plugin == 'core' ? 'Settings' : 'Plugin Settings - ' . $plugin);
 
 $query = 'SELECT `key`, `value` FROM `' . TABLE_PREFIX . 'settings` WHERE `plugin_name` = ' . $db->quote($plugin) . ';';
 $query = $db->query($query);
@@ -79,16 +85,43 @@ if($query->rowCount() > 0) {
 				<div class="box-body">
 					<button name="save" type="submit" class="btn btn-primary">Save</button>
 				</div>
-					<?php
+				<br/>
+				<ul class="nav nav-tabs" id="myTab">
+				<?php
+					$i = 0;
+					foreach($settingsFile as $key => $setting) {
+					if ($setting['type'] === 'category') {
+						?>
+							<li class="nav-item">
+								<button class="nav-link<?= ($i === 0 ? ' active' : ''); ?>" id="home-tab-<?= $i++; ?>" data-toggle="tab" data-target="#tab-<?= str_replace(' ', '', $setting['title']); ?>" type="button"><?= $setting['title']; ?></button>
+							</li>
+				<?php
+						}
+					}
+					?>
+				</ul>
+				<div class="tab-content" id="tab-content">
+				<?php
 
 					$checkbox = function ($key, $type, $value) {
 						echo '<label><input type="radio" id="' . $key . '" name="settings[' . $key . ']" value="' . ($type ? 'true' : 'false') . '" ' . ($value === $type ? 'checked' : '') . '/>' . ($type ? 'Yes' : 'No') . '</label> ';
 					};
 
 					$i = 0;
+					$j = 0;
 					foreach($settingsFile as $key => $setting) {
-						if($setting['type'] === 'section') {
-							if($i++ !== 0) {
+						if ($setting['type'] === 'category') {
+							if ($j++ !== 0) {
+								echo '</tbody></table></div>';
+							}
+							?>
+					<div class="tab-pane fade show<?= ($j === 1 ? ' active' : ''); ?>" id="tab-<?= str_replace(' ', '', $setting['title']); ?>">
+					<?php
+							continue;
+						}
+
+						if ($setting['type'] === 'section') {
+							if ($i++ !== 0) {
 								echo '</tbody></table>';
 							}
 					?>
@@ -120,7 +153,7 @@ if($query->rowCount() > 0) {
 										}
 									}
 									else {
-										$value = (isset($setting['default']) ? $setting['default'] : false);
+										$value = ($setting['default'] ?? false);
 									}
 
 									$checkbox($key, true, $value);
@@ -128,11 +161,11 @@ if($query->rowCount() > 0) {
 								}
 
 								else if (in_array($setting['type'], ['text', 'number', 'email', 'password'])) {
-									echo '<input class="form-control" type="' . $setting['type'] . '" name="settings[' . $key . ']" value="' . (isset($settingsDb[$key]) ? $settingsDb[$key] : (!empty($setting['default']) ? $setting['default'] : '')) . '" id="' . $key . '"/>';
+									echo '<input class="form-control" type="' . $setting['type'] . '" name="settings[' . $key . ']" value="' . ($settingsDb[$key] ?? ($setting['default'] ?? '')) . '" id="' . $key . '"/>';
 								}
 
 								else if($setting['type'] === 'textarea') {
-									echo '<textarea class="form-control" name="settings[' . $key . ']" id="' . $key . '">' . (isset($settingsDb[$key]) ? $settingsDb[$key] : (!empty($setting['default']) ? $setting['default'] : '')) . '</textarea>';
+									echo '<textarea class="form-control" name="settings[' . $key . ']" id="' . $key . '">' . ($settingsDb[$key] ?? ($setting['default'] ?? '')) . '</textarea>';
 								}
 
 								if ($setting['type'] === 'options') {
@@ -159,6 +192,15 @@ if($query->rowCount() > 0) {
 										$setting['options'] = $clients;
 									}
 
+									else {
+										if (is_string($setting['options'])) {
+											$setting['options'] = explode(',', $setting['options']);
+											foreach ($setting['options'] as &$option) {
+												$option = trim($option);
+											}
+										}
+									}
+
 									echo '<select class="form-control" name="settings[' . $key . ']" id="' . $key . '">';
 									foreach ($setting['options'] as $value => $option) {
 										$compareTo = (isset($settingsDb[$key]) ? $settingsDb[$key] : (isset($setting['default']) ? $setting['default'] : ''));
@@ -189,10 +231,12 @@ if($query->rowCount() > 0) {
 					?>
 					</tbody>
 				</table>
+					</div>
+				</div>
 				<div class="box-footer">
 					<button name="save" type="submit" class="btn btn-primary">Save</button>
 				</div>
 			</div>
 		</div>
 	</div>
-</form>
\ No newline at end of file
+</form>
diff --git a/config.php b/config.php
index 3d8954b1..19d83b30 100644
--- a/config.php
+++ b/config.php
@@ -54,41 +54,7 @@ $config = array(
 		//'2' => 'Your Second World Name'
 	),
 
-	// images
-	'outfit_images_url' => 'https://outfit-images.ots.me/outfit.php', // set to animoutfit.php for animated outfit
-	'outfit_images_wrong_looktypes' => [75, 126, 127, 266, 302], // this looktypes needs to have different margin-top and margin-left because they are wrong positioned
-	'item_images_url' => 'https://item-images.ots.me/1092/', // set to images/items if you host your own items in images folder
-	'item_images_extension' => '.gif',
-
-	// creatures
-	'creatures_images_url' => 'images/monsters/', // set to images/monsters if you host your own creatures in images folder
-	'creatures_images_extension' => '.gif',
-	'creatures_images_preview' => false,  // set to true to allow picture previews for creatures
-	'creatures_items_url' => 'https://tibia.fandom.com/wiki/', // set to website which shows details about items.
-	'creatures_loot_percentage' => true, // set to true to show the loot tooltip percent
-
-	// account
-	'account_management' => true, // disable if you're using other method to manage users (fe. tfs account manager)
-	'account_login_by_email' => false, // use email instead of Account Name like in latest Tibia
-	'account_login_by_email_fallback' => false, // allow also additionally login by Account Name/Number (for users that might forget their email)
-	'account_create_auto_login' => false, // auto login after creating account?
-	'account_create_character_create' => true, // allow directly to create character on create account page?
-	'account_mail_verify' => false, // force users to confirm their email addresses when registering
-	'account_mail_confirmed_reward' => [ // reward users for confirming their E-Mails
-		// account_mail_verify needs to be enabled too
-		'premium_days' => 0,
-		'premium_points' => 0,
-		'coins' => 0,
-		'message' => 'You received %d %s for confirming your E-Mail address.' // example: You received 20 premium points for confirming your E-Mail address.
-	],
-	'account_mail_unique' => true, // email addresses cannot be duplicated? (one account = one email)
 	'account_mail_block_plus_sign' => true, // block email with '+' signs like test+box@gmail.com (help protect against spamming accounts)
-	'account_premium_days' => 0, // default premium days on new account
-	'account_premium_points' => 0, // default premium points on new account
-	'account_welcome_mail' => true, // send welcome email when user registers
-	'account_mail_change' => 2, // how many days user need to change email to account - block hackers
-	'account_country' => true, // user will be able to set country of origin when registering account, this information will be viewable in others places aswell
-	'account_country_recognize' => true, // should country of user be automatically recognized by his IP? This makes an external API call to http://ipinfo.io
 	'account_change_character_name' => false, // can user change their character name for premium points?
 	'account_change_character_name_points' => 30, // cost of name change
 	'account_change_character_sex' => false, // can user change their character sex for premium points?
@@ -169,31 +135,6 @@ $config = array(
 		1 => 'Sample town'
 	),
 
-	// guilds
-	'guild_management' => true, // enable guild management system on the site?
-	'guild_need_level' => 1, // min. level to form a guild
-	'guild_need_premium' => true, // require premium account to form a guild?
-	'guild_image_size_kb' => 80, // maximum size of the guild logo image in KB (kilobytes)
-	'guild_description_default' => 'New guild. Leader must edit this text :)',
-	'guild_description_chars_limit' => 1000, // limit of guild description
-	'guild_description_lines_limit' => 6, // limit of lines, if description has more lines it will be showed as long text, without 'enters'
-	'guild_motd_chars_limit' => 150, // limit of MOTD (message of the day) that is shown later in the game on the guild channel
-
-	// online page
-	'online_record' => true, // display players record?
-	'online_vocations' => false, // display vocation statistics?
-	'online_vocations_images' => false, // display vocation images?
-	'online_skulls' => false, // display skull images
-	'online_outfit' => true,
-	'online_afk' => false,
-
-	// support list page
-	'team_style' => 2, // 1/2 (1 - normal table, 2 - in boxes, grouped by group id)
-	'team_display_status' => true,
-	'team_display_lastlogin' => true,
-	'team_display_world' => false,
-	'team_display_outfit' => true,
-
 	// bans page
 	'bans_per_page' => 20,
 
diff --git a/plugins/example-settings-plugin.json b/plugins/example-settings-plugin.json
deleted file mode 100644
index b951b113..00000000
--- a/plugins/example-settings-plugin.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-	"enabled": 1,
-	"name": "Example Settings Plugin",
-	"description": "This is just an example of a Plugin for MyAAC.",
-	"version": "1.0",
-	"author": "nobody",
-	"contact": "nobody@example.org",
-	"require": {
-		"myaac": "0.9.0"
-	}
- }
diff --git a/plugins/example-settings-plugin/settings.php b/plugins/example-settings-plugin/settings.php
deleted file mode 100644
index 6b7a2fe0..00000000
--- a/plugins/example-settings-plugin/settings.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-return [
-	'donation_type' => [
-		'name' => 'Donation Type',
-		'type' => 'options',
-		'options' => ['points' => 'Points', 'coins' => 'Coins'],
-		'default' => 'points',
-		'desc' => 'What should be added to player account?',
-	],
-	'default_outfit_colors' => [
-		'name' => 'Default Outfit Colors',
-		'type' => 'text',
-		'default' => '4,38,87,114',
-		'desc' => "Default colors of outfits/addons in addons category<br/>
-		doesn't matter for othire and other servers without addons<br/>
-		you can use this outfit generator: http://sleqqus.idl.pl/tlg<br/>
-		Format: head,body,legs,feet",
-	],
-	'section_1' => [
-		'type' => 'section',
-		'title' => 'Section Test',
-	],
-	'just_testing_boolean' => [
-		'name' => 'Just Testing Boolean',
-		'type' => 'boolean',
-		'default' => false,
-		'desc' => "Some description.",
-	],
-	'just_testing_number' => [
-		'name' => 'Just Testing Number',
-		'type' => 'number',
-		'default' => 999,
-		'desc' => "Some description.",
-	],
-];
\ No newline at end of file
diff --git a/plugins/example.json b/plugins/example.json
index 6591a333..20415897 100644
--- a/plugins/example.json
+++ b/plugins/example.json
@@ -39,5 +39,6 @@
 			"redirect_from": "/redirectExample",
 			"redirect_to": "account/manage"
 		}
-	}
+	},
+	"settings": "plugins/your-plugin-folder/settings.php"
  }
diff --git a/system/libs/plugins.php b/system/libs/plugins.php
index 5568f992..13f8a08f 100644
--- a/system/libs/plugins.php
+++ b/system/libs/plugins.php
@@ -207,13 +207,33 @@ class Plugins {
 		return $hooks;
 	}
 
+	public static function getPluginSettings($pluginName)
+	{
+		$plugin_json = self::getPluginJson($pluginName);
+		if (!$plugin_json) {
+			return false;
+		}
+
+		if (!isset($plugin_json['settings']) || !file_exists(BASE . $plugin_json['settings'])) {
+			return false;
+		}
+
+		return $plugin_json['settings'];
+	}
+
 	public static function getPluginJson($name = null)
 	{
 		if(!isset($name)) {
 			return self::$plugin_json;
 		}
 
-		$string = file_get_contents(PLUGINS . $name . '.json');
+		$pathToPlugin = PLUGINS . $name . '.json';
+		if (!file_exists($pathToPlugin)) {
+			self::$warnings[] = "Cannot load " . $name . ".json. File doesn't exist.";
+			return false;
+		}
+
+		$string = file_get_contents($pathToPlugin);
 		$string = self::removeComments($string);
 		$plugin_json = json_decode($string, true);
 		if ($plugin_json == null) {
diff --git a/system/settings.php b/system/settings.php
index 076043e6..ff19ac2f 100644
--- a/system/settings.php
+++ b/system/settings.php
@@ -1,6 +1,10 @@
 <?php
 
 return [
+	'category_1' => [
+		'type' => 'category',
+		'title' => 'General'
+	],
 	'section_1' => [
 		'type' => 'section',
 		'title' => 'Template'
@@ -63,13 +67,13 @@ might bring some performance when disabled',
 	'charset' => [
 		'name' => 'Meta Charset',
 		'type' => 'text',
-		'desc' => 'Charset used in <meta>',
+		'desc' => 'Charset used in ' . escapeHtml('<meta>'),
 		'default' => 'utf-8',
 	],
 	'meta_description' => [
 		'name' => 'Meta Description',
 		'type' => 'textarea',
-		'desc' => 'description of the site in <meta>',
+		'desc' => 'description of the site in ' . escapeHtml('<meta>'),
 		'default' => 'Tibia is a free massive multiplayer online role playing game (MMORPG).',
 	],
 	'meta_keywords' => [
@@ -81,7 +85,7 @@ might bring some performance when disabled',
 	'footer' => [
 		'name' => 'Footer',
 		'type' => 'textarea',
-		'desc' => 'For example: "' . htmlspecialchars('<br/>') . 'Your Server &copy; 2020. All rights reserved."',
+		'desc' => 'For example: "' . escapeHtml('<br/>') . 'Your Server &copy; 2020. All rights reserved."',
 		'default' => '',
 	],
 	'language' => [
@@ -90,14 +94,18 @@ might bring some performance when disabled',
 		'options' => ['en' => 'English'],
 		'desc' => 'default language (currently only English available)',
 		'default' => 'en',
-	],
+	],*/
 	/*'language_allow_change' => [
 		'name' => 'Language Allow Change',
 		'type' => 'boolean',
 		'default' => false,
 		'desc' => 'default language (currently only English available)'
 	],*/
-	'section_4' => [
+	[
+		'type' => 'category',
+		'title' => 'Counters',
+	],
+	[
 		'type' => 'section',
 		'title' => 'Visitors Counter & Views Counter'
 	],
@@ -119,9 +127,121 @@ might bring some performance when disabled',
 		'desc' => 'Enable Views Counter? It will show how many times the website has been viewed by users',
 		'default' => true,
 	],
-	'section_5' => [
+	[
+		'type' => 'category',
+		'title' => 'Account',
+	],
+	[
 		'type' => 'section',
-		'title' => 'Images URL'
+		'title' => 'Account Settings'
+	],
+	'account_management' => [
+		'name' => 'Enable Account Management',
+		'type' => 'boolean',
+		'desc' => "disable if you're using other method to manage users (fe. tfs account manager)",
+		'default' => true,
+	],
+	'account_login_by_email' => [
+		'name' => 'Account Login By E-Mail',
+		'type' => 'boolean',
+		'desc' => "use email instead of Account Name like in latest Tibia",
+		'default' => true,
+	],
+	'account_login_by_email_fallback' => [
+		'name' => 'Account Login By E-Mail Fallback',
+		'type' => 'boolean',
+		'desc' => "allow also additionally login by Account Name/Number (for users that might forget their email). Works only if Account Login By E-Mail is also enabled",
+		'default' => false,
+	],
+	'account_create_auto_login' => [
+		'name' => 'Account Create Auto Login',
+		'type' => 'boolean',
+		'desc' => 'Auto login after creating account?',
+		'default' => false,
+	],
+	'account_create_character_create' => [
+		'name' => 'Account Create Character Create',
+		'type' => 'boolean',
+		'desc' => 'Allow to create character directly on create account page?',
+		'default' => true,
+	],
+	'account_mail_verify' => [
+		'name' => 'Account Mail Verify',
+		'type' => 'boolean',
+		'desc' => 'Force users to confirm their email addresses when registering account',
+		'default' => false,
+	],
+	'account_mail_unique' => [
+		'name' => 'Account Mail Unique',
+		'type' => 'boolean',
+		'desc' => 'Email addresses cannot be duplicated? (one account = one email)',
+		'default' => true,
+	],
+	'account_premium_days' => [
+		'name' => 'Default Account Premium Days',
+		'type' => 'number',
+		'desc' => 'Default premium days on new account',
+		'default' => 0,
+	],
+	'account_premium_points' => [
+		'name' => 'Default Account Premium Points',
+		'type' => 'number',
+		'desc' => 'Default premium points on new account',
+		'default' => 0,
+	],
+	'account_welcome_mail' => [
+		'name' => 'Account Welcome Mail',
+		'type' => 'boolean',
+		'desc' => 'Send welcome email when user registers',
+		'default' => true,
+	],
+	'account_mail_change' => [
+		'name' => 'Account Mail Change Days',
+		'type' => 'number',
+		'desc' => 'How many days user need to change email to account - block hackers',
+		'default' => 2,
+	],
+	'account_country' => [
+		'name' => 'Account Country',
+		'type' => 'boolean',
+		'desc' => 'User will be able to set country of origin when registering account, this information will be viewable in others places as well',
+		'default' => true,
+	],
+	'account_country_recognize' => [
+		'name' => 'Auto Recognize Account Country',
+		'type' => 'boolean',
+		'desc' => 'should country of user be automatically recognized by his IP? This makes an external API call to http://ipinfo.io',
+		'default' => true,
+	],
+	[
+		'type' => 'section',
+		'title' => 'Reward Users for confirming their E-Mails. Works only with Account Mail Verify enabled'
+	],
+	'account_mail_confirmed_reward_premium_days' => [
+		'name' => 'Reward Premium Points',
+		'type' => 'number',
+		'desc' => '0 to disable',
+		'default' => 0,
+	],
+	'account_mail_confirmed_reward_premium_points' => [
+		'name' => 'Reward Premium Points',
+		'type' => 'number',
+		'desc' => '0 to disable',
+		'default' => 0,
+	],
+	'account_mail_confirmed_reward_coins' => [
+		'name' => 'Reward Premium Points',
+		'type' => 'number',
+		'desc' => '0 to disable. Works only with servers that supports coins',
+		'default' => 0,
+	],
+	[
+		'type' => 'category',
+		'title' => 'Images',
+	],
+	[
+		'type' => 'section',
+		'title' => 'Item and Outfit Images'
 	],
 	'outfit_images_url' => [
 		'name' => 'Outfit Images URL',
@@ -129,10 +249,201 @@ might bring some performance when disabled',
 		'desc' => 'Set to animoutfit.php for animated outfit',
 		'default' => 'http://outfit-images.ots.me/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',
+	],
 	'item_images_url' => [
 		'name' => 'Item Images URL',
 		'type' => 'text',
 		'desc' => 'Set to images/items if you host your own items in images folder',
 		'default' => 'http://item-images.ots.me/1092/',
 	],
-];
\ No newline at end of file
+	'item_images_extension' => [
+		'name' => 'Item Images File Extension',
+		'type' => 'text',
+		'desc' => '',
+		'default' => '.gif',
+	],
+	[
+		'type' => 'section',
+		'title' => 'Monsters images'
+	],
+	'creatures_images_url' => [
+		'name' => 'Creatures images URL',
+		'type' => 'text',
+		'desc' => 'Set to images/monsters if you host your own creatures in images folder',
+		'default' => 'images/monsters/',
+	],
+	'creatures_images_extension' => [
+		'name' => 'Creatures Images File Extension',
+		'type' => 'text',
+		'desc' => '',
+		'default' => '.gif',
+	],
+	'creatures_images_preview' => [
+		'name' => 'Item Images URL',
+		'type' => 'boolean',
+		'desc' => 'Set to true to allow picture previews for creatures',
+		'default' => false,
+	],
+	'creatures_items_url' => [
+		'name' => 'Creatures Items URL',
+		'type' => 'text',
+		'desc' => 'Set to website which shows details about items',
+		'default' => 'https://tibia.fandom.com/wiki/',
+	],
+	'creatures_loot_percentage' => [
+		'name' => 'Creatures Items URL',
+		'type' => 'boolean',
+		'desc' => 'Set to true to show the loot tooltip percent',
+		'default' => true,
+	],
+	[
+		'type' => 'category',
+		'title' => 'Guilds',
+	],
+	[
+		'type' => 'section',
+		'title' => 'Guilds'
+	],
+	'guild_management' => [
+		'name' => 'Enable Guilds Management',
+		'type' => 'boolean',
+		'desc' => 'Enable guild management system on the site',
+		'default' => true,
+	],
+	'guild_need_level' => [
+		'name' => 'Guild Need Level',
+		'type' => 'number',
+		'desc' => 'Min. level to form a guild',
+		'default' => 1,
+	],
+	'guild_need_premium' => [
+		'name' => 'Guild Need Premium',
+		'type' => 'boolean',
+		'desc' => 'Require premium account to form a guild?',
+		'default' => true,
+	],
+	'guild_image_size_kb' => [
+		'name' => 'Guild Image Size',
+		'type' => 'number',
+		'desc' => 'Maximum size of the guild logo image in KB (kilobytes)',
+		'default' => 80,
+	],
+	'guild_description_default' => [
+		'name' => 'Default Guild Description',
+		'type' => 'text',
+		'desc' => 'Default description set on new guild',
+		'default' => 'New guild. Leader must edit this text :)',
+	],
+	'guild_description_chars_limit' => [
+		'name' => 'Guild Description Characters Limit',
+		'type' => 'boolean',
+		'desc' => 'How many characters can be in guild description',
+		'default' => 1000,
+	],
+	'guild_description_lines_limit' => [
+		'name' => 'Guild Description Lines Limit',
+		'type' => 'number',
+		'desc' => "Limit of lines, if description has more lines it will be showed as long text, without 'enters'",
+		'default' => 6,
+	],
+	'guild_motd_chars_limit' => [
+		'name' => 'Guild MOTD Characters Limit',
+		'type' => 'boolean',
+		'desc' => 'Limit of MOTD (message of the day) that is shown later in the game on the guild channel',
+		'default' => 150,
+	],
+	[
+		'type' => 'category',
+		'title' => 'Pages',
+	],
+	'section_10' => [
+		'type' => 'section',
+		'title' => 'Online Page'
+	],
+	'online_record' => [
+		'name' => 'Display Players Record',
+		'type' => 'boolean',
+		'desc' => '',
+		'default' => true,
+	],
+	'online_vocations' => [
+		'name' => 'Display Vocation Statistics',
+		'type' => 'boolean',
+		'desc' => '',
+		'default' => false,
+	],
+	'online_vocations_images' => [
+		'name' => 'Display Vocation Images',
+		'type' => 'boolean',
+		'desc' => 'Only if Display Vocation Statistics enabled',
+		'default' => true,
+	],
+	'online_skulls' => [
+		'name' => 'Display skull images',
+		'type' => 'boolean',
+		'desc' => '',
+		'default' => true,
+	],
+	'online_outfit' => [
+		'name' => 'Display Player Outfit',
+		'type' => 'boolean',
+		'desc' => '',
+		'default' => true,
+	],
+	'online_afk' => [
+		'name' => 'Display Players AFK',
+		'type' => 'boolean',
+		'desc' => '',
+		'default' => false,
+	],
+	[
+		'type' => 'section',
+		'title' => 'Team Page'
+	],
+	'team_style' => [
+		'name' => 'Style',
+		'type' => 'options',
+		'desc' => '',
+		'options' => ['normal table', 'in boxes, grouped by group id'],
+		'default' => 2,
+	],
+	'team_display_status' => [
+		'name' => 'Display Online Status',
+		'type' => 'boolean',
+		'desc' => '',
+		'default' => true,
+	],
+	'team_display_lastlogin' => [
+		'name' => 'Display Last Login',
+		'type' => 'boolean',
+		'desc' => '',
+		'default' => true,
+	],
+	'team_display_world' => [
+		'name' => 'Display World',
+		'type' => 'boolean',
+		'desc' => '',
+		'default' => false,
+	],
+	'team_display_outfit' => [
+		'name' => 'Display Outfit',
+		'type' => 'boolean',
+		'desc' => '',
+		'default' => true,
+	],
+	[
+		'type' => 'section',
+		'title' => 'Bans Page'
+	],
+	'bans_per_page' => [
+		'name' => 'Display Players Record',
+		'type' => 'boolean',
+		'desc' => '',
+		'default' => true,
+	],
+];