diff --git a/README.md b/README.md
index f8b8ccd5..8d7cc99c 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ Also, this project is not meant to be used for commercial puposes of any kind!
## Requirements
-+ Webserver running PHP ≥ 8.0 including extensions:
++ Webserver running PHP ≥ 8.2 including extensions:
+ [SimpleXML](https://www.php.net/manual/en/book.simplexml.php)
+ [GD](https://www.php.net/manual/en/book.image)
+ [MySQL Improved](https://www.php.net/manual/en/book.mysqli.php)
diff --git a/includes/ajaxHandler/account.class.php b/includes/ajaxHandler/account.class.php
index c6ad3ce3..f887f165 100644
--- a/includes/ajaxHandler/account.class.php
+++ b/includes/ajaxHandler/account.class.php
@@ -20,17 +20,11 @@ class AjaxAccount extends AjaxHandler
'remove' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
// 'sessionKey' => ['filter' => FILTER_SANITIZE_NUMBER_INT]
);
- protected $_get = array(
- 'locale' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkLocale']
- );
public function __construct(array $params)
{
parent::__construct($params);
- if (is_numeric($this->_get['locale']))
- User::useLocale($this->_get['locale']);
-
if (!$this->params || !User::$id)
return;
@@ -60,12 +54,12 @@ class AjaxAccount extends AjaxHandler
// we don't get signaled whether an id should be added to or removed from either includes or excludes
// so we throw everything into one table and toggle the mode if its already in here
- $includes = DB::Aowow()->selectCol('SELECT typeId FROM ?_profiler_excludes WHERE type = ?d AND typeId IN (?a)', $type, $ids);
+ $includes = DB::Aowow()->selectCol('SELECT `typeId` FROM ?_profiler_excludes WHERE `type` = ?d AND `typeId` IN (?a)', $type, $ids);
foreach ($ids as $typeId)
- DB::Aowow()->query('INSERT INTO ?_account_excludes (`userId`, `type`, `typeId`, `mode`) VALUES (?a) ON DUPLICATE KEY UPDATE mode = (mode ^ 0x3)', array(
- User::$id, $type, $typeId, in_array($typeId, $includes) ? 2 : 1
- ));
+ DB::Aowow()->query('INSERT INTO ?_account_excludes (`userId`, `type`, `typeId`, `mode`) VALUES (?a) ON DUPLICATE KEY UPDATE `mode` = (`mode` ^ 0x3)',
+ [User::$id, $type, $typeId, in_array($typeId, $includes) ? 2 : 1]
+ );
return;
}
diff --git a/includes/ajaxHandler/comment.class.php b/includes/ajaxHandler/comment.class.php
index 9fbf2b11..12bf766e 100644
--- a/includes/ajaxHandler/comment.class.php
+++ b/includes/ajaxHandler/comment.class.php
@@ -248,7 +248,7 @@ class AjaxComment extends AjaxHandler
if ($this->_get['rating'] < 0)
$val *= -1;
- if (User::getCurDailyVotes() <= 0)
+ if (User::getCurrentDailyVotes() <= 0)
return Util::toJSON(['error' => 1, 'message' => Lang::main('tooManyVotes')]);
else if (!$target || $val != $this->_get['rating'])
return Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
diff --git a/includes/ajaxHandler/data.class.php b/includes/ajaxHandler/data.class.php
index bc1f8adc..ec57e1ea 100644
--- a/includes/ajaxHandler/data.class.php
+++ b/includes/ajaxHandler/data.class.php
@@ -6,7 +6,7 @@ if (!defined('AOWOW_REVISION'))
class AjaxData extends AjaxHandler
{
protected $_get = array(
- 'locale' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkLocale' ],
+ 'locale' => ['filter' => FILTER_CALLBACK, 'options' => 'Locale::tryFrom' ],
't' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkTextLine'],
'catg' => ['filter' => FILTER_SANITIZE_NUMBER_INT ],
'skill' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxData::checkSkill' ],
@@ -18,8 +18,8 @@ class AjaxData extends AjaxHandler
{
parent::__construct($params);
- if (is_numeric($this->_get['locale']))
- User::useLocale($this->_get['locale']);
+ if ($this->_get['locale']?->validate())
+ Lang::load($this->_get['locale']);
// always this one
$this->handler = 'handleData';
@@ -103,7 +103,7 @@ class AjaxData extends AjaxHandler
case 'pets':
case 'zones':
if (!Util::loadStaticFile($set, $result, true) && Cfg::get('DEBUG'))
- $result .= "alert('could not fetch static data: ".$set." for locale: ".User::$localeString."');";
+ $result .= "alert('could not fetch static data: ".$set." for locale: ".Lang::getLocale()->json()."');";
$result .= "\n\n";
break;
@@ -118,7 +118,7 @@ class AjaxData extends AjaxHandler
protected static function checkSkill(string $val) : array
{
- return array_intersect([171, 164, 333, 202, 182, 773, 755, 165, 186, 393, 197, 185, 129, 356], explode(',', $val));
+ return array_intersect(array_merge(SKILLS_TRADE_PRIMARY, [SKILL_FIRST_AID, SKILL_COOKING, SKILL_FISHING]), explode(',', $val));
}
protected static function checkCallback(string $val) : bool
diff --git a/includes/ajaxHandler/locale.class.php b/includes/ajaxHandler/locale.class.php
index b6c64d22..e89fb33a 100644
--- a/includes/ajaxHandler/locale.class.php
+++ b/includes/ajaxHandler/locale.class.php
@@ -6,7 +6,7 @@ if (!defined('AOWOW_REVISION'))
class AjaxLocale extends AjaxHandler
{
protected $_get = array(
- 'locale' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkLocale']
+ 'locale' => ['filter' => FILTER_CALLBACK, 'options' => 'Locale::tryFrom']
);
public function __construct(array $params)
@@ -23,8 +23,11 @@ class AjaxLocale extends AjaxHandler
*/
protected function handleLocale() : string
{
- User::setLocale($this->_get['locale']);
- User::save();
+ if ($this->_get['locale']?->validate())
+ {
+ User::$preferedLoc = $this->_get['locale'];
+ User::save(true);
+ }
return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '.';
}
diff --git a/includes/ajaxHandler/profile.class.php b/includes/ajaxHandler/profile.class.php
index 4e90a835..00aa72ce 100644
--- a/includes/ajaxHandler/profile.class.php
+++ b/includes/ajaxHandler/profile.class.php
@@ -654,10 +654,10 @@ class AjaxProfile extends AjaxHandler
{
// get and apply inventory
$gItems[$iId] = array(
- 'name_'.User::$localeString => $phItems->getField('name', true),
- 'quality' => $phItems->getField('quality'),
- 'icon' => $phItems->getField('iconString'),
- 'jsonequip' => $data[$iId]
+ 'name_'.Lang::getLocale()->json() => $phItems->getField('name', true),
+ 'quality' => $phItems->getField('quality'),
+ 'icon' => $phItems->getField('iconString'),
+ 'jsonequip' => $data[$iId]
);
$profile['inventory'][$slot] = [$iId, 0, 0, 0, 0, 0, 0, 0];
@@ -682,10 +682,10 @@ class AjaxProfile extends AjaxHandler
{
// get and apply inventory
$gItems[$i['item']] = array(
- 'name_'.User::$localeString => $itemz->getField('name', true),
- 'quality' => $itemz->getField('quality'),
- 'icon' => $itemz->getField('iconString'),
- 'jsonequip' => $data[$i['item']]
+ 'name_'.Lang::getLocale()->json() => $itemz->getField('name', true),
+ 'quality' => $itemz->getField('quality'),
+ 'icon' => $itemz->getField('iconString'),
+ 'jsonequip' => $data[$i['item']]
);
$profile['inventory'][$i['slot']] = [$i['item'], $i['subItem'], $i['permEnchant'], $i['tempEnchant'], $i['gem1'], $i['gem2'], $i['gem3'], $i['gem4']];
}
@@ -719,7 +719,7 @@ class AjaxProfile extends AjaxHandler
// }
// }
- // $buff .= 'g_spells.add('.$id.", {id:".$id.", name:'".Util::jsEscape(mb_substr($data['name'], 1))."', icon:'".$data['icon']."', modifier:".Util::toJSON($mods)."});\n";
+ // $buff .= 'g_spells.add('.$id.", {id:".$id.", name:'".Util::jsEscape(mb_substr($data['name'], 1))."', icon:'".$data['icon']."', callback:".Util::toJSON($mods)."});\n";
// }
// $buff .= "\n";
// }
diff --git a/includes/basetype.class.php b/includes/basetype.class.php
index e9889fb7..5f65f5bf 100644
--- a/includes/basetype.class.php
+++ b/includes/basetype.class.php
@@ -1479,7 +1479,7 @@ abstract class Filter
private function genericString($field, $value, $strFlags)
{
if ($strFlags & STR_LOCALIZED)
- $field .= '_loc'.User::$localeId;
+ $field .= '_loc'.Lang::getLocale()->value;
return $this->modularizeString([$field], (string)$value, $strFlags & STR_MATCH_EXACT, $strFlags & STR_ALLOW_SHORT);
}
diff --git a/includes/defines.php b/includes/defines.php
index 2940324b..00f21947 100644
--- a/includes/defines.php
+++ b/includes/defines.php
@@ -139,21 +139,6 @@ define('U_GROUP_MODERATOR', (U_GROUP_ADMIN|U_GROUP_MOD|U_GROUP_B
define('U_GROUP_COMMENTS_MODERATOR', (U_GROUP_MODERATOR|U_GROUP_LOCALIZER));
define('U_GROUP_PREMIUM_PERMISSIONS', (U_GROUP_PREMIUM|U_GROUP_STAFF|U_GROUP_VIP));
-// Locales
-define('LOCALE_EN', 0); // enGB, enUS
-define('LOCALE_KR', 1); // koKR [aowwo unused]
-define('LOCALE_FR', 2); // frFR
-define('LOCALE_DE', 3); // deDE
-define('LOCALE_CN', 4); // zhCN, enCN
-define('LOCALE_TW', 5); // zhTW, enTW [aowow unused]
-define('LOCALE_ES', 6); // esES
-define('LOCALE_MX', 7); // esMX [aowow unused]
-define('LOCALE_RU', 8); // ruRU
-define('LOCALE_JP', 9); // jaJP [aowow & 335 unused]
-define('LOCALE_PT', 10); // ptPT, ptBR [aowow unused]
-define('LOCALE_IT', 11); // itIT [aowow & 335 unused]
-define('LOCALE_MASK_ALL', 0b000101011101);
-
// red buttons on the top of the page
define('BUTTON_WOWHEAD', 0);
define('BUTTON_UPGRADE', 1);
@@ -330,7 +315,6 @@ define('GUIDE_CU_NO_RATING', 0x200);
define('MAX_LEVEL', 80);
define('MAX_SKILL', 450);
-define('MAX_LOCALES', 16); // technical limitation, 6 in use here
define('WOW_BUILD', 12340);
// Loot handles
diff --git a/includes/game.php b/includes/game.php
index 30a57b0c..a856389e 100644
--- a/includes/game.php
+++ b/includes/game.php
@@ -220,7 +220,7 @@ class Game
$pages = [];
while ($ptId)
{
- if ($row = DB::World()->selectRow('SELECT ptl.Text AS Text_loc?d, pt.* FROM page_text pt LEFT JOIN page_text_locale ptl ON pt.ID = ptl.ID AND locale = ? WHERE pt.ID = ?d', User::$localeId, User::$localeString, $ptId))
+ if ($row = DB::World()->selectRow('SELECT ptl.Text AS Text_loc?d, pt.* FROM page_text pt LEFT JOIN page_text_locale ptl ON pt.ID = ptl.ID AND locale = ? WHERE pt.ID = ?d', Lang::getLocale()->value, Lang::getLocale()->json(), $ptId))
{
$ptId = $row['NextPageID'];
$pages[] = Util::parseHtmlText(Util::localizedString($row, 'Text'));
@@ -392,28 +392,20 @@ class Game
$quotes = [];
$soundIds = [];
- $quoteSrc = DB::World()->select('
- SELECT
- ct.GroupID AS ARRAY_KEY, ct.ID as ARRAY_KEY2,
- ct.`Type` AS `talkType`,
- ct.TextRange AS `range`,
- IFNULL(bct.`LanguageID`, ct.`Language`) AS lang,
- IFNULL(NULLIF(bct.Text, ""), IFNULL(NULLIF(bct.Text1, ""), IFNULL(ct.`Text`, ""))) AS text_loc0,
- {IFNULL(NULLIF(bctl.Text, ""), IFNULL(NULLIF(bctl.Text1, ""), IFNULL(ctl.Text, ""))) AS text_loc?d,}
- IF(bct.SoundEntriesID > 0, bct.SoundEntriesID, ct.Sound) AS soundId
- FROM
- creature_text ct
- {LEFT JOIN
- creature_text_locale ctl ON ct.CreatureID = ctl.CreatureID AND ct.GroupID = ctl.GroupID AND ct.ID = ctl.ID AND ctl.Locale = ?}
- LEFT JOIN
- broadcast_text bct ON ct.BroadcastTextId = bct.ID
- {LEFT JOIN
- broadcast_text_locale bctl ON ct.BroadcastTextId = bctl.ID AND bctl.locale = ?}
- WHERE
- ct.CreatureID = ?d',
- User::$localeId ?: DBSIMPLE_SKIP,
- User::$localeId ? Util::$localeStrings[User::$localeId] : DBSIMPLE_SKIP,
- User::$localeId ? Util::$localeStrings[User::$localeId] : DBSIMPLE_SKIP,
+ $quoteSrc = DB::World()->select(
+ 'SELECT ct.`GroupID` AS ARRAY_KEY, ct.`ID` AS ARRAY_KEY2, ct.`Type` AS "talkType", ct.TextRange AS "range",
+ IFNULL(bct.`LanguageID`, ct.`Language`) AS "lang",
+ IFNULL(NULLIF(bct.`Text`, ""), IFNULL(NULLIF(bct.`Text1`, ""), IFNULL(ct.`Text`, ""))) AS "text_loc0",
+ { IFNULL(NULLIF(bctl.`Text`, ""), IFNULL(NULLIF(bctl.`Text1`, ""), IFNULL(ctl.`Text`, ""))) AS text_loc?d, }
+ IF(bct.`SoundEntriesID` > 0, bct.`SoundEntriesID`, ct.`Sound`) AS "soundId"
+ FROM creature_text ct
+ { LEFT JOIN creature_text_locale ctl ON ct.`CreatureID` = ctl.`CreatureID` AND ct.`GroupID` = ctl.`GroupID` AND ct.`ID` = ctl.`ID` AND ctl.`Locale` = ? }
+ LEFT JOIN broadcast_text bct ON ct.`BroadcastTextId` = bct.`ID`
+ { LEFT JOIN broadcast_text_locale bctl ON ct.`BroadcastTextId` = bctl.`ID` AND bctl.`locale` = ? }
+ WHERE ct.`CreatureID` = ?d',
+ Lang::getLocale()->value ?: DBSIMPLE_SKIP,
+ Lang::getLocale()->value ? Lang::getLocale()->json() : DBSIMPLE_SKIP,
+ Lang::getLocale()->value ? Lang::getLocale()->json() : DBSIMPLE_SKIP,
$creatureId
);
diff --git a/includes/kernel.php b/includes/kernel.php
index 5a8d5527..72c01fac 100644
--- a/includes/kernel.php
+++ b/includes/kernel.php
@@ -3,7 +3,7 @@
mb_internal_encoding('UTF-8');
mysqli_report(MYSQLI_REPORT_ERROR);
-define('AOWOW_REVISION', 39);
+define('AOWOW_REVISION', 40);
define('OS_WIN', substr(PHP_OS, 0, 3) == 'WIN'); // OS_WIN as per compile info of php
define('CLI', PHP_SAPI === 'cli');
define('CLI_HAS_E', CLI && // WIN10 and later usually support ANSI escape sequences
@@ -11,25 +11,30 @@ define('CLI_HAS_E', CLI && // WIN10 and later u
$reqExt = ['SimpleXML', 'gd', 'mysqli', 'mbstring', 'fileinfo'/*, 'gmp'*/];
+$badExt = ['Intl']; // Intl contains its own class Locale. What? Namespaces? Never heard of those!
$error = '';
-if ($ext = array_filter($reqExt, function($x) { return !extension_loaded($x); }))
+if ($ext = array_filter($reqExt, fn($x) => !extension_loaded($x)))
$error .= 'Required Extension '.implode(', ', $ext)." was not found. Please check if it should exist, using \"php -m\"\n\n";
-if (version_compare(PHP_VERSION, '8.0.0') < 0)
- $error .= 'PHP Version 8.0 or higher required! Your version is '.PHP_VERSION.".\nCore functions are unavailable!\n";
+if ($ext = array_filter($badExt, fn($x) => extension_loaded($x)))
+ $error .= 'Loaded Extension '.implode(', ', $ext)." is incompatible and must be disabled.\n\n";
+
+if (version_compare(PHP_VERSION, '8.2.0') < 0)
+ $error .= 'PHP Version 8.2 or higher required! Your version is '.PHP_VERSION.".\nCore functions are unavailable!\n";
if ($error)
die(CLI ? strip_tags($error) : $error);
require_once 'includes/defines.php';
+require_once 'includes/locale.class.php';
require_once 'includes/stats.class.php'; // Game entity statistics conversion
require_once 'includes/libs/DbSimple/Generic.php'; // Libraray: http://en.dklab.ru/lib/DbSimple (using variant: https://github.com/ivan1986/DbSimple/tree/master)
require_once 'includes/utilities.php'; // helper functions
require_once 'includes/config.class.php'; // Config holder
require_once 'includes/game.php'; // game related data & functions
-require_once 'includes/profiler.class.php';
-require_once 'includes/user.class.php';
+require_once 'includes/profiler.class.php'; // Profiler feature
+require_once 'includes/user.class.php'; // Session handling
require_once 'includes/markup.class.php'; // manipulate markup text
require_once 'includes/database.class.php'; // wrap DBSimple
require_once 'includes/community.class.php'; // handle comments, screenshots and videos
@@ -213,6 +218,12 @@ if (!CLI)
if (User::init())
User::save(); // save user-variables in session
+ // hard override locale for this call (should this be here..?)
+ if (isset($_GET['locale']) && ($loc = Locale::tryFrom($_GET['locale'])))
+ Lang::load($loc);
+ else
+ Lang::load(User::$preferedLoc);
+
// set up some logging (~10 queries will execute before we init the user and load the config)
if (Cfg::get('DEBUG') >= CLI::LOG_INFO && User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN))
{
@@ -227,17 +238,6 @@ if (!CLI)
DB::Characters($idx)->setLogger(['DB', 'profiler']);
}
- // hard-override locale for this call (should this be here..?)
- // all strings attached..
- if (isset($_GET['locale']))
- {
- $loc = intVal($_GET['locale']);
- if ($loc <= MAX_LOCALES && $loc >= 0 && (Cfg::get('LOCALES') & (1 << $loc)))
- User::useLocale($loc);
- }
-
- Lang::load(User::$localeId);
-
// parse page-parameters .. sanitize before use!
$str = explode('&', $_SERVER['QUERY_STRING'] ?? '', 2)[0];
$_ = explode('=', $str, 2);
@@ -245,6 +245,6 @@ if (!CLI)
$pageParam = $_[1] ?? '';
}
else if (DB::isConnected(DB_AOWOW))
- Lang::load(LOCALE_EN);
+ Lang::load(Locale::EN);
?>
diff --git a/includes/locale.class.php b/includes/locale.class.php
new file mode 100644
index 00000000..cc7d08e6
--- /dev/null
+++ b/includes/locale.class.php
@@ -0,0 +1,209 @@
+ 'en',
+ self::KR => 'kt',
+ self::FR => 'fr',
+ self::DE => 'de',
+ self::CN => 'cn',
+ self::TW => 'tw',
+ self::ES => 'es',
+ self::MX => 'mx',
+ self::RU => 'ru',
+ self::JP => 'jp',
+ self::PT => 'pt',
+ self::IT => 'it'
+ };
+ }
+
+ public function json() : string // internal usage / json string
+ {
+ return match ($this)
+ {
+ self::EN => 'enus',
+ self::KR => 'kokr',
+ self::FR => 'frfr',
+ self::DE => 'dede',
+ self::CN => 'zhcn',
+ self::TW => 'zhtw',
+ self::ES => 'eses',
+ self::MX => 'esmx',
+ self::RU => 'ruru',
+ self::JP => 'jajp',
+ self::PT => 'ptpt',
+ self::IT => 'itit'
+ };
+ }
+
+ public function title() : string // localized language name
+ {
+ return match ($this)
+ {
+ self::EN => 'English',
+ self::KR => '한국어',
+ self::FR => 'Français',
+ self::DE => 'Deutsch',
+ self::CN => '简体中文',
+ self::TW => '繁體中文',
+ self::ES => 'Español',
+ self::MX => 'Mexicano',
+ self::RU => 'Русский',
+ self::JP => '日本語',
+ self::PT => 'Português',
+ self::IT => 'Italiano'
+ };
+ }
+
+ public function gameDirs() : array // setup data source / wow client locale code
+ {
+ return match ($this)
+ {
+ self::EN => ['enGB', 'enUS', ''],
+ self::KR => ['koKR'],
+ self::FR => ['frFR'],
+ self::DE => ['deDE'],
+ self::CN => ['zhCN', 'enCN'],
+ self::TW => ['zhTW', 'enTW'],
+ self::ES => ['esES'],
+ self::MX => ['esMX'],
+ self::RU => ['ruRU'],
+ self::JP => ['jaJP'],
+ self::PT => ['ptPT', 'ptBR'],
+ self::IT => ['itIT']
+ };
+ }
+
+ public function httpCode() : array // HTTP_ACCEPT_LANGUAGE
+ {
+ return match ($this)
+ {
+ self::EN => ['en', 'en-au', 'en-bz', 'en-ca', 'en-ie', 'en-jm', 'en-nz', 'en-ph', 'en-za', 'en-tt', 'en-gb', 'en-us', 'en-zw'],
+ self::KR => ['ko', 'ko-kp', 'ko-kr'],
+ self::FR => ['fr', 'fr-be', 'fr-ca', 'fr-fr', 'fr-lu', 'fr-mc', 'fr-ch'],
+ self::DE => ['de', 'de-at', 'de-de', 'de-li', 'de-lu', 'de-ch'],
+ self::CN => ['zh', 'zh-hk', 'zh-cn', 'zh-sg'],
+ self::TW => ['tw', 'zh-tw'],
+ self::ES => ['es', 'es-ar', 'es-bo', 'es-cl', 'es-co', 'es-cr', 'es-do', 'es-ec', 'es-sv', 'es-gt', 'es-hn', 'es-ni', 'es-pa', 'es-py', 'es-pe', 'es-pr', 'es-es', 'es-uy', 'es-ve'],
+ self::MX => ['mx', 'es-mx'],
+ self::RU => ['ru', 'ru-mo'],
+ self::JP => ['ja'],
+ self::PT => ['pt', 'pt-br'],
+ self::IT => ['it', 'it-ch']
+ };
+ }
+
+ public function isLogographic() : bool
+ {
+ return $this == Locale::CN || $this == Locale::TW || $this == Locale::KR;
+ }
+
+ public function validate() : ?self
+ {
+ return ($this->maskBit() & self::MASK_ALL & (Cfg::get('LOCALES') ?: 0xFFFF)) ? $this : null;
+ }
+
+ public function maskBit() : int
+ {
+ return (1 << $this->value);
+ }
+
+ public static function tryFromDomain(string $str) : ?self
+ {
+ foreach (self::cases() as $l)
+ if ($l->validate() && $str == $l->domain())
+ return $l;
+
+ return null;
+ }
+
+ public static function tryFromHttpAcceptLanguage(string $httpAccept) : ?self
+ {
+ if (!$httpAccept)
+ return null;
+
+ $available = [];
+
+ // e.g.: de,de-DE;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
+ foreach (explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $loc)
+ if (preg_match('/([a-z\-]+)(?:\s*;\s*q\s*=\s*([\.\d]+))?/ui', $loc, $m, PREG_UNMATCHED_AS_NULL))
+ $available[Util::lower($m[1])] = floatVal($m[2] ?? 1); // no quality set: assume 100%
+
+ arsort($available, SORT_NUMERIC); // highest quality on top
+
+ foreach ($available as $code => $_)
+ foreach (self::cases() as $l)
+ if ($l->validate() && in_array($code, $l->httpCode()))
+ return $l;
+
+ return null;
+ }
+
+ public static function getFallback() : self
+ {
+ foreach (Locale::cases() as $l)
+ if ($l->validate())
+ return $l;
+
+ // wow, you really fucked up your config mate!
+ trigger_error('Locale::getFallback - there are no valid locales', E_USER_ERROR);
+ return self::EN;
+ }
+}
+
+
+/* The shape of things to come */
+class LocString
+{
+ private WeakMap $store;
+
+ public function __construct(array $data, string $key = 'name', ?callable $callback = null)
+ {
+ $this->store = new WeakMap();
+
+ $callback ??= fn($x) => $x;
+
+ if (!array_filter($data, fn($v, $k) => $v && strstr($k, $key.'_loc'), ARRAY_FILTER_USE_BOTH))
+ trigger_error('LocString - is entrirely empty', E_USER_WARNING);
+
+ foreach (Locale::cases() as $l)
+ $this->store[$l] = (string)$callback($data[$key.'_loc'.$l->value] ?? '');
+ }
+
+ public function __toString() : string
+ {
+ if ($str = $this->store[Lang::getLocale()])
+ return $str;
+
+ foreach (Locale::cases() as $l) // desired loc not set, use any other
+ if ($str = $this->store[$l])
+ return Cfg::get('DEBUG') ? '['.$str.']' : $str;
+
+ return Cfg::get('DEBUG') ? '[LOCSTRING]' : '';
+ }
+}
+
+?>
diff --git a/includes/smartAI.class.php b/includes/smartAI.class.php
index 67b7649e..40ecd7ff 100644
--- a/includes/smartAI.class.php
+++ b/includes/smartAI.class.php
@@ -918,16 +918,18 @@ class SmartAI
$this->jsGlobals[Type::NPC][] = $e['param'][1];
break;
case SAI_EVENT_LINK: // 61 - Used to link together multiple events as a chain of events.
- $e['param'][10] = LANG::concat(DB::World()->selectCol('SELECT CONCAT("#[b]", id, "[/b]") FROM smart_scripts WHERE link = ?d AND entryorguid = ?d AND source_type = ?d', $this->itr['id'], $this->entry, $this->srcType), false);
+ if ($links = DB::World()->selectCol('SELECT `id` FROM smart_scripts WHERE `link` = ?d AND `entryorguid` = ?d AND `source_type` = ?d', $this->itr['id'], $this->entry, $this->srcType))
+ $e['param'][10] = LANG::concat($links, false, fn($x) => "#[b]".$x."[/b]");
break;
case SAI_EVENT_GOSSIP_SELECT: // 62 - On gossip clicked (gossip_menu_option335).
- $gmo = DB::World()->selectRow('SELECT gmo.OptionText AS text_loc0 {, gmol.OptionText AS text_loc?d}
- FROM gossip_menu_option gmo LEFT JOIN gossip_menu_option_locale gmol ON gmo.MenuID = gmol.MenuID AND gmo.OptionID = gmol.OptionID AND gmol.Locale = ?d
- WHERE gmo.MenuId = ?d AND gmo.OptionID = ?d',
- User::$localeId ? Util::$localeStrings[User::$localeId] : DBSIMPLE_SKIP,
- User::$localeId,
- $e['param'][0],
- $e['param'][1]
+ $gmo = DB::World()->selectRow(
+ 'SELECT gmo.`OptionText` AS "text_loc0" {, gmol.`OptionText` AS text_loc?d }
+ FROM gossip_menu_option gmo
+ LEFT JOIN gossip_menu_option_locale gmol ON gmo.`MenuID` = gmol.`MenuID` AND gmo.`OptionID` = gmol.`OptionID` AND gmol.`Locale` = ?d
+ WHERE gmo.`MenuId` = ?d AND gmo.`OptionID` = ?d',
+ Lang::getLocale() != Locale::EN ? Lang::getLocale()->json() : DBSIMPLE_SKIP,
+ Lang::getLocale()->value,
+ $e['param'][0], $e['param'][1]
);
if ($gmo)
@@ -941,11 +943,11 @@ class SmartAI
break;
case SAI_EVENT_DISTANCE_CREATURE: // 75 - On creature guid OR any instance of creature entry is within distance.
if ($e['param'][0])
- $e['param'][10] = DB::World()->selectCell('SELECT id FROM creature WHERE guid = ?d', $e['param'][0]);
+ $e['param'][10] = DB::World()->selectCell('SELECT `id` FROM creature WHERE `guid` = ?d', $e['param'][0]);
// do not break;
case SAI_EVENT_DISTANCE_GAMEOBJECT: // 76 - On gameobject guid OR any instance of gameobject entry is within distance.
if ($e['param'][0] && !$e['param'][10])
- $e['param'][10] = DB::World()->selectCell('SELECT id FROM gameobject WHERE guid = ?d', $e['param'][0]);
+ $e['param'][10] = DB::World()->selectCell('SELECT `id` FROM gameobject WHERE `guid` = ?d', $e['param'][0]);
else if ($e['param'][1])
$e['param'][10] = $e['param'][1];
else if (!$e['param'][10])
@@ -1244,7 +1246,7 @@ class SmartAI
JOIN ?_taxinodes tn1 ON tp.startNodeId = tn1.id
JOIN ?_taxinodes tn2 ON tp.endNodeId = tn2.id
WHERE tp.id = ?d',
- User::$localeId, User::$localeId, User::$localeId, User::$localeId, $a['param'][0]
+ Lang::getLocale()->value, Lang::getLocale()->value, Lang::getLocale()->value, Lang::getLocale()->value, $a['param'][0]
);
$a['param'][6] = Util::localizedString($nodes, 'start');
$a['param'][7] = Util::localizedString($nodes, 'end');
diff --git a/includes/types/achievement.class.php b/includes/types/achievement.class.php
index c349888f..329d9b33 100644
--- a/includes/types/achievement.class.php
+++ b/includes/types/achievement.class.php
@@ -343,9 +343,9 @@ class AchievementListFilter extends Filter
{
$_ = [];
if (isset($_v['ex']) && $_v['ex'] == 'on')
- $_ = $this->modularizeString(['name_loc'.User::$localeId, 'reward_loc'.User::$localeId, 'description_loc'.User::$localeId]);
+ $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value, 'reward_loc'.Lang::getLocale()->value, 'description_loc'.Lang::getLocale()->value]);
else
- $_ = $this->modularizeString(['name_loc'.User::$localeId]);
+ $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value]);
if ($_)
$parts[] = $_;
diff --git a/includes/types/creature.class.php b/includes/types/creature.class.php
index 909256a1..63ff9126 100644
--- a/includes/types/creature.class.php
+++ b/includes/types/creature.class.php
@@ -353,9 +353,9 @@ class CreatureListFilter extends Filter
{
$_ = [];
if (isset($_v['ex']) && $_v['ex'] == 'on')
- $_ = $this->modularizeString(['name_loc'.User::$localeId, 'subname_loc'.User::$localeId]);
+ $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value, 'subname_loc'.Lang::getLocale()->value]);
else
- $_ = $this->modularizeString(['name_loc'.User::$localeId]);
+ $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value]);
if ($_)
$parts[] = $_;
diff --git a/includes/types/enchantment.class.php b/includes/types/enchantment.class.php
index 7080ab9b..aaad7e95 100644
--- a/includes/types/enchantment.class.php
+++ b/includes/types/enchantment.class.php
@@ -13,7 +13,7 @@ class EnchantmentList extends BaseType
public static $dataTable = '?_itemenchantment';
private $jsonStats = [];
- private $relSpells = [];
+ private SpellList $relSpells;
private $triggerIds = [];
protected $queryBase = 'SELECT ie.*, ie.id AS ARRAY_KEY FROM ?_itemenchantment ie';
@@ -26,6 +26,8 @@ class EnchantmentList extends BaseType
{
parent::__construct($conditions, $miscData);
+ $relSpells = [];
+
// post processing
foreach ($this->iterate() as &$curTpl)
{
@@ -40,15 +42,15 @@ class EnchantmentList extends BaseType
case ENCHANTMENT_TYPE_COMBAT_SPELL:
$proc = -$this->getField('ppmRate') ?: ($this->getField('procChance') ?: $this->getField('amount'.$i));
$curTpl['spells'][$i] = [$curTpl['object'.$i], SPELL_TRIGGER_HIT, $curTpl['charges'], $proc];
- $this->relSpells[] = $curTpl['object'.$i];
+ $relSpells[] = $curTpl['object'.$i];
break;
case ENCHANTMENT_TYPE_EQUIP_SPELL:
$curTpl['spells'][$i] = [$curTpl['object'.$i], SPELL_TRIGGER_EQUIP, $curTpl['charges'], 0];
- $this->relSpells[] = $curTpl['object'.$i];
+ $relSpells[] = $curTpl['object'.$i];
break;
case ENCHANTMENT_TYPE_USE_SPELL:
$curTpl['spells'][$i] = [$curTpl['object'.$i], SPELL_TRIGGER_USE, $curTpl['charges'], 0];
- $this->relSpells[] = $curTpl['object'.$i];
+ $relSpells[] = $curTpl['object'.$i];
break;
}
}
@@ -56,8 +58,8 @@ class EnchantmentList extends BaseType
$this->jsonStats[$this->id] = (new StatsContainer)->fromJson($curTpl, true);
}
- if ($this->relSpells)
- $this->relSpells = new SpellList(array(['id', $this->relSpells]));
+ if ($relSpells)
+ $this->relSpells = new SpellList(array(['id', $relSpells]));
}
// use if you JUST need the name
@@ -241,7 +243,7 @@ class EnchantmentListFilter extends Filter
//string
if (isset($_v['na']))
- if ($_ = $this->modularizeString(['name_loc'.User::$localeId]))
+ if ($_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value]))
$parts[] = $_;
// type
diff --git a/includes/types/gameobject.class.php b/includes/types/gameobject.class.php
index 4802cf26..38bfc97d 100644
--- a/includes/types/gameobject.class.php
+++ b/includes/types/gameobject.class.php
@@ -180,7 +180,7 @@ class GameObjectListFilter extends Filter
// name
if (isset($_v['na']))
- if ($_ = $this->modularizeString(['name_loc'.User::$localeId]))
+ if ($_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value]))
$parts[] = $_;
return $parts;
diff --git a/includes/types/item.class.php b/includes/types/item.class.php
index 1b8d2960..5e6e1248 100644
--- a/includes/types/item.class.php
+++ b/includes/types/item.class.php
@@ -1935,7 +1935,7 @@ class ItemListFilter extends Filter
104 => [FILTER_CR_STRING, 'description', STR_LOCALIZED ], // flavortext
105 => [FILTER_CR_CALLBACK, 'cbDropsInInstance', SRC_FLAG_DUNGEON_DROP, 1 ], // dropsinnormal [heroicdungeon-any]
106 => [FILTER_CR_CALLBACK, 'cbDropsInInstance', SRC_FLAG_DUNGEON_DROP, 2 ], // dropsinheroic [heroicdungeon-any]
- 107 => [FILTER_CR_NYI_PH, null, 1, ], // effecttext [str] not yet parsed ['effectsParsed_loc'.User::$localeId, $cr[2]]
+ 107 => [FILTER_CR_NYI_PH, null, 1, ], // effecttext [str] not yet parsed ['effectsParsed_loc'.Lang::getLocale()->value, $cr[2]]
109 => [FILTER_CR_CALLBACK, 'cbArmorBonus', null, null ], // armorbonus [op] [int]
111 => [FILTER_CR_NUMERIC, 'requiredSkillRank', NUM_CAST_INT, true ], // reqskillrank
113 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
@@ -2122,7 +2122,7 @@ class ItemListFilter extends Filter
// name
if (isset($_v['na']))
- if ($_ = $this->modularizeString(['name_loc'.User::$localeId]))
+ if ($_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value]))
$parts[] = $_;
// usable-by (not excluded by requiredClass && armor or weapons match mask from ?_classes)
@@ -2277,7 +2277,7 @@ class ItemListFilter extends Filter
$n = preg_replace(Filter::PATTERN_NAME, '', $cr[2]);
$n = $this->transformString($n, false);
- $randIds = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, ABS(`id`) AS `id`, name_loc?d, `name_loc0` FROM ?_itemrandomenchant WHERE name_loc?d LIKE ?', User::$localeId, User::$localeId, $n);
+ $randIds = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, ABS(`id`) AS `id`, name_loc?d, `name_loc0` FROM ?_itemrandomenchant WHERE name_loc?d LIKE ?', Lang::getLocale()->value, Lang::getLocale()->value, $n);
$tplIds = $randIds ? DB::World()->select('SELECT `entry`, `ench` FROM item_enchantment_template WHERE `ench` IN (?a)', array_column($randIds, 'id')) : [];
foreach ($tplIds as &$set)
{
diff --git a/includes/types/itemset.class.php b/includes/types/itemset.class.php
index b89d55f1..2c8255d9 100644
--- a/includes/types/itemset.class.php
+++ b/includes/types/itemset.class.php
@@ -211,7 +211,7 @@ class ItemsetListFilter extends Filter
// name [str]
if (isset($_v['na']))
- if ($_ = $this->modularizeString(['name_loc'.User::$localeId]))
+ if ($_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value]))
$parts[] = $_;
// quality [enum]
diff --git a/includes/types/quest.class.php b/includes/types/quest.class.php
index 9566be48..4355e673 100644
--- a/includes/types/quest.class.php
+++ b/includes/types/quest.class.php
@@ -498,9 +498,9 @@ class QuestListFilter extends Filter
{
$_ = [];
if (isset($_v['ex']) && $_v['ex'] == 'on')
- $_ = $this->modularizeString(['name_loc'.User::$localeId, 'objectives_loc'.User::$localeId, 'details_loc'.User::$localeId]);
+ $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value, 'objectives_loc'.Lang::getLocale()->value, 'details_loc'.Lang::getLocale()->value]);
else
- $_ = $this->modularizeString(['name_loc'.User::$localeId]);
+ $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value]);
if ($_)
$parts[] = $_;
diff --git a/includes/types/spell.class.php b/includes/types/spell.class.php
index 2d9ce1af..d6ec6e42 100644
--- a/includes/types/spell.class.php
+++ b/includes/types/spell.class.php
@@ -1436,8 +1436,8 @@ class SpellList extends BaseType
$this->charLevel = $level;
// step -1: already handled?
- if (isset($this->parsedText[$this->id][$type][User::$localeId][$this->charLevel][(int)$this->interactive]))
- return $this->parsedText[$this->id][$type][User::$localeId][$this->charLevel][(int)$this->interactive];
+ if (isset($this->parsedText[$this->id][$type][Lang::getLocale()->value][$this->charLevel][(int)$this->interactive]))
+ return $this->parsedText[$this->id][$type][Lang::getLocale()->value][$this->charLevel][(int)$this->interactive];
// step 0: get text
$data = $this->getField($type, true);
@@ -1543,7 +1543,7 @@ class SpellList extends BaseType
$data = strtr($data, ["\r" => '', "\n" => '
']);
// cache result
- $this->parsedText[$this->id][$type][User::$localeId][$this->charLevel][(int)$this->interactive] = [$data, $relSpells, $this->scaling[$this->id]];
+ $this->parsedText[$this->id][$type][Lang::getLocale()->value][$this->charLevel][(int)$this->interactive] = [$data, $relSpells, $this->scaling[$this->id]];
return [$data, $relSpells, $this->scaling[$this->id]];
}
@@ -2451,9 +2451,9 @@ class SpellListFilter extends Filter
{
$_ = [];
if (isset($_v['ex']) && $_v['ex'] == 'on')
- $_ = $this->modularizeString(['name_loc'.User::$localeId, 'buff_loc'.User::$localeId, 'description_loc'.User::$localeId]);
+ $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value, 'buff_loc'.Lang::getLocale()->value, 'description_loc'.Lang::getLocale()->value]);
else
- $_ = $this->modularizeString(['name_loc'.User::$localeId]);
+ $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value]);
if ($_)
$parts[] = $_;
diff --git a/includes/types/title.class.php b/includes/types/title.class.php
index b1360e5b..88a42843 100644
--- a/includes/types/title.class.php
+++ b/includes/types/title.class.php
@@ -45,9 +45,13 @@ class TitleList extends BaseType
unset($_curTpl['src3']);
// shorthand for more generic access
- foreach (Util::$localeStrings as $i => $str)
- if ($str)
- $_curTpl['name_loc'.$i] = trim(str_replace('%s', '', $_curTpl['male_loc'.$i]));
+ // i don't see it being used anywhere..?
+ /*
+ foreach (Locale::cases() as $loc)
+ if ($loc->validate())
+ $_curTpl['name'] = new LocString($_curTpl, 'male', fn($x) => trim(str_replace('%s', '', $x)));
+ // $_curTpl['name_loc'.$loc->value] = trim(str_replace('%s', '', $_curTpl['male_loc'.$loc->value]));
+ */
}
}
diff --git a/includes/user.class.php b/includes/user.class.php
index 5686a144..e453797a 100644
--- a/includes/user.class.php
+++ b/includes/user.class.php
@@ -6,28 +6,34 @@ if (!defined('AOWOW_REVISION'))
class User
{
- public static $id = 0;
- public static $displayName = '';
- public static $banStatus = 0x0; // see ACC_BAN_* defines
- public static $groups = 0x0;
- public static $perms = 0;
- public static $localeId = 0;
- public static $localeString = 'enus';
- public static $avatar = 'inv_misc_questionmark';
- public static $dailyVotes = 0;
+ public static int $id = 0;
+ public static string $displayName = '';
+ public static int $banStatus = 0x0; // see ACC_BAN_* defines
+ public static int $groups = 0x0;
+ public static int $perms = 0;
+ public static string $avatar = 'inv_misc_questionmark';
+ public static int $dailyVotes = 0;
public static $ip = null;
- private static $reputation = 0;
- private static $dataKey = '';
- private static $expires = false;
- private static $passHash = '';
- private static $excludeGroups = 1;
- private static $profiles = null;
+ private static int $reputation = 0;
+ private static string $dataKey = '';
+ private static bool $expires = false;
+ private static string $passHash = '';
+ private static int $excludeGroups = 1;
+
+ public static Locale $preferedLoc;
+ private static LocalProfileList $profiles;
public static function init()
{
self::setIP();
- self::setLocale();
+
+ if (isset($_SESSION['locale']) && $_SESSION['locale'] instanceof Locale)
+ self::$preferedLoc = $_SESSION['locale']->validate() ?? Locale::getFallback();
+ else if (!empty($_SERVER["HTTP_ACCEPT_LANGUAGE"]) && ($loc = Locale::tryFromHttpAcceptLanguage($_SERVER["HTTP_ACCEPT_LANGUAGE"])))
+ self::$preferedLoc = $loc;
+ else
+ self::$preferedLoc = Locale::getFallback();
// session have a dataKey to access the JScripts (yes, also the anons)
if (empty($_SESSION['dataKey']))
@@ -39,12 +45,12 @@ class User
return false;
// check IP bans
- if ($ipBan = DB::Aowow()->selectRow('SELECT count, unbanDate FROM ?_account_bannedips WHERE ip = ? AND type = 0', self::$ip))
+ if ($ipBan = DB::Aowow()->selectRow('SELECT `count`, `unbanDate` FROM ?_account_bannedips WHERE `ip` = ? AND `type` = 0', self::$ip))
{
if ($ipBan['count'] > Cfg::get('ACC_FAILED_AUTH_COUNT') && $ipBan['unbanDate'] > time())
return false;
else if ($ipBan['unbanDate'] <= time())
- DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE ip = ?', self::$ip);
+ DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE `ip` = ?', self::$ip);
}
// try to restore session
@@ -55,36 +61,39 @@ class User
if (!empty($_SESSION['timeout']) && $_SESSION['timeout'] <= time())
return false;
- $query = DB::Aowow()->SelectRow('
- SELECT a.id, a.passHash, a.displayName, a.locale, a.userGroups, a.userPerms, a.allowExpire, BIT_OR(ab.typeMask) AS bans, IFNULL(SUM(r.amount), 0) as reputation, a.avatar, a.dailyVotes, a.excludeGroups
+ $uData = DB::Aowow()->SelectRow(
+ 'SELECT a.`id`, a.`passHash`, a.`displayName`, a.`locale`, a.`userGroups`, a.`userPerms`, a.`allowExpire`, BIT_OR(ab.`typeMask`) AS "bans", IFNULL(SUM(r.`amount`), 0) AS "reputation", a.`avatar`, a.`dailyVotes`, a.`excludeGroups`
FROM ?_account a
- LEFT JOIN ?_account_banned ab ON a.id = ab.userId AND ab.end > UNIX_TIMESTAMP()
- LEFT JOIN ?_account_reputation r ON a.id = r.userId
- WHERE a.id = ?d
- GROUP BY a.id',
+ LEFT JOIN ?_account_banned ab ON a.`id` = ab.`userId` AND ab.`end` > UNIX_TIMESTAMP()
+ LEFT JOIN ?_account_reputation r ON a.`id` = r.`userId`
+ WHERE a.`id` = ?d
+ GROUP BY a.`id`',
$_SESSION['user']
);
- if (!$query)
+ if (!$uData)
return false;
+ if ($loc = Locale::tryFrom($uData['locale']))
+ self::$preferedLoc = $loc;
+
// password changed, terminate session
- if (AUTH_MODE_SELF && $query['passHash'] != $_SESSION['hash'])
+ if (AUTH_MODE_SELF && $uData['passHash'] != $_SESSION['hash'])
{
self::destroy();
return false;
}
- self::$id = intval($query['id']);
- self::$displayName = $query['displayName'];
- self::$passHash = $query['passHash'];
- self::$expires = (bool)$query['allowExpire'];
- self::$reputation = $query['reputation'];
- self::$banStatus = $query['bans'];
- self::$groups = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userGroups']);
- self::$perms = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userPerms']);
- self::$dailyVotes = $query['dailyVotes'];
- self::$excludeGroups = $query['excludeGroups'];
+ self::$id = intVal($uData['id']);
+ self::$displayName = $uData['displayName'];
+ self::$passHash = $uData['passHash'];
+ self::$expires = (bool)$uData['allowExpire'];
+ self::$reputation = $uData['reputation'];
+ self::$banStatus = $uData['bans'];
+ self::$groups = $uData['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($uData['userGroups']);
+ self::$perms = $uData['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($uData['userPerms']);
+ self::$dailyVotes = $uData['dailyVotes'];
+ self::$excludeGroups = $uData['excludeGroups'];
$conditions = array(
[['cuFlags', PROFILER_CU_DELETED, '&'], 0],
@@ -96,11 +105,8 @@ class User
self::$profiles = (new LocalProfileList($conditions));
- if ($query['avatar'])
- self::$avatar = $query['avatar'];
-
- if (self::$localeId != $query['locale']) // reset, if changed
- self::setLocale(intVal($query['locale']));
+ if ($uData['avatar'])
+ self::$avatar = $uData['avatar'];
// stuff, that updates on a daily basis goes here (if you keep you session alive indefinitly, the signin-handler doesn't do very much)
// - conscutive visits
@@ -115,10 +121,10 @@ class User
// daily votes (we need to reset this one)
self::$dailyVotes = self::getMaxDailyVotes();
- DB::Aowow()->query('
- UPDATE ?_account
- SET dailyVotes = ?d, prevLogin = curLogin, curLogin = UNIX_TIMESTAMP(), prevIP = curIP, curIP = ?
- WHERE id = ?d',
+ DB::Aowow()->query(
+ 'UPDATE ?_account
+ SET `dailyVotes` = ?d, `prevLogin` = `curLogin`, `curLogin` = UNIX_TIMESTAMP(), `prevIP` = `curIP`, `curIP` = ?
+ WHERE `id` = ?d',
self::$dailyVotes,
self::$ip,
self::$id
@@ -131,16 +137,16 @@ class User
// increment consecutive visits (next day or first of new month and not more than 48h)
// i bet my ass i forgott a corner case
if ((date('j', $lastLogin) + 1 == date('j') || (date('j') == 1 && date('n', $lastLogin) != date('n'))) && (time() - $lastLogin) < 2 * DAY)
- DB::Aowow()->query('UPDATE ?_account SET consecutiveVisits = consecutiveVisits + 1 WHERE id = ?d', self::$id);
+ DB::Aowow()->query('UPDATE ?_account SET `consecutiveVisits` = `consecutiveVisits` + 1 WHERE `id` = ?d', self::$id);
else
- DB::Aowow()->query('UPDATE ?_account SET consecutiveVisits = 0 WHERE id = ?d', self::$id);
+ DB::Aowow()->query('UPDATE ?_account SET `consecutiveVisits` = 0 WHERE `id` = ?d', self::$id);
}
}
return true;
}
- private static function setIP()
+ private static function setIP() : void
{
$ipAddr = '';
$method = ['HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR'];
@@ -165,68 +171,32 @@ class User
self::$ip = $ipAddr ?: null;
}
- /****************/
- /* set language */
- /****************/
-
- // set and save
- public static function setLocale($set = -1)
+ public static function save(bool $toDB = false)
{
- $loc = LOCALE_EN;
+ $_SESSION['user'] = self::$id;
+ $_SESSION['hash'] = self::$passHash;
+ $_SESSION['locale'] = self::$preferedLoc;
+ $_SESSION['timeout'] = self::$expires ? time() + Cfg::get('SESSION_TIMEOUT_DELAY') : 0;
+ // $_SESSION['dataKey'] does not depend on user login status and is set in User::init()
- // get
- if ($set != -1 && isset(Util::$localeStrings[$set]))
- $loc = $set;
- else if (isset($_SESSION['locale']) && isset(Util::$localeStrings[$_SESSION['locale']]))
- $loc = $_SESSION['locale'];
- else if (!empty($_SERVER["HTTP_ACCEPT_LANGUAGE"]))
- {
- $loc = strtolower(substr($_SERVER["HTTP_ACCEPT_LANGUAGE"], 0, 2));
- switch ($loc) {
- case 'fr': $loc = LOCALE_FR; break;
- case 'de': $loc = LOCALE_DE; break;
- case 'zh': $loc = LOCALE_CN; break; // may cause issues in future with zh-tw
- case 'es': $loc = LOCALE_ES; break;
- case 'ru': $loc = LOCALE_RU; break;
- default: $loc = LOCALE_EN;
- }
- }
-
- // check; pick first viable if failed
- $allowedLoc = Cfg::get('LOCALES');
- if ($allowedLoc && !($allowedLoc & (1 << $loc)))
- {
- foreach (Util::$localeStrings as $idx => $__)
- {
- if ($allowedLoc & (1 << $idx))
- {
- $loc = $idx;
- break;
- }
- }
- }
-
- // set
- if (self::$id)
- DB::Aowow()->query('UPDATE ?_account SET locale = ? WHERE id = ?', $loc, self::$id);
-
- self::useLocale($loc);
+ if (self::$id && $toDB)
+ DB::Aowow()->query('UPDATE ?_account SET `locale` = ? WHERE `id` = ?', self::$preferedLoc->value, self::$id);
}
- // only use once
- public static function useLocale($use)
+ public static function destroy()
{
- self::$localeId = isset(Util::$localeStrings[$use]) ? $use : LOCALE_EN;
- self::$localeString = self::localeString(self::$localeId);
+ session_regenerate_id(true); // session itself is not destroyed; status changed => regenerate id
+ session_unset();
+
+ $_SESSION['locale'] = self::$preferedLoc; // keep locale
+ $_SESSION['dataKey'] = self::$dataKey; // keep dataKey
+
+ self::$id = 0;
+ self::$displayName = '';
+ self::$perms = 0;
+ self::$groups = U_GROUP_NONE;
}
- private static function localeString($loc = -1)
- {
- if (!isset(Util::$localeStrings[$loc]))
- $loc = 0;
-
- return Util::$localeStrings[$loc];
- }
/*******************/
/* auth mechanisms */
@@ -245,21 +215,21 @@ class User
return AUTH_INTERNAL_ERR;
// handle login try limitation
- $ip = DB::Aowow()->selectRow('SELECT ip, count, unbanDate FROM ?_account_bannedips WHERE type = 0 AND ip = ?', self::$ip);
+ $ip = DB::Aowow()->selectRow('SELECT `ip`, `count`, `unbanDate` FROM ?_account_bannedips WHERE `type` = 0 AND `ip` = ?', self::$ip);
if (!$ip || $ip['unbanDate'] < time()) // no entry exists or time expired; set count to 1
- DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 0, 1, UNIX_TIMESTAMP() + ?d)', self::$ip, Cfg::get('ACC_FAILED_AUTH_BLOCK'));
+ DB::Aowow()->query('REPLACE INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, 0, 1, UNIX_TIMESTAMP() + ?d)', self::$ip, Cfg::get('ACC_FAILED_AUTH_BLOCK'));
else // entry already exists; increment count
- DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ?', Cfg::get('ACC_FAILED_AUTH_BLOCK'), self::$ip);
+ DB::Aowow()->query('UPDATE ?_account_bannedips SET `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + ?d WHERE `ip` = ?', Cfg::get('ACC_FAILED_AUTH_BLOCK'), self::$ip);
if ($ip && $ip['count'] >= Cfg::get('ACC_FAILED_AUTH_COUNT') && $ip['unbanDate'] >= time())
return AUTH_IPBANNED;
- $query = DB::Aowow()->SelectRow('
- SELECT a.id, a.passHash, BIT_OR(ab.typeMask) AS bans, a.status
+ $query = DB::Aowow()->SelectRow(
+ 'SELECT a.`id`, a.`passHash`, BIT_OR(ab.`typeMask`) AS "bans", a.`status`
FROM ?_account a
- LEFT JOIN ?_account_banned ab ON a.id = ab.userId AND ab.end > UNIX_TIMESTAMP()
- WHERE a.user = ?
- GROUP BY a.id',
+ LEFT JOIN ?_account_banned ab ON a.`id` = ab.`userId` AND ab.`end` > UNIX_TIMESTAMP()
+ WHERE a.`user` = ?
+ GROUP BY a.`id`',
$name
);
if (!$query)
@@ -270,7 +240,7 @@ class User
return AUTH_WRONGPASS;
// successfull auth; clear bans for this IP
- DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE type = 0 AND ip = ?', self::$ip);
+ DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE `type` = 0 AND `ip` = ?', self::$ip);
if ($query['bans'] & (ACC_BAN_PERM | ACC_BAN_TEMP))
return AUTH_BANNED;
@@ -304,10 +274,19 @@ class User
case AUTH_MODE_EXTERNAL:
{
if (!file_exists('config/extAuth.php'))
+ {
+ trigger_error('config/extAuth.php not found');
return AUTH_INTERNAL_ERR;
+ }
require 'config/extAuth.php';
+ if (!function_exists('extAuth'))
+ {
+ trigger_error('external auth function extAuth() not defined in config/extAuth.php');
+ return AUTH_INTERNAL_ERR;
+ }
+
$extGroup = -1;
$result = extAuth($name, $pass, $extId, $extGroup);
@@ -335,27 +314,22 @@ class User
return AUTH_OK;
}
- // create a linked account for our settings if nessecary
- private static function checkOrCreateInDB($extId, $name, $userGroup = -1)
+ // create a linked account for our settings if necessary
+ private static function checkOrCreateInDB(int $extId, string $name, int $userGroup = -1) : int
{
- if (!intVal($extId))
- return 0;
-
- $userGroup = intVal($userGroup);
-
- if ($_ = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE extId = ?d', $extId))
+ if ($_ = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE `extId` = ?d', $extId))
{
if ($userGroup >= U_GROUP_NONE)
- DB::Aowow()->query('UPDATE ?_account SET userGroups = ?d WHERE extId = ?d', $userGroup, $extId);
+ DB::Aowow()->query('UPDATE ?_account SET `userGroups` = ?d WHERE `extId` = ?d', $userGroup, $extId);
return $_;
}
- $newId = DB::Aowow()->query('INSERT IGNORE INTO ?_account (extId, user, displayName, joinDate, prevIP, prevLogin, locale, status, userGroups) VALUES (?d, ?, ?, UNIX_TIMESTAMP(), ?, UNIX_TIMESTAMP(), ?d, ?d, ?d)',
+ $newId = DB::Aowow()->query('INSERT IGNORE INTO ?_account (`extId`, `user`, `displayName`, `joinDate`, `prevIP`, `prevLogin`, `locale`, `status`, `userGroups`) VALUES (?d, ?, ?, UNIX_TIMESTAMP(), ?, UNIX_TIMESTAMP(), ?d, ?d, ?d)',
$extId,
$name,
Util::ucFirst($name),
- isset($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : '',
- User::$localeId,
+ $_SERVER["REMOTE_ADDR"] ?? '',
+ self::$preferedLoc->value,
ACC_STATUS_OK,
$userGroup >= U_GROUP_NONE ? $userGroup : U_GROUP_NONE
);
@@ -439,39 +413,17 @@ class User
return $errCode == 0;
}
- public static function save()
- {
- $_SESSION['user'] = self::$id;
- $_SESSION['hash'] = self::$passHash;
- $_SESSION['locale'] = self::$localeId;
- $_SESSION['timeout'] = self::$expires ? time() + Cfg::get('SESSION_TIMEOUT_DELAY') : 0;
- // $_SESSION['dataKey'] does not depend on user login status and is set in User::init()
- }
-
- public static function destroy()
- {
- session_regenerate_id(true); // session itself is not destroyed; status changed => regenerate id
- session_unset();
-
- $_SESSION['locale'] = self::$localeId; // keep locale
- $_SESSION['dataKey'] = self::$dataKey; // keep dataKey
-
- self::$id = 0;
- self::$displayName = '';
- self::$perms = 0;
- self::$groups = U_GROUP_NONE;
- }
/*********************/
/* access management */
/*********************/
- public static function isInGroup($group)
+ public static function isInGroup($group) : bool
{
return (self::$groups & $group) != 0;
}
- public static function canComment()
+ public static function canComment() : bool
{
if (!self::$id || self::$banStatus & (ACC_BAN_COMMENT | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
@@ -479,7 +431,7 @@ class User
return self::$perms || self::$reputation >= Cfg::get('REP_REQ_COMMENT');
}
- public static function canReply()
+ public static function canReply() : bool
{
if (!self::$id || self::$banStatus & (ACC_BAN_COMMENT | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
@@ -487,7 +439,7 @@ class User
return self::$perms || self::$reputation >= Cfg::get('REP_REQ_REPLY');
}
- public static function canUpvote()
+ public static function canUpvote() : bool
{
if (!self::$id || self::$banStatus & (ACC_BAN_COMMENT | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
@@ -495,7 +447,7 @@ class User
return self::$perms || (self::$reputation >= Cfg::get('REP_REQ_UPVOTE') && self::$dailyVotes > 0);
}
- public static function canDownvote()
+ public static function canDownvote() : bool
{
if (!self::$id || self::$banStatus & (ACC_BAN_RATE | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
@@ -503,7 +455,7 @@ class User
return self::$perms || (self::$reputation >= Cfg::get('REP_REQ_DOWNVOTE') && self::$dailyVotes > 0);
}
- public static function canSupervote()
+ public static function canSupervote() : bool
{
if (!self::$id || self::$banStatus & (ACC_BAN_RATE | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
@@ -511,7 +463,7 @@ class User
return self::$reputation >= Cfg::get('REP_REQ_SUPERVOTE');
}
- public static function canUploadScreenshot()
+ public static function canUploadScreenshot() : bool
{
if (!self::$id || self::$banStatus & (ACC_BAN_SCREENSHOT | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
@@ -519,7 +471,7 @@ class User
return true;
}
- public static function canWriteGuide()
+ public static function canWriteGuide() : bool
{
if (!self::$id || self::$banStatus & (ACC_BAN_GUIDE | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
@@ -527,7 +479,7 @@ class User
return true;
}
- public static function canSuggestVideo()
+ public static function canSuggestVideo() : bool
{
if (!self::$id || self::$banStatus & (ACC_BAN_VIDEO | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
@@ -535,27 +487,28 @@ class User
return true;
}
- public static function isPremium()
+ public static function isPremium() : bool
{
return self::isInGroup(U_GROUP_PREMIUM) || self::$reputation >= Cfg::get('REP_REQ_PREMIUM');
}
+
/**************/
/* js-related */
/**************/
- public static function decrementDailyVotes()
+ public static function decrementDailyVotes() : void
{
self::$dailyVotes--;
- DB::Aowow()->query('UPDATE ?_account SET dailyVotes = ?d WHERE id = ?d', self::$dailyVotes, self::$id);
+ DB::Aowow()->query('UPDATE ?_account SET `dailyVotes` = ?d WHERE `id` = ?d', self::$dailyVotes, self::$id);
}
- public static function getCurDailyVotes()
+ public static function getCurrentDailyVotes() : int
{
return self::$dailyVotes;
}
- public static function getMaxDailyVotes()
+ public static function getMaxDailyVotes() : int
{
if (!self::$id || self::$banStatus & (ACC_BAN_PERM | ACC_BAN_TEMP))
return 0;
@@ -563,12 +516,12 @@ class User
return Cfg::get('USER_MAX_VOTES') + (self::$reputation >= Cfg::get('REP_REQ_VOTEMORE_BASE') ? 1 + intVal((self::$reputation - Cfg::get('REP_REQ_VOTEMORE_BASE')) / Cfg::get('REP_REQ_VOTEMORE_ADD')) : 0);
}
- public static function getReputation()
+ public static function getReputation() : int
{
return self::$reputation;
}
- public static function getUserGlobals()
+ public static function getUserGlobals() : array
{
$gUser = array(
'id' => self::$id,
@@ -613,34 +566,34 @@ class User
return $gUser;
}
- public static function getWeightScales()
+ public static function getWeightScales() : array
{
$result = [];
- $res = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, name FROM ?_account_weightscales WHERE userId = ?d', self::$id);
+ $res = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `name` FROM ?_account_weightscales WHERE `userId` = ?d', self::$id);
if (!$res)
return $result;
- $weights = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, `field` AS ARRAY_KEY2, val FROM ?_account_weightscale_data WHERE id IN (?a)', array_keys($res));
+ $weights = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `field` AS ARRAY_KEY2, `val` FROM ?_account_weightscale_data WHERE `id` IN (?a)', array_keys($res));
foreach ($weights as $id => $data)
$result[] = array_merge(['name' => $res[$id], 'id' => $id], $data);
return $result;
}
- public static function getProfilerExclusions()
+ public static function getProfilerExclusions() : array
{
$result = [];
$modes = [1 => 'excludes', 2 => 'includes'];
foreach ($modes as $mode => $field)
- if ($ex = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, typeId AS ARRAY_KEY2, typeId FROM ?_account_excludes WHERE mode = ?d AND userId = ?d', $mode, self::$id))
+ if ($ex = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, `typeId` AS ARRAY_KEY2, `typeId` FROM ?_account_excludes WHERE `mode` = ?d AND `userId` = ?d', $mode, self::$id))
foreach ($ex as $type => $ids)
$result[$field][$type] = array_values($ids);
return $result;
}
- public static function getCharacters()
+ public static function getCharacters() : array
{
if (!self::$profiles)
return [];
@@ -648,7 +601,7 @@ class User
return self::$profiles->getJSGlobals(PROFILEINFO_CHARACTER);
}
- public static function getProfiles()
+ public static function getProfiles() : array
{
if (!self::$profiles)
return [];
@@ -675,7 +628,7 @@ class User
return [];
}
- public static function getGuides()
+ public static function getGuides() : array
{
$result = [];
@@ -689,17 +642,17 @@ class User
return $result;
}
- public static function getCookies()
+ public static function getCookies() : array
{
$data = [];
if (self::$id)
- $data = DB::Aowow()->selectCol('SELECT name AS ARRAY_KEY, data FROM ?_account_cookies WHERE userId = ?d', self::$id);
+ $data = DB::Aowow()->selectCol('SELECT `name` AS ARRAY_KEY, `data` FROM ?_account_cookies WHERE `userId` = ?d', self::$id);
return $data;
}
- public static function getFavorites()
+ public static function getFavorites() : array
{
if (!self::$id)
return [];
diff --git a/includes/utilities.php b/includes/utilities.php
index 2467de88..7186e146 100644
--- a/includes/utilities.php
+++ b/includes/utilities.php
@@ -77,22 +77,6 @@ trait TrRequestData
return 0;
}
- private static function checkLocale(string $val) : int
- {
- if (preg_match('/^'.implode('|', array_keys(array_filter(Util::$localeStrings))).'$/', $val))
- return intVal($val);
-
- return -1;
- }
-
- private static function checkDomain(string $val) : string
- {
- if (preg_match('/^'.implode('|', array_filter(Util::$subDomains)).'$/i', $val))
- return strtolower($val);
-
- return '';
- }
-
private static function checkIdList(string $val) : array
{
if (preg_match('/^-?\d+(,-?\d+)*$/', $val))
@@ -538,14 +522,6 @@ abstract class Util
private static $perfectGems = null;
- public static $localeStrings = array( // zero-indexed
- 'enus', null, 'frfr', 'dede', 'zhcn', null, 'eses', null, 'ruru'
- );
-
- public static $subDomains = array(
- 'en', null, 'fr', 'de', 'cn', null, 'es', null, 'ru'
- );
-
public static $regions = array(
'us', 'eu', 'kr', 'tw', 'cn', 'dev'
);
@@ -919,19 +895,19 @@ abstract class Util
$silent = true;
// default case: selected locale available
- if (!empty($data[$field.'_loc'.User::$localeId]))
- return $data[$field.'_loc'.User::$localeId];
+ if (!empty($data[$field.'_loc'.Lang::getLocale()->value]))
+ return $data[$field.'_loc'.Lang::getLocale()->value];
// locale not enUS; aowow-type localization available; add brackets if not silent
- else if (User::$localeId != LOCALE_EN && !empty($data[$field.'_loc0']))
+ else if (Lang::getLocale() != Locale::EN && !empty($data[$field.'_loc0']))
return $silent ? $data[$field.'_loc0'] : '['.$data[$field.'_loc0'].']';
// locale not enUS; TC localization; add brackets if not silent
- else if (User::$localeId != LOCALE_EN && !empty($data[$field]))
+ else if (Lang::getLocale() != Locale::EN && !empty($data[$field]))
return $silent ? $data[$field] : '['.$data[$field].']';
// locale enUS; TC localization; return normal
- else if (User::$localeId == LOCALE_EN && !empty($data[$field]))
+ else if (Lang::getLocale() == Locale::EN && !empty($data[$field]))
return $data[$field];
// nothing to find; be empty
@@ -969,22 +945,6 @@ abstract class Util
return Lang::item('ratingString', [$statId, $result, $level]);
}
- public static function powerUseLocale(string $domain = 'en') : void
- {
- foreach (Util::$subDomains as $k => $v)
- {
- if ($domain != $v)
- continue;
-
- User::useLocale($k);
- Lang::load($k);
- return;
- }
-
- User::useLocale(LOCALE_EN);
- Lang::load(LOCALE_EN);
- }
-
// default ucFirst doesn't convert UTF-8 chars
public static function ucFirst($str)
{
@@ -1097,8 +1057,8 @@ abstract class Util
$success = true;
if ($localized)
{
- if (file_exists('datasets/'.User::$localeString.'/'.$file))
- $result .= file_get_contents('datasets/'.User::$localeString.'/'.$file);
+ if (file_exists('datasets/'.Lang::getLocale()->json().'/'.$file))
+ $result .= file_get_contents('datasets/'.Lang::getLocale()->json().'/'.$file);
else if (file_exists('datasets/enus/'.$file))
$result .= file_get_contents('datasets/enus/'.$file);
else
diff --git a/index.php b/index.php
index e570eeeb..74c9dfbc 100644
--- a/index.php
+++ b/index.php
@@ -121,7 +121,7 @@ switch ($pageCall)
if (is_callable([$classInstance, 'display']))
$classInstance->display();
else if (isset($_GET['power']))
- die('$WowheadPower.register(0, '.User::$localeId.', {})');
+ die('$WowheadPower.register(0, '.Lang::getLocale()->value.', {})');
else // in conjunction with a proper rewriteRule in .htaccess...
(new GenericPage($pageCall))->error();
}
@@ -153,7 +153,7 @@ switch ($pageCall)
break;
default: // unk parameter given -> ErrorPage
if (isset($_GET['power']))
- die('$WowheadPower.register(0, '.User::$localeId.', {})');
+ die('$WowheadPower.register(0, '.Lang::getLocale()->value.', {})');
else // in conjunction with a proper rewriteRule in .htaccess...
(new GenericPage($pageCall))->error();
break;
diff --git a/localization/lang.class.php b/localization/lang.class.php
index 12d8c820..30711951 100644
--- a/localization/lang.class.php
+++ b/localization/lang.class.php
@@ -44,28 +44,21 @@ class Lang
private static $emote;
private static $enchantment;
- private static $locId;
- private static $locales = array(
- LOCALE_EN => 'English',
- LOCALE_FR => 'Français',
- LOCALE_DE => 'Deutsch',
- LOCALE_CN => '简体中文',
- LOCALE_ES => 'Español',
- LOCALE_RU => 'Русский'
- );
+ private static ?Locale $locale = null;
public const FMT_RAW = 0;
public const FMT_HTML = 1;
public const FMT_MARKUP = 2;
- public static function load(int $locale) : void
+ public static function load(Locale $loc) : void
{
- if (!isset(Util::$localeStrings[$locale]))
- die($locale.' is not a known locale!');
- if (!file_exists('localization/locale_'.Util::$localeStrings[$locale].'.php'))
- die('File for locale '.$locale.' not found.');
+ if (self::$locale == $loc)
+ return;
+
+ if (!file_exists('localization/locale_'.$loc->json().'.php'))
+ die('File for locale '.$loc->name.' not found.');
else
- require 'localization/locale_'.Util::$localeStrings[$locale].'.php';
+ require 'localization/locale_'.$loc->json().'.php';
foreach ($lang as $k => $v)
self::$$k = $v;
@@ -75,10 +68,15 @@ class Lang
self::$item['cat'][2][1][14] .= ' ('.self::$item['cat'][2][0].')';
self::$main['moreTitles']['privilege'] = self::$privileges['_privileges'];
- self::$locId = $locale;
+ self::$locale = $loc;
}
- public static function __callStatic(string $prop, array $args) // : ?string|array
+ public static function getLocale() : Locale
+ {
+ return self::$locale;
+ }
+
+ public static function __callStatic(string $prop, ?array $args = []) : string|array|null
{
$vspfArgs = [];
foreach ($args as $i => $arg)
@@ -96,9 +94,11 @@ class Lang
$dbt = debug_backtrace()[0];
$file = explode(DIRECTORY_SEPARATOR, $dbt['file']);
trigger_error('Lang - undefined property Lang::$'.$prop.'[\''.implode('\'][\'', $args).'\'], called in '.array_pop($file).':'.$dbt['line'], E_USER_WARNING);
+
+ return null;
}
- public static function exist(string $prop, ...$args)
+ public static function exist(string $prop, string ...$args) : string|array|null
{
if (!isset(self::$$prop))
return null;
@@ -120,12 +120,12 @@ class Lang
$b = '';
$i = 0;
$n = count($args);
+
+ $callback ??= fn($x) => $x;
+
foreach ($args as $k => $arg)
{
- if (is_callable($callback))
- $b .= $callback($arg, $k);
- else
- $b .= $arg;
+ $b .= $callback($arg, $k);
if ($n > 1 && $i < ($n - 2))
$b .= ', ';
@@ -192,13 +192,13 @@ class Lang
foreach ($row as &$r)
$r = implode(' ', $r);
- switch ($fmt)
+ $separator = match ($fmt)
{
- case self::FMT_HTML: $separator = '
'; break;
- case self::FMT_MARKUP: $separator = '[br]'; break;
- case self::FMT_RAW:
- default: $separator = "\n"; break;
- }
+ self::FMT_HTML => '
',
+ self::FMT_MARKUP => '[br]',
+ self::FMT_RAW => "\n",
+ default => "\n"
+ };
return implode($separator, $row);
}
@@ -223,20 +223,20 @@ class Lang
}
// todo: expand
- public static function getInfoBoxForFlags(int $flags) : array
+ public static function getInfoBoxForFlags(int $cuFlags) : array
{
$tmp = [];
- if ($flags & CUSTOM_DISABLED)
+ if ($cuFlags & CUSTOM_DISABLED)
$tmp[] = '[tooltip name=disabledHint]'.Util::jsEscape(self::main('disabledHint')).'[/tooltip][span class=tip tooltip=disabledHint]'.Util::jsEscape(self::main('disabled')).'[/span]';
- if ($flags & CUSTOM_SERVERSIDE)
+ if ($cuFlags & CUSTOM_SERVERSIDE)
$tmp[] = '[tooltip name=serversideHint]'.Util::jsEscape(self::main('serversideHint')).'[/tooltip][span class=tip tooltip=serversideHint]'.Util::jsEscape(self::main('serverside')).'[/span]';
- if ($flags & CUSTOM_UNAVAILABLE)
+ if ($cuFlags & CUSTOM_UNAVAILABLE)
$tmp[] = self::main('unavailable');
- if ($flags & CUSTOM_EXCLUDE_FOR_LISTVIEW && User::isInGroup(U_GROUP_STAFF))
+ if ($cuFlags & CUSTOM_EXCLUDE_FOR_LISTVIEW && User::isInGroup(U_GROUP_STAFF))
$tmp[] = '[tooltip name=excludedHint]This entry is excluded from lists and is not searchable.[/tooltip][span tooltip=excludedHint class="tip q10"]Hidden[/span]';
return $tmp;
@@ -246,7 +246,7 @@ class Lang
{
$locks = [];
$ids = [];
- $lock = DB::Aowow()->selectRow('SELECT * FROM ?_lock WHERE id = ?d', $lockId);
+ $lock = DB::Aowow()->selectRow('SELECT * FROM ?_lock WHERE `id` = ?d', $lockId);
if (!$lock)
return $locks;
@@ -518,13 +518,13 @@ class Lang
{
$tmp = self::game('difficulty').self::main('colon');
- switch ($fmt)
+ $base = match ($fmt)
{
- case self::FMT_HTML: $base = '%2$s '; break;
- case self::FMT_MARKUP: $base = '[color=r%1$d]%2$s[/color] '; break;
- case self::FMT_RAW:
- default: $base = '%2$s '; break;
- }
+ self::FMT_HTML => '%2$s ',
+ self::FMT_MARKUP => '[color=r%1$d]%2$s[/color] ',
+ self::FMT_RAW => '%2$s ',
+ default => '%2$s '
+ };
for ($i = 0; $i < 4; $i++)
if (!empty($bp[$i]))
@@ -594,7 +594,7 @@ class Lang
return '';
}
- private static function vspf(/* array|string */ $var, array $args = []) // : array|string
+ private static function vspf(null|array|string $var, array $args = []) : null|array|string
{
if (is_array($var))
{
@@ -775,7 +775,7 @@ class Lang
switch (strtolower($word[1]))
{
case 'h':
- if (self::$locId != LOCALE_FR)
+ if (self::$locale != Locale::FR)
return 'de ' . $word;
case 'a':
case 'e':
@@ -810,7 +810,7 @@ class Lang
{
[$_, $num, $pad, $singular, $plural1, $plural2] = array_pad($m, 6, null);
- if (self::$locId != LOCALE_RU || !$plural2)
+ if (self::$locale != Locale::RU || !$plural2)
return $num . $pad . ($num == 1 ? $singular : $plural1);
// singular - ends in 1, but not teen number
diff --git a/pages/account.php b/pages/account.php
index 6874792c..64a6872d 100644
--- a/pages/account.php
+++ b/pages/account.php
@@ -437,7 +437,7 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
$this->_post['email'],
User::$ip,
$this->_post['remember_me'] != 'yes',
- User::$localeId,
+ Lang::getLocale()->value,
U_GROUP_PENDING,
ACC_STATUS_NEW,
Cfg::get('ACC_CREATE_SAVE_DECAY'),
diff --git a/pages/achievement.php b/pages/achievement.php
index b2b854d3..5ac531b8 100644
--- a/pages/achievement.php
+++ b/pages/achievement.php
@@ -38,7 +38,7 @@ class AchievementPage extends GenericPage
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
- protected $_get = ['domain' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkDomain']];
+ protected $_get = ['domain' => ['filter' => FILTER_CALLBACK, 'options' => 'Locale::tryFromDomain']];
private $powerTpl = '$WowheadPower.registerAchievement(%d, %d, %s);';
@@ -48,7 +48,7 @@ class AchievementPage extends GenericPage
// temp locale
if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain'])
- Util::powerUseLocale($this->_get['domain']);
+ Lang::load($this->_get['domain']);
$this->typeId = intVal($id);
@@ -233,7 +233,7 @@ class AchievementPage extends GenericPage
// tab: see also
$conditions = array(
- ['name_loc'.User::$localeId, $this->subject->getField('name', true)],
+ ['name_loc'.Lang::getLocale()->value, $this->subject->getField('name', true)],
['id', $this->typeId, '!']
);
$saList = new AchievementList($conditions);
@@ -551,12 +551,12 @@ class AchievementPage extends GenericPage
$power = new StdClass();
if (!$this->subject->error)
{
- $power->{'name_'.User::$localeString} = $this->subject->getField('name', true);
- $power->icon = rawurlencode($this->subject->getField('iconString', true, true));
- $power->{'tooltip_'.User::$localeString} = $this->subject->renderTooltip();
+ $power->{'name_'.Lang::getLocale()->json()} = $this->subject->getField('name', true);
+ $power->icon = rawurlencode($this->subject->getField('iconString', true, true));
+ $power->{'tooltip_'.Lang::getLocale()->json()} = $this->subject->renderTooltip();
}
- return sprintf($this->powerTpl, $this->typeId, User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER));
+ return sprintf($this->powerTpl, $this->typeId, Lang::getLocale()->value, Util::toJSON($power, JSON_AOWOW_POWER));
}
private function createMail(&$reqCss = false)
diff --git a/pages/compare.php b/pages/compare.php
index 80780353..202bff1b 100644
--- a/pages/compare.php
+++ b/pages/compare.php
@@ -87,10 +87,10 @@ class ComparePage extends GenericPage
$si['enchantment'] = implode(', ', $si['enchantment']);
$this->cmpItems[$itemId] = [
- 'name_'.User::$localeString => $iList->getField('name', true),
- 'quality' => $iList->getField('quality'),
- 'icon' => $iList->getField('iconString'),
- 'jsonequip' => $data[$itemId]
+ 'name_'.Lang::getLocale()->json() => $iList->getField('name', true),
+ 'quality' => $iList->getField('quality'),
+ 'icon' => $iList->getField('iconString'),
+ 'jsonequip' => $data[$itemId]
];
}
}
diff --git a/pages/currency.php b/pages/currency.php
index c318001c..7c076d77 100644
--- a/pages/currency.php
+++ b/pages/currency.php
@@ -17,7 +17,7 @@ class CurrencyPage extends GenericPage
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
- protected $_get = ['domain' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkDomain']];
+ protected $_get = ['domain' => ['filter' => FILTER_CALLBACK, 'options' => 'Locale::tryFromDomain']];
private $powerTpl = '$WowheadPower.registerCurrency(%d, %d, %s);';
@@ -27,7 +27,7 @@ class CurrencyPage extends GenericPage
// temp locale
if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain'])
- Util::powerUseLocale($this->_get['domain']);
+ Lang::load($this->_get['domain']);
$this->typeId = intVal($id);
@@ -231,12 +231,12 @@ class CurrencyPage extends GenericPage
$power = new StdClass();
if (!$this->subject->error)
{
- $power->{'name_'.User::$localeString} = $this->subject->getField('name', true);
- $power->icon = rawurlencode($this->subject->getField('iconString', true, true));
- $power->{'tooltip_'.User::$localeString} = $this->subject->renderTooltip();
+ $power->{'name_'.Lang::getLocale()->json()} = $this->subject->getField('name', true);
+ $power->icon = rawurlencode($this->subject->getField('iconString', true, true));
+ $power->{'tooltip_'.Lang::getLocale()->json()} = $this->subject->renderTooltip();
}
- return sprintf($this->powerTpl, $this->typeId, User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER));
+ return sprintf($this->powerTpl, $this->typeId, Lang::getLocale()->value, Util::toJSON($power, JSON_AOWOW_POWER));
}
}
diff --git a/pages/emote.php b/pages/emote.php
index de72dfeb..6d330d68 100644
--- a/pages/emote.php
+++ b/pages/emote.php
@@ -89,7 +89,7 @@ class EmotePage extends GenericPage
if ($this->subject->getField('cuFlags') & EMOTE_CU_MISSING_CMD)
$text .= Lang::emote('noCommand').'[br][br]';
- else if ($aliasses = DB::Aowow()->selectCol('SELECT command FROM ?_emotes_aliasses WHERE id = ?d AND locales & ?d', $this->typeId, 1 << User::$localeId))
+ else if ($aliasses = DB::Aowow()->selectCol('SELECT command FROM ?_emotes_aliasses WHERE id = ?d AND locales & ?d', $this->typeId, 1 << Lang::getLocale()->value))
{
$text .= '[h3]'.Lang::emote('aliases').'[/h3][ul]';
foreach ($aliasses as $a)
diff --git a/pages/event.php b/pages/event.php
index bea2cfa5..50e85eb3 100644
--- a/pages/event.php
+++ b/pages/event.php
@@ -19,7 +19,7 @@ class EventPage extends GenericPage
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
- protected $_get = ['domain' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkDomain']];
+ protected $_get = ['domain' => ['filter' => FILTER_CALLBACK, 'options' => 'Locale::tryFromDomain']];
private $powerTpl = '$WowheadPower.registerHoliday(%d, %d, %s);';
private $hId = 0;
@@ -31,7 +31,7 @@ class EventPage extends GenericPage
// temp locale
if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain'])
- Util::powerUseLocale($this->_get['domain']);
+ Lang::load($this->_get['domain']);
$this->typeId = intVal($id);
@@ -283,15 +283,15 @@ class EventPage extends GenericPage
$power = new StdClass();
if (!$this->subject->error)
{
- $power->{'name_'.User::$localeString} = $this->subject->getField('name', true);
+ $power->{'name_'.Lang::getLocale()->json()} = $this->subject->getField('name', true);
if ($this->subject->getField('iconString') != 'trade_engineering')
$power->icon = rawurlencode($this->subject->getField('iconString', true, true));
- $power->{'tooltip_'.User::$localeString} = $this->subject->renderTooltip();
+ $power->{'tooltip_'.Lang::getLocale()->json()} = $this->subject->renderTooltip();
}
- return sprintf($this->powerTpl, $this->typeId, User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER));
+ return sprintf($this->powerTpl, $this->typeId, Lang::getLocale()->value, Util::toJSON($power, JSON_AOWOW_POWER));
}
protected function postCache()
@@ -309,7 +309,7 @@ class EventPage extends GenericPage
else
{
if ($this->hId)
- $this->wowheadLink = sprintf(WOWHEAD_LINK, Util::$subDomains[User::$localeId], 'event', $this->hId);
+ $this->wowheadLink = sprintf(WOWHEAD_LINK, Lang::getLocale()->domain(), 'event', $this->hId);
/********************/
/* finalize infobox */
diff --git a/pages/genericPage.class.php b/pages/genericPage.class.php
index 07f1a8c8..e73e5ece 100644
--- a/pages/genericPage.class.php
+++ b/pages/genericPage.class.php
@@ -30,7 +30,7 @@ trait TrDetailPage
$staff = intVal($withStaff && User::isInGroup(U_GROUP_EMPLOYEE));
// mode, type, typeId, employee-flag, localeId, category, filter
- $key = [$this->mode, $this->type, $this->typeId, $staff, User::$localeId, '-1', '-1'];
+ $key = [$this->mode, $this->type, $this->typeId, $staff, Lang::getLocale()->value, '-1', '-1'];
// item special: can modify tooltips
if (isset($this->enhancedTT))
@@ -70,7 +70,7 @@ trait TrListPage
$staff = intVal($withStaff && User::isInGroup(U_GROUP_EMPLOYEE));
// mode, type, typeId, employee-flag, localeId,
- $key = [$this->mode, $this->type, '-1', $staff, User::$localeId];
+ $key = [$this->mode, $this->type, '-1', $staff, Lang::getLocale()->value];
//category
$key[] = $this->category ? implode('.', $this->category) : '-1';
@@ -100,7 +100,7 @@ trait TrProfiler
$staff = intVal($withStaff && User::isInGroup(U_GROUP_EMPLOYEE));
// mode, type, typeId, employee-flag, localeId, category, filter
- $key = [$this->mode, $this->type, $this->subject->getField('id'), $staff, User::$localeId, '-1', '-1'];
+ $key = [$this->mode, $this->type, $this->subject->getField('id'), $staff, Lang::getLocale()->value, '-1', '-1'];
return implode('_', $key);
}
@@ -141,7 +141,7 @@ trait TrProfiler
}
}
- protected function initialSync() : void
+ protected function initialSync() : never
{
$this->prepareContent();
@@ -320,7 +320,7 @@ class GenericPage
$this->gFavorites = User::getFavorites();
$this->pageTemplate['pageName'] = strtolower($pageCall);
- $this->wowheadLink = sprintf(WOWHEAD_LINK, Util::$subDomains[User::$localeId], $pageCall, $pageParam);
+ $this->wowheadLink = sprintf(WOWHEAD_LINK,Lang::getLocale()->domain(), $pageCall, $pageParam);
if (!$this->isValidPage())
$this->error();
@@ -495,15 +495,15 @@ class GenericPage
// insert locale string
if ($flags & SC_FLAG_LOCALIZED)
- $str = sprintf($str, User::$localeString);
+ $str = sprintf($str, Lang::getLocale()->json());
if ($dynData)
{
- $app[] = 'locale='.User::$localeId;
+ $app[] = 'locale='.Lang::getLocale()->value;
$app[] = 't='.$_SESSION['dataKey'];
}
- else if (($flags & SC_FLAG_APPEND_LOCALE) && User::$localeId)
- $app[] = 'lang='.Util::$subDomains[User::$localeId];
+ else if (($flags & SC_FLAG_APPEND_LOCALE) && Lang::getLocale() != Locale::EN)
+ $app[] = 'lang='.Lang::getLocale()->domain();
// append anti-cache timestamp
if (!($flags & SC_FLAG_NO_TIMESTAMP) && !$dynData)
@@ -549,10 +549,10 @@ class GenericPage
Type::GUIDE, $this->typeId, $this->guideRevision);
else if (!empty($this->articleUrl))
$article = DB::Aowow()->selectRow('SELECT `article`, `quickInfo`, `locale`, `editAccess` FROM ?_articles WHERE `url` = ? AND `locale` IN (?a) ORDER BY `locale` DESC, `rev` DESC LIMIT 1',
- $this->articleUrl, [User::$localeId, LOCALE_EN]);
+ $this->articleUrl, [Lang::getLocale()->value, Locale::EN->value]);
else if (!empty($this->type) && isset($this->typeId))
$article = DB::Aowow()->selectRow('SELECT `article`, `quickInfo`, `locale`, `editAccess` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d AND `locale` IN (?a) ORDER BY `locale` DESC, `rev` DESC LIMIT 1',
- $this->type, $this->typeId, [User::$localeId, LOCALE_EN]);
+ $this->type, $this->typeId, [Lang::getLocale()->value, Locale::EN->value]);
if ($article)
{
@@ -586,7 +586,7 @@ class GenericPage
if (empty($this->infobox) && !empty($article['quickInfo']))
$this->infobox = $article['quickInfo'];
- if ($article['locale'] != User::$localeId)
+ if ($article['locale'] != Lang::getLocale()->value)
$this->article['params']['prepend'] = '