mirror of
				https://github.com/slawkens/myaac.git
				synced 2025-11-01 00:16:24 +01:00 
			
		
		
		
	Compare commits
	
		
			36 Commits
		
	
	
		
			v1.1
			...
			feature/ac
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 9d976256c3 | ||
|   | 3c1210fefa | ||
|   | 45af46b3c1 | ||
|   | 58feff50c6 | ||
|   | 464492d826 | ||
|   | 887b5068ad | ||
|   | 4c5cc8b573 | ||
|   | 529bdcf016 | ||
|   | 3e3f4bb5a5 | ||
|   | aa52df6e2e | ||
|   | 3b31915c22 | ||
|   | 1c6cda4df9 | ||
|   | cf51f7aa8d | ||
|   | ed89b99cd1 | ||
|   | 9d7fc98e1e | ||
|   | f646856e20 | ||
|   | f97f8cbe87 | ||
|   | 3da3e62c5b | ||
|   | d5dd7297b5 | ||
|   | 73de93a561 | ||
|   | ae1161d770 | ||
|   | 4455964db4 | ||
|   | 84870dbf2d | ||
|   | 329153fda6 | ||
|   | 4b6024dc45 | ||
|   | 28fef952f8 | ||
|   | 4fda4f643b | ||
|   | 19686725dc | ||
|   | a73fb1003e | ||
|   | b46ddb43d0 | ||
|   | de468a8dcd | ||
|   | ea51ad27c3 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | d2a4748a59 | ||
|   | a1ed209591 | ||
|   | 34321613d6 | ||
|   | 19b290feb7 | 
							
								
								
									
										6
									
								
								.github/workflows/cypress.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/cypress.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,9 +1,9 @@ | ||||
| name: Cypress | ||||
| on: | ||||
|   pull_request: | ||||
|     branches: [master] | ||||
|     branches: [main] | ||||
|   push: | ||||
|     branches: [master] | ||||
|     branches: [main] | ||||
|  | ||||
| jobs: | ||||
|   cypress: | ||||
| @@ -35,7 +35,7 @@ jobs: | ||||
|         - name: Checkout MyAAC | ||||
|           uses: actions/checkout@v4 | ||||
|           with: | ||||
|             ref: master | ||||
|             ref: main | ||||
|  | ||||
|         - uses: actions/setup-node@v4 | ||||
|           with: | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/phplint.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/phplint.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,9 +1,9 @@ | ||||
| name: PHP Linting | ||||
| on: | ||||
|   pull_request: | ||||
|     branches: [master] | ||||
|     branches: [main] | ||||
|   push: | ||||
|     branches: [master] | ||||
|     branches: [main] | ||||
|  | ||||
| jobs: | ||||
|   phplint: | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/phpstan.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/phpstan.yml
									
									
									
									
										vendored
									
									
								
							| @@ -2,9 +2,9 @@ name: "PHPStan" | ||||
|  | ||||
| on: | ||||
|   pull_request: | ||||
|     branches: [master] | ||||
|     branches: [main] | ||||
|   push: | ||||
|     branches: [master] | ||||
|     branches: [main] | ||||
|  | ||||
| jobs: | ||||
|   tests: | ||||
|   | ||||
							
								
								
									
										22
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,5 +1,25 @@ | ||||
| # Changelog | ||||
|  | ||||
| ## [1.2 - 09.02.2025] | ||||
|  | ||||
| ### Added | ||||
| * Twig session(key) function + reworked session functions to accept multi-array like in Laravel (https://github.com/slawkens/myaac/commit/b46ddb43d03ef7e5fc34e555e92e856bdc905691) | ||||
| * add template_name to twig variables (https://github.com/slawkens/myaac/commit/ae1161d77050bda181802b4496c9de920a7bb1bc) | ||||
| * add HOOK_INIT, executed just after $hooks are loaded (https://github.com/slawkens/myaac/commit/19686725dc810f63a07f049f82c66cf336d90ca6) | ||||
|  | ||||
| ### Changed | ||||
| * settings: password input hide/show, enable Save button only if changes has been made, save settings in transaction (https://github.com/slawkens/myaac/commit/4fda4f643b60a151179e5dd4f04912fb2618d98f, https://github.com/slawkens/myaac/commit/28fef952f857b79d64bc7495ffa5e1999e68e192, https://github.com/slawkens/myaac/commit/4b6024dc451accadb6c469fa282a9a764c1c0a81) | ||||
| * rework menus: Different categories can have different colors + Option to reset menus (https://github.com/slawkens/myaac/commit/73de93a561f6b13111e019075724357d8a617249, https://github.com/slawkens/myaac/commit/3da3e62c5b12390d75de9b3320729bcca6e0b458) | ||||
|  | ||||
| ### Fixed | ||||
| * highscores: Fix online status + vocation for TFS 0.x (https://github.com/slawkens/myaac/commit/ea51ad27c38be88d86514cb979bb394fcfbef1f0) | ||||
| * clear cache button in admin bar needed to be clicked twice until it worked (https://github.com/slawkens/myaac/commit/ea51ad27c38be88d86514cb979bb394fcfbef1f0) | ||||
| * HOOK_STARTUP location (https://github.com/slawkens/myaac/commit/a73fb1003ee3f812cf182d1834d65f08e6f60d1f) | ||||
| * if vocation name has more words (https://github.com/slawkens/myaac/commit/9d7fc98e1e0a96b59ecc1a7c39800a64445db364) | ||||
|  | ||||
| ### Updated | ||||
| * Bump twig/twig from 3.18.0 to 3.19.0 (#284) | ||||
|  | ||||
| ## [1.1 - 27.01.2025] | ||||
|  | ||||
| ### Changed | ||||
| @@ -10,7 +30,7 @@ | ||||
|  | ||||
| ### Fixed | ||||
| * general fixes in the tibiacom template menus, better support for custom menus | ||||
| * make functions_custom.php optional | ||||
| * make functions_custom.php optional (https://github.com/slawkens/myaac/commit/dc2b5afd9980984e2b259c9fc99f2ade46f70a5a) | ||||
| * error in CLI, where BASE_URL is not defined (https://github.com/slawkens/myaac/commit/4d749b881582f64b5a46196dbbb5ee8097127f03) | ||||
| * hook ACCOUNT_LOGIN_BEFORE_ACCOUNT location (https://github.com/slawkens/myaac/commit/669c447fca8643ce56d9ef8c1374ec647c780998) | ||||
|  | ||||
|   | ||||
							
								
								
									
										2
									
								
								CREDITS
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								CREDITS
									
									
									
									
									
								
							| @@ -1,3 +1,3 @@ | ||||
| * Gesior.pl (2007 - 2008) | ||||
| * Slawkens (2009 - 2023) | ||||
| * Slawkens (2009 - 2025) | ||||
| * Contributors listed in CONTRIBUTORS.txt | ||||
|   | ||||
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							| @@ -10,12 +10,12 @@ Official website: https://my-aac.org | ||||
| [](https://discord.gg/2J39Wus) | ||||
| [](https://github.com/slawkens/myaac/issues?q=is%3Aissue+is%3Aclosed) | ||||
|  | ||||
| | Version | Status                 | Branch  | Requirements   | | ||||
| |:--------|:-----------------------|:--------|:---------------| | ||||
| | **1.x** | **Active development** | develop | **PHP >= 8.1** | | ||||
| | 0.9.x   | Not developed anymore  | 0.9     | PHP >= 7.2.5   | | ||||
| | 0.8.x   | Active support         | master  | PHP >= 7.2.5   | | ||||
| | 0.7.x   | End Of Life            | 0.7     | PHP >= 5.3.3   | | ||||
| | Version | Status                 | Branch | Requirements   | | ||||
| |:--------|:-----------------------|:-------|:---------------| | ||||
| | **1.x** | **Active development** | master | **PHP >= 8.1** | | ||||
| | 0.9.x   | Not developed anymore  | 0.9    | PHP >= 7.2.5   | | ||||
| | 0.8.x   | Active support         | 0.8    | PHP >= 7.2.5   | | ||||
| | 0.7.x   | End Of Life            | 0.7    | PHP >= 5.3.3   | | ||||
|  | ||||
| ### Requirements | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
|  */ | ||||
|  | ||||
| use MyAAC\Models\Account as AccountModel; | ||||
| use MyAAC\Models\AccountAction; | ||||
| use MyAAC\Models\Player; | ||||
|  | ||||
| defined('MYAAC') or die('Direct access not allowed!'); | ||||
| @@ -466,9 +467,8 @@ else if (isset($_REQUEST['search'])) { | ||||
| 									</thead> | ||||
| 									<tbody> | ||||
| 										<?php | ||||
| 											$accountActions = \MyAAC\Models\AccountAction::where('account_id', $account->getId())->orderByDesc('date')->get(); | ||||
| 											$accountActions = AccountAction::where('account_id', $account->getId())->orderByDesc('date')->get(); | ||||
| 											foreach ($accountActions as $i => $log): | ||||
| 												$log->ip = ($log->ip != 0 ? long2ip($log->ip) : inet_ntop($log->ipv6)); | ||||
| 												?> | ||||
| 											<tr> | ||||
| 												<td><?php echo $i + 1; ?></td> | ||||
|   | ||||
| @@ -59,11 +59,7 @@ if (isset($_POST['template'])) { | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		$cache = Cache::getInstance(); | ||||
| 		if ($cache->enabled()) { | ||||
| 			$cache->delete('template_menus'); | ||||
| 		} | ||||
|  | ||||
| 		onTemplateMenusChange(); | ||||
| 		success('Saved at ' . date('H:i')); | ||||
| 	} | ||||
|  | ||||
| @@ -82,38 +78,48 @@ if (isset($_POST['template'])) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (isset($_GET['reset_colors'])) { | ||||
| 		if (isset($config['menu_default_color'])) { | ||||
| 			Menu::where('template', $template)->update(['color' => str_replace('#', '', $config['menu_default_color'])]); | ||||
| 			success('Colors has been reset.'); | ||||
| 		} | ||||
| 		else { | ||||
| 			warning('There is no default color defined, cannot reset colors.'); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!isset($config['menu_categories'])) { | ||||
| 		echo "No menu categories set in template config.php.<br/>This template doesn't support dynamic menus."; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (isset($_GET['reset_colors'])) { | ||||
| 		foreach ($config['menu_categories'] as $id => $options) { | ||||
| 			$color = $options['default_links_color'] ?? ($config['menu_default_links_color'] ?? ($config['menu_default_color'] ?? '#ffffff')); | ||||
| 			Menu::where('template', $template)->where('category', $id)->update(['color' => str_replace('#', '', $color)]); | ||||
| 		} | ||||
|  | ||||
| 		onTemplateMenusChange(); | ||||
| 		success('Colors has been reset at ' . date('H:i')); | ||||
| 	} | ||||
|  | ||||
| 	if (isset($_GET['reset_menus'])) { | ||||
| 		$configMenus = config('menus'); | ||||
| 		if (isset($configMenus)) { | ||||
| 			Plugins::installMenus($template, config('menus'), true); | ||||
|  | ||||
| 			onTemplateMenusChange(); | ||||
| 			success('Menus has been reset at ' . date('H:i')); | ||||
| 		} | ||||
| 		else { | ||||
| 			error("This template don't support reinstalling menus."); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	$title = 'Menus - ' . $template; | ||||
|  | ||||
| 	$canResetColors = isset($config['menu_default_color']) || isset($config['menu_default_links_color']); | ||||
| 	foreach ($config['menu_categories'] as $id => $options) { | ||||
| 		if (isset($options['default_links_color'])) { | ||||
| 			$canResetColors = true; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	$twig->display('admin.menus.header.html.twig', [ | ||||
| 		'template' => $template, | ||||
| 		'canResetColors' => $canResetColors | ||||
| 	]); | ||||
| 	?> | ||||
| 	<div align="center" class="text-center"> | ||||
| 		<p class="note">You are editing: <?= $template ?><br/><br/> | ||||
| 			Hint: You can drag menu items.<br/> | ||||
| 			Hint: Add links to external sites using: <b>http://</b> or <b>https://</b> prefix.<br/> | ||||
| 			Not all templates support blank and colorful links. | ||||
| 		</p> | ||||
| 		<?php if (isset($config['menu_default_color'])) {?> | ||||
| 		<form method="post" action="?p=menus&reset_colors" onsubmit="return confirm('Do you really want to reset colors?');"> | ||||
| 			<?php csrf(); ?> | ||||
| 			<input type="hidden" name="template" value="<?php echo $template ?>"/> | ||||
| 			<button type="submit" class="btn btn-danger">Reset Colors to default</button> | ||||
| 		</form> | ||||
| 		<br/> | ||||
| 		<?php } ?> | ||||
| 	</div> | ||||
| 	<?php | ||||
| 	$menus = Menu::query() | ||||
| 		->select('name', 'link', 'blank', 'color', 'category', 'ordering') | ||||
| @@ -143,12 +149,13 @@ if (isset($_POST['template'])) { | ||||
| 								if (isset($menus[$id])) { | ||||
| 									$i = 0; | ||||
| 									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 (empty($menu['color']) ? ($config['menu_default_color'] ?? '#ffffff') : $menu['color']); ?>"/> | ||||
| 											<input class="color-picker" type="text" name="menu_color[<?php echo $id ?>][]" value="<?php echo $color; ?>"/> | ||||
| 											<a class="remove-button" id="remove-button-<?php echo $id ?>-<?php echo $i ?>"><i class="fas fa-trash"></a></i></li> | ||||
| 										<?php $i++; $last_id[$id] = $i; | ||||
| 									endforeach; | ||||
| @@ -172,7 +179,6 @@ if (isset($_POST['template'])) { | ||||
| 	$twig->display('admin.menus.js.html.twig', array( | ||||
| 		'menus' => $menus, | ||||
| 		'last_id' => $last_id, | ||||
| 		'menu_default_color' => $config['menu_default_color'] ?? '#ffffff' | ||||
| 	)); | ||||
| 	?> | ||||
| 	<?php | ||||
| @@ -194,3 +200,11 @@ if (isset($_POST['template'])) { | ||||
| 		'templates' => $templates | ||||
| 	)); | ||||
| } | ||||
|  | ||||
| function onTemplateMenusChange(): void | ||||
| { | ||||
| 	$cache = Cache::getInstance(); | ||||
| 	if ($cache->enabled()) { | ||||
| 		$cache->delete('template_menus'); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -26,8 +26,8 @@ | ||||
| if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is required.'); | ||||
|  | ||||
| const MYAAC = true; | ||||
| const MYAAC_VERSION = '1.1'; | ||||
| const DATABASE_VERSION = 43; | ||||
| const MYAAC_VERSION = '1.2'; | ||||
| const DATABASE_VERSION = 44; | ||||
| const TABLE_PREFIX = 'myaac_'; | ||||
| define('START_TIME', microtime(true)); | ||||
| define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX')); | ||||
|   | ||||
							
								
								
									
										18
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										18
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							| @@ -2637,16 +2637,16 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "twig/twig", | ||||
|             "version": "v3.18.0", | ||||
|             "version": "v3.19.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/twigphp/Twig.git", | ||||
|                 "reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50" | ||||
|                 "reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/twigphp/Twig/zipball/acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50", | ||||
|                 "reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50", | ||||
|                 "url": "https://api.github.com/repos/twigphp/Twig/zipball/d4f8c2b86374f08efc859323dbcd95c590f7124e", | ||||
|                 "reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
| @@ -2701,7 +2701,7 @@ | ||||
|             ], | ||||
|             "support": { | ||||
|                 "issues": "https://github.com/twigphp/Twig/issues", | ||||
|                 "source": "https://github.com/twigphp/Twig/tree/v3.18.0" | ||||
|                 "source": "https://github.com/twigphp/Twig/tree/v3.19.0" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
| @@ -2713,7 +2713,7 @@ | ||||
|                     "type": "tidelift" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2024-12-29T10:51:50+00:00" | ||||
|             "time": "2025-01-29T07:06:14+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "voku/portable-ascii", | ||||
| @@ -2910,7 +2910,7 @@ | ||||
|     ], | ||||
|     "aliases": [], | ||||
|     "minimum-stability": "stable", | ||||
|     "stability-flags": {}, | ||||
|     "stability-flags": [], | ||||
|     "prefer-stable": false, | ||||
|     "prefer-lowest": false, | ||||
|     "platform": { | ||||
| @@ -2921,6 +2921,6 @@ | ||||
|         "ext-xml": "*", | ||||
|         "ext-dom": "*" | ||||
|     }, | ||||
|     "platform-dev": {}, | ||||
|     "plugin-api-version": "2.6.0" | ||||
|     "platform-dev": [], | ||||
|     "plugin-api-version": "2.3.0" | ||||
| } | ||||
|   | ||||
| @@ -76,6 +76,8 @@ require_once SYSTEM . 'status.php'; | ||||
| $twig->addGlobal('config', $config); | ||||
| $twig->addGlobal('status', $status); | ||||
|  | ||||
| $hooks->trigger(HOOK_STARTUP); | ||||
|  | ||||
| // backward support for gesior | ||||
| if(setting('core.backward_support')) { | ||||
| 	define('INITIALIZED', true); | ||||
| @@ -117,8 +119,6 @@ if(setting('core.backward_support')) { | ||||
|  | ||||
| require_once SYSTEM . 'router.php'; | ||||
|  | ||||
| $hooks->trigger(HOOK_STARTUP); | ||||
|  | ||||
| // anonymous usage statistics | ||||
| // sent only when user agrees | ||||
| if(setting('core.anonymous_usage_statistics')) { | ||||
|   | ||||
| @@ -2,12 +2,12 @@ SET @myaac_database_version = 43; | ||||
|  | ||||
| CREATE TABLE `myaac_account_actions` | ||||
| ( | ||||
| 	`id` INT(11) NOT NULL AUTO_INCREMENT, | ||||
| 	`account_id` INT(11) NOT NULL, | ||||
| 	`ip` INT(10) UNSIGNED NOT NULL DEFAULT 0, | ||||
| 	`ipv6` BINARY(16) NOT NULL DEFAULT 0, | ||||
| 	`ip` VARCHAR(45) NOT NULL DEFAULT '', | ||||
| 	`date` INT(11) NOT NULL DEFAULT 0, | ||||
| 	`action` VARCHAR(255) NOT NULL DEFAULT '', | ||||
| 	KEY (`account_id`) | ||||
| 	PRIMARY KEY (`id`) | ||||
| ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; | ||||
|  | ||||
| CREATE TABLE `myaac_admin_menu` | ||||
|   | ||||
| @@ -22,7 +22,7 @@ if [ $1 = "prepare" ]; then | ||||
| 	mkdir -p tmp | ||||
|  | ||||
| 	# get myaac from git archive | ||||
| 	git archive --format zip --output tmp/myaac.zip master | ||||
| 	git archive --format zip --output tmp/myaac.zip main | ||||
|  | ||||
| 	cd tmp/ || exit | ||||
|  | ||||
|   | ||||
| @@ -121,7 +121,7 @@ function getPlayerLink($name, $generate = true, bool $colored = false): string | ||||
|  | ||||
| function getMonsterLink($name, $generate = true): string | ||||
| { | ||||
| 	$url = BASE_URL . (setting('core.friendly_urls') ? '' : 'index.php/') . 'monsters/' . urlencode($name); | ||||
| 	$url = BASE_URL . (setting('core.friendly_urls') ? '' : 'index.php/') . 'monsters?name=' . urlencode($name); | ||||
|  | ||||
| 	if(!$generate) return $url; | ||||
| 	return generateLink($url, $name); | ||||
| @@ -129,16 +129,14 @@ function getMonsterLink($name, $generate = true): string | ||||
|  | ||||
| function getHouseLink($name, $generate = true): string | ||||
| { | ||||
| 	if(is_numeric($name)) | ||||
| 	{ | ||||
| 	if(is_numeric($name)) { | ||||
| 		$house = House::find(intval($name), ['name']); | ||||
| 		if ($house) { | ||||
| 			$name = $house->name; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	$url = BASE_URL . (setting('core.friendly_urls') ? '' : 'index.php/') . 'houses/' . urlencode($name); | ||||
| 	$url = BASE_URL . (setting('core.friendly_urls') ? '' : 'index.php/') . 'houses?name=' . urlencode($name); | ||||
|  | ||||
| 	if(!$generate) return $url; | ||||
| 	return generateLink($url, $name); | ||||
| @@ -984,31 +982,29 @@ function load_config_lua($filename) | ||||
| 				continue; | ||||
| 			} | ||||
| 			$tmp_exp = explode('=', $line, 2); | ||||
| 			if(strpos($line, 'dofile') !== false) | ||||
| 			{ | ||||
| 			if(str_contains($line, 'dofile')) { | ||||
| 				$delimiter = '"'; | ||||
| 				if(strpos($line, $delimiter) === false) | ||||
| 				if(!str_contains($line, $delimiter)) { | ||||
| 					$delimiter = "'"; | ||||
| 				} | ||||
|  | ||||
| 				$tmp = explode($delimiter, $line); | ||||
| 				$result = array_merge($result, load_config_lua($config['server_path'] . $tmp[1])); | ||||
| 			} | ||||
| 			else if(count($tmp_exp) >= 2) | ||||
| 			{ | ||||
| 			else if(count($tmp_exp) >= 2) { | ||||
| 				$key = trim($tmp_exp[0]); | ||||
| 				if(0 !== strpos($key, '--')) | ||||
| 				{ | ||||
| 				if(!str_starts_with($key, '--')) { | ||||
| 					$value = trim($tmp_exp[1]); | ||||
| 					if(strpos($value, '--') !== false) {// found some deep comment | ||||
| 					if(str_contains($value, '--')) {// found some deep comment | ||||
| 						$value = preg_replace('/--.*$/i', '', $value); | ||||
| 					} | ||||
|  | ||||
| 					if(is_numeric($value)) | ||||
| 						$result[$key] = (float) $value; | ||||
| 					elseif(in_array(@$value[0], array("'", '"')) && in_array(@$value[strlen($value) - 1], array("'", '"'))) | ||||
| 						$result[$key] = (string) substr(substr($value, 1), 0, -1); | ||||
| 						$result[$key] = substr(substr($value, 1), 0, -1); | ||||
| 					elseif(in_array($value, array('true', 'false'))) | ||||
| 						$result[$key] = ($value === 'true') ? true : false; | ||||
| 						$result[$key] = $value === 'true'; | ||||
| 					elseif(@$value[0] === '{') { | ||||
| 						// arrays are not supported yet | ||||
| 						// just ignore the error | ||||
| @@ -1016,7 +1012,7 @@ function load_config_lua($filename) | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						foreach($result as $tmp_key => $tmp_value) // load values definied by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull | ||||
| 						foreach($result as $tmp_key => $tmp_value) // load values defined by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull | ||||
| 							$value = str_replace($tmp_key, $tmp_value, $value); | ||||
| 						$ret = @eval("return $value;"); | ||||
| 						if((string) $ret == '' && trim($value) !== '""') // = parser error | ||||
| @@ -1030,8 +1026,7 @@ function load_config_lua($filename) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	$result = array_merge($result, isset($config['lua']) ? $config['lua'] : array()); | ||||
| 	return $result; | ||||
| 	return array_merge($result, $config['lua'] ?? []); | ||||
| } | ||||
|  | ||||
| function str_replace_first($search,$replace, $subject) { | ||||
| @@ -1057,17 +1052,36 @@ function get_browser_real_ip() { | ||||
|  | ||||
| 	return '0'; | ||||
| } | ||||
| function setSession($key, $data): void { | ||||
| 	$_SESSION[setting('core.session_prefix') . $key] = $data; | ||||
| function setSession($key, $value = null): void { | ||||
| 	if (!is_array($key)) { | ||||
| 		$key = [$key => $value]; | ||||
| 	} | ||||
|  | ||||
| 	foreach ($key as $arrayKey => $arrayValue) { | ||||
| 		if (is_null($arrayValue)) { | ||||
| 			unsetSession($arrayKey); | ||||
| 		} | ||||
| 		else { | ||||
| 			$_SESSION[setting('core.session_prefix') . $arrayKey] = $arrayValue; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| function getSession($key) { | ||||
| 	$key = setting('core.session_prefix') . $key; | ||||
| 	return $_SESSION[$key] ?? false; | ||||
| 	return $_SESSION[setting('core.session_prefix') . $key] ?? null; | ||||
| } | ||||
| function unsetSession($key): void { | ||||
| 	unset($_SESSION[setting('core.session_prefix') . $key]); | ||||
| } | ||||
|  | ||||
| function session($key): mixed { | ||||
| 	if (is_array($key)) { | ||||
| 		setSession($key); | ||||
| 		return null; | ||||
| 	} | ||||
|  | ||||
| 	return getSession($key); | ||||
| } | ||||
|  | ||||
| function csrf(bool $return = false): string { | ||||
| 	return CsrfToken::create($return); | ||||
| } | ||||
| @@ -1107,10 +1121,6 @@ function getTopPlayers($limit = 5, $skill = 'level') { | ||||
| 			$columns[] = 'lookaddons'; | ||||
| 		} | ||||
|  | ||||
| 		if ($db->hasColumn('players', 'online')) { | ||||
| 			$columns[] = 'online'; | ||||
| 		} | ||||
|  | ||||
| 		return Player::query() | ||||
| 			->select($columns) | ||||
| 			->withOnlineStatus() | ||||
|   | ||||
| @@ -50,6 +50,7 @@ $cache = Cache::getInstance(); | ||||
| global $hooks; | ||||
| $hooks = new Hooks(); | ||||
| $hooks->load(); | ||||
| $hooks->trigger(HOOK_INIT); | ||||
|  | ||||
| // twig | ||||
| require_once SYSTEM . 'twig.php'; | ||||
|   | ||||
| @@ -12,6 +12,8 @@ | ||||
|  * @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU Lesser General Public License, Version 3 | ||||
|  */ | ||||
|  | ||||
| use MyAAC\Models\AccountAction; | ||||
|  | ||||
| /** | ||||
|  * OTServ account abstraction. | ||||
|  * | ||||
| @@ -1010,26 +1012,16 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable | ||||
|  | ||||
| 	public function logAction($action) | ||||
| 	{ | ||||
| 		$ip = get_browser_real_ip(); | ||||
| 		if(strpos($ip, ":") === false) { | ||||
| 			$ipv6 = '0'; | ||||
| 		} | ||||
| 		else { | ||||
| 			$ipv6 = $ip; | ||||
| 			$ip = ''; | ||||
| 		} | ||||
|  | ||||
| 		return $this->db->exec('INSERT INTO `' . TABLE_PREFIX . 'account_actions` (`account_id`, `ip`, `ipv6`, `date`, `action`) VALUES (' . $this->db->quote($this->getId()).', ' . ($ip == '' ? '0' : $this->db->quote(ip2long($ip))) . ', (' . ($ipv6 == '0' ? $this->db->quote('') : $this->db->quote(inet_pton($ipv6))) . '), UNIX_TIMESTAMP(NOW()), ' . $this->db->quote($action).')'); | ||||
| 		AccountAction::create([ | ||||
| 			'account_id' => $this->getId(), | ||||
| 			'ip' => get_browser_real_ip(), | ||||
| 			'date' => time(), | ||||
| 			'action' => $action, | ||||
| 		]); | ||||
| 	} | ||||
|  | ||||
| 	public function getActionsLog($limit1, $limit2) | ||||
| 	{ | ||||
| 		$actions = array(); | ||||
|  | ||||
| 		foreach($this->db->query('SELECT `ip`, `ipv6`, `date`, `action` FROM `' . TABLE_PREFIX . 'account_actions` WHERE `account_id` = ' . $this->data['id'] . ' ORDER by `date` DESC LIMIT ' . $limit1 . ', ' . $limit2 . '')->fetchAll() as $a) | ||||
| 			$actions[] = array('ip' => $a['ip'], 'ipv6' => $a['ipv6'], 'date' => $a['date'], 'action' => $a['action']); | ||||
|  | ||||
| 		return $actions; | ||||
| 	public function getActionsLog($limit) { | ||||
| 		return AccountAction::where('account_id', $this->data['id'])->orderByDesc('date')->limit($limit)->get()->toArray(); | ||||
| 	} | ||||
| /** | ||||
|  * Returns players iterator. | ||||
|   | ||||
| @@ -14,12 +14,12 @@ $account_logged = new OTS_Account(); | ||||
|  | ||||
| // stay-logged with sessions | ||||
| $current_session = getSession('account'); | ||||
| if($current_session !== false) | ||||
| if($current_session) | ||||
| { | ||||
| 	$account_logged->load($current_session); | ||||
| 	if($account_logged->isLoaded() && $account_logged->getPassword() == getSession('password') | ||||
| 		//&& (!isset($_SESSION['admin']) || admin()) | ||||
| 		&& (getSession('remember_me') !== false || getSession('last_visit') > time() - 15 * 60)) {  // login for 15 minutes if "remember me" is not used | ||||
| 		&& (getSession('remember_me') || getSession('last_visit') > time() - 15 * 60)) {  // login for 15 minutes if "remember me" is not used | ||||
| 			$logged = true; | ||||
| 	} | ||||
| 	else { | ||||
|   | ||||
							
								
								
									
										27
									
								
								system/migrations/44.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								system/migrations/44.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| <?php | ||||
| /** | ||||
|  * @var OTS_DB_MySQL $db | ||||
|  */ | ||||
|  | ||||
| // 2025-02-27 | ||||
| // remove ipv6, change to ip (for both ipv4 + ipv6) as VARCHAR(45) | ||||
| $up = function () use ($db) { | ||||
| 	$db->query("ALTER TABLE `myaac_account_actions` DROP KEY `account_id`;"); | ||||
| 	$db->query("ALTER TABLE  `myaac_account_actions` ADD COLUMN `id` INT(11) NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`id`);"); | ||||
|  | ||||
| 	$db->modifyColumn(TABLE_PREFIX . 'account_actions', 'ip', "VARCHAR(45) NOT NULL DEFAULT ''"); | ||||
| 	$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ip` = INET_NTOA(`ip`) WHERE `ip` != '0';"); | ||||
| 	$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ip` = INET6_NTOA(`ipv6`) WHERE `ip` = '0';"); | ||||
| 	$db->dropColumn(TABLE_PREFIX . 'account_actions', 'ipv6'); | ||||
| }; | ||||
|  | ||||
| $down = function () use ($db) { | ||||
| 	$db->query("ALTER TABLE `" . TABLE_PREFIX . "account_actions` DROP `id`;"); | ||||
| 	$db->query("ALTER TABLE  `" . TABLE_PREFIX . "account_actions` ADD KEY (`account_id`);"); | ||||
|  | ||||
| 	$db->addColumn(TABLE_PREFIX . 'account_actions', 'ipv6', "BINARY(16) NOT NULL DEFAULT 0x00000000000000000000000000000000 AFTER ip"); | ||||
| 	$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ipv6` = INET6_ATON(ip) WHERE NOT IS_IPV4(`ip`);"); | ||||
| 	$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ip` = INET_ATON(`ip`) WHERE IS_IPV4(`ip`);"); | ||||
| 	$db->query("UPDATE `" . TABLE_PREFIX . "account_actions` SET `ip` = 0 WHERE `ipv6` != 0x00000000000000000000000000000000;"); | ||||
| 	$db->modifyColumn(TABLE_PREFIX . 'account_actions', 'ip', "INT(11) UNSIGNED NOT NULL DEFAULT 0;"); | ||||
| }; | ||||
| @@ -85,12 +85,8 @@ if($email_new_time > 1) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| $actions = array(); | ||||
| foreach($account_logged->getActionsLog(0, 1000) as $action) { | ||||
| 	$actions[] = array('action' => $action['action'], 'date' => $action['date'], 'ip' => $action['ip'] != 0 ? long2ip($action['ip']) : inet_ntop($action['ipv6'])); | ||||
| } | ||||
| $actions = $account_logged->getActionsLog(1000); | ||||
|  | ||||
| $players = array(); | ||||
| /** @var OTS_Players_List $account_players */ | ||||
| $account_players = $account_logged->getPlayersList(); | ||||
| $account_players->orderBy('id'); | ||||
|   | ||||
| @@ -21,9 +21,9 @@ $settingHighscoresCountryBox = setting('core.highscores_country_box'); | ||||
| if(config('account_country') && $settingHighscoresCountryBox) | ||||
| 	require SYSTEM . 'countries.conf.php'; | ||||
|  | ||||
| $list = $_GET['list'] ?? 'experience'; | ||||
| $list = urldecode($_GET['list'] ?? 'experience'); | ||||
| $page = $_GET['page'] ?? 1; | ||||
| $vocation = $_GET['vocation'] ?? 'all'; | ||||
| $vocation = urldecode($_GET['vocation'] ?? 'all'); | ||||
|  | ||||
| if(!is_numeric($page) || $page < 1 || $page > PHP_INT_MAX) { | ||||
| 	$page = 1; | ||||
| @@ -234,10 +234,6 @@ foreach($highscores as $id => &$player) | ||||
| 			$player['experience'] = number_format($player['experience']); | ||||
| 		} | ||||
|  | ||||
| 		if(!$settingHighscoresVocation) { | ||||
| 			unset($player['vocation']); | ||||
| 		} | ||||
|  | ||||
| 		$player['link'] = getPlayerLink($player['name'], false); | ||||
| 		$player['flag'] = getFlagImage($player['country']); | ||||
| 		if($settingHighscoresOutfit) { | ||||
|   | ||||
| @@ -30,11 +30,11 @@ $state = ''; | ||||
| $order = ''; | ||||
| $type = ''; | ||||
|  | ||||
| if(isset($_GET['page']) && $_GET['page'] == 'view' && isset($_REQUEST['house'])) | ||||
| if(isset($_REQUEST['name'])) | ||||
| { | ||||
| 	$beds = array("", "one", "two", "three", "fourth", "fifth"); | ||||
| 	$houseName = $_REQUEST['house']; | ||||
| 	$houseId = (Validator::number($_REQUEST['house']) ? $_REQUEST['house'] : -1); | ||||
| 	$houseName = urldecode($_REQUEST['name']); | ||||
| 	$houseId = (Validator::number($_REQUEST['name']) ? $_REQUEST['name'] : -1); | ||||
| 	$selectHouse = $db->query('SELECT * FROM ' . $db->tableName('houses') . ' WHERE ' . $db->fieldName('name') . ' LIKE ' . $db->quote($houseName) . ' OR `id` = ' . $db->quote($houseId)); | ||||
|  | ||||
| 	$house = array(); | ||||
| @@ -187,7 +187,7 @@ if(isset($_POST['town']) && isset($_POST['state']) && isset($_POST['order']) && | ||||
| 				$houseRent = 'Free'; | ||||
| 		} | ||||
|  | ||||
| 		$houses[] = array('owner' => $owner, 'name' => $house['name'], 'size' => ($hasTilesColumn ? $house['tiles'] : $house['size']), 'rent' => $house['rent'], 'rentedBy' => $houseRent); | ||||
| 		$houses[] = array('owner' => $owner, 'name' => $house['name'], 'size' => ($hasTilesColumn ? $house['tiles'] : $house['size']), 'rent' => $house['rent'], 'rentedBy' => $houseRent, 'link' => getHouseLink($house['name'], false)); | ||||
| 	} | ||||
|  | ||||
| 	$housesSearch = true; | ||||
|   | ||||
| @@ -39,10 +39,10 @@ return [ | ||||
|  | ||||
| 	[['GET', 'POST'], 'guilds/{guild:string}', 'guilds/show.php'], | ||||
|  | ||||
| 	['GET', 'highscores/{list:alphanum}/{vocation:alphanum}/{page:int}', 'highscores.php'], | ||||
| 	['GET', 'highscores/{list:alphanum}/{page:int}', 'highscores.php'], | ||||
| 	['GET', 'highscores/{list:alphanum}/{vocation:alphanum}', 'highscores.php'], | ||||
| 	['GET', 'highscores/{list:alphanum}', 'highscores.php'], | ||||
| 	['GET', 'highscores/{list:string}/{vocation:string}/{page:int}', 'highscores.php'], | ||||
| 	['GET', 'highscores/{list:string}/{page:int}', 'highscores.php'], | ||||
| 	['GET', 'highscores/{list:string}/{vocation:string}', 'highscores.php'], | ||||
| 	['GET', 'highscores/{list:string}', 'highscores.php'], | ||||
| /* | ||||
| 	'/^polls\/[0-9]+\/?$/' => array('subtopic' => 'polls', 'id' => '$1'), | ||||
| 	'/^spells\/[A-Za-z0-9-_%]+\/[A-Za-z0-9-_]+\/?$/' => array('subtopic' => 'spells', 'vocation' => '$1', 'order' => '$2'), | ||||
|   | ||||
| @@ -348,7 +348,7 @@ return [ | ||||
| 		], | ||||
| 		'database_password' => [ | ||||
| 			'name' => 'Database Password', | ||||
| 			'type' => 'text', | ||||
| 			'type' => 'password', | ||||
| 			'default' => '', | ||||
| 			'show_if' => [ | ||||
| 				'database_overwrite', '=', 'true' | ||||
| @@ -1403,7 +1403,7 @@ Sent by MyAAC,<br/> | ||||
| 			'name' => 'Outfit Images URL', | ||||
| 			'type' => 'text', | ||||
| 			'desc' => 'Set to animoutfit.php for animated outfit', | ||||
| 			'default' => 'https://outfit-images.ots.me/outfit.php', | ||||
| 			'default' => 'https://outfit-images.ots.me/latest/outfit.php', | ||||
| 		], | ||||
| 		'outfit_images_wrong_looktypes' => [ | ||||
| 			'name' => 'Outfit Images Wrong Looktypes', | ||||
|   | ||||
| @@ -9,6 +9,6 @@ class AccountAction extends Model { | ||||
|  | ||||
| 	public $timestamps = false; | ||||
|  | ||||
| 	protected $fillable = ['account_id', 'ip', 'ipv6', 'date', 'action']; | ||||
| 	protected $fillable = ['account_id', 'ip', 'date', 'action']; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -49,8 +49,8 @@ class Player extends Model { | ||||
| 	public function getVocationNameAttribute() | ||||
| 	{ | ||||
| 		$vocation = $this->vocation; | ||||
| 		if (isset($this->promotion)) { | ||||
| 			$vocation *= $this->promotion; | ||||
| 		if (isset($this->promotion) && $this->promotion > 0) { | ||||
| 			$vocation += ($this->promotion * setting('core.vocations_amount')); | ||||
| 		} | ||||
|  | ||||
| 		return config('vocations')[$vocation] ?? 'Unknown'; | ||||
| @@ -80,11 +80,17 @@ class Player extends Model { | ||||
| 		$query->where($column, 0); | ||||
| 	} | ||||
|  | ||||
| 	public function scopeWithOnlineStatus($query) { | ||||
| 	public function scopeWithOnlineStatus($query) | ||||
| 	{ | ||||
| 		global $db; | ||||
| 		$query->when($db->hasTable('players_online'), function ($query) { | ||||
| 			$query->with('onlineTable'); | ||||
| 		}); | ||||
| 		if ($db->hasColumn('players', 'online')) { | ||||
| 			$query->addSelect('online'); | ||||
| 		} | ||||
| 		else { | ||||
| 			$query->when($db->hasTable('players_online'), function ($query) { | ||||
| 				$query->with('onlineTable'); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public function getOutfitUrlAttribute() { | ||||
|   | ||||
| @@ -72,24 +72,32 @@ class Settings implements \ArrayAccess | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		$this->errors = []; | ||||
| 		ModelsSettings::where('name', $pluginName)->delete(); | ||||
| 		foreach ($values as $key => $value) { | ||||
| 			$errorMessage = ''; | ||||
| 			if (isset($settings['settings'][$key]['callbacks']['beforeSave']) && !$settings['settings'][$key]['callbacks']['beforeSave']($key, $value, $errorMessage)) { | ||||
| 				$this->errors[] = $errorMessage; | ||||
| 				continue; | ||||
| 			} | ||||
| 		global $db; | ||||
|  | ||||
| 		try { | ||||
| 			$db->beginTransaction(); | ||||
|  | ||||
| 			$this->errors = []; | ||||
| 			ModelsSettings::where('name', $pluginName)->delete(); | ||||
| 			foreach ($values as $key => $value) { | ||||
| 				$errorMessage = ''; | ||||
| 				if (isset($settings['settings'][$key]['callbacks']['beforeSave']) && !$settings['settings'][$key]['callbacks']['beforeSave']($key, $value, $errorMessage)) { | ||||
| 					$this->errors[] = $errorMessage; | ||||
| 					continue; | ||||
| 				} | ||||
|  | ||||
| 			try { | ||||
| 				ModelsSettings::create([ | ||||
| 					'name' => $pluginName, | ||||
| 					'key' => $key, | ||||
| 					'value' => $value | ||||
| 				]); | ||||
| 			} catch (\PDOException $error) { | ||||
| 				$this->errors[] = 'Error while saving setting (' . $pluginName . ' - ' . $key . '): ' . $error->getMessage(); | ||||
| 			} | ||||
|  | ||||
| 			$db->commit(); | ||||
| 		} catch (\Exception $error) { | ||||
| 			$db->rollBack(); | ||||
| 			$this->errors[] = 'Error while saving settings (' . $pluginName . ')<br/>' . $error->getMessage(); | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		$this->clearCache(); | ||||
| @@ -247,7 +255,15 @@ class Settings implements \ArrayAccess | ||||
| 						$min = $max = $step = ''; | ||||
| 					} | ||||
|  | ||||
| 					if ($setting['type'] === 'password') { | ||||
| 						echo '<div class="input-group" id="show-hide-' . $key . '">'; | ||||
| 					} | ||||
|  | ||||
| 					echo '<input class="form-control" type="' . $setting['type'] . '" name="settings[' . $key . ']" value="' . ($settingsDb[$key] ?? ($setting['default'] ?? '')) . '" id="' . $key . '"' . $min . $max . $step . '/>'; | ||||
|  | ||||
| 					if ($setting['type'] === 'password') { | ||||
| 						echo '<div class="input-group-append input-group-text"><a href=""><i class="fas fa-eye-slash" ></i></a></div></div>'; | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				else if($setting['type'] === 'textarea') { | ||||
|   | ||||
| @@ -5,6 +5,7 @@ const SKILL_BALANCE = -2; | ||||
|  | ||||
| $i = 0; | ||||
|  | ||||
| define('HOOK_INIT', ++$i); | ||||
| define('HOOK_STARTUP', ++$i); | ||||
| define('HOOK_BEFORE_PAGE', ++$i); | ||||
| define('HOOK_AFTER_PAGE', ++$i); | ||||
| @@ -96,7 +97,7 @@ define('HOOK_CACHE_CLEAR', ++$i); | ||||
| define('HOOK_INSTALL_FINISH', ++$i); | ||||
| define('HOOK_INSTALL_FINISH_END', ++$i); | ||||
|  | ||||
| const HOOK_FIRST = HOOK_STARTUP; | ||||
| const HOOK_FIRST = HOOK_INIT; | ||||
| define('HOOK_LAST', $i); | ||||
|  | ||||
| function is_sub_dir($path = NULL, $parent_folder = BASE): bool|string | ||||
|   | ||||
| @@ -41,7 +41,7 @@ if(setting('core.template_allow_change')) | ||||
| 	} | ||||
| 	else { | ||||
| 		$template_session = getSession('template'); | ||||
| 		if ($template_session !== false) { | ||||
| 		if ($template_session) { | ||||
| 			if (!preg_match("/[^A-z0-9_\-]/", $template_session)) { | ||||
| 				$template_name = $template_session; | ||||
| 			} | ||||
| @@ -133,6 +133,7 @@ if($forumSetting != '') | ||||
| 		$template['link_forum'] = "<a href='" . $forumSetting . "' target='_blank'>"; | ||||
| } | ||||
|  | ||||
| $twig->addGlobal('template_name', $template_name); | ||||
| $twig->addGlobal('template_path', $template_path); | ||||
| if($twig_loader) { | ||||
| 	$twig_loader->prependPath(BASE . $template_path); | ||||
| @@ -152,17 +153,40 @@ function get_template_menus(): array | ||||
| 		return $result->toArray(); | ||||
| 	}); | ||||
|  | ||||
| 	$menus = array(); | ||||
| 	$configMenuCategories = config('menu_categories'); | ||||
| 	$configMenuDefaultColor = config('menu_default_links_color') ?? config('menu_default_color'); | ||||
|  | ||||
| 	$menus = []; | ||||
| 	foreach($result as $menu) { | ||||
| 		$link_full = strpos(trim($menu['link']), 'http') === 0 ? $menu['link'] : getLink($menu['link']); | ||||
| 		$menus[$menu['category']][] = array('name' => $menu['name'], 'link' => $menu['link'], 'link_full' => $link_full, 'blank' => $menu['blank'] == 1, 'target_blank' => ($menu['blank'] == 1 ? ' target="blank"' : ''), 'color' => $menu['color']); | ||||
| 		if (empty($menu['link'])) { | ||||
| 			$menu['link'] = 'news'; | ||||
| 		} | ||||
|  | ||||
| 		$link_full = (str_starts_with(trim($menu['link']), 'http') ? $menu['link'] : getLink($menu['link'])); | ||||
| 		$target_blank = ($menu['blank'] == 1 ? ' target="blank"' : ''); | ||||
|  | ||||
| 		$color = (empty($menu['color']) ? ($configMenuCategories[$menu['category']]['default_links_color'] ?? ($configMenuDefaultColor ?? '')) : $menu['color']); | ||||
|  | ||||
| 		$color = str_replace('#', '', $color); | ||||
|  | ||||
| 		if (in_array('#' . $color, [$configMenuCategories[$menu['category']]['default_links_color'] ?? '', $configMenuDefaultColor])) { | ||||
| 			$color = ''; | ||||
| 		} | ||||
|  | ||||
| 		$style_color = (empty($color) ? '' : 'style="color: #' . $color . ' !important"'); | ||||
|  | ||||
| 		$menus[$menu['category']][] = [ | ||||
| 			'name' => $menu['name'], | ||||
| 			'link' => $menu['link'], 'link_full' => $link_full, | ||||
| 			'blank' => $menu['blank'] == 1, 'target_blank' => $target_blank, | ||||
| 			'color' => $color, 'style_color' => $style_color, | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| 	$new_menus = array(); | ||||
| 	$new_menus = []; | ||||
| 	/** | ||||
| 	 * @var array $configMenuCategories | ||||
| 	 */ | ||||
| 	$configMenuCategories = config('menu_categories'); | ||||
| 	if($configMenuCategories === null) { | ||||
| 		return []; | ||||
| 	} | ||||
|   | ||||
| @@ -110,7 +110,7 @@ html { margin-top: 32px !important; } | ||||
| 			<form method="post" action="{{ constant('ADMIN_URL') }}?p=dashboard"> | ||||
| 				{{ csrf() }} | ||||
| 				<input type="hidden" name="clear_cache" value="1" /> | ||||
| 				<a class="ab-item" href="#" onclick="confirm('Are you sure that you want to clear cache?') && $(this).closest('form').submit()" title="Clear Cache">Clear Cache</a> | ||||
| 				<a class="ab-item" href="javascript:void(0);" onclick="confirm('Are you sure that you want to clear cache?') && $(this).closest('form').submit()" title="Clear Cache">Clear Cache</a> | ||||
| 			</form> | ||||
| 		</li> | ||||
| 	</ul> | ||||
|   | ||||
							
								
								
									
										31
									
								
								system/templates/admin.menus.header.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								system/templates/admin.menus.header.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| <div align="center" class="text-center"> | ||||
| 	<p class="note">You are editing: {{ template }}<br/><br/> | ||||
| 		Hint: You can drag menu items.<br/> | ||||
| 		Hint: Add links to external sites using: <b>http://</b> or <b>https://</b> prefix.<br/> | ||||
| 		Not all templates support blank and colorful links. | ||||
| 	</p> | ||||
| 	<div class="row text-center"> | ||||
| 		<div class="col-md-2 col-sm-1"></div> | ||||
| 		<div class="col-md-8 col-sm-10"> | ||||
| 			<div class="row justify-content-center"> | ||||
| 				{% if config('menus') is not null %} | ||||
| 				<form method="post" action="?p=menus&reset_menus" onsubmit="return confirm('Do you really want to reset menus?');"> | ||||
| 					{{ csrf() }} | ||||
| 					<input type="hidden" name="template" value="{{ template }}"/> | ||||
| 					<button type="submit" class="btn btn-danger">Reset Menus to default</button> | ||||
| 				</form> | ||||
| 				<br/> | ||||
| 				{% endif %} | ||||
| 				{% if canResetColors %} | ||||
| 				<form method="post" action="?p=menus&reset_colors" onsubmit="return confirm('Do you really want to reset colors?');"> | ||||
| 					{{ csrf() }} | ||||
| 					<input type="hidden" name="template" value="{{ template }}"/> | ||||
| 					<button type="submit" class="btn btn-warning" style="margin-left: 20px">Reset Colors to default</button> | ||||
| 				</form> | ||||
| 				<br/> | ||||
| 				{% endif %} | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="col-md-2 col-sm-1"></div> | ||||
| 	</div> | ||||
| </div> | ||||
| @@ -1,31 +1,41 @@ | ||||
| <link type="text/css" rel="stylesheet" href="{{ constant('BASE_URL') }}tools/ext/jquery-ui/themes/base/jquery-ui.min.css"> | ||||
| <script> | ||||
| 	var last_id = []; | ||||
| 	let last_id = []; | ||||
| 	let colors = []; | ||||
|  | ||||
| 	{% for cat, menu in menus %} | ||||
| 	last_id[{{ cat }}] = {{ last_id[cat] }}; | ||||
| 	{% endfor %} | ||||
| 	$(function () { | ||||
| 		$(".sortable").sortable(); | ||||
| 		$(".sortable").disableSelection(); | ||||
|  | ||||
| 		$(".remove-button").click(function () { | ||||
| 	{% set menuDefaultLinksColor = config('menu_default_links_color') %} | ||||
| 	{% for cat, options in config('menu_categories') %} | ||||
| 	colors[{{ cat }}] = '{{ options['default_links_color'] ?? (menuDefaultLinksColor ?? config('menu_default_color')) }}'; | ||||
| 	{% endfor %} | ||||
|  | ||||
| 	$(function () { | ||||
| 		const $sortable = $(".sortable"); | ||||
| 		$sortable.sortable(); | ||||
| 		$sortable.disableSelection(); | ||||
|  | ||||
| 		$(".remove-button").on('click', function () { | ||||
| 			var id = $(this).attr("id"); | ||||
| 			$('#list-' + id.replace('remove-button-', '')).remove(); | ||||
| 		}); | ||||
|  | ||||
| 		$(".add-button").click(function () { | ||||
| 		$(".add-button").on('click', function () { | ||||
| 			var cat = $(this).attr("id").replace('add-button-', ''); | ||||
| 			var id = last_id[cat]; | ||||
| 			last_id[cat]++; | ||||
| 			$('#sortable-' + cat).append('<li class="ui-state-default" id="list-' + cat + '-' + id + '"><label>Name:</label> <input type="text" name="menu[' + cat + '][]" value=""/> <label>Link:</label> <input type="text" name="menu_link[' + cat + '][]" value=""/><input type="hidden" name="menu_blank[' + cat + '][]" value="0" /> <label><input class="blank-checkbox" type="checkbox"/><span title="Open in New Window">New Window</span></label> <input class="color-picker" type="text" name="menu_color[' + cat + '][]" value="{{ menu_default_color }}" /><a class="remove-button" id="remove-button-' + cat + '-' + id + '"><i class="fas fa-trash"></i></a></li>'); //add input bo | ||||
| 			$('#remove-button-' + cat + '-' + id).click(function () { | ||||
| 			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(); | ||||
| 			}); | ||||
|  | ||||
| 			initialiceSpectrum(); | ||||
| 			initializeSpectrum(); | ||||
| 		}); | ||||
|  | ||||
| 		$("#menus-form").submit(function (e) { | ||||
| 		$("#menus-form").on('submit', function (e) { | ||||
| 			$('.blank-checkbox:not(:checked)').each(function (i, obj) { | ||||
| 				$(obj).parent().prev().val("off"); | ||||
| 			}); | ||||
| @@ -52,11 +62,11 @@ | ||||
| <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"> | ||||
| 	$(document).ready(function () { | ||||
| 		initialiceSpectrum(); | ||||
| 	$(function () { | ||||
| 		initializeSpectrum(); | ||||
| 	}); | ||||
|  | ||||
| 	function initialiceSpectrum() { | ||||
| 	function initializeSpectrum() { | ||||
| 		$(".color-picker").spectrum({ | ||||
| 			preferredFormat: "hex", | ||||
| 			showInput: true, | ||||
|   | ||||
| @@ -80,7 +80,26 @@ | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| 	$('#settings').submit(function(e) { | ||||
| 	const noChangesText = "No changes has been made"; | ||||
|  | ||||
| 	$('form') | ||||
| 		.each(function(){ | ||||
| 			$(this).data('serialized', $(this).serialize()) | ||||
| 		}) | ||||
| 		.on('change input', function(){ | ||||
| 			const disable = $(this).serialize() === $(this).data('serialized'); | ||||
| 			$(this) | ||||
| 				.find('input:submit, button:submit') | ||||
| 				.prop('disabled', disable) | ||||
| 				.prop('title', disable ? noChangesText : '') | ||||
| 			; | ||||
| 		}) | ||||
| 		.find('input:submit, button:submit') | ||||
| 		.prop('disabled', true) | ||||
| 		.prop('title', noChangesText) | ||||
| 	; | ||||
|  | ||||
| 	$('#settings').on('submit', function(e) { | ||||
| 		e.preventDefault(); | ||||
|  | ||||
| 		$.ajax({ | ||||
| @@ -94,6 +113,13 @@ | ||||
| 					duration: 3000, | ||||
| 					escapeMarkup: false, | ||||
| 				}).showToast(); | ||||
|  | ||||
| 				let $settings = $('#settings'); | ||||
| 				$settings.data('serialized', $settings.serialize()); | ||||
| 				$settings | ||||
| 					.find('input:submit, button:submit') | ||||
| 					.prop('disabled', true) | ||||
| 					.prop('title', noChangesText); | ||||
| 			}, | ||||
| 			error : function(response) { | ||||
| 				Toastify({ | ||||
| @@ -109,3 +135,27 @@ | ||||
| 		}); | ||||
| 	}); | ||||
| </script> | ||||
|  | ||||
| <script> | ||||
| {% for key, value in settings %} | ||||
| 	{% if value.type == 'password' %} | ||||
| 			$(function () { | ||||
| 				$('#show-hide-{{ key}} a').on('click', function(event) { | ||||
| 					event.preventDefault(); | ||||
|  | ||||
| 					const $showHideIcon = $('#show-hide-{{ key}} i'); | ||||
| 					const $showHideInput = $('#show-hide-{{ key }} input'); | ||||
| 					if($showHideInput.attr('type') === 'text'){ | ||||
| 						$showHideInput.attr('type', 'password'); | ||||
| 						$showHideIcon.addClass('fa-eye-slash'); | ||||
| 						$showHideIcon.removeClass('fa-eye'); | ||||
| 					}else if($showHideInput.attr("type") === 'password'){ | ||||
| 						$showHideInput.attr('type', 'text'); | ||||
| 						$showHideIcon.removeClass('fa-eye-slash'); | ||||
| 						$showHideIcon.addClass('fa-eye'); | ||||
| 					} | ||||
| 				}); | ||||
| 			}); | ||||
| 	{% endif %} | ||||
| {% endfor %} | ||||
| </script> | ||||
|   | ||||
| @@ -9,19 +9,17 @@ | ||||
| 					<td> | ||||
| 						<label for="skillFilter">Choose a Skill</label> | ||||
| 						<select onchange="location = this.value;" id="skillFilter"> | ||||
| 							{% set i = 0 %} | ||||
| 							{% for link, name in types %} | ||||
| 								<option value="{{ getLink('highscores') }}/{{ link }}{% if vocation is not null %}/{{ vocation }}{% endif %}" class="size_xs" {% if list is not null and list == link %}selected{% endif %}>{{ name }}</option> | ||||
| 								<option value="{{ getLink('highscores') }}/{{ link|urlencode }}{% if vocation is not null %}/{{ vocation|lower|urlencode }}{% endif %}" class="size_xs" {% if list is not null and list == link %}selected{% endif %}>{{ name }}</option> | ||||
| 							{% endfor %} | ||||
| 						</select> | ||||
| 					</td> | ||||
| 					<td> | ||||
| 						<label for="vocationFilter">Choose a vocation</label> | ||||
| 						<select onchange="location = this.value;" id="vocationFilter"> | ||||
| 							<option value="{{ getLink('highscores') }}/{{ list }}" class="size_xs">[ALL]</option> | ||||
| 							{% set i = 0 %} | ||||
| 							<option value="{{ getLink('highscores') }}/{{ list|urlencode }}" class="size_xs">[ALL]</option> | ||||
| 							{% for i in 0..config.vocations_amount %} | ||||
| 								<option value="{{ getLink('highscores') }}/{{ list }}/{{ config.vocations[i]|lower }}" class="size_xs" {% if vocationId is not null and vocationId == i %}selected{% endif %}>{{ config.vocations[i]}}</option> | ||||
| 								<option value="{{ getLink('highscores') }}/{{ list|urlencode }}/{{ config.vocations[i]|lower|urlencode }}" class="size_xs" {% if vocationId is not null and vocationId == i %}selected{% endif %}>{{ config.vocations[i]}}</option> | ||||
| 							{% endfor %} | ||||
| 						</select> | ||||
| 					</td> | ||||
| @@ -105,7 +103,7 @@ | ||||
| 				<tr bgcolor="{{ config.lightborder }}"> | ||||
| 					<td> | ||||
| 						{% for link, name in types %} | ||||
| 						<a href="{{ getLink('highscores') }}/{{ link }}{% if vocation is not null %}/{{ vocation }}{% endif %}" class="size_xs">{{ name }}</a><br/> | ||||
| 							<a href="{{ getLink('highscores') }}/{{ link|urlencode }}{% if vocation is not null %}/{{ vocation|urlencode }}{% endif %}" class="size_xs">{{ name }}</a><br/> | ||||
| 						{% endfor %} | ||||
| 					</td> | ||||
| 				</tr> | ||||
| @@ -118,9 +116,9 @@ | ||||
| 				</tr> | ||||
| 				<tr bgcolor="{{ config.lightborder }}"> | ||||
| 					<td> | ||||
| 						<a href="{{ getLink('highscores') }}/{{ list }}" class="size_xs">[ALL]</a><br/> | ||||
| 						<a href="{{ getLink('highscores') }}/{{ list|urlencode }}" class="size_xs">[ALL]</a><br/> | ||||
| 						{% for i in 0..config.vocations_amount %} | ||||
| 						<a href="{{ getLink('highscores') }}/{{ list }}/{{ config.vocations[i]|lower }}" class="size_xs">{{ config.vocations[i]}}</a><br/> | ||||
| 							<a href="{{ getLink('highscores') }}/{{ list|urlencode }}/{{ config.vocations[i]|lower|urlencode }}" class="size_xs">{{ config.vocations[i]}}</a><br/> | ||||
| 						{% endfor %} | ||||
| 					</td> | ||||
| 				</tr> | ||||
|   | ||||
| @@ -56,10 +56,9 @@ the search criteria and start a new search. | ||||
| 					</td> | ||||
|  | ||||
| 					<td> | ||||
| 						<form action="{{ getLink('houses/view') }}" method="post"> | ||||
| 							<input type="hidden" name="house" value="{{ house.name }}"> | ||||
| 						<a href="{{ house.link }}"> | ||||
| 							{{ include('buttons.view.html.twig') }} | ||||
| 						</form> | ||||
| 						</a> | ||||
| 					</td> | ||||
| 				</tr> | ||||
| 			{% endfor %} | ||||
|   | ||||
| @@ -140,6 +140,11 @@ $function = new TwigFunction('csrfToken', function () { | ||||
| }); | ||||
| $twig->addFunction($function); | ||||
|  | ||||
| $function = new TwigFunction('session', function ($key) { | ||||
| 	return session($key); | ||||
| }); | ||||
| $twig->addFunction($function); | ||||
|  | ||||
| $filter = new TwigFilter('urlencode', function ($s) { | ||||
| 	return urlencode($s); | ||||
| }); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <?php | ||||
| $config['menu_default_color'] = '#ffffff'; | ||||
| $config['menu_default_links_color'] = '#ffffff'; | ||||
|  | ||||
| $config['menu_categories'] = array( | ||||
| 	MENU_CATEGORY_NEWS => array('id' => 'news', 'name' => 'Latest News'), | ||||
| @@ -8,3 +8,5 @@ $config['menu_categories'] = array( | ||||
| 	MENU_CATEGORY_LIBRARY => array('id' => 'library', 'name' => 'Library'), | ||||
| 	MENU_CATEGORY_SHOP => array('id' => 'shops', 'name' => 'Shop') | ||||
| ); | ||||
|  | ||||
| $config['menus'] = require __DIR__ . '/menus.php'; | ||||
|   | ||||
| @@ -42,8 +42,6 @@ defined('MYAAC') or die('Direct access not allowed!'); | ||||
|  | ||||
| 			<div id="mainsubmenu"> | ||||
| 				<?php | ||||
| 				$default_menu_color = "ffffff"; | ||||
|  | ||||
| 				foreach($menus as $category => $menu) { | ||||
| 					if(!isset($menus[$category])) { | ||||
| 						continue; | ||||
| @@ -54,8 +52,8 @@ defined('MYAAC') or die('Direct access not allowed!'); | ||||
| 					$size = count($menus[$category]); | ||||
| 					$i = 0; | ||||
|  | ||||
| 					foreach($menus[$category] as $menu) { | ||||
| 						echo '<a href="' . $menu['link_full'] . '"' . ($menu['blank'] ? ' target="_blank"' : '') . ' style="color: #' . (strlen($menu['color']) == 0 ? $default_menu_color : $menu['color']) . ';">' . $menu['name'] . '</a>'; | ||||
| 					foreach($menus[$category] as $link) { | ||||
| 						echo '<a href="' . $link['link_full'] . '" ' . $link['target_blank'] . ' ' . $link['style_color'] . '>' . $link['name'] . '</a>'; | ||||
|  | ||||
| 						if(++$i != $size) { | ||||
| 							echo '<span class="separator"></span>'; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <?php | ||||
| $config['menu_default_color'] = '#ffffff'; | ||||
| $config['menu_default_links_color'] = '#ffffff'; | ||||
|  | ||||
| $config['menu_categories'] = array( | ||||
| 	MENU_CATEGORY_NEWS => array('id' => 'news', 'name' => 'Latest News'), | ||||
| @@ -9,3 +9,5 @@ $config['menu_categories'] = array( | ||||
| 	MENU_CATEGORY_LIBRARY => array('id' => 'library', 'name' => 'Library'), | ||||
| 	MENU_CATEGORY_SHOP => array('id' => 'shops', 'name' => 'Shop') | ||||
| ); | ||||
|  | ||||
| $config['menus'] = require __DIR__ . '/menus.php'; | ||||
|   | ||||
| @@ -7,7 +7,7 @@ if(strlen($text) > 100) // max limit | ||||
| putenv('GDFONTPATH=' . __DIR__); | ||||
|  | ||||
| // create image | ||||
| $image = imagecreatetruecolor(250, 28); | ||||
| $image = imagecreatetruecolor(600, 28); | ||||
|  | ||||
| // make the background transparent | ||||
| imagecolortransparent($image, imagecolorallocate($image, 0, 0, 0)); | ||||
|   | ||||
| @@ -372,19 +372,13 @@ foreach($config['menu_categories'] as $id => $cat) { | ||||
| 	</span> | ||||
| 	<div id='<?php echo $cat['id']; ?>_Submenu' class='Submenu'> | ||||
| 	<?php | ||||
| 		$default_menu_color = "ffffff"; | ||||
|  | ||||
| 		foreach($menus[$id] as $category => $menu) { | ||||
| 			if (empty($menu['link'])) { | ||||
| 				$menu['link'] = 'news'; | ||||
| 			} | ||||
| 			$link_color = '#' . (strlen($menu['color']) == 0 ? $default_menu_color : $menu['color']); | ||||
| 			?> | ||||
| 			<a href='<?php echo $menu['link_full']; ?>'<?= $menu['target_blank']?>> | ||||
| 				<div id='submenu_<?php echo str_replace('/', '_', $menu['link']); ?>' class='Submenuitem' onMouseOver='MouseOverSubmenuItem(this)' onMouseOut='MouseOutSubmenuItem(this)' style="color: <?php echo $link_color; ?>;"> | ||||
| 				<div id='submenu_<?php echo str_replace('/', '_', $menu['link']); ?>' class='Submenuitem' onMouseOver='MouseOverSubmenuItem(this)' onMouseOut='MouseOutSubmenuItem(this)' > | ||||
| 					<div class='LeftChain' style='background-image:url(<?php echo $template_path; ?>/images/general/chain.gif);'></div> | ||||
| 					<div id='ActiveSubmenuItemIcon_<?php echo str_replace('/', '_', $menu['link']); ?>' class='ActiveSubmenuItemIcon' style='background-image:url(<?php echo $template_path; ?>/images/menu/icon-activesubmenu.gif);'></div> | ||||
| 					<div class='SubmenuitemLabel' style="color: <?php echo $link_color; ?>;"><?php echo $menu['name']; ?></div> | ||||
| 					<div class='SubmenuitemLabel' <?php echo $menu['style_color']; ?>><?php echo $menu['name']; ?></div> | ||||
| 					<div class='RightChain' style='background-image:url(<?php echo $template_path; ?>/images/general/chain.gif);'></div> | ||||
| 				</div> | ||||
| 			</a> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user