diff --git a/includes/ajaxHandler/filter.class.php b/includes/ajaxHandler/filter.class.php index 951a84be..3f93f68c 100644 --- a/includes/ajaxHandler/filter.class.php +++ b/includes/ajaxHandler/filter.class.php @@ -19,6 +19,8 @@ class AjaxFilter extends AjaxHandler if (!$params) return; + parent::__construct($params); + $p = explode('=', $params[0]); $this->page = $p[0]; @@ -32,55 +34,15 @@ class AjaxFilter extends AjaxHandler $opts = ['parentCats' => $this->cat]; - switch ($p[0]) + // so usually the page call is just the DBTypes file string with a plural 's' .. but then there are currencies + $fileStr = match ($this->page) { - case 'achievements': - $this->filter = (new AchievementListFilter(true, $opts)); - break; - case 'areatriggers': - $this->filter = (new AreaTriggerListFilter(true, $opts)); - break; - case 'enchantments': - $this->filter = (new EnchantmentListFilter(true, $opts)); - break; - case 'icons': - $this->filter = (new IconListFilter(true, $opts)); - break; - case 'items': - $this->filter = (new ItemListFilter(true, $opts)); - break; - case 'itemsets': - $this->filter = (new ItemsetListFilter(true, $opts)); - break; - case 'npcs': - $this->filter = (new CreatureListFilter(true, $opts)); - break; - case 'objects': - $this->filter = (new GameObjectListFilter(true, $opts)); - break; - case 'quests': - $this->filter = (new QuestListFilter(true, $opts)); - break; - case 'sounds': - $this->filter = (new SoundListFilter(true, $opts)); - break; - case 'spells': - $this->filter = (new SpellListFilter(true, $opts)); - break; - case 'profiles': - $this->filter = (new ProfileListFilter(true, $opts)); - break; - case 'guilds': - $this->filter = (new GuildListFilter(true, $opts)); - break; - case 'arena-teams': - $this->filter = (new ArenaTeamListFilter(true, $opts)); - break; - default: - return; - } + 'currencies' => 'currency', + default => substr($this->page, 0, -1) + }; - parent::__construct($params); + // yes, the whole _POST! .. should the input fields be exposed and static so they can be evaluated via BaseResponse::initRequestData() ? + $this->filter = Type::newFilter($fileStr, $_POST, $opts); // always this one $this->handler = 'handleFilter'; @@ -90,20 +52,16 @@ class AjaxFilter extends AjaxHandler { $url = '?'.$this->page; - $this->filter->mergeCat($this->cat); + $this->filter?->mergeCat($this->cat); if ($this->cat) $url .= '='.implode('.', $this->cat); - $fi = []; - if ($x = $this->filter->getFilterString()) + if ($x = $this->filter?->buildGETParam()) $url .= '&filter='.$x; - if ($this->filter->error) - $_SESSION['fiError'] = get_class($this->filter); - - if ($fi) - $url .= '&filter='.implode(';', $fi); + if ($this->filter?->error) + $_SESSION['error']['fi'] = get_class($this->filter); // do get request return $url; diff --git a/includes/components/filter.class.php b/includes/components/filter.class.php new file mode 100644 index 00000000..b139b444 --- /dev/null +++ b/includes/components/filter.class.php @@ -0,0 +1,726 @@ +parentCats[0] = $v; // directly redirect onto this region + $v = ''; // remove from filter + + return true; + } + + return false; + } + + protected function cbServerCheck(string &$v) : bool + { + foreach (Profiler::getRealms() as $realm) + if (Profiler::urlize($realm['name'], true) == $v) + { + $this->parentCats[1] = $v; // directly redirect onto this server + $v = ''; // remove from filter + + return true; + } + + return false; + } +} + +abstract class Filter +{ + private static $wCards = ['*' => '%', '?' => '_']; + + public const CR_BOOLEAN = 1; + public const CR_FLAG = 2; + public const CR_NUMERIC = 3; + public const CR_STRING = 4; + public const CR_ENUM = 5; + public const CR_STAFFFLAG = 6; + public const CR_CALLBACK = 7; + public const CR_NYI_PH = 999; + + public const V_EQUAL = 8; + public const V_RANGE = 9; + public const V_LIST = 10; + public const V_CALLBACK = 11; + public const V_REGEX = 12; + + protected const ENUM_ANY = -2323; + protected const ENUM_NONE = -2324; + + protected const PATTERN_NAME = '/[\p{C};%\\\\]/ui'; + protected const PATTERN_CRV = '/[\p{C};:%\\\\]/ui'; + protected const PATTERN_INT = '/\D/'; + public const PATTERN_PARAM = '/^[\p{L}\p{Sm} \d\p{P}]+$/i'; + + protected const ENUM_FACTION = array( 469, 1037, 1106, 529, 1012, 87, 21, 910, 609, 942, 909, 530, 69, 577, 930, 1068, 1104, 729, 369, 92, + 54, 946, 67, 1052, 749, 47, 989, 1090, 1098, 978, 1011, 93, 1015, 1038, 76, 470, 349, 1031, 1077, 809, + 911, 890, 970, 169, 730, 72, 70, 932, 1156, 933, 510, 1126, 1067, 1073, 509, 941, 1105, 990, 934, 935, + 1094, 1119, 1124, 1064, 967, 1091, 59, 947, 81, 576, 922, 68, 1050, 1085, 889, 589, 270); + protected const ENUM_CURRENCY = array(32572, 32569, 29736, 44128, 20560, 20559, 29434, 37829, 23247, 44990, 24368, 52027, 52030, 43016, 41596, 34052, 45624, 49426, 40752, 47241, + 40753, 29024, 24245, 26045, 26044, 38425, 29735, 24579, 24581, 32897, 22484, 52026, 52029, 4291, 28558, 43228, 34664, 47242, 52025, 52028, + 37836, 20558, 34597, 43589); + protected const ENUM_EVENT = array( 372, 283, 285, 353, 420, 400, 284, 201, 374, 409, 141, 324, 321, 424, 423, 327, 341, 181, 404, 398, + 301); + protected const ENUM_ZONE = array( 4494, 36, 2597, 3358, 45, 331, 3790, 4277, 16, 3524, 3, 3959, 719, 1584, 25, 1583, 2677, 3702, 3522, 4, + 3525, 3537, 46, 1941, 2918, 3905, 4024, 2817, 4395, 4378, 148, 393, 1657, 41, 2257, 405, 2557, 65, 4196, 1, + 14, 10, 15, 139, 12, 3430, 3820, 361, 357, 3433, 721, 394, 3923, 4416, 2917, 4272, 4820, 4264, 3483, 3562, + 267, 495, 4742, 3606, 210, 4812, 1537, 4710, 4080, 3457, 38, 4131, 3836, 3792, 2100, 2717, 493, 215, 3518, 3698, + 3456, 3523, 2367, 2159, 1637, 4813, 4298, 2437, 722, 491, 44, 3429, 3968, 796, 2057, 51, 3607, 3791, 3789, 209, + 3520, 3703, 3711, 1377, 3487, 130, 3679, 406, 1519, 4384, 33, 2017, 1477, 4075, 8, 440, 141, 3428, 3519, 3848, + 17, 2366, 3840, 3713, 3847, 3775, 4100, 1581, 3557, 3845, 4500, 4809, 47, 3849, 4265, 4493, 4228, 3698, 4406, 3714, + 3717, 3715, 717, 67, 3716, 457, 4415, 400, 1638, 1216, 85, 4723, 4722, 1337, 4273, 490, 1497, 206, 1196, 4603, + 718, 3277, 28, 40, 11, 4197, 618, 3521, 3805, 66, 1176, 1977); + protected const ENUM_HEROICDUNGEON = array( 4494, 3790, 4277, 4196, 4416, 4272, 4820, 4264, 3562, 4131, 3792, 2367, 4813, 3791, 3789, 3848, 2366, 3713, 3847, 4100, + 4809, 3849, 4265, 4228, 3714, 3717, 3715, 3716, 4415, 4723, 206, 1196); + protected const ENUM_MULTIMODERAID = array( 4812, 3456, 2159, 4500, 4493, 4722, 4273, 4603, 4987); + protected const ENUM_HEROICRAID = array( 4987, 4812, 4722); + protected const ENUM_CLASSS = array( null, 1, 2, 3, 4, 5, 6, 7, 8, 9, null, 11, true, false); + protected const ENUM_RACE = array( null, 1, 2, 3, 4, 5, 6, 7, 8, null, 10, 11, true, false); + protected const ENUM_PROFESSION = array( null, 171, 164, 185, 333, 202, 129, 755, 165, 186, 197, true, false, 356, 182, 773); + + public bool $error = false; // erroneous search fields + + // item related + public array $upgrades = []; // [itemId => slotId] + public array $extraOpts = []; // score for statWeights + public array $wtCnd = []; // DBType condition for statWeights + + private array $cndSet = []; // db type query storage + private array $rawData = []; + + /* genericFilter: [FILTER_TYPE, colOrFnName, param1, param2] + [self::CR_BOOLEAN, , , null] + [self::CR_FLAG, , , ] # default param2: matchExact + [self::CR_NUMERIC, , , ] + [self::CR_STRING, , , null] + [self::CR_ENUM, , , ] # param3 ? crv is val in enum : key in enum + [self::CR_STAFFFLAG, , null, null] + [self::CR_CALLBACK, , , ] + [self::CR_NYI_PH, null, , param2] # mostly 1: to ignore this criterium; 0: to fail the whole query + */ + protected array $genericFilter = []; + protected array $inputFields = []; // list of input fields defined per page - fieldName => [checkType, checkValue[, fieldIsArray]] + protected array $enums = []; // validation for opt lists per page - criteriumID => [validOptionList] + protected string $type = ''; // set by child + protected array $parentCats = []; // used to validate ty-filter + + // express Filters in template + public string $fiInit = ''; // str: filter template (and init html form) + public string $fiType = ''; // str: filter template (set without init) + public array $fiSetCriteria = []; // fn params (cr, crs, crv) + public array $fiSetWeights = []; // fn params (weights, nt, ids, stealth) + public array $fiReputationCols = []; // fn params ([[factionId, factionName], ...]) + public array $fiExtraCols = []; // + public string $query = ''; // as in url query params + public array $values = []; // old fiData['v'] + public array $criteria = []; // old fiData['c'] + + // parse the provided request into a usable format + public function __construct(string|array $data, array $opts = []) + { + $this->parentCats = $opts['parentCats'] ?? []; + + // use fn fi_init() if we have a criteria selector, else use var fi_type + if ($this->genericFilter) + $this->fiInit = $this->type; + else + $this->fiType = $this->type; + + if (is_array($data)) + $this->rawData = $data; // could set >query for consistency sake, but is not used when converting from POST + + if (is_string($data)) + { + // an error occured, while processing POST + if (isset($_SESSION['error']['fi'])) + { + $this->error = $_SESSION['error']['fi'] == get_class($this); + unset($_SESSION['error']['fi']); + } + + $this->query = $data; + $this->rawData = $this->transformGET($data); + } + + $this->initFields(); + } + + public function mergeCat(array &$cats) : void + { + foreach ($this->parentCats as $idx => $cat) + $cats[$idx] = $cat; + } + + private function &criteriaIterator() : \Generator + { + if (!$this->criteria) + return; + + for ($i = 0; $i < count($this->criteria['cr']); $i++) + { + // throws a notice if yielded directly "Only variable references should be yielded by reference" + $v = [&$this->criteria['cr'][$i], &$this->criteria['crs'][$i], &$this->criteria['crv'][$i]]; + yield $i => $v; + } + } + + + /***********************/ + /* get prepared values */ + /***********************/ + + public function buildGETParam(array $override = [], array $addCr = []) : string + { + $get = []; + foreach (array_merge($this->criteria, $this->values, $override) as $k => $v) + { + if (isset($addCr[$k])) + { + $v = $v ? array_merge((array)$v, (array)$addCr[$k]) : $addCr[$k]; + unset($addCr[$k]); + } + + if ($v === '' || $v === null || $v === []) + continue; + + $get[$k] = $k.'='.(is_array($v) ? implode(':', $v) : $v); + } + + // no criteria were set, so no merge occured .. append + if ($addCr) + { + $get['cr'] = 'cr='.$addCr['cr']; + $get['crs'] = 'crs='.$addCr['crs']; + $get['crv'] = 'crv='.$addCr['crv']; + } + + return implode(';', $get); + } + + public function getConditions() : array + { + if (!$this->cndSet) + { + // values + $this->cndSet = $this->createSQLForValues(); + + // criteria + foreach ($this->criteriaIterator() as &$_cr) + if ($cnd = $this->createSQLForCriterium(...$_cr)) + $this->cndSet[] = $cnd; + + if ($this->cndSet) // Note: TYPE_SOUND does not use 'match any' + array_unshift($this->cndSet, empty($this->values['ma']) ? 'AND' : 'OR'); + } + + return $this->cndSet; + } + + public function getSetCriteria(int ...$cr) : array + { + if (!$cr || !$this->fiSetCriteria) + return $this->fiSetCriteria; + + return array_intersect($this->fiSetCriteria['cr'], $cr); + } + + + /**********************/ + /* input sanitization */ + /**********************/ + + private function transformGET(string $get) : array + { + if (!$get) + return []; + + $data = []; + foreach (explode(';', $get) as $field) + { + if (!strstr($field, '=')) + { + trigger_error('Filter::transformGET - malformed GET string', E_USER_NOTICE); + $this->error = true; + continue; + } + + [$k, $v] = explode('=', $field); + + if (!isset($this->inputFields[$k])) + { + trigger_error('Filter::transformGET - GET param not in filter: '.$k, E_USER_NOTICE); + $this->error = true; + continue; + } + + $asArray = $this->inputFields[$k][2]; + + $data[$k] = $asArray ? explode(':', $v) : $v; + } + + return $data; + } + + private function initFields() : void + { + foreach ($this->inputFields as $inp => [$type, $valid, $asArray]) + { + $var = in_array($inp, ['cr', 'crs', 'crv']) ? 'criteria' : 'values'; + + if (!isset($this->rawData[$inp]) || $this->rawData[$inp] === '') + { + $this->$var[$inp] = $asArray ? [] : null; + continue; + } + + $val = $this->rawData[$inp]; + + if ($asArray) + { + // quirk: in the POST step criteria can be [[''], null, null] if not selected. + $buff = []; + foreach ($val as $v) + if ($v !== '' && $this->checkInput($type, $valid, $v)) + $buff[] = $v; + + $this->$var[$inp] = $buff; + } + else + $this->$var[$inp] = $this->checkInput($type, $valid, $val) ? $val : null; + } + } + + public function evalCriteria() : void // [cr]iterium, [cr].[s]ign, [cr].[v]alue + { + if (empty($this->criteria['cr']) && empty($this->criteria['crs']) && empty($this->criteria['crv'])) + return; + else if (empty($this->criteria['cr']) || empty($this->criteria['crs']) || empty($this->criteria['crv'])) + { + unset($this->criteria['cr']); + unset($this->criteria['crs']); + unset($this->criteria['crv']); + + trigger_error('Filter::setCriteria - one of cr, crs, crv is missing', E_USER_NOTICE); + $this->error = true; + return; + } + + $_cr = &$this->criteria['cr']; + $_crs = &$this->criteria['crs']; + $_crv = &$this->criteria['crv']; + + if (count($_cr) != count($_crv) || count($_cr) != count($_crs) || count($_cr) > 5 || count($_crs) > 5 /*|| count($_crv) > 5*/) + { + // use min provided criterion as basis; 5 criteria at most + $min = max(5, min(count($_cr), count($_crv), count($_crs))); + if (count($_cr) > $min) + array_splice($_cr, $min); + + if (count($_crv) > $min) + array_splice($_crv, $min); + + if (count($_crs) > $min) + array_splice($_crs, $min); + + trigger_error('Filter::setCriteria - cr, crs, crv are imbalanced', E_USER_NOTICE); + $this->error = true; + } + + for ($i = 0; $i < count($_cr); $i++) + { + // conduct filter specific checks & casts here + $unsetme = false; + if (isset($this->genericFilter[$_cr[$i]])) + { + $gf = $this->genericFilter[$_cr[$i]]; + switch ($gf[0]) + { + case self::CR_NUMERIC: + $_ = $_crs[$i]; + if (!Util::checkNumeric($_crv[$i], $gf[2]) || !$this->int2Op($_)) + $unsetme = true; + break; + case self::CR_BOOLEAN: + case self::CR_FLAG: + $_ = $_crs[$i]; + if (!$this->int2Bool($_)) + $unsetme = true; + break; + case self::CR_ENUM: + case self::CR_STAFFFLAG: + if (!Util::checkNumeric($_crs[$i], NUM_CAST_INT)) + $unsetme = true; + break; + } + } + + if (!$unsetme && intval($_cr[$i]) && $_crs[$i] !== '' && $_crv[$i] !== '') + continue; + + unset($_cr[$i]); + unset($_crs[$i]); + unset($_crv[$i]); + + trigger_error('Filter::setCriteria - generic check failed ["'.$_cr[$i].'", "'.$_crs[$i].'", "'.$_crv[$i].'"]', E_USER_NOTICE); + $this->error = true; + } + + $this->fiSetCriteria = array( + 'cr' => $_cr, + 'crs' => $_crs, + 'crv' => $_crv + ); + } + + public function evalWeights() : void + { + // both empty: not in use; not an error + if (!$this->values['wt'] && !$this->values['wtv']) + return; + + // one empty: erroneous manual input? + if (!$this->values['wt'] || !$this->values['wtv']) + { + unset($this->values['wt']); + unset($this->values['wtv']); + + trigger_error('Filter::setWeights - one of wt, wtv is missing', E_USER_NOTICE); + $this->error = true; + return; + } + + $_wt = &$this->values['wt']; + $_wtv = &$this->values['wtv']; + + $nwt = count($_wt); + $nwtv = count($_wtv); + + if ($nwt != $nwtv) + { + trigger_error('Filter::setWeights - wt, wtv are imbalanced', E_USER_NOTICE); + $this->error = true; + } + + if ($nwt > $nwtv) + array_splice($_wt, $nwtv); + else if ($nwtv > $nwt) + array_splice($_wtv, $nwt); + + $this->fiSetWeights = [$_wt, $_wtv]; + } + + protected function checkInput(int $type, mixed $valid, mixed &$val, bool $recursive = false) : bool + { + switch ($type) + { + case self::V_EQUAL: + if (gettype($valid) == 'integer') + $val = intval($val); + else if (gettype($valid) == 'double') + $val = floatval($val); + else /* if (gettype($valid) == 'string') */ + $val = strval($val); + + if ($valid == $val) + return true; + + break; + case self::V_LIST: + if (!Util::checkNumeric($val, NUM_CAST_INT)) + return false; + + foreach ($valid as $k => $v) + { + if (gettype($v) != 'array') + continue; + + if ($this->checkInput(self::V_RANGE, $v, $val, true)) + return true; + + unset($valid[$k]); + } + + if (in_array($val, $valid)) + return true; + + break; + case self::V_RANGE: + if (Util::checkNumeric($val, NUM_CAST_INT) && $val >= $valid[0] && $val <= $valid[1]) + return true; + + break; + case self::V_CALLBACK: + if ($this->$valid($val)) + return true; + + break; + case self::V_REGEX: + if (!preg_match($valid, $val)) + return true; + + break; + } + + if (!$recursive) + { + trigger_error('Filter::checkInput - check failed [type: '.$type.' valid: '.((string)$valid).' val: '.((string)$val).']', E_USER_NOTICE); + $this->error = true; + } + + return false; + } + + protected function transformToken(string $string, bool $exact) : string + { + // escape manually entered _; entering % should be prohibited + $string = str_replace('_', '\\_', $string); + + // now replace search wildcards with sql wildcards + $string = strtr($string, self::$wCards); + + return sprintf($exact ? '%s' : '%%%s%%', $string); + } + + protected function tokenizeString(array $fields, string $string = '', bool $exact = false, bool $shortStr = false) : array + { + if (!$string && $this->values['na']) + $string = $this->values['na']; + + $qry = []; + foreach ($fields as $f) + { + $sub = []; + $parts = $exact ? [$string] : array_filter(explode(' ', $string)); + foreach ($parts as $p) + { + if ($p[0] == '-' && (mb_strlen($p) > 3 || $shortStr)) + $sub[] = [$f, $this->transformToken(mb_substr($p, 1), $exact), '!']; + else if ($p[0] != '-' && (mb_strlen($p) > 2 || $shortStr)) + $sub[] = [$f, $this->transformToken($p, $exact)]; + } + + // single cnd? + if (!$sub) + continue; + else if (count($sub) > 1) + array_unshift($sub, 'AND'); + else + $sub = $sub[0]; + + $qry[] = $sub; + } + + // single cnd? + if (!$qry) + { + trigger_error('Filter::tokenizeString - could not tokenize string: '.$string, E_USER_NOTICE); + $this->error = true; + } + else if (count($qry) > 1) + array_unshift($qry, 'OR'); + else + $qry = $qry[0]; + + return $qry; + } + + protected function int2Op(mixed &$op) : bool + { + $op = match ($op) { + 1 => '>', + 2 => '>=', + 3 => '=', + 4 => '<=', + 5 => '<', + 6 => '!=', + default => null + }; + + return $op !== null; + } + + protected function int2Bool(mixed &$op) : bool + { + $op = match ($op) { + 1 => true, + 2 => false, + default => null + }; + + return $op !== null; + } + + protected function list2Mask(array $list, bool $noOffset = false) : int + { + $mask = 0x0; + $o = $noOffset ? 0 : 1; // schoolMask requires this..? + + foreach ($list as $itm) + $mask += (1 << (intval($itm) - $o)); + + return $mask; + } + + + /**************************/ + /* create conditions from */ + /* generic criteria */ + /**************************/ + + private function genericBoolean(string $field, int $op, bool $isString) : ?array + { + if ($this->int2Bool($op)) + { + $value = $isString ? '' : 0; + $operator = $op ? '!' : null; + + return [$field, $value, $operator]; + } + + return null; + } + + private function genericBooleanFlags(string $field, int $value, int $op, ?bool $matchAny = false) : ?array + { + if (!$this->int2Bool($op)) + return null; + + if (!$op) + return [[$field, $value, '&'], 0]; + else if ($matchAny) + return [[$field, $value, '&'], 0, '!']; + else + return [[$field, $value, '&'], $value]; + } + + private function genericString(string $field, string $value, ?int $strFlags) : ?array + { + $strFlags ??= 0x0; + + if ($strFlags & STR_LOCALIZED) + $field .= '_loc'.Lang::getLocale()->value; + + return $this->tokenizeString([$field], $value, $strFlags & STR_MATCH_EXACT, $strFlags & STR_ALLOW_SHORT); + } + + private function genericNumeric(string $field, int|float $value, int $op, int $typeCast) : ?array + { + if (!Util::checkNumeric($value, $typeCast)) + return null; + + if ($this->int2Op($op)) + return [$field, $value, $op]; + + return null; + } + + private function genericEnum(string $field, mixed $value) : ?array + { + if (is_bool($value)) + return [$field, 0, ($value ? '>' : '<=')]; + else if ($value == self::ENUM_ANY) + return [$field, 0, '!']; + else if ($value == self::ENUM_NONE) + return [$field, 0]; + else if ($value !== null) + return [$field, $value]; + + return null; + } + + private function genericCriterion(int $cr, int $crs, string $crv) : ?array + { + [$crType, $colOrFn, $param1, $param2] = array_pad($this->genericFilter[$cr], 4, null); + $result = null; + + switch ($crType) + { + case self::CR_NUMERIC: + $result = $this->genericNumeric($colOrFn, $crv, $crs, $param1); + break; + case self::CR_FLAG: + $result = $this->genericBooleanFlags($colOrFn, $param1, $crs, $param2); + break; + case self::CR_STAFFFLAG: + if (User::isInGroup(U_GROUP_EMPLOYEE) && $crs > 0) + $result = $this->genericBooleanFlags($colOrFn, (1 << ($crs - 1)), true); + break; + case self::CR_BOOLEAN: + $result = $this->genericBoolean($colOrFn, $crs, !empty($param1)); + break; + case self::CR_STRING: + $result = $this->genericString($colOrFn, $crv, $param1); + break; + case self::CR_ENUM: + if (!$param2 && isset($this->enums[$cr][$crs])) + $result = $this->genericEnum($colOrFn, $this->enums[$cr][$crs]); + if ($param2 && in_array($crs, $this->enums[$cr])) + $result = $this->genericEnum($colOrFn, $crs); + else if ($param1 && ($crs == self::ENUM_ANY || $crs == self::ENUM_NONE)) + $result = $this->genericEnum($colOrFn, $crs); + break; + case self::CR_CALLBACK: + $result = $this->{$colOrFn}($cr, $crs, $crv, $param1, $param2); + break; + case self::CR_NYI_PH: // do not limit with not implemented filters + if (is_int($param1)) + return [$param1]; + + // for nonsensical values; compare against 0 + if ($this->int2Op($crs) && Util::checkNumeric($crv)) + { + if ($crs == '=') + $crs = '=='; + + return eval('return ('.$crv.' '.$crs.' 0);') ? [1] : [0]; + } + else + return [0]; + } + + if ($result && $crType == self::CR_NUMERIC && !empty($param2)) + $this->fiExtraCols[] = $cr; + + return $result; + } + + + /***********************************/ + /* create conditions from */ + /* non-generic values and criteria */ + /***********************************/ + + protected function createSQLForCriterium(int &$cr, int &$crs, string &$crv) : array + { + if (!$this->genericFilter) // criteria not in use - no error + return []; + + if (isset($this->genericFilter[$cr])) + if ($genCr = $this->genericCriterion($cr, $crs, $crv)) + return $genCr; + + trigger_error('Filter::createSQLForCriterium - received unhandled criterium: ["'.$cr.'", "'.$crs.'", "'.$crv.'"]', E_USER_NOTICE); + $this->error = true; + + unset($cr, $crs, $crv); + + return []; + } + + abstract protected function createSQLForValues() : array; +} + +?> diff --git a/includes/defines.php b/includes/defines.php index ac71c8b8..fe4850d8 100644 --- a/includes/defines.php +++ b/includes/defines.php @@ -597,10 +597,10 @@ define('LOCK_PROPERTY_HERBALISM', 2); define('LOCK_PROPERTY_MINING', 3); // Creature -define('NPC_TYPEFLAG_HERBLOOT', 0x0100); -define('NPC_TYPEFLAG_MININGLOOT', 0x0200); -define('NPC_TYPEFLAG_ENGINEERLOOT', 0x8000); -define('NPC_TYPEFLAG_SPECIALLOOT', NPC_TYPEFLAG_ENGINEERLOOT | NPC_TYPEFLAG_MININGLOOT | NPC_TYPEFLAG_HERBLOOT); +define('NPC_TYPEFLAG_SKIN_WITH_HERBALISM', 0x0100); +define('NPC_TYPEFLAG_SKIN_WITH_MINING', 0x0200); +define('NPC_TYPEFLAG_SKIN_WITH_ENGINEERING', 0x8000); +define('NPC_TYPEFLAG_SPECIALLOOT', NPC_TYPEFLAG_SKIN_WITH_ENGINEERING | NPC_TYPEFLAG_SKIN_WITH_MINING | NPC_TYPEFLAG_SKIN_WITH_HERBALISM); define('NPC_RANK_NORMAL', 0); define('NPC_RANK_ELITE', 1); diff --git a/includes/game/loot.class.php b/includes/game/loot.class.php index c7029470..4ba60daf 100644 --- a/includes/game/loot.class.php +++ b/includes/game/loot.class.php @@ -644,11 +644,11 @@ class Loot foreach ($srcObj->iterate() as $curTpl) { - if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_HERBLOOT) + if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_SKIN_WITH_HERBALISM) $tabId = 9; - else if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_ENGINEERLOOT) + else if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_SKIN_WITH_ENGINEERING) $tabId = 8; - else if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_MININGLOOT) + else if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_SKIN_WITH_MINING) $tabId = 7; else if ($tabId < 0) $tabId = abs($tabId); // general case (skinning) diff --git a/includes/types/achievement.class.php b/includes/types/achievement.class.php index 9257e203..548bd60a 100644 --- a/includes/types/achievement.class.php +++ b/includes/types/achievement.class.php @@ -283,8 +283,8 @@ class AchievementList extends BaseType class AchievementListFilter extends Filter { - - protected $enums = array( + protected string $type = 'achievements'; + protected array $enums = array( 4 => parent::ENUM_ZONE, // location 11 => array( 327 => 160, // Lunar Festival @@ -307,7 +307,7 @@ class AchievementListFilter extends Filter ) ); - protected $genericFilter = array( + protected array $genericFilter = array( 2 => [parent::CR_BOOLEAN, 'reward_loc0', true ], // givesreward 3 => [parent::CR_STRING, 'reward', STR_LOCALIZED ], // rewardtext 4 => [parent::CR_NYI_PH, null, 1, ], // location [enum] @@ -323,7 +323,7 @@ class AchievementListFilter extends Filter 18 => [parent::CR_STAFFFLAG, 'flags', ] // flags ); - protected $inputFields = array( + protected array $inputFields = array( 'cr' => [parent::V_RANGE, [2, 18], true ], // criteria ids 'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 99999]], true ], // criteria operators 'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiters @@ -335,47 +335,43 @@ class AchievementListFilter extends Filter 'maxpt' => [parent::V_RANGE, [1, 99], false] // required level max ); - protected function createSQLForValues() + protected function createSQLForValues() : array { $parts = []; - $_v = &$this->fiData['v']; + $_v = &$this->values; // name ex: +description, +rewards - if (isset($_v['na'])) + if ($_v['na']) { $_ = []; - if (isset($_v['ex']) && $_v['ex'] == 'on') - $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value, 'reward_loc'.Lang::getLocale()->value, 'description_loc'.Lang::getLocale()->value]); + if ($_v['ex'] == 'on') + $_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value, 'reward_loc'.Lang::getLocale()->value, 'description_loc'.Lang::getLocale()->value]); else - $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value]); + $_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]); if ($_) $parts[] = $_; } // points min - if (isset($_v['minpt'])) + if ($_v['minpt']) $parts[] = ['points', $_v['minpt'], '>=']; // points max - if (isset($_v['maxpt'])) + if ($_v['maxpt']) $parts[] = ['points', $_v['maxpt'], '<=']; // faction (side) - if (isset($_v['si'])) + if ($_v['si']) { - switch ($_v['si']) + $parts[] = match ($_v['si']) { - case -SIDE_ALLIANCE: // equals faction - case -SIDE_HORDE: - $parts[] = ['faction', -$_v['si']]; - break; - case SIDE_ALLIANCE: // includes faction - case SIDE_HORDE: - case SIDE_BOTH: - $parts[] = ['faction', $_v['si'], '&']; - break; - } + -SIDE_ALLIANCE, // equals faction + -SIDE_HORDE => ['faction', -$_v['si']], + SIDE_ALLIANCE, // includes faction + SIDE_HORDE, + SIDE_BOTH => ['faction', $_v['si'], '&'] + }; } return $parts; diff --git a/includes/types/areatrigger.class.php b/includes/types/areatrigger.class.php index e7e33577..99d333e2 100644 --- a/includes/types/areatrigger.class.php +++ b/includes/types/areatrigger.class.php @@ -59,12 +59,13 @@ class AreaTriggerList extends BaseType class AreaTriggerListFilter extends Filter { - protected $genericFilter = array( + protected string $type = 'areatrigger'; + protected array $genericFilter = array( 2 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT] // id ); // fieldId => [checkType, checkValue[, fieldIsArray]] - protected $inputFields = array( + protected array $inputFields = array( 'cr' => [parent::V_LIST, [2], true ], // criteria ids 'crs' => [parent::V_RANGE, [1, 6], true ], // criteria operators 'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - all criteria are numeric here @@ -73,18 +74,18 @@ class AreaTriggerListFilter extends Filter 'ty' => [parent::V_RANGE, [0, 5], true ] // types ); - protected function createSQLForValues() + protected function createSQLForValues() : array { $parts = []; - $_v = &$this->fiData['v']; + $_v = &$this->values; // name [str] - if (isset($_v['na'])) - if ($_ = $this->modularizeString(['name'])) + if ($_v['na']) + if ($_ = $this->tokenizeString(['name'])) $parts[] = $_; // type [list] - if (isset($_v['ty'])) + if ($_v['ty']) $parts[] = ['type', $_v['ty']]; return $parts; diff --git a/includes/types/arenateam.class.php b/includes/types/arenateam.class.php index 4f4bd946..50ae9c74 100644 --- a/includes/types/arenateam.class.php +++ b/includes/types/arenateam.class.php @@ -48,10 +48,9 @@ class ArenaTeamListFilter extends Filter { use TrProfilerFilter; - public $extraOpts = []; - protected $genericFilter = []; - - protected $inputFields = array( + protected string $type = 'arenateams'; + protected array $genericFilter = []; + protected array $inputFields = array( 'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter 'ma' => [parent::V_EQUAL, 1, false], // match any / all filter 'ex' => [parent::V_EQUAL, 'on', false], // only match exact @@ -61,29 +60,28 @@ class ArenaTeamListFilter extends Filter 'sv' => [parent::V_CALLBACK, 'cbServerCheck', false], // server ); - protected function createSQLForValues() + public array $extraOpts = []; + + protected function createSQLForValues() : array { $parts = []; - $_v = $this->fiData['v']; + $_v = $this->values; // region (rg), battlegroup (bg) and server (sv) are passed to ArenaTeamList as miscData and handled there // name [str] - if (!empty($_v['na'])) - if ($_ = $this->modularizeString(['at.name'], $_v['na'], !empty($_v['ex']) && $_v['ex'] == 'on')) + if ($_v['na']) + if ($_ = $this->tokenizeString(['at.name'], $_v['na'], $_v['ex'] == 'on')) $parts[] = $_; // side [list] - if (!empty($_v['si'])) - { - if ($_v['si'] == SIDE_ALLIANCE) - $parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_ALLIANCE)]; - else if ($_v['si'] == SIDE_HORDE) - $parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_HORDE)]; - } + if ($_v['si'] == SIDE_ALLIANCE) + $parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_ALLIANCE)]; + else if ($_v['si'] == SIDE_HORDE) + $parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_HORDE)]; // size [int] - if (!empty($_v['sz'])) + if ($_v['sz']) $parts[] = ['at.type', $_v['sz']]; return $parts; diff --git a/includes/types/basetype.class.php b/includes/types/basetype.class.php index 57c2ca8a..ec1fdff9 100644 --- a/includes/types/basetype.class.php +++ b/includes/types/basetype.class.php @@ -918,743 +918,4 @@ trait sourceHelper } } -trait TrProfilerFilter -{ - protected function cbRegionCheck(string &$v) : bool - { - if (in_array($v, Util::$regions)) - { - $this->parentCats[0] = $v; // directly redirect onto this region - $v = ''; // remove from filter - - return true; - } - - return false; - } - - protected function cbServerCheck(string &$v) : bool - { - foreach (Profiler::getRealms() as $realm) - { - if (Profiler::urlize($realm['name']) != $v) - continue; - - $this->parentCats[1] = $v; // directly redirect onto this server - $v = ''; // remove from filter - - return true; - } - - return false; - } -} - -abstract class Filter -{ - private static $wCards = ['*' => '%', '?' => '_']; - - public const CR_BOOLEAN = 1; - public const CR_FLAG = 2; - public const CR_NUMERIC = 3; - public const CR_STRING = 4; - public const CR_ENUM = 5; - public const CR_STAFFFLAG = 6; - public const CR_CALLBACK = 7; - public const CR_NYI_PH = 999; - - public const V_EQUAL = 8; - public const V_RANGE = 9; - public const V_LIST = 10; - public const V_CALLBACK = 11; - public const V_REGEX = 12; - - protected const ENUM_ANY = -2323; - protected const ENUM_NONE = -2324; - - protected const PATTERN_NAME = '/[\p{C};%\\\\]/ui'; - protected const PATTERN_CRV = '/[\p{C};:%\\\\]/ui'; - protected const PATTERN_INT = '/\D/'; - - protected const ENUM_FACTION = array( 469, 1037, 1106, 529, 1012, 87, 21, 910, 609, 942, 909, 530, 69, 577, 930, 1068, 1104, 729, 369, 92, - 54, 946, 67, 1052, 749, 47, 989, 1090, 1098, 978, 1011, 93, 1015, 1038, 76, 470, 349, 1031, 1077, 809, - 911, 890, 970, 169, 730, 72, 70, 932, 1156, 933, 510, 1126, 1067, 1073, 509, 941, 1105, 990, 934, 935, - 1094, 1119, 1124, 1064, 967, 1091, 59, 947, 81, 576, 922, 68, 1050, 1085, 889, 589, 270); - protected const ENUM_CURRENCY = array(32572, 32569, 29736, 44128, 20560, 20559, 29434, 37829, 23247, 44990, 24368, 52027, 52030, 43016, 41596, 34052, 45624, 49426, 40752, 47241, - 40753, 29024, 24245, 26045, 26044, 38425, 29735, 24579, 24581, 32897, 22484, 52026, 52029, 4291, 28558, 43228, 34664, 47242, 52025, 52028, - 37836, 20558, 34597, 43589); - protected const ENUM_EVENT = array( 372, 283, 285, 353, 420, 400, 284, 201, 374, 409, 141, 324, 321, 424, 423, 327, 341, 181, 404, 398, - 301); - protected const ENUM_ZONE = array( 4494, 36, 2597, 3358, 45, 331, 3790, 4277, 16, 3524, 3, 3959, 719, 1584, 25, 1583, 2677, 3702, 3522, 4, - 3525, 3537, 46, 1941, 2918, 3905, 4024, 2817, 4395, 4378, 148, 393, 1657, 41, 2257, 405, 2557, 65, 4196, 1, - 14, 10, 15, 139, 12, 3430, 3820, 361, 357, 3433, 721, 394, 3923, 4416, 2917, 4272, 4820, 4264, 3483, 3562, - 267, 495, 4742, 3606, 210, 4812, 1537, 4710, 4080, 3457, 38, 4131, 3836, 3792, 2100, 2717, 493, 215, 3518, 3698, - 3456, 3523, 2367, 2159, 1637, 4813, 4298, 2437, 722, 491, 44, 3429, 3968, 796, 2057, 51, 3607, 3791, 3789, 209, - 3520, 3703, 3711, 1377, 3487, 130, 3679, 406, 1519, 4384, 33, 2017, 1477, 4075, 8, 440, 141, 3428, 3519, 3848, - 17, 2366, 3840, 3713, 3847, 3775, 4100, 1581, 3557, 3845, 4500, 4809, 47, 3849, 4265, 4493, 4228, 3698, 4406, 3714, - 3717, 3715, 717, 67, 3716, 457, 4415, 400, 1638, 1216, 85, 4723, 4722, 1337, 4273, 490, 1497, 206, 1196, 4603, - 718, 3277, 28, 40, 11, 4197, 618, 3521, 3805, 66, 1176, 1977); - protected const ENUM_HEROICDUNGEON = array( 4494, 3790, 4277, 4196, 4416, 4272, 4820, 4264, 3562, 4131, 3792, 2367, 4813, 3791, 3789, 3848, 2366, 3713, 3847, 4100, - 4809, 3849, 4265, 4228, 3714, 3717, 3715, 3716, 4415, 4723, 206, 1196); - protected const ENUM_MULTIMODERAID = array( 4812, 3456, 2159, 4500, 4493, 4722, 4273, 4603, 4987); - protected const ENUM_HEROICRAID = array( 4987, 4812, 4722); - protected const ENUM_CLASSS = array( null, 1, 2, 3, 4, 5, 6, 7, 8, 9, null, 11, true, false); - protected const ENUM_RACE = array( null, 1, 2, 3, 4, 5, 6, 7, 8, null, 10, 11, true, false); - protected const ENUM_PROFESSION = array( null, 171, 164, 185, 333, 202, 129, 755, 165, 186, 197, true, false, 356, 182, 773); - - public $error = false; // erroneous search fields - - private $cndSet = []; - - /* genericFilter: [FILTER_TYPE, colOrFnName, param1, param2] - [self::CR_BOOLEAN, , , null] - [self::CR_FLAG, , , ] # default param2: matchExact - [self::CR_NUMERIC, , , ] - [self::CR_STRING, , , null] - [self::CR_ENUM, , , ] # param3 ? crv is val in enum : key in enum - [self::CR_STAFFFLAG, , null, null] - [self::CR_CALLBACK, , , ] - [self::CR_NYI_PH, null, , param2] # mostly 1: to ignore this criterium; 0: to fail the whole query - */ - protected $genericFilter = []; - - protected $enums = []; // criteriumID => [validOptionList] - - /* - fieldId => [checkType, checkValue[, fieldIsArray]] - */ - protected $inputFields = []; // list of input fields defined per page - protected $parentCats = []; // used to validate ty-filter - protected $fiData = ['c' => [], 'v' =>[]]; - protected $formData = array( // data to fill form fields - 'form' => [], // base form - unsanitized - 'setCriteria' => [], // dynamic criteria list - index checked - 'setWeights' => [], // dynamic weights list - index checked - 'extraCols' => [], // extra columns for LV - added as required - 'reputationCols' => [] // simlar and exclusive to extraCols - added as required - ); - - // parse the provided request into a usable format - public function __construct(bool $fromPOST = false, array $opts = []) - { - if (!empty($opts['parentCats'])) - $this->parentCats = $opts['parentCats']; - - if ($fromPOST) - $this->evaluatePOST(); - else - { - // an error occured, while processing POST - if (isset($_SESSION['fiError'])) - { - $this->error = $_SESSION['fiError'] == get_class($this); - unset($_SESSION['fiError']); - } - - $this->evaluateGET(); - } - } - - // use to generate cacheKey for filterable pages - public function __sleep() - { - return ['formData']; - } - - public function mergeCat(&$cats) : void - { - foreach ($this->parentCats as $idx => $cat) - $cats[$idx] = $cat; - } - - private function &criteriaIterator() : \Generator - { - if (!$this->fiData['c']) - return; - - for ($i = 0; $i < count($this->fiData['c']['cr']); $i++) - { - // throws a notice if yielded directly "Only variable references should be yielded by reference" - $v = [&$this->fiData['c']['cr'][$i], &$this->fiData['c']['crs'][$i], &$this->fiData['c']['crv'][$i]]; - yield $i => $v; - } - } - - - /***********************/ - /* get prepared values */ - /***********************/ - - public function getFilterString(array $override = [], array $addCr = []) : string - { - $filterURL = []; - foreach (array_merge($this->fiData['c'], $this->fiData['v'], $override) as $k => $v) - { - if (isset($addCr[$k])) - { - $v = $v ? array_merge((array)$v, (array)$addCr[$k]) : $addCr[$k]; - unset($addCr[$k]); - } - - if (is_array($v) && !empty($v)) - $filterURL[$k] = $k.'='.implode(':', $v); - else if ($v !== '') - $filterURL[$k] = $k.'='.$v; - } - - // no criteria were set, so no merge occured .. append - if ($addCr) - { - $filterURL['cr'] = 'cr='.$addCr['cr']; - $filterURL['crs'] = 'crs='.$addCr['crs']; - $filterURL['crv'] = 'crv='.$addCr['crv']; - } - - return implode(';', $filterURL); - } - - // [ExtraCol, ...] - public function getExtraCols() : array - { - return array_unique($this->formData['extraCols']); - } - - // ['cr' => Criterium, 'crs' => CriteriumSign, 'crv' => CriteriumValue] - public function getSetCriteria() : array - { - return $this->formData['setCriteria']; - } - - // [WeightID, WeightValue] - public function getSetWeights() : array - { - return $this->formData['setWeights']; - } - - // [ExtraCol, ...] - public function getReputationCols() : array - { - return $this->formData['reputationCols']; - } - - // [inputField => FieldValue, ...] - public function getForm() : array - { - return $this->formData['form']; - } - - public function getConditions() : array - { - if (!$this->cndSet) - { - // values - $this->cndSet = $this->createSQLForValues(); - - // criteria - foreach ($this->criteriaIterator() as &$_cr) - if ($cnd = $this->createSQLForCriterium(...$_cr)) - $this->cndSet[] = $cnd; - - if ($this->cndSet) - array_unshift($this->cndSet, empty($this->fiData['v']['ma']) ? 'AND' : 'OR'); - } - - return $this->cndSet; - } - - - /**********************/ - /* input sanitization */ - /**********************/ - - private function evaluatePOST() : void - { - // doesn't need to set formData['form']; this happens in GET-step - foreach ($this->inputFields as $inp => [$type, $valid, $asArray]) - { - if (!isset($_POST[$inp]) || $_POST[$inp] === '') - continue; - - $val = $_POST[$inp]; - $k = in_array($inp, ['cr', 'crs', 'crv']) ? 'c' : 'v'; - - if ($asArray) - { - $buff = []; - foreach ((array)$val as $v) - if ($v !== '' && $this->checkInput($type, $valid, $v) && $v !== '') - $buff[] = $v; - - if ($buff) - $this->fiData[$k][$inp] = $buff; - } - else if ($val !== '' && $this->checkInput($type, $valid, $val) && $val !== '') - $this->fiData[$k][$inp] = $val; - } - - $this->setWeights(); - $this->setCriteria(); - } - - private function evaluateGET() : void - { - if (empty($_GET['filter'])) - return; - - // squash into usable format - $post = []; - foreach (explode(';', $_GET['filter']) as $f) - { - if (!strstr($f, '=')) - { - $this->error = true; - continue; - } - - $_ = explode('=', $f); - $post[$_[0]] = $_[1]; - } - - foreach ($this->inputFields as $inp => [$type, $valid, $asArray]) - { - if (!isset($post[$inp]) || $post[$inp] === '') - continue; - - $val = $post[$inp]; - $k = in_array($inp, ['cr', 'crs', 'crv']) ? 'c' : 'v'; - - if ($asArray) - { - $buff = []; - foreach (explode(':', $val) as $v) - if ($v !== '' && $this->checkInput($type, $valid, $v) && $v !== '') - $buff[] = $v; - - if ($buff) - { - if ($k == 'v') - $this->formData['form'][$inp] = $buff; - - $this->fiData[$k][$inp] = $buff; - } - } - else if ($val !== '' && $this->checkInput($type, $valid, $val) && $val !== '') - { - if ($k == 'v') - $this->formData['form'][$inp] = $val; - - $this->fiData[$k][$inp] = $val; - } - } - - $this->setWeights(); - $this->setCriteria(); - } - - private function setCriteria() : void // [cr]iterium, [cr].[s]ign, [cr].[v]alue - { - if (empty($this->fiData['c']['cr']) && empty($this->fiData['c']['crs']) && empty($this->fiData['c']['crv'])) - return; - else if (empty($this->fiData['c']['cr']) || empty($this->fiData['c']['crs']) || empty($this->fiData['c']['crv'])) - { - unset($this->fiData['c']['cr']); - unset($this->fiData['c']['crs']); - unset($this->fiData['c']['crv']); - - $this->error = true; - return; - } - - $_cr = &$this->fiData['c']['cr']; - $_crs = &$this->fiData['c']['crs']; - $_crv = &$this->fiData['c']['crv']; - - if (count($_cr) != count($_crv) || count($_cr) != count($_crs) || count($_cr) > 5 || count($_crs) > 5 /*|| count($_crv) > 5*/) - { - // use min provided criterion as basis; 5 criteria at most - $min = max(5, min(count($_cr), count($_crv), count($_crs))); - if (count($_cr) > $min) - array_splice($_cr, $min); - - if (count($_crv) > $min) - array_splice($_crv, $min); - - if (count($_crs) > $min) - array_splice($_crs, $min); - - $this->error = true; - } - - for ($i = 0; $i < count($_cr); $i++) - { - // conduct filter specific checks & casts here - $unsetme = false; - if (isset($this->genericFilter[$_cr[$i]])) - { - $gf = $this->genericFilter[$_cr[$i]]; - switch ($gf[0]) - { - case self::CR_NUMERIC: - $_ = $_crs[$i]; - if (!Util::checkNumeric($_crv[$i], $gf[2]) || !$this->int2Op($_)) - $unsetme = true; - break; - case self::CR_BOOLEAN: - case self::CR_FLAG: - $_ = $_crs[$i]; - if (!$this->int2Bool($_)) - $unsetme = true; - break; - case self::CR_ENUM: - case self::CR_STAFFFLAG: - if (!Util::checkNumeric($_crs[$i], NUM_CAST_INT)) - $unsetme = true; - break; - } - } - - if (!$unsetme && intval($_cr[$i]) && $_crs[$i] !== '' && $_crv[$i] !== '') - continue; - - unset($_cr[$i]); - unset($_crs[$i]); - unset($_crv[$i]); - - $this->error = true; - } - - $this->formData['setCriteria'] = array( - 'cr' => $_cr, - 'crs' => $_crs, - 'crv' => $_crv - ); - } - - private function setWeights() : void - { - // both empty: not in use - if (empty($this->fiData['v']['wt']) && empty($this->fiData['v']['wtv'])) - return; - - // one empty: erroneous manual input? - if (empty($this->fiData['v']['wt']) || empty($this->fiData['v']['wtv'])) - { - unset($this->fiData['v']['wt']); - unset($this->fiData['v']['wtv']); - - $this->error = true; - return; - } - - $_wt = &$this->fiData['v']['wt']; - $_wtv = &$this->fiData['v']['wtv']; - - $nwt = count($_wt); - $nwtv = count($_wtv); - - if ($nwt > $nwtv) - { - array_splice($_wt, $nwtv); - $this->error = true; - } - else if ($nwtv > $nwt) - { - array_splice($_wtv, $nwt); - $this->error = true; - } - - $this->formData['setWeights'] = [$_wt, $_wtv]; - } - - protected function checkInput(int $type, mixed $valid, mixed &$val, bool $recursive = false) : bool - { - switch ($type) - { - case self::V_EQUAL: - if (gettype($valid) == 'integer') - $val = intval($val); - else if (gettype($valid) == 'double') - $val = floatval($val); - else /* if (gettype($valid) == 'string') */ - $val = strval($val); - - if ($valid == $val) - return true; - - break; - case self::V_LIST: - if (!Util::checkNumeric($val, NUM_CAST_INT)) - return false; - - foreach ($valid as $k => $v) - { - if (gettype($v) != 'array') - continue; - - if ($this->checkInput(self::V_RANGE, $v, $val, true)) - return true; - - unset($valid[$k]); - } - - if (in_array($val, $valid)) - return true; - - break; - case self::V_RANGE: - if (Util::checkNumeric($val, NUM_CAST_INT) && $val >= $valid[0] && $val <= $valid[1]) - return true; - - break; - case self::V_CALLBACK: - if ($this->$valid($val)) - return true; - - break; - case self::V_REGEX: - if (!preg_match($valid, $val)) - return true; - - break; - } - - if (!$recursive) - $this->error = true; - - return false; - } - - protected function transformString(string $string, bool $exact) : string - { - // escape manually entered _; entering % should be prohibited - $string = str_replace('_', '\\_', $string); - - // now replace search wildcards with sql wildcards - $string = strtr($string, self::$wCards); - - return sprintf($exact ? '%s' : '%%%s%%', $string); - } - - protected function modularizeString(array $fields, string $string = '', bool $exact = false, bool $shortStr = false) : array - { - if (!$string && !empty($this->fiData['v']['na'])) - $string = $this->fiData['v']['na']; - - $qry = []; - foreach ($fields as $f) - { - $sub = []; - $parts = $exact ? [$string] : array_filter(explode(' ', $string)); - foreach ($parts as $p) - { - if ($p[0] == '-' && (mb_strlen($p) > 3 || $shortStr)) - $sub[] = [$f, $this->transformString(mb_substr($p, 1), $exact), '!']; - else if ($p[0] != '-' && (mb_strlen($p) > 2 || $shortStr)) - $sub[] = [$f, $this->transformString($p, $exact)]; - } - - // single cnd? - if (!$sub) - continue; - else if (count($sub) > 1) - array_unshift($sub, 'AND'); - else - $sub = $sub[0]; - - $qry[] = $sub; - } - - // single cnd? - if (!$qry) - $this->error = true; - else if (count($qry) > 1) - array_unshift($qry, 'OR'); - else - $qry = $qry[0]; - - return $qry; - } - - protected function int2Op(mixed &$op) : bool - { - switch ($op) - { - case 1: $op = '>'; return true; - case 2: $op = '>='; return true; - case 3: $op = '='; return true; - case 4: $op = '<='; return true; - case 5: $op = '<'; return true; - case 6: $op = '!='; return true; - default: return false; - } - } - - protected function int2Bool(mixed &$op) : bool - { - switch ($op) - { - case 1: $op = true; return true; - case 2: $op = false; return true; - default: return false; - } - } - - protected function list2Mask(array $list, bool $noOffset = false) : int - { - $mask = 0x0; - $o = $noOffset ? 0 : 1; // schoolMask requires this..? - - foreach ($list as $itm) - $mask += (1 << (intval($itm) - $o)); - - return $mask; - } - - - /**************************/ - /* create conditions from */ - /* generic criteria */ - /**************************/ - - private function genericBoolean($field, $op, bool $isString) : ?array - { - if ($this->int2Bool($op)) - { - $value = $isString ? '' : 0; - $operator = $op ? '!' : null; - - return [$field, $value, $operator]; - } - - return null; - } - - private function genericBooleanFlags($field, $value, $op, ?bool $matchAny = false) : ?array - { - if (!$this->int2Bool($op)) - return null; - - if (!$op) - return [[$field, $value, '&'], 0]; - else if ($matchAny) - return [[$field, $value, '&'], 0, '!']; - else - return [[$field, $value, '&'], $value]; - } - - private function genericString($field, $value, $strFlags) : ?array - { - if ($strFlags & STR_LOCALIZED) - $field .= '_loc'.Lang::getLocale()->value; - - return $this->modularizeString([$field], (string)$value, $strFlags & STR_MATCH_EXACT, $strFlags & STR_ALLOW_SHORT); - } - - private function genericNumeric($field, $value, $op, $typeCast) : ?array - { - if (!Util::checkNumeric($value, $typeCast)) - return null; - - if ($this->int2Op($op)) - return [$field, $value, $op]; - - return null; - } - - private function genericEnum($field, $value) : ?array - { - if (is_bool($value)) - return [$field, 0, ($value ? '>' : '<=')]; - else if ($value == self::ENUM_ANY) - return [$field, 0, '!']; - else if ($value == self::ENUM_NONE) - return [$field, 0]; - else if ($value !== null) - return [$field, $value]; - - return null; - } - - protected function genericCriterion(int $cr, int $crs, string $crv) : ?array - { - [$crType, $colOrFn, $param1, $param2] = array_pad($this->genericFilter[$cr], 4, null); - $result = null; - - switch ($crType) - { - case self::CR_NUMERIC: - $result = $this->genericNumeric($colOrFn, $crv, $crs, $param1); - break; - case self::CR_FLAG: - $result = $this->genericBooleanFlags($colOrFn, $param1, $crs, $param2); - break; - case self::CR_STAFFFLAG: - if (User::isInGroup(U_GROUP_EMPLOYEE) && $crs > 0) - $result = $this->genericBooleanFlags($colOrFn, (1 << ($crs - 1)), true); - break; - case self::CR_BOOLEAN: - $result = $this->genericBoolean($colOrFn, $crs, !empty($param1)); - break; - case self::CR_STRING: - $result = $this->genericString($colOrFn, $crv, $param1); - break; - case self::CR_ENUM: - if (!$param2 && isset($this->enums[$cr][$crs])) - $result = $this->genericEnum($colOrFn, $this->enums[$cr][$crs]); - if ($param2 && in_array($crs, $this->enums[$cr])) - $result = $this->genericEnum($colOrFn, $crs); - else if ($param1 && ($crs == self::ENUM_ANY || $crs == self::ENUM_NONE)) - $result = $this->genericEnum($colOrFn, $crs); - break; - case self::CR_CALLBACK: - $result = $this->{$colOrFn}($cr, $crs, $crv, $param1, $param2); - break; - case self::CR_NYI_PH: // do not limit with not implemented filters - if (is_int($param1)) - return [$param1]; - - // for nonsensical values; compare against 0 - if ($this->int2Op($crs) && Util::checkNumeric($crv)) - { - if ($crs == '=') - $crs = '=='; - - return eval('return ('.$crv.' '.$crs.' 0);') ? [1] : [0]; - } - else - return [0]; - } - - if ($result && $crType == self::CR_NUMERIC && !empty($param2)) - $this->formData['extraCols'][] = $cr; - - return $result; - } - - - /***********************************/ - /* create conditions from */ - /* non-generic values and criteria */ - /***********************************/ - - protected function createSQLForCriterium(int &$cr, int &$crs, string &$crv) : array - { - if (!$this->genericFilter) // criteria not in use - no error - return []; - - if (in_array($cr, array_keys($this->genericFilter))) - if ($genCr = $this->genericCriterion($cr, $crs, $crv)) - return $genCr; - - $this->error = true; - trigger_error('Filter::createSQLForCriterium - received unhandled criterium: ["'.$cr.'", "'.$crs.'", "'.$crv.'"]', E_USER_WARNING); - - unset($cr, $crs, $crv); - - return []; - } - - abstract protected function createSQLForValues(); -} - ?> diff --git a/includes/types/creature.class.php b/includes/types/creature.class.php index fb6e0af9..83179d9f 100644 --- a/includes/types/creature.class.php +++ b/includes/types/creature.class.php @@ -282,8 +282,8 @@ class CreatureList extends BaseType class CreatureListFilter extends Filter { - public $extraOpts = null; - protected $enums = array( + protected string $type = 'npcs'; + protected array $enums = array( 3 => parent::ENUM_FACTION, // faction 6 => parent::ENUM_ZONE, // foundin 42 => parent::ENUM_FACTION, // increasesrepwith @@ -291,46 +291,46 @@ class CreatureListFilter extends Filter 38 => parent::ENUM_EVENT // relatedevent ); - protected $genericFilter = array( - 1 => [parent::CR_CALLBACK, 'cbHealthMana', 'healthMax', 'healthMin'], // health [num] - 2 => [parent::CR_CALLBACK, 'cbHealthMana', 'manaMin', 'manaMax' ], // mana [num] - 3 => [parent::CR_CALLBACK, 'cbFaction', null, null ], // faction [enum] - 5 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_REPAIRER ], // canrepair - 6 => [parent::CR_ENUM, 's.areaId', false, true ], // foundin - 7 => [parent::CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [enum] - 8 => [parent::CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [enum] - 9 => [parent::CR_BOOLEAN, 'lootId', ], // lootable - 10 => [parent::CR_CALLBACK, 'cbRegularSkinLoot', NPC_TYPEFLAG_SPECIALLOOT ], // skinnable [yn] - 11 => [parent::CR_BOOLEAN, 'pickpocketLootId', ], // pickpocketable - 12 => [parent::CR_CALLBACK, 'cbMoneyDrop', null, null ], // averagemoneydropped [op] [int] - 15 => [parent::CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_HERBLOOT, null ], // gatherable [yn] - 16 => [parent::CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_MININGLOOT, null ], // minable [yn] - 18 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_AUCTIONEER ], // auctioneer - 19 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker - 20 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_BATTLEMASTER ], // battlemaster - 21 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_FLIGHT_MASTER ], // flightmaster - 22 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // guildmaster - 23 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_INNKEEPER ], // innkeeper - 24 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_CLASS_TRAINER ], // talentunlearner - 25 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // tabardvendor - 27 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_STABLE_MASTER ], // stablemaster - 28 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_TRAINER ], // trainer - 29 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_VENDOR ], // vendor - 31 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots - 32 => [parent::CR_FLAG, 'cuFlags', NPC_CU_INSTANCE_BOSS ], // instanceboss - 33 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments - 34 => [parent::CR_STRING, 'modelId', STR_MATCH_EXACT | STR_ALLOW_SHORT ], // usemodel [str] (wants int in string fmt <_<) - 35 => [parent::CR_STRING, 'textureString' ], // useskin [str] - 37 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true ], // id - 38 => [parent::CR_CALLBACK, 'cbRelEvent', null, null ], // relatedevent [enum] - 40 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos - 41 => [parent::CR_NYI_PH, 1, null ], // haslocation [yn] [staff] - 42 => [parent::CR_CALLBACK, 'cbReputation', '>', null ], // increasesrepwith [enum] - 43 => [parent::CR_CALLBACK, 'cbReputation', '<', null ], // decreasesrepwith [enum] - 44 => [parent::CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_ENGINEERLOOT, null ] // salvageable [yn] + protected array $genericFilter = array( + 1 => [parent::CR_CALLBACK, 'cbHealthMana', 'healthMax', 'healthMin'], // health [num] + 2 => [parent::CR_CALLBACK, 'cbHealthMana', 'manaMin', 'manaMax' ], // mana [num] + 3 => [parent::CR_CALLBACK, 'cbFaction', null, null ], // faction [enum] + 5 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_REPAIRER ], // canrepair + 6 => [parent::CR_ENUM, 's.areaId', false, true ], // foundin + 7 => [parent::CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [enum] + 8 => [parent::CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [enum] + 9 => [parent::CR_BOOLEAN, 'lootId', ], // lootable + 10 => [parent::CR_CALLBACK, 'cbRegularSkinLoot', NPC_TYPEFLAG_SPECIALLOOT ], // skinnable [yn] + 11 => [parent::CR_BOOLEAN, 'pickpocketLootId', ], // pickpocketable + 12 => [parent::CR_CALLBACK, 'cbMoneyDrop', null, null ], // averagemoneydropped [op] [int] + 15 => [parent::CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_SKIN_WITH_HERBALISM, null ], // gatherable [yn] + 16 => [parent::CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_SKIN_WITH_MINING, null ], // minable [yn] + 18 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_AUCTIONEER ], // auctioneer + 19 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker + 20 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_BATTLEMASTER ], // battlemaster + 21 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_FLIGHT_MASTER ], // flightmaster + 22 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // guildmaster + 23 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_INNKEEPER ], // innkeeper + 24 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_CLASS_TRAINER ], // talentunlearner + 25 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // tabardvendor + 27 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_STABLE_MASTER ], // stablemaster + 28 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_TRAINER ], // trainer + 29 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_VENDOR ], // vendor + 31 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots + 32 => [parent::CR_FLAG, 'cuFlags', NPC_CU_INSTANCE_BOSS ], // instanceboss + 33 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments + 34 => [parent::CR_STRING, 'modelId', STR_MATCH_EXACT | STR_ALLOW_SHORT ], // usemodel [str] (wants int in string fmt <_<) + 35 => [parent::CR_STRING, 'textureString' ], // useskin [str] + 37 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true ], // id + 38 => [parent::CR_CALLBACK, 'cbRelEvent', null, null ], // relatedevent [enum] + 40 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 41 => [parent::CR_NYI_PH, 1, null ], // haslocation [yn] [staff] + 42 => [parent::CR_CALLBACK, 'cbReputation', '>', null ], // increasesrepwith [enum] + 43 => [parent::CR_CALLBACK, 'cbReputation', '<', null ], // decreasesrepwith [enum] + 44 => [parent::CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_SKIN_WITH_ENGINEERING, null ] // salvageable [yn] ); - protected $inputFields = array( + protected array $inputFields = array( 'cr' => [parent::V_LIST, [[1, 3],[5, 12], 15, 16, [18, 25], [27, 29], [31, 35], 37, 38, [40, 44]], true ], // criteria ids 'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 9999]], true ], // criteria operators 'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiter @@ -345,46 +345,48 @@ class CreatureListFilter extends Filter 'rh' => [parent::V_LIST, [-1, 0, 1], false] // react horde [int] ); - protected function createSQLForValues() + public array $extraOpts = []; + + protected function createSQLForValues() : array { $parts = []; - $_v = &$this->fiData['v']; + $_v = &$this->values; // name [str] - if (isset($_v['na'])) + if ($_v['na']) { $_ = []; - if (isset($_v['ex']) && $_v['ex'] == 'on') - $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value, 'subname_loc'.Lang::getLocale()->value]); + if ($_v['ex'] == 'on') + $_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value, 'subname_loc'.Lang::getLocale()->value]); else - $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value]); + $_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]); if ($_) $parts[] = $_; } // pet family [list] - if (isset($_v['fa'])) + if ($_v['fa']) $parts[] = ['family', $_v['fa']]; // creatureLevel min [int] - if (isset($_v['minle'])) + if ($_v['minle']) $parts[] = ['minLevel', $_v['minle'], '>=']; // creatureLevel max [int] - if (isset($_v['maxle'])) + if ($_v['maxle']) $parts[] = ['maxLevel', $_v['maxle'], '<=']; // classification [list] - if (isset($_v['cl'])) + if ($_v['cl']) $parts[] = ['rank', $_v['cl']]; // react Alliance [int] - if (isset($_v['ra'])) + if ($_v['ra']) $parts[] = ['ft.A', $_v['ra']]; // react Horde [int] - if (isset($_v['rh'])) + if ($_v['rh']) $parts[] = ['ft.H', $_v['rh']]; return $parts; @@ -514,7 +516,7 @@ class CreatureListFilter extends Filter return null; if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE `id` = ?d', $crs)) - $this->formData['reputationCols'][] = [$crs, Util::localizedString($_, 'name')]; + $this->fiReputationCols[] = [$crs, Util::localizedString($_, 'name')]; if ($cIds = DB::World()->selectCol('SELECT `creature_id` FROM creature_onkill_reputation WHERE (`RewOnKillRepFaction1` = ?d AND `RewOnKillRepValue1` '.$op.' 0) OR (`RewOnKillRepFaction2` = ?d AND `RewOnKillRepValue2` '.$op.' 0)', $crs, $crs)) return ['id', $cIds]; diff --git a/includes/types/enchantment.class.php b/includes/types/enchantment.class.php index e756b551..0a3900bf 100644 --- a/includes/types/enchantment.class.php +++ b/includes/types/enchantment.class.php @@ -168,11 +168,12 @@ class EnchantmentList extends BaseType class EnchantmentListFilter extends Filter { - protected $enums = array( + protected string $type = 'enchantments'; + protected array $enums = array( 3 => parent::ENUM_PROFESSION // requiresprof ); - protected $genericFilter = array( + protected array $genericFilter = array( 2 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true], // id 3 => [parent::CR_ENUM, 'skillLine' ], // requiresprof 4 => [parent::CR_NUMERIC, 'skillLevel', NUM_CAST_INT ], // reqskillrank @@ -234,7 +235,7 @@ class EnchantmentListFilter extends Filter 123 => [parent::CR_NUMERIC, 'is.splpwr', NUM_CAST_INT, true] // splpwr ); - protected $inputFields = array( + protected array $inputFields = array( 'cr' => [parent::V_RANGE, [2, 123], true ], // criteria ids 'crs' => [parent::V_RANGE, [1, 15], true ], // criteria operators 'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - only numerals @@ -243,18 +244,18 @@ class EnchantmentListFilter extends Filter 'ty' => [parent::V_RANGE, [1, 8], true ] // types ); - protected function createSQLForValues() + protected function createSQLForValues() : array { $parts = []; - $_v = &$this->fiData['v']; + $_v = &$this->values; //string - if (isset($_v['na'])) - if ($_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value])) + if ($_v['na']) + if ($_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value])) $parts[] = $_; // type - if (isset($_v['ty'])) + if ($_v['ty']) $parts[] = ['OR', ['type1', $_v['ty']], ['type2', $_v['ty']], ['type3', $_v['ty']]]; return $parts; diff --git a/includes/types/gameobject.class.php b/includes/types/gameobject.class.php index 7db24f7f..b418ce2b 100644 --- a/includes/types/gameobject.class.php +++ b/includes/types/gameobject.class.php @@ -145,14 +145,14 @@ class GameObjectList extends BaseType class GameObjectListFilter extends Filter { - public $extraOpts = []; - protected $enums = array( + protected string $type = 'objects'; + protected array $enums = array( 1 => parent::ENUM_ZONE, 16 => parent::ENUM_EVENT, 50 => [1, 2, 3, 4, 663, 883] ); - protected $genericFilter = array( + protected array $genericFilter = array( 1 => [parent::CR_ENUM, 's.areaId', false, true], // foundin 2 => [parent::CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [side] 3 => [parent::CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [side] @@ -167,7 +167,7 @@ class GameObjectListFilter extends Filter 50 => [parent::CR_ENUM, 'spellFocusId', true, true], // spellfocus ); - protected $inputFields = array( + protected array $inputFields = array( 'cr' => [parent::V_LIST, [[1, 5], 7, 11, 13, 15, 16, 18, 50], true ], // criteria ids 'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 5000]], true ], // criteria operators 'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - only numeric input values expected @@ -175,14 +175,16 @@ class GameObjectListFilter extends Filter 'ma' => [parent::V_EQUAL, 1, false] // match any / all filter ); - protected function createSQLForValues() + public array $extraOpts = []; + + protected function createSQLForValues() : array { $parts = []; - $_v = $this->fiData['v']; + $_v = $this->values; // name - if (isset($_v['na'])) - if ($_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value])) + if ($_v['na']) + if ($_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value])) $parts[] = $_; return $parts; diff --git a/includes/types/guild.class.php b/includes/types/guild.class.php index 4c95443e..9f1f2757 100644 --- a/includes/types/guild.class.php +++ b/includes/types/guild.class.php @@ -91,10 +91,9 @@ class GuildListFilter extends Filter { use TrProfilerFilter; - public $extraOpts = []; - protected $genericFilter = []; - - protected $inputFields = array( + protected string $type = 'guilds'; + protected array $genericFilter = []; + protected array $inputFields = array( 'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter 'ma' => [parent::V_EQUAL, 1, false], // match any / all filter 'ex' => [parent::V_EQUAL, 'on', false], // only match exact @@ -103,26 +102,25 @@ class GuildListFilter extends Filter 'sv' => [parent::V_CALLBACK, 'cbServerCheck', false], // server ); - protected function createSQLForValues() + public array $extraOpts = []; + + protected function createSQLForValues() : array { $parts = []; - $_v = $this->fiData['v']; + $_v = $this->values; // region (rg), battlegroup (bg) and server (sv) are passed to GuildList as miscData and handled there // name [str] - if (!empty($_v['na'])) - if ($_ = $this->modularizeString(['g.name'], $_v['na'], !empty($_v['ex']) && $_v['ex'] == 'on')) + if ($_v['na']) + if ($_ = $this->tokenizeString(['g.name'], $_v['na'], $_v['ex'] == 'on')) $parts[] = $_; // side [list] - if (!empty($_v['si'])) - { - if ($_v['si'] == SIDE_ALLIANCE) - $parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_ALLIANCE)]; - else if ($_v['si'] == SIDE_HORDE) - $parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_HORDE)]; - } + if ($_v['si'] == SIDE_ALLIANCE) + $parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_ALLIANCE)]; + else if ($_v['si'] == SIDE_HORDE) + $parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_HORDE)]; return $parts; } diff --git a/includes/types/icon.class.php b/includes/types/icon.class.php index 26514105..5933dccb 100644 --- a/includes/types/icon.class.php +++ b/includes/types/icon.class.php @@ -103,9 +103,8 @@ class IconList extends BaseType class IconListFilter extends Filter { - public $extraOpts = null; - - private $criterion2field = array( + private array $totalUses = []; + private array $criterion2field = array( 1 => '?_items', // items [num] 2 => '?_spell', // spells [num] 3 => '?_achievement', // achievements [num] @@ -119,9 +118,9 @@ class IconListFilter extends Filter 11 => '', // classes [num] 13 => '' // used [num] ); - private $totalUses = []; - protected $genericFilter = array( + protected string $type = 'icons'; + protected array $genericFilter = array( 1 => [parent::CR_CALLBACK, 'cbUseAny' ], // items [num] 2 => [parent::CR_CALLBACK, 'cbUseAny' ], // spells [num] 3 => [parent::CR_CALLBACK, 'cbUseAny' ], // achievements [num] @@ -131,7 +130,7 @@ class IconListFilter extends Filter 13 => [parent::CR_CALLBACK, 'cbUseAll' ] // used [num] ); - protected $inputFields = array( + protected array $inputFields = array( 'cr' => [parent::V_LIST, [1, 2, 3, 6, 9, 11, 13], true ], // criteria ids 'crs' => [parent::V_RANGE, [1, 6], true ], // criteria operators 'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - all criteria are numeric here @@ -139,6 +138,8 @@ class IconListFilter extends Filter 'ma' => [parent::V_EQUAL, 1, false] // match any / all filter ); + public array $extraOpts = []; + private function _getCnd(string $op, int $val, string $tbl) : ?array { switch ($op) @@ -168,14 +169,14 @@ class IconListFilter extends Filter return $ids ? ['id', array_keys($ids), '!'] : [1]; } - protected function createSQLForValues() + protected function createSQLForValues() : array { $parts = []; - $_v = &$this->fiData['v']; + $_v = &$this->values; //string - if (isset($_v['na'])) - if ($_ = $this->modularizeString(['name'])) + if ($_v['na']) + if ($_ = $this->tokenizeString(['name'])) $parts[] = $_; return $parts; diff --git a/includes/types/item.class.php b/includes/types/item.class.php index 07e762d3..043001ce 100644 --- a/includes/types/item.class.php +++ b/includes/types/item.class.php @@ -1777,13 +1777,12 @@ class ItemList extends BaseType class ItemListFilter extends Filter { - private $ubFilter = []; // usable-by - limit weapon/armor selection per CharClass - itemClass => available itemsubclasses - private $extCostQuery = 'SELECT `item` FROM npc_vendor WHERE `extendedCost` IN (?a) UNION - SELECT `item` FROM game_event_npc_vendor WHERE `extendedCost` IN (?a)'; + private array $ubFilter = []; // usable-by - limit weapon/armor selection per CharClass - itemClass => available itemsubclasses + private string $extCostQuery = 'SELECT `item` FROM npc_vendor WHERE `extendedCost` IN (?a) UNION + SELECT `item` FROM game_event_npc_vendor WHERE `extendedCost` IN (?a)'; - public $extraOpts = []; // score for statWeights - public $wtCnd = []; - protected $enums = array( + protected string $type = 'items'; + protected array $enums = array( 16 => parent::ENUM_ZONE, // drops in zone 17 => parent::ENUM_FACTION, // requiresrepwith 99 => parent::ENUM_PROFESSION, // requiresprof @@ -1851,7 +1850,7 @@ class ItemListFilter extends Filter ) ); - protected $genericFilter = array( + protected array $genericFilter = array( 2 => [parent::CR_CALLBACK, 'cbFieldHasVal', 'bonding', 1 ], // bindonpickup [yn] 3 => [parent::CR_CALLBACK, 'cbFieldHasVal', 'bonding', 2 ], // bindonequip [yn] 4 => [parent::CR_CALLBACK, 'cbFieldHasVal', 'bonding', 3 ], // bindonuse [yn] @@ -2010,64 +2009,47 @@ class ItemListFilter extends Filter 177 => [parent::CR_STAFFFLAG, 'flagsExtra' ], // flags2 ); - protected $inputFields = array( - 'wt' => [parent::V_CALLBACK, 'cbWeightKeyCheck', true ], // weight keys - 'wtv' => [parent::V_RANGE, [1, 999], true ], // weight values - 'jc' => [parent::V_LIST, [1], false], // use jewelcrafter gems for weight calculation - 'gm' => [parent::V_LIST, [2, 3, 4], false], // gem rarity for weight calculation - 'cr' => [parent::V_RANGE, [1, 177], true ], // criteria ids - 'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 99999]], true ], // criteria operators - 'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiters - 'upg' => [parent::V_REGEX, '/[^\d:]/ui', false], // upgrade item ids - 'gb' => [parent::V_LIST, [0, 1, 2, 3], false], // search result grouping - 'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter - 'ma' => [parent::V_EQUAL, 1, false], // match any / all filter - 'ub' => [parent::V_LIST, [[1, 9], 11], false], // usable by classId - 'qu' => [parent::V_RANGE, [0, 7], true ], // quality ids - 'ty' => [parent::V_CALLBACK, 'cbTypeCheck', true ], // item type - dynamic by current group - 'sl' => [parent::V_CALLBACK, 'cbSlotCheck', true ], // item slot - dynamic by current group - 'si' => [parent::V_LIST, [1, 2, 3, -1, -2], false], // side - 'minle' => [parent::V_RANGE, [1, 999], false], // item level min - 'maxle' => [parent::V_RANGE, [1, 999], false], // item level max - 'minrl' => [parent::V_RANGE, [1, MAX_LEVEL], false], // required level min - 'maxrl' => [parent::V_RANGE, [1, MAX_LEVEL], false] // required level max + protected array $inputFields = array( + 'wt' => [parent::V_CALLBACK, 'cbWeightKeyCheck', true ], // weight keys + 'wtv' => [parent::V_RANGE, [1, 999], true ], // weight values + 'jc' => [parent::V_LIST, [1], false], // use jewelcrafter gems for weight calculation + 'gm' => [parent::V_LIST, [2, 3, 4], false], // gem rarity for weight calculation + 'cr' => [parent::V_RANGE, [1, 177], true ], // criteria ids + 'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 99999]], true ], // criteria operators + 'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiters + 'upg' => [parent::V_REGEX, '/[^\d:]/ui', true ], // upgrade item ids + 'gb' => [parent::V_LIST, [0, 1, 2, 3], false], // search result grouping + 'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter + 'ma' => [parent::V_EQUAL, 1, false], // match any / all filter + 'ub' => [parent::V_LIST, [[1, 9], 11], false], // usable by classId + 'qu' => [parent::V_RANGE, [0, 7], true ], // quality ids + 'ty' => [parent::V_CALLBACK, 'cbTypeCheck', true ], // item type - dynamic by current group + 'sl' => [parent::V_CALLBACK, 'cbSlotCheck', true ], // item slot - dynamic by current group + 'si' => [parent::V_LIST, [-SIDE_HORDE, -SIDE_ALLIANCE, SIDE_ALLIANCE, SIDE_HORDE, SIDE_BOTH], false], // side + 'minle' => [parent::V_RANGE, [1, 999], false], // item level min + 'maxle' => [parent::V_RANGE, [1, 999], false], // item level max + 'minrl' => [parent::V_RANGE, [1, MAX_LEVEL], false], // required level min + 'maxrl' => [parent::V_RANGE, [1, MAX_LEVEL], false] // required level max ); - public function __construct($fromPOST = false, $opts = []) - { - $classes = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `weaponTypeMask` AS "0", `armorTypeMask` AS "1" FROM ?_classes'); - foreach ($classes as $cId => [$weaponTypeMask, $armorTypeMask]) - { - // preselect misc subclasses - $this->ubFilter[$cId] = [ITEM_CLASS_WEAPON => [ITEM_SUBCLASS_MISC_WEAPON], ITEM_CLASS_ARMOR => [ITEM_SUBCLASS_MISC_ARMOR]]; - - for ($i = 0; $i < 21; $i++) - if ($weaponTypeMask & (1 << $i)) - $this->ubFilter[$cId][ITEM_CLASS_WEAPON][] = $i; - - for ($i = 0; $i < 11; $i++) - if ($armorTypeMask & (1 << $i)) - $this->ubFilter[$cId][ITEM_CLASS_ARMOR][] = $i; - } - - parent::__construct($fromPOST, $opts); - } + public array $extraOpts = []; // score for statWeights + public array $wtCnd = []; public function createConditionsForWeights() : array { - if (empty($this->fiData['v']['wt'])) + if (empty($this->values['wt'])) return []; $this->wtCnd = []; $select = []; $wtSum = 0; - foreach ($this->fiData['v']['wt'] as $k => $v) + foreach ($this->values['wt'] as $k => $v) { if ($idx = Stat::getIndexFrom(Stat::IDX_FILTER_CR_ID, $v)) { $str = Stat::getJsonString($idx); - $qty = intVal($this->fiData['v']['wtv'][$k]); + $qty = intVal($this->values['wtv'][$k]); $select[] = '(IFNULL(`is`.`'.$str.'`, 0) * '.$qty.')'; $this->wtCnd[] = ['is.'.$str, 0, '>']; @@ -2082,28 +2064,51 @@ class ItemListFilter extends Filter if ($select) { - $this->extraOpts['is']['s'][] = ', IF(is.typeId IS NULL, 0, ('.implode(' + ', $select).') / '.$wtSum.') AS score'; + $this->extraOpts['is']['s'][] = ', IF(`is`.`typeId` IS NULL, 0, ('.implode(' + ', $select).') / '.$wtSum.') AS "score"'; $this->extraOpts['is']['o'][] = 'score DESC'; $this->extraOpts['i']['o'][] = null; // remove default ordering } else - $this->extraOpts['is']['s'][] = ', 0 AS score'; // prevent errors + $this->extraOpts['is']['s'][] = ', 0 AS "score"'; // prevent errors return $this->wtCnd; } - public function isCurrencyFor(int $itemId) : bool + public static function isCurrencyFor(int $itemId) : bool { return in_array($itemId, self::ENUM_CURRENCY); } + public function getConditions() : array + { + if (!$this->ubFilter) + { + $classes = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `weaponTypeMask` AS "0", `armorTypeMask` AS "1" FROM ?_classes'); + foreach ($classes as $cId => [$weaponTypeMask, $armorTypeMask]) + { + // preselect misc subclasses + $this->ubFilter[$cId] = [ITEM_CLASS_WEAPON => [ITEM_SUBCLASS_MISC_WEAPON], ITEM_CLASS_ARMOR => [ITEM_SUBCLASS_MISC_ARMOR]]; + + for ($i = 0; $i < 21; $i++) + if ($weaponTypeMask & (1 << $i)) + $this->ubFilter[$cId][ITEM_CLASS_WEAPON][] = $i; + + for ($i = 0; $i < 11; $i++) + if ($armorTypeMask & (1 << $i)) + $this->ubFilter[$cId][ITEM_CLASS_ARMOR][] = $i; + } + } + + return parent::getConditions(); + } + protected function createSQLForValues() : array { $parts = []; - $_v = $this->fiData['v']; + $_v = $this->values; // weights - if (!empty($_v['wt']) && !empty($_v['wtv'])) + if ($_v['wt'] && $_v['wtv']) { // gm - gem quality (qualityId) // jc - jc-gems included (bool) @@ -2112,37 +2117,25 @@ class ItemListFilter extends Filter $parts[] = $_; foreach ($_v['wt'] as $_) - $this->formData['extraCols'][] = $_; + $this->fiExtraCols[] = $_; } // upgrade for [form only] - if (isset($_v['upg'])) + if ($_v['upg']) { - $_ = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `slot` FROM ?_items WHERE `class` IN (?a) AND `id` IN (?a)', [ITEM_CLASS_WEAPON, ITEM_CLASS_GEM, ITEM_CLASS_ARMOR], explode(':', $_v['upg'])); - if ($_ === null) - { - unset($_v['upg']); - unset($this->formData['form']['upg']); - } + if ($this->upgrades = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `slot` FROM ?_items WHERE `class` IN (?a) AND `id` IN (?a)', [ITEM_CLASS_WEAPON, ITEM_CLASS_GEM, ITEM_CLASS_ARMOR], $_v['upg'])) + $parts[] = ['slot', $this->upgrades]; else - { - $this->formData['form']['upg'] = $_; - if ($_) - $parts[] = ['slot', $_]; - } + $_v['upg'] = null; } - // group by [form only] - if (isset($_v['gb'])) - $this->formData['form']['gb'] = $_v['gb']; - // name - if (isset($_v['na'])) - if ($_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value])) + if ($_v['na']) + if ($_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value])) $parts[] = $_; // usable-by (not excluded by requiredClass && armor or weapons match mask from ?_classes) - if (isset($_v['ub'])) + if ($_v['ub']) { $parts[] = array( 'AND', @@ -2157,57 +2150,48 @@ class ItemListFilter extends Filter } // quality [list] - if (isset($_v['qu'])) + if ($_v['qu'] !== null) $parts[] = ['quality', $_v['qu']]; // type - if (isset($_v['ty'])) + if ($_v['ty'] !== null) $parts[] = ['subclass', $_v['ty']]; // slot - if (isset($_v['sl'])) + if ($_v['sl']) $parts[] = ['slot', $_v['sl']]; // side - if (isset($_v['si'])) + if ($_v['si']) { - $ex = [['requiredRace', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL, '!']; - $notEx = ['OR', ['requiredRace', 0], [['requiredRace', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL]]; + $excl = [['requiredRace', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL, '!']; + $incl = ['OR', ['requiredRace', 0], [['requiredRace', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL]]; - switch ($_v['si']) + // we sanitized v['si'] earlier .. right? + $parts[] = match ($_v['si']) { - case SIDE_BOTH: - $parts[] = ['OR', [['flagsExtra', 0x3, '&'], [0, 3]], ['requiredRace', ChrRace::MASK_ALL], ['requiredRace', 0]]; - break; - case SIDE_HORDE: - $parts[] = ['AND', [['flagsExtra', 0x3, '&'], [0, 1]], ['OR', $notEx, ['requiredRace', ChrRace::MASK_HORDE, '&']]]; - break; - case -SIDE_HORDE: - $parts[] = ['OR', [['flagsExtra', 0x3, '&'], 1], ['AND', $ex, ['requiredRace', ChrRace::MASK_HORDE, '&']]]; - break; - case SIDE_ALLIANCE: - $parts[] = ['AND', [['flagsExtra', 0x3, '&'], [0, 2]], ['OR', $notEx, ['requiredRace', ChrRace::MASK_ALLIANCE, '&']]]; - break; - case -SIDE_ALLIANCE: - $parts[] = ['OR', [['flagsExtra', 0x3, '&'], 2], ['AND', $ex, ['requiredRace', ChrRace::MASK_ALLIANCE, '&']]]; - break; - } + SIDE_BOTH => ['OR', [['flagsExtra', 0x3, '&'], [0, 3]], ['requiredRace', ChrRace::MASK_ALL], ['requiredRace', 0]], + SIDE_HORDE => ['AND', [['flagsExtra', 0x3, '&'], [0, 1]], ['OR', $incl, ['requiredRace', ChrRace::MASK_HORDE, '&']]], + -SIDE_HORDE => ['OR', [['flagsExtra', 0x3, '&'], 1], ['AND', $excl, ['requiredRace', ChrRace::MASK_HORDE, '&']]], + SIDE_ALLIANCE => ['AND', [['flagsExtra', 0x3, '&'], [0, 2]], ['OR', $incl, ['requiredRace', ChrRace::MASK_ALLIANCE, '&']]], + -SIDE_ALLIANCE => ['OR', [['flagsExtra', 0x3, '&'], 2], ['AND', $excl, ['requiredRace', ChrRace::MASK_ALLIANCE, '&']]], + }; } // itemLevel min - if (isset($_v['minle'])) + if ($_v['minle']) $parts[] = ['itemLevel', $_v['minle'], '>=']; // itemLevel max - if (isset($_v['maxle'])) + if ($_v['maxle']) $parts[] = ['itemLevel', $_v['maxle'], '<=']; // reqLevel min - if (isset($_v['minrl'])) + if ($_v['minrl']) $parts[] = ['requiredLevel', $_v['minrl'], '>=']; // reqLevel max - if (isset($_v['maxrl'])) + if ($_v['maxrl']) $parts[] = ['requiredLevel', $_v['maxrl'], '<=']; return $parts; @@ -2271,7 +2255,7 @@ class ItemListFilter extends Filter protected function cbHasRandEnchant(int $cr, int $crs, string $crv) : ?array { $n = preg_replace(parent::PATTERN_NAME, '', $crv); - $n = $this->transformString($n, false); + $n = $this->transformToken($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 ?', 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')) : []; @@ -2303,7 +2287,7 @@ class ItemListFilter extends Filter if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs)) return null; - $this->formData['extraCols'][] = $cr; + $this->fiExtraCols[] = $cr; $items = [0]; if ($costs = DB::Aowow()->selectCol('SELECT `id` FROM ?_itemextendedcost WHERE `reqPersonalrating` '.$crs.' '.$crv)) @@ -2339,7 +2323,7 @@ class ItemListFilter extends Filter if (!Util::checkNumeric($crv, NUM_CAST_FLOAT) || !$this->int2Op($crs)) return null; - $this->formData['extraCols'][] = $cr; + $this->fiExtraCols[] = $cr; return ['AND', ['armordamagemodifier', $crv, $crs], ['class', ITEM_CLASS_ARMOR]]; } @@ -2441,7 +2425,7 @@ class ItemListFilter extends Filter if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs)) return null; - $this->formData['extraCols'][] = $cr; + $this->fiExtraCols[] = $cr; return ['AND', ['flags', ITEM_FLAG_OPENABLE, '&'], ['((minMoneyLoot + maxMoneyLoot) / 2)', $crv, $crs]]; } @@ -2452,8 +2436,8 @@ class ItemListFilter extends Filter $crv *= 1000; // field supplied in milliseconds - $this->formData['extraCols'][] = $cr; - $this->extraOpts['is']['s'][] = ', GREATEST(`spellCooldown1`, `spellCooldown2`, `spellCooldown3`, `spellCooldown4`, `spellCooldown5`) AS "cooldown"'; + $this->fiExtraCols[] = $cr; + $this->extraOpts['is']['s'][] = ', GREATEST(`spellCooldown1`, `spellCooldown2`, `spellCooldown3`, `spellCooldown4`, `spellCooldown5`) AS "cooldown"'; return [ 'OR', @@ -2534,25 +2518,14 @@ class ItemListFilter extends Filter protected function cbObjectiveOfQuest(int $cr, int $crs, string $crv) : ?array { - $w = ''; - switch ($crs) + $w = match ($crs) { - case 1: // Yes - case 5: // No - $w = 1; - break; - case 2: // Alliance - $w = '`reqRaceMask` & '.ChrRace::MASK_ALLIANCE.' AND (`reqRaceMask` & '.ChrRace::MASK_HORDE.') = 0'; - break; - case 3: // Horde - $w = '`reqRaceMask` & '.ChrRace::MASK_HORDE.' AND (`reqRaceMask` & '.ChrRace::MASK_ALLIANCE.') = 0'; - break; - case 4: // Both - $w = '(`reqRaceMask` & '.ChrRace::MASK_ALLIANCE.' AND `reqRaceMask` & '.ChrRace::MASK_HORDE.') OR `reqRaceMask` = 0'; - break; - default: - return null; - } + 1, 5 => 1, // Yes / No + 2 => '`reqRaceMask` & '.ChrRace::MASK_ALLIANCE.' AND (`reqRaceMask` & '.ChrRace::MASK_HORDE.') = 0', // Alliance + 3 => '`reqRaceMask` & '.ChrRace::MASK_HORDE.' AND (`reqRaceMask` & '.ChrRace::MASK_ALLIANCE.') = 0', // Horde + 4 => '(`reqRaceMask` & '.ChrRace::MASK_ALLIANCE.' AND `reqRaceMask` & '.ChrRace::MASK_HORDE.') OR `reqRaceMask` = 0', // Both + default => null + }; $itemIds = DB::Aowow()->selectCol(sprintf( 'SELECT `reqItemId1` FROM ?_quests WHERE %1$s UNION SELECT `reqItemId2` FROM ?_quests WHERE %1$s UNION @@ -2577,8 +2550,8 @@ class ItemListFilter extends Filter return null; $ids = []; - $spells = DB::Aowow()->select( // todo (med): hmm, selecting all using SpellList would exhaust 128MB of memory :x .. see, that we only select the fields that are really needed - 'SELECT `reagent1`, `reagent2`, `reagent3`, `reagent4`, `reagent5`, `reagent6`, `reagent7`, `reagent8`, + $spells = DB::Aowow()->select( // todo (med): hmm, selecting all using SpellList would exhaust 128MB of memory :x .. see, that we only select the fields that are really needed + 'SELECT `reagent1`, `reagent2`, `reagent3`, `reagent4`, `reagent5`, `reagent6`, `reagent7`, `reagent8`, `reagentCount1`, `reagentCount2`, `reagentCount3`, `reagentCount4`, `reagentCount5`, `reagentCount6`, `reagentCount7`, `reagentCount8` FROM ?_spell WHERE `skillLine1` IN (?a)', diff --git a/includes/types/itemset.class.php b/includes/types/itemset.class.php index e2a1eaba..32727ec8 100644 --- a/includes/types/itemset.class.php +++ b/includes/types/itemset.class.php @@ -167,11 +167,12 @@ class ItemsetList extends BaseType // missing filter: "Available to Players" class ItemsetListFilter extends Filter { - protected $enums = array( + protected string $type = 'itemsets'; + protected array $enums = array( 6 => parent::ENUM_EVENT ); - protected $genericFilter = array( + protected array $genericFilter = array( 2 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true], // id 3 => [parent::CR_NUMERIC, 'npieces', NUM_CAST_INT ], // pieces 4 => [parent::CR_STRING, 'bonusText', STR_LOCALIZED ], // bonustext @@ -183,7 +184,7 @@ class ItemsetListFilter extends Filter 12 => [parent::CR_CALLBACK, 'cbAvaliable', ] // available to players [yn] ); - protected $inputFields = array( + protected array $inputFields = array( 'cr' => [parent::V_RANGE, [2, 12], true ], // criteria ids 'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 424]], true ], // criteria operators 'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiters @@ -199,46 +200,46 @@ class ItemsetListFilter extends Filter 'ta' => [parent::V_RANGE, [1, 30], false] // tag / content group ); - protected function createSQLForValues() + protected function createSQLForValues() : array { $parts = []; - $_v = &$this->fiData['v']; + $_v = &$this->values; // name [str] - if (isset($_v['na'])) - if ($_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value])) + if ($_v['na']) + if ($_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value])) $parts[] = $_; // quality [enum] - if (isset($_v['qu'])) + if ($_v['qu']) $parts[] = ['quality', $_v['qu']]; // type [enum] - if (isset($_v['ty'])) + if ($_v['ty']) $parts[] = ['type', $_v['ty']]; // itemLevel min [int] - if (isset($_v['minle'])) + if ($_v['minle']) $parts[] = ['minLevel', $_v['minle'], '>=']; // itemLevel max [int] - if (isset($_v['maxle'])) + if ($_v['maxle']) $parts[] = ['maxLevel', $_v['maxle'], '<=']; // reqLevel min [int] - if (isset($_v['minrl'])) + if ($_v['minrl']) $parts[] = ['reqLevel', $_v['minrl'], '>=']; // reqLevel max [int] - if (isset($_v['maxrl'])) + if ($_v['maxrl']) $parts[] = ['reqLevel', $_v['maxrl'], '<=']; // class [enum] - if (isset($_v['cl'])) + if ($_v['cl']) $parts[] = ['classMask', $this->list2Mask([$_v['cl']]), '&']; // tag [enum] - if (isset($_v['ta'])) + if ($_v['ta']) $parts[] = ['contentGroup', intVal($_v['ta'])]; return $parts; diff --git a/includes/types/profile.class.php b/includes/types/profile.class.php index ced02086..8d3e5122 100644 --- a/includes/types/profile.class.php +++ b/includes/types/profile.class.php @@ -241,12 +241,8 @@ class ProfileListFilter extends Filter { use TrProfilerFilter; - public $useLocalList = false; - public $extraOpts = []; - - private $realms = []; - - protected $genericFilter = array( + protected string $type = 'profiles'; + protected array $genericFilter = array( 2 => [parent::CR_NUMERIC, 'gearscore', NUM_CAST_INT ], // gearscore [num] 3 => [parent::CR_CALLBACK, 'cbAchievs', null, null], // achievementpoints [num] 5 => [parent::CR_NUMERIC, 'talenttree1', NUM_CAST_INT ], // talenttree1 [num] @@ -279,7 +275,7 @@ class ProfileListFilter extends Filter 36 => [parent::CR_CALLBACK, 'cbHasGuild', null, null] // hasguild [yn] ); - protected $inputFields = array( + protected array $inputFields = array( 'cr' => [parent::V_RANGE, [1, 36], true ], // criteria ids 'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 5000]], true ], // criteria operators 'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values @@ -295,29 +291,27 @@ class ProfileListFilter extends Filter 'sv' => [parent::V_CALLBACK, 'cbServerCheck', false], // server ); + public bool $useLocalList = false; + public array $extraOpts = []; + /* heads up! a couple of filters are too complex to be run against the characters database if they are selected, force useage of LocalProfileList */ - public function __construct($fromPOST = false, $opts = []) + public function __construct(string|array $data, array $opts = []) { - if (!empty($opts['realms'])) - $this->realms = $opts['realms']; - else - $this->realms = array_keys(Profiler::getRealms()); + parent::__construct($data, $opts); - parent::__construct($fromPOST, $opts); - - if (!empty($this->fiData['c']['cr'])) - if (array_intersect($this->fiData['c']['cr'], [2, 5, 6, 7, 21])) + if (!empty($this->criteria['cr'])) + if (array_intersect($this->criteria['cr'], [2, 5, 6, 7, 21])) $this->useLocalList = true; } - protected function createSQLForValues() + protected function createSQLForValues() : array { $parts = []; - $_v = $this->fiData['v']; + $_v = $this->values; // region (rg), battlegroup (bg) and server (sv) are passed to ProflieList as miscData and handled there @@ -325,37 +319,34 @@ class ProfileListFilter extends Filter $k = $this->useLocalList ? 'p' : 'c'; // name [str] - the table is case sensitive. Since i don't want to destroy indizes, lets alter the search terms - if (!empty($_v['na'])) + if ($_v['na']) { - $lower = $this->modularizeString([$k.'.name'], Util::lower($_v['na']), !empty($_v['ex']) && $_v['ex'] == 'on', true); - $proper = $this->modularizeString([$k.'.name'], Util::ucWords($_v['na']), !empty($_v['ex']) && $_v['ex'] == 'on', true); + $lower = $this->tokenizeString([$k.'.name'], Util::lower($_v['na']), $_v['ex'] == 'on', true); + $proper = $this->tokenizeString([$k.'.name'], Util::ucWords($_v['na']), $_v['ex'] == 'on', true); $parts[] = ['OR', $lower, $proper]; } // side [list] - if (!empty($_v['si'])) - { - if ($_v['si'] == SIDE_ALLIANCE) - $parts[] = [$k.'.race', ChrRace::fromMask(ChrRace::MASK_ALLIANCE)]; - else if ($_v['si'] == SIDE_HORDE) - $parts[] = [$k.'.race', ChrRace::fromMask(ChrRace::MASK_HORDE)]; - } + if ($_v['si'] == SIDE_ALLIANCE) + $parts[] = [$k.'.race', ChrRace::fromMask(ChrRace::MASK_ALLIANCE)]; + else if ($_v['si'] == SIDE_HORDE) + $parts[] = [$k.'.race', ChrRace::fromMask(ChrRace::MASK_HORDE)]; // race [list] - if (!empty($_v['ra'])) + if ($_v['ra']) $parts[] = [$k.'.race', $_v['ra']]; // class [list] - if (!empty($_v['cl'])) + if ($_v['cl']) $parts[] = [$k.'.class', $_v['cl']]; // min level [int] - if (isset($_v['minle'])) + if ($_v['minle']) $parts[] = [$k.'.level', $_v['minle'], '>=']; // max level [int] - if (isset($_v['maxle'])) + if ($_v['maxle']) $parts[] = [$k.'.level', $_v['maxle'], '<=']; return $parts; @@ -367,15 +358,15 @@ class ProfileListFilter extends Filter return null; $k = 'sk_'.Util::createHash(12); - $col = 'skill'.$skillId; + $col = 'skill-'.$skillId; - $this->formData['extraCols'][$skillId] = $col; + $this->fiExtraCols[$skillId] = $col; if ($this->useLocalList) { $this->extraOpts[$k] = array( 'j' => [sprintf('?_profiler_completion_skills %1$s ON `%1$s`.`id` = p.`id` AND `%1$s`.`skillId` = %2$d AND `%1$s`.`value` %3$s %4$d', $k, $skillId, $crs, $crv), true], - 's' => [', '.$k.'.`value` AS '.$col] + 's' => [', '.$k.'.`value` AS "'.$col.'"'] ); return [$k.'.skillId', null, '!']; } @@ -383,7 +374,7 @@ class ProfileListFilter extends Filter { $this->extraOpts[$k] = array( 'j' => [sprintf('character_skills %1$s ON `%1$s`.`guid` = c.`guid` AND `%1$s`.`skill` = %2$d AND `%1$s`.`value` %3$s %4$d', $k, $skillId, $crs, $crv), true], - 's' => [', '.$k.'.`value` AS '.$col] + 's' => [', '.$k.'.`value` AS "'.$col.'"'] ); return [$k.'.skill', null, '!']; } @@ -394,7 +385,7 @@ class ProfileListFilter extends Filter if (!Util::checkNumeric($crv, NUM_CAST_INT)) return null; - if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_achievement WHERE `id` = ?d', $crv)) + if (!Type::validateIds(Type::ACHIEVEMENT, $crv)) return null; $k = 'acv_'.Util::createHash(12); @@ -416,7 +407,7 @@ class ProfileListFilter extends Filter if (!Util::checkNumeric($crv, NUM_CAST_INT)) return null; - if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_items WHERE `id` = ?d', $crv)) + if (!Type::validateIds(Type::ITEM, $crv)) return null; $k = 'i_'.Util::createHash(12); @@ -449,7 +440,7 @@ class ProfileListFilter extends Filter protected function cbTeamName(int $cr, int $crs, string $crv, $size) : ?array { - if ($_ = $this->modularizeString(['at.name'], $crv)) + if ($_ = $this->tokenizeString(['at.name'], $crv)) return ['AND', ['at.type', $size], $_]; return null; diff --git a/includes/types/quest.class.php b/includes/types/quest.class.php index b470ead5..b29830c1 100644 --- a/includes/types/quest.class.php +++ b/includes/types/quest.class.php @@ -429,8 +429,8 @@ class QuestList extends BaseType class QuestListFilter extends Filter { - public $extraOpts = []; - protected $enums = array( + protected string $type = 'quests'; + protected array $enums = array( 37 => parent::ENUM_CLASSS, // classspecific 38 => parent::ENUM_RACE, // racespecific 9 => parent::ENUM_FACTION, // objectiveearnrepwith @@ -440,7 +440,7 @@ class QuestListFilter extends Filter 10 => parent::ENUM_FACTION // decreasesrepwith ); - protected $genericFilter = array( + protected array $genericFilter = array( 1 => [parent::CR_CALLBACK, 'cbReputation', '>', null], // increasesrepwith 2 => [parent::CR_NUMERIC, 'rewardXP', NUM_CAST_INT ], // experiencegained 3 => [parent::CR_NUMERIC, 'rewardOrReqMoney', NUM_CAST_INT ], // moneyrewarded @@ -475,7 +475,7 @@ class QuestListFilter extends Filter 45 => [parent::CR_BOOLEAN, 'rewardTitleId' ] // titlerewarded ); - protected $inputFields = array( + protected array $inputFields = array( 'cr' => [parent::V_RANGE, [1, 45], true ], // criteria ids 'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 99999]], true ], // criteria operators 'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - only numerals @@ -490,68 +490,60 @@ class QuestListFilter extends Filter 'ty' => [parent::V_LIST, [0, 1, 21, 41, 62, [81, 85], 88, 89], true ] // type ); - protected function createSQLForValues() + public array $extraOpts = []; + + protected function createSQLForValues() : array { $parts = []; - $_v = $this->fiData['v']; + $_v = $this->values; // name - if (isset($_v['na'])) + if ($_v['na']) { $_ = []; - if (isset($_v['ex']) && $_v['ex'] == 'on') - $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value, 'objectives_loc'.Lang::getLocale()->value, 'details_loc'.Lang::getLocale()->value]); + if ($_v['ex'] == 'on') + $_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value, 'objectives_loc'.Lang::getLocale()->value, 'details_loc'.Lang::getLocale()->value]); else - $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value]); + $_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]); if ($_) $parts[] = $_; } // level min - if (isset($_v['minle'])) + if ($_v['minle']) $parts[] = ['level', $_v['minle'], '>=']; // not considering quests that are always at player level (-1) // level max - if (isset($_v['maxle'])) + if ($_v['maxle']) $parts[] = ['level', $_v['maxle'], '<=']; // reqLevel min - if (isset($_v['minrl'])) + if ($_v['minrl']) $parts[] = ['minLevel', $_v['minrl'], '>=']; // ignoring maxLevel // reqLevel max - if (isset($_v['maxrl'])) + if ($_v['maxrl']) $parts[] = ['minLevel', $_v['maxrl'], '<=']; // ignoring maxLevel // side - if (isset($_v['si'])) + if ($_v['si']) { - $ex = [['reqRaceMask', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL, '!']; - $notEx = ['OR', ['reqRaceMask', 0], [['reqRaceMask', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL]]; + $excl = [['reqRaceMask', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL, '!']; + $incl = ['OR', ['reqRaceMask', 0], [['reqRaceMask', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL]]; - switch ($_v['si']) + $parts[] = match ($_v['si']) { - case SIDE_BOTH: - $parts[] = $notEx; - break; - case SIDE_HORDE: - $parts[] = ['OR', $notEx, ['reqRaceMask', ChrRace::MASK_HORDE, '&']]; - break; - case -SIDE_HORDE: - $parts[] = ['AND', $ex, ['reqRaceMask', ChrRace::MASK_HORDE, '&']]; - break; - case SIDE_ALLIANCE: - $parts[] = ['OR', $notEx, ['reqRaceMask', ChrRace::MASK_ALLIANCE, '&']]; - break; - case -SIDE_ALLIANCE: - $parts[] = ['AND', $ex, ['reqRaceMask', ChrRace::MASK_ALLIANCE, '&']]; - break; - } + SIDE_BOTH => $incl, + SIDE_HORDE => ['OR', $incl, ['reqRaceMask', ChrRace::MASK_HORDE, '&']], + -SIDE_HORDE => ['AND', $excl, ['reqRaceMask', ChrRace::MASK_HORDE, '&']], + SIDE_ALLIANCE => ['OR', $incl, ['reqRaceMask', ChrRace::MASK_ALLIANCE, '&']], + -SIDE_ALLIANCE => ['AND', $excl, ['reqRaceMask', ChrRace::MASK_ALLIANCE, '&']] + }; } // type [list] - if (isset($_v['ty'])) + if ($_v['ty'] !== null) $parts[] = ['type', $_v['ty']]; return $parts; @@ -566,7 +558,7 @@ class QuestListFilter extends Filter return null; if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE `id` = ?d', $crs)) - $this->formData['reputationCols'][] = [$crs, Util::localizedString($_, 'name')]; + $this->fiReputationCols[] = [$crs, Util::localizedString($_, 'name')]; return [ 'OR', diff --git a/includes/types/sound.class.php b/includes/types/sound.class.php index 121f732e..86cb9891 100644 --- a/includes/types/sound.class.php +++ b/includes/types/sound.class.php @@ -98,23 +98,24 @@ class SoundList extends BaseType class SoundListFilter extends Filter { - protected $inputFields = array( - 'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter - 'ty' => [parent::V_LIST, [[1, 4], 6, 9, 10, 12, 13, 14, 16, 17, [19, 23], [25, 31], 50, 52, 53], true ] // type + protected string $type = 'sounds'; + protected array $inputFields = array( + 'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter + 'ty' => [parent::V_LIST, [[1, 4], 6, 9, 10, 12, 13, 14, 16, 17, [19, 31], 50, 52, 53], true ] // type ); - protected function createSQLForValues() + protected function createSQLForValues() : array { $parts = []; - $_v = &$this->fiData['v']; + $_v = &$this->values; // name [str] - if (isset($_v['na'])) - if ($_ = $this->modularizeString(['name'])) + if ($_v['na']) + if ($_ = $this->tokenizeString(['name'])) $parts[] = $_; // type [list] - if (isset($_v['ty'])) + if ($_v['ty']) $parts[] = ['cat', $_v['ty']]; return $parts; diff --git a/includes/types/spell.class.php b/includes/types/spell.class.php index d6c7c993..e8347bb7 100644 --- a/includes/types/spell.class.php +++ b/includes/types/spell.class.php @@ -2359,7 +2359,8 @@ class SpellListFilter extends Filter ) ); - protected $enums = array( + protected string $type = 'spells'; + protected array $enums = array( 9 => array( // sources index 1 => true, // Any 2 => false, // None @@ -2409,7 +2410,7 @@ class SpellListFilter extends Filter ) ); - protected $genericFilter = array( + protected array $genericFilter = array( 1 => [parent::CR_CALLBACK, 'cbCost', ], // costAbs [op] [int] 2 => [parent::CR_NUMERIC, 'powerCostPercent', NUM_CAST_INT ], // prcntbasemanarequired 3 => [parent::CR_BOOLEAN, 'spellFocusObject' ], // requiresnearbyobject @@ -2513,7 +2514,7 @@ class SpellListFilter extends Filter 116 => [parent::CR_BOOLEAN, 'startRecoveryTime' ] // onGlobalCooldown [yn] ); - protected $inputFields = array( + protected array $inputFields = array( 'cr' => [parent::V_RANGE, [1, 116], true ], // criteria ids 'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 99999]], true ], // criteria operators 'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiters @@ -2532,62 +2533,62 @@ class SpellListFilter extends Filter 'me' => [parent::V_RANGE, [1, 31], false] // mechanics ); - protected function createSQLForValues() + protected function createSQLForValues() : array { $parts = []; - $_v = &$this->fiData['v']; + $_v = &$this->values; //string (extended) - if (isset($_v['na'])) + if ($_v['na']) { $_ = []; - if (isset($_v['ex']) && $_v['ex'] == 'on') - $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value, 'buff_loc'.Lang::getLocale()->value, 'description_loc'.Lang::getLocale()->value]); + if ($_v['ex'] == 'on') + $_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value, 'buff_loc'.Lang::getLocale()->value, 'description_loc'.Lang::getLocale()->value]); else - $_ = $this->modularizeString(['name_loc'.Lang::getLocale()->value]); + $_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]); if ($_) $parts[] = $_; } // spellLevel min todo (low): talentSpells (typeCat -2) commonly have spellLevel 1 (and talentLevel >1) -> query is inaccurate - if (isset($_v['minle'])) + if ($_v['minle']) $parts[] = ['spellLevel', $_v['minle'], '>=']; // spellLevel max - if (isset($_v['maxle'])) + if ($_v['maxle']) $parts[] = ['spellLevel', $_v['maxle'], '<=']; // skillLevel min - if (isset($_v['minrs'])) + if ($_v['minrs']) $parts[] = ['learnedAt', $_v['minrs'], '>=']; // skillLevel max - if (isset($_v['maxrs'])) + if ($_v['maxrs']) $parts[] = ['learnedAt', $_v['maxrs'], '<=']; // race - if (isset($_v['ra'])) + if ($_v['ra']) $parts[] = ['AND', [['reqRaceMask', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL, '!'], ['reqRaceMask', $this->list2Mask([$_v['ra']]), '&']]; // class [list] - if (isset($_v['cl'])) + if ($_v['cl']) $parts[] = ['reqClassMask', $this->list2Mask($_v['cl']), '&']; // school [list] - if (isset($_v['sc'])) + if ($_v['sc']) $parts[] = ['schoolMask', $this->list2Mask($_v['sc'], true), '&']; // glyph type [list] wonky, admittedly, but consult SPELL_CU_* in defines and it makes sense - if (isset($_v['gl'])) + if ($_v['gl']) $parts[] = ['cuFlags', ($this->list2Mask($_v['gl']) << 6), '&']; // dispel type - if (isset($_v['dt'])) + if ($_v['dt']) $parts[] = ['dispelType', $_v['dt']]; // mechanic - if (isset($_v['me'])) + if ($_v['me']) $parts[] = ['OR', ['mechanic', $_v['me']], ['effect1Mechanic', $_v['me']], ['effect2Mechanic', $_v['me']], ['effect3Mechanic', $_v['me']]]; return $parts; diff --git a/pages/achievements.php b/pages/achievements.php index 41528b4b..c716b867 100644 --- a/pages/achievements.php +++ b/pages/achievements.php @@ -48,10 +48,11 @@ class AchievementsPage extends GenericPage public function __construct($pageCall, $pageParam) { $this->getCategoryFromUrl($pageParam); - $this->filterObj = new AchievementListFilter(false, $this->category); parent::__construct($pageCall, $pageParam); + $this->filterObj = new AchievementListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + $this->name = Util::ucFirst(Lang::game('achievements')); $this->subCat = $pageParam ? '='.$pageParam : ''; } @@ -67,13 +68,7 @@ class AchievementsPage extends GenericPage if ($this->category) $conditions[] = ['category', (int)end($this->category)]; - // recreate form selection - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $this->filter['initData'] = ['init' => 'achievements']; - - if ($x = $this->filterObj->getSetCriteria()) - $this->filter['initData']['sc'] = $x; + $this->filterObj->evalCriteria(); if ($fiCnd = $this->filterObj->getConditions()) $conditions[] = $fiCnd; @@ -103,7 +98,7 @@ class AchievementsPage extends GenericPage if ($acvList->hasDiffFields('category')) $tabData['visibleCols'] = ['category']; - if (!empty($this->filter['fi']['extraCols'])) + if ($this->filterObj->fiExtraCols) $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; // create note if search limit was exceeded diff --git a/pages/areatriggers.php b/pages/areatriggers.php index 9b9ccee7..7c32bd24 100644 --- a/pages/areatriggers.php +++ b/pages/areatriggers.php @@ -29,22 +29,16 @@ class AreaTriggersPage extends GenericPage if (isset($this->category[0])) header('Location: ?areatriggers&filter=ty='.$this->category[0], true, 302); - $this->filterObj = new AreaTriggerListFilter(); - parent::__construct($pageCall, $pageParam); + $this->filterObj = new AreaTriggerListFilter($this->_get['filter'] ?? ''); + $this->name = Util::ucFirst(Lang::game('areatriggers')); } protected function generateContent() { - // recreate form selection - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $this->filter['initData'] = ['init' => 'areatrigger']; - - if ($x = $this->filterObj->getSetCriteria()) - $this->filter['initData']['sc'] = $x; + $this->filterObj->evalCriteria(); $conditions = []; if ($_ = $this->filterObj->getConditions()) @@ -75,15 +69,15 @@ class AreaTriggersPage extends GenericPage { array_unshift($this->title, $this->name); - $form = $this->filterObj->getForm(); - if (isset($form['ty']) && count($form['ty']) == 1) + $form = $this->filterObj->values; + if (count($form['ty']) == 1) array_unshift($this->title, Lang::areatrigger('types', $form['ty'][0])); } protected function generatePath() { - $form = $this->filterObj->getForm(); - if (isset($form['ty']) && count($form['ty']) == 1) + $form = $this->filterObj->values; + if (count($form['ty']) == 1) $this->path[] = $form['ty']; } } diff --git a/pages/arenateams.php b/pages/arenateams.php index 0634670b..e56e2ebe 100644 --- a/pages/arenateams.php +++ b/pages/arenateams.php @@ -12,10 +12,9 @@ class ArenaTeamsPage extends GenericPage { use TrProfiler; - private $filterObj = null; + protected $filterObj = null; protected $subCat = ''; - protected $filter = []; protected $lvTabs = []; protected $type = Type::ARENA_TEAM; @@ -38,9 +37,9 @@ class ArenaTeamsPage extends GenericPage if (!Cfg::get('PROFILER_ENABLE')) $this->error(); - $this->getSubjectFromUrl($pageParam); + $this->filterObj = new ArenaTeamListFilter($this->_get['filter'] ?? ''); - $this->filterObj = new ArenaTeamListFilter(); + $this->getSubjectFromUrl($pageParam); foreach (Profiler::getRealms() as $idx => $r) { @@ -75,14 +74,11 @@ class ArenaTeamsPage extends GenericPage if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = ['at.seasonGames', 0, '>']; + $this->filterObj->evalCriteria(); + if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; - // recreate form selection - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $this->filter['initData'] = ['type' => 'arenateams']; - $tabData = array( 'id' => 'arena-teams', 'hideCount' => 1, @@ -92,7 +88,7 @@ class ArenaTeamsPage extends GenericPage 'hiddenCols' => ['arenateam', 'guild'], ); - if (empty($this->filter['sz'])) + if (!$this->filterObj->values['sz']) $tabData['visibleCols'][] = 'size'; $miscParams = ['calcTotal' => true]; @@ -113,7 +109,7 @@ class ArenaTeamsPage extends GenericPage $tabData['data'] = array_values($teams->getListviewData()); // create note if search limit was exceeded - if ($this->filter['query'] && $teams->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) + if ($this->filterObj->query && $teams->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) { $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound2', $this->sumSubjects, $teams->getMatches()); $tabData['_truncated'] = 1; diff --git a/pages/currency.php b/pages/currency.php index ceba9082..8bb4dbb3 100644 --- a/pages/currency.php +++ b/pages/currency.php @@ -201,7 +201,7 @@ class CurrencyPage extends GenericPage else $w = '`reqItemId1` = '.$_itemId.' OR `reqItemId2` = '.$_itemId.' OR `reqItemId3` = '.$_itemId.' OR `reqItemId4` = '.$_itemId.' OR `reqItemId5` = '.$_itemId; - if (!$n && (new ItemListFilter())->isCurrencyFor($_itemId)) + if (!$n && ItemListFilter::isCurrencyFor($_itemId)) $n = '?items&filter=cr=158;crs='.$_itemId.';crv=0'; $xCosts = DB::Aowow()->selectCol('SELECT `id` FROM ?_itemextendedcost WHERE '.$w); diff --git a/pages/enchantments.php b/pages/enchantments.php index 9816cf3c..fb346dee 100644 --- a/pages/enchantments.php +++ b/pages/enchantments.php @@ -24,10 +24,11 @@ class EnchantmentsPage extends GenericPage public function __construct($pageCall, $pageParam) { $this->getCategoryFromUrl($pageParam); - $this->filterObj = new EnchantmentListFilter(false, ['parentCats' => $this->category]); parent::__construct($pageCall, $pageParam); + $this->filterObj = new EnchantmentListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + $this->name = Util::ucFirst(Lang::game('enchantments')); $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; } @@ -44,6 +45,8 @@ class EnchantmentsPage extends GenericPage if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; + $this->filterObj->evalCriteria(); + if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; @@ -52,26 +55,19 @@ class EnchantmentsPage extends GenericPage $tabData['data'] = array_values($ench->getListviewData()); $this->extendGlobalData($ench->getJSGlobals()); - // recreate form selection - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $this->filter['initData'] = ['init' => 'enchantments']; - - if ($x = $this->filterObj->getSetCriteria()) - $this->filter['initData']['sc'] = $x; - - $xCols = $this->filterObj->getExtraCols(); + $xCols = []; foreach (Stat::getFilterCriteriumIdFor() as $idx => $fiId) - if (array_column($tabData['data'], Stat::getJsonString($idx))) + if (array_filter(array_column($tabData['data'], Stat::getJsonString($idx)))) $xCols[] = $fiId; - if (array_column($tabData['data'], 'dmg')) - $xCols[] = 34; + // some kind of declaration conflict going on here..., expects colId for WEAPON_DAMAGE_MAX but jsonString is WEAPON_DAMAGE + if (array_filter(array_column($tabData['data'], 'dmg'))) + $xCols[] = Stat::getFilterCriteriumId(Stat::WEAPON_DAMAGE_MAX); if ($xCols) - $this->filter['initData']['ec'] = array_values(array_unique($xCols)); + $this->filterObj->fiExtraCols = array_merge($this->filterObj->fiExtraCols, $xCols); - if ($xCols) + if ($this->filterObj->fiExtraCols) $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; if ($ench->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) @@ -94,17 +90,17 @@ class EnchantmentsPage extends GenericPage protected function generateTitle() { - $form = $this->filterObj->getForm('form'); - if (!empty($form['ty']) && intVal($form['ty']) && $form['ty'] > 0 && $form['ty'] < 9) - array_unshift($this->title, Lang::enchantment('types', $form['ty'])); + $form = $this->filterObj->values; + if (count($form['ty']) == 1) + array_unshift($this->title, Lang::enchantment('types', $form['ty'][0])); array_unshift($this->title, $this->name); } protected function generatePath() { - $form = $this->filterObj->getForm('form'); - if (isset($form['ty']) && count($form['ty']) == 1) + $form = $this->filterObj->values; + if (count($form['ty']) == 1) $this->path[] = $form['ty'][0]; } } diff --git a/pages/genericPage.class.php b/pages/genericPage.class.php index 6d50e048..f73fff90 100644 --- a/pages/genericPage.class.php +++ b/pages/genericPage.class.php @@ -61,11 +61,10 @@ trait TrListPage { protected $category = null; protected $subCat = ''; - protected $filter = []; protected $lvTabs = []; // most pages have this protected $redButtons = []; // see template/redButtons.tpl.php - private $filterObj = null; + protected ?Filter $filterObj = null; protected function generateCacheKey(bool $withStaff = true) : string { @@ -447,8 +446,8 @@ class GenericPage if (!empty($this->path)) $this->pageTemplate['breadcrumb'] = $this->path; - if (!empty($this->filter)) - $this->pageTemplate['filter'] = empty($this->filter['query']) ? 0 : 1; + if (!empty($this->filterObj)) + $this->pageTemplate['filter'] = empty($this->filterObj->query) ? 0 : 1; if (method_exists($this, 'postCache')) // e.g. update dates for events and such $this->postCache(); diff --git a/pages/guilds.php b/pages/guilds.php index 2258430d..099f1a91 100644 --- a/pages/guilds.php +++ b/pages/guilds.php @@ -12,10 +12,9 @@ class GuildsPage extends GenericPage { use TrProfiler; - private $filterObj = null; + protected $filterObj = null; protected $subCat = ''; - protected $filter = []; protected $lvTabs = []; protected $type = Type::GUILD; @@ -36,7 +35,7 @@ class GuildsPage extends GenericPage $this->getSubjectFromUrl($pageParam); - $this->filterObj = new GuildListFilter(); + $this->filterObj = new GuildListFilter($this->_get['filter'] ?? ''); foreach (Profiler::getRealms() as $idx => $r) { @@ -72,14 +71,12 @@ class GuildsPage extends GenericPage ['c.level', MAX_LEVEL, '<='], // prevents JS errors [['c.extra_flags', Profiler::CHAR_GMFLAGS, '&'], 0] ); + + $this->filterObj->evalCriteria(); + if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; - // recreate form selection - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $this->filter['initData'] = ['type' => 'guilds']; - $tabData = array( 'id' => 'guilds', 'hideCount' => 1, @@ -109,7 +106,7 @@ class GuildsPage extends GenericPage $tabData['data'] = array_values($guilds->getListviewData()); // create note if search limit was exceeded - if ($this->filter['query'] && $guilds->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) + if ($this->filterObj->query && $guilds->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) { $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_guildsfound2', $this->sumSubjects, $guilds->getMatches()); $tabData['_truncated'] = 1; diff --git a/pages/icons.php b/pages/icons.php index 80b80fc3..c40a3d88 100644 --- a/pages/icons.php +++ b/pages/icons.php @@ -23,10 +23,10 @@ class IconsPage extends GenericPage public function __construct($pageCall) { - $this->filterObj = new IconListFilter(); - parent::__construct($pageCall); + $this->filterObj = new IconListFilter($this->_get['filter'] ?? ''); + $this->name = Util::ucFirst(Lang::game('icons')); } @@ -43,6 +43,8 @@ class IconsPage extends GenericPage if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; + $this->filterObj->evalCriteria(); + if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; @@ -51,14 +53,6 @@ class IconsPage extends GenericPage $tabData['data'] = array_values($icons->getListviewData()); $this->extendGlobalData($icons->getJSGlobals()); - // recreate form selection - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $this->filter['initData'] = ['init' => 'icons']; - - if ($x = $this->filterObj->getSetCriteria()) - $this->filter['initData']['sc'] = $x; - if ($icons->getMatches() > $sqlLimit) { $tabData['note'] = sprintf(Util::$tryFilteringEntityString, $icons->getMatches(), 'LANG.types[29][3]', $sqlLimit); @@ -73,31 +67,20 @@ class IconsPage extends GenericPage protected function generateTitle() { - $setCrt = $this->filterObj->getSetCriteria(); + $setCrt = $this->filterObj->fiSetCriteria; $title = $this->name; - if (isset($setCrt['cr']) && count($setCrt['cr']) == 1) + if (count($setCrt['cr']) == 1) { - switch ($setCrt['cr'][0]) + $title = match($setCrt['cr'][0]) { - case 1: - $title = Util::ucFirst(Lang::game('item')).' '.$title; - break; - case 2: - $title = Util::ucFirst(Lang::game('spell')).' '.$title; - break; - case 3: - $title = Util::ucFirst(Lang::game('achievement')).' '.$title; - break; - case 6: - $title = Util::ucFirst(Lang::game('currency')).' '.$title; - break; - case 9: - $title = Util::ucFirst(Lang::game('pet')).' '.$title; - break; - case 11: - $title = Util::ucFirst(Lang::game('class')).' '.$title; - break; - } + 1 => Util::ucFirst(Lang::game('item')), + 2 => Util::ucFirst(Lang::game('spell')), + 3 => Util::ucFirst(Lang::game('achievement')), + 6 => Util::ucFirst(Lang::game('currency')), + 9 => Util::ucFirst(Lang::game('pet')), + 11 => Util::ucFirst(Lang::game('class')), + default => '' + } . ' ' . $title; } array_unshift($this->title, $title); @@ -105,8 +88,8 @@ class IconsPage extends GenericPage protected function generatePath() { - $setCrt = $this->filterObj->getSetCriteria(); - if (isset($setCrt['cr']) && count($setCrt['cr']) == 1) + $setCrt = $this->filterObj->fiSetCriteria; + if (count($setCrt['cr']) == 1) $this->path[] = $setCrt['cr'][0]; } } diff --git a/pages/item.php b/pages/item.php index 0fef7c86..a85a52ba 100644 --- a/pages/item.php +++ b/pages/item.php @@ -866,7 +866,7 @@ class ItemPage extends genericPage else $w = '`reqItemId1` = '.$this->typeId.' OR `reqItemId2` = '.$this->typeId.' OR `reqItemId3` = '.$this->typeId.' OR `reqItemId4` = '.$this->typeId.' OR `reqItemId5` = '.$this->typeId; - if (!$n && (new ItemListFilter())->isCurrencyFor($this->typeId)) + if (!$n && ItemListFilter::isCurrencyFor($this->typeId)) $n = '?items&filter=cr=158;crs='.$this->typeId.';crv=0'; $xCosts = DB::Aowow()->selectCol('SELECT `id` FROM ?_itemextendedcost WHERE '.$w); diff --git a/pages/items.php b/pages/items.php index 9ef10744..23502445 100644 --- a/pages/items.php +++ b/pages/items.php @@ -15,6 +15,9 @@ class ItemsPage extends GenericPage protected $forceTabs = false; protected $gemScores = []; + protected $typeList = []; // rightPanel - content by context + protected $slotList = []; // rightPanel - INV_TYPE_Xs + protected $type = Type::ITEM; protected $tpl = 'items'; protected $path = [0, 0]; @@ -92,10 +95,10 @@ class ItemsPage extends GenericPage { $this->getCategoryFromUrl($pageParam); - $this->filterObj = new ItemListFilter(false, ['parentCats' => $this->category]); - parent::__construct($pageCall, $pageParam); + $this->filterObj = new ItemListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + $this->name = Util::ucFirst(Lang::game('items')); $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; } @@ -114,42 +117,25 @@ class ItemsPage extends GenericPage /* evaluate filter */ /*******************/ - // recreate form selection (must be evaluated first via getConditions()) + $this->filterObj->evalCriteria(); + $this->filterObj->evalWeights(); + if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $this->filter['initData'] = ['init' => 'items']; + // recreate form selection (must be evaluated first via getConditions()) + $fiForm = $this->filterObj->values; - if ($x = $this->filterObj->getSetCriteria()) - $this->filter['initData']['sc'] = $x; + $xCols = $this->filterObj->fiExtraCols; - $xCols = $this->filterObj->getExtraCols(); - if ($xCols) - $this->filter['initData']['ec'] = $xCols; - - if ($x = $this->filterObj->getSetWeights()) - $this->filter['initData']['sw'] = $x; - - $menu = $this->createExtraMenus(); - foreach ($menu['type'][0] as $k => $str) - if ($str && (!$menu['type'][1] || ($menu['type'][1] & (1 << $k)))) - $this->filter['type'][$k] = $str; - - foreach ($menu['slot'][0] as $k => $str) - if ($str && (!$menu['slot'][1] || ($menu['slot'][1] & (1 << $k)))) - $this->filter['slot'][$k] = $str; - - if (isset($this->filter['slot'][INVTYPE_SHIELD])) // "Off Hand" => "Shield" - $this->filter['slot'][INVTYPE_SHIELD] = Lang::item('armorSubClass', 6); + $this->createExtraMenus(); $infoMask = ITEMINFO_JSON; if (array_intersect([63, 64, 125], $xCols)) // 63:buyPrice; 64:sellPrice; 125:reqarenartng $infoMask |= ITEMINFO_VENDOR; if ($xCols) - $this->sharedLV['extraCols'] = '$fi_getExtraCols(fi_extraCols, '.($this->filter['gm'] ?? 0).', '.(array_intersect([63], $xCols) ? 1 : 0).')'; + $this->sharedLV['extraCols'] = '$fi_getExtraCols(fi_extraCols, '.($fiForm['gm'] ?? 0).', '.(array_intersect([63], $xCols) ? 1 : 0).')'; if ($this->filterObj->error) $this->sharedLV['_errors'] = '$1'; @@ -171,7 +157,7 @@ class ItemsPage extends GenericPage /* handle auto-gemming */ /***********************/ - $this->gemScores = $this->createGemScores(); + $this->gemScores = $this->createGemScores($fiForm['gm'] ?? 0); /*************************/ @@ -179,9 +165,9 @@ class ItemsPage extends GenericPage /*************************/ $upgItemData = []; - if (!empty($this->filter['upg']) && !empty($this->filterObj->getSetWeights())) + if ($this->filterObj->upgrades && $this->filterObj->fiSetWeights) { - $upgItems = new ItemList(array(['id', array_keys($this->filter['upg'])]), ['extraOpts' => $this->filterObj->extraOpts]); + $upgItems = new ItemList(array(['id', array_keys($this->filterObj->upgrades)]), ['extraOpts' => $this->filterObj->extraOpts]); if (!$upgItems->error) { $upgItemData = $upgItems->getListviewData($infoMask); @@ -192,8 +178,8 @@ class ItemsPage extends GenericPage if ($upgItemData) // check if upItems cover multiple slots { $singleSlot = true; - $ref = reset($this->filter['upg']); - foreach ($this->filter['upg'] as $slot) + $ref = reset($this->filterObj->upgrades); + foreach ($this->filterObj->upgrades as $slot) { if ($slot == $ref) continue; @@ -252,10 +238,10 @@ class ItemsPage extends GenericPage $groups = array_merge($availableSlots[ITEM_CLASS_ARMOR], $availableSlots[ITEM_CLASS_WEAPON]); if (isset($this->filter['sl'])) // skip lookups for unselected slots - $groups = array_intersect($groups, (array)$this->filter['sl']); + $groups = array_intersect($groups, $this->filter['sl']); - if (!empty($this->filter['upg'])) // skip lookups for slots we dont have items to upgrade for - $groups = array_intersect($groups, (array)$this->filter['upg']); + if (!empty($this->filterObj->upgrades)) // skip lookups for slots we dont have items to upgrade for + $groups = array_intersect($groups, $this->filterObj->upgrades); if ($groups) { @@ -338,7 +324,7 @@ class ItemsPage extends GenericPage { if ($grouping == 1) // slot: match upgradeItem to slot { - $upg = array_keys(array_filter($this->filter['upg'], function ($v) use ($group) { + $upg = array_keys(array_filter($this->filterObj->upgrades, function ($v) use ($group) { return $v == $group; })); @@ -350,7 +336,7 @@ class ItemsPage extends GenericPage } else if ($grouping) { - $upg = array_keys($this->filter['upg']); + $upg = array_keys($this->filterObj->upgrades); $tabData['_upgradeIds'] = $upg; foreach ($upgItemData as $uId => $data) // using numeric keys => cant use array_merge $tabData['data'][$uId] = $data; @@ -376,8 +362,8 @@ class ItemsPage extends GenericPage $tabData['tabs'] = '$tabsGroups'; } - if (!empty($this->filterObj->getSetWeights())) - if ($items->hasSetFields('armor')) + if ($this->filterObj->fiSetWeights) + if ($items->hasSetFields('tplArmor')) $tabData['visibleCols'][] = 'armor'; // create note if search limit was exceeded; overwriting 'note' is intentional @@ -394,7 +380,7 @@ class ItemsPage extends GenericPage { case 1: $override['sl'] = $group; - $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmoreslot, \''.$cls.'\', \''.$this->filterObj->getFilterString($override).'\')'; + $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmoreslot, \''.$cls.'\', \''.$this->filterObj->buildGETParam($override).'\')'; break; case 2: if ($group > 0) @@ -405,11 +391,11 @@ class ItemsPage extends GenericPage else $override['maxle'] = abs($group) - 1; - $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmorelevel, \''.$cls.'\', \''.$this->filterObj->getFilterString($override).'\')'; + $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmorelevel, \''.$cls.'\', \''.$this->filterObj->buildGETParam($override).'\')'; break; case 3: if ($_ = [null, 3, 4, 5, 6, 7, 9, 10, 11][$group]) - $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmoresource, \''.$cls.'\', \''.$this->filterObj->getFilterString($override, ['cr' => 128, 'crs' => $_, 'crv' => 0]).'\')'; + $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmoresource, \''.$cls.'\', \''.$this->filterObj->buildGETParam($override, ['cr' => 128, 'crs' => $_, 'crv' => 0]).'\')'; break; } @@ -432,10 +418,6 @@ class ItemsPage extends GenericPage $this->lvTabs[] = [ItemList::$brickFile, $tabData]; } - // reformat for use in template - if (!empty($this->filter['upg'])) - $this->filter['upg'] = implode(':', array_keys($this->filter['upg'])); - // whoops, we have no data? create emergency content if (empty($this->lvTabs)) { @@ -476,51 +458,51 @@ class ItemsPage extends GenericPage $this->path[] = $c; // if slot-dropdown is available && Armor && $path points to Armor-Class - $form = $this->filterObj->getForm(); - if (count($this->path) == 4 && $this->category[0] == 4 && isset($form['sl']) && count($form['sl']) == 1) + $form = $this->filterObj->values; + if (count($this->path) == 4 && $this->category[0] == 4 && count($form['sl']) == 1) $this->path[] = $form['sl'][0]; - else if (isset($this->category[0]) && $this->category[0] == 0 && isset($form['ty']) && count($form['ty']) == 1) + else if (isset($this->category[0]) && $this->category[0] == 0 && count($form['ty']) == 1) $this->path[] = $form['ty'][0]; } // fetch best possible gems for chosen weights - private function createGemScores() + private function createGemScores(int $gemQuality) : array { $gemScores = []; - if (empty($this->filterObj->getSetWeights())) + if (!$this->filterObj->fiSetWeights) return []; - if (!empty($this->filter['gm'])) + if (!$gemQuality) + return []; + + $this->sharedLV['computeDataFunc'] = '$fi_scoreSockets'; + + $q = intVal($this->filterObj->values['gm']); + $mask = 0xE; + $cnd = [10, ['class', ITEM_CLASS_GEM], ['gemColorMask', &$mask, '&'], ['quality', &$q]]; + if (!$this->filterObj->values['jc']) + $cnd[] = ['itemLimitCategory', 0]; // Jeweler's Gems + + If ($this->filterObj->wtCnd) + $cnd[] = $this->filterObj->wtCnd; + + $anyColor = new ItemList($cnd, ['extraOpts' => $this->filterObj->extraOpts]); + if (!$anyColor->error) { - $this->sharedLV['computeDataFunc'] = '$fi_scoreSockets'; + $this->extendGlobalData($anyColor->getJSGlobals()); + $gemScores[0] = array_values($anyColor->getListviewData(ITEMINFO_GEM)); + } - $q = intVal($this->filter['gm']); - $mask = 0xE; - $cnd = [10, ['class', ITEM_CLASS_GEM], ['gemColorMask', &$mask, '&'], ['quality', &$q]]; - if (!isset($this->filter['jc'])) - $cnd[] = ['itemLimitCategory', 0]; // Jeweler's Gems - - If ($this->filterObj->wtCnd) - $cnd[] = $this->filterObj->wtCnd; - - $anyColor = new ItemList($cnd, ['extraOpts' => $this->filterObj->extraOpts]); - if (!$anyColor->error) + for ($i = 0; $i < 4; $i++) + { + $mask = 1 << $i; + $q = !$i ? ITEM_QUALITY_RARE : intVal($gemQuality); // meta gems are always included.. ($q is backReferenced) + $byColor = new ItemList($cnd, ['extraOpts' => $this->filterObj->extraOpts]); + if (!$byColor->error) { - $this->extendGlobalData($anyColor->getJSGlobals()); - $gemScores[0] = array_values($anyColor->getListviewData(ITEMINFO_GEM)); - } - - for ($i = 0; $i < 4; $i++) - { - $mask = 1 << $i; - $q = !$i ? ITEM_QUALITY_RARE : intVal($this->filter['gm']); // meta gems are always included.. ($q is backReferenced) - $byColor = new ItemList($cnd, ['extraOpts' => $this->filterObj->extraOpts]); - if (!$byColor->error) - { - $this->extendGlobalData($byColor->getJSGlobals()); - $gemScores[$mask] = array_values($byColor->getListviewData(ITEMINFO_GEM)); - } + $this->extendGlobalData($byColor->getJSGlobals()); + $gemScores[$mask] = array_values($byColor->getListviewData(ITEMINFO_GEM)); } } @@ -534,17 +516,15 @@ class ItemsPage extends GenericPage } // display available submenus 'type' and 'slot', if applicable - private function createExtraMenus() + private function createExtraMenus() : void { - $menu = array( - 'type' => [[], null], - 'slot' => [[], null] - ); + $typeData = $slotData = []; + $typeMask = $slotMask = 0x0; if (!$this->category) { - $menu['slot'] = [Lang::item('inventoryType'), null]; - asort($menu['slot'][0]); + $slotData = [Lang::item('inventoryType'), null]; + asort($slotData); } else { @@ -558,31 +538,37 @@ class ItemsPage extends GenericPage switch ($this->category[0]) { case 0: - $menu['type'] = [Lang::item('cat', 0, 1), null]; + $typeData = Lang::item('cat', 0, 1); if (!isset($this->category[1]) || in_array($this->category[1], [6, -3])) { - $menu['slot'] = [Lang::item('inventoryType'), 0x63EFEA]; - asort($menu['slot'][0]); + $slotData = Lang::item('inventoryType'); + $slotMask = 0x63EFEA; + asort($slotData); } break; case 2: if (!isset($this->category[1])) - $menu['type'] = [Lang::spell('weaponSubClass'), null]; + $typeData = Lang::spell('weaponSubClass'); - $menu['slot'] = [Lang::item('inventoryType'), 0x262A000]; - asort($menu['slot'][0]); + $slotData = Lang::item('inventoryType'); + $slotMask = 0x262A000; + asort($slotData); break; case 4: if (!isset($this->category[1])) { - $menu['slot'] = [Lang::item('inventoryType'), 0x10895FFE]; - $menu['type'] = [Lang::item('cat', 4, 1), null]; + $slotData = Lang::item('inventoryType'); + $slotMask = 0x10895FFE; + $typeData = Lang::item('cat', 4, 1); } else if (in_array($this->category[1], [1, 2, 3, 4])) - $menu['slot'] = [Lang::item('inventoryType'), 0x7EA]; + { + $slotData = Lang::item('inventoryType'); + $slotMask = 0x7EA; + } - asort($menu['slot'][0]); + asort($slotData); break; case 16: if (!isset($this->category[2])) @@ -598,13 +584,19 @@ class ItemsPage extends GenericPage $this->sharedLV['hiddenCols'][] = 'slot'; case 15: if (!isset($this->category[1])) - $menu['type'] = [$catList[1], null]; + $typeData = $catList[1]; break; } } - return $menu; + foreach ($typeData as $k => $str) + if ($str && (!$typeMask || ($typeMask & (1 << $k)))) + $this->typeList[$k] = $str; + + foreach ($slotData as $k => $str) // "Off Hand" => "Shield" + if ($str && (!$slotMask || ($slotMask & (1 << $k)))) + $this->slotList[$k] = $k == INVTYPE_SHIELD ? Lang::item('armorSubClass', 6) : $str; } } diff --git a/pages/itemsets.php b/pages/itemsets.php index 87b274b5..2b8a599c 100644 --- a/pages/itemsets.php +++ b/pages/itemsets.php @@ -24,10 +24,11 @@ class ItemsetsPage extends GenericPage public function __construct($pageCall, $pageParam) { $this->getCategoryFromUrl($pageParam); - $this->filterObj = new ItemsetListFilter(false, ['parentCats' => $this->category]); parent::__construct($pageCall, $pageParam); + $this->filterObj = new ItemsetListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + $this->name = Util::ucFirst(Lang::game('itemsets')); } @@ -40,21 +41,15 @@ class ItemsetsPage extends GenericPage if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; + $this->filterObj->evalCriteria(); + if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; $itemsets = new ItemsetList($conditions, ['calcTotal' => true]); $this->extendGlobalData($itemsets->getJSGlobals()); - // recreate form selection - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $this->filter['initData'] = ['init' => 'itemsets']; - - if ($x = $this->filterObj->getSetCriteria()) - $this->filter['initData']['sc'] = $x; - - $xCols = $this->filterObj->getExtraCols(); + $xCols = $this->filterObj->fiExtraCols; if ($xCols) $this->filter['initData']['ec'] = $xCols; @@ -87,15 +82,15 @@ class ItemsetsPage extends GenericPage { array_unshift($this->title, $this->name); - $form = $this->filterObj->getForm('form'); - if (isset($form['cl'])) + $form = $this->filterObj->values; + if ($form['cl']) array_unshift($this->title, Lang::game('cl', $form['cl'])); } protected function generatePath() { - $form = $this->filterObj->getForm('form'); - if (isset($form['cl'])) + $form = $this->filterObj->values; + if ($form['cl']) $this->path[] = $form['cl']; } } diff --git a/pages/npc.php b/pages/npc.php index 189f888b..703f19f4 100644 --- a/pages/npc.php +++ b/pages/npc.php @@ -680,11 +680,11 @@ class NpcPage extends GenericPage // tabs: this creature contains.. $skinTab = ['tab_skinning', 'skinning', SKILL_SKINNING]; - if ($_typeFlags & NPC_TYPEFLAG_HERBLOOT) + if ($_typeFlags & NPC_TYPEFLAG_SKIN_WITH_HERBALISM) $skinTab = ['tab_herbalism', 'herbalism', SKILL_HERBALISM]; - else if ($_typeFlags & NPC_TYPEFLAG_MININGLOOT) + else if ($_typeFlags & NPC_TYPEFLAG_SKIN_WITH_MINING) $skinTab = ['tab_mining', 'mining', SKILL_MINING]; - else if ($_typeFlags & NPC_TYPEFLAG_ENGINEERLOOT) + else if ($_typeFlags & NPC_TYPEFLAG_SKIN_WITH_ENGINEERING) $skinTab = ['tab_engineering', 'engineering', SKILL_ENGINEERING]; /* diff --git a/pages/npcs.php b/pages/npcs.php index 6b798132..cd5e9bd8 100644 --- a/pages/npcs.php +++ b/pages/npcs.php @@ -27,10 +27,11 @@ class NpcsPage extends GenericPage public function __construct($pageCall, $pageParam) { $this->getCategoryFromUrl($pageParam); - $this->filterObj = new CreatureListFilter(false, ['parentCats' => $this->category]); parent::__construct($pageCall, $pageParam); + $this->filterObj = new CreatureListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + $this->name = Util::ucFirst(Lang::game('npcs')); $this->subCat = $pageParam ? '='.$pageParam : ''; } @@ -50,33 +51,21 @@ class NpcsPage extends GenericPage $this->petFamPanel = $this->category[0] == 1; } + $this->filterObj->evalCriteria(); + if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; // beast subtypes are selected via filter $npcs = new CreatureList($conditions, ['extraOpts' => $this->filterObj->extraOpts, 'calcTotal' => true]); - // recreate form selection - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $this->filter['initData'] = ['init' => 'npcs']; - - $rCols = $this->filterObj->getReputationCols(); - $xCols = $this->filterObj->getExtraCols(); - if ($rCols) - $this->filter['initData']['rc'] = $rCols; - - if ($xCols) - $this->filter['initData']['ec'] = $xCols; - - if ($x = $this->filterObj->getSetCriteria()) - $this->filter['initData']['sc'] = $x; + $rCols = $this->filterObj->fiReputationCols; $tabData = ['data' => array_values($npcs->getListviewData($rCols ? NPCINFO_REP : 0x0))]; if ($rCols) // never use pretty-print $tabData['extraCols'] = '$fi_getReputationCols('.Util::toJSON($rCols, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE).')'; - else if ($xCols) + else if ($this->filterObj->fiExtraCols) $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; if ($this->category) @@ -107,9 +96,9 @@ class NpcsPage extends GenericPage if ($this->category) array_unshift($this->title, Lang::npc('cat', $this->category[0])); - $form = $this->filterObj->getForm(); - if (isset($form['fa']) && !is_array($form['fa'])) - array_unshift($this->title, Lang::game('fa', $form['fa'])); + $form = $this->filterObj->values; + if (count($form['fa']) == 1) + array_unshift($this->title, Lang::game('fa', $form['fa'][0])); } protected function generatePath() @@ -117,9 +106,9 @@ class NpcsPage extends GenericPage if ($this->category) $this->path[] = $this->category[0]; - $form = $this->filterObj->getForm(); - if (isset($form['fa']) && !is_array($form['fa'])) - $this->path[] = $form['fa']; + $form = $this->filterObj->values; + if (count($form['fa'])) + $this->path[] = $form['fa'][0]; } } diff --git a/pages/objects.php b/pages/objects.php index 8f46d233..5d516ea9 100644 --- a/pages/objects.php +++ b/pages/objects.php @@ -25,10 +25,11 @@ class ObjectsPage extends GenericPage public function __construct($pageCall, $pageParam) { $this->getCategoryFromUrl($pageParam); - $this->filterObj = new GameObjectListFilter(false, ['parentCats' => $this->category]); parent::__construct($pageCall, $pageParam); + $this->filterObj = new GameObjectListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + $this->name = Util::ucFirst(Lang::game('objects')); $this->subCat = $pageParam ? '='.$pageParam : ''; } @@ -45,13 +46,7 @@ class ObjectsPage extends GenericPage if ($this->category) $conditions[] = ['typeCat', (int)$this->category[0]]; - // recreate form selection - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $this->filter['initData'] = ['init' => 'objects']; - - if ($x = $this->filterObj->getSetCriteria()) - $this->filter['initData']['sc'] = $x; + $this->filterObj->evalCriteria(); if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; diff --git a/pages/profiles.php b/pages/profiles.php index 0c6fe4be..e772354a 100644 --- a/pages/profiles.php +++ b/pages/profiles.php @@ -12,10 +12,9 @@ class ProfilesPage extends GenericPage { use TrProfiler; - private $filterObj = null; + protected $filterObj = null; protected $subCat = ''; - protected $filter = []; protected $lvTabs = []; protected $roster = 0; // $_GET['roster'] = 1|2|3|4 .. 2,3,4 arenateam-size (4 => 5-man), 1 guild .. it puts a resync button on the lv... @@ -55,7 +54,7 @@ class ProfilesPage extends GenericPage $realms[] = $idx; } - $this->filterObj = new ProfileListFilter(false, ['realms' => $realms]); + $this->filterObj = new ProfileListFilter($this->_get['filter'] ?? '', ['realms' => $realms]); $this->name = Util::ucFirst(Lang::game('profiles')); $this->subCat = $pageParam ? '='.$pageParam : ''; @@ -77,6 +76,8 @@ class ProfilesPage extends GenericPage $conditions = []; + $this->filterObj->evalCriteria(); + if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; @@ -87,15 +88,8 @@ class ProfilesPage extends GenericPage $conditions[] = [['extra_flags', Profiler::CHAR_GMFLAGS, '&'], 0]; } - // recreate form selection - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $this->filter['initData'] = ['init' => 'profiles']; - - if ($x = $this->filterObj->getSetCriteria()) + if ($x = $this->filterObj->fiSetCriteria) { - $this->filter['initData']['sc'] = $x; - if ($r = array_intersect([9, 12, 15, 18], $x['cr'])) if (count($r) == 1) $this->roster = (reset($r) - 6) / 3; // 1, 2, 3, or 4 @@ -108,13 +102,13 @@ class ProfilesPage extends GenericPage 'onBeforeCreate' => '$pr_initRosterListview' // puts a resync button on the lv ); - $extraCols = $this->filterObj->getExtraCols(); + $extraCols = $this->filterObj->fiExtraCols; if ($extraCols) { $xc = []; foreach ($extraCols as $idx => $col) if ($idx > 0) - $xc[] = "\$Listview.funcBox.createSimpleCol('Skill' + ".$idx.", g_spell_skills[".$idx."], '7%', 'skill' + ".$idx.")"; + $xc[] = "\$Listview.funcBox.createSimpleCol('Skill' + ".$idx.", g_spell_skills[".$idx."], '7%', 'skill-' + ".$idx.")"; $tabData['extraCols'] = $xc; } @@ -161,14 +155,14 @@ class ProfilesPage extends GenericPage else $this->roster = 0; - $tabData['data'] = array_values($profiles->getListviewData($addInfoMask, array_filter($extraCols, function ($x) { return $x > 0; }, ARRAY_FILTER_USE_KEY))); + $tabData['data'] = array_values($profiles->getListviewData($addInfoMask, array_filter($extraCols, fn($x) => $x > 0, ARRAY_FILTER_USE_KEY))); - if ($sc = $this->filterObj->getSetCriteria()) + if ($sc = $this->filterObj->fiSetCriteria) if (in_array(10, $sc['cr']) && !in_array('guildrank', $tabData['visibleCols'])) $tabData['visibleCols'][] = 'guildrank'; // create note if search limit was exceeded - if ($this->filter['query'] && $profiles->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) + if ($this->filterObj->query && $profiles->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) { $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_charactersfound2', $this->sumSubjects, $profiles->getMatches()); $tabData['_truncated'] = 1; diff --git a/pages/quests.php b/pages/quests.php index 3172a09d..a69f6210 100644 --- a/pages/quests.php +++ b/pages/quests.php @@ -26,10 +26,11 @@ class QuestsPage extends GenericPage $this->validCats = Game::$questClasses; // not allowed to set this as default $this->getCategoryFromUrl($pageParam); - $this->filterObj = new QuestListFilter(false, ['parentCats' => $this->category]); parent::__construct($pageCall, $pageParam); + $this->filterObj = new QuestListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + $this->name = Util::ucFirst(Lang::game('quests')); $this->subCat = $pageParam ? '='.$pageParam : ''; } @@ -46,6 +47,8 @@ class QuestsPage extends GenericPage else if (isset($this->category[0])) $conditions[] = ['zoneOrSort', $this->validCats[$this->category[0]]]; + $this->filterObj->evalCriteria(); + if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; @@ -53,27 +56,11 @@ class QuestsPage extends GenericPage $this->extendGlobalData($quests->getJSGlobals()); - // recreate form selection - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $this->filter['initData'] = ['init' => 'quests']; - - $rCols = $this->filterObj->getReputationCols(); - $xCols = $this->filterObj->getExtraCols(); - if ($rCols) - $this->filter['initData']['rc'] = $rCols; - - if ($xCols) - $this->filter['initData']['ec'] = $xCols; - - if ($x = $this->filterObj->getSetCriteria()) - $this->filter['initData']['sc'] = $x; - $tabData = ['data' => array_values($quests->getListviewData())]; - if ($rCols) - $tabData['extraCols'] = '$fi_getReputationCols('.json_encode($rCols, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE).')'; - else if ($xCols) + if ($rCols = $this->filterObj->fiReputationCols) // never use pretty-print + $tabData['extraCols'] = '$fi_getReputationCols('.Util::toJSON($rCols, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE).')'; + else if ($this->filterObj->fiExtraCols) $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; // create note if search limit was exceeded diff --git a/pages/search.php b/pages/search.php index af8bf81c..fabf70a1 100644 --- a/pages/search.php +++ b/pages/search.php @@ -573,15 +573,14 @@ class SearchPage extends GenericPage if ($_ = array_filter($this->_get['slots'] ?? [])) $cnd[] = ['slot', $_]; - // trick ItemListFilter into evaluating weights - if ($this->statWeights) - $_GET['filter'] = 'wt='.implode(':', $this->statWeights[0]).';wtv='.implode(':', $this->statWeights[1]); - - $itemFilter = new ItemListFilter(); - if ($_ = $itemFilter->createConditionsForWeights()) + if ($this->_get['wt'] && $this->_get['wtv']) { - $miscData['extraOpts'] = $itemFilter->extraOpts; - $cnd = array_merge($cnd, [$_]); + $itemFilter = new ItemListFilter($this->_get); + if ($_ = $itemFilter->createConditionsForWeights()) + { + $miscData['extraOpts'] = $itemFilter->extraOpts; + $cnd = array_merge($cnd, [$_]); + } } } else diff --git a/pages/sounds.php b/pages/sounds.php index 6263e45d..ea064f36 100644 --- a/pages/sounds.php +++ b/pages/sounds.php @@ -28,10 +28,10 @@ class SoundsPage extends GenericPage if (isset($this->category[0])) header('Location: ?sounds&filter=ty='.$this->category[0], true, 302); - $this->filterObj = new SoundListFilter(); - parent::__construct($pageCall, $pageParam); + $this->filterObj = new SoundListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + $this->name = Util::ucFirst(Lang::game('sounds')); } @@ -42,13 +42,12 @@ class SoundsPage extends GenericPage BUTTON_PLAYLIST => true ); + $this->filterObj->evalCriteria(); + $conditions = []; if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; - $this->filter = $this->filterObj->getForm(); - $this->filter['query'] = $this->_get['filter']; - $tabData = []; $sounds = new SoundList($conditions, ['calcTotal' => true]); if (!$sounds->error) @@ -78,15 +77,15 @@ class SoundsPage extends GenericPage { array_unshift($this->title, $this->name); - $form = $this->filterObj->getForm(); - if (isset($form['ty']) && count($form['ty']) == 1) + $form = $this->filterObj->values; + if (count($form['ty']) == 1) array_unshift($this->title, Lang::sound('cat', $form['ty'][0])); } protected function generatePath() { - $form = $this->filterObj->getForm(); - if (isset($form['ty']) && count($form['ty']) == 1) + $form = $this->filterObj->values; + if (count($form['ty']) == 1) $this->path[] = $form['ty'][0]; } } diff --git a/setup/tools/sqlgen/source.ss.php b/setup/tools/sqlgen/source.ss.php index a5ccf499..7aece181 100644 --- a/setup/tools/sqlgen/source.ss.php +++ b/setup/tools/sqlgen/source.ss.php @@ -690,7 +690,7 @@ CLISetup::registerSetup("sql", new class extends SetupScript SELECT gt.`entry`, IF(glt.`Reference` > 0, -glt.`Reference`, glt.`Item`) `itemOrRef`, ?d AS "srcType" FROM gameobject_template gt JOIN gameobject_loot_template glt ON glt.`entry` = gt.`data1` WHERE gt.`type` = ?d AND gt.`data1` > 0 AND gt.`data0` IN (?a)) src LEFT JOIN item_template it ON src.itemOrRef > 0 AND src.`itemOrRef` = it.`entry` GROUP BY `refOrItem`, src.`entry`', - Type::NPC, NPC_TYPEFLAG_HERBLOOT, + Type::NPC, NPC_TYPEFLAG_SKIN_WITH_HERBALISM, Type::OBJECT, OBJECT_CHEST, DB::Aowow()->selectCol('SELECT `id` FROM dbc_lock WHERE `properties1` = ?d', LOCK_PROPERTY_HERBALISM) ); @@ -779,7 +779,7 @@ CLISetup::registerSetup("sql", new class extends SetupScript SELECT gt.`entry`, IF(glt.`Reference` > 0, -glt.`Reference`, glt.`Item`) `itemOrRef`, ?d AS "srcType" FROM gameobject_template gt JOIN gameobject_loot_template glt ON glt.`entry` = gt.`data1` WHERE gt.`type` = ?d AND gt.`data1` > 0 AND gt.`data0` IN (?a)) src LEFT JOIN item_template it ON src.itemOrRef > 0 AND src.`itemOrRef` = it.`entry` GROUP BY `refOrItem`, src.`entry`', - Type::NPC, NPC_TYPEFLAG_MININGLOOT, + Type::NPC, NPC_TYPEFLAG_SKIN_WITH_MINING, Type::OBJECT, OBJECT_CHEST, DB::Aowow()->selectCol('SELECT `id` FROM dbc_lock WHERE `properties1` = ?d', LOCK_PROPERTY_MINING) ); @@ -916,7 +916,7 @@ CLISetup::registerSetup("sql", new class extends SetupScript FROM (SELECT ct.`entry`, IF(slt.`Reference` > 0, -slt.`Reference`, slt.`Item`) `itemOrRef` FROM creature_template ct JOIN skinning_loot_template slt ON slt.`entry` = ct.`skinloot` WHERE (`type_flags` & ?d) AND ct.`skinloot` > 0) src LEFT JOIN item_template it ON src.`itemOrRef` > 0 AND src.`itemOrRef` = it.`entry` GROUP BY `refOrItem`, src.`entry`', - NPC_TYPEFLAG_ENGINEERLOOT + NPC_TYPEFLAG_SKIN_WITH_ENGINEERING ); if (!$salvageLoot) diff --git a/static/js/filters.js b/static/js/filters.js index 7655c527..670abd94 100644 --- a/static/js/filters.js +++ b/static/js/filters.js @@ -709,11 +709,11 @@ function fi_reset(_this) { fi_resetCriterion($WH.ge('fi_criteria')); fi_resetCriterion($WH.ge('fi_weight')); - // custom start (originally a click on reset would reload the page with empty filters) + // aowow - custom start (originally a click on reset would reload the page with empty filters) var bc = PageTemplate.get('breadcrumb'); if (typeof bc[1] !== 'undefined') Menu.modifyUrl(Menu.findItem(mn_database, [bc[1]]), {}, {}); - // custom end + // aowow - custom end var _ = $WH.ge('sdkgnsdkn436'); if (_) { diff --git a/template/bricks/filter.tpl.php b/template/bricks/filter.tpl.php index 0cbc279c..e54c3104 100644 --- a/template/bricks/filter.tpl.php +++ b/template/bricks/filter.tpl.php @@ -3,24 +3,31 @@ diff --git a/template/bricks/pageTemplate.tpl.php b/template/bricks/pageTemplate.tpl.php index 1a9e5d77..3c3c0a76 100644 --- a/template/bricks/pageTemplate.tpl.php +++ b/template/bricks/pageTemplate.tpl.php @@ -31,9 +31,10 @@ if (!empty($this->pageTemplate)): echo " PageTemplate.init();\n"; endif; -if (!empty($fi)): - echo " Menu.modifyUrl(Menu.findItem(mn_database, [".$fi['menuItem']."]), { filter: '+=".Util::jsEscape($fi['query'])."' }, { onAppendCollision: fi_mergeFilterParams, onAppendEmpty: fi_setFilterParams, menuUrl: Menu.getItemUrl(Menu.findItem(mn_database, [".$fi['menuItem']."])) });\n"; - // $(document).ready(function(){ Menu.modifyUrl(Menu.findItem(mn_path, [1,5]), { filter: 'na=Malgayne'}, { onAppendCollision: fi_mergeFilterParams }) }); +if (isset($fiQuery) && count($fiMenuItem) > 1 && array_slice($fiMenuItem, 0, 2) == [1, 5]): + echo " \$(document).ready(function(){ Menu.modifyUrl(Menu.findItem(mn_path, ".Util::toJSON($fiMenuItem)."), { filter: '".$this->jsEscape($fiQuery)."'}, { onAppendCollision: fi_mergeFilterParams }) });\n"; +elseif (isset($fiQuery)): + echo " Menu.modifyUrl(Menu.findItem(mn_database, ".Util::toJSON($fiMenuItem)."), { filter: '+=".Util::jsEscape($fiQuery)."' }, { onAppendCollision: fi_mergeFilterParams, onAppendEmpty: fi_setFilterParams, menuUrl: Menu.getItemUrl(Menu.findItem(mn_database, ".Util::toJSON($fiMenuItem).")) });\n"; endif; ?> //]]> diff --git a/template/pages/achievements.tpl.php b/template/pages/achievements.tpl.php index b521146a..500b7ac5 100644 --- a/template/pages/achievements.tpl.php +++ b/template/pages/achievements.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,10 +12,10 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 9]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [9]]); ?> -
+
@@ -65,7 +65,7 @@ endforeach;
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?> diff --git a/template/pages/areatriggers.tpl.php b/template/pages/areatriggers.tpl.php index c45cb460..5709d0e3 100644 --- a/template/pages/areatriggers.tpl.php +++ b/template/pages/areatriggers.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,10 +12,10 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 101]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [101]]); ?>

name; ?> brick('redButtons'); ?>

-
+
@@ -57,7 +57,7 @@ endforeach;
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?> diff --git a/template/pages/arena-teams.tpl.php b/template/pages/arena-teams.tpl.php index 165eee59..3bcdc325 100644 --- a/template/pages/arena-teams.tpl.php +++ b/template/pages/arena-teams.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,11 +12,11 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 2]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [2]]); # for some arcane reason a newline (\n) means, the first childNode is a text instead of the form for the following div ?> -
@@ -68,7 +68,7 @@ $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?> diff --git a/template/pages/enchantments.tpl.php b/template/pages/enchantments.tpl.php index 5d3fc766..a00c8bd8 100644 --- a/template/pages/enchantments.tpl.php +++ b/template/pages/enchantments.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,10 +12,10 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 101]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [101]]); ?> -
+
@@ -59,7 +59,7 @@ endforeach;
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?> diff --git a/template/pages/guilds.tpl.php b/template/pages/guilds.tpl.php index 2a51e176..edcac5f8 100644 --- a/template/pages/guilds.tpl.php +++ b/template/pages/guilds.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,11 +12,11 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 2]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [2]]); # for some arcane reason a newline (\n) means, the first childNode is a text instead of the form for the following div ?> -
@@ -63,7 +63,7 @@ $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?> diff --git a/template/pages/icons.tpl.php b/template/pages/icons.tpl.php index 076a0b1a..11f6c9c9 100644 --- a/template/pages/icons.tpl.php +++ b/template/pages/icons.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,10 +12,10 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 101]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [101]]); ?> -
+
@@ -45,7 +45,7 @@ $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?> diff --git a/template/pages/items.tpl.php b/template/pages/items.tpl.php index 8367ec29..7bcfd170 100644 --- a/template/pages/items.tpl.php +++ b/template/pages/items.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,10 +12,10 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 0]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [0]]); ?> -
+
@@ -31,15 +31,15 @@ endforeach;
slotList): ?>
- $str): + foreach ($this->slotList as $k => $str): echo ' \n"; endforeach; ?> @@ -48,15 +48,15 @@ if (!empty($f['slot'])): typeList): ?>
- $str): + foreach ($fthis->typeList as $k => $str): $selected = false; if (isset($f['ty']) && in_array($k, (array)$f['ty'])): $selected = true; @@ -186,7 +186,7 @@ endforeach;
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?> diff --git a/template/pages/itemsets.tpl.php b/template/pages/itemsets.tpl.php index 9ca451a8..4371e603 100644 --- a/template/pages/itemsets.tpl.php +++ b/template/pages/itemsets.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,10 +12,10 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 2]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [2]]); ?> -
+
@@ -106,7 +106,7 @@ endforeach;
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?> diff --git a/template/pages/npcs.tpl.php b/template/pages/npcs.tpl.php index f58cf8e0..cb732ed6 100644 --- a/template/pages/npcs.tpl.php +++ b/template/pages/npcs.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,10 +12,10 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 4]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [4]]); ?> -
+
@@ -98,7 +98,7 @@ endforeach;
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?> diff --git a/template/pages/objects.tpl.php b/template/pages/objects.tpl.php index 9428c3d6..ffc6f356 100644 --- a/template/pages/objects.tpl.php +++ b/template/pages/objects.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,10 +12,10 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 5]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [5]]); ?> -
+
@@ -39,7 +39,7 @@ $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?> diff --git a/template/pages/profiles.tpl.php b/template/pages/profiles.tpl.php index f9544a8e..cb34d113 100644 --- a/template/pages/profiles.tpl.php +++ b/template/pages/profiles.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,11 +12,11 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 2]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [2]]); # for some arcane reason a newline (\n) means, the first childNode is a text instead of the form for the following div ?> -
@@ -112,7 +112,7 @@ endforeach; ?>
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?> diff --git a/template/pages/quests.tpl.php b/template/pages/quests.tpl.php index 0e3b1bdf..ef5fca5b 100644 --- a/template/pages/quests.tpl.php +++ b/template/pages/quests.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,10 +12,10 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 3]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [3]]); ?> -
+
@@ -80,7 +80,7 @@ endforeach;
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?> diff --git a/template/pages/sounds.tpl.php b/template/pages/sounds.tpl.php index a439722d..e3676613 100644 --- a/template/pages/sounds.tpl.php +++ b/template/pages/sounds.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,10 +12,10 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 101]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [101]]); ?>

name; ?> brick('redButtons'); ?>

-
+
diff --git a/template/pages/spells.tpl.php b/template/pages/spells.tpl.php index 9351158f..627e3deb 100644 --- a/template/pages/spells.tpl.php +++ b/template/pages/spells.tpl.php @@ -2,7 +2,7 @@ brick('header'); -$f = $this->filter; // shorthand +$f = $this->filterObj->values // shorthand ?>
@@ -12,10 +12,10 @@ $f = $this->filter; // shorthand brick('announcement'); -$this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 1]]); +$this->brick('pageTemplate', ['fiQuery' => $this->filterObj->query, 'fiMenuItem' => [1]]); ?> -
+
@@ -146,7 +146,7 @@ endforeach;
-brick('filter', ['fi' => $f['initData']]); ?> +brick('filter'); ?> brick('lvTabs'); ?>
 />