mirror of
				https://github.com/slawkens/myaac.git
				synced 2025-10-30 23:46:24 +01:00 
			
		
		
		
	Compare commits
	
		
			42 Commits
		
	
	
		
			v1.8
			...
			feature/2f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | abee4b3962 | ||
|   | fbdb6890b9 | ||
|   | 041f58ed11 | ||
|   | 4eab805d26 | ||
|   | 3f24f961b1 | ||
|   | 0b86459940 | ||
|   | 7a9b11434e | ||
|   | 9725a3c2bd | ||
|   | 46adeefce3 | ||
|   | e4b66f34ac | ||
|   | 2465bb6f9a | ||
|   | 42671c5c19 | ||
|   | fec773ba4b | ||
|   | 1b9f68c9ec | ||
|   | 7a08f91d3f | ||
|   | 4b948e9510 | ||
|   | 17ca93d020 | ||
|   | bcc4b48eb0 | ||
|   | f8c4332e03 | ||
|   | 235e0f394d | ||
|   | 3451715e96 | ||
|   | d85681880e | ||
|   | 4701461b1f | ||
|   | 482f4067b2 | ||
|   | 2f26748112 | ||
|   | 98073a110a | ||
|   | 11dae90fa9 | ||
|   | 03c7dd0002 | ||
|   | 20f99903ae | ||
|   | b6e1620f14 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 9cb7792623 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 0db908be18 | ||
|   | 785d38312b | ||
|   | e435062025 | ||
|   | ecc9bd4042 | ||
|   | 797377e428 | ||
|   | 96b5df9d74 | ||
|   | b3dfc56c96 | ||
|   | 96d6e04bd2 | ||
|   | 9146eee327 | ||
|   | 3d97fa0719 | ||
|   | a66cafceab | 
| @@ -1,6 +1,22 @@ | |||||||
| # Changelog | # Changelog | ||||||
|  |  | ||||||
| ## [1.8 - 01.08.2025] | ## [1.8.1 - 05.09.2025] | ||||||
|  |  | ||||||
|  | ### Added | ||||||
|  | * New Commands: plugin:enable/disable/uninstall {plugin-name} (https://github.com/slawkens/myaac/commit/7a08f91d3fc0897c1ff76089ef3c649a2c6d2003, https://github.com/slawkens/myaac/commit/fec773ba4b740f35c0a3ef92ca8444a4c7d02082) | ||||||
|  | * Gifts: Added Transferable Coins to the store dropdown menu in the admin area (by @andreoam, #321) (https://github.com/slawkens/myaac/commit/42671c5c199dd9e91c774d8c9d30da9e12f1b695) | ||||||
|  |  | ||||||
|  | ### Changed | ||||||
|  | * Commands: Allow settings to be changed/reset by plugin name (https://github.com/slawkens/myaac/commit/f8c4332e03e838d285ea0afb4b72b7c23e324d45, https://github.com/slawkens/myaac/commit/4b948e9510f7ba69d00f84d7fdaea8b3bf05b630) | ||||||
|  | * Templates: Menus should be saved for each template separately (https://github.com/slawkens/myaac/commit/482f4067b2a2e7513d9ba214274a361ffaf123d8) | ||||||
|  |  | ||||||
|  | ### Fixed | ||||||
|  | * Online: Fix skulls display (#320) (https://github.com/slawkens/myaac/commit/98073a110ae13f9592ec9d2c4d1d1aace87587a9) | ||||||
|  | * Online: Fix if there is no world_id in the server_record table (https://github.com/slawkens/myaac/commit/b6e1620f14c20eecfc9001a7d86dfb67942985c6) (Reported by @gesior in #318) | ||||||
|  | * tibiacom: some fixes to menus (https://github.com/slawkens/myaac/commit/20f99903ae80c74ad66c1cf5a5ea8d0b0fc2fd70, https://github.com/slawkens/myaac/commit/11dae90fa94fbbf47447017db5e5847c33d6aadf) | ||||||
|  | * Guilds: Fix for some servers that don't have guild_invites table (https://github.com/slawkens/myaac/commit/9725a3c2bdb7003f5cb48febb77604c31a9b805b) | ||||||
|  |  | ||||||
|  | ## [1.8 - 02.08.2025] | ||||||
|  |  | ||||||
| ### Added | ### Added | ||||||
| * Templates - Kathrine: Possibility to add custom menu categories (https://github.com/slawkens/myaac/commit/ec11c1402417c25980582467546d1c1e9bb8267f) | * Templates - Kathrine: Possibility to add custom menu categories (https://github.com/slawkens/myaac/commit/ec11c1402417c25980582467546d1c1e9bb8267f) | ||||||
|   | |||||||
| @@ -26,8 +26,8 @@ | |||||||
| if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is required.'); | if (version_compare(phpversion(), '8.1', '<')) die('PHP version 8.1 or higher is required.'); | ||||||
|  |  | ||||||
| const MYAAC = true; | const MYAAC = true; | ||||||
| const MYAAC_VERSION = '1.8'; | const MYAAC_VERSION = '1.8.2-dev'; | ||||||
| const DATABASE_VERSION = 45; | const DATABASE_VERSION = 46; | ||||||
| const TABLE_PREFIX = 'myaac_'; | const TABLE_PREFIX = 'myaac_'; | ||||||
| define('START_TIME', microtime(true)); | define('START_TIME', microtime(true)); | ||||||
| define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX')); | define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX')); | ||||||
|   | |||||||
| @@ -19,7 +19,8 @@ | |||||||
|         "symfony/var-dumper": "^6.4", |         "symfony/var-dumper": "^6.4", | ||||||
|         "filp/whoops": "^2.15", |         "filp/whoops": "^2.15", | ||||||
|         "maximebf/debugbar": "1.*", |         "maximebf/debugbar": "1.*", | ||||||
|         "guzzlehttp/guzzle": "7.9.3" |         "guzzlehttp/guzzle": "7.9.3", | ||||||
|  |         "spomky-labs/otphp": "^11.3" | ||||||
|     }, |     }, | ||||||
|     "require-dev": { |     "require-dev": { | ||||||
|         "phpstan/phpstan": "^1.10" |         "phpstan/phpstan": "^1.10" | ||||||
|   | |||||||
							
								
								
									
										151
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										151
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							| @@ -4,7 +4,7 @@ | |||||||
|         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", |         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | ||||||
|         "This file is @generated automatically" |         "This file is @generated automatically" | ||||||
|     ], |     ], | ||||||
|     "content-hash": "5317e97a5025ebc2a977214bd3fa964c", |     "content-hash": "07419f6fe133f9bebc99557f3df843c8", | ||||||
|     "packages": [ |     "packages": [ | ||||||
|         { |         { | ||||||
|             "name": "brick/math", |             "name": "brick/math", | ||||||
| @@ -1556,6 +1556,73 @@ | |||||||
|             }, |             }, | ||||||
|             "time": "2018-02-13T20:26:39+00:00" |             "time": "2018-02-13T20:26:39+00:00" | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             "name": "paragonie/constant_time_encoding", | ||||||
|  |             "version": "v3.0.0", | ||||||
|  |             "source": { | ||||||
|  |                 "type": "git", | ||||||
|  |                 "url": "https://github.com/paragonie/constant_time_encoding.git", | ||||||
|  |                 "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" | ||||||
|  |             }, | ||||||
|  |             "dist": { | ||||||
|  |                 "type": "zip", | ||||||
|  |                 "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", | ||||||
|  |                 "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", | ||||||
|  |                 "shasum": "" | ||||||
|  |             }, | ||||||
|  |             "require": { | ||||||
|  |                 "php": "^8" | ||||||
|  |             }, | ||||||
|  |             "require-dev": { | ||||||
|  |                 "phpunit/phpunit": "^9", | ||||||
|  |                 "vimeo/psalm": "^4|^5" | ||||||
|  |             }, | ||||||
|  |             "type": "library", | ||||||
|  |             "autoload": { | ||||||
|  |                 "psr-4": { | ||||||
|  |                     "ParagonIE\\ConstantTime\\": "src/" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             "notification-url": "https://packagist.org/downloads/", | ||||||
|  |             "license": [ | ||||||
|  |                 "MIT" | ||||||
|  |             ], | ||||||
|  |             "authors": [ | ||||||
|  |                 { | ||||||
|  |                     "name": "Paragon Initiative Enterprises", | ||||||
|  |                     "email": "security@paragonie.com", | ||||||
|  |                     "homepage": "https://paragonie.com", | ||||||
|  |                     "role": "Maintainer" | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                     "name": "Steve 'Sc00bz' Thomas", | ||||||
|  |                     "email": "steve@tobtu.com", | ||||||
|  |                     "homepage": "https://www.tobtu.com", | ||||||
|  |                     "role": "Original Developer" | ||||||
|  |                 } | ||||||
|  |             ], | ||||||
|  |             "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", | ||||||
|  |             "keywords": [ | ||||||
|  |                 "base16", | ||||||
|  |                 "base32", | ||||||
|  |                 "base32_decode", | ||||||
|  |                 "base32_encode", | ||||||
|  |                 "base64", | ||||||
|  |                 "base64_decode", | ||||||
|  |                 "base64_encode", | ||||||
|  |                 "bin2hex", | ||||||
|  |                 "encoding", | ||||||
|  |                 "hex", | ||||||
|  |                 "hex2bin", | ||||||
|  |                 "rfc4648" | ||||||
|  |             ], | ||||||
|  |             "support": { | ||||||
|  |                 "email": "info@paragonie.com", | ||||||
|  |                 "issues": "https://github.com/paragonie/constant_time_encoding/issues", | ||||||
|  |                 "source": "https://github.com/paragonie/constant_time_encoding" | ||||||
|  |             }, | ||||||
|  |             "time": "2024-05-08T12:36:18+00:00" | ||||||
|  |         }, | ||||||
|         { |         { | ||||||
|             "name": "peppeocchi/php-cron-scheduler", |             "name": "peppeocchi/php-cron-scheduler", | ||||||
|             "version": "v4.0", |             "version": "v4.0", | ||||||
| @@ -2102,6 +2169,88 @@ | |||||||
|             }, |             }, | ||||||
|             "time": "2019-03-08T08:55:37+00:00" |             "time": "2019-03-08T08:55:37+00:00" | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             "name": "spomky-labs/otphp", | ||||||
|  |             "version": "11.3.0", | ||||||
|  |             "source": { | ||||||
|  |                 "type": "git", | ||||||
|  |                 "url": "https://github.com/Spomky-Labs/otphp.git", | ||||||
|  |                 "reference": "2d8ccb5fc992b9cc65ef321fa4f00fefdb3f4b33" | ||||||
|  |             }, | ||||||
|  |             "dist": { | ||||||
|  |                 "type": "zip", | ||||||
|  |                 "url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/2d8ccb5fc992b9cc65ef321fa4f00fefdb3f4b33", | ||||||
|  |                 "reference": "2d8ccb5fc992b9cc65ef321fa4f00fefdb3f4b33", | ||||||
|  |                 "shasum": "" | ||||||
|  |             }, | ||||||
|  |             "require": { | ||||||
|  |                 "ext-mbstring": "*", | ||||||
|  |                 "paragonie/constant_time_encoding": "^2.0 || ^3.0", | ||||||
|  |                 "php": ">=8.1", | ||||||
|  |                 "psr/clock": "^1.0", | ||||||
|  |                 "symfony/deprecation-contracts": "^3.2" | ||||||
|  |             }, | ||||||
|  |             "require-dev": { | ||||||
|  |                 "ekino/phpstan-banned-code": "^1.0", | ||||||
|  |                 "infection/infection": "^0.26|^0.27|^0.28|^0.29", | ||||||
|  |                 "php-parallel-lint/php-parallel-lint": "^1.3", | ||||||
|  |                 "phpstan/phpstan": "^1.0", | ||||||
|  |                 "phpstan/phpstan-deprecation-rules": "^1.0", | ||||||
|  |                 "phpstan/phpstan-phpunit": "^1.0", | ||||||
|  |                 "phpstan/phpstan-strict-rules": "^1.0", | ||||||
|  |                 "phpunit/phpunit": "^9.5.26|^10.0|^11.0", | ||||||
|  |                 "qossmic/deptrac-shim": "^1.0", | ||||||
|  |                 "rector/rector": "^1.0", | ||||||
|  |                 "symfony/phpunit-bridge": "^6.1|^7.0", | ||||||
|  |                 "symplify/easy-coding-standard": "^12.0" | ||||||
|  |             }, | ||||||
|  |             "type": "library", | ||||||
|  |             "autoload": { | ||||||
|  |                 "psr-4": { | ||||||
|  |                     "OTPHP\\": "src/" | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             "notification-url": "https://packagist.org/downloads/", | ||||||
|  |             "license": [ | ||||||
|  |                 "MIT" | ||||||
|  |             ], | ||||||
|  |             "authors": [ | ||||||
|  |                 { | ||||||
|  |                     "name": "Florent Morselli", | ||||||
|  |                     "homepage": "https://github.com/Spomky" | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                     "name": "All contributors", | ||||||
|  |                     "homepage": "https://github.com/Spomky-Labs/otphp/contributors" | ||||||
|  |                 } | ||||||
|  |             ], | ||||||
|  |             "description": "A PHP library for generating one time passwords according to RFC 4226 (HOTP Algorithm) and the RFC 6238 (TOTP Algorithm) and compatible with Google Authenticator", | ||||||
|  |             "homepage": "https://github.com/Spomky-Labs/otphp", | ||||||
|  |             "keywords": [ | ||||||
|  |                 "FreeOTP", | ||||||
|  |                 "RFC 4226", | ||||||
|  |                 "RFC 6238", | ||||||
|  |                 "google authenticator", | ||||||
|  |                 "hotp", | ||||||
|  |                 "otp", | ||||||
|  |                 "totp" | ||||||
|  |             ], | ||||||
|  |             "support": { | ||||||
|  |                 "issues": "https://github.com/Spomky-Labs/otphp/issues", | ||||||
|  |                 "source": "https://github.com/Spomky-Labs/otphp/tree/11.3.0" | ||||||
|  |             }, | ||||||
|  |             "funding": [ | ||||||
|  |                 { | ||||||
|  |                     "url": "https://github.com/Spomky", | ||||||
|  |                     "type": "github" | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                     "url": "https://www.patreon.com/FlorentMorselli", | ||||||
|  |                     "type": "patreon" | ||||||
|  |                 } | ||||||
|  |             ], | ||||||
|  |             "time": "2024-06-12T11:22:32+00:00" | ||||||
|  |         }, | ||||||
|         { |         { | ||||||
|             "name": "symfony/console", |             "name": "symfony/console", | ||||||
|             "version": "v6.4.17", |             "version": "v6.4.17", | ||||||
|   | |||||||
| @@ -10,6 +10,15 @@ CREATE TABLE `myaac_account_actions` | |||||||
| 	KEY (`account_id`) | 	KEY (`account_id`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; | ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; | ||||||
|  |  | ||||||
|  | CREATE TABLE `myaac_account_email_codes` | ||||||
|  | ( | ||||||
|  | 	`id` int(11) NOT NULL AUTO_INCREMENT, | ||||||
|  | 	`account_id` int NOT NULL, | ||||||
|  | 	`code` varchar(6) NOT NULL, | ||||||
|  | 	`created_at` int NOT NULL, | ||||||
|  | 	PRIMARY KEY (`id`) | ||||||
|  | ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; | ||||||
|  |  | ||||||
| CREATE TABLE `myaac_admin_menu` | CREATE TABLE `myaac_admin_menu` | ||||||
| ( | ( | ||||||
| 	`id` int NOT NULL AUTO_INCREMENT, | 	`id` int NOT NULL AUTO_INCREMENT, | ||||||
|   | |||||||
| @@ -42,45 +42,44 @@ if(!$error) { | |||||||
| 	$configToSave['cache_prefix'] = 'myaac_' . generateRandomString(8, true, false, true); | 	$configToSave['cache_prefix'] = 'myaac_' . generateRandomString(8, true, false, true); | ||||||
| 	$configToSave['database_auto_migrate'] = true; | 	$configToSave['database_auto_migrate'] = true; | ||||||
|  |  | ||||||
| 	if(!$error) { | 	$content = ''; | ||||||
| 		$content = ''; | 	$saved = Settings::saveConfig($configToSave, BASE . 'config.local.php', $content); | ||||||
| 		$saved = Settings::saveConfig($configToSave, BASE . 'config.local.php', $content); | 	if ($saved || file_exists(BASE . 'config.local.php')) { | ||||||
| 		if ($saved) { | 		success($locale['step_database_config_saved']); | ||||||
| 			success($locale['step_database_config_saved']); | 		$_SESSION['saved'] = true; | ||||||
| 			$_SESSION['saved'] = true; |  | ||||||
|  |  | ||||||
| 			require BASE . 'config.local.php'; | 		require BASE . 'config.local.php'; | ||||||
| 			require BASE . 'install/includes/config.php'; | 		require BASE . 'install/includes/config.php'; | ||||||
|  |  | ||||||
| 			if (!$error) { | 		if (!$error) { | ||||||
| 				require BASE . 'install/includes/database.php'; | 			require BASE . 'install/includes/database.php'; | ||||||
|  |  | ||||||
| 				if (isset($database_error)) { // we failed connect to the database | 			if (isset($database_error)) { // we failed connect to the database | ||||||
| 					error($database_error); | 				error($database_error); | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				if (!$db->hasTable('accounts')) { | ||||||
|  | 					$tmp = str_replace('$TABLE$', 'accounts', $locale['step_database_error_table']); | ||||||
|  | 					error($tmp); | ||||||
|  | 					$error = true; | ||||||
| 				} | 				} | ||||||
| 				else { |  | ||||||
| 					if (!$db->hasTable('accounts')) { |  | ||||||
| 						$tmp = str_replace('$TABLE$', 'accounts', $locale['step_database_error_table']); |  | ||||||
| 						error($tmp); |  | ||||||
| 						$error = true; |  | ||||||
| 					} |  | ||||||
|  |  | ||||||
| 					if (!$error) { | 				if (!$error) { | ||||||
| 						$twig->display('install.installer.html.twig', array( | 					$twig->display('install.installer.html.twig', array( | ||||||
| 							'url' => 'tools/5-database.php', | 						'url' => 'tools/5-database.php', | ||||||
| 							'message' => $locale['loading_spinner'] | 						'message' => $locale['loading_spinner'] | ||||||
| 						)); | 					)); | ||||||
| 					} |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} else { |  | ||||||
| 			$_SESSION['config_content'] = $content; |  | ||||||
| 			unset($_SESSION['saved']); |  | ||||||
|  |  | ||||||
| 			$locale['step_database_error_file'] = str_replace('$FILE$', '<b>' . BASE . 'config.php</b>', $locale['step_database_error_file']); |  | ||||||
| 			error($locale['step_database_error_file'] . '<br/> |  | ||||||
| 				<textarea cols="70" rows="10">' . $content . '</textarea>'); |  | ||||||
| 		} | 		} | ||||||
|  | 	} else { | ||||||
|  | 		$error = true; | ||||||
|  | 		$_SESSION['config_content'] = $content; | ||||||
|  | 		unset($_SESSION['saved']); | ||||||
|  |  | ||||||
|  | 		$locale['step_database_error_file'] = str_replace('$FILE$', '<b>' . BASE . 'config.local.php</b>', $locale['step_database_error_file']); | ||||||
|  | 		error($locale['step_database_error_file'] . '<br/> | ||||||
|  | 			<textarea cols="70" rows="10">' . $content . '</textarea>'); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| ?> | ?> | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -976,15 +976,16 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/form-data": { |     "node_modules/form-data": { | ||||||
|       "version": "4.0.2", |       "version": "4.0.4", | ||||||
|       "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", |       "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", | ||||||
|       "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", |       "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "asynckit": "^0.4.0", |         "asynckit": "^0.4.0", | ||||||
|         "combined-stream": "^1.0.8", |         "combined-stream": "^1.0.8", | ||||||
|         "es-set-tostringtag": "^2.1.0", |         "es-set-tostringtag": "^2.1.0", | ||||||
|  |         "hasown": "^2.0.2", | ||||||
|         "mime-types": "^2.1.12" |         "mime-types": "^2.1.12" | ||||||
|       }, |       }, | ||||||
|       "engines": { |       "engines": { | ||||||
| @@ -2084,9 +2085,9 @@ | |||||||
|       "license": "MIT" |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|     "node_modules/tmp": { |     "node_modules/tmp": { | ||||||
|       "version": "0.2.3", |       "version": "0.2.4", | ||||||
|       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", |       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", | ||||||
|       "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", |       "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								system/migrations/46-account_email_codes.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								system/migrations/46-account_email_codes.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | CREATE TABLE `myaac_account_email_codes` | ||||||
|  | ( | ||||||
|  | 	`id` int(11) NOT NULL AUTO_INCREMENT, | ||||||
|  | 	`account_id` int NOT NULL, | ||||||
|  | 	`code` varchar(6) NOT NULL, | ||||||
|  | 	`created_at` int NOT NULL, | ||||||
|  | 	PRIMARY KEY (`id`) | ||||||
|  | ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8mb4; | ||||||
							
								
								
									
										27
									
								
								system/migrations/46.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								system/migrations/46.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | <?php | ||||||
|  | // add the myaac_account_email_codes | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @var OTS_DB_MySQL $db | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | $up = function () use ($db) { | ||||||
|  | 	if (!$db->hasColumn('accounts', '2fa_type')) { | ||||||
|  | 		$db->addColumn('accounts', '2fa_type', "tinyint NOT NULL DEFAULT 0 AFTER `web_flags`"); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// add myaac_account_email_codes table | ||||||
|  | 	if (!$db->hasTable(TABLE_PREFIX . 'account_email_codes')) { | ||||||
|  | 		$db->exec(file_get_contents(__DIR__ . '/46-account_email_codes.sql')); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | $down = function () use ($db) { | ||||||
|  | 	if ($db->hasColumn('accounts', '2fa_type')) { | ||||||
|  | 		$db->dropColumn('accounts', '2fa_type'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	//if ($db->hasTable(TABLE_PREFIX . 'account_email_codes')) { | ||||||
|  | 	//	$db->dropTable(TABLE_PREFIX . 'account_email_codes'); | ||||||
|  | 	//} | ||||||
|  | }; | ||||||
							
								
								
									
										124
									
								
								system/pages/account/2fa.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								system/pages/account/2fa.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | |||||||
|  | <?php | ||||||
|  | /** | ||||||
|  |  * 2-factor authentication | ||||||
|  |  * | ||||||
|  |  * @package   MyAAC | ||||||
|  |  * @author    Slawkens <slawkens@gmail.com> | ||||||
|  |  * @copyright 2019 MyAAC | ||||||
|  |  * @link      https://my-aac.org | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | use MyAAC\TwoFactorAuth\TwoFactorAuth; | ||||||
|  |  | ||||||
|  | defined('MYAAC') or die('Direct access not allowed!'); | ||||||
|  |  | ||||||
|  | $title = 'Two Factor Authentication'; | ||||||
|  | require __DIR__ . '/base.php'; | ||||||
|  |  | ||||||
|  | csrfProtect(); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @var OTS_Account $account_logged | ||||||
|  |  */ | ||||||
|  | $step = $_REQUEST['step'] ?? ''; | ||||||
|  | $code = $_REQUEST['auth-code'] ?? ''; | ||||||
|  |  | ||||||
|  | if ((!setting('core.mail_enabled')) && ACTION == 'email-code') { | ||||||
|  | 	$twig->display('error_box.html.twig',  ['errors' => ['Account two-factor e-mail authentication disabled.']]); | ||||||
|  | 	return; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | if (!isset($account_logged) || !$account_logged->isLoaded()) { | ||||||
|  | 	$current_session = getSession('account'); | ||||||
|  | 	if($current_session) { | ||||||
|  | 		$account_logged = new OTS_Account(); | ||||||
|  | 		$account_logged->load($current_session); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | $twoFactorAuth = TwoFactorAuth::getInstance($account_logged); | ||||||
|  | $twig->addGlobal('account_logged', $account_logged); | ||||||
|  |  | ||||||
|  | if (ACTION == 'email-code') { | ||||||
|  | 	if ($step == 'resend') { | ||||||
|  | 		if ($twoFactorAuth->hasRecentEmailCode(15 * 60)) { | ||||||
|  | 			$errors = ['Sorry, one email per 15 minutes']; | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			$twoFactorAuth->resendEmailCode(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (!empty($errors)) { | ||||||
|  | 			$twig->display('error_box.html.twig',  ['errors' => $errors]); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		$twig->display('account.2fa.email.login.html.twig'); | ||||||
|  | 	} | ||||||
|  | 	else if ($step == 'activate') { | ||||||
|  | 		if (!$twoFactorAuth->hasRecentEmailCode(15 * 60)) { | ||||||
|  | 			$twoFactorAuth->resendEmailCode(); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (isset($_POST['save'])) { | ||||||
|  | 			if (!empty($code)) { | ||||||
|  | 				$twoFactorAuth->setAuthGateway(TwoFactorAuth::TYPE_EMAIL); | ||||||
|  | 				if ($twoFactorAuth->getAuthGateway()->verifyCode($code)) { | ||||||
|  | 					$serverName = configLua('serverName'); | ||||||
|  |  | ||||||
|  | 					$twoFactorAuth->enable(TwoFactorAuth::TYPE_EMAIL); | ||||||
|  | 					$twoFactorAuth->deleteOldCodes(); | ||||||
|  |  | ||||||
|  | 					$twig->display('success.html.twig', [ | ||||||
|  | 						'title' => 'Email Code Authentication Activated', | ||||||
|  | 						'description' => sprintf('You have successfully activated <b>email code authentication</b> for your account. This means an <b>email code</b> will be sent to the email address assigned to your account whenever you try to log in to the %s client or the %s website. In order to log in, you will need to enter the <b>most recent email code</b> you have received.', $serverName, $serverName) | ||||||
|  | 					]); | ||||||
|  |  | ||||||
|  | 					return; | ||||||
|  | 				} | ||||||
|  | 				else { | ||||||
|  | 					$errors[] = 'Invalid email code!'; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (!empty($errors)) { | ||||||
|  | 			$twig->display('error_box.html.twig', ['errors' => $errors]); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		$twig->display('account.2fa.email_code.html.twig', ['wrongCode' => count($errors) > 0]); | ||||||
|  | 	} | ||||||
|  | 	else if ($step == 'deactivate') { | ||||||
|  | 		//if (!$twoFactorAuth->hasRecentEmailCode(15 * 60)) { | ||||||
|  | 		//	$twoFactorAuth->resendEmailCode(); | ||||||
|  | 		//} | ||||||
|  |  | ||||||
|  | 		/*if (isset($_POST['save'])) { | ||||||
|  | 			if (!empty($code)) { | ||||||
|  | 				if ($twoFactorAuth->getAuthGateway()->verifyCode($code)) { | ||||||
|  | */ | ||||||
|  | 					$twoFactorAuth->disable(); | ||||||
|  | 					$twoFactorAuth->deleteOldCodes(); | ||||||
|  |  | ||||||
|  | 					$twig->display('success.html.twig', | ||||||
|  | 						[ | ||||||
|  | 							'title' => 'Email Code Authentication Deactivated', | ||||||
|  | 							'description' => 'You have successfully <b>deactivated</b> the <b>Email Code Authentication</b> for your account.' | ||||||
|  | 						] | ||||||
|  | 					); | ||||||
|  | 					/* | ||||||
|  | 				} | ||||||
|  | 				else { | ||||||
|  | 					$errors[] = 'Invalid email code!'; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}*/ | ||||||
|  |  | ||||||
|  | 		/* | ||||||
|  | 		if (!empty($errors)) { | ||||||
|  | 			$twig->display('error_box.html.twig', ['errors' => $errors]); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		$twig->display('account.2fa.email.deactivate.html.twig', ['wrongCode' => count($errors) > 0]); | ||||||
|  | 		*/ | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -17,6 +17,10 @@ if(!$logged) | |||||||
| 	if(!empty($errors)) | 	if(!empty($errors)) | ||||||
| 		$twig->display('error_box.html.twig', array('errors' => $errors)); | 		$twig->display('error_box.html.twig', array('errors' => $errors)); | ||||||
|  |  | ||||||
|  | 	if (defined('HIDE_LOGIN_BOX') && HIDE_LOGIN_BOX) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	$twig->display('account.login.html.twig', array( | 	$twig->display('account.login.html.twig', array( | ||||||
| 		'redirect' => $_REQUEST['redirect'] ?? null, | 		'redirect' => $_REQUEST['redirect'] ?? null, | ||||||
| 		'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number', | 		'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number', | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| use MyAAC\RateLimit; | use MyAAC\RateLimit; | ||||||
|  | use MyAAC\TwoFactorAuth\TwoFactorAuth; | ||||||
|  |  | ||||||
| defined('MYAAC') or die('Direct access not allowed!'); | defined('MYAAC') or die('Direct access not allowed!'); | ||||||
|  |  | ||||||
| @@ -50,8 +51,14 @@ if(!empty($login_account) && !empty($login_password)) | |||||||
| 		if (setting('core.account_mail_verify') && (int)$account_logged->getCustomField('email_verified') !== 1) { | 		if (setting('core.account_mail_verify') && (int)$account_logged->getCustomField('email_verified') !== 1) { | ||||||
| 			$errors[] = 'Your account is not verified. Please verify your email address. If the message is not coming check the SPAM folder in your E-Mail client.'; | 			$errors[] = 'Your account is not verified. Please verify your email address. If the message is not coming check the SPAM folder in your E-Mail client.'; | ||||||
| 		} else { | 		} else { | ||||||
| 			session_regenerate_id(); |  | ||||||
| 			setSession('account', $account_logged->getId()); | 			setSession('account', $account_logged->getId()); | ||||||
|  |  | ||||||
|  | 			$twoFactorAuth = TwoFactorAuth::getInstance($account_logged); | ||||||
|  | 			if (!$twoFactorAuth->process($login_account, $login_password, $remember_me, $_POST['auth-code'] ?? '')) { | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			session_regenerate_id(); | ||||||
| 			setSession('password', encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password)); | 			setSession('password', encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password)); | ||||||
| 			if($remember_me) { | 			if($remember_me) { | ||||||
| 				setSession('remember_me', true); | 				setSession('remember_me', true); | ||||||
|   | |||||||
| @@ -8,6 +8,9 @@ | |||||||
|  * @copyright 2019 MyAAC |  * @copyright 2019 MyAAC | ||||||
|  * @link      https://my-aac.org |  * @link      https://my-aac.org | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | use MyAAC\TwoFactorAuth\TwoFactorAuth; | ||||||
|  |  | ||||||
| defined('MYAAC') or die('Direct access not allowed!'); | defined('MYAAC') or die('Direct access not allowed!'); | ||||||
|  |  | ||||||
| $title = 'Account Management'; | $title = 'Account Management'; | ||||||
| @@ -111,6 +114,8 @@ $twig->display('account.management.html.twig', array( | |||||||
| 	'account_registered' => $account_registered, | 	'account_registered' => $account_registered, | ||||||
| 	'account_rlname' => $account_rlname, | 	'account_rlname' => $account_rlname, | ||||||
| 	'account_location' => $account_location, | 	'account_location' => $account_location, | ||||||
|  | 	'twoFactorViews' => TwoFactorAuth::getInstance($account_logged)->getAccountManageViews(), | ||||||
|  |  | ||||||
| 	'actions' => $actions, | 	'actions' => $actions, | ||||||
| 	'players' => $account_players | 	'players' => $account_players, | ||||||
| )); | )); | ||||||
|   | |||||||
| @@ -23,6 +23,12 @@ if(!Validator::guildName($guild_name)) { | |||||||
| 	$errors[] = Validator::getLastError(); | 	$errors[] = Validator::getLastError(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | if (!$db->hasTableAndColumns('guild_invites', ['player_id'])) { | ||||||
|  | 	$errors[] = "Guild invite is not possible on this website."; | ||||||
|  | 	$twig->display('error_box.html.twig', ['errors' => $errors]); | ||||||
|  | 	return; | ||||||
|  | } | ||||||
|  |  | ||||||
| if(empty($errors)) { | if(empty($errors)) { | ||||||
| 	$guild = new OTS_Guild(); | 	$guild = new OTS_Guild(); | ||||||
| 	$guild->find($guild_name); | 	$guild->find($guild_name); | ||||||
| @@ -58,7 +64,7 @@ if(empty($errors)) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| if(!$guild_vice) { | if(empty($errors) && !$guild_vice) { | ||||||
| 	$errors[] = 'You are not a leader or vice leader of guild <b>'.$guild_name.'</b>.'.$level_in_guild; | 	$errors[] = 'You are not a leader or vice leader of guild <b>'.$guild_name.'</b>.'.$level_in_guild; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -84,6 +90,7 @@ if(isset($_POST['todo']) && $_POST['todo'] == 'save') { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| if(empty($errors)) { | if(empty($errors)) { | ||||||
| 	include(SYSTEM . 'libs/pot/InvitesDriver.php'); | 	include(SYSTEM . 'libs/pot/InvitesDriver.php'); | ||||||
| 	new InvitesDriver($guild); | 	new InvitesDriver($guild); | ||||||
| @@ -104,6 +111,7 @@ if(!empty($errors)) { | |||||||
| else { | else { | ||||||
| 	if(isset($_POST['todo']) && $_POST['todo'] == 'save') { | 	if(isset($_POST['todo']) && $_POST['todo'] == 'save') { | ||||||
| 		$guild->invite($player); | 		$guild->invite($player); | ||||||
|  |  | ||||||
| 		$twig->display('success.html.twig', array( | 		$twig->display('success.html.twig', array( | ||||||
| 			'title' => 'Invite player', | 			'title' => 'Invite player', | ||||||
| 			'description' => 'Player with name <b>' . $player->getName() . '</b> has been invited to your guild.', | 			'description' => 'Player with name <b>' . $player->getName() . '</b> has been invited to your guild.', | ||||||
|   | |||||||
| @@ -121,25 +121,28 @@ foreach($rank_list as $rank) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| include(SYSTEM . 'libs/pot/InvitesDriver.php'); | $invited_list = []; | ||||||
| new InvitesDriver($guild); |  | ||||||
| $invited_list = $guild->listInvites(); |  | ||||||
| $show_accept_invite = 0; | $show_accept_invite = 0; | ||||||
| if($logged && count($invited_list) > 0) |  | ||||||
| { | if ($db->hasTableAndColumns('guild_invites', ['player_id'])) { | ||||||
| 	foreach($invited_list as $invited_player) | 	include(SYSTEM . 'libs/pot/InvitesDriver.php'); | ||||||
| 	{ | 	new InvitesDriver($guild); | ||||||
| 		if(count($account_players) > 0) | 	$invited_list = $guild->listInvites(); | ||||||
| 		{ |  | ||||||
| 			foreach($account_players as $player_from_acc) | 	if($logged && count($invited_list) > 0) { | ||||||
| 			{ | 		foreach($invited_list as $invited_player) { | ||||||
| 				if($player_from_acc->isLoaded() && $invited_player->isLoaded() && $player_from_acc->getName() == $invited_player->getName()) | 			if(count($account_players) > 0) { | ||||||
| 					$show_accept_invite++; | 				foreach($account_players as $player_from_acc) { | ||||||
|  | 					if($player_from_acc->isLoaded() && $invited_player->isLoaded() && $player_from_acc->getName() == $invited_player->getName()) { | ||||||
|  | 						$show_accept_invite++; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| $useGuildNick = $db->hasTable('guild_members') || $db->hasTable('guild_membership') || $db->hasColumn('players', 'guildnick'); | $useGuildNick = $db->hasTable('guild_members') || $db->hasTable('guild_membership') || $db->hasColumn('players', 'guildnick'); | ||||||
|  |  | ||||||
| $twig->display('guilds.view.html.twig', array( | $twig->display('guilds.view.html.twig', array( | ||||||
|   | |||||||
| @@ -105,8 +105,17 @@ $cached = Cache::remember("online_$order", setting('core.online_cache_ttl') * 60 | |||||||
| 			$result = null; | 			$result = null; | ||||||
| 			$timestamp = false; | 			$timestamp = false; | ||||||
| 			if($db->hasTable('server_record')) { | 			if($db->hasTable('server_record')) { | ||||||
| 				$timestamp = true; | 				$timestamp = $db->hasColumn('server_record', 'timestamp'); | ||||||
| 				$result = ServerRecord::where('world_id', configLua('worldId'))->orderByDesc('record')->first()->toArray(); | 				$serverRecordQuery = ServerRecord::query(); | ||||||
|  |  | ||||||
|  | 				if ($db->hasColumn('server_record', 'world_id')) { | ||||||
|  | 					$serverRecordQuery->where('world_id', configLua('worldId')); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				$result = $serverRecordQuery->orderByDesc('record')->first(); | ||||||
|  | 				if ($result) { | ||||||
|  | 					$result = $result->toArray(); | ||||||
|  | 				} | ||||||
| 			} else if($db->hasTable('server_config')) { // tfs 1.0 | 			} else if($db->hasTable('server_config')) { // tfs 1.0 | ||||||
| 				$row = ServerConfig::where('config', 'players_record')->first(); | 				$row = ServerConfig::where('config', 'players_record')->first(); | ||||||
| 				if ($row) { | 				if ($row) { | ||||||
|   | |||||||
| @@ -94,19 +94,30 @@ $dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r) | |||||||
| 		$routesFinal[] = ['*', $page, '__database__/' . $page, 100]; | 		$routesFinal[] = ['*', $page, '__database__/' . $page, 100]; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	$routes = require SYSTEM . 'routes.php'; | ||||||
| 	Plugins::clearWarnings(); | 	Plugins::clearWarnings(); | ||||||
| 	foreach (Plugins::getRoutes() as $route) { |  | ||||||
| 		$routesFinal[] = [$route[0], $route[1], $route[2], $route[3] ?? 1000]; | 	foreach (Plugins::getRoutes() as $pluginRoute) { | ||||||
|  |  | ||||||
|  | 		$routesFinal[] = [$pluginRoute[0], $pluginRoute[1], $pluginRoute[2], $pluginRoute[3] ?? 1000]; | ||||||
|  |  | ||||||
|  | 		// Possibility to override routes with plugins pages, like characters.php | ||||||
|  | 		foreach ($routes as &$route) { | ||||||
|  | 			if (str_contains($pluginRoute[2], 'pages/' . $route[2])) { | ||||||
|  | 				$route[2] = $pluginRoute[2]; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| /* | /* | ||||||
| 		echo '<pre>'; | 		echo '<pre>'; | ||||||
| 		var_dump($route[1], $route[3], $route[2]); | 		var_dump($pluginRoute[1], $pluginRoute[3], $pluginRoute[2]); | ||||||
| 		echo '/<pre>'; | 		echo '/<pre>'; | ||||||
| */ | */ | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	$routes = require SYSTEM . 'routes.php'; |  | ||||||
| 	foreach ($routes as $route) { | 	foreach ($routes as $route) { | ||||||
| 		if (!str_contains($route[2], '__redirect__') && !str_contains($route[2], '__database__')) { | 		if (!str_contains($route[2], '__redirect__') && !str_contains($route[2], '__database__') | ||||||
|  | 			&& !str_contains($route[2], 'plugins/') | ||||||
|  | 		) { | ||||||
| 			if (!is_file(BASE . 'system/pages/' . $route[2])) { | 			if (!is_file(BASE . 'system/pages/' . $route[2])) { | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|   | |||||||
| @@ -28,6 +28,15 @@ if (!IS_CLI) { | |||||||
| 	$siteURL = $serverUrl . $baseDir; | 	$siteURL = $serverUrl . $baseDir; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | $donateColumnOptions = [ | ||||||
|  | 	'premium_points' => 'Premium Points', | ||||||
|  | 	'coins' => 'Coins', | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | if (defined('HAS_ACCOUNT_COINS_TRANSFERABLE') && (HAS_ACCOUNT_COINS_TRANSFERABLE || HAS_ACCOUNT_TRANSFERABLE_COINS)) { | ||||||
|  | 	$donateColumnOptions[ACCOUNT_COINS_TRANSFERABLE_COLUMN] = 'Coins Transferable'; | ||||||
|  | } | ||||||
|  |  | ||||||
| return [ | return [ | ||||||
| 	'name' => 'MyAAC', | 	'name' => 'MyAAC', | ||||||
| 	'settings' => [ | 	'settings' => [ | ||||||
| @@ -1295,7 +1304,7 @@ Sent by MyAAC,<br/> | |||||||
| 			'name' => 'Data Center', | 			'name' => 'Data Center', | ||||||
| 			'type' => 'text', | 			'type' => 'text', | ||||||
| 			'desc' => 'Server Location, will be shown on online page', | 			'desc' => 'Server Location, will be shown on online page', | ||||||
| 			'default' => 'Frankfurt - Germany', | 			'default' => 'Poland - Warsaw', | ||||||
| 		], | 		], | ||||||
| 		[ | 		[ | ||||||
| 			'type' => 'section', | 			'type' => 'section', | ||||||
| @@ -1598,13 +1607,14 @@ Sent by MyAAC,<br/> | |||||||
| 			'name' => 'Donate Column', | 			'name' => 'Donate Column', | ||||||
| 			'type' => 'options', | 			'type' => 'options', | ||||||
| 			'desc' => 'What to give to player after donation - what column in accounts table to use.', | 			'desc' => 'What to give to player after donation - what column in accounts table to use.', | ||||||
| 			'options' => ['premium_points' => 'Premium Points', 'coins' => 'Coins'], | 			'options' => $donateColumnOptions, | ||||||
| 			'default' => 'premium_points', | 			'default' => 'premium_points', | ||||||
| 			'callbacks' => [ | 			'callbacks' => [ | ||||||
| 				'beforeSave' => function($key, $value, &$errorMessage) { | 				'beforeSave' => function($key, $value, &$errorMessage) { | ||||||
| 					global $db; | 					global $db; | ||||||
| 					if ($value == 'coins' && !HAS_ACCOUNT_COINS) { |  | ||||||
| 						$errorMessage = "Shop: Donate Column: Cannot set column to coins, because it doesn't exist in database."; | 					if (!$db->hasColumn('accounts', $value)) { | ||||||
|  | 						$errorMessage = "Shop: Donate Column: Cannot set column to $value, because it doesn't exist in database."; | ||||||
| 						return false; | 						return false; | ||||||
| 					} | 					} | ||||||
| 					return true; | 					return true; | ||||||
|   | |||||||
| @@ -45,6 +45,22 @@ class MigrateRunCommand extends Command | |||||||
|  |  | ||||||
| 		$down = $input->getOption('down') ?? false; | 		$down = $input->getOption('down') ?? false; | ||||||
|  |  | ||||||
|  | 		/** | ||||||
|  | 		 * Sort according to $down option. | ||||||
|  | 		 * Do we really want it? | ||||||
|  | 		 * Or should we use order provided by user, | ||||||
|  | 		 *      even when it's not sorted correctly? | ||||||
|  | 		 * Leaving it for consideration. | ||||||
|  | 		 */ | ||||||
|  | 		/* | ||||||
|  | 		if ($down) { | ||||||
|  | 			rsort($ids); | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			sort($ids); | ||||||
|  | 		} | ||||||
|  | 		*/ | ||||||
|  |  | ||||||
| 		foreach ($ids as $id) { | 		foreach ($ids as $id) { | ||||||
| 			$this->executeMigration($id, $io, !$down); | 			$this->executeMigration($id, $io, !$down); | ||||||
| 		} | 		} | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								system/src/Commands/PluginDisableCommand.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								system/src/Commands/PluginDisableCommand.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace MyAAC\Commands; | ||||||
|  |  | ||||||
|  | use MyAAC\Plugins; | ||||||
|  | use Symfony\Component\Console\Input\InputArgument; | ||||||
|  | use Symfony\Component\Console\Input\InputInterface; | ||||||
|  | use Symfony\Component\Console\Output\OutputInterface; | ||||||
|  | use Symfony\Component\Console\Style\SymfonyStyle; | ||||||
|  |  | ||||||
|  | class PluginDisableCommand extends Command | ||||||
|  | { | ||||||
|  | 	protected function configure(): void | ||||||
|  | 	{ | ||||||
|  | 		$this->setName('plugin:disable') | ||||||
|  | 			->setDescription('This command disables plugin') | ||||||
|  | 			->addArgument('plugin-name', InputArgument::REQUIRED, 'Plugin that you want to disable'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	protected function execute(InputInterface $input, OutputInterface $output): int | ||||||
|  | 	{ | ||||||
|  | 		require SYSTEM . 'init.php'; | ||||||
|  |  | ||||||
|  | 		$io = new SymfonyStyle($input, $output); | ||||||
|  |  | ||||||
|  | 		$pluginName = $input->getArgument('plugin-name'); | ||||||
|  |  | ||||||
|  | 		if (!Plugins::disable($pluginName)) { | ||||||
|  | 			$io->error('Error while disabling plugin ' . $pluginName . ': ' . Plugins::getError()); | ||||||
|  | 			return 2; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		$io->success('Successfully disabled plugin ' . $pluginName); | ||||||
|  | 		return Command::SUCCESS; | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								system/src/Commands/PluginEnableCommand.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								system/src/Commands/PluginEnableCommand.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace MyAAC\Commands; | ||||||
|  |  | ||||||
|  | use MyAAC\Plugins; | ||||||
|  | use Symfony\Component\Console\Input\InputArgument; | ||||||
|  | use Symfony\Component\Console\Input\InputInterface; | ||||||
|  | use Symfony\Component\Console\Output\OutputInterface; | ||||||
|  | use Symfony\Component\Console\Style\SymfonyStyle; | ||||||
|  |  | ||||||
|  | class PluginEnableCommand extends Command | ||||||
|  | { | ||||||
|  | 	protected function configure(): void | ||||||
|  | 	{ | ||||||
|  | 		$this->setName('plugin:enable') | ||||||
|  | 			->setDescription('This command enables plugin') | ||||||
|  | 			->addArgument('plugin-name', InputArgument::REQUIRED, 'Plugin that you want to enable'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	protected function execute(InputInterface $input, OutputInterface $output): int | ||||||
|  | 	{ | ||||||
|  | 		require SYSTEM . 'init.php'; | ||||||
|  |  | ||||||
|  | 		$io = new SymfonyStyle($input, $output); | ||||||
|  |  | ||||||
|  | 		$pluginName = $input->getArgument('plugin-name'); | ||||||
|  |  | ||||||
|  | 		if (!Plugins::enable($pluginName)) { | ||||||
|  | 			$io->error('Error while enabling plugin ' . $pluginName . ': ' . Plugins::getError()); | ||||||
|  | 			return 2; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		$io->success('Successfully enabled plugin ' . $pluginName); | ||||||
|  | 		return Command::SUCCESS; | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -8,7 +8,7 @@ use Symfony\Component\Console\Input\InputInterface; | |||||||
| use Symfony\Component\Console\Output\OutputInterface; | use Symfony\Component\Console\Output\OutputInterface; | ||||||
| use Symfony\Component\Console\Style\SymfonyStyle; | use Symfony\Component\Console\Style\SymfonyStyle; | ||||||
| 
 | 
 | ||||||
| class PluginInstallInstallCommand extends Command | class PluginSetupCommand extends Command | ||||||
| { | { | ||||||
| 	protected function configure(): void | 	protected function configure(): void | ||||||
| 	{ | 	{ | ||||||
							
								
								
									
										40
									
								
								system/src/Commands/PluginUninstallCommand.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								system/src/Commands/PluginUninstallCommand.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace MyAAC\Commands; | ||||||
|  |  | ||||||
|  | use MyAAC\Plugins; | ||||||
|  | use Symfony\Component\Console\Input\InputArgument; | ||||||
|  | use Symfony\Component\Console\Input\InputInterface; | ||||||
|  | use Symfony\Component\Console\Output\OutputInterface; | ||||||
|  | use Symfony\Component\Console\Style\SymfonyStyle; | ||||||
|  |  | ||||||
|  | class PluginUninstallCommand extends Command | ||||||
|  | { | ||||||
|  | 	protected function configure(): void | ||||||
|  | 	{ | ||||||
|  | 		$this->setName('plugin:uninstall') | ||||||
|  | 			->setDescription('This command uninstalls plugin') | ||||||
|  | 			->addArgument('plugin-name', InputArgument::REQUIRED, 'Plugin that you want to uninstall'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	protected function execute(InputInterface $input, OutputInterface $output): int | ||||||
|  | 	{ | ||||||
|  | 		require SYSTEM . 'init.php'; | ||||||
|  |  | ||||||
|  | 		$io = new SymfonyStyle($input, $output); | ||||||
|  |  | ||||||
|  | 		$pluginName = $input->getArgument('plugin-name'); | ||||||
|  |  | ||||||
|  | 		if (!Plugins::uninstall($pluginName)) { | ||||||
|  | 			$io->error('Error while uninstalling plugin ' . $pluginName . ': ' . Plugins::getError()); | ||||||
|  | 			return 2; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		foreach(Plugins::getWarnings() as $warning) { | ||||||
|  | 			$io->warning($warning); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		$io->success('Successfully uninstalled plugin ' . $pluginName); | ||||||
|  | 		return Command::SUCCESS; | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -3,6 +3,7 @@ | |||||||
| namespace MyAAC\Commands; | namespace MyAAC\Commands; | ||||||
|  |  | ||||||
| use MyAAC\Models\Settings as SettingsModel; | use MyAAC\Models\Settings as SettingsModel; | ||||||
|  | use MyAAC\Plugins; | ||||||
| use MyAAC\Settings; | use MyAAC\Settings; | ||||||
| use Symfony\Component\Console\Input\InputArgument; | use Symfony\Component\Console\Input\InputArgument; | ||||||
| use Symfony\Component\Console\Input\InputInterface; | use Symfony\Component\Console\Input\InputInterface; | ||||||
| @@ -34,7 +35,14 @@ class SettingsResetCommand extends Command | |||||||
| 			return Command::FAILURE; | 			return Command::FAILURE; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (!$name) { | 		// find by plugin name | ||||||
|  | 		foreach (Plugins::getAllPluginsSettings() as $key => $setting) { | ||||||
|  | 			if ($setting['pluginFilename'] === $name) { | ||||||
|  | 				$name = $key; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (empty($name)) { | ||||||
| 			SettingsModel::truncate(); | 			SettingsModel::truncate(); | ||||||
| 		} | 		} | ||||||
| 		else { | 		else { | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ | |||||||
| namespace MyAAC\Commands; | namespace MyAAC\Commands; | ||||||
|  |  | ||||||
| use MyAAC\Models\Settings as SettingsModel; | use MyAAC\Models\Settings as SettingsModel; | ||||||
|  | use MyAAC\Plugins; | ||||||
| use MyAAC\Settings; | use MyAAC\Settings; | ||||||
| use Symfony\Component\Console\Input\InputArgument; | use Symfony\Component\Console\Input\InputArgument; | ||||||
| use Symfony\Component\Console\Input\InputInterface; | use Symfony\Component\Console\Input\InputInterface; | ||||||
| @@ -17,7 +18,7 @@ class SettingsSetCommand extends Command | |||||||
| 			->setDescription('Updates the setting specified by argument in database') | 			->setDescription('Updates the setting specified by argument in database') | ||||||
| 			->addArgument('key', | 			->addArgument('key', | ||||||
| 				InputArgument::REQUIRED, | 				InputArgument::REQUIRED, | ||||||
| 				'Setting name/key' | 				'Setting key in format name.key' | ||||||
| 			) | 			) | ||||||
| 			->addArgument('value', | 			->addArgument('value', | ||||||
| 				InputArgument::REQUIRED, | 				InputArgument::REQUIRED, | ||||||
| @@ -34,6 +35,18 @@ class SettingsSetCommand extends Command | |||||||
| 		$key = $input->getArgument('key'); | 		$key = $input->getArgument('key'); | ||||||
| 		$value = $input->getArgument('value'); | 		$value = $input->getArgument('value'); | ||||||
|  |  | ||||||
|  | 		// format settings_name.key | ||||||
|  | 		// example: core.template | ||||||
|  | 		$explode = explode('.', $key); | ||||||
|  |  | ||||||
|  | 		// find by plugin name | ||||||
|  | 		foreach (Plugins::getAllPluginsSettings() as $_key => $setting) { | ||||||
|  | 			if ($setting['pluginFilename'] === $explode[0]) { | ||||||
|  | 				$explode[0] = $_key; | ||||||
|  | 				$key = implode('.', $explode); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		$settings = Settings::getInstance(); | 		$settings = Settings::getInstance(); | ||||||
| 		$settings->clearCache(); | 		$settings->clearCache(); | ||||||
| 		$settings->load(); | 		$settings->load(); | ||||||
| @@ -44,10 +57,6 @@ class SettingsSetCommand extends Command | |||||||
| 			return Command::FAILURE; | 			return Command::FAILURE; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// format plugin_name.key |  | ||||||
| 		// example: core.template |  | ||||||
| 		$explode = explode('.', $key); |  | ||||||
|  |  | ||||||
| 		$settings->updateInDatabase($explode[0], $explode[1], $value); | 		$settings->updateInDatabase($explode[0], $explode[1], $value); | ||||||
| 		$settings->clearCache(); | 		$settings->clearCache(); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								system/src/Models/AccountEMailCode.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								system/src/Models/AccountEMailCode.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace MyAAC\Models; | ||||||
|  | use Illuminate\Database\Eloquent\Model; | ||||||
|  |  | ||||||
|  | class AccountEMailCode extends Model { | ||||||
|  |  | ||||||
|  | 	protected $table = TABLE_PREFIX . 'account_email_codes'; | ||||||
|  |  | ||||||
|  | 	public $timestamps = false; | ||||||
|  |  | ||||||
|  | 	protected $fillable = ['account_id', 'code', 'created_at']; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -7,16 +7,13 @@ use MyAAC\Models\Settings as ModelsSettings; | |||||||
|  |  | ||||||
| class Settings implements \ArrayAccess | class Settings implements \ArrayAccess | ||||||
| { | { | ||||||
| 	static private $instance; | 	static private ?Settings $instance = null; | ||||||
| 	private $settingsFile = []; | 	private array $settingsFile = []; | ||||||
| 	private $settingsDatabase = []; | 	private array $settingsDatabase = []; | ||||||
| 	private $cache = []; | 	private array $cache = []; | ||||||
| 	private $valuesAsked = []; | 	private array $valuesAsked = []; | ||||||
| 	private $errors = []; | 	private array $errors = []; | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * @return Settings |  | ||||||
| 	 */ |  | ||||||
| 	public static function getInstance(): Settings | 	public static function getInstance(): Settings | ||||||
| 	{ | 	{ | ||||||
| 		if (!self::$instance) { | 		if (!self::$instance) { | ||||||
| @@ -26,28 +23,21 @@ class Settings implements \ArrayAccess | |||||||
| 		return self::$instance; | 		return self::$instance; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function load() | 	public function load(): void | ||||||
| 	{ | 	{ | ||||||
| 		$cache = Cache::getInstance(); | 		$this->settingsDatabase = Cache::remember('settings', 10 * 60, function () { | ||||||
| 		if ($cache->enabled()) { | 			$settingsDatabase = []; | ||||||
| 			$tmp = ''; |  | ||||||
| 			if ($cache->fetch('settings', $tmp)) { | 			$settings = ModelsSettings::all(); | ||||||
| 				$this->settingsDatabase = unserialize($tmp); | 			foreach ($settings as $setting) { | ||||||
| 				return; | 				$settingsDatabase[$setting->name][$setting->key] = $setting->value; | ||||||
| 			} | 			} | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		$settings = ModelsSettings::all(); | 			return $settingsDatabase; | ||||||
| 		foreach ($settings as $setting) { | 		}); | ||||||
| 			$this->settingsDatabase[$setting->name][$setting->key] = $setting->value; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if ($cache->enabled()) { |  | ||||||
| 			$cache->set('settings', serialize($this->settingsDatabase), 600); |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function save($pluginName, $values) | 	public function save($pluginName, $values): bool | ||||||
| 	{ | 	{ | ||||||
| 		$this->loadPlugin($pluginName); | 		$this->loadPlugin($pluginName); | ||||||
|  |  | ||||||
| @@ -104,7 +94,7 @@ class Settings implements \ArrayAccess | |||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function updateInDatabase($pluginName, $key, $value) | 	public function updateInDatabase($pluginName, $key, $value): void | ||||||
| 	{ | 	{ | ||||||
| 		if (ModelsSettings::where(['name' => $pluginName, 'key' => $key])->exists()) { | 		if (ModelsSettings::where(['name' => $pluginName, 'key' => $key])->exists()) { | ||||||
| 			ModelsSettings::where(['name' => $pluginName, 'key' => $key])->update(['value' => $value]); | 			ModelsSettings::where(['name' => $pluginName, 'key' => $key])->update(['value' => $value]); | ||||||
| @@ -117,7 +107,7 @@ class Settings implements \ArrayAccess | |||||||
| 		$this->clearCache(); | 		$this->clearCache(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public function deleteFromDatabase($pluginName, $key = null) | 	public function deleteFromDatabase($pluginName, $key = null): void | ||||||
| 	{ | 	{ | ||||||
| 		if (!isset($key)) { | 		if (!isset($key)) { | ||||||
| 			ModelsSettings::where('name', $pluginName)->delete(); | 			ModelsSettings::where('name', $pluginName)->delete(); | ||||||
| @@ -217,7 +207,7 @@ class Settings implements \ArrayAccess | |||||||
| 				if (isset($setting['hidden']) && $setting['hidden']) { | 				if (isset($setting['hidden']) && $setting['hidden']) { | ||||||
| 					$value = ''; | 					$value = ''; | ||||||
| 					if ($setting['type'] === 'boolean') { | 					if ($setting['type'] === 'boolean') { | ||||||
| 						$value = ($setting['default'] ? 'true' : 'false'); | 						$value = (getBoolean($setting['default']) ? 'true' : 'false'); | ||||||
| 					} | 					} | ||||||
| 					else if (in_array($setting['type'], ['text', 'number', 'float', 'double', 'email', 'password', 'textarea'])) { | 					else if (in_array($setting['type'], ['text', 'number', 'float', 'double', 'email', 'password', 'textarea'])) { | ||||||
| 						$value = $setting['default']; | 						$value = $setting['default']; | ||||||
| @@ -230,12 +220,7 @@ class Settings implements \ArrayAccess | |||||||
| 				} | 				} | ||||||
| 				else if ($setting['type'] === 'boolean') { | 				else if ($setting['type'] === 'boolean') { | ||||||
| 					if(isset($settingsDb[$key])) { | 					if(isset($settingsDb[$key])) { | ||||||
| 						if($settingsDb[$key] === 'true') { | 						$value = getBoolean($settingsDb[$key]); | ||||||
| 							$value = true; |  | ||||||
| 						} |  | ||||||
| 						else { |  | ||||||
| 							$value = false; |  | ||||||
| 						} |  | ||||||
| 					} | 					} | ||||||
| 					else { | 					else { | ||||||
| 						$value = ($setting['default'] ?? false); | 						$value = ($setting['default'] ?? false); | ||||||
| @@ -383,7 +368,7 @@ class Settings implements \ArrayAccess | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	#[\ReturnTypeWillChange] | 	#[\ReturnTypeWillChange] | ||||||
| 	public function offsetSet($offset, $value) | 	public function offsetSet($offset, $value): void | ||||||
| 	{ | 	{ | ||||||
| 		if (is_null($offset)) { | 		if (is_null($offset)) { | ||||||
| 			throw new \RuntimeException("Settings: You cannot set empty offset with value: $value!"); | 			throw new \RuntimeException("Settings: You cannot set empty offset with value: $value!"); | ||||||
| @@ -423,7 +408,7 @@ class Settings implements \ArrayAccess | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	#[\ReturnTypeWillChange] | 	#[\ReturnTypeWillChange] | ||||||
| 	public function offsetUnset($offset) | 	public function offsetUnset($offset): void | ||||||
| 	{ | 	{ | ||||||
| 		$this->loadPlugin($offset); | 		$this->loadPlugin($offset); | ||||||
|  |  | ||||||
| @@ -455,7 +440,7 @@ class Settings implements \ArrayAccess | |||||||
| 	 * @return array|mixed | 	 * @return array|mixed | ||||||
| 	 */ | 	 */ | ||||||
| 	#[\ReturnTypeWillChange] | 	#[\ReturnTypeWillChange] | ||||||
| 	public function offsetGet($offset) | 	public function offsetGet($offset): mixed | ||||||
| 	{ | 	{ | ||||||
| 		// try cache hit | 		// try cache hit | ||||||
| 		if(isset($this->cache[$offset])) { | 		if(isset($this->cache[$offset])) { | ||||||
| @@ -521,7 +506,7 @@ class Settings implements \ArrayAccess | |||||||
| 		return $ret; | 		return $ret; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	private function updateValuesAsked($offset) | 	private function updateValuesAsked($offset): void | ||||||
| 	{ | 	{ | ||||||
| 		$pluginKeyName = $offset; | 		$pluginKeyName = $offset; | ||||||
| 		if (strpos($offset, '.')) { | 		if (strpos($offset, '.')) { | ||||||
| @@ -537,7 +522,7 @@ class Settings implements \ArrayAccess | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	private function loadPlugin($offset) | 	private function loadPlugin($offset): void | ||||||
| 	{ | 	{ | ||||||
| 		$this->updateValuesAsked($offset); | 		$this->updateValuesAsked($offset); | ||||||
|  |  | ||||||
| @@ -566,7 +551,7 @@ class Settings implements \ArrayAccess | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public static function saveConfig($config, $filename, &$content = '') | 	public static function saveConfig($config, $filename, &$content = ''): bool|int | ||||||
| 	{ | 	{ | ||||||
| 		$content = "<?php" . PHP_EOL; | 		$content = "<?php" . PHP_EOL; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								system/src/TwoFactorAuth/Gateway/AppAuthGateway.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								system/src/TwoFactorAuth/Gateway/AppAuthGateway.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace MyAAC\TwoFactorAuth\Gateway; | ||||||
|  |  | ||||||
|  | use MyAAC\TwoFactorAuth\Interface\AuthGatewayInterface; | ||||||
|  |  | ||||||
|  | class AppAuthGateway extends BaseAuthGateway implements AuthGatewayInterface | ||||||
|  | { | ||||||
|  | 	public function verifyCode(string $code): bool | ||||||
|  | 	{ | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								system/src/TwoFactorAuth/Gateway/BaseAuthGateway.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								system/src/TwoFactorAuth/Gateway/BaseAuthGateway.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace MyAAC\TwoFactorAuth\Gateway; | ||||||
|  |  | ||||||
|  | class BaseAuthGateway | ||||||
|  | { | ||||||
|  | 	protected \OTS_Account $account; | ||||||
|  |  | ||||||
|  | 	public function __construct(\OTS_Account $account) { | ||||||
|  | 		$this->account = $account; | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								system/src/TwoFactorAuth/Gateway/EmailAuthGateway.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								system/src/TwoFactorAuth/Gateway/EmailAuthGateway.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace MyAAC\TwoFactorAuth\Gateway; | ||||||
|  |  | ||||||
|  | use MyAAC\Models\AccountEMailCode; | ||||||
|  | use MyAAC\TwoFactorAuth\Interface\AuthGatewayInterface; | ||||||
|  | use MyAAC\TwoFactorAuth\TwoFactorAuth; | ||||||
|  |  | ||||||
|  | class EmailAuthGateway extends BaseAuthGateway implements AuthGatewayInterface | ||||||
|  | { | ||||||
|  | 	public function verifyCode(string $code): bool | ||||||
|  | 	{ | ||||||
|  | 		return AccountEMailCode::where('account_id', '=', $this->account->getId())->where('code', $code)->where('created_at', '>', time() - TwoFactorAuth::EMAIL_CODE_VALID_UNTIL)->first() !== null; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace MyAAC\TwoFactorAuth\Interface; | ||||||
|  |  | ||||||
|  | interface AuthGatewayInterface | ||||||
|  | { | ||||||
|  | 	public function __construct(\OTS_Account $account); | ||||||
|  | 	public function verifyCode(string $code): bool; | ||||||
|  | } | ||||||
							
								
								
									
										183
									
								
								system/src/TwoFactorAuth/TwoFactorAuth.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								system/src/TwoFactorAuth/TwoFactorAuth.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,183 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace MyAAC\TwoFactorAuth; | ||||||
|  |  | ||||||
|  | use MyAAC\Models\AccountEMailCode; | ||||||
|  | use MyAAC\TwoFactorAuth\Gateway\AppAuthGateway; | ||||||
|  | use MyAAC\TwoFactorAuth\Gateway\EmailAuthGateway; | ||||||
|  |  | ||||||
|  | class TwoFactorAuth | ||||||
|  | { | ||||||
|  | 	const TYPE_NONE = 0; | ||||||
|  | 	const TYPE_EMAIL = 1; | ||||||
|  | 	const TYPE_APP = 2; | ||||||
|  | 	// maybe later | ||||||
|  | 	//const TYPE_SMS = 3; | ||||||
|  |  | ||||||
|  | 	const EMAIL_CODE_VALID_UNTIL = 24 * 60 * 60; | ||||||
|  |  | ||||||
|  | 	private static self $instance; | ||||||
|  |  | ||||||
|  | 	private \OTS_Account $account; | ||||||
|  | 	private int $authType; | ||||||
|  | 	private EmailAuthGateway|AppAuthGateway $authGateway; | ||||||
|  |  | ||||||
|  | 	public function __construct(\OTS_Account|int $account) { | ||||||
|  | 		if (is_int($account)) { | ||||||
|  | 			$this->account = new \OTS_Account(); | ||||||
|  | 			$this->account->load($account); | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			$this->account = $account; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		$this->authType = (int)$this->account->getCustomField('2fa_type'); | ||||||
|  | 		$this->setAuthGateway($this->authType); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public static function getInstance($account = null): self | ||||||
|  | 	{ | ||||||
|  | 		if (!isset(self::$instance)) { | ||||||
|  | 			self::$instance = new self($account); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return self::$instance; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public function process($login_account, $login_password, $remember_me, $code): bool | ||||||
|  | 	{ | ||||||
|  | 		global $twig; | ||||||
|  |  | ||||||
|  | 		if (!$this->isActive()) { | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (empty($code)) { | ||||||
|  | 			if ($this->authType == self::TYPE_EMAIL) { | ||||||
|  | 				if (!$this->hasRecentEmailCode(15 * 60)) { | ||||||
|  | 					$this->resendEmailCode(); | ||||||
|  | 					//success('Resent email.'); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				define('HIDE_LOGIN_BOX', true); | ||||||
|  | 				$twig->display('account.2fa.email.login.html.twig', [ | ||||||
|  | 					'account_login' => $login_account, | ||||||
|  | 					'password_login' => $login_password, | ||||||
|  | 					'remember_me' => $remember_me, | ||||||
|  | 				]); | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				echo 'Two Factor App Auth'; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if ($this->getAuthGateway()->verifyCode($code)) { | ||||||
|  | 			if ($this->authType === self::TYPE_EMAIL) { | ||||||
|  | 				$this->deleteOldCodes(); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (setting('core.mail_enabled')) { | ||||||
|  | 			$mailBody = $twig->render('mail.account.2fa.email-code.wrong-attempt.html.twig'); | ||||||
|  |  | ||||||
|  | 			if (!_mail($this->account->getEMail(), configLua('serverName') . ' - Failed Two-Factor Authentication Attempt', $mailBody)) { | ||||||
|  | 				error('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log'); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		define('HIDE_LOGIN_BOX', true); | ||||||
|  |  | ||||||
|  | 		$errors[] = 'Invalid email code!'; | ||||||
|  | 		$twig->display('error_box.html.twig', ['errors' => $errors]); | ||||||
|  |  | ||||||
|  | 		$twig->display('account.2fa.email.login.html.twig', | ||||||
|  | 			[ | ||||||
|  | 				'account_login' => $login_account, | ||||||
|  | 				'password_login' => $login_password, | ||||||
|  | 				'remember_me' => $remember_me, | ||||||
|  |  | ||||||
|  | 				'wrongCode' => true, | ||||||
|  | 			]); | ||||||
|  |  | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public function setAuthGateway(int $authType): void | ||||||
|  | 	{ | ||||||
|  | 		if ($authType === self::TYPE_EMAIL) { | ||||||
|  | 			$this->authGateway = new EmailAuthGateway($this->account); | ||||||
|  | 		} | ||||||
|  | 		else if ($authType === self::TYPE_APP) { | ||||||
|  | 			$this->authGateway = new AppAuthGateway($this->account); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public function getAccountManageViews(): array | ||||||
|  | 	{ | ||||||
|  | 		$twoFactorView = 'account.2fa.protected.html.twig'; | ||||||
|  | 		if ($this->authType == self::TYPE_EMAIL) { | ||||||
|  | 			$twoFactorView2 = 'account.2fa.email.activated.html.twig'; | ||||||
|  | 		} | ||||||
|  | 		elseif ($this->authType == self::TYPE_APP) { | ||||||
|  | 			$twoFactorView2 = 'account.2fa.app.activated.html.twig'; | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			$twoFactorView = 'account.2fa.connect.html.twig'; | ||||||
|  | 			$twoFactorView2 = 'account.2fa.email.activate.html.twig'; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return [$twoFactorView, $twoFactorView2]; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public function enable(int $type): void { | ||||||
|  | 		$this->account->setCustomField('2fa_type', $type); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public function disable(): void { | ||||||
|  | 		$this->account->setCustomField('2fa_type', self::TYPE_NONE); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public function isActive(): bool { | ||||||
|  | 		return $this->authType != self::TYPE_NONE; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public function getAuthType(): int { | ||||||
|  | 		return $this->authType; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public function getAuthGateway(): AppAuthGateway|EmailAuthGateway  { | ||||||
|  | 		return $this->authGateway; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public function hasRecentEmailCode($since = self::EMAIL_CODE_VALID_UNTIL): bool { | ||||||
|  | 		return AccountEMailCode::where('account_id', '=', $this->account->getId())->where('created_at', '>', time() - $since)->first() !== null; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public function deleteOldCodes(): void { | ||||||
|  | 		AccountEMailCode::where('account_id', '=', $this->account->getId())->delete(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	public function resendEmailCode(): void | ||||||
|  | 	{ | ||||||
|  | 		global $twig; | ||||||
|  |  | ||||||
|  | 		$newCode = generateRandomString(6, true, false, true); | ||||||
|  | 		AccountEMailCode::create([ | ||||||
|  | 			'account_id' => $this->account->getId(), | ||||||
|  | 			'code' => $newCode, | ||||||
|  | 			'created_at' => time(), | ||||||
|  | 		]); | ||||||
|  |  | ||||||
|  | 		$mailBody = $twig->render('mail.account.2fa.email-code.html.twig', [ | ||||||
|  | 			'code' => $newCode, | ||||||
|  | 		]); | ||||||
|  |  | ||||||
|  | 		if (!_mail($this->account->getEMail(), configLua('serverName') . ' - Requested Authentication Email Code', $mailBody)) { | ||||||
|  | 			error('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log'); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -148,7 +148,7 @@ function get_template_menus(): array | |||||||
| { | { | ||||||
| 	global $template_name; | 	global $template_name; | ||||||
|  |  | ||||||
| 	$result = Cache::remember('template_menus', 10 * 60, function () use ($template_name) { | 	$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', 'blank', 'color', 'category']) | ||||||
| 			->where('template', $template_name) | 			->where('template', $template_name) | ||||||
| 			->orderBy('category') | 			->orderBy('category') | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								system/templates/account.2fa.connect.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								system/templates/account.2fa.connect.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | <tr> | ||||||
|  | 	<td> | ||||||
|  | 		<div class="TableShadowContainerRightTop"> | ||||||
|  | 			<div class="TableShadowRightTop" style="background-image:url({{ template_path }}/images/global/content/table-shadow-rt.gif);"></div> | ||||||
|  | 		</div> | ||||||
|  | 		<div class="TableContentAndRightShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-rm.gif);"> | ||||||
|  | 			<div class="TableContentContainer"> | ||||||
|  | 				<table class="TableContent" width="100%" style="border:1px solid #faf0d7;"> | ||||||
|  | 					<tbody><tr> | ||||||
|  | 						<td class="LabelV"><b>Connect your {{ config.lua.serverName }} account to an authenticator app!</b> | ||||||
|  | 							<div style="float: right; font-size: 1px;"> | ||||||
|  | 								<form action="{{ getLink('account/2fa') }}?action=email-code" method="post" style="margin: 0px; padding: 0px;"> | ||||||
|  | 									{{ csrf() }} | ||||||
|  | 									{% set button_name = 'Request' %} | ||||||
|  | 									{% include('buttons.base.html.twig') %} | ||||||
|  | 								</form> | ||||||
|  | 							</div> | ||||||
|  | 						</td> | ||||||
|  | 					</tr> | ||||||
|  | 					<tr> | ||||||
|  | 						<td> | ||||||
|  | 							<p>As a first step to connect an <b>authenticator app</b> to your account, click on "Request"! An email with a confirmation key will be sent to the email address assigned to your account.</p> | ||||||
|  | 						</td> | ||||||
|  | 					</tr> | ||||||
|  | 					</tbody> | ||||||
|  | 				</table> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 		<div class="TableShadowContainer"> | ||||||
|  | 			<div class="TableBottomShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-bm.gif);"> | ||||||
|  | 				<div class="TableBottomLeftShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-bl.gif);"></div> | ||||||
|  | 				<div class="TableBottomRightShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-br.gif);"></div> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 	</td> | ||||||
|  | </tr> | ||||||
							
								
								
									
										37
									
								
								system/templates/account.2fa.email.activate.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								system/templates/account.2fa.email.activate.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | <tr> | ||||||
|  | 	<td> | ||||||
|  | 		<div class="TableShadowContainerRightTop"> | ||||||
|  | 			<div class="TableShadowRightTop" style="background-image:url({{ template_path }}/images/global/content/table-shadow-rt.gif);"></div> | ||||||
|  | 		</div> | ||||||
|  | 		<div class="TableContentAndRightShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-rm.gif);"> | ||||||
|  | 			<div class="TableContentContainer"> | ||||||
|  | 				<table class="TableContent" width="100%" style="border:1px solid #faf0d7;"> | ||||||
|  | 					<tbody> | ||||||
|  | 					<tr> | ||||||
|  | 						<td class="LabelV"><b>Activate email code authentication for your account!</b> | ||||||
|  | 							<div style="float: right; font-size: 1px;"> | ||||||
|  | 								<form action="{{ getLink('account/2fa') }}?action=email-code&step=activate" method="post" style="margin: 0; padding: 0;"> | ||||||
|  | 									{{ csrf() }} | ||||||
|  | 									{% set button_name = 'Request' %} | ||||||
|  | 									{% include('buttons.base.html.twig') %} | ||||||
|  | 								</form> | ||||||
|  | 							</div> | ||||||
|  | 						</td> | ||||||
|  | 					</tr> | ||||||
|  | 					<tr> | ||||||
|  | 						<td> | ||||||
|  | 							<p>As a first step to activate <b>email code authentication</b> for your account, click on "Request"! An <b>email code</b> will be sent to the email address assigned to your account. You will be asked to enter this <b>email code</b> on the next page within 24 hours.</p> | ||||||
|  | 						</td> | ||||||
|  | 					</tr> | ||||||
|  | 					</tbody> | ||||||
|  | 				</table> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 		<div class="TableShadowContainer"> | ||||||
|  | 			<div class="TableBottomShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-bm.gif);"> | ||||||
|  | 				<div class="TableBottomLeftShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-bl.gif);"></div> | ||||||
|  | 				<div class="TableBottomRightShadow" style="background-image:url({{ template_path }}/images/global/content/table-shadow-br.gif);"></div> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 	</td> | ||||||
|  | </tr> | ||||||
							
								
								
									
										26
									
								
								system/templates/account.2fa.email.activated.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								system/templates/account.2fa.email.activated.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | <tr> | ||||||
|  | 	<td> | ||||||
|  | 		<div class="TableContentContainer "> | ||||||
|  | 			<table class="TableContent" width="100%" style="border:1px solid #faf0d7;"> | ||||||
|  | 				<tbody> | ||||||
|  | 				<tr> | ||||||
|  | 					<td> | ||||||
|  | 						<div style="float: right; width: 135px;"> | ||||||
|  | 							<form action="{{ getLink('account/2fa') }}?action=email-code" method="post" style="padding:0;margin:0;"> | ||||||
|  | 								{{ csrf() }} | ||||||
|  | 								<input type="hidden" name="step" value="deactivate"> | ||||||
|  | 								{% set button_name = 'Deactivate' %} | ||||||
|  | 								{{ include('buttons.base.html.twig') }} | ||||||
|  | 							</form> | ||||||
|  | 						</div> | ||||||
|  | 						<b>Two-Factor Email Code Authentication <span style="color: green">Activated</span>!</b> | ||||||
|  | 						<p>To deactivate <b>email code authentication</b>, click on the "Deactivate" button.</p> | ||||||
|  | 						<!--p>You will have to confirm the deactivation by entering an <b>email code</b> which will be sent | ||||||
|  | 							to the email address assigned to your account.</p--> | ||||||
|  | 					</td> | ||||||
|  | 				</tr> | ||||||
|  | 				</tbody> | ||||||
|  | 			</table> | ||||||
|  | 		</div> | ||||||
|  | 	</td> | ||||||
|  | </tr> | ||||||
							
								
								
									
										109
									
								
								system/templates/account.2fa.email.deactivate.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								system/templates/account.2fa.email.deactivate.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | |||||||
|  | {% set title = 'Deactivate Email Code Authentication' %} | ||||||
|  | {% set content %} | ||||||
|  | 	<table style="width:100%;"> | ||||||
|  | 		<tbody> | ||||||
|  | 		<tr> | ||||||
|  | 			<td> | ||||||
|  | 				<div class="TableContentContainer "> | ||||||
|  | 					<table class="TableContent" width="100%" style="border:1px solid #faf0d7;"> | ||||||
|  | 						<tbody> | ||||||
|  | 						<tr> | ||||||
|  | 							<td>To deactivate <b>two-factor email code authentication</b> for your account, enter the | ||||||
|  | 								received <b>email code</b> below. Note, however, that <b>email code authentication</b> | ||||||
|  | 								is an important security feature which helps to prevent any unauthorised access to your | ||||||
|  | 								Tibia account. | ||||||
|  | 							</td> | ||||||
|  | 						</tr> | ||||||
|  | 						</tbody> | ||||||
|  | 					</table> | ||||||
|  | 				</div> | ||||||
|  | 			</td> | ||||||
|  | 		</tr> | ||||||
|  | 		<tr> | ||||||
|  | 			<td> | ||||||
|  | 				<div class="TableContentContainer"> | ||||||
|  | 					<table class="TableContent" width="100%" style="border:1px solid #faf0d7;"> | ||||||
|  | 						<tbody> | ||||||
|  | 						<tr> | ||||||
|  | 							<td> | ||||||
|  | 								<div style="float: right;"> | ||||||
|  | 									<form | ||||||
|  | 										action="{{ getLink('account/2fa') }}?action=email-code&step=resend" | ||||||
|  | 										method="post" | ||||||
|  | 										style="padding:0;margin:0;" | ||||||
|  | 									> | ||||||
|  | 										{{ csrf() }} | ||||||
|  |  | ||||||
|  | 										{% set button_name = 'Resend Email Code' %} | ||||||
|  | 										{{ include('buttons.base.html.twig') }} | ||||||
|  | 									</form> | ||||||
|  | 								</div> | ||||||
|  | 								An <b>email code</b> has already been sent to the email address assigned to your | ||||||
|  | 								account. | ||||||
|  | 								Please check your email account's spam/junk filter and make sure that your mailbox is | ||||||
|  | 								not | ||||||
|  | 								full.<br>In case you need a new email code, you can request one by clicking on "Resend | ||||||
|  | 								Email | ||||||
|  | 								Code". | ||||||
|  | 							</td> | ||||||
|  | 						</tr> | ||||||
|  | 						</tbody> | ||||||
|  | 					</table> | ||||||
|  | 				</div> | ||||||
|  | 			</td> | ||||||
|  | 		</tr> | ||||||
|  | 		<tr> | ||||||
|  | 			<td> | ||||||
|  | 				<div class="TableContentContainer"> | ||||||
|  | 					<table class="TableContent" width="100%" style="border:1px solid #faf0d7;"> | ||||||
|  | 						<tbody> | ||||||
|  | 						<tr> | ||||||
|  | 							<td>To complete the deactivation of <b>email code authentication</b>, please enter the <b>email | ||||||
|  | 									code</b> you received at the email address assigned to your account. | ||||||
|  | 								<div style="margin-top: 15px; margin-bottom: 15px;"> | ||||||
|  | 									<div class="LabelV150 {{ wrongCode ? 'red' : '' }}" style="float:left;"><label | ||||||
|  | 											for="email-code">Email Code:</label></div> | ||||||
|  | 									<input form="form-code" id="auth-code" name="email-code" maxlength="15" | ||||||
|  | 										   autocomplete="off"> | ||||||
|  | 									{% if wrongCode %} | ||||||
|  | 										<br/> | ||||||
|  | 										<div class="LabelV150" style="float:left;">  </div> | ||||||
|  | 										<div class="FormFieldError">Invalid email code!</div> | ||||||
|  | 									{% endif %} | ||||||
|  | 								</div> | ||||||
|  | 							</td> | ||||||
|  | 						</tr> | ||||||
|  | 						</tbody> | ||||||
|  | 					</table> | ||||||
|  | 				</div> | ||||||
|  | 			</td> | ||||||
|  | 		</tr> | ||||||
|  | 		</tbody> | ||||||
|  | 	</table> | ||||||
|  | {% endset %} | ||||||
|  | {% include 'tables.headline.html.twig' %} | ||||||
|  | <table style="width: 100%;"> | ||||||
|  | 	<tbody> | ||||||
|  | 	<tr align="center" valign="top"> | ||||||
|  | 		<td> | ||||||
|  | 			<form id="form-code" method="post" action="{{ getLink('account/2fa') }}?action=email-code"> | ||||||
|  | 				{{ csrf() }} | ||||||
|  |  | ||||||
|  | 				<input type="hidden" name="step" value="deactivate"> | ||||||
|  | 				<input type="hidden" name="save" value="1"> | ||||||
|  |  | ||||||
|  | 				{% set button_name = 'Continue' %} | ||||||
|  | 				{% set button_color = 'green' %} | ||||||
|  | 				{{ include('buttons.submit.html.twig') }} | ||||||
|  | 			</form> | ||||||
|  | 		</td> | ||||||
|  | 		<td> | ||||||
|  | 			<form action="{{ getLink('account/manage') }}" method="post" style="padding:0;margin:0;"> | ||||||
|  | 				{{ csrf() }} | ||||||
|  | 				{% set button_color = 'blue' %} | ||||||
|  | 				{{ include('buttons.back.html.twig') }} | ||||||
|  | 			</form> | ||||||
|  | 		</td> | ||||||
|  | 	</tr> | ||||||
|  | 	</tbody> | ||||||
|  | </table> | ||||||
							
								
								
									
										92
									
								
								system/templates/account.2fa.email.login.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								system/templates/account.2fa.email.login.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | |||||||
|  | {% set title = 'Enter Email Code' %} | ||||||
|  | {% set content %} | ||||||
|  | <table style="width:100%;"> | ||||||
|  | 	<tbody> | ||||||
|  | 	<tr> | ||||||
|  | 		<td> | ||||||
|  | 			<div class="TableContentContainer"> | ||||||
|  | 				<table class="TableContent" width="100%" style="border:1px solid #faf0d7;"> | ||||||
|  | 					<tbody> | ||||||
|  | 					<tr> | ||||||
|  | 						<td> | ||||||
|  | 							<div style="float: right;"> | ||||||
|  | 								<form | ||||||
|  | 										action="{{ getLink('account/2fa') }}?action=email-code&step=resend" | ||||||
|  | 										method="post" | ||||||
|  | 										style="padding:0;margin:0;" | ||||||
|  | 								> | ||||||
|  | 									{{ csrf() }} | ||||||
|  |  | ||||||
|  | 									{% set button_name = 'Resend Email Code' %} | ||||||
|  | 									{{ include('buttons.base.html.twig') }} | ||||||
|  | 								</form> | ||||||
|  | 							</div> | ||||||
|  | 							An <b>email code</b> has already been sent to the email address assigned to your account. | ||||||
|  | 							Please check your email account's spam/junk filter and make sure that your mailbox is not | ||||||
|  | 							full.<br>In case you need a new email code, you can request one by clicking on "Resend Email | ||||||
|  | 							Code". | ||||||
|  | 						</td> | ||||||
|  | 					</tr> | ||||||
|  | 					</tbody> | ||||||
|  | 				</table> | ||||||
|  | 			</div> | ||||||
|  | 		</td> | ||||||
|  | 	</tr> | ||||||
|  | 	<tr> | ||||||
|  | 		<td> | ||||||
|  | 			<div class="TableContentContainer"> | ||||||
|  | 				<table class="TableContent" width="100%" style="border:1px solid #faf0d7;"> | ||||||
|  | 					<tbody> | ||||||
|  | 					<tr> | ||||||
|  | 						<td><b>Email code authentication is activated for your account.</b><br><br>Please enter the <b>most | ||||||
|  | 								recent email code</b> you have received in order to log in.<br> | ||||||
|  | 							<div style="margin-top: 15px; margin-bottom: 15px;"> | ||||||
|  | 								<div class="LabelV150 {{ wrongCode ? 'red' : '' }}" style="float:left;"><label for="email-code">Email Code:</label></div> | ||||||
|  | 								<input form="form-code" id="auth-code" name="auth-code" maxlength="15" autocomplete="off"> | ||||||
|  | 								{% if wrongCode %} | ||||||
|  | 									<br/> | ||||||
|  | 									<div class="LabelV150" style="float:left;">  </div> | ||||||
|  | 									<div class="FormFieldError">Invalid email code!</div> | ||||||
|  | 								{% endif %} | ||||||
|  | 							</div> | ||||||
|  | 						</td> | ||||||
|  | 					</tr> | ||||||
|  | 					</tbody> | ||||||
|  | 				</table> | ||||||
|  | 			</div> | ||||||
|  | 		</td> | ||||||
|  | 	</tr> | ||||||
|  | 	</tbody> | ||||||
|  | </table> | ||||||
|  | {% endset %} | ||||||
|  | {% include 'tables.headline.html.twig' %} | ||||||
|  | <table style="width: 100%;"> | ||||||
|  | 	<tbody> | ||||||
|  | 	<tr align="center" valign="top"> | ||||||
|  | 		<td> | ||||||
|  | 			<form id="form-code" method="post" action="{{ getLink('account/manage') }}"> | ||||||
|  | 				{{ csrf() }} | ||||||
|  |  | ||||||
|  | 				<input type="hidden" name="account_login" value="{{ account_login ?? '' }}" /> | ||||||
|  | 				<input type="hidden" name="password_login" value="{{ password_login ?? '' }}" /> | ||||||
|  | 				{% if remember_me %} | ||||||
|  | 					<input type="hidden" name="remember_me" value="true" /> | ||||||
|  | 				{% endif %} | ||||||
|  |  | ||||||
|  | 				<input type="hidden" name="step" value="verify"> | ||||||
|  | 				{% set button_name = 'Continue' %} | ||||||
|  | 				{% set button_color = 'green' %} | ||||||
|  | 				{{ include('buttons.submit.html.twig') }} | ||||||
|  | 			</form> | ||||||
|  | 		</td> | ||||||
|  | 		<td> | ||||||
|  | 			<form action="{{ getLink('account/manage') }}" method="post" style="padding:0;margin:0;"> | ||||||
|  | 				{{ csrf() }} | ||||||
|  |  | ||||||
|  | 				{% set button_color = 'blue' %} | ||||||
|  | 				{{ include('buttons.back.html.twig') }} | ||||||
|  | 			</form> | ||||||
|  | 		</td> | ||||||
|  | 	</tr> | ||||||
|  | 	</tbody> | ||||||
|  | </table> | ||||||
							
								
								
									
										110
									
								
								system/templates/account.2fa.email_code.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								system/templates/account.2fa.email_code.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | |||||||
|  | {% set title = 'Activate Email Code Authentication' %} | ||||||
|  |  | ||||||
|  | {% set content %} | ||||||
|  | <table style="width:100%;"> | ||||||
|  | 	<tbody> | ||||||
|  | 	<tr> | ||||||
|  | 		<td> | ||||||
|  | 			<div class="TableContentContainer"> | ||||||
|  | 				<table class="TableContent" width="100%" style="border:1px solid #faf0d7;"> | ||||||
|  | 					<tbody> | ||||||
|  | 					<tr> | ||||||
|  | 						<td>Enter the email code below to activate <b>two-factor email code authentication</b>. Note | ||||||
|  | 							that this code is only valid for 24 hours.<br><br> | ||||||
|  | 							<div class="AttentionSign"><img src="{{ template_path }}/images/global/content/attentionsign.gif"></div> | ||||||
|  | 							<b>Note:</b> Once you have email code authentication activated, an <b>email code</b> will be | ||||||
|  | 							sent to the email address assigned to your account whenever you try to log in to the Tibia | ||||||
|  | 							client or the {{ config.lua.serverName }} website. In order to log in, you will need to enter the <b>most recent | ||||||
|  | 								email code</b> you have received. | ||||||
|  | 						</td> | ||||||
|  | 					</tr> | ||||||
|  | 					</tbody> | ||||||
|  | 				</table> | ||||||
|  | 			</div> | ||||||
|  | 		</td> | ||||||
|  | 	</tr> | ||||||
|  | 	<tr> | ||||||
|  | 		<td> | ||||||
|  | 			<div class="TableContentContainer"> | ||||||
|  | 				<table class="TableContent" width="100%" style="border:1px solid #faf0d7;"> | ||||||
|  | 					<tbody> | ||||||
|  | 					<tr> | ||||||
|  | 						<td> | ||||||
|  | 							<div style="float: right;"> | ||||||
|  | 								<form action="{{ getLink('account/2fa') }}?action=email-code" | ||||||
|  | 									method="post" style="padding:0;margin:0;"> | ||||||
|  | 									{{ csrf() }} | ||||||
|  |  | ||||||
|  | 									{% if account_logged is defined %} | ||||||
|  | 										<input type="hidden" name="account_logged" value="{{ account_logged.getId() }}"> | ||||||
|  | 									{% endif %} | ||||||
|  | 									<input type="hidden" name="step" value="resend"> | ||||||
|  |  | ||||||
|  | 									{% set button_name = 'Resend Email Code' %} | ||||||
|  | 									{% include('buttons.base.html.twig') %} | ||||||
|  | 								</form> | ||||||
|  | 							</div> | ||||||
|  | 							An <b>email code</b> has already been sent to the email address assigned to your account. | ||||||
|  | 							Please check your email account's spam/junk filter and make sure that your mailbox is not | ||||||
|  | 							full.<br>In case you need a new email code, you can request one by clicking on "Resend Email | ||||||
|  | 							Code". | ||||||
|  | 						</td> | ||||||
|  | 					</tr> | ||||||
|  | 					</tbody> | ||||||
|  | 				</table> | ||||||
|  | 			</div> | ||||||
|  | 		</td> | ||||||
|  | 	</tr> | ||||||
|  | 	<tr> | ||||||
|  | 		<td> | ||||||
|  | 			<div class="TableContentContainer"> | ||||||
|  | 				<table class="TableContent" width="100%" style="border:1px solid #faf0d7;"> | ||||||
|  | 					<tbody> | ||||||
|  | 					<tr> | ||||||
|  | 						<td>To complete the activation of email code authentication for your Tibia account, please enter | ||||||
|  | 							the email code you received at the email address assigned to your account. | ||||||
|  | 							<div style="margin-top: 15px; margin-bottom: 15px;"> | ||||||
|  | 								<div class="LabelV150 {{ wrongCode ? 'red' : '' }}" style="float:left;">Email Code:</div> | ||||||
|  | 								<input form="confirmActivateForm" name="auth-code" maxlength="6"> | ||||||
|  | 								{% if wrongCode %} | ||||||
|  | 									<br/> | ||||||
|  | 									<div class="LabelV150" style="float:left;">  </div> | ||||||
|  | 									<div class="FormFieldError">Invalid email code!</div> | ||||||
|  | 								{% endif %} | ||||||
|  | 							</div> | ||||||
|  | 						</td> | ||||||
|  | 					</tr> | ||||||
|  | 					</tbody> | ||||||
|  | 				</table> | ||||||
|  | 			</div> | ||||||
|  | 		</td> | ||||||
|  | 	</tr> | ||||||
|  | 	</tbody> | ||||||
|  | </table> | ||||||
|  | {% endset %} | ||||||
|  | {% include 'tables.headline.html.twig' %} | ||||||
|  | <br/> | ||||||
|  | <table style="width: 100%;"> | ||||||
|  | 	<tbody> | ||||||
|  | 	<tr align="center" valign="top"> | ||||||
|  | 		<td> | ||||||
|  | 			<form id="confirmActivateForm" action="{{ getLink('account/2fa') }}?action=email-code" method="post" style="padding:0;margin:0;"> | ||||||
|  | 				{{ csrf() }} | ||||||
|  |  | ||||||
|  | 				<input type="hidden" name="step" value="activate"> | ||||||
|  | 				<input type="hidden" name="save" value="1"> | ||||||
|  |  | ||||||
|  | 				{% set button_color = 'green' %} | ||||||
|  | 				{{ include('buttons.submit.html.twig') }} | ||||||
|  | 			</form> | ||||||
|  | 		</td> | ||||||
|  | 		<td> | ||||||
|  | 			<form action="{{ getLink('account/manage') }}" method="post" style="padding:0;margin:0;"> | ||||||
|  | 				{{ csrf() }} | ||||||
|  | 				{% set button_color = 'blue' %} | ||||||
|  | 				{{ include('buttons.back.html.twig') }} | ||||||
|  | 			</form> | ||||||
|  | 		</td> | ||||||
|  | 	</tr> | ||||||
|  | 	</tbody> | ||||||
|  | </table> | ||||||
							
								
								
									
										12
									
								
								system/templates/account.2fa.main.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								system/templates/account.2fa.main.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | {% set title = 'Two-Factor Authentication' %} | ||||||
|  |  | ||||||
|  | {% set content %} | ||||||
|  | <table style="width:100%;"> | ||||||
|  | 	<tbody> | ||||||
|  | 	{{ include(twoFactorViews[0]) }} | ||||||
|  | 	{{ include(twoFactorViews[1]) }} | ||||||
|  | 	</tbody> | ||||||
|  | </table> | ||||||
|  | {% endset %} | ||||||
|  | {% include('tables.headline.html.twig') %} | ||||||
|  | <br/> | ||||||
							
								
								
									
										18
									
								
								system/templates/account.2fa.protected.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								system/templates/account.2fa.protected.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | <tr> | ||||||
|  | 	<td> | ||||||
|  | 		<div class="TableContentContainer "> | ||||||
|  | 			<table class="TableContent" width="100%" style="border:1px solid #faf0d7;"> | ||||||
|  | 				<tbody> | ||||||
|  | 				<tr> | ||||||
|  | 					<td> | ||||||
|  | 						<div class="InTableRightButtonContainer"></div> | ||||||
|  | 						<b>Two-Factor Authenticator App</b> | ||||||
|  | 						<p>Your account is currently protected by email code authentication. If you prefer to use a <b>two-factor | ||||||
|  | 								authentication app</b>, you have to "Deactivate" email code authentication first.</p> | ||||||
|  | 					</td> | ||||||
|  | 				</tr> | ||||||
|  | 				</tbody> | ||||||
|  | 			</table> | ||||||
|  | 		</div> | ||||||
|  | 	</td> | ||||||
|  | </tr> | ||||||
| @@ -147,6 +147,9 @@ | |||||||
| 			{% include('buttons.base.html.twig') %} | 			{% include('buttons.base.html.twig') %} | ||||||
| 		</form> | 		</form> | ||||||
| 		<br/> | 		<br/> | ||||||
|  |  | ||||||
|  | 		{{ include('account.2fa.main.html.twig') }} | ||||||
|  |  | ||||||
| 		{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }} | 		{{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }} | ||||||
| 		<a name="Account+Logs" ></a> | 		<a name="Account+Logs" ></a> | ||||||
| 		<h2>Account Logs</h2> | 		<h2>Account Logs</h2> | ||||||
|   | |||||||
| @@ -235,14 +235,16 @@ | |||||||
| 															{% endif %} | 															{% endif %} | ||||||
|  |  | ||||||
| 															{% if isVice %} | 															{% if isVice %} | ||||||
| 																<form action="{{ getLink('guilds') }}?action=invite&guild={{ guild_name|url_encode }}" method="post"> | 																{% if db.hasTableAndColumns('guild_invites', ['player_id']) %} | ||||||
| 																	{{ csrf() }} | 																	<form action="{{ getLink('guilds') }}?action=invite&guild={{ guild_name|url_encode }}" method="post"> | ||||||
| 																	<td> | 																		{{ csrf() }} | ||||||
| 																		{% set button_name = 'Invite Character' %} | 																		<td> | ||||||
| 																		{% set button_image = '_sbutton_invitecharacter' %} | 																			{% set button_name = 'Invite Character' %} | ||||||
| 																		{% include('buttons.base.html.twig') %} | 																			{% set button_image = '_sbutton_invitecharacter' %} | ||||||
| 																	</td> | 																			{% include('buttons.base.html.twig') %} | ||||||
| 																</form> | 																		</td> | ||||||
|  | 																	</form> | ||||||
|  | 																{% endif %} | ||||||
|  |  | ||||||
| 																<form action="{{ getLink('guilds') }}?action=change_rank&guild={{ guild_name|url_encode }}" method="post"> | 																<form action="{{ getLink('guilds') }}?action=change_rank&guild={{ guild_name|url_encode }}" method="post"> | ||||||
| 																	{{ csrf() }} | 																	{{ csrf() }} | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								system/templates/mail.account.2fa.email-code.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								system/templates/mail.account.2fa.email-code.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | Dear {{ config.lua.serverName}} player, | ||||||
|  | <br/><br/> | ||||||
|  | Your account is protected by email code authentication, and you requested a new email code: | ||||||
|  | <br/><br/> | ||||||
|  | <p>{{ code }}</p> | ||||||
|  | <br/> | ||||||
|  | Note that the code is only valid for 24 hours. | ||||||
|  | <br/><br/> | ||||||
|  | Kind Regards, | ||||||
| @@ -0,0 +1,5 @@ | |||||||
|  | Dear {{ config.lua.serverName}} player,<br/> | ||||||
|  | <br/> | ||||||
|  | A <strong>wrong two-factor authentication code</strong> was entered for your {{ config.lua.serverName}} account. If you simply mistyped the code, please try again.<br/> | ||||||
|  | <br/> | ||||||
|  | However, if this was <strong>not you</strong>, someone else may be trying to access your account. Since they already know your password, we strongly recommend that you <strong>change your password immediately</strong>. | ||||||
| @@ -90,7 +90,7 @@ | |||||||
| 		</td> | 		</td> | ||||||
| 	</tr> | 	</tr> | ||||||
|  |  | ||||||
| 	{% if setting('core.online_record') %} | 	{% if setting('core.online_record') and record|length > 0 %} | ||||||
| 	<tr> | 	<tr> | ||||||
| 		<td class="LabelV150"><b>Online Record:</b></td> | 		<td class="LabelV150"><b>Online Record:</b></td> | ||||||
| 		<td> | 		<td> | ||||||
| @@ -161,7 +161,7 @@ | |||||||
| 			{% endif %} | 			{% endif %} | ||||||
|  |  | ||||||
| 			<td style="width:70%; text-align:left"> | 			<td style="width:70%; text-align:left"> | ||||||
| 				{{ player.name|raw }}{{ player.skull }} | 				{{ player.name|raw }}{{ player.skull|raw }} | ||||||
| 			</td> | 			</td> | ||||||
| 			<td style="width:10%">{{ player.level }}</td> | 			<td style="width:10%">{{ player.level }}</td> | ||||||
| 			<td style="width:20%">{{ player.vocation }}</td> | 			<td style="width:20%">{{ player.vocation }}</td> | ||||||
|   | |||||||
| @@ -290,6 +290,9 @@ | |||||||
| {% endset %} | {% endset %} | ||||||
| {% include 'tables.headline.html.twig' %} | {% include 'tables.headline.html.twig' %} | ||||||
| <br/> | <br/> | ||||||
|  |  | ||||||
|  | {{ include('account.2fa.main.html.twig') }} | ||||||
|  |  | ||||||
| {{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }} | {{ hook('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS') }} | ||||||
| <a name="Account+Logs" ></a> | <a name="Account+Logs" ></a> | ||||||
| <div class="TopButtonContainer"> | <div class="TopButtonContainer"> | ||||||
|   | |||||||
| @@ -943,6 +943,14 @@ img { | |||||||
|   font-size: 8pt; |   font-size: 8pt; | ||||||
|   color: red; |   color: red; | ||||||
| } | } | ||||||
|  | .AttentionSign img { | ||||||
|  | 	float: left; | ||||||
|  | 	top: 3px; | ||||||
|  | 	left: 8px; | ||||||
|  | 	width: 15px; | ||||||
|  | 	height: 13px; | ||||||
|  | 	margin-right: 5px; | ||||||
|  | } | ||||||
| .SmallBox { | .SmallBox { | ||||||
|   position: relative; |   position: relative; | ||||||
|   font-size: 1px; |   font-size: 1px; | ||||||
|   | |||||||
| @@ -164,6 +164,10 @@ if(isset($config['boxes'])) | |||||||
| 		function InitializeMenu() | 		function InitializeMenu() | ||||||
| 		{ | 		{ | ||||||
| 		  for(menuItemName in menu[0]) { | 		  for(menuItemName in menu[0]) { | ||||||
|  | 			  if (!document.getElementById(menuItemName+"_Submenu")) { | ||||||
|  | 				  continue; | ||||||
|  | 			  } | ||||||
|  |  | ||||||
| 			if(menu[0][menuItemName] == "0") { | 			if(menu[0][menuItemName] == "0") { | ||||||
| 			  document.getElementById(menuItemName+"_Submenu").style.visibility = "hidden"; | 			  document.getElementById(menuItemName+"_Submenu").style.visibility = "hidden"; | ||||||
| 			  document.getElementById(menuItemName+"_Submenu").style.display = "none"; | 			  document.getElementById(menuItemName+"_Submenu").style.display = "none"; | ||||||
| @@ -387,7 +391,7 @@ foreach($config['menu_categories'] as $id => $cat) { | |||||||
| 	?> | 	?> | ||||||
| 	</div> | 	</div> | ||||||
| 	<?php | 	<?php | ||||||
| 	if($id == MENU_CATEGORY_SHOP || (!setting('core.gifts_system') && $i == $countElements)) { | 	if ($i == $countElements) { | ||||||
| 	?> | 	?> | ||||||
| 		<div id='MenuBottom' style='background-image:url(<?php echo $template_path; ?>/images/general/box-bottom.gif);'></div> | 		<div id='MenuBottom' style='background-image:url(<?php echo $template_path; ?>/images/general/box-bottom.gif);'></div> | ||||||
| 	<?php | 	<?php | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user