From 8ae05b7466c13c30f7b4df8bcffba9fa94f28bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Lob=C3=A3o?= Date: Tue, 10 Jun 2025 01:45:11 -0300 Subject: [PATCH 1/4] Update IP address fields in multiple tables Modified IP address columns in the following tables: Changes: varchar(15) to varchar(100) cp_emailchange cp_ipbanlog cp_loginlog cp_pwchange cp_resetpass Purpose: to support IPv6 and extended IP formats. --- data/schemas/logindb/cp_emailchange.20250609165101.sql | 3 +++ data/schemas/logindb/cp_ipbanlog.20250609165101.sql | 1 + data/schemas/logindb/cp_loginlog.20250609165101.sql | 1 + data/schemas/logindb/cp_pwchange.20250609165101.sql | 1 + data/schemas/logindb/cp_resetpass.20250609165101.sql | 3 +++ 5 files changed, 9 insertions(+) create mode 100644 data/schemas/logindb/cp_emailchange.20250609165101.sql create mode 100644 data/schemas/logindb/cp_ipbanlog.20250609165101.sql create mode 100644 data/schemas/logindb/cp_loginlog.20250609165101.sql create mode 100644 data/schemas/logindb/cp_pwchange.20250609165101.sql create mode 100644 data/schemas/logindb/cp_resetpass.20250609165101.sql diff --git a/data/schemas/logindb/cp_emailchange.20250609165101.sql b/data/schemas/logindb/cp_emailchange.20250609165101.sql new file mode 100644 index 00000000..a7bf44e3 --- /dev/null +++ b/data/schemas/logindb/cp_emailchange.20250609165101.sql @@ -0,0 +1,3 @@ +ALTER TABLE cp_emailchange +MODIFY COLUMN request_ip VARCHAR(100) NOT NULL, +MODIFY COLUMN change_ip VARCHAR(100) DEFAULT NULL; \ No newline at end of file diff --git a/data/schemas/logindb/cp_ipbanlog.20250609165101.sql b/data/schemas/logindb/cp_ipbanlog.20250609165101.sql new file mode 100644 index 00000000..40f62f1a --- /dev/null +++ b/data/schemas/logindb/cp_ipbanlog.20250609165101.sql @@ -0,0 +1 @@ +ALTER TABLE cp_ipbanlog MODIFY COLUMN ip_address VARCHAR(100) NOT NULL; \ No newline at end of file diff --git a/data/schemas/logindb/cp_loginlog.20250609165101.sql b/data/schemas/logindb/cp_loginlog.20250609165101.sql new file mode 100644 index 00000000..761169b7 --- /dev/null +++ b/data/schemas/logindb/cp_loginlog.20250609165101.sql @@ -0,0 +1 @@ +ALTER TABLE cp_loginlog MODIFY COLUMN ip VARCHAR(100) NOT NULL; \ No newline at end of file diff --git a/data/schemas/logindb/cp_pwchange.20250609165101.sql b/data/schemas/logindb/cp_pwchange.20250609165101.sql new file mode 100644 index 00000000..88cebd0f --- /dev/null +++ b/data/schemas/logindb/cp_pwchange.20250609165101.sql @@ -0,0 +1 @@ +ALTER TABLE cp_pwchange MODIFY COLUMN change_ip VARCHAR(100) NOT NULL; \ No newline at end of file diff --git a/data/schemas/logindb/cp_resetpass.20250609165101.sql b/data/schemas/logindb/cp_resetpass.20250609165101.sql new file mode 100644 index 00000000..d33a1cdb --- /dev/null +++ b/data/schemas/logindb/cp_resetpass.20250609165101.sql @@ -0,0 +1,3 @@ +ALTER TABLE cp_resetpass +MODIFY COLUMN request_ip VARCHAR(100) NOT NULL, +MODIFY COLUMN reset_ip VARCHAR(100) DEFAULT NULL; \ No newline at end of file From d4920ef764e093eb14180c63741df1c74781045c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Lob=C3=A3o?= Date: Sat, 14 Jun 2025 12:34:24 -0300 Subject: [PATCH 2/4] fix(db): reduce IP field length from 100 to 39 characters --- data/schemas/logindb/cp_createlog.20080928225124.sql | 4 ++-- data/schemas/logindb/cp_emailchange.20250609165101.sql | 4 ++-- data/schemas/logindb/cp_loginlog.20250609165101.sql | 2 +- data/schemas/logindb/cp_pwchange.20250609165101.sql | 2 +- data/schemas/logindb/cp_resetpass.20250609165101.sql | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/data/schemas/logindb/cp_createlog.20080928225124.sql b/data/schemas/logindb/cp_createlog.20080928225124.sql index 5e45e0a1..eed3a5b8 100644 --- a/data/schemas/logindb/cp_createlog.20080928225124.sql +++ b/data/schemas/logindb/cp_createlog.20080928225124.sql @@ -6,8 +6,8 @@ CREATE TABLE `cp_createlog` ( `sex` enum('M','F','S') NOT NULL default 'M', `email` varchar(39) NOT NULL, `reg_date` datetime NOT NULL default '0000-00-00 00:00:00', - `reg_ip` varchar(100) NOT NULL, + `reg_ip` varchar(39) NOT NULL, `delete_date` datetime default NULL, PRIMARY KEY (`id`), KEY `name` (`userid`) -) ENGINE=MyISAM ; \ No newline at end of file +) ENGINE=MyISAM; diff --git a/data/schemas/logindb/cp_emailchange.20250609165101.sql b/data/schemas/logindb/cp_emailchange.20250609165101.sql index a7bf44e3..deba0b56 100644 --- a/data/schemas/logindb/cp_emailchange.20250609165101.sql +++ b/data/schemas/logindb/cp_emailchange.20250609165101.sql @@ -1,3 +1,3 @@ ALTER TABLE cp_emailchange -MODIFY COLUMN request_ip VARCHAR(100) NOT NULL, -MODIFY COLUMN change_ip VARCHAR(100) DEFAULT NULL; \ No newline at end of file +MODIFY COLUMN request_ip VARCHAR(39) NOT NULL, +MODIFY COLUMN change_ip VARCHAR(39) DEFAULT NULL; diff --git a/data/schemas/logindb/cp_loginlog.20250609165101.sql b/data/schemas/logindb/cp_loginlog.20250609165101.sql index 761169b7..418b98a5 100644 --- a/data/schemas/logindb/cp_loginlog.20250609165101.sql +++ b/data/schemas/logindb/cp_loginlog.20250609165101.sql @@ -1 +1 @@ -ALTER TABLE cp_loginlog MODIFY COLUMN ip VARCHAR(100) NOT NULL; \ No newline at end of file +ALTER TABLE cp_loginlog MODIFY COLUMN ip VARCHAR(39) NOT NULL; diff --git a/data/schemas/logindb/cp_pwchange.20250609165101.sql b/data/schemas/logindb/cp_pwchange.20250609165101.sql index 88cebd0f..8adca5c9 100644 --- a/data/schemas/logindb/cp_pwchange.20250609165101.sql +++ b/data/schemas/logindb/cp_pwchange.20250609165101.sql @@ -1 +1 @@ -ALTER TABLE cp_pwchange MODIFY COLUMN change_ip VARCHAR(100) NOT NULL; \ No newline at end of file +ALTER TABLE cp_pwchange MODIFY COLUMN change_ip VARCHAR(39) NOT NULL; diff --git a/data/schemas/logindb/cp_resetpass.20250609165101.sql b/data/schemas/logindb/cp_resetpass.20250609165101.sql index d33a1cdb..f8dbfde4 100644 --- a/data/schemas/logindb/cp_resetpass.20250609165101.sql +++ b/data/schemas/logindb/cp_resetpass.20250609165101.sql @@ -1,3 +1,3 @@ ALTER TABLE cp_resetpass -MODIFY COLUMN request_ip VARCHAR(100) NOT NULL, -MODIFY COLUMN reset_ip VARCHAR(100) DEFAULT NULL; \ No newline at end of file +MODIFY COLUMN request_ip VARCHAR(39) NOT NULL, +MODIFY COLUMN reset_ip VARCHAR(39) DEFAULT NULL; From e63de54ca6c9459cacc637ddcc7e4a43e32a93fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Lob=C3=A3o?= Date: Tue, 17 Jun 2025 18:42:14 -0300 Subject: [PATCH 3/4] fix(db): reduce IP field length from 100 to 39 characters and end of line. --- data/schemas/logindb/cp_ipbanlog.20250609165101.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/schemas/logindb/cp_ipbanlog.20250609165101.sql b/data/schemas/logindb/cp_ipbanlog.20250609165101.sql index 40f62f1a..03d9137d 100644 --- a/data/schemas/logindb/cp_ipbanlog.20250609165101.sql +++ b/data/schemas/logindb/cp_ipbanlog.20250609165101.sql @@ -1 +1 @@ -ALTER TABLE cp_ipbanlog MODIFY COLUMN ip_address VARCHAR(100) NOT NULL; \ No newline at end of file +ALTER TABLE cp_ipbanlog MODIFY COLUMN ip_address VARCHAR(39) NOT NULL; From e4bf455be388ed8ab44e3fc0c2ae8ed4ef271648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Lob=C3=A3o?= Date: Thu, 26 Jun 2025 03:16:38 -0300 Subject: [PATCH 4/4] fix: resolve deprecated notices and warnings in recent PHP versions fix(compatibility): resolve deprecated notices and warnings for PHP 8.x+ - Replaced deprecated function calls and adjusted code to comply with modern PHP standards. - Addressed various warnings related to optional parameters and null type declarations. - Refactored equipJobsToArray() for clarity and compatibility. - Unnecessary indentation and trailing whitespace were removed across multiple files. docs(readme): updated README with installation guide and PHP requirements - Added a basic installation guide to README.md. - Updated minimum PHP version to 7.0 due to usage of the null coalescing - operator (??). - Improved formatting and documentation consistency. BREAKING CHANGE: Requires PHP >= 7.0. --- README.md | 131 ++++++++++--- lib/Flux.php | 255 ++++++++++++------------ lib/Flux/Config.php | 32 +-- lib/Flux/Template.php | 290 ++++++++++++++-------------- modules/cplog/ban.php | 4 +- modules/cplog/changemail.php | 14 +- modules/cplog/changepass.php | 8 +- modules/cplog/ipban.php | 4 +- modules/cplog/login.php | 4 +- modules/cplog/paypal.php | 8 +- modules/cplog/resetpass.php | 13 +- modules/item/index.php | 60 +++--- modules/logdata/login.php | 14 +- modules/monster/index.php | 54 +++--- modules/ranking/alchemist.php | 2 +- modules/ranking/blacksmith.php | 2 +- modules/ranking/character.php | 2 +- modules/ranking/death.php | 2 +- modules/ranking/zeny.php | 2 +- themes/default/account/create.php | 22 +-- themes/default/account/index.php | 20 +- themes/default/account/login.php | 2 +- themes/default/character/online.php | 8 +- themes/default/cplog/ban.php | 4 +- themes/default/cplog/changemail.php | 14 +- themes/default/cplog/changepass.php | 10 +- themes/default/cplog/ipban.php | 4 +- themes/default/cplog/login.php | 12 +- themes/default/cplog/paypal.php | 16 +- themes/default/cplog/resetpass.php | 12 +- themes/default/ipban/add.php | 4 +- themes/default/ipban/edit.php | 2 +- themes/default/item/add.php | 8 +- themes/default/item/edit.php | 2 +- themes/default/item/index.php | 24 +-- themes/default/logdata/login.php | 10 +- themes/default/monster/index.php | 10 +- themes/default/monster/view.php | 10 +- 38 files changed, 587 insertions(+), 508 deletions(-) diff --git a/README.md b/README.md index 0d9c06d5..a0fde2dc 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,100 @@ -FluxCP -====== - -Flux Control Panel (FluxCP) for Hercules server. - -Requirements ---------- -* PHP 5.2 -* PDO and PDO-MYSQL extensions for PHP5 (including PHP_MYSQL support) -* MySQL 5 -* Optional: GD2 (for guild emblems and registration CAPTCHA) -* Optional: Tidy (for cleaner HTML output) -* Optional: mod_rewrite support for UseCleanUrls feature - -Authors ---------- -The following people have contributed to the development of FluxCP through the years. - -Thank you for all your hard work. - -* Boooo -* Byteflux86 -* Gepard -* hemagx -* jaBote -* Mumbles -* Mysterious -* Paradox924X -* shugotenshi -* Streusel -* Xantara +# FluxCP - Hercules Version + +FluxCP is a **web-based control panel** for managing [Hercules](https://github.com/HerculesWS/Hercules) servers. +It allows players to register accounts, view server stats, rankings, and provides administrative tools for GMs. + +--- + +## 📋 Requirements + +- **PHP 7.0+** +- **MySQL 5.x+** +- **PHP extensions**: + - `PDO` and `PDO_MYSQL` + - `GD2` (optional – for displaying guild emblems and CAPTCHA) + - `Tidy` (optional – for cleaner HTML output) +- **Apache (recommended)** with: + - `mod_rewrite` enabled (optional – for UseCleanUrls feature) + +--- + +## ⚙️ Installation Guide + +1. **Download and extract FluxCP** to your web server directory (e.g. `public_html/`, `www/` or `htdocs`): + +```bash +git clone https://github.com/HerculesWS/FluxCP.git +``` + +2. **Create a database** for your control panel (separate from your main RO databases, optional but recommended). + +3. **Edit the application configuration**: + + Open the file `config/application.php` and configure the following: +```php + 'ServerAddress' => 'mysite.com', + 'BaseURI' => '', + 'InstallerPassword' => 'InstallPasswordHere', +``` + - `ServerAddress` with your domain, e.g., `mysite.com` + - `BaseURI` only if the files are not in the web server root, e.g., `/site` or `/painel` + - `InstallerPassword` with a secure password for the installation page + - `ThemeName` (optional) if using custom themes; add your theme to the array + + **PayPal setup (optional):** + - `DonationCurrency` with the currency code, e.g., `USD` for US dollars + - `AcceptDonations` must be set to `true` + - `PayPalBusinessEmail` with your PayPal account email + +4. **Configure the server and database connection**: + + Open the file `config/servers.php` and set: +```php +'ServerName' => 'FluxRO', +'DbConfig' => array( + ... + 'Hostname' => '127.0.0.1', + 'Username' => 'ragnarok', + 'Password' => 'ragnarok', + 'Database' => 'ragnarok', + ... +'LogsDbConfig' => array( + ... + 'Username' => 'ragnarok_log', + 'Password' => 'ragnarok_log', + 'Database' => 'ragnarok_log', + ... +``` + - `ServerName` with the name of your server + - Under `DbConfig` and `LogsDbConfig`, set `hostname`, `username`, `password`, and `database` with the correct access credentials. If FluxCP uses the same database as your emulator, the values will likely be similar. + - In `LoginServer`, update `Address` and `Port` with the correct login server details + - In `CharMapServers`, update: + - `ServerName` with your server's name + - `Renewal` to `true` and `PreRenewal` to `false` + - `ExpRates` and `DropRates` if using FluxCP's web database + - `CharServer` and `MapServer` with the correct `Address` and `Port` + +5. **Point your browser** to the folder where FluxCP is installed and follow the web-based installer steps. + +6. **Done!** You should now be able to access your FluxCP panel. + +--- + +## 👥 Contributors + +The following people have contributed to the development of FluxCP over the years. +Thank you for your hard work and dedication! + +|# |# |# | +|---|---|---| +| Boooo | Byteflux86 | Gepard | +| hemagx | jaBote | Mumbles | +| Mysterious | Paradox924X | shugotenshi | +| Streusel | Xantara | | + + +--- + +## 📄 License + +FluxCP is released under the terms of the LGPL-3.0 License. \ No newline at end of file diff --git a/lib/Flux.php b/lib/Flux.php index 729fb31c..124a2a0b 100755 --- a/lib/Flux.php +++ b/lib/Flux.php @@ -23,12 +23,12 @@ class Flux { * Current version. */ const VERSION = '2.0.0'; - + /** * Repository SVN version or GIT hash of the top-level revision. */ const REPOSVERSION = FLUX_REPOSVERSION; - + /** * Application-specific configuration object. * @@ -36,7 +36,7 @@ class Flux { * @var Flux_Config */ public static $appConfig; - + /** * Servers configuration object. * @@ -44,7 +44,7 @@ class Flux { * @var Flux_Config */ public static $serversConfig; - + /** * Messages configuration object. * @@ -52,7 +52,7 @@ class Flux { * @var Flux_Config */ public static $messagesConfig; - + /** * Collection of Flux_Athena objects. * @@ -60,7 +60,7 @@ class Flux { * @var array */ public static $servers = array(); - + /** * Registry where Flux_LoginAthenaGroup instances are kept for easy * searching. @@ -69,7 +69,7 @@ class Flux { * @var array */ public static $loginAthenaGroupRegistry = array(); - + /** * Registry where Flux_Athena instances are kept for easy searching. * @@ -77,7 +77,7 @@ class Flux { * @var array */ public static $athenaServerRegistry = array(); - + /** * Object containing all of Flux's session data. * @@ -85,17 +85,17 @@ class Flux { * @var Flux_SessionData */ public static $sessionData; - + /** * */ public static $numberOfQueries = 0; - + /** * */ public static $addons = array(); - + /** * Initialize Flux application. This will handle configuration parsing and * instanciating of objects crucial to the control panel. @@ -113,24 +113,24 @@ public static function initialize($options = array()) self::raise("Missing required option `$option' in Flux::initialize()"); } } - + // Parse application and server configuration files, this will also // handle configuration file normalization. See the source for the // below methods for more details on what's being done. self::$appConfig = self::parseAppConfigFile($options['appConfigFile']); self::$serversConfig = self::parseServersConfigFile($options['serversConfigFile']); //self::$messagesConfig = self::parseMessagesConfigFile($options['messagesConfigFile']); // Deprecated. - + // Using newer language system. self::$messagesConfig = self::parseLanguageConfigFile(); - + // Initialize server objects. self::initializeServerObjects(); - + // Initialize add-ons. self::initializeAddons(); } - + /** * Initialize each Login/Char/Map server object and contain them in their * own collective Athena object. @@ -144,28 +144,28 @@ public static function initializeServerObjects() foreach (self::$serversConfig->getChildrenConfigs() as $key => $config) { $connection = new Flux_Connection($config->getDbConfig(), $config->getLogsDbConfig()); $loginServer = new Flux_LoginServer($config->getLoginServer()); - + // LoginAthenaGroup maintains the grouping of a central login // server and its underlying Athena objects. self::$servers[$key] = new Flux_LoginAthenaGroup($config->getServerName(), $connection, $loginServer); - + // Add into registry. self::registerServerGroup($config->getServerName(), self::$servers[$key]); - + foreach ($config->getCharMapServers()->getChildrenConfigs() as $charMapServer) { $charServer = new Flux_CharServer($charMapServer->getCharServer()); $mapServer = new Flux_MapServer($charMapServer->getMapServer()); - + // Create the collective server object, Flux_Athena. $athena = new Flux_Athena($charMapServer, $loginServer, $charServer, $mapServer); self::$servers[$key]->addAthenaServer($athena); - + // Add into registry. self::registerAthenaServer($config->getServerName(), $charMapServer->getServerName(), $athena); } } } - + /** * */ @@ -174,20 +174,20 @@ public static function initializeAddons() if (!is_dir(FLUX_ADDON_DIR)) { return false; } - + foreach (glob(FLUX_ADDON_DIR.'/*') as $addonDir) { if (is_dir($addonDir)) { $addonName = basename($addonDir); $addonObject = new Flux_Addon($addonName, $addonDir); self::$addons[$addonName] = $addonObject; - + // Merge configurations. self::$appConfig->merge($addonObject->addonConfig); self::$messagesConfig->merge($addonObject->messagesConfig, false); } } } - + /** * Wrapper method for setting and getting values from the appConfig. * @@ -205,7 +205,7 @@ public static function config($key, $value = null, $options = array()) return self::$appConfig->get($key); } } - + /** * Wrapper method for setting and getting values from the messagesConfig. * @@ -223,7 +223,7 @@ public static function message($key, $value = null, $options = array()) return self::$messagesConfig->get($key); } } - + /** * Convenience method for raising Flux_Error exceptions. * @@ -246,7 +246,7 @@ public static function parseConfig(array $configArr) { return new Flux_Config($configArr); } - + /** * Parse a PHP array returned as the result of an included file into a * Flux_Config configuration object. @@ -258,7 +258,7 @@ public static function parseConfigFile($filename, $cache=true) { $basename = basename(str_replace(' ', '', ucwords(str_replace(array('/', '\\', '_'), ' ', $filename))), '.php').'.cache.php'; $cachefile = FLUX_DATA_DIR."/tmp/$basename"; - + if ($cache && file_exists($cachefile) && filemtime($cachefile) > filemtime($filename)) { return unserialize(file_get_contents($cachefile, false, null, 28)); } @@ -267,7 +267,7 @@ public static function parseConfigFile($filename, $cache=true) // Uses require, thus assumes the file returns an array. $config = require $filename; ob_end_clean(); - + // Cache config file. $cf = self::parseConfig($config); @@ -280,11 +280,11 @@ public static function parseConfigFile($filename, $cache=true) fwrite($fp, $s=serialize($cf), strlen($s)); fclose($fp); } - + return $cf; } } - + /** * Parse a file in an application-config specific manner. * @@ -294,7 +294,7 @@ public static function parseConfigFile($filename, $cache=true) public static function parseAppConfigFile($filename) { $config = self::parseConfigFile($filename, false); - + if (!$config->getServerAddress()) { self::raise("ServerAddress must be specified in your application config."); } @@ -311,7 +311,7 @@ public static function parseAppConfigFile($filename) if (!($config->getPayPalReceiverEmails() instanceOf Flux_Config)) { self::raise("PayPalReceiverEmails must be an array."); } - + // Sanitize BaseURI. (leading forward slash is mandatory.) $baseURI = $config->get('BaseURI'); if (strlen($baseURI) && $baseURI[0] != '/') { @@ -320,10 +320,10 @@ public static function parseAppConfigFile($filename) elseif (trim($baseURI) === '') { $config->set('BaseURI', '/'); } - + return $config; } - + /** * Parse a file in a servers-config specific manner. This method gets a bit * nasty so beware of ugly code ;) @@ -337,45 +337,45 @@ public static function parseServersConfigFile($filename) $options = array('overwrite' => false, 'force' => true); // Config::set() options. $serverNames = array(); $athenaServerNames = array(); - + if (!count($config->toArray())) { self::raise('At least one server configuration must be present.'); } - + foreach ($config->getChildrenConfigs() as $topConfig) { // // Top-level normalization. // - + if (!($serverName = $topConfig->getServerName())) { self::raise('ServerName is required for each top-level server configuration, check your servers configuration file.'); } elseif (in_array($serverName, $serverNames)) { self::raise("The server name '$serverName' has already been configured. Please use another name."); } - + $serverNames[] = $serverName; $athenaServerNames[$serverName] = array(); - + $topConfig->setDbConfig(array(), $options); $topConfig->setLogsDbConfig(array(), $options); $topConfig->setLoginServer(array(), $options); $topConfig->setCharMapServers(array(), $options); - + $dbConfig = $topConfig->getDbConfig(); $logsDbConfig = $topConfig->getLogsDbConfig(); $loginServer = $topConfig->getLoginServer(); - + foreach (array($dbConfig, $logsDbConfig) as $_dbConfig) { $_dbConfig->setHostname('localhost', $options); $_dbConfig->setUsername('ragnarok', $options); $_dbConfig->setPassword('ragnarok', $options); $_dbConfig->setPersistent(true, $options); } - + $loginServer->setDatabase($dbConfig->getDatabase(), $options); $loginServer->setUseMD5(true, $options); - + // Raise error if missing essential configuration directives. if (!$loginServer->getAddress()) { self::raise('Address is required for each LoginServer section in your servers configuration.'); @@ -383,11 +383,11 @@ public static function parseServersConfigFile($filename) elseif (!$loginServer->getPort()) { self::raise('Port is required for each LoginServer section in your servers configuration.'); } - + if (!$topConfig->getCharMapServers() || !count($topConfig->getCharMapServers()->toArray())) { self::raise('CharMapServers must be an array and contain at least 1 char/map server entry.'); } - + foreach ($topConfig->getCharMapServers()->getChildrenConfigs() as $charMapServer) { // // Char/Map normalization. @@ -417,25 +417,25 @@ public static function parseServersConfigFile($filename) $charMapServer->setRenewal(true, $options); $charMapServer->setCharServer(array(), $options); $charMapServer->setMapServer(array(), $options); - $charMapServer->setDatabase($dbConfig->getDatabase(), $options); - + $charMapServer->setDatabase($dbConfig->getDatabase(), $options); + if (!($athenaServerName = $charMapServer->getServerName())) { self::raise('ServerName is required for each CharMapServers pair in your servers configuration.'); } elseif (in_array($athenaServerName, $athenaServerNames[$serverName])) { self::raise("The server name '$athenaServerName' under '$serverName' has already been configured. Please use another name."); } - + $athenaServerNames[$serverName][] = $athenaServerName; $charServer = $charMapServer->getCharServer(); - + if (!$charServer->getAddress()) { self::raise('Address is required for each CharServer section in your servers configuration.'); } elseif (!$charServer->getPort()) { self::raise('Port is required for each CharServer section in your servers configuration.'); } - + $mapServer = $charMapServer->getMapServer(); if (!$mapServer->getAddress()) { self::raise('Address is required for each MapServer section in your servers configuration.'); @@ -445,10 +445,10 @@ public static function parseServersConfigFile($filename) } } } - + return $config; } - + /** * Parses a messages configuration file. (Deprecated) * @@ -461,7 +461,7 @@ public static function parseMessagesConfigFile($filename) // Nothing yet. return $config; } - + /** * Parses a language configuration file, can also parse a language config * for any addon. @@ -473,11 +473,11 @@ public static function parseLanguageConfigFile($addonName=null) { $default = $addonName ? FLUX_ADDON_DIR."/$addonName/lang/en_us.php" : FLUX_LANG_DIR.'/en_us.php'; $current = $default; - + if ($lang=self::config('DefaultLanguage')) { $current = $addonName ? FLUX_ADDON_DIR."/$addonName/lang/$lang.php" : FLUX_LANG_DIR."/$lang.php"; } - + if (file_exists($default)) { $def = self::parseConfigFile($default); } @@ -485,15 +485,15 @@ public static function parseLanguageConfigFile($addonName=null) $tmp = array(); $def = new Flux_Config($tmp); } - + if ($current != $default && file_exists($current)) { $cur = self::parseConfigFile($current); $def->merge($cur, false); } - + return $def; } - + /** * Check whether or not a theme exists. * @@ -504,7 +504,7 @@ public static function themeExists($themeName) { return is_dir(FLUX_THEME_DIR."/$themeName"); } - + /** * Register the server group into the registry. * @@ -518,7 +518,7 @@ private static function registerServerGroup($serverName, Flux_LoginAthenaGroup $ self::$loginAthenaGroupRegistry[$serverName] = $serverGroup; return $serverGroup; } - + /** * Register the Athena server into the registry. * @@ -533,11 +533,11 @@ private static function registerAthenaServer($serverName, $athenaServerName, Flu if (!array_key_exists($serverName, self::$athenaServerRegistry) || !is_array(self::$athenaServerRegistry[$serverName])) { self::$athenaServerRegistry[$serverName] = array(); } - + self::$athenaServerRegistry[$serverName][$athenaServerName] = $athenaServer; return $athenaServer; } - + /** * Get Flux_LoginAthenaGroup server object by its ServerName. * @@ -548,7 +548,7 @@ private static function registerAthenaServer($serverName, $athenaServerName, Flu public static function getServerGroupByName($serverName) { $registry = &self::$loginAthenaGroupRegistry; - + if (array_key_exists($serverName, $registry) && $registry[$serverName] instanceOf Flux_LoginAthenaGroup) { return $registry[$serverName]; } @@ -556,7 +556,7 @@ public static function getServerGroupByName($serverName) return false; } } - + /** * Get Flux_Athena instance by its group/server names. * @@ -570,14 +570,14 @@ public static function getAthenaServerByName($serverName, $athenaServerName) $registry = &self::$athenaServerRegistry; if (array_key_exists($serverName, $registry) && array_key_exists($athenaServerName, $registry[$serverName]) && $registry[$serverName][$athenaServerName] instanceOf Flux_Athena) { - + return $registry[$serverName][$athenaServerName]; } else { return false; } } - + /** * Hashes a password for use in comparison with the login.user_pass column. * @@ -590,7 +590,7 @@ public static function hashPassword($password) // Default hashing schema is MD5. return md5($password); } - + /** * Get the job class name from a job ID. * @@ -602,7 +602,7 @@ public static function getJobClass($id) { $key = "JobClasses.$id"; $class = self::config($key); - + if ($class) { return $class; } @@ -610,7 +610,7 @@ public static function getJobClass($id) return false; } } - + /** * Get the job ID from a job class name. * @@ -628,7 +628,7 @@ public static function getJobID($class) return false; } } - + /** * Get the homunculus class name from a homun class ID. * @@ -640,7 +640,7 @@ public static function getHomunClass($id) { $key = "HomunClasses.$id"; $class = self::config($key); - + if ($class) { return $class; } @@ -661,11 +661,11 @@ public static function getItemType($id, $id2) { $key = "ItemTypes.$id"; $type = self::config($key); - + if ($id2) { $key = "ItemTypes2.$id.$id2"; $type2 = self::config($key); - + if ($type && $type2) { $type .= ' - ' . $type2; } @@ -673,7 +673,7 @@ public static function getItemType($id, $id2) $type = $type2; } } - + if ($type) { return $type; } @@ -681,7 +681,7 @@ public static function getItemType($id, $id2) return false; } } - + /** * Get the equip location combination name from an equip location combination type. * @@ -693,7 +693,7 @@ public static function getEquipLocationCombination($id) { $key = "EquipLocationCombinations.$id"; $combination = self::config($key); - + if ($combination) { return $combination; } @@ -701,7 +701,7 @@ public static function getEquipLocationCombination($id) return false; } } - + /** * Process donations that have been put on hold. */ @@ -712,19 +712,19 @@ public static function processHeldCredits() $trustTable = self::config('FluxTables.DonationTrustTable'); $loginAthenaGroups = self::$loginAthenaGroupRegistry; list ($cancel, $accept) = array(array(), array()); - + foreach ($loginAthenaGroups as $loginAthenaGroup) { $sql = "SELECT account_id, payer_email, credits, mc_gross, txn_id, hold_until "; $sql .= "FROM {$loginAthenaGroup->loginDatabase}.$txnLogTable "; $sql .= "WHERE account_id > 0 AND hold_until IS NOT NULL AND payment_status = 'Completed'"; $sth = $loginAthenaGroup->connection->getStatement($sql); - + if ($sth->execute() && ($txn=$sth->fetchAll())) { foreach ($txn as $t) { $sql = "SELECT id FROM {$loginAthenaGroup->loginDatabase}.$txnLogTable "; $sql .= "WHERE payment_status IN ('Cancelled_Reversed', 'Reversed', 'Refunded') AND parent_txn_id = ? LIMIT 1"; $sth = $loginAthenaGroup->connection->getStatement($sql); - + if ($sth->execute(array($t->txn_id)) && ($r=$sth->fetch()) && $r->id) { $cancel[] = $t->txn_id; } @@ -733,7 +733,7 @@ public static function processHeldCredits() } } } - + if (!empty($cancel)) { $ids = implode(', ', array_fill(0, count($cancel), '?')); $sql = "UPDATE {$loginAthenaGroup->loginDatabase}.$txnLogTable "; @@ -741,29 +741,29 @@ public static function processHeldCredits() $sth = $loginAthenaGroup->connection->getStatement($sql); $sth->execute($cancel); } - + $sql2 = "INSERT INTO {$loginAthenaGroup->loginDatabase}.$trustTable (account_id, email, create_date)"; $sql2 .= "VALUES (?, ?, NOW())"; $sth2 = $loginAthenaGroup->connection->getStatement($sql2); - + $sql3 = "SELECT id FROM {$loginAthenaGroup->loginDatabase}.$trustTable WHERE "; $sql3 .= "delete_date IS NULL AND account_id = ? AND email = ? LIMIT 1"; $sth3 = $loginAthenaGroup->connection->getStatement($sql3); - + $idvals = array(); - + foreach ($accept as $txn) { $loginAthenaGroup->loginServer->depositCredits($txn->account_id, $txn->credits, $txn->mc_gross); $sth3->execute(array($txn->account_id, $txn->payer_email)); $row = $sth3->fetch(); - + if (!$row) { $sth2->execute(array($txn->account_id, $txn->payer_email)); } - + $idvals[] = $txn->txn_id; } - + if (!empty($idvals)) { $ids = implode(', ', array_fill(0, count($idvals), '?')); $sql = "UPDATE {$loginAthenaGroup->loginDatabase}.$txnLogTable "; @@ -774,7 +774,7 @@ public static function processHeldCredits() } } } - + /** * */ @@ -782,18 +782,18 @@ public static function pruneUnconfirmedAccounts() { $tbl = Flux::config('FluxTables.AccountCreateTable'); $expire = (int)Flux::config('EmailConfirmExpire'); - + foreach (self::$loginAthenaGroupRegistry as $loginAthenaGroup) { $db = $loginAthenaGroup->loginDatabase; $sql = "DELETE $db.login, $db.$tbl FROM $db.login INNER JOIN $db.$tbl "; $sql .= "WHERE login.account_id = $tbl.account_id AND $tbl.confirmed = 0 "; $sql .= "AND $tbl.confirm_code IS NOT NULL AND $tbl.confirm_expire <= NOW()"; $sth = $loginAthenaGroup->connection->getStatement($sql); - + $sth->execute(); } } - + /** * Get array of equip_location bits. (bit => loc_name pairs) * @return array @@ -803,7 +803,7 @@ public static function getEquipLocationList() $equiplocations = Flux::config('EquipLocations')->toArray(); return $equiplocations; } - + /** * Get array of equip_upper bits. (bit => upper_name pairs) * @return array @@ -813,7 +813,7 @@ public static function getEquipUpperList() $equipupper = Flux::config('EquipUpper')->toArray(); return $equipupper; } - + /** * Get array of equip_jobs bits. (bit => job_name pairs) */ @@ -822,7 +822,7 @@ public static function getEquipJobsList() $equipjobs = Flux::config('EquipJobs')->toArray(); return $equipjobs; } - + /** * Check whether a particular item type is stackable. * @param int $type @@ -833,7 +833,7 @@ public static function isStackableItemType($type) $nonstackables = array(1, 4, 5, 7, 8, 9); return !in_array($type, $nonstackables); } - + /** * Perform a bitwise AND from each bit in getEquipLocationList() on $bitmask * to determine which bits have been set. @@ -844,16 +844,16 @@ public static function equipLocationsToArray($bitmask) { $arr = array(); $bits = self::getEquipLocationList(); - + foreach ($bits as $bit => $name) { if ($bitmask & $bit) { $arr[] = $bit; } } - + return $arr; } - + /** * Perform a bitwise AND from each bit in getEquipUpperList() on $bitmask * to determine which bits have been set. @@ -864,36 +864,45 @@ public static function equipUpperToArray($bitmask) { $arr = array(); $bits = self::getEquipUpperList(); - + foreach ($bits as $bit => $name) { if ($bitmask & $bit) { $arr[] = $bit; } } - + return $arr; } - + /** * Perform a bitwise AND from each bit in getEquipJobsList() on $bitmask * to determine which bits have been set. - * @param int $bitmask - * @return array + * + * @param int|string $bitmask Bitmask as an integer or numeric string + * @return int[] Array of bits set in the bitmask */ public static function equipJobsToArray($bitmask) { - $arr = array(); - $bits = self::getEquipJobsList(); - - foreach ($bits as $bit => $name) { - if ($bitmask & $bit) { - $arr[] = $bit; - } - } - - return $arr; + $arr = array(); + $bits = self::getEquipJobsList(); + + // Safe conversion to int, avoids warnings in newer PHP versions (8.1+) + if (is_string($bitmask) && is_numeric($bitmask)) { + // Casting to float first prevents precision warnings in PHP 8.1+ + $bitmask = (int)(float)$bitmask; + } elseif (!is_int($bitmask)) { + $bitmask = 0; + } + + foreach ($bits as $bit => $name) { + if ($bitmask & $bit) { + $arr[] = $bit; + } + } + + return $arr; } - + /** * */ @@ -901,16 +910,16 @@ public static function monsterModeToArray($bitmask) { $arr = array(); $bits = self::config('MonsterModes')->toArray(); - + foreach ($bits as $bit => $name) { if ($bitmask & $bit) { $arr[] = $bit; } } - + return $arr; } - + /** * */ @@ -918,10 +927,10 @@ public static function elementName($ele) { $neutral = Flux::config('Elements.0'); $element = Flux::config("Elements.$ele"); - + return is_null($element) ? $neutral : $element; } - + /** * */ @@ -930,7 +939,7 @@ public static function monsterRaceName($race) $race = Flux::config("MonsterRaces.$race"); return $race; } - + /** * */ diff --git a/lib/Flux/Config.php b/lib/Flux/Config.php index ffeab73a..a9cf2c14 100644 --- a/lib/Flux/Config.php +++ b/lib/Flux/Config.php @@ -15,7 +15,7 @@ class Flux_Config { * @var array */ private $configArr; - + /** * Default options for setter. * @@ -23,7 +23,7 @@ class Flux_Config { * @var array */ private $defaultSetOptions = array('overwrite' => true, 'force' => true); - + /** * This is here for any developer's convenience, just in case he/she would * like to re-use this library without having to depend on the Flux_Error @@ -37,7 +37,7 @@ class Flux_Config { * @var string */ private $exceptionClass = 'Flux_Error'; - + /** * Construct a Flux_Config instance which acts as a more convenient * accessor to the specified configuration array. @@ -49,7 +49,7 @@ public function __construct(array &$configArr) { $this->configArr = &$configArr; } - + /** * This is here... for no real GOOD reason, but should the need arise, at * least you aren't deprived of access to it. @@ -61,7 +61,7 @@ public function &toArray() { return $this->configArr; } - + /** * Goes through each child in the array which is also an array, and returns * them collectively as an array of Flux_Config instances. @@ -79,7 +79,7 @@ public function &getChildrenConfigs() } return $children; } - + /** * Get the value held by the specified key. If the value is an array it * will be returned as an instance of Flux_Config by default, unless @@ -94,10 +94,10 @@ public function &getChildrenConfigs() */ public function get($key, $configObjectIfArray = true) { - $keys = explode('.', $key); + $keys = explode('.', $key ?? ''); $base = &$this->configArr; $size = count($keys) - 1; - + for ($i = 0; $i < $size; ++$i) { $currentKey = $keys[$i]; if (is_array($base) && array_key_exists($currentKey, $base)) { @@ -108,7 +108,7 @@ public function get($key, $configObjectIfArray = true) return null; } } - + $currentKey = $keys[$size]; if (array_key_exists($currentKey, $base)) { $value = &$base[$currentKey]; @@ -130,7 +130,7 @@ public function get($key, $configObjectIfArray = true) return null; } } - + /** * Set a key to hold the specified value. The format for specifying a key * is 100% identical to Flux_Config::get(). @@ -150,7 +150,7 @@ public function set($key, $value, $options = array()) $keys = explode('.', $key); $base = &$this->configArr; $size = count($keys) - 1; - + for ($i = 0; $i < $size; ++$i) { $currentKey = $keys[$i]; if (is_array($base) && array_key_exists($currentKey, $base)) { @@ -165,16 +165,16 @@ public function set($key, $value, $options = array()) return false; } } - + $currentKey = $keys[$size]; if (array_key_exists($currentKey, $base) && !$opts['overwrite']) { return false; } - + $base[$currentKey] = $value; return $value; } - + /** * Convenience method for raising an internal exception. * @@ -186,7 +186,7 @@ public function raise($message) $exceptionClass = $this->exceptionClass; throw new $exceptionClass($message); } - + /** * Adds the ability to call set() as native methods. * @@ -212,7 +212,7 @@ public function __call($method, $args = array()) return $this->set($m[1], $args[0], $options); } } - + /** * */ diff --git a/lib/Flux/Template.php b/lib/Flux/Template.php index 0549a808..1fac4254 100644 --- a/lib/Flux/Template.php +++ b/lib/Flux/Template.php @@ -22,7 +22,7 @@ class Flux_Template { * @var array */ private $defaultData = array(); - + /** * Request parameters. * @@ -30,7 +30,7 @@ class Flux_Template { * @var Flux_Config */ protected $params; - + /** * Base URI of the entire application. * @@ -38,7 +38,7 @@ class Flux_Template { * @var string */ protected $basePath; - + /** * Module path. * @@ -46,7 +46,7 @@ class Flux_Template { * @var string */ protected $modulePath; - + /** * Module name. * @@ -54,7 +54,7 @@ class Flux_Template { * @var string */ protected $moduleName; - + /** * Theme path. * @@ -62,7 +62,7 @@ class Flux_Template { * @var string */ protected $themePath; - + /** * Theme name. * @@ -70,7 +70,7 @@ class Flux_Template { * @var string */ protected $themeName; - + /** * Action name. Actions exist as modulePath/moduleName/actionName.php. * @@ -78,7 +78,7 @@ class Flux_Template { * @var string */ protected $actionName; - + /** * Action path, would be the path format documented in $actionName. * @@ -86,7 +86,7 @@ class Flux_Template { * @var string */ protected $actionPath; - + /** * View name, this is usually the same as the actionName. * @@ -94,7 +94,7 @@ class Flux_Template { * @var string */ protected $viewName; - + /** * View path, follows a similar (or rather, exact) format like actionPath, * except there would be a themePath and viewName instead. @@ -103,7 +103,7 @@ class Flux_Template { * @var string */ protected $viewPath; - + /** * Header name. The header file would exist under the themePath's top level * and the headerName would simply be the file's basename without the .php @@ -111,9 +111,9 @@ class Flux_Template { * * @access protected * @var string - */ + */ protected $headerName; - + /** * The actual path to the header file. * @@ -121,7 +121,7 @@ class Flux_Template { * @var string */ protected $headerPath; - + /** * The footer name. * Similar to headerName. This name is usually 'footer'. @@ -130,7 +130,7 @@ class Flux_Template { * @var string */ protected $footerName; - + /** * The actual path to the footer file. * @@ -138,7 +138,7 @@ class Flux_Template { * @var string */ protected $footerPath; - + /** * Whether or not to use mod_rewrite-powered clean URLs or just plain old * query strings. @@ -147,7 +147,7 @@ class Flux_Template { * @var string */ protected $useCleanUrls; - + /** * URL of the current module/action being viewed. * @@ -155,7 +155,7 @@ class Flux_Template { * @var string */ protected $url; - + /** * URL of the current module/action being viewed. (including query string) * @@ -164,7 +164,7 @@ class Flux_Template { */ protected $urlWithQs; protected $urlWithQS; // compatibility. - + /** * Module/action for missing action's event. * @@ -172,7 +172,7 @@ class Flux_Template { * @var array */ protected $missingActionModuleAction; - + /** * Module/action for missing view's event. * @@ -180,7 +180,7 @@ class Flux_Template { * @var array */ protected $missingViewModuleAction; - + /** * Inherit view / controllers from another theme ? * @@ -188,7 +188,7 @@ class Flux_Template { * @var Flux_Template */ public $parentTemplate; - + /** * List of themes loaded, use for avoid circular dependencies * @@ -196,7 +196,7 @@ class Flux_Template { * @var array */ static public $themeLoaded = array(); - + /** * HTTP referer. * @@ -204,7 +204,7 @@ class Flux_Template { * @var string */ public $referer; - + /** * Construct new template onbject. * @@ -246,7 +246,7 @@ public function __construct(Flux_Config $config) } } - + /** * Any data that gets set here will be available to all templates as global * variables unless they are overridden by variables of the same name set @@ -260,7 +260,7 @@ public function setDefaultData(array &$data) $this->defaultData = $data; return $data; } - + /** * Render a template, but before doing so, call the action file and render * the header->view->footer in that order. @@ -277,10 +277,10 @@ public function render(array $dataArr = array()) ini_set('zlib.output_compression', 'On'); ini_set('zlib.output_compression_level', (int)Flux::config('GzipCompressionLevel')); } - + $addon = false; $this->actionPath = sprintf('%s/%s/%s.php', $this->modulePath, $this->moduleName, $this->actionName); - + if (!file_exists($this->actionPath)) { foreach (Flux::$addons as $_tmpAddon) { if ($_tmpAddon->respondsTo($this->moduleName, $this->actionName)) { @@ -288,7 +288,7 @@ public function render(array $dataArr = array()) $this->actionPath = sprintf('%s/%s/%s.php', $addon->moduleDir, $this->moduleName, $this->actionName); } } - + if (!$addon) { $this->moduleName = $this->missingActionModuleAction[0]; $this->actionName = $this->missingActionModuleAction[1]; @@ -296,12 +296,12 @@ public function render(array $dataArr = array()) $this->actionPath = sprintf('%s/%s/%s.php', $this->modulePath, $this->moduleName, $this->actionName); } } - + $this->viewPath = $this->themePath(sprintf('%s/%s.php', $this->moduleName, $this->actionName), true); - + if (!file_exists($this->viewPath) && $addon) { $this->viewPath = $addon->getView( $this, $this->moduleName, $this->actionName); - + if ( $this->viewPath === false ) { $this->moduleName = $this->missingViewModuleAction[0]; $this->actionName = $this->missingViewModuleAction[1]; @@ -310,12 +310,12 @@ public function render(array $dataArr = array()) $this->viewPath = $this->themePath(sprintf('%s/%s.php', $this->moduleName, $this->viewName), true); } } - + $this->headerPath = $this->themePath($this->headerName.'.php', true); $this->footerPath = $this->themePath($this->footerName.'.php', true); $this->url = $this->url($this->moduleName, $this->actionName); $this->urlWithQS = $this->url; - + if (!empty($_SERVER['QUERY_STRING'])) { if ($this->useCleanUrls) { $this->urlWithQS .= "?{$_SERVER['QUERY_STRING']}"; @@ -325,17 +325,17 @@ public function render(array $dataArr = array()) list ($key,$val) = explode('=', $line, 2); $key = urldecode($key); $val = urldecode($val); - + if ($key != 'module' && $key != 'action') { $this->urlWithQS .= sprintf('&%s=%s', urlencode($key), urlencode($val)); } } } } - + // Compatibility. $this->urlWithQs = $this->urlWithQS; - + // Tidy up! if (Flux::config('OutputCleanHTML')) { $dispatcher = Flux_Dispatcher::getInstance(); @@ -356,34 +356,34 @@ public function render(array $dataArr = array()) ob_start(); } } - + // Merge with default data. $data = array_merge($this->defaultData, $dataArr); - + // Extract data array and make them appear as though they were global // variables from the template. extract($data, EXTR_REFS); - + // Files object. $files = new Flux_Config($_FILES); - + $preprocessorPath = sprintf('%s/main/preprocess.php', $this->modulePath); if (file_exists($preprocessorPath)) { include $preprocessorPath; } - + include $this->actionPath; - + $pageMenuFile = FLUX_ROOT."/modules/{$this->moduleName}/pagemenu/{$this->actionName}.php"; $pageMenuItems = array(); - + // Get the main menu file first (located in the actual module). if (file_exists($pageMenuFile)) { ob_start(); $pageMenuItems = include $pageMenuFile; ob_end_clean(); } - + $addonPageMenuFiles = glob(FLUX_ADDON_DIR."/*/modules/{$this->moduleName}/pagemenu/{$this->actionName}.php"); if ($addonPageMenuFiles) { foreach ($addonPageMenuFiles as $addonPageMenuFile) { @@ -392,17 +392,17 @@ public function render(array $dataArr = array()) ob_end_clean(); } } - + if (file_exists($this->headerPath)) { include $this->headerPath; } - + include $this->viewPath; - + if (file_exists($this->footerPath)) { include $this->footerPath; } - + // Really, tidy up! if (Flux::config('OutputCleanHTML') && !$tidyIgnore && function_exists('tidy_repair_string')) { $content = ob_get_clean(); @@ -410,7 +410,7 @@ public function render(array $dataArr = array()) echo $content; } } - + /** * Returns an array of menu items that should be diplayed from the theme. * Only menu items the current user (and their group level) have access to @@ -426,11 +426,11 @@ public function getMenuItems($adminMenus = false) $defaultAction = Flux_Dispatcher::getInstance()->defaultAction; $menuItems = Flux::config('MenuItems'); $allowedItems = array(); - + if (!($menuItems instanceOf Flux_Config)) { return array(); } - + foreach ($menuItems->toArray() as $categoryName => $menu) { foreach ($menu as $menuName => $menuItem) { $module = array_key_exists('module', $menuItem) ? $menuItem['module'] : false; @@ -452,7 +452,7 @@ public function getMenuItems($adminMenus = false) if (empty($allowedItems[$categoryName])) { $allowedItems[$categoryName] = array(); } - + if ($exturl) { $allowedItems[$categoryName][] = array( 'name' => $menuName, @@ -474,10 +474,10 @@ public function getMenuItems($adminMenus = false) } } } - + return $allowedItems; } - + /** * @see Flux_Template::getMenuItems() */ @@ -485,7 +485,7 @@ public function getAdminMenuItems() { return $this->getMenuItems(true); } - + /** * Get sub-menu items for a particular module. * @@ -498,20 +498,20 @@ public function getSubMenuItems($moduleName = null) $moduleName = $moduleName ? $moduleName : $this->moduleName; $subMenuItems = Flux::config('SubMenuItems'); $allowedItems = array(); - + if (!($subMenuItems instanceOf Flux_Config) || !( ($menus = $subMenuItems->get($moduleName)) instanceOf Flux_Config )) { return array(); } - + foreach ($menus->toArray() as $actionName => $menuName) { if ($auth->actionAllowed($moduleName, $actionName)) { $allowedItems[] = array('name' => $menuName, 'module' => $moduleName, 'action' => $actionName); } } - + return $allowedItems; } - + /** * Get an array of login server names. * @@ -521,7 +521,7 @@ public function getServerNames() { return array_keys(Flux::$loginAthenaGroupRegistry); } - + /** * Determine if more than 1 server exists. * @@ -531,7 +531,7 @@ public function hasManyServers() { return count(Flux::$loginAthenaGroupRegistry) > 1; } - + /** * Obtain the absolute web path of the specified user path. Specify the * path as a relative path. @@ -551,7 +551,7 @@ public function path($path, $included = false) } return preg_replace('&/{2,}&', '/', $path); } - + /** * Similar to the path() method, but uses the $themePath as the path from * which the user-specified path is relative. @@ -577,7 +577,7 @@ public function themePath($path, $included = false) $uri = $this->path("{$this->themePath}/{$this->themeName}/{$path}", $included); // normalized basePath. - $base = preg_replace('/(\/+)$/', '', $this->basePath ) . '/'; + $base = preg_replace('/(\/+)$/', '', $this->basePath ) . '/'; $base = preg_quote( $base, '/' ); $chk = FLUX_ROOT .'/'. preg_replace('/^('.$base.')/', '', $uri ); @@ -593,7 +593,7 @@ public function themePath($path, $included = false) return $uri . $frag; } - + /** * Create a URI based on the setting of $useCleanUrls. This will determine * whether or not we will create a mod_rewrite-based clean URL or just a @@ -608,15 +608,15 @@ public function url($moduleName, $actionName = null, $params = array()) $defaultAction = Flux_Dispatcher::getInstance()->defaultAction; $serverProtocol = ''; $serverAddress = ''; - + if ($params instanceOf Flux_Config) { $params = $params->toArray(); } - + if (array_key_exists('_host', $params)) { $_host = $params['_host']; $_https = false; - + if ($_host && ($addr=Flux::config('ServerAddress'))) { if (array_key_exists('_https', $params)) { $_https = $params['_https']; @@ -631,24 +631,24 @@ public function url($moduleName, $actionName = null, $params = array()) $serverProtocol = $_https ? 'https://' : 'http://'; $serverAddress = $addr; } - + unset($params['_host']); - + if (array_key_exists('_https', $params)) { unset($params['_https']); } } - + $queryString = ''; - + if (count($params)) { $queryString .= Flux::config('UseCleanUrls') ? '?' : '&'; foreach ($params as $param => $value) { - $queryString .= sprintf('%s=%s&', $param, urlencode($value)); + $queryString .= sprintf('%s=%s&', $param, urlencode($value ?? '')); } $queryString = rtrim($queryString, '&'); } - + if ($this->useCleanUrls) { if ($actionName && $actionName != $defaultAction) { $url = sprintf('%s/%s/%s/%s', $this->basePath, $moduleName, $actionName, $queryString); @@ -667,7 +667,7 @@ public function url($moduleName, $actionName = null, $params = array()) } return $serverProtocol.preg_replace('&/{2,}&', '/', "$serverAddress/$url"); } - + /** * Format currency strings. * @@ -685,7 +685,7 @@ public function formatCurrency($number) ); return $amount; } - + /** * Format a MySQL DATE column according to the DateFormat config. * @@ -698,7 +698,7 @@ public function formatDate($date = null) $ts = $date ? strtotime($date) : time(); return date(Flux::config('DateFormat'), $ts); } - + /** * Format a MySQL DATETIME column according to the DateTimeFormat config. * @@ -711,7 +711,7 @@ public function formatDateTime($dateTime = null) $ts = $dateTime ? strtotime($dateTime) : time(); return date(Flux::config('DateTimeFormat'), $ts); } - + /** * Create a series of select fields matching a MySQL DATE format. * @@ -729,14 +729,14 @@ public function dateField($name, $value = null, $fowardYears = null, $backwardYe if(!isset($backwardYears)) { $backwardYears = (int)Flux::config('BackwardYears'); } - + $ts = $value && !preg_match('/^0000-00-00(?: 00:00:00)?$/', $value) ? strtotime($value) : time(); $year = ($year =$this->params->get("{$name}_year")) ? $year : date('Y', $ts); $month = ($month=$this->params->get("{$name}_month")) ? $month : date('m', $ts); $day = ($day =$this->params->get("{$name}_day")) ? $day : date('d', $ts); $fw = $year + $fowardYears; $bw = $year - $backwardYears; - + // Get years. $years = sprintf(''; - + // Get months. $months = sprintf(''; - + // Get days. $days = sprintf(''; - + return sprintf('%s-%s-%s', $years, $months, $days); } - + /** * Create a series of select fields matching a MySQL DATETIME format. * @@ -791,7 +791,7 @@ public function dateTimeField($name, $value = null) $hour = date('H', $ts); $minute = date('i', $ts); $second = date('s', $ts); - + // Get hours. $hours = sprintf(''; - + // Get minutes. $minutes = sprintf(''; - + // Get seconds. $seconds = sprintf(''; - + return sprintf('%s @ %s:%s:%s', $dateField, $hours, $minutes, $seconds); } - + /** * Returns "up" or "down" in a span HTML element with either the class * .up or .down, based on the value of $bool. True returns up, false @@ -844,7 +844,7 @@ public function serverUpDown($bool) $class = $bool ? 'up' : 'down'; return sprintf('%s', $class, $bool ? 'Online' : 'Offline'); } - + /** * Redirect client to another location. Script execution is terminated * after instructing the client to redirect. @@ -856,11 +856,11 @@ public function redirect($location = null) if (is_null($location)) { $location = $this->basePath; } - + header("Location: $location"); exit; } - + /** * Guess the HTTP server's current full URL. * @@ -872,18 +872,18 @@ public function entireUrl($withRequest = true) $proto = empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === "off" ? 'http://' : 'https://'; $hostname = empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST']; $request = $_SERVER['REQUEST_URI']; - + if ($withRequest) { $url = $proto.$hostname.$request; } else { $url = $proto.$hostname.'/'.$this->basePath; } - + $url = rtrim(preg_replace('&/{2,}&', '/', $url), '/'); return $url; } - + /** * Convenience method for retrieving a paginator instance. * @@ -897,7 +897,7 @@ public function getPaginator($total, array $options = array()) $paginator = new Flux_Paginator($total, $this->url($this->moduleName, $this->actionName, array('_host' => false)), $options); return $paginator; } - + /** * Link to an account view page. * @@ -916,7 +916,7 @@ public function linkToAccount($accountID, $text) return false; } } - + /** * Link to an account search. * @@ -935,7 +935,7 @@ public function linkToAccountSearch($params, $text) return false; } } - + /** * Link to a character view page. * @@ -951,7 +951,7 @@ public function linkToCharacter($charID, $text, $server = null) if ($server) { $params['preferred_server'] = $server; } - + $url = $this->url('character', 'view', $params); return sprintf('%s', $url, htmlspecialchars($text)); } @@ -959,7 +959,7 @@ public function linkToCharacter($charID, $text, $server = null) return false; } } - + /** * Deny entry to a page if called. This method should be used from a module * script, and no where else. @@ -969,7 +969,7 @@ public function deny() $location = $this->url('unauthorized'); $this->redirect($location); } - + /** * Get the full gender string from a gender letter (e.g. M for Male). * @@ -994,7 +994,7 @@ public function genderText($gender) break; } } - + /** * Get the account state name corresponding to the state number. * @@ -1006,7 +1006,7 @@ public function accountStateText($state) { $text = false; $state = (int)$state; - + switch ($state) { case 0: $text = Flux::message('AccountStateNormal'); @@ -1017,7 +1017,7 @@ public function accountStateText($state) $class = 'state-permanently-banned'; break; } - + if ($text) { $text = htmlspecialchars($text); return sprintf('