mode, $this->type, $this->typeId, $staff, User::$localeId, '-1', '-1']; // item special: can modify tooltips if (isset($this->enhancedTT)) $key[] = md5(serialize($this->enhancedTT)); return implode('_', $key); } protected function applyCCErrors() : void { if (!empty($_SESSION['error']['co'])) $this->coError = $_SESSION['error']['co']; if (!empty($_SESSION['error']['ss'])) $this->ssError = $_SESSION['error']['ss']; if (!empty($_SESSION['error']['vi'])) $this->viError = $_SESSION['error']['vi']; unset($_SESSION['error']); } } trait TrListPage { protected $category = null; protected $filter = []; protected $lvTabs = []; // most pages have this private $filterObj = null; protected function generateCacheKey(bool $withStaff = true) : string { $staff = intVal($withStaff && User::isInGroup(U_GROUP_EMPLOYEE)); // mode, type, typeId, employee-flag, localeId, $key = [$this->mode, $this->type, '-1', $staff, User::$localeId]; //category $key[] = $this->category ? implode('.', $this->category) : '-1'; // filter $key[] = $this->filterObj ? md5(serialize($this->filterObj)) : '-1'; return implode('_', $key); } } trait TrProfiler { protected $region = ''; protected $realm = ''; protected $realmId = 0; protected $battlegroup = ''; // not implemented, since no pserver supports it protected $subjectName = ''; protected $subjectGUID = 0; protected $sumSubjects = 0; protected $doResync = null; protected function generateCacheKey(bool $withStaff = true) : string { $staff = intVal($withStaff && User::isInGroup(U_GROUP_EMPLOYEE)); // mode, type, typeId, employee-flag, localeId, category, filter $key = [$this->mode, $this->type, $this->subject->getField('id'), $staff, User::$localeId, '-1', '-1']; return implode('_', $key); } protected function getSubjectFromUrl(string $pageParam) : void { if (!$pageParam) return; // cat[0] is always region // cat[1] is realm or bGroup (must be realm if cat[2] is set) // cat[2] is arena-team, guild or player $cat = explode('.', $pageParam, 3); $cat = array_map('urldecode', $cat); if (array_search($cat[0], Util::$regions) === false) return; $this->region = $cat[0]; // if ($cat[1] == Profiler::urlize(CFG_BATTLEGROUP)) // $this->battlegroup = CFG_BATTLEGROUP; if (isset($cat[1])) { foreach (Profiler::getRealms() as $rId => $r) { if (Profiler::urlize($r['name'], true) == $cat[1]) { $this->realm = $r['name']; $this->realmId = $rId; if (isset($cat[2]) && mb_strlen($cat[2]) >= 2) $this->subjectName = $cat[2]; // cannot reconstruct original name from urlized form; match against special name field break; } } } } protected function initialSync() : void { $this->prepareContent(); $this->hasComContent = false; $this->notFound = array( 'title' => sprintf(Lang::profiler('firstUseTitle'), $this->subjectName, $this->realm), 'msg' => '' ); if (isset($this->tabId)) $this->pageTemplate['activeTab'] = $this->tabId; $this->sumSQLStats(); $this->display('text-page-generic'); exit(); } protected function generatePath() : void { if ($this->region) { $this->path[] = $this->region; if ($this->realm) $this->path[] = Profiler::urlize($this->realm, true); // else // $this->path[] = Profiler::urlize(CFG_BATTLEGROUP); } } } class GenericPage { protected $tpl = ''; protected $reqUGroup = U_GROUP_NONE; protected $reqAuth = false; protected $mode = CACHE_TYPE_NONE; protected $jsGlobals = []; protected $lvData = []; protected $title = [CFG_NAME]; // for title-Element protected $name = ''; // for h1-Element protected $tabId = null; protected $gDataKey = false; // adds the dataKey to the user vars protected $js = []; protected $css = []; // private vars don't get cached private $time = 0; private $cacheDir = 'cache/template/'; private $jsgBuffer = []; private $gPageInfo = []; private $gUser = []; private $pageTemplate = []; private $community = ['co' => [], 'sc' => [], 'vi' => []]; private $cacheLoaded = []; private $skipCache = 0x0; private $memcached = null; private $mysql = ['time' => 0, 'count' => 0]; private $headerLogo = ''; private $fullParams = ''; private $lvTemplates = array( 'achievement' => ['template' => 'achievement', 'id' => 'achievements', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_achievements' ], 'areatrigger' => ['template' => 'areatrigger', 'id' => 'areatrigger', 'parent' => 'lv-generic', 'data' => [], ], 'calendar' => ['template' => 'holidaycal', 'id' => 'calendar', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_calendar' ], 'class' => ['template' => 'classs', 'id' => 'classes', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_classes' ], 'commentpreview' => ['template' => 'commentpreview', 'id' => 'comments', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_comments' ], 'creature' => ['template' => 'npc', 'id' => 'npcs', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_npcs' ], 'currency' => ['template' => 'currency', 'id' => 'currencies', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_currencies' ], 'emote' => ['template' => 'emote', 'id' => 'emotes', 'parent' => 'lv-generic', 'data' => [] ], 'enchantment' => ['template' => 'enchantment', 'id' => 'enchantments', 'parent' => 'lv-generic', 'data' => [] ], 'event' => ['template' => 'holiday', 'id' => 'holidays', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_holidays' ], 'faction' => ['template' => 'faction', 'id' => 'factions', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_factions' ], 'genericmodel' => ['template' => 'genericmodel', 'id' => 'same-model-as', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_samemodelas' ], 'icongallery' => ['template' => 'icongallery', 'id' => 'icons', 'parent' => 'lv-generic', 'data' => [] ], 'item' => ['template' => 'item', 'id' => 'items', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_items' ], 'itemset' => ['template' => 'itemset', 'id' => 'itemsets', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_itemsets' ], 'mail' => ['template' => 'mail', 'id' => 'mails', 'parent' => 'lv-generic', 'data' => [] ], 'model' => ['template' => 'model', 'id' => 'gallery', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_gallery' ], 'object' => ['template' => 'object', 'id' => 'objects', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_objects' ], 'pet' => ['template' => 'pet', 'id' => 'hunter-pets', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_pets' ], 'profile' => ['template' => 'profile', 'id' => 'profiles', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_profiles' ], 'quest' => ['template' => 'quest', 'id' => 'quests', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_quests' ], 'race' => ['template' => 'race', 'id' => 'races', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_races' ], 'replypreview' => ['template' => 'replypreview', 'id' => 'comment-replies', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_commentreplies'], 'reputationhistory' => ['template' => 'reputationhistory', 'id' => 'reputation', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_reputation' ], 'screenshot' => ['template' => 'screenshot', 'id' => 'screenshots', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_screenshots' ], 'skill' => ['template' => 'skill', 'id' => 'skills', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_skills' ], 'sound' => ['template' => 'sound', 'id' => 'sounds', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.types[19][2]' ], 'spell' => ['template' => 'spell', 'id' => 'spells', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_spells' ], 'title' => ['template' => 'title', 'id' => 'titles', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_titles' ], 'topusers' => ['template' => 'topusers', 'id' => 'topusers', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.topusers' ], 'video' => ['template' => 'video', 'id' => 'videos', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_videos' ], 'zone' => ['template' => 'zone', 'id' => 'zones', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_zones' ] ); public function __construct(string $pageCall = '', string $pageParam = '') { $this->time = microtime(true); $this->fullParams = $pageCall; if ($pageParam) $this->fullParams .= '='.$pageParam; if (CFG_CACHE_DIR && Util::writeDir(CFG_CACHE_DIR)) $this->cacheDir = mb_substr(CFG_CACHE_DIR, -1) != '/' ? CFG_CACHE_DIR.'/' : CFG_CACHE_DIR; // force page refresh if (isset($_GET['refresh']) && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_DEV)) { if ($_GET['refresh'] == 'filecache') $this->skipCache = CACHE_MODE_FILECACHE; else if ($_GET['refresh'] == 'memcached') $this->skipCache = CACHE_MODE_MEMCACHED; else if ($_GET['refresh'] == '') $this->skipCache = CACHE_MODE_FILECACHE | CACHE_MODE_MEMCACHED; } // display modes if (isset($_GET['power']) && method_exists($this, 'generateTooltip')) $this->mode = CACHE_TYPE_TOOLTIP; else if (isset($_GET['xml']) && method_exists($this, 'generateXML')) $this->mode = CACHE_TYPE_XML; else { // get alt header logo if ($ahl = DB::Aowow()->selectCell('SELECT altHeaderLogo FROM ?_home_featuredbox WHERE ?d BETWEEN startDate AND endDate ORDER BY id DESC', time())) $this->headerLogo = Util::defStatic($ahl); $this->gUser = User::getUserGlobals(); $this->gFavorites = User::getFavorites(); $this->pageTemplate['pageName'] = strtolower($pageCall); if (!$this->isValidPage()) $this->error(); } // requires authed user if ($this->reqAuth && !User::$id) $this->forwardToSignIn($_SERVER['QUERY_STRING'] ?? ''); // restricted access if ($this->reqUGroup && !User::isInGroup($this->reqUGroup)) { if (User::$id) $this->error(); else $this->forwardToSignIn($_SERVER['QUERY_STRING'] ?? ''); } if (CFG_MAINTENANCE && !User::isInGroup(U_GROUP_EMPLOYEE)) $this->maintenance(); else if (CFG_MAINTENANCE && User::isInGroup(U_GROUP_EMPLOYEE)) Util::addNote(U_GROUP_EMPLOYEE, 'Maintenance mode enabled!'); // get errors from previous page from session and apply to template if (method_exists($this, 'applyCCErrors')) $this->applyCCErrors(); } /**********/ /* Checks */ /**********/ // "template_exists" private function isSaneInclude(string $path, string $file) : bool { if (preg_match('/[^\w\-]/i', str_replace('admin/', '', $file))) return false; if (!is_file($path.$file.'.tpl.php')) return false; return true; } // has a valid combination of categories private function isValidPage() : bool { if (!isset($this->category) || empty($this->validCats)) return true; $c = $this->category; // shorthand switch (count($c)) { case 0: // no params works always return true; case 1: // null is valid || value in a 1-dim-array || (key for a n-dim-array && ( has more subcats || no further subCats )) $filtered = array_filter($this->validCats, function ($x) { return is_int($x); }); return $c[0] === null || in_array($c[0], $filtered) || (!empty($this->validCats[$c[0]]) && (is_array($this->validCats[$c[0]]) || $this->validCats[$c[0]] === true)); case 2: // first param has to be a key. otherwise invalid if (!isset($this->validCats[$c[0]])) return false; // check if the sub-array is n-imensional if (is_array($this->validCats[$c[0]]) && count($this->validCats[$c[0]]) == count($this->validCats[$c[0]], COUNT_RECURSIVE)) return in_array($c[1], $this->validCats[$c[0]]); // second param is value in second level array else return isset($this->validCats[$c[0]][$c[1]]); // check if params is key of another array case 3: // 3 params MUST point to a specific value return isset($this->validCats[$c[0]][$c[1]]) && in_array($c[2], $this->validCats[$c[0]][$c[1]]); } return false; } /****************/ /* Prepare Page */ /****************/ // get from cache ?: run generators protected function prepareContent() : void { if (!$this->loadCache()) { $this->addArticle(); $this->generateContent(); $this->generatePath(); $this->generateTitle(); $this->applyGlobals(); $this->saveCache(); } if (isset($this->type) && isset($this->typeId)) { $this->gPageInfo = array( // varies slightly for special pages like maps, user-dashboard or profiler 'type' => $this->type, 'typeId' => $this->typeId, 'name' => $this->name ); } else if (!empty($this->articleUrl)) { $this->gPageInfo = array( 'articleUrl' => $this->fullParams, // is actually be the url-param 'editAccess' => isset($this->editAccess) ? $this->editAccess : (U_GROUP_ADMIN | U_GROUP_EDITOR | U_GROUP_BUREAU) ); } if (!empty($this->path)) $this->pageTemplate['breadcrumb'] = $this->path; if (!empty($this->filter)) $this->pageTemplate['filter'] = empty($this->filter['query']) ? 0 : 1; if (method_exists($this, 'postCache')) // e.g. update dates for events and such $this->postCache(); // determine contribute tabs if (isset($this->subject) && !isset($this->contribute)) { $x = get_class($this->subject); $this->contribute = $x::$contribute; } if (!empty($this->hasComContent)) // get comments, screenshots, videos { $jsGlobals = []; $this->community = CommunityContent::getAll($this->type, $this->typeId, $jsGlobals); $this->extendGlobalData($jsGlobals); // as comments are not cached, those globals cant be either $this->applyGlobals(); } $this->time = microtime(true) - $this->time; $this->sumSQLStats(); } public function addJS($name, bool $unshift = false) : void { if (is_array($name)) { foreach ($name as $n) $this->addJS($n, $unshift); } else if (!in_array($name, $this->js)) { if ($unshift) array_unshift($this->js, $name); else $this->js[] = $name; } } public function addCSS(array $struct, bool $unshift = false) : void { if (is_array($struct) && empty($struct['path']) && empty($struct['string'])) { foreach ($struct as $s) $this->addCSS($s, $unshift); } else if (!in_array($struct, $this->css)) { if ($unshift) array_unshift($this->css, $struct); else $this->css[] = $struct; } } // get article & static infobox (run before processing jsGlobals) private function addArticle() :void { $article = []; if (!empty($this->type) && isset($this->typeId)) { $article = DB::Aowow()->selectRow('SELECT article, quickInfo, locale, editAccess FROM ?_articles WHERE type = ?d AND typeId = ?d AND locale = ?d UNION ALL SELECT article, quickInfo, locale, editAccess FROM ?_articles WHERE type = ?d AND typeId = ?d AND locale = 0 ORDER BY locale DESC LIMIT 1', $this->type, $this->typeId, User::$localeId, $this->type, $this->typeId ); } else if (!empty($this->articleUrl)) { $article = DB::Aowow()->selectRow('SELECT article, quickInfo, locale, editAccess FROM ?_articles WHERE url = ? AND locale = ?d UNION ALL SELECT article, quickInfo, locale, editAccess FROM ?_articles WHERE url = ? AND locale = 0 ORDER BY locale DESC LIMIT 1', $this->articleUrl, User::$localeId, $this->articleUrl ); } if ($article) { if ($article['article']) (new Markup($article['article']))->parseGlobalsFromText($this->jsgBuffer); if ($article['quickInfo']) (new Markup($article['quickInfo']))->parseGlobalsFromText($this->jsgBuffer); $this->article = array( 'text' => Util::jsEscape(Util::defStatic($article['article'])), 'params' => [] ); if (!empty($this->type) && isset($this->typeId)) $this->article['params']['dbpage'] = true; // convert U_GROUP_* to MARKUP.CLASS_* (as seen in js-object Markup) if($article['editAccess'] & (U_GROUP_ADMIN | U_GROUP_VIP | U_GROUP_DEV)) $this->article['params']['allow'] = '$Markup.CLASS_ADMIN'; else if($article['editAccess'] & U_GROUP_STAFF) $this->article['params']['allow'] = '$Markup.CLASS_STAFF'; else if($article['editAccess'] & U_GROUP_PREMIUM) $this->article['params']['allow'] = '$Markup.CLASS_PREMIUM'; else if($article['editAccess'] & U_GROUP_PENDING) $this->article['params']['allow'] = '$Markup.CLASS_PENDING'; else $this->article['params']['allow'] = '$Markup.CLASS_USER'; $this->editAccess = $article['editAccess']; if (empty($this->infobox) && !empty($article['quickInfo'])) $this->infobox = $article['quickInfo']; if ($article['locale'] != User::$localeId) $this->article['params']['prepend'] = '