[FILTER_CALLBACK, ['options' => 'AjaxProfile::checkId']], // 'items' => [FILTER_CALLBACK, ['options' => 'AjaxProfile::checkItems']], 'size' => [FILTER_SANITIZE_STRING, 0xC], // FILTER_FLAG_STRIP_LOW | *_HIGH ); public function __construct(array $params) { parent::__construct($params); if (!$this->params) return; switch ($this->params[0]) { case 'link': case 'unlink': $this->handler = 'handleLink'; // always returns null break; case 'pin': case 'unpin': $this->handler = 'handlePin'; // always returns null break; case 'public': case 'private': $this->handler = 'handlePrivacy'; // always returns null break; case 'avatar': $this->handler = 'handleAvatar'; // sets an image header break; // so it has to die here or another header will be set case 'resync': case 'status': $this->handler = 'handleResync'; break; case 'save': $this->handler = 'handleSave'; break; case 'delete': $this->handler = 'handleDelete'; break; case 'purge': $this->handler = 'handlePurge'; break; case 'summary': // page is generated by jScript die(); // just be empty case 'load': $this->handler = 'handleLoad'; break; } } protected function handleLink($id, $mode) // links char with account { /* params id: user: [optional] return: null */ } protected function handlePin($id, $mode) // (un)favorite { /* params id: user: [optional] return: null */ } protected function handlePrivacy($id, $mode) // public visibility { /* params id: user: [optional] return: null */ } protected function handleAvatar() // image { // something happened in the last years: those textures do not include tiny icons $sizes = [/* 'tiny' => 15, */'small' => 18, 'medium' => 36, 'large' => 56]; $aPath = 'uploads/avatars/%d.jpg'; $s = $this->_get['size'] ?: 'medium'; if (!$this->_get['id'] || !preg_match('/^([0-9]+)\.(jpg|gif)$/', $this->_get['id'][0], $matches) || !in_array($s, array_keys($sizes))) return; $this->contentType = 'image/'.$matches[2]; $id = $matches[1]; $dest = imageCreateTruecolor($sizes[$s], $sizes[$s]); if (file_exists(sprintf($aPath, $id))) { $offsetX = $offsetY = 0; switch ($s) { case 'tiny': $offsetX += $sizes['small']; case 'small': $offsetY += $sizes['medium']; case 'medium': $offsetX += $sizes['large']; } $src = imageCreateFromJpeg(printf($aPath, $id)); imagecopymerge($dest, $src, 0, 0, $offsetX, $offsetY, $sizes[$s], $sizes[$s], 100); } if ($matches[2] == 'gif') imageGif($dest); else imageJpeg($dest); return; } protected function handleResync() // resync init and status requests { /* params id: user: [optional] return null [onOK] int or str [onError] */ if ($this->params[0] == 'resync') return '1'; else // $this->params[0] == 'status' { /* not all fields are required, if zero they are omitted statusCode: 0: end the request 1: waiting 2: working... 3: ready; click to view 4: error / retry errorCode: 0: unk error 1: char does not exist 2: armory gone [ processId, [StatusCode, timeToRefresh, iCount, errorCode, iNResyncs], []... ] */ return '[0, [4, 10000, 1, 2]]'; } } protected function handleSave() // unKill a profile { /* params GET id: params POST name, level, class, race, gender, nomodel, talenttree1, talenttree2, talenttree3, activespec, talentbuild1, glyphs1, talentbuild2, glyphs2, gearscore, icon, public [always] description, source, copy, inv { inventory: array containing itemLinks } [optional] } return int > 0 [profileId, if we came from an armoryProfile create a new one] int < 0 [onError] str [onError] */ return 'NYI'; } protected function handleDelete() // kill a profile { /* params id: return null */ return 'NYI'; } protected function handlePurge() // removes certain saved information but not the entire character { /* params id: data: [string, tabName?] return null */ return 'NYI'; } protected function handleLoad() { /* params id: profileId items: string [itemIds.join(':')] unnamed: unixtime [only to force the browser to reload instead of cache] return lots... */ // titles, achievements, characterData, talents (, pets) // and some onLoad-hook to .. load it registerProfile($data) // everything else goes through data.php .. strangely enough if (!$this->_get['id']) return; $char = new ProfileList(array(['id', $this->_get['id'][0]])); // or string or whatever $buff = ''; if ($it = array_column($char->getField('inventory'), 0)) { $itemz = new ItemList(array(['id', $it, CFG_SQL_LIMIT_NONE])); $data = $itemz->getListviewData(ITEMINFO_JSON | ITEMINFO_SUBITEMS); // get and apply inventory foreach ($itemz->iterate() as $iId => $__) $buff .= 'g_items.add('.$iId.', {name_'.User::$localeString.":'".Util::jsEscape($itemz->getField('name', true))."', quality:".$itemz->getField('quality').", icon:'".$itemz->getField('iconString')."', jsonequip:".Util::toJSON($data[$iId])."});\n"; $buff .= "\n"; } if ($au = $char->getField('auras')) { $auraz = new SpellList(array(['id', $char->getField('auras')], CFG_SQL_LIMIT_NONE)); $dataz = $auraz->getListviewData(); $modz = $auraz->getProfilerMods(); // get and apply aura-mods foreach ($dataz as $id => $data) { $mods = []; if (!empty($modz[$id])) { foreach ($modz[$id] as $k => $v) { if (is_array($v)) $mods[] = $v; else if ($str = @Game::$itemMods[$k]) $mods[$str] = $v; } } $buff .= 'g_spells.add('.$id.", {id:".$id.", name:'".Util::jsEscape(mb_substr($data['name'], 1))."', icon:'".$data['icon']."', modifier:".Util::toJSON($mods)."});\n"; } $buff .= "\n"; } /* depending on progress-achievements // required by progress in JScript move to handleLoad()? Util::$pageTemplate->extendGlobalIds(TYPE_NPC, [29120, 31134, 29306, 29311, 23980, 27656, 26861, 26723, 28923, 15991]); */ // load available titles Util::loadStaticFile('p-titles-'.$char->getField('gender'), $buff, true); // load available achievements if (!Util::loadStaticFile('p-achievements', $buff, true)) { $buff .= "\n\ng_achievement_catorder = [];"; $buff .= "\n\ng_achievement_points = [0];"; } // excludes; structure UNK type => [maskBit => [typeIds]] ? /* g_user.excludes = [type:[typeIds]] g_user.includes = [type:[typeIds]] g_user.excludegroups = groupMask // requires g_user.settings != null maskBit are matched against fieldId from excludeGroups id: 1, label: LANG.dialog_notavail id: 2, label: LANG.dialog_tcg id: 4, label: LANG.dialog_collector id: 8, label: LANG.dialog_promo id: 16, label: LANG.dialog_nonus id: 96, label: LANG.dialog_faction id: 896, label: LANG.dialog_profession id: 1024, label: LANG.dialog_noexalted */ // $buff .= "\n\ng_excludes = {};"; // add profile to buffer $buff .= "\n\n\$WowheadProfiler.registerProfile(".Util::toJSON($char->getEntry(2)).");"; // can't use JSON_NUMERIC_CHECK or the talent-string becomes a float return $buff."\n"; } protected function checkId($val) { // expecting id-list if (preg_match('/\d+(,\d+)*/', $val)) return array_map('intVal', explode(',', $val)); return null; } protected function checkItems($val) { // expecting item-list if (preg_match('/\d+(:\d+)*/', $val)) return array_map('intVal', explode(': ', $val)); return null; } } ?>