Template/Endpoints (Base)

* redo page render following the logic of:
      Response ─┬─> TextResponse ─> TextResponseImpl
                └─> TemplateResponse ─> TemplateResponseImpl
    * split up giant files, one per response path
    * caching becomes a trait, implemented where necessary
        * TextResponses (Ajax) can now be cached
    * make use of previously defined php classes for js objects
        * Tabs, Listview, Tooltip, Announcement, Markup, Book, ...
    * \Aowow\Template\PageTemplate is the new class to be cached
    * do not discard error messages generated after vars have been sent to template
      and store in session for display at a later time
    * implement tracking consent management
    * move logic out of template into their respective endpoints
This commit is contained in:
Sarjuuk 2025-07-27 04:48:48 +02:00
parent aeb84327d6
commit 226f521439
72 changed files with 2798 additions and 1973 deletions

View file

@ -1,7 +1,7 @@
<?php namespace Aowow; ?>
<?php
if (!empty($this->contribute)):
if ($this->contribute):
?>
<div class="clear"></div>
<div class="text">
@ -11,7 +11,7 @@ if (!empty($this->contribute)):
<div class="text" style="margin-right: 310px">
<div class="tabbed-contents" style="clear: none">
<?php
$this->localizedBrick('contrib');
$this->localizedBrick('contrib', ['coError' => $this->community['coError'], 'ssError' => $this->community['ssError'], 'viError' => $this->community['viError']]);
?>
</div>
</div>

View file

@ -1,33 +0,0 @@
<?php namespace Aowow; ?>
<script type="text/javascript">//<![CDATA[
<?php if (isset($this->region) && isset($this->realm)): ?>
pr_setRegionRealm($WH.ge('fi').firstChild, '<?=$this->region; ?>', '<?=$this->realm; ?>');
<?php if ($this->filterObj->values['ra']): ?>
pr_onChangeRace();
<?php
endif;
endif;
if ($this->filterObj->fiInit): // str: filter template (and init html form)
echo " fi_init('".$this->filterObj->fiInit."');\n";
elseif ($this->filterObj->fiType): // str: filter template (set without init)
echo " var fi_type = '".$this->filterObj->fiType."'\n";
endif;
if ($this->filterObj->fiSetCriteria):
echo ' fi_setCriteria('.mb_substr(Util::toJSON(array_values($this->filterObj->fiSetCriteria)), 1, -1).");\n";
endif;
if ($this->filterObj->fiSetWeights):
/*
nt: don't try to match provided weights on predefined weight sets (preselects preset from opt list and ..?)
ids: weights are encoded as ids, not by their js name and need conversion before use
stealth: the ub-selector (items filter) will not visually change (so what..?)
*/
echo ' fi_setWeights('.Util::toJSON($this->filterObj->fiSetWeights).", 0, 1, 1);\n";
endif;
if ($this->filterObj->fiExtraCols):
echo ' fi_extraCols = '.Util::toJSON(array_values(array_unique($this->filterObj->fiExtraCols))).";\n";
endif;
?>
//]]></script>

View file

@ -1,23 +1,27 @@
<?php namespace Aowow; ?>
<?php
namespace Aowow\Template;
use \Aowow\Lang;
?>
<div class="footer">
<?php
if (User::isInGroup(U_GROUP_EMPLOYEE) && ($this->time || isset($this->mysql) || $this->isCached)):
if ($this->pageStats):
echo " <table style=\"margin:auto;\">\n";
if (isset($this->mysql)):
echo ' <tr><td style="text-align:left;">'.Lang::main('numSQL') .'</td><td>'.$this->mysql['count']."</td></tr>\n";
echo ' <tr><td style="text-align:left;">'.Lang::main('timeSQL').'</td><td>'.Util::formatTime($this->mysql['time'] * 1000, true)."</td></tr>\n";
if ($x = $this->pageStats['sql']):
echo ' <tr><td style="text-align:left;">'.Lang::main('numSQL') .'</td><td>'.$x['count']."</td></tr>\n";
echo ' <tr><td style="text-align:left;">'.Lang::main('timeSQL').'</td><td>'.$x['time']."</td></tr>\n";
endif;
if ($this->time):
echo ' <tr><td style="text-align:left;">Page generated in</td><td>'.Util::formatTime($this->time * 1000, true)."</td></tr>\n";
if ($x = $this->pageStats['time']):
echo ' <tr><td style="text-align:left;">Page generated in</td><td>'.$x."</td></tr>\n";
endif;
if ($this->cacheLoaded && $this->cacheLoaded[0] == CACHE_MODE_FILECACHE):
echo " <tr><td style=\"text-align:left;\">reloaded from filecache</td><td>created".Lang::main('colon').date(Lang::main('dateFmtLong'), $this->cacheLoaded[1])."</td></tr>\n";
elseif ($this->cacheLoaded && $this->cacheLoaded[0] == CACHE_MODE_MEMCACHED):
echo " <tr><td style=\"text-align:left;\">reloaded from memcached</td><td>created".Lang::main('colon').date(Lang::main('dateFmtLong'), $this->cacheLoaded[1])."</td></tr>\n";
if ($this->pageStats['cache'] && $this->pageStats['cache'][0] == CACHE_MODE_FILECACHE):
echo " <tr><td style=\"text-align:left;\">Stored in filecache</td><td>".$this->pageStats['cache'][1]."</td></tr>\n";
elseif ($this->pageStats['cache'] && $this->pageStats['cache'][0] == CACHE_MODE_MEMCACHED):
echo " <tr><td style=\"text-align:left;\">Stored in Memcached</td><td>".$this->pageStats['cache'][1]."</td></tr>\n";
endif;
echo " </table>\n";
@ -33,15 +37,13 @@ endif;
<div id="noscript-text"><?=Lang::main('noJScript'); ?></div>
</noscript>
<script type="text/javascript">DomContentLoaded.now()</script>
<?php
if (Cfg::get('DEBUG') >= LOG_LEVEL_INFO && User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN)):
?>
<?=$this->localizedBrickIf($this->consentFooter, 'consent'); ?>
<?php if ($this->dbProfiles): ?>
<script type="text/javascript">
window.open("/", "SqlLog", "width=1800,height=200,top=100,left=100,status=no,location=no,toolbar=no,menubar=no").document.write('<?=DB::getProfiles();?>');
window.open("/", "SqlLog", "width=1800,height=200,top=100,left=100,status=no,location=no,toolbar=no,menubar=no")?.document?.write('<?=$this->dbProfiles;?>');
</script>
<?php
endif;
?>
<?php endif; ?>
</body>
</html>

View file

@ -1,61 +1,49 @@
<?php namespace Aowow; ?>
<?php
namespace Aowow\Template;
<title><?=Util::htmlEscape(implode(' - ', $this->title)); ?></title>
use \Aowow\Lang;
?>
<title><?=$this->concat('title', ' - '); ?></title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="SHORTCUT ICON" href="<?=Cfg::get('STATIC_URL'); ?>/images/logos/favicon.ico" />
<link rel="search" type="application/opensearchdescription+xml" href="<?=Cfg::get('STATIC_URL'); ?>/download/searchplugins/aowow.xml" title="Aowow" />
<?php if (isset($this->rss)): ?>
<link rel="alternate" type="application/rss+xml" title="<?=Util::htmlEscape(implode(' - ', $this->title)); ?>" href="<?=$this->rss; ?>"/>
<?php endif;
foreach ($this->css as [$type, $css]):
if ($type == SC_CSS_FILE):
echo ' <link rel="stylesheet" type="text/css" href="'.$css."\" />\n";
elseif ($type == SC_CSS_STRING):
echo ' <style type="text/css">'.$css."</style>\n";
endif;
endforeach;
?>
<link rel="SHORTCUT ICON" href="<?=$this->gStaticUrl; ?>/images/logos/favicon.ico" />
<link rel="search" type="application/opensearchdescription+xml" href="<?=$this->gStaticUrl; ?>/download/searchplugins/aowow.xml" title="<?=Lang::main('search');?>" />
<?=$this->renderArray('css', 4); ?>
<script type="text/javascript">
var g_serverTime = new Date('<?=date(Util::$dateFormatInternal); ?>');
var g_staticUrl = "<?=Cfg::get('STATIC_URL'); ?>";
var g_host = "<?=Cfg::get('HOST_URL'); ?>";
var g_serverTime = <?=$this->gServerTime; ?>;
var g_staticUrl = "<?=$this->gStaticUrl; ?>";
var g_host = "<?=$this->gHost; ?>";
<?php
if ($this->gDataKey):
echo " var g_dataKey = '".$_SESSION['dataKey']."'\n";
endif;
?>
</script>
<?=$this->renderArray('js', 4); ?>
<script type="text/javascript">
var g_user = <?=$this->json($this->user::getUserGlobal()); ?>;
<?php
foreach ($this->js as [$type, $js]):
if ($type == SC_JS_FILE):
echo ' <script type="text/javascript" src="'.$js."\"></script>\n";
elseif ($type == SC_JS_STRING):
echo ' <script type="text/javascript">'.$js."</script>\n";
endif;
endforeach;
?>
<script type="text/javascript">
var g_user = <?=Util::toJSON($this->gUser, JSON_UNESCAPED_UNICODE); ?>;
<?php
if ($this->gFavorites):
echo " g_favorites = ".Util::toJSON($this->gFavorites).";\n";
if ($fav = $this->user::getFavorites()):
echo " g_favorites = ".$this->json($fav).";\n";
endif;
?>
</script>
<?php
if (Cfg::get('ANALYTICS_USER')):
?>
<?php if ($this->analyticsTag): ?>
<script async src="https://www.googletagmanager.com/gtag/js?id=<?=$this->analyticsTag; ?>"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '<?=Cfg::get('ANALYTICS_USER'); ?>', 'auto');
ga('send', 'pageview');
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '<?=$this->analyticsTag; ?>');
</script>
<?php
endif;
if ($this->rss):
?>
<link rel="alternate" type="application/rss+xml" title="<?=$this->concat('title', ' - '); ?>" href="<?=$this->rss; ?>"/>
<?php
endif;
?>

View file

@ -1,7 +1,9 @@
<?php namespace Aowow; ?>
<?php
namespace Aowow\Template;
?>
<?php
if (!empty($this->headIcons)):
if ($this->headIcons):
foreach ($this->headIcons as $k => $v):
echo '<div id="h1-icon-'.$k."\" class=\"h1-icon\"></div>\n";
endforeach;
@ -9,7 +11,7 @@ if (!empty($this->headIcons)):
<script type="text/javascript">//<![CDATA[
<?php
foreach ($this->headIcons as $k => $v):
echo "\$WH.ge('h1-icon-".$k."').appendChild(Icon.create('".Util::jsEscape($v)."', 1));\n";
echo "\$WH.ge('h1-icon-".$k."').appendChild(Icon.create('".$this->escJS($v)."', 1));\n";
endforeach;
?>
//]]></script>

View file

@ -1,14 +1,15 @@
<?php namespace Aowow; ?>
<?php
namespace Aowow\Template;
?>
<!DOCTYPE html>
<html>
<head>
<?php $this->brick('head'); ?>
</head>
<body<?=(User::isPremium() ? ' class="premium-logo"' : null); ?>>
<body<?=($this->user::isPremium() ? ' class="premium-logo"' : ''); ?>>
<div id="layers"></div>
<?php if ($this->headerLogo): ?>
<?php if ($this->headerLogo): ?>
<style type="text/css">
.header-logo {
background: url(<?=$this->headerLogo; ?>) no-repeat center 0 !important;
@ -21,18 +22,18 @@
<div class="header" id="header">
<div id="header-logo">
<a class="header-logo" href="."></a>
<h1><?=Util::htmlEscape($this->name); ?></h1>
<h1><?=$this->concat('title', ' - '); ?></h1>
</div>
</div>
<div id="wrapper" class="wrapper">
<div class="toplinks linklist"><?php $this->brick('headerMenu'); ?></div>
<div class="toptabs" id="toptabs"></div>
<div class="topbar" id="topbar">
<div class="topbar-search"><form action="."><a href="javascript:;"></a><input name="search" size="35" id="livesearch-generic" value="<?=Util::htmlEscape($this->search ?? ''); ?>" /></form></div>
<div class="topbar-search"><form action="."><a href="javascript:;"></a><input name="search" size="35" id="livesearch-generic" value="<?=$this->search; ?>" /></form></div>
<div class="topbar-browse" id="topbar-browse"></div>
<div class="topbar-buttons" id="topbar-buttons"></div>
</div>
<script type="text/javascript">
<?=$this->writeGlobalVars(); ?>
<?=$this->renderGlobalVars(12); ?>
</script>

View file

@ -1,10 +1,14 @@
<?php namespace Aowow; ?>
<?php
namespace Aowow\Template;
use \Aowow\Lang;
?>
<?php
if (User::isLoggedIn()):
if ($this->user::isLoggedIn()):
echo '<span id="toplinks-favorites"><a class="hassubmenu"></a>|</span>';
echo '<a id="toplinks-user">'.User::$username.'</a>';
echo '<span id="toplinks-rep" title="'.Lang::main('reputationTip').'">(<a href="?reputation">'.User::getReputation().'</a>)</span>';
echo '<a id="toplinks-user">'.$this->user::$username.'</a>';
echo '<span id="toplinks-rep" title="'.Lang::main('reputationTip').'">(<a href="?reputation">'.$this->user::getReputation().'</a>)</span>';
else:
echo '<a href="?account=signin">'.Lang::main('signIn').'</a>';
endif;

View file

@ -0,0 +1,16 @@
<?php
namespace Aowow\Template;
use \Aowow\Lang;
?>
<div class="pad3"></div>
<div class="inputbox">
<h1><?=$head ?? ''; ?></h1>
<div id="inputbox-error"><?=$error ?? ''; ?></div>
<?php if ($message ?? ''): ?>
<div style="text-align: center; font-size: 110%"><?=$message; ?></div>
<?php else: ?>
<div class="clear"></div>
<?php endif; ?>
</div>

View file

@ -1,40 +1,40 @@
<?php namespace Aowow; ?>
<?php
namespace Aowow\Template;
?>
<script type="text/javascript">//<![CDATA[
<?php
if ($this->contribute & CONTRIBUTE_CO):
echo " var lv_comments = ".Util::toJSON($this->community['co']).";\n";
echo " var lv_comments = ".$this->community['co'].";\n";
endif;
if ($this->contribute & CONTRIBUTE_SS):
echo " var lv_screenshots = ".Util::toJSON($this->community['ss']).";\n";
echo " var lv_screenshots = ".$this->community['ss'].";\n";
endif;
if ($this->contribute & CONTRIBUTE_VI):
echo " var lv_videos = ".Util::toJSON($this->community['vi']).";\n";
echo " var lv_videos = ".$this->community['vi'].";\n";
endif;
if (!empty($this->gPageInfo)):
echo " var g_pageInfo = ".Util::toJSON($this->gPageInfo).";\n";
if ($this->gPageInfo):
echo " var g_pageInfo = ".$this->json('gPageInfo').";\n";
// only used by item.php
if (User::isLoggedIn() && isset($this->redButtons[BUTTON_EQUIP])):
echo " DomContentLoaded.addEvent(function() { pr_addEquipButton('equip-pinned-button', ".$this->typeId."); });\n";
// set by ItemBaseEndpoint
if ($this->user::isLoggedIn() && !empty($this->redButtons[BUTTON_EQUIP])):
echo " \$(document).ready(function() { pr_addEquipButton('equip-pinned-button', ".$this->typeId."); });\n";
endif;
endif;
if (!empty($this->pageTemplate)):
if (Lang::getLocale()->value && $this->pageTemplate['pageName'] != 'home'):
echo " Locale.set(".Lang::getLocale()->value.");\n";
if ($this->pageTemplate):
if ($this->locale->value && $this->pageTemplate['pageName'] != 'home'):
echo " Locale.set(".$this->locale->value.");\n";
endif;
echo " PageTemplate.set(".Util::toJSON($this->pageTemplate).");\n";
echo " PageTemplate.set(".$this->json('pageTemplate').");\n";
endif;
echo " PageTemplate.init();\n";
endif;
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";
echo " \$(document).ready(function(){ Menu.modifyUrl(Menu.findItem(mn_path, ".$this->json($fiMenuItem)."), { filter: '".$this->escJS($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";
echo " Menu.modifyUrl(Menu.findItem(mn_database, ".$this->json($fiMenuItem)."), { filter: '+=".$this->escJS($fiQuery)."' }, { onAppendCollision: fi_mergeFilterParams, onAppendEmpty: fi_setFilterParams, menuUrl: Menu.getItemUrl(Menu.findItem(mn_database, ".$this->json($fiMenuItem).")) });\n";
endif;
?>
//]]></script>

View file

@ -1,4 +1,8 @@
<?php namespace Aowow; ?>
<?php
namespace Aowow\Template;
use \Aowow\Lang;
?>
<?php
// link to wowhead
@ -18,7 +22,7 @@ endif;
// ingame-links/markdown/ect
if (isset($this->redButtons[BUTTON_LINKS])):
if ($b = $this->redButtons[BUTTON_LINKS]):
echo '<a href="javascript:;" id="open-links-button" class="button-red" onclick="this.blur(); Links.show('.strtr(Util::toJSON($b, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE | JSON_HEX_APOS), ['"' => "'"]).');"><em><b><i>'.Lang::main('links').'</i></b><span>'.Lang::main('links').'</span></em></a>';
echo '<a href="javascript:;" id="open-links-button" class="button-red" onclick="this.blur(); Links.show('.strtr($this->json($b, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE | JSON_HEX_APOS), ['"' => "'"]).');"><em><b><i>'.Lang::main('links').'</i></b><span>'.Lang::main('links').'</span></em></a>';
else:
echo '<a href="javascript:;" id="open-links-button" class="button-red button-red-disabled"><em><b><i>'.Lang::main('links').'</i></b><span>'.Lang::main('links').'</span></em></a>';
endif;
@ -27,7 +31,7 @@ endif;
// view in 3D
if (isset($this->redButtons[BUTTON_VIEW3D])):
if ($b = $this->redButtons[BUTTON_VIEW3D]): // json_encode puts property names in brackets wich is not cool with inline javascript
echo '<a href="javascript:;" id="view3D-button" class="button-red" onclick="this.blur(); ModelViewer.show('.strtr(Util::toJSON($b, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE | JSON_HEX_APOS), ['"' => "'"]).')"><em><b><i>'.Lang::main('view3D').'</i></b><span>'.Lang::main('view3D').'</span></em></a>';
echo '<a href="javascript:;" id="view3D-button" class="button-red" onclick="this.blur(); ModelViewer.show('.strtr($this->json($b, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE | JSON_HEX_APOS), ['"' => "'"]).')"><em><b><i>'.Lang::main('view3D').'</i></b><span>'.Lang::main('view3D').'</span></em></a>';
else:
echo '<a href="javascript:;" id="view3D-button" class="button-red button-red-disabled"><em><b><i>'.Lang::main('view3D').'</i></b><span>'.Lang::main('view3D').'</span></em></a>';
endif;
@ -36,7 +40,7 @@ endif;
// item comparison tool
if (isset($this->redButtons[BUTTON_COMPARE])):
if ($b = $this->redButtons[BUTTON_COMPARE]):
echo '<a href="javascript:;" class="button-red" onclick="this.blur(); su_addToSaved(\''.(isset($b['eqList']) ? $b['eqList'] : $this->typeId).'\', '.(isset($b['qty']) ? $b['qty'] : 1).')"><em><b><i>'.Lang::main('compare').'</i></b><span>'.Lang::main('compare').'</span></em></a>';
echo '<a href="javascript:;" class="button-red" onclick="this.blur(); su_addToSaved(\''.($b['eqList'] ?? $this->typeId).'\', '.($b['qty'] ?? 1).')"><em><b><i>'.Lang::main('compare').'</i></b><span>'.Lang::main('compare').'</span></em></a>';
else:
echo '<a href="javascript:;" class="button-red button-red-disabled"><em><b><i>'.Lang::main('compare').'</i></b><span>'.Lang::main('compare').'</span></em></a>';
endif;