['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkDomain'], 'new' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkEmptySet'] ); private $isCustom = false; private $profile = null; private $subject = null; private $rnItr = 0; private $powerTpl = '$WowheadPower.registerProfile(%s, %d, %s);'; public function __construct($pageCall, $pageParam) { if (!CFG_PROFILER_ENABLE) $this->error(); $params = array_map('urldecode', explode('.', $pageParam)); if ($params[0]) $params[0] = Profiler::urlize($params[0]); if (isset($params[1])) $params[1] = Profiler::urlize($params[1], true); parent::__construct($pageCall, $pageParam); // temp locale if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain']) Util::powerUseLocale($this->_get['domain']); if (count($params) == 1 && intval($params[0])) { // redundancy much? $this->subjectGUID = intval($params[0]); $this->profile = intval($params[0]); $this->isCustom = true; // until proven otherwise $this->subject = new LocalProfileList(array(['id', intval($params[0])])); if ($this->subject->error) $this->notFound(); if (!$this->subject->isVisibleToUser()) $this->notFound(); if (!$this->subject->isCustom()) header('Location: '.$this->subject->getProfileUrl(), true, 302); } else if (count($params) == 3) { $this->getSubjectFromUrl($pageParam); if (!$this->subjectName) $this->notFound(); // names MUST be ucFirst. Since we don't expect partial matches, search this way $this->profile = $params; // pending rename if (preg_match('/([^\-]+)-(\d+)/i', $this->subjectName, $m)) { $this->subjectName = $m[1]; $this->rnItr = $m[2]; } // 3 possibilities // 1) already synced to aowow if ($subject = DB::Aowow()->selectRow('SELECT id, realmGUID, cuFlags FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID IS NOT NULL AND name = ? AND renameItr = ?d', $this->realmId, Util::ucFirst($this->subjectName), $this->rnItr)) { $this->subjectGUID = $subject['id']; if ($subject['cuFlags'] & PROFILER_CU_NEEDS_RESYNC) { $this->handleIncompleteData($params, $subject['realmGUID']); return; } $this->subject = new LocalProfileList(array(['id', $subject['id']])); if ($this->subject->error) $this->notFound(); } // 2) not yet synced but exists on realm (and not a gm character) else if (!$this->rnItr && ($char = DB::Characters($this->realmId)->selectRow('SELECT c.guid AS realmGUID, c.name, c.race, c.class, c.level, c.gender, c.at_login, g.guildid AS guildGUID, IFNULL(g.name, "") AS guildName, IFNULL(gm.rank, 0) AS guildRank FROM characters c LEFT JOIN guild_member gm ON gm.guid = c.guid LEFT JOIN guild g ON g.guildid = gm.guildid WHERE c.name = ? AND level <= ?d AND (extra_flags & ?d) = 0', Util::ucFirst($this->subjectName), MAX_LEVEL, Profiler::CHAR_GMFLAGS))) { $char['realm'] = $this->realmId; $char['cuFlags'] = PROFILER_CU_NEEDS_RESYNC; if ($char['at_login'] & 0x1) $char['renameItr'] = DB::Aowow()->selectCell('SELECT MAX(renameItr) FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID IS NOT NULL AND name = ?', $this->realmId, $char['name']); if ($char['guildGUID']) { // create empty guild if nessecary to satisfy foreign keys $char['guild'] = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_guild WHERE realm = ?d AND realmGUID = ?d', $this->realmId, $char['guildGUID']); if (!$char['guild']) $char['guild'] = DB::Aowow()->query('INSERT INTO ?_profiler_guild (realm, realmGUID, cuFlags, name) VALUES (?d, ?d, ?d, ?)', $this->realmId, $char['guildGUID'], PROFILER_CU_NEEDS_RESYNC, $char['guildName']); } unset($char['guildGUID']); unset($char['guildName']); // create entry from realm with enough basic info to disply tooltips DB::Aowow()->query('REPLACE INTO ?_profiler_profiles (?#) VALUES (?a)', array_keys($char), array_values($char)); $this->subjectGUID = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID = ?d', $this->realmId, $char['realmGUID']); $this->handleIncompleteData($params, $char['realmGUID']); } // 3) does not exist at all else $this->notFound(); } else if (($params && $params[0]) || !$this->_get['new']) $this->notFound(); else if ($this->_get['new']) $this->mode = CACHE_TYPE_NONE; } protected function generateContent() { if ($this->doResync) return; // + .titles ? $this->addScript([JS_FILE, '?data=enchants.gems.glyphs.itemsets.pets.pet-talents.quick-excludes.realms.statistics.weight-presets.achievements&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]); // as demanded by the raid activity tracker $bossIds = array( /* Halion */ /* ruby */ 39863, /* Valanar, Lana'thel, Saurfang, Festergut, Deathwisper, Marrowgar, Putricide, Rotface, Sindragosa, Valithria, Lich King */ /* icc */ 37970, 37955, 37813, 36626, 36855, 36612, 36678, 36627, 36853, 36789, 36597, /* Jaraxxus, Anub'arak */ /* toc */ 34780, 34564, /* Onyxia */ /* ony */ 10184, /* Flame Levi, Ignis, Razorscale, XT-002, Kologarn, Auriaya, Freya, Hodir, Mimiron, Thorim, Vezaxx, Yogg, Algalon */ /* uld */ 33113, 33118, 33186, 33293, 32930, 33515, 32906, 32845, 33350, 32864, 33271, 33288, 32871, /* Anub, Faerlina, Maexxna, Noth, Heigan, Loatheb, Razuvious, Gothik, Patchwerk, Grobbulus, Gluth, Thaddius, Sapphiron, Kel'Thuzad */ /* nax */ 15956, 15953, 15952, 15954, 15936, 16011, 16061, 16060, 16028, 15931, 15932, 15928, 15989, 15990 ); $this->extendGlobalIds(Type::NPC, ...$bossIds); // dummy title from dungeon encounter foreach (Lang::profiler('encounterNames') as $id => $name) $this->extendGlobalData([Type::NPC => [$id => ['name_'.User::$localeString => $name]]]); } protected function generatePath() { } protected function generateTitle() { array_unshift($this->title, Util::ucFirst(Lang::game('profile'))); } protected function generateTooltip() { $id = $this->profile; if (!$this->isCustom) $id = "'".$this->profile[0].'.'.urlencode($this->profile[1]).'.'.urlencode($this->profile[2])."'"; $power = new StdClass(); if ($this->subject && !$this->subject->error && $this->subject->isVisibleToUser()) { $n = $this->subject->getField('name'); $l = $this->subject->getField('level'); $r = $this->subject->getField('race'); $c = $this->subject->getField('class'); $g = $this->subject->getField('gender'); if ($this->isCustom) $n .= Lang::profiler('customProfile'); else if ($_ = $this->subject->getField('title')) if ($title = (new TitleList(array(['id', $_])))->getField($g ? 'female' : 'male', true)) $n = sprintf($title, $n); $power->{'name_'.User::$localeString} = $n; $power->{'tooltip_'.User::$localeString} = $this->subject->renderTooltip(); $power->icon = '$$WH.g_getProfileIcon('.$r.', '.$c.', '.$g.', '.$l.', \''.$this->subject->getIcon().'\')'; } return sprintf($this->powerTpl, $id, User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER)); } public function display(string $override = ''): void { if ($this->mode != CACHE_TYPE_TOOLTIP) parent::display($override); // do not cache profile tooltips header(MIME_TYPE_JSON); die($this->generateTooltip()); } public function notFound(string $title = '', string $msg = '') : void { parent::notFound($title ?: Util::ucFirst(Lang::profiler('profiler')), $msg ?: Lang::profiler('notFound', 'profile')); } private function handleIncompleteData($params, $guid) { if ($this->mode == CACHE_TYPE_TOOLTIP) // enable tooltip display with basic data we just added { $this->subject = new LocalProfileList(array(['id', $this->subjectGUID]), ['sv' => $params[1]]); if ($this->subject->error) $this->notFound(); $this->profile = $params; } else // display empty page and queue status { $this->mode = CACHE_TYPE_NONE; // queue full fetch if ($newId = Profiler::scheduleResync(Type::PROFILE, $this->realmId, $guid)) { $this->doResync = ['profile', $newId]; $this->initialSync(); } else // todo: base info should have been created in __construct .. why are we here..? header('Location: ?profiles='.$params[0].'.'.$params[1].'&filter=na='.Util::ucFirst($this->subjectName).';ex=on'); } } } ?>