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,71 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxHandler
{
use TrRequestData;
protected $validParams = [];
protected $params = [];
protected $handler;
protected $contentType = MIME_TYPE_JSON;
public $doRedirect = false;
public function __construct(array $params)
{
$this->params = $params;
$this->initRequestData();
}
public function handle(string &$out) : bool
{
if (!$this->handler)
return false;
if ($this->validParams)
{
if (count($this->params) != 1)
return false;
if (!in_array($this->params[0], $this->validParams))
return false;
}
$out = $this->{$this->handler}() ?? '';
return true;
}
public function getContentType() : string
{
return $this->contentType;
}
protected function reqPOST(string ...$keys) : bool
{
foreach ($keys as $k)
if (!isset($this->_post[$k]) || $this->_post[$k] === null || $this->_post[$k] === '')
return false;
return true;
}
protected function reqGET(string ...$keys) : bool
{
foreach ($keys as $k)
if (!isset($this->_get[$k]) || $this->_get[$k] === null || $this->_get[$k] === '')
return false;
return true;
}
}
?>

View File

@@ -632,10 +632,7 @@ trait spawnHelper
$wpSum = []; $wpSum = [];
$wpIdx = 0; $wpIdx = 0;
$worldPos = []; $worldPos = [];
$spawns = DB::Aowow()->select("SELECT * FROM ?_spawns WHERE `type` = ?d AND `typeId` IN (?a) AND `posX` > 0 AND `posY` > 0", self::$type, $this->getFoundIDs()); $spawns = DB::Aowow()->select("SELECT * FROM ?_spawns WHERE `type` = ?d AND `typeId` IN (?a) AND `posX` > 0 AND `posY` > 0", self::$type, $this->getFoundIDs()) ?: [];
if (!$spawns)
return;
if (!$skipAdmin && User::isInGroup(U_GROUP_MODERATOR)) if (!$skipAdmin && User::isInGroup(U_GROUP_MODERATOR))
if ($guids = array_column(array_filter($spawns, fn($x) => $x['guid'] > 0 || $x['type'] != Type::NPC), 'guid')) if ($guids = array_column(array_filter($spawns, fn($x) => $x['guid'] > 0 || $x['type'] != Type::NPC), 'guid'))

View File

@@ -43,7 +43,7 @@ class Announcement implements \JsonSerializable
public function jsonSerialize() : array public function jsonSerialize() : array
{ {
$json = array( $json = array(
'parent' => 'announcement-' . abs($this->id), 'parent' => 'announcement-' . $this->id,
'id' => $this->editable ? -$this->id : $this->id, 'id' => $this->editable ? -$this->id : $this->id,
'mode' => $this->mode, 'mode' => $this->mode,
'status' => $this->status, 'status' => $this->status,

View File

@@ -0,0 +1,559 @@
<?php
namespace Aowow\Template;
use \Aowow\Util, \Aowow\Cfg, \Aowow\Lang, \Aowow\User;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class PageTemplate
{
private const GUIDE_RATING_TPL = "$(document).ready(function() { $('#guiderating').append(GetStars(%.10F, %s, %u, %u)); });\n";
private readonly \Aowow\Locale $locale;
private array $displayHooks = [];
private array $dataLoader = [];
private array $scripts = [];
private array $js = [];
private array $css = [];
// generic data, that's just accessed from the template
private array $rawData = []; // copied from $context
private array $pageData = []; // processed by display hooks
// template data that needs further processing .. ! WARNING ! they will not get aut fetched from $context as they are already defined here
protected array $guideRating = [];
private string $gStaticUrl;
private string $gHost;
private string $gServerTime;
private ?string $analyticsTag = null;
private bool $consentFooter = false;
private string $dbProfiles = '';
private readonly string $user; // becomes User object
/*******************/
/* basic execution */
/* */
/* 1) Init */
/*******************/
public function __construct(private string $template, private ?\Aowow\TemplateResponse $context = null)
{
$this->locale = Lang::getLocale();
$this->gStaticUrl = Cfg::get('STATIC_URL');
$this->gHost = Cfg::get('HOST_URL');
$this->analyticsTag = Cfg::get('GTAG_MEASUREMENT_ID');
$this->gServerTime = sprintf("new Date('%s')", date(Util::$dateFormatInternal));
$this->user = User::class;
}
public function addDataLoader(string ...$dataFile) : void
{
foreach ($dataFile as $df)
$this->dataLoader[] = $df;
}
public function addScript(int $type, string $str, int $flags = 0x0) : bool
{
$tpl = match ($type)
{
SC_CSS_FILE => '<link rel="stylesheet" type="text/css" href="%1$s%2$s" />',
SC_CSS_STRING => '<style type="text/css">%1$s</style>',
SC_JS_FILE => '<script type="text/javascript" src="%1$s%2$s"></script>',
SC_JS_STRING => '<script type="text/javascript">%1$s</script>',
default => ''
};
if (!$tpl || !$str)
if (!$str)
{
trigger_error('PageTemplate::addScript - content empty', E_USER_WARNING);
return false;
}
if (!$tpl)
{
trigger_error('PageTemplate::addScript - unknown script type #'.$type, E_USER_WARNING);
return false;
}
// insert locale string
if ($flags & SC_FLAG_LOCALIZED)
$str = sprintf($str, Lang::getLocale()->json());
$this->scripts[] = [$type, $str, $flags, $tpl];
return true;
}
/* (optional) set pre-render hooks */
public function registerDisplayHook(string $var, callable $fn) : void
{
$this->displayHooks[$var][] = $fn;
}
private function getDisplayHooks(string $var) : array
{
return $this->displayHooks[$var] ?? [];
}
/* 3) self test, ready to be cached now */
public function prepare() : bool
{
if (!self::test('template/pages/', $this->template))
{
trigger_error('Error: nonexistent template requested: template/pages/'.$this->template.'.tpl.php', E_USER_ERROR);
return false;
}
// TODO - more checks and preparations
return true;
}
/* 4) display */
public function render() : void
{
$this->update();
include('template/pages/'.$this->template.'.tpl.php');
}
/***********/
/* loaders */
/***********/
// "template_exists"
public static function test(string $path, string $file) : bool
{
if (!preg_match('/^[\w\-_]+(\.tpl(\.php)?)?$/i', $file))
return false;
if ($path && preg_match('/\\{2,}|\/{2,}|\.{2,}|~/i', $path))
return false;
if (!is_file('template/'.$path.$file))
return false;
return true;
}
// load brick
private function brick(string $file, array $localVars = []) : void
{
$file .= '.tpl.php';
if (!self::test('bricks/', $file))
{
trigger_error('Nonexistent template requested: template/bricks/'.$file, E_USER_ERROR);
return;
}
foreach ($localVars as $n => $v)
$$n = $v;
include('template/bricks/'.$file);
}
private function brickIf(mixed $boolish, string $file, array $localVars = []) : void
{
if ($boolish)
$this->brick($file, $localVars);
}
// load brick with more text then vars
private function localizedBrick(string $file, array $localVars = []) : void
{
foreach ($localVars as $n => $v)
$$n = $v;
$_file = $file.'_'.$this->locale->value.'.tpl.php';
if (self::test('localized/', $_file))
{
include('template/localized/'.$_file);
return;
}
$_file = $file.'_'.$this->locale->getFallback()->value.'.tpl.php';
if (self::test('localized/', $_file))
{
include('template/localized/'.$_file);
return;
}
trigger_error('Nonexistent template requested: template/localized/'.$_file, E_USER_ERROR);
}
private function localizedBrickIf(mixed $boolish, string $file, array $localVars = []) : void
{
if ($boolish)
$this->localizedBrick($file, $localVars);
}
/****************/
/* Util wrapper */
/****************/
private function cfg(string $name) : mixed
{
return Cfg::get($name);
}
private function json(mixed $var, int $jsonFlags = 0x0) : string
{
if (is_string($var) && $this->$var)
$var = $this->$var;
return preg_replace('/script\s*\>/i', 'scr"+"ipt>', Util::toJSON($var, $jsonFlags));
}
private function escHTML(string $var) : string|array
{
return Util::htmlEscape($this->$var ?? $var);
}
private function escJS(string $var) : string|array
{
return Util::jsEscape($this->$var ?? $var);
}
private function ucFirst(string $var) : string
{
return Util::ucFirst($this->$var ?? $var);
}
/*****************/
/* render helper */
/*****************/
private function concat(string $arrVar, string $separator = '') : string
{
if (!is_array($this->$arrVar))
return '';
return implode($separator, $this->$arrVar);
}
private function renderArray(string|array $arrVar, int $lpad = 0) : string
{
$data = [];
if (is_string($arrVar) && isset($this->$arrVar) && is_array($this->$arrVar))
$data = $this->$arrVar;
else if (is_array($arrVar))
$data = $arrVar;
$buff = '';
foreach ($data as $x)
$buff .= str_repeat(' ', $lpad) . $x . "\n";
return $buff;
}
// load jsGlobals
private function renderGlobalVars(int $lpad = 0) : string
{
$buff = '';
if ($this->guideRating)
$buff .= str_repeat(' ', $lpad).sprintf(self::GUIDE_RATING_TPL, ...$this->guideRating);
foreach ($this->jsGlobals as [$jsVar, $data, $extraData])
{
$buff .= str_repeat(' ', $lpad).'var _ = '.$jsVar.';';
foreach ($data as $key => $data)
$buff .= ' _['.(is_numeric($key) ? $key : "'".$key."'")."]=".Util::toJSON($data).';';
$buff .= "\n";
if (isset($this->gPageInfo['type']) && isset($this->gPageInfo['typeId']) && isset($extraData[$this->gPageInfo['typeId']]))
{
$buff .= "\n";
foreach ($extraData[$this->gPageInfo['typeId']] as $k => $v)
if ($v)
$buff .= str_repeat(' ', $lpad).'_['.$this->gPageInfo['typeId'].'].'.$k.' = '.Util::toJSON($v).";\n";
$buff .= "\n";
}
}
return $buff;
}
private function renderSeriesItem(int $idx, array $list, int $lpad = 0) : string
{
$result = '<tr><th>'.($idx + 1).'</th><td><div>';
$end = array_key_last($list);
foreach ($list as $k => $i) // itemItr
{
$wrap = match ($i['side'])
{
SIDE_ALLIANCE => '<span class="icon-alliance-padded">%s</span>',
SIDE_HORDE => '<span class="icon-horde">%s</span>',
default => '%s'
};
if ($i['typeId'] == $this->typeId)
$result .= sprintf($wrap, '<b>'.$i['name'].'</b>');
else
$result .= sprintf($wrap, '<a href="?'.$i['typeStr'].'='.$i['typeId'].'">'.$i['name'].'</a>');
if ($end != $k)
$result .= '<br />';
}
return str_repeat(' ', $lpad) . $result . "</div></td></tr>\n";
}
private function renderFilter(int $lpad = 0) : string
{
$result = [];
// it's worth noting, that this only works on non-cached page calls. Luckily Profiler pages are not cached.
if ($this->context instanceof \Aowow\IProfilerList)
{
$result[] = "pr_setRegionRealm(\$WH.ge('fi').firstChild, '".$this->region."', '".$this->realm."');";
if ($this->filter->values['ra'])
$result[] = "pr_onChangeRace();";
}
if ($this->filter->fiInit) // str: filter template (and init html form)
$result[] = "fi_init('".$this->filter->fiInit."');";
else if ($this->filter->fiType) // str: filter template (set without init)
$result[] = "var fi_type = '".$this->filter->fiType."'";
if ($this->filter->fiSetCriteria) // arr:criteria, arr:signs, arr:values
$result[] = 'fi_setCriteria('.mb_substr(Util::toJSON(array_values($this->filter->fiSetCriteria)), 1, -1).");";
/*
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..?)
*/
if ($this->filter->fiSetWeights) // arr:weights, bool:nt[0], bool:ids[1], bool:stealth[1]
$result[] = 'fi_setWeights('.Util::toJSON(array_values($this->filter->fiSetWeights)).', 0, 1, 1);';
if ($this->filter->fiExtraCols) // arr:extraCols
$result[] = 'fi_extraCols = '.Util::toJSON(array_values(array_unique($this->filter->fiExtraCols))).";";
return str_repeat(' ', $lpad)."<script type=\"text/javascript\">//<![CDATA[\n".
$this->renderArray($result, $lpad + 4).
str_repeat(' ', $lpad)."//]]></script>\n";
}
private function makeOptionsList(array $data, mixed $selectedIdx = null, int $lpad = 0, ?callable $callback = null) : string
{
$callback ??= fn(&$v, &$k) => $v; // default callback: skip empty descriptors
$options = '';
foreach ($data as $idx => $str)
{
$extraAttributes = [];
if (!$callback($str, $idx, $extraAttributes))
continue;
if ($idx === '' || !$str)
continue;
$options .= str_repeat(' ', max(0, $lpad)).'<option';
foreach ($extraAttributes as $k => $v)
$options .= ' '.$k.'="'.$v.'"';
if (is_array($selectedIdx) && in_array($idx, $selectedIdx))
$options .= ' selected="selected"';
else if (!is_null($selectedIdx) && $selectedIdx == $idx)
$options .= ' selected="selected"';
$options .= ' value="'.$idx.'">'.$str.'</option>'.($lpad < 0 ? '' : "\n");
}
return $options;
}
private function makeRadiosList(string $name, array $data, mixed $selectedIdx = null, int $lpad = 0, ?callable $callback = null) : string
{
$callback ??= fn(&$v, &$k) => $v; // default callback: skip empty descriptors
$options = '';
foreach ($data as $idx => [$title, $id])
{
$extraAttributes = [];
if (!$callback($title, $idx, $extraAttributes))
continue;
if ($id === '' || !$title)
continue;
$options .= str_repeat(' ', max(0, $lpad)).'<input type="radio" name="'.$name.'" value="'.$idx.'" id="'.$name.'-'.$id.'"';
if (!is_null($selectedIdx) && $selectedIdx == $idx)
$options .= ' checked="checked"';
$options .= '/><label for="'.$name.'-'.$id.'"';
foreach ($extraAttributes as $k => $v)
$options .= ' '.$k.'="'.$v.'"';
$options .= '>'.$title.'</label>'.($lpad < 0 ? '' : "\n");
}
return $options;
}
// unordered stuff
private function prepareScripts() : void
{
$this->js = $this->css = [];
foreach ($this->scripts as [$type, $str, $flags, $tpl])
{
$app = [];
if (($flags & SC_FLAG_APPEND_LOCALE) && $this->locale != \Aowow\Locale::EN)
$app[] = 'lang='.$this->locale->domain();
// append anti-cache timestamp
if (!($flags & SC_FLAG_NO_TIMESTAMP))
if ($type == SC_JS_FILE || $type == SC_CSS_FILE)
$app[] = filemtime('static/'.$str) ?: 0;
if ($app)
$appendix = '?'.implode('&', $app);
if ($type == SC_JS_FILE || $type == SC_CSS_FILE)
$str = Cfg::get('STATIC_URL').'/'.$str;
if ($flags & SC_FLAG_PREFIX)
{
if ($type == SC_JS_FILE || $type == SC_JS_STRING)
array_unshift($this->js, sprintf($tpl, $str, $appendix ?? ''));
else
array_unshift($this->css, sprintf($tpl, $str, $appendix ?? ''));
}
else
{
if ($type == SC_JS_FILE || $type == SC_JS_STRING)
array_push($this->js, sprintf($tpl, $str, $appendix ?? ''));
else
array_push($this->css, sprintf($tpl, $str, $appendix ?? ''));
}
}
if ($data = array_unique($this->dataLoader))
{
$args = array(
'data' => implode('.', $data),
'locale' => $this->locale->value,
't' => $_SESSION['dataKey']
);
array_push($this->js, '<script type="text/javascript" src="'.Cfg::get('HOST_URL').'/?'.http_build_query($args).'"></script>');
}
}
// refresh vars that shouldn't be cached
private function update() : void
{
// analytics + consent
if (!isset($_COOKIE['consent']))
{
$this->addScript(SC_CSS_FILE, 'css/consent.css');
$this->addScript(SC_JS_FILE, 'js/consent.js');
$this->consentFooter = true;
$this->analyticsTag = null;
}
else if ($this->analyticsTag && !$_COOKIE['consent'])
$this->analyticsTag = null;
// js + css
$this->prepareScripts();
// db profiling
if (Cfg::get('DEBUG') >= LOG_LEVEL_INFO && User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN))
$this->dbProfiles = \Aowow\DB::getProfiles();
}
public function setListviewError() : void
{
if (!$this->lvTabs)
return;
foreach ($this->lvTabs->iterate() as $lv)
if ($lv instanceof \Aowow\Listview)
$lv->setError();
}
// pre-serialization: if a var is relevant it was stored in $rawData
public function __sleep() : array
{
$this->context = null; // unlink from TemplateResponse
$this->pageData = []; // clear modified data
$vars = [];
foreach ($this as $k => $_)
$vars[] = $k;
return $vars;
}
public function __wakeup() : void
{
$this->gStaticUrl = Cfg::get('STATIC_URL');
$this->gHost = Cfg::get('HOST_URL');
$this->analyticsTag = Cfg::get('GTAG_MEASUREMENT_ID');
$this->gServerTime = sprintf("new Date('%s')", date(Util::$dateFormatInternal));
}
public function __set(string $var, mixed $value) : void
{
$this->pageData[$var] = $value;
}
public function __get(string $var) : mixed
{
// modified data exists
if (isset($this->pageData[$var]))
return $this->pageData[$var];
if (!isset($this->rawData[$var]))
{
if (!$this->context)
return null;
if (!property_exists($this->context, $var))
return null;
$this->rawData[$var] = $this->context->$var;
}
if ($hooks = $this->getDisplayHooks($var))
{
if (is_object($this->rawData[$var])) // is frontend component
$this->pageData[$var] = clone $this->rawData[$var];
else
$this->pageData[$var] = $this->rawData[$var];
foreach ($hooks as $fn)
$fn($this, $this->pageData[$var]);
}
return $this->pageData[$var] ?? $this->rawData[$var];
}
}

View File

@@ -0,0 +1,657 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
trait TrRecoveryHelper
{
const MODE_INFO = 0;
const MODE_FORM_PASS = 1;
const MODE_FORM_EMAIL = 2;
private function startRecovery(int $newStatus, string $mailTemplate, string $email) : string
{
if (!$newStatus <= ACC_STATUS_NEW && $newStatus > ACC_STATUS_CHANGE_PASS)
return Lang::main('intError');
// check if already processing
if ($_ = DB::Aowow()->selectCell('SELECT `statusTimer` - UNIX_TIMESTAMP() FROM ?_account WHERE `email` = ? AND `status` > ?d AND `statusTimer` > UNIX_TIMESTAMP()', $email, ACC_STATUS_NEW))
return sprintf(Lang::account('isRecovering'), Util::formatTime($_ * 1000));
// create new token and write to db
$token = Util::createHash();
if (!DB::Aowow()->query('UPDATE ?_account SET `token` = ?, `status` = ?d, `statusTimer` = UNIX_TIMESTAMP() + ?d WHERE `email` = ?', $token, $newStatus, Cfg::get('ACC_RECOVERY_DECAY'), $email))
return Lang::main('intError');
// send recovery mail
if (!Util::sendMail($email, $mailTemplate, [$token], Cfg::get('ACC_RECOVERY_DECAY')))
return sprintf(Lang::main('intError2'), 'send mail');
return '';
}
}
trait TrGetNext
{
private function getNext(bool $forHeader = false) : string
{
$next = '';
if (!empty($this->_get['next']))
$next = $this->_get['next'];
else if (isset($_SERVER['HTTP_REFERER']) && strstr($_SERVER['HTTP_REFERER'], '?'))
$next = explode('?', $_SERVER['HTTP_REFERER'])[1];
else if ($forHeader)
return '.';
return ($forHeader ? '?' : '').$next;
}
}
Interface ICache
{
public function saveCache(string|Template\PageTemplate $toCache) : void;
public function loadCache(bool|string|Template\PageTemplate &$fromCache) : bool;
public function setOnCacheLoaded(callable $callback, mixed $params = null) : void;
public function getCacheKeyComponents() : array;
public function applyOnCacheLoaded(mixed &$data) : mixed;
}
trait TrCache
{
private const STORE_METHOD_OBJECT = 0;
private const STORE_METHOD_STRING = 1;
private int $_cacheType = CACHE_TYPE_NONE;
private int $skipCache = 0x0;
private ?int $decay = null;
private string $cacheDir = 'cache/template/';
private bool $cacheInited = false;
private ?\Memcached $memcached = null;
private array $onCacheLoaded = [null, null]; // post-load updater
public static array $cacheStats = []; // load info for page footer
// visible properties or given strings are cached
public function saveCache(string|object $toCache) : void
{
$this->initCache();
if ($this->_cacheType == CACHE_TYPE_NONE)
return;
if (!Cfg::get('CACHE_MODE') /* || Cfg::get('DEBUG') */)
return;
if (!$this->decay)
return;
$cKey = $this->formatCacheKey();
$method = is_object($toCache) ? self::STORE_METHOD_OBJECT : self::STORE_METHOD_STRING;
if ($method == self::STORE_METHOD_OBJECT)
$toCache = serialize($toCache);
else
$toCache = (string)$toCache;
if (is_callable($this->onCacheLoaded[0]))
$postCache = serialize($this->onCacheLoaded);
if (Cfg::get('CACHE_MODE') & CACHE_MODE_MEMCACHED)
{
// on &refresh also clear related
if ($this->skipCache & CACHE_MODE_MEMCACHED)
$this->deleteCache(CACHE_MODE_MEMCACHED);
$data = array(
'timestamp' => time(),
'lifetime' => $this->decay,
'revision' => AOWOW_REVISION,
'method' => $method,
'postCache' => $postCache ?? null,
'data' => $toCache
);
$this->memcached()?->set($cKey[2], $data);
}
if (Cfg::get('CACHE_MODE') & CACHE_MODE_FILECACHE)
{
$data = time()." ".$this->decay." ".AOWOW_REVISION." ".$method."\n";
$data .= ($postCache ?? '')."\n";
$data .= gzcompress($toCache, 9);
// on &refresh also clear related
if ($this->skipCache & CACHE_MODE_FILECACHE)
$this->deleteCache(CACHE_MODE_FILECACHE);
if (Util::writeDir($this->cacheDir . implode(DIRECTORY_SEPARATOR, array_slice($cKey, 0, 2))))
file_put_contents($this->cacheDir . implode(DIRECTORY_SEPARATOR, $cKey), $data);
}
}
public function loadCache(mixed &$fromCache) : bool
{
$this->initCache();
if ($this->_cacheType == CACHE_TYPE_NONE)
return false;
if (!Cfg::get('CACHE_MODE') /* || Cfg::get('DEBUG') */)
return false;
$cKey = $this->formatCacheKey();
$rev = $method = $data = $postCache = null;
if ((Cfg::get('CACHE_MODE') & CACHE_MODE_MEMCACHED) && !($this->skipCache & CACHE_MODE_MEMCACHED))
{
if ($cache = $this->memcached()?->get($cKey[2]))
{
$method = $cache['method'];
$data = $cache['data'];
$postCache = $cache['postCache'];
if (($cache['timestamp'] + $cache['lifetime']) > time() && $cache['revision'] == AOWOW_REVISION)
self::$cacheStats = [CACHE_MODE_MEMCACHED, $cache['timestamp'], $cache['lifetime']];
}
}
if (!$data && (Cfg::get('CACHE_MODE') & CACHE_MODE_FILECACHE) && !($this->skipCache & CACHE_MODE_FILECACHE))
{
$file = $this->cacheDir . implode(DIRECTORY_SEPARATOR, $cKey);
if (!file_exists($file))
return false;
$content = file_get_contents($file);
if (!$content)
return false;
[$head, $postCache, $data] = explode("\n", $content, 3);
if (substr_count($head, ' ') != 3)
return false;
[$time, $lifetime, $rev, $method] = explode(' ', $head);
if (($time + $lifetime) < time() || $rev != AOWOW_REVISION)
return false;
self::$cacheStats = [CACHE_MODE_FILECACHE, $time, $lifetime];
$data = gzuncompress($data);
}
if (!$data)
return false;
if ($postCache)
$this->onCacheLoaded = unserialize($postCache);
$fromCache = false;
if ($method == self::STORE_METHOD_OBJECT)
$fromCache = unserialize($data);
else if ($method == self::STORE_METHOD_STRING)
$fromCache = $data;
return $fromCache !== false;
}
public function deleteCache(int $modeMask = 0x3) : void
{
$this->initCache();
// type+typeId+catg; 3+6+10
$cKey = $this->formatCacheKey();
$cKey[2] = substr($cKey[2], 0, 19);
if ($modeMask & CACHE_MODE_MEMCACHED)
foreach ($this->memcached()?->getAllKeys() ?? [] as $k)
if (strpos($k, $cKey[2]) === 0)
$this->memcached()?->delete($k);
if ($modeMask & CACHE_MODE_FILECACHE)
foreach (glob(implode(DIRECTORY_SEPARATOR, $cKey).'*') as $file)
unlink($file);
}
private function memcached() : ?\Memcached
{
if (!$this->memcached && (Cfg::get('CACHE_MODE') & CACHE_MODE_MEMCACHED))
{
$this->memcached = new \Memcached();
$this->memcached->addServer('localhost', 11211);
}
return $this->memcached;
}
private function initCache() : void
{
// php's missing trait property conflict resolution is going to be the end of me
// also allow reevaluation even if inited. It may have changed in generate(), because of an error.
if (isset($this->cacheType))
$this->_cacheType = $this->cacheType;
if ($this->cacheInited)
return;
// force 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;
}
$this->decay ??= Cfg::get('CACHE_DECAY');
$cacheDir = Cfg::get('CACHE_DIR');
if ($cacheDir && Util::writeDir($cacheDir))
$this->cacheDir = mb_substr($cacheDir, -1) != '/' ? $cacheDir.'/' : $cacheDir;
$this->cacheInited = true;
}
// https://stackoverflow.com/questions/466521
private function formatCacheKey() : array
{
[$dbType, $dbTypeId, $category, $staffMask, $miscInfo] = $this->getCacheKeyComponents();
$fileKey = '';
// DBType: 3
$fileKey .= str_pad(dechex($dbType & 0xFFF), 3, 0, STR_PAD_LEFT);
// DBTypeId: 6
$fileKey .= str_pad(dechex($dbTypeId & 0xFFFFFF), 6, 0, STR_PAD_LEFT);
// category: (2+4+4)
$fileKey .= str_pad(dechex($category & 0xFFFFFFFFFF), 2+4+4, 0, STR_PAD_LEFT);
// cacheType: 1
$fileKey .= str_pad(dechex($this->_cacheType & 0xF), 1, 0, STR_PAD_LEFT);
// localeId: 2,
$fileKey .= str_pad(dechex(Lang::getLocale()->value & 0xFF), 2, 0, STR_PAD_LEFT);
// staff mask: 4
$fileKey .= str_pad(dechex($staffMask & 0xFFFFFFFF), 4, 0, STR_PAD_LEFT);
// optioal: miscInfo
if ($miscInfo)
$fileKey .= '-'.$miscInfo;
// topDir, 2ndDir, file
return array(
str_pad(dechex($dbType & 0xFF), 2, 0, STR_PAD_LEFT),
str_pad(dechex(($dbTypeId > 0 ? $dbTypeId : $category) & 0xFF), 2, 0, STR_PAD_LEFT),
$fileKey
);
}
public function setOnCacheLoaded(callable $callback, mixed $params = null) : void
{
$this->onCacheLoaded = [$callback, $params];
}
public function applyOnCacheLoaded(mixed &$data) : mixed
{
if (is_callable($this->onCacheLoaded[0]))
return $this->onCacheLoaded[0]($data, $this->onCacheLoaded[1]);
return $data;
}
public function setCacheDecay(int $seconds) : void
{
if ($seconds < 0)
return;
$this->decay = $seconds;
}
abstract public function getCacheKeyComponents() : array;
}
trait TrSearch
{
private int $maxResults = 500;
private string $query = ''; // sanitized search string
private int $searchMask = 0; // what to search for
private Search $searchObj;
public function getCacheKeyComponents() : array
{
return array(
-1, // DBType
-1, // DBTypeId
$this->searchMask, // category
User::$groups, // staff mask
md5($this->query) // misc (here search query)
);
}
}
Interface IProfilerList
{
public function getRegions() : void;
}
trait TrProfiler
{
protected int $realmId = 0;
protected string $battlegroup = ''; // not implemented, since no pserver supports it
public string $region = '';
public string $realm = '';
private 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 character
$cat = explode('.', mb_strtolower($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::get('BATTLEGROUP')))
// $this->battlegroup = Cfg::get('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 = mb_strtolower($cat[2]); // cannot reconstruct original name from urlized form; match against special name field
break;
}
}
}
}
private function followBreadcrumbPath() : void
{
if ($this->region)
{
$this->breadcrumb[] = $this->region;
if ($this->realm)
$this->breadcrumb[] = Profiler::urlize($this->realm, true);
// else
// $this->breadcrumb[] = Profiler::urlize(Cfg::get('BATTLEGROUP'));
}
}
}
trait TrProfilerDetail
{
use TrProfiler { TrProfiler::getSubjectFromUrl as _getSubjectFromUrl; }
protected string $subjectName = '';
public int $typeId = 0;
public ?array $doResync = null;
private function getSubjectFromUrl(string $pageParam) : void
{
if (!$pageParam)
return;
if (Util::checkNumeric($pageParam, NUM_CAST_INT))
$this->typeId = $pageParam;
else
$this->_getSubjectFromUrl($pageParam);
}
private function handleIncompleteData(int $type, int $guid) : void
{
// queue full fetch
if ($newId = Profiler::scheduleResync($type, $this->realmId, $guid))
{
$this->template = 'text-page-generic';
$this->doResync = [Type::getFileString($type), $newId];
$this->inputbox = ['inputbox-status', ['head' => Lang::profiler('firstUseTitle', [Util::ucFirst($this->subjectName), $this->realm])]];
return;
}
// todo: base info should have been created in __construct .. why are we here..?
$this->forward('?'.Type::getFileString($type).'s='.$this->region.'.'.Profiler::urlize($this->realm, true).'&filter=na='.Util::ucFirst($this->subjectName).';ex=on');
}
}
trait TrProfilerList
{
use TrProfiler;
public array $regions = [];
public function getRegions() : void
{
$usedRegions = array_column(Profiler::getRealms(), 'region');
foreach (Util::$regions as $idx => $id)
if (in_array($id, $usedRegions))
$this->regions[$id] = Lang::profiler('regions', $id);
}
}
abstract class BaseResponse
{
protected const PATTERN_TEXT_LINE = '/[\p{Cc}\p{Cf}\p{Co}\p{Cs}\p{Cn}]/ui';
protected const PATTERN_TEXT_BLOB = '/[\x00-\x09\x0B-\x1F\p{Cf}\p{Co}\p{Cs}\p{Cn}]/ui';
protected static array $sql = []; // debug: sql stats container
protected array $expectedPOST = []; // fill with variables you that are going to be used; eg:
protected array $expectedGET = []; // 'id' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkIdList']
protected array $expectedCOOKIE = [];
protected array $_post = []; // the filtered variable result
protected array $_get = [];
protected array $_cookie = [];
protected int $requiredUserGroup = U_GROUP_NONE; // by default accessible to everone
protected bool $requiresLogin = false; // normal users and guests are both U_GROUP_NONE, soooo.....
protected mixed $result = null;
public function __construct()
{
$this->initRequestData();
if (!User::isInGroup($this->requiredUserGroup))
$this->onUserGroupMismatch();
if ($this->requiresLogin && !User::isLoggedIn())
$this->onUserGroupMismatch();
}
public function process() : void
{
$fromCache = false;
if ($this instanceof ICache)
$fromCache = $this->loadCache($this->result);
if (!$this->result)
$this->generate();
$this->display();
if ($this instanceof ICache && !$fromCache)
$this->saveCache($this->result);
}
private function initRequestData() : void
{
// php bug? If INPUT_X is empty, filter_input_array returns null/fails
// only really relevant for INPUT_POST
// manuall set everything null in this case
if ($this->expectedPOST)
{
if ($_POST)
$this->_post = filter_input_array(INPUT_POST, $this->expectedPOST);
else
$this->_post = array_fill_keys(array_keys($this->expectedPOST), null);
}
if ($this->expectedGET)
{
if ($_GET)
$this->_get = filter_input_array(INPUT_GET, $this->expectedGET);
else
$this->_get = array_fill_keys(array_keys($this->expectedGET), null);
}
if ($this->expectedCOOKIE)
{
if ($_COOKIE)
$this->_cookie = filter_input_array(INPUT_COOKIE, $this->expectedCOOKIE);
else
$this->_cookie = array_fill_keys(array_keys($this->expectedCOOKIE), null);
}
}
protected function forward(string $url = '') : never
{
$this->sendNoCacheHeader();
header('Location: '.($url ?: '.'), true, 302);
exit;
}
protected function forwardToSignIn(string $next = '') : never
{
$this->forward('?account=signin'.($next ? '&next='.$next : ''));
}
protected function sumSQLStats() : void
{
Util::arraySumByKey(self::$sql, DB::Aowow()->getStatistics(), DB::World()->getStatistics());
foreach (Profiler::getRealms() as $rId => $_)
Util::arraySumByKey(self::$sql, DB::Characters($rId)->getStatistics());
}
protected function sendNoCacheHeader()
{
header('Expires: Sat, 01 Jan 2000 01:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
}
/****************************/
/* required Parameter tests */
/****************************/
protected function assertPOST(string ...$keys) : bool
{
foreach ($keys as $k) // not sent by browser || empty text field sent || validation failed
if (!isset($this->_post[$k]) || $this->_post[$k] === null || $this->_post[$k] === '' || $this->_post[$k] === false)
return false;
return true;
}
protected function assertGET(string ...$keys) : bool
{
foreach ($keys as $k)
if (!isset($this->_get[$k]) || $this->_get[$k] === null || $this->_get[$k] === '' || $this->_get[$k] === false)
return false;
return true;
}
protected function assertCOOKIE(string ...$keys) : bool
{
foreach ($keys as $k)
if (!isset($this->_cookie[$k]) || $this->_cookie[$k] === null || $this->_cookie[$k] === '' || $this->_cookie[$k] === false)
return false;
return true;
}
/*******************************/
/* Parameter validation checks */
/*******************************/
protected static function checkRememberMe(string $val) : bool
{
return $val === 'yes';
}
protected static function checkCheckbox(string $val) : bool
{
return $val === 'on';
}
protected static function checkEmptySet(string $val) : bool
{
return $val === ''; // parameter is set and expected to be empty
}
protected static function checkIdList(string $val) : array
{
if (preg_match('/^-?\d+(,-?\d+)*$/', $val))
return array_map('intVal', explode(',', $val));
return [];
}
protected static function checkIntArray(string $val) : array
{
if (preg_match('/^-?\d+(:-?\d+)*$/', $val))
return array_map('intVal', explode(':', $val));
return [];
}
protected static function checkIdListUnsigned(string $val) : array
{
if (preg_match('/^\d+(,\d+)*$/', $val))
return array_map('intVal', explode(',', $val));
return [];
}
protected static function checkTextLine(string $val) : string
{
// trim non-printable chars
return preg_replace(self::PATTERN_TEXT_LINE, '', trim(urldecode($val)));
}
protected static function checkTextBlob(string $val) : string
{
// trim non-printable chars + excessive whitespaces (pattern includes \r)
$str = preg_replace(self::PATTERN_TEXT_BLOB, '', trim($val));
return preg_replace('/ +/', ' ', trim($str));
}
/********************/
/* child implements */
/********************/
// calc response
abstract protected function generate() : void;
// send response
abstract protected function display() : void;
// handling differs by medium
abstract protected function onUserGroupMismatch() : never;
}
?>

View File

@@ -0,0 +1,687 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
trait TrDetailPage
{
// template vars
public ?InfoboxMarkup $infobox = null;
public ?InfoboxMarkup $contributions = null;
public ?array $series = null;
public ?string $transfer = null; // faction transfer equivalent data
public ?Markup $smartAI = null;
public ?array $map = null;
public array $headIcons = [];
public function getCacheKeyComponents() : array
{
return array(
$this->type, // DBType
$this->typeId, // DBTypeId
-1, // category
User::$groups, // staff mask
'' // misc (here unused)
);
}
}
trait TrListPage
{
public ?string $subCat = null;
public ?Filter $filter = null;
public function getCacheKeyComponents() : array
{
// max. 3 catgs
// catg max 65535
if ($this->category)
{
$catg = 0x0;
for ($i = 0; $i < 3; $i++)
{
$catg <<= 4;
$catg |= ($this->category[$i] ?? 0) & 0xFFFF;
}
}
if ($get = $this->filter?->buildGETParam())
$misc = md5($get);
return array(
$this->type, // DBType
-1, // DBTypeId
$catg ?? -1, // category
User::$groups, // staff mask
$misc ?? '' // misc (here filter)
);
}
}
trait TrGuideEditor
{
public int $typeId = 0;
public int $editCategory = 0;
public int $editClassId = 0;
public int $editSpecId = 0;
public int $editRev = 0;
public int $editStatus = GUIDE_STATUS_DRAFT;
public string $editStatusColor = GuideMgr::STATUS_COLORS[GUIDE_STATUS_DRAFT];
public string $editTitle = '';
public string $editName = '';
public string $editDescription = '';
public string $editText = '';
public Locale $editLocale = Locale::EN;
}
class TemplateResponse extends BaseResponse
{
final protected const /* int */ TAB_DATABASE = 0;
final protected const /* int */ TAB_TOOLS = 1;
final protected const /* int */ TAB_MORE = 2;
final protected const /* int */ TAB_COMMUNITY = 3;
final protected const /* int */ TAB_STAFF = 4;
final protected const /* int */ TAB_GUIDES = 6;
private array $jsgBuffer = []; // throw any db type references in here to be processed later
private array $header = [];
private string $fullParams = ''; // effectively articleUrl
protected string $template = '';
protected array $breadcrumb = [];
protected ?int $activeTab = null; // [Database, Tools, More, Community, Staff, null, Guides] ?? none
protected string $pageName = '';
protected array $category = [];
protected array $validCats = [];
protected ?string $articleUrl = null;
protected bool $filterError = false; // retroactively apply error notice to fixed filter result
protected array $dataLoader = []; // ?data=x.y.z as javascript
protected array $scripts = array(
[SC_JS_FILE, 'js/jquery-3.7.0.min.js', SC_FLAG_NO_TIMESTAMP ],
[SC_JS_FILE, 'js/basic.js' ],
[SC_JS_FILE, 'widgets/power.js', SC_FLAG_NO_TIMESTAMP | SC_FLAG_APPEND_LOCALE],
[SC_JS_FILE, 'js/locale_%s.js', SC_FLAG_LOCALIZED ],
[SC_JS_FILE, 'js/global.js' ],
[SC_JS_FILE, 'js/locale.js' ],
[SC_JS_FILE, 'js/Markup.js' ],
[SC_CSS_FILE, 'css/basic.css' ],
[SC_CSS_FILE, 'css/global.css' ],
[SC_CSS_FILE, 'css/aowow.css' ],
[SC_CSS_FILE, 'css/locale_%s.css', SC_FLAG_LOCALIZED ]
);
// debug: stats
protected static float $time = 0.0;
// protected static array $sql = [];
// protected static array $cacheStats = [];
public array $pageStats = []; // static properties carry the values, this is just for the PageTemplate to reference
// send to template
public array $title = []; // head title components
public string $h1 = ''; // body title
public string $h1Link = ''; //
public ?string $headerLogo = null; // url to non-standard logo for events etc.
public string $search = ''; // prefilled search bar
public string $wowheadLink = 'https://wowhead.com/';
public int $contribute = CONTRIBUTE_NONE;
public ?array $inputbox = null;
public ?string $rss = null; // link rel=alternate for rss auto-discovery
public ?string $tabsTitle = null;
public ?Markup $extraText = null;
public ?string $extraHTML = null;
public array $redButtons = []; // see template/redButtons.tpl.php
// send to template, but it is js stuff
public array $gPageInfo = [];
public bool $gDataKey = false; // send g_DataKey to template or don't (stored in $_SESSION)
public ?Markup $article = null;
public ?Tabs $lvTabs = null;
public array $pageTemplate = []; // js PageTemplate object
public array $jsGlobals = []; // ready to be used in template
public function __construct(string $pageParam = '')
{
$this->title[] = Cfg::get('NAME');
self::$time = microtime(true);
parent::__construct();
$this->fullParams = $this->pageName;
if ($pageParam)
$this->fullParams .= '='.$pageParam;
// prep js+css includes
$parentVars = get_class_vars(__CLASS__);
if ($parentVars['scripts'] != $this->scripts) // additions set in child class
$this->scripts = array_merge($parentVars['scripts'], $this->scripts);
if (User::isInGroup(U_GROUP_STAFF | U_GROUP_SCREENSHOT | U_GROUP_VIDEO))
array_push($this->scripts, [SC_CSS_FILE, 'css/staff.css'], [SC_JS_FILE, 'js/staff.js']);
// 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);
if ($this->pageName)
{
$this->wowheadLink = sprintf(WOWHEAD_LINK, Lang::getLocale()->domain(), $this->pageName, $pageParam ? '=' . $pageParam : '');
$this->pageTemplate['pageName'] = $this->pageName;
}
if (!is_null($this->activeTab))
$this->pageTemplate['activeTab'] = $this->activeTab;
if (!$this->isValidPage())
$this->onInvalidCategory();
if (Cfg::get('MAINTENANCE') && !User::isInGroup(U_GROUP_EMPLOYEE))
$this->generateMaintenance();
else if (Cfg::get('MAINTENANCE') && User::isInGroup(U_GROUP_EMPLOYEE))
Util::addNote('Maintenance mode enabled!');
}
// by default goto login page
protected function onUserGroupMismatch() : never
{
if (User::isLoggedIn())
$this->generateError();
$this->forwardToSignIn($_SERVER['QUERY_STRING'] ?? '');
}
// by default show error page
protected function onInvalidCategory() : never
{
$this->generateError();
}
// just pass through
protected function addScript(array ...$scriptDefs) : void
{
if (!$this->result)
$this->scripts = array_merge($this->scripts, $scriptDefs);
else
foreach ($scriptDefs as $s)
$this->result->addScript(...$s);
}
protected function addDataLoader(string ...$dataFiles) : void
{
if (!$this->result)
$this->dataLoader = array_merge($this->dataLoader, $dataFiles);
else
$this->result->addDataLoader($dataFiles);
}
public static function pageStatsHook(Template\PageTemplate &$pt, array &$stats) : void
{
if (User::isInGroup(U_GROUP_EMPLOYEE))
{
$stats['time'] = Util::formatTime((microtime(true) - self::$time) * 1000, true);
$stats['sql'] = ['count' => parent::$sql['count'], 'time' => Util::formatTime(parent::$sql['time'] * 1000, true)];
$stats['cache'] = !empty(static::$cacheStats) ? [static::$cacheStats[0], Util::formatTimeDiff(static::$cacheStats[1])] : null;
}
else
$stats = [];
}
protected function getCategoryFromUrl(string $pageParam) : void
{
$arr = explode('.', $pageParam);
foreach ($arr as $v)
{
if (!is_numeric($v))
break;
$this->category[] = (int)$v;
}
}
// functionally this should be in PageTemplate but inaccessible there
protected function fmtStaffTip(?string $text, string $tip) : string
{
if (!$text || !User::isInGroup(U_GROUP_EMPLOYEE))
return $text ?? '';
else
return sprintf(Util::$dfnString, $tip, $text);
}
/**********************/
/* Prepare js-Globals */
/**********************/
// add typeIds <int|array[int]> that should be displayed as jsGlobal on the page
public function extendGlobalIds(int $type, int ...$ids) : void
{
if (!$type || !$ids)
return;
if (!isset($this->jsgBuffer[$type]))
$this->jsgBuffer[$type] = [];
foreach ($ids as $id)
$this->jsgBuffer[$type][] = $id;
}
// add jsGlobals or typeIds (can be mixed in one array: TYPE => [mixeddata]) to display on the page
public function extendGlobalData(array $data, ?array $extra = null) : void
{
foreach ($data as $type => $globals)
{
if (!is_array($globals) || !$globals)
continue;
$this->initJSGlobal($type);
// can be id => data
// or idx => id
// and may be mixed
foreach ($globals as $k => $v)
{
if (is_array($v))
{
// localize name fields .. except for icons .. icons are special
if ($type != Type::ICON)
{
foreach (['name', 'namefemale'] as $n)
{
if (!isset($v[$n]))
continue;
$v[$n . '_'.Lang::getLocale()->json()] = $v[$n];
unset($v[$n]);
}
}
$this->jsGlobals[$type][1][$k] = $v;
}
else if (is_numeric($v))
$this->extendGlobalIds($type, $v);
}
}
if ($extra)
{
$namedExtra = [];
foreach ($extra as $typeId => $data)
foreach ($data as $k => $v)
$namedExtra[$typeId][$k.'_'.Lang::getLocale()->json()] = $v;
$this->jsGlobals[$type][2] = $namedExtra;
}
}
// init store for type
private function initJSGlobal(int $type) : void
{
$jsg = &$this->jsGlobals; // shortcut
if (isset($jsg[$type]))
return;
if ($tpl = Type::getJSGlobalTemplate($type))
$jsg[$type] = $tpl;
}
// lookup jsGlobals from collected typeIds
private function applyGlobals() : void
{
foreach ($this->jsgBuffer as $type => $ids)
{
foreach ($ids as $k => $id) // filter already generated data, maybe we can save a lookup or two
if (isset($this->jsGlobals[$type][1][$id]))
unset($ids[$k]);
if (!$ids)
continue;
$this->initJSGlobal($type);
$obj = Type::newList($type, [Cfg::get('SQL_LIMIT_NONE'), ['id', array_unique($ids, SORT_NUMERIC)]]);
if (!$obj)
continue;
$this->extendGlobalData($obj->getJSGlobals(GLOBALINFO_SELF));
// delete processed ids
$this->jsgBuffer[$type] = [];
}
}
/************************/
/* Generic Page Content */
/************************/
// get announcements and notes for user
private function addAnnouncements(bool $onlyGenerics = false) : void
{
$announcements = [];
// display occured notices
$notes = $_SESSION['notes'] ?? [];
unset($_SESSION['notes']);
$notes[] = [...Util::getNotes(), 'One or more issues occured during page generation'];
foreach ($notes as $i => [$messages, $logLevel, $head])
{
if (!$messages)
continue;
array_unshift($messages, $head);
$colors = array( // [border, text]
LOG_LEVEL_ERROR => ['C50F1F', 'E51223'],
LOG_LEVEL_WARN => ['C19C00', 'E5B700'],
LOG_LEVEL_INFO => ['3A96DD', '42ADFF']
);
$text = new LocString(['name_loc' . Lang::getLocale()->value => '[span]'.implode("[br]", $messages).'[/span]'], callback: Util::defStatic(...));
$style = 'color: #'.($colors[$logLevel][1] ?? 'fff').'; font-weight: bold; font-size: 14px; padding-left: 40px; background-image: url('.Cfg::get('STATIC_URL').'/images/announcements/warn-small.png); background-size: 15px 15px; background-position: 12px center; border: dashed 2px #'.($colors[$logLevel][0] ?? 'fff').';';
$announcements[] = new Announcement(-$i, 'internal error', $text, style: $style);
}
// fetch announcements
$fromDB = DB::Aowow()->select(
'SELECT `id`, `mode`, `status`, `name`, `style`, `text_loc0`, `text_loc2`, `text_loc3`, `text_loc4`, `text_loc6`, `text_loc8`
FROM ?_announcements
WHERE (`status` = ?d { OR `status` = ?d } ) AND
(`page` = "*" { OR `page` = ? } ) AND
(`groupMask` = 0 OR `groupMask` & ?d)',
Announcement::STATUS_ENABLED, User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU) ? Announcement::STATUS_DISABLED : DBSIMPLE_SKIP,
$onlyGenerics || !$this->pageName ? DBSIMPLE_SKIP : $this->pageName,
User::$groups
);
foreach ($fromDB as $a)
if (($ann = new Announcement($a['id'], $a['name'], new LocString($a, 'text', Util::defStatic(...)), $a['mode'], $a['status'], Util::defStatic($a['style'])))->status != Announcement::STATUS_DELETED)
$announcements[] = $ann;
$this->result->announcements = $announcements;
}
// get article & static infobox (run before processing jsGlobals)
private function addArticle() : void
{
if ($this->article)
return;
$article = [];
if (isset($this->guideRevision))
$article = DB::Aowow()->selectRow('SELECT `article`, `locale`, `editAccess` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d AND `rev` = ?d',
Type::GUIDE, $this->typeId, $this->guideRevision);
if (!$article && $this->gPageInfo['articleUrl'])
$article = DB::Aowow()->selectRow('SELECT `article`, `locale`, `editAccess` FROM ?_articles WHERE `url` = ? AND `locale` IN (?a) ORDER BY `locale` DESC, `rev` DESC LIMIT 1',
$this->gPageInfo['articleUrl'], [Lang::getLocale()->value, Locale::EN->value]);
if (!$article && !empty($this->type) && isset($this->typeId))
$article = DB::Aowow()->selectRow('SELECT `article`, `locale`, `editAccess` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d AND `locale` IN (?a) ORDER BY `locale` DESC, `rev` DESC LIMIT 1',
$this->type, $this->typeId, [Lang::getLocale()->value, Locale::EN->value]);
if (!$article)
return;
$text = Util::defStatic($article['article']);
$opts = [];
// convert U_GROUP_* to MARKUP.CLASS_* (as seen in js-object Markup)
if ($article['editAccess'] & (U_GROUP_ADMIN | U_GROUP_VIP | U_GROUP_DEV))
$opts['allow'] = Markup::CLASS_ADMIN;
else if ($article['editAccess'] & U_GROUP_STAFF)
$opts['allow'] = Markup::CLASS_STAFF;
else if ($article['editAccess'] & U_GROUP_PREMIUM)
$opts['allow'] = Markup::CLASS_PREMIUM;
else if ($article['editAccess'] & U_GROUP_PENDING)
$opts['allow'] = Markup::CLASS_PENDING;
else
$opts['allow'] = Markup::CLASS_USER;
if (!empty($this->type) && isset($this->typeId))
$opts['dbpage'] = 1;
if ($article['locale'] != Lang::getLocale()->value)
$opts['prepend'] = '<div class="notice-box"><span class="icon-bubble">'.Lang::main('langOnly', [Lang::lang($article['locale'])]).'</span></div>';
$this->article = new Markup($text, $opts);
if ($jsg = $this->article->getJsGlobals())
$this->extendGlobalData($jsg);
$this->gPageInfo['editAccess'] = $article['editAccess'];
if (method_exists($this, 'postArticle')) // e.g. update variables in article
$this->postArticle($this->article['text']);
}
private function addCommunityContent() : void
{
$community = array(
'coError' => $_SESSION['error']['co'] ?? null,
'ssError' => $_SESSION['error']['ss'] ?? null,
'viError' => $_SESSION['error']['vi'] ?? null
);
if ($this->contribute & CONTRIBUTE_CO)
$community['co'] = Util::toJSON(CommunityContent::getComments($this->type, $this->typeId));
if ($this->contribute & CONTRIBUTE_SS)
$community['ss'] = Util::toJSON(CommunityContent::getScreenshots($this->type, $this->typeId));
if ($this->contribute & CONTRIBUTE_VI)
$community['vi'] = Util::toJSON(CommunityContent::getVideos($this->type, $this->typeId));
unset($_SESSION['error']);
// as comments are not cached, those globals cant be either
$this->extendGlobalData(CommunityContent::getJSGlobals());
$this->result->community = $community;
$this->applyGlobals();
}
/**************/
/* Generators */
/**************/
protected function generate() : void
{
$this->result = new Template\PageTemplate($this->template, $this);
foreach ($this->scripts as $s)
$this->result->addScript(...$s);
$this->result->addDataLoader(...$this->dataLoader);
// static::class so pageStatsHook defined here, can access cacheStats defined in the implementation
$this->result->registerDisplayHook('pageStats', [static::class, 'pageStatsHook']);
// only adds edit links to the staff menu: precursor to guides?
if (!($this instanceof GuideBaseResponse))
$this->gPageInfo += array(
'articleUrl' => $this->articleUrl ?? $this->fullParams, // is actually be the url-param
'editAccess' => (U_GROUP_ADMIN | U_GROUP_EDITOR | U_GROUP_BUREAU)
);
if ($this->breadcrumb)
$this->pageTemplate['breadcrumb'] = $this->breadcrumb;
if (isset($this->filter))
$this->pageTemplate['filter'] = $this->filter->query ? 1 : 0;
$this->addArticle();
$this->applyGlobals();
}
// we admit this page exists and an error occured on it
public function generateError(?string $altPageName = null) : never
{
$this->result = new Template\PageTemplate('text-page-generic', $this);
// only use own script defs
foreach (get_class_vars(self::class)['scripts'] as $s)
$this->result->addScript(...$s);
if (User::isInGroup(U_GROUP_STAFF | U_GROUP_SCREENSHOT | U_GROUP_VIDEO))
{
$this->result->addScript(SC_CSS_FILE, 'css/staff.css');
$this->result->addScript(SC_JS_FILE, 'js/staff.js');
}
$this->result->registerDisplayHook('pageStats', [self::class, 'pageStatsHook']);
$this->title[] = Lang::main('errPageTitle');
$this->h1 = Lang::main('errPageTitle');
$this->articleUrl = 'page-not-found';
$this->gPageInfo += array(
'articleUrl' => 'page-not-found',
'editAccess' => (U_GROUP_ADMIN | U_GROUP_EDITOR | U_GROUP_BUREAU)
);
$this->pageTemplate['pageName'] ??= $altPageName ?? 'page-not-found';
$this->addArticle();
$this->sumSQLStats();
$this->header[] = ['HTTP/1.0 404 Not Found', true, 404];
$this->display(true);
exit;
}
// we do not have this page
public function generateNotFound(string $title = '', string $msg = '') : never
{
$this->result = new Template\PageTemplate('text-page-generic', $this);
// only use own script defs
foreach (get_class_vars(self::class)['scripts'] as $s)
$this->result->addScript(...$s);
if (User::isInGroup(U_GROUP_STAFF | U_GROUP_SCREENSHOT | U_GROUP_VIDEO))
{
$this->result->addScript(SC_CSS_FILE, 'css/staff.css');
$this->result->addScript(SC_JS_FILE, 'js/staff.js');
}
$this->result->registerDisplayHook('pageStats', [self::class, 'pageStatsHook']);
array_unshift($this->title, Lang::main('nfPageTitle'));
$this->inputbox = ['inputbox-status', array(
'head' => isset($this->typeId) ? Util::ucWords($title).' #'.$this->typeId : $title,
'error' => !$msg && isset($this->typeId) ? Lang::main('pageNotFound', [$title]) : $msg
)];
$this->contribute = CONTRIBUTE_NONE;
if (!empty($this->breadcrumb))
$this->pageTemplate['breadcrumb'] = $this->breadcrumb;
$this->sumSQLStats();
$this->header[] = ['HTTP/1.0 404 Not Found', true, 404];
$this->display(true);
exit;
}
// display brb gnomes
public function generateMaintenance() : never
{
$this->result = new Template\PageTemplate('maintenance', $this);
$this->header[] = ['HTTP/1.0 503 Service Temporarily Unavailable', true, 503];
$this->header[] = ['Retry-After: '.(3 * HOUR)];
$this->display(true);
exit;
}
protected function display(bool $withError = false) : void
{
$this->title = Util::htmlEscape($this->title);
$this->search = Util::htmlEscape($this->search);
// can't escape >h1 here, because CharTitles legitimately add HTML
$this->addAnnouncements($withError);
if (!$withError)
$this->addCommunityContent();
// force jsGlobals from Announcements/CommunityContent into PageTemplate
// as this may be loaded from cache, it will be unlinked from its response
if ($ptJSG = $this->result->jsGlobals)
{
Util::mergeJsGlobals($ptJSG, $this->jsGlobals);
$this->result->jsGlobals = $ptJSG;
}
else if ($this->jsGlobals)
$this->result->jsGlobals = $this->jsGlobals;
if ($this instanceof ICache)
$this->applyOnCacheLoaded($this->result);
if ($this->result && $this->filterError)
$this->result->setListviewError();
$this->sumSQLStats();
// Heisenbug: IE11 and FF32 will sometimes (under unknown circumstances) cache 302 redirects and stop
// re-requesting them from the server but load them from local cache, thus breaking menu features.
$this->sendNoCacheHeader();
foreach ($this->header as $h)
header(...$h);
$this->result?->render();
}
/**********/
/* Checks */
/**********/
// has a valid combination of categories
private function isValidPage() : bool
{
if (!$this->category || !$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, fn ($x) => 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;
}
}
?>

View File

@@ -0,0 +1,163 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
trait TrTooltip
{
private array $enhancedTT = [];
public function getCacheKeyComponents() : array
{
$key = array(
$this->type, // DBType
$this->typeId, // DBTypeId
-1, // category
User::$groups, // staff mask
'' // misc (here tooltip)
);
if ($this->enhancedTT)
$key[4] = md5(serialize($this->enhancedTT));
return $key;
}
}
trait TrRss
{
private array $feedData = [];
protected function generateRSS(string $title, string $link) : string
{
$root = new SimpleXML('<rss />');
$root->addAttribute('version', '2.0');
$channel = $root->addChild('channel');
$channel->addChild('title', Cfg::get('NAME_SHORT').' - '.$title);
$channel->addChild('link', Cfg::get('HOST_URL').'/?'.$link);
$channel->addChild('description', Cfg::get('NAME'));
$channel->addChild('language', implode('-', str_split(Lang::getLocale()->json(), 2)));
$channel->addChild('ttl', Cfg::get('TTL_RSS'));
$channel->addChild('lastBuildDate', date(DATE_RSS));
foreach ($this->feedData as $row)
{
$item = $channel->addChild('item');
foreach ($row as $key => [$isCData, $attrib, $text])
{
if ($isCData && $text)
$child = $item->addChild($key)->addCData($text);
else
$child = $item->addChild($key, $text);
foreach ($attrib as $k => $v)
$child->addAttribute($k, $v);
}
}
return $root->asXML();
}
}
trait TrCommunityHelper
{
private function handleCaption(?string $caption) : string
{
if (!$caption)
return '';
// trim excessive whitespaces
$caption = trim(preg_replace('/\s{2,}/', ' ', $caption));
// shorten to fit db
$caption = substr($caption, 0, 200);
// jsEscape just in case
return Util::jsEscape($caption);
}
}
abstract class TextResponse extends BaseResponse
{
protected string $contentType = MIME_TYPE_JAVASCRIPT;
protected ?string $redirectTo = null;
protected array $params = [];
/// generation stats
protected static float $time = 0.0;
public function __construct(string $pageParam = '')
{
self::$time = microtime(true);
$this->params = explode('.', $pageParam);
// todo - validate params?
parent::__construct();
if (Cfg::get('MAINTENANCE') && !User::isInGroup(U_GROUP_EMPLOYEE))
$this->generate404();
}
// by default ajax has nothing to say
protected function onUserGroupMismatch() : never
{
trigger_error('TextResponse::onUserGroupMismatch - loggedIn: '.($this->requiresLogin ? 'yes' : 'no').'; expected: '.Util::asHex($this->requiredUserGroup).'; is: '.Util::asHex(User::$groups), E_USER_WARNING);
$this->generate403();
}
public function generate404(?string $out = null) : never
{
header('HTTP/1.0 404 Not Found', true, 404);
header($this->contentType);
exit($out);
}
public function generate403(?string $out = null) : never
{
header('HTTP/1.0 403 Forbidden', true, 403);
header($this->contentType);
exit($out);
}
protected function display() : void
{
if ($this->redirectTo)
$this->forward($this->redirectTo);
$out = ($this instanceof ICache) ? $this->applyOnCacheLoaded($this->result) : $this->result;
$this->sendNoCacheHeader();
header($this->contentType);
// NOTE - this may fuck up some javascripts that say they expect ajax, but use the whole string anyway
// so it's limited to tooltips
if (Cfg::get('DEBUG') && User::isInGroup(U_GROUP_STAFF) && $this->result instanceof Tooltip)
{
$this->sumSQLStats();
echo "/*\n";
echo " * generated in ".Util::formatTime((microtime(true) - self::$time) * 1000)."\n";
echo " * " . parent::$sql['count'] . " SQL queries in " . Util::formatTime(parent::$sql['time'] * 1000) . "\n";
if ($this instanceof ICache && static::$cacheStats)
{
[$mode, $set, $lifetime] = static::$cacheStats;
echo " * stored in " . ($mode == CACHE_MODE_MEMCACHED ? 'Memcached' : 'filecache') . ":\n";
echo " * + ".date('c', $set) . ' - ' . Util::formatTimeDiff($set) . "\n";
echo " * - ".date('c', $set + $lifetime) . ' - in '.Util::formatTime(($set + $lifetime - time()) * 1000) . "\n";
}
echo " */\n\n";
}
echo $out;
}
}
?>

View File

@@ -56,6 +56,8 @@ class UserList extends DBTypeList
public function getListviewData() : array { return []; } public function getListviewData() : array { return []; }
public function renderTooltip() : ?string { return null; } public function renderTooltip() : ?string { return null; }
public static function getName($id) : ?LocString { return null; }
} }
?> ?>

View File

@@ -17,7 +17,7 @@ define('TDB_WORLD_EXPECTED_VER', 24041);
// as of 01.01.2024 https://www.wowhead.com/wotlk/de/spell=40120/{seo} // as of 01.01.2024 https://www.wowhead.com/wotlk/de/spell=40120/{seo}
// https://www.wowhead.com/wotlk/es/search=vuelo // https://www.wowhead.com/wotlk/es/search=vuelo
define('WOWHEAD_LINK', 'https://www.wowhead.com/wotlk/%s/%s=%s'); define('WOWHEAD_LINK', 'https://www.wowhead.com/wotlk/%s/%s%s');
define('LOG_LEVEL_ERROR', 1); define('LOG_LEVEL_ERROR', 1);
define('LOG_LEVEL_WARN', 2); define('LOG_LEVEL_WARN', 2);
@@ -25,7 +25,8 @@ define('LOG_LEVEL_INFO', 3);
define('MIME_TYPE_TEXT', 'Content-Type: text/plain; charset=utf-8'); define('MIME_TYPE_TEXT', 'Content-Type: text/plain; charset=utf-8');
define('MIME_TYPE_XML', 'Content-Type: text/xml; charset=utf-8'); define('MIME_TYPE_XML', 'Content-Type: text/xml; charset=utf-8');
define('MIME_TYPE_JSON', 'Content-Type: application/x-javascript; charset=utf-8'); define('MIME_TYPE_JAVASCRIPT', 'Content-Type: application/x-javascript; charset=utf-8');
define('MIME_TYPE_JSON', 'Content-Type: application/json; charset=utf-8');
define('MIME_TYPE_OPENSEARCH', 'Content-Type: application/x-suggestions+json; charset=utf-8'); define('MIME_TYPE_OPENSEARCH', 'Content-Type: application/x-suggestions+json; charset=utf-8');
define('MIME_TYPE_RSS', 'Content-Type: application/rss+xml; charset=utf-8'); define('MIME_TYPE_RSS', 'Content-Type: application/rss+xml; charset=utf-8');
define('MIME_TYPE_JPEG', 'Content-Type: image/jpeg'); define('MIME_TYPE_JPEG', 'Content-Type: image/jpeg');

View File

@@ -68,6 +68,8 @@ spl_autoload_register(function (string $class) : void
require_once 'includes/components/'.strtolower($class).'.class.php'; require_once 'includes/components/'.strtolower($class).'.class.php';
else if (file_exists('includes/components/frontend/'.strtolower($class).'.class.php')) else if (file_exists('includes/components/frontend/'.strtolower($class).'.class.php'))
require_once 'includes/components/frontend/'.strtolower($class).'.class.php'; require_once 'includes/components/frontend/'.strtolower($class).'.class.php';
else if (file_exists('includes/components/response/'.strtolower($class).'.class.php'))
require_once 'includes/components/response/'.strtolower($class).'.class.php';
}); });
// TC systems in components // TC systems in components
@@ -121,41 +123,6 @@ spl_autoload_register(function (string $class) : void
throw new \Exception('could not register type class: '.$cl); throw new \Exception('could not register type class: '.$cl);
}); });
// endpoint loader
spl_autoload_register(function (string $class) : void
{
if ($i = strrpos($class, '\\'))
$class = substr($class, $i + 1);
if (preg_match('/[^\w]/i', $class))
return;
$class = strtolower($class);
if (stripos($class, 'ajax') === 0) // handles ajax and jsonp requests
{
if (file_exists('includes/ajaxHandler/'.strtr($class, ['ajax' => '']).'.class.php'))
{
require_once 'includes/ajaxHandler/ajaxHandler.class.php';
require_once 'includes/ajaxHandler/'.strtr($class, ['ajax' => '']).'.class.php';
}
else
throw new \Exception('could not register ajaxHandler class: '.$class);
return;
}
else if (stripos($class, 'page')) // handles templated pages
{
if (file_exists('pages/'.strtr($class, ['page' => '']).'.php'))
{
require_once 'pages/genericPage.class.php';
require_once 'pages/'.strtr($class, ['page' => '']).'.php';
}
else if ($class == 'genericpage') // may be called directly in fatal error case
require_once 'pages/genericPage.class.php';
}
});
set_error_handler(function(int $errNo, string $errStr, string $errFile, int $errLine) : bool set_error_handler(function(int $errNo, string $errStr, string $errFile, int $errLine) : bool
{ {
// either from test function or handled separately // either from test function or handled separately
@@ -208,13 +175,17 @@ set_exception_handler(function (\Throwable $e) : void
else else
{ {
Util::addNote('Exception - '.$e->getMessage().' @ '.$e->getFile(). ':'.$e->getLine()."\n".$e->getTraceAsString(), U_GROUP_EMPLOYEE, LOG_LEVEL_ERROR); Util::addNote('Exception - '.$e->getMessage().' @ '.$e->getFile(). ':'.$e->getLine()."\n".$e->getTraceAsString(), U_GROUP_EMPLOYEE, LOG_LEVEL_ERROR);
(new GenericPage())->error(); (new TemplateResponse())->generateError();
} }
}); });
// handle fatal errors // handle fatal errors
register_shutdown_function(function() : void register_shutdown_function(function() : void
{ {
// defer undisplayed error/exception notes
if (!CLI && ($n = Util::getNotes()))
$_SESSION['notes'][] = [$n[0], $n[1], 'Defered issues from previous request'];
if ($e = error_get_last()) if ($e = error_get_last())
{ {
if (DB::isConnected(DB_AOWOW)) if (DB::isConnected(DB_AOWOW))
@@ -263,7 +234,7 @@ if (!CLI)
{ {
// not displaying the brb gnomes as static_host is missing, but eh... // not displaying the brb gnomes as static_host is missing, but eh...
if (!DB::isConnected(DB_AOWOW) || !DB::isConnected(DB_WORLD) || !Cfg::get('HOST_URL') || !Cfg::get('STATIC_URL')) if (!DB::isConnected(DB_AOWOW) || !DB::isConnected(DB_WORLD) || !Cfg::get('HOST_URL') || !Cfg::get('STATIC_URL'))
(new GenericPage())->maintenance(); (new TemplateResponse())->generateMaintenance();
// Setup Session // Setup Session
$cacheDir = Cfg::get('SESSION_CACHE_DIR'); $cacheDir = Cfg::get('SESSION_CACHE_DIR');
@@ -275,7 +246,7 @@ if (!CLI)
if (!session_start()) if (!session_start())
{ {
trigger_error('failed to start session', E_USER_ERROR); trigger_error('failed to start session', E_USER_ERROR);
(new GenericPage())->error(); (new TemplateResponse())->generateError();
} }
if (User::init()) if (User::init())
@@ -300,12 +271,6 @@ if (!CLI)
if (DB::isConnected(DB_CHARACTERS . $idx)) if (DB::isConnected(DB_CHARACTERS . $idx))
DB::Characters($idx)->setLogger(DB::profiler(...)); DB::Characters($idx)->setLogger(DB::profiler(...));
} }
// parse page-parameters .. sanitize before use!
$str = explode('&', $_SERVER['QUERY_STRING'] ?? '', 2)[0];
$_ = explode('=', $str, 2);
$pageCall = mb_strtolower($_[0]);
$pageParam = $_[1] ?? '';
} }
?> ?>

View File

@@ -18,104 +18,6 @@ class SimpleXML extends \SimpleXMLElement
} }
} }
trait TrRequestData
{
// const in trait supported in php8.2+
public const PATTERN_TEXT_LINE = '/[\p{Cc}\p{Cf}\p{Co}\p{Cs}\p{Cn}]/ui';
public const PATTERN_TEXT_BLOB = '/[\x00-\x09\x0B-\x1F\p{Cf}\p{Co}\p{Cs}\p{Cn}]/ui';
protected $_get = []; // fill with variables you that are going to be used; eg:
protected $_post = []; // 'id' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkIdList']
protected $_cookie = [];
private $filtered = false;
private function initRequestData() : void
{
if ($this->filtered)
return;
// php bug? If INPUT_X is empty, filter_input_array returns null/fails
// only really relevant for INPUT_POST
// manuall set everything null in this case
if ($this->_post)
{
if ($_POST)
$this->_post = filter_input_array(INPUT_POST, $this->_post);
else
$this->_post = array_fill_keys(array_keys($this->_post), null);
}
if ($this->_get)
{
if ($_GET)
$this->_get = filter_input_array(INPUT_GET, $this->_get);
else
$this->_get = array_fill_keys(array_keys($this->_get), null);
}
if ($this->_cookie)
{
if ($_COOKIE)
$this->_cookie = filter_input_array(INPUT_COOKIE, $this->_cookie);
else
$this->_cookie = array_fill_keys(array_keys($this->_cookie), null);
}
$this->filtered = true;
}
private static function checkEmptySet(string $val) : bool
{
return $val === ''; // parameter is expected to be empty
}
private static function checkInt(string $val) : int
{
if (preg_match('/^-?\d+$/', $val))
return intVal($val);
return 0;
}
private static function checkIdList(string $val) : array
{
if (preg_match('/^-?\d+(,-?\d+)*$/', $val))
return array_map('intVal', explode(',', $val));
return [];
}
private static function checkIntArray(string $val) : array
{
if (preg_match('/^-?\d+(:-?\d+)*$/', $val))
return array_map('intVal', explode(':', $val));
return [];
}
private static function checkIdListUnsigned(string $val) : array
{
if (preg_match('/^\d+(,\d+)*$/', $val))
return array_map('intVal', explode(',', $val));
return [];
}
private static function checkTextLine(string $val) : string
{
// trim non-printable chars
return preg_replace(self::PATTERN_TEXT_LINE, '', $val);
}
private static function checkTextBlob(string $val) : string
{
// trim non-printable chars
return preg_replace(self::PATTERN_TEXT_BLOB, '', $val);
}
}
abstract class Util abstract class Util
{ {
@@ -175,9 +77,7 @@ abstract class Util
public static $guideratingString = " $(document).ready(function() {\n $('#guiderating').append(GetStars(%.10F, %s, %u, %u));\n });"; public static $guideratingString = " $(document).ready(function() {\n $('#guiderating').append(GetStars(%.10F, %s, %u, %u));\n });";
public static $expansionString = array( // 3 & 4 unused .. obviously public static $expansionString = [null, 'bc', 'wotlk'];
null, 'bc', 'wotlk', 'cata', 'mop'
);
public static $tcEncoding = '0zMcmVokRsaqbdrfwihuGINALpTjnyxtgevElBCDFHJKOPQSUWXYZ123456789'; public static $tcEncoding = '0zMcmVokRsaqbdrfwihuGINALpTjnyxtgevElBCDFHJKOPQSUWXYZ123456789';
private static $notes = []; private static $notes = [];
@@ -191,7 +91,7 @@ abstract class Util
{ {
$notes = []; $notes = [];
$severity = LOG_LEVEL_INFO; $severity = LOG_LEVEL_INFO;
foreach (self::$notes as [$note, $uGroup, $level]) foreach (self::$notes as $k => [$note, $uGroup, $level])
{ {
if ($uGroup && !User::isInGroup($uGroup)) if ($uGroup && !User::isInGroup($uGroup))
continue; continue;
@@ -200,6 +100,7 @@ abstract class Util
$severity = $level; $severity = $level;
$notes[] = $note; $notes[] = $note;
unset(self::$notes[$k]);
} }
return [$notes, $severity]; return [$notes, $severity];
@@ -309,14 +210,14 @@ abstract class Util
if ($ms) if ($ms)
return $ms."\u{00A0}".Lang::timeUnits('ab', 7); return $ms."\u{00A0}".Lang::timeUnits('ab', 7);
return '0 '.Lang::timeUnits('ab', 6); return "0\u{00A0}".Lang::timeUnits('ab', 6);
} }
else else
{ {
$_ = $d + $h / 24; $_ = $d + $h / 24;
if ($_ > 1 && !($_ % 365)) // whole years if ($_ > 1 && !($_ % 365)) // whole years
return round(($d + $h / 24) / 364, 2)."\u{00A0}".Lang::timeUnits($d / 364 == 1 && !$h ? 'sg' : 'pl', 0); return round(($d + $h / 24) / 365, 2)."\u{00A0}".Lang::timeUnits($d / 365 == 1 && !$h ? 'sg' : 'pl', 0);
if ($_ > 1 && !($_ % 30)) // whole month if ($_ > 1 && !($_ % 30)) // whole months
return round(($d + $h / 24) / 30, 2)."\u{00A0}".Lang::timeUnits($d / 30 == 1 && !$h ? 'sg' : 'pl', 1); return round(($d + $h / 24) / 30, 2)."\u{00A0}".Lang::timeUnits($d / 30 == 1 && !$h ? 'sg' : 'pl', 1);
if ($_ > 1 && !($_ % 7)) // whole weeks if ($_ > 1 && !($_ % 7)) // whole weeks
return round(($d + $h / 24) / 7, 2)."\u{00A0}".Lang::timeUnits($d / 7 == 1 && !$h ? 'sg' : 'pl', 2); return round(($d + $h / 24) / 7, 2)."\u{00A0}".Lang::timeUnits($d / 7 == 1 && !$h ? 'sg' : 'pl', 2);
@@ -329,15 +230,15 @@ abstract class Util
if ($s) if ($s)
return round($s + $ms / 1000, 2)."\u{00A0}".Lang::timeUnits($s == 1 && !$ms ? 'sg' : 'pl', 6); return round($s + $ms / 1000, 2)."\u{00A0}".Lang::timeUnits($s == 1 && !$ms ? 'sg' : 'pl', 6);
if ($ms) if ($ms)
return $ms." ".Lang::timeUnits($ms == 1 ? 'sg' : 'pl', 7); return $ms."\u{00A0}".Lang::timeUnits($ms == 1 ? 'sg' : 'pl', 7);
return '0 '.Lang::timeUnits('pl', 6); return "0\u{00A0}".Lang::timeUnits('pl', 6);
} }
} }
public static function formatTimeDiff(int $sec) : string public static function formatTimeDiff(int $sec) : string
{ {
$delta = time() - $sec; $delta = abs(time() - $sec);
[, $s, $m, $h, $d] = self::parseTime($delta * 1000); [, $s, $m, $h, $d] = self::parseTime($delta * 1000);
@@ -476,21 +377,23 @@ abstract class Util
)); ));
} }
public static function defStatic($data) public static function defStatic(array|string $data) : array|string
{ {
if (is_array($data)) if (is_array($data))
{ {
foreach ($data as &$v) foreach ($data as &$v)
if ($v)
$v = self::defStatic($v); $v = self::defStatic($v);
return $data; return $data;
} }
return strtr($data, array( return strtr($data, array(
'<script' => '<scr"+"ipt',
'script>' => 'scr"+"ipt>',
'HOST_URL' => Cfg::get('HOST_URL'), 'HOST_URL' => Cfg::get('HOST_URL'),
'STATIC_URL' => Cfg::get('STATIC_URL') 'STATIC_URL' => Cfg::get('STATIC_URL'),
'NAME' => Cfg::get('NAME'),
'NAME_SHORT' => Cfg::get('NAME_SHORT'),
'CONTACT_EMAIL' => Cfg::get('CONTACT_EMAIL')
)); ));
} }
@@ -558,7 +461,7 @@ abstract class Util
$first = mb_substr($str, 0, 1); $first = mb_substr($str, 0, 1);
$rest = mb_substr($str, 1); $rest = mb_substr($str, 1);
return mb_strtoupper($first).mb_strtolower($rest); return mb_strtoupper($first).$rest;
} }
public static function ucWords(string $str) : string public static function ucWords(string $str) : string
@@ -864,15 +767,6 @@ abstract class Util
return DB::Aowow()->query('INSERT IGNORE INTO ?_account_reputation (?#) VALUES (?a)', array_keys($x), array_values($x)); return DB::Aowow()->query('INSERT IGNORE INTO ?_account_reputation (?#) VALUES (?a)', array_keys($x), array_values($x));
} }
public static function sendNoCacheHeader()
{
header('Expires: Sat, 01 Jan 2000 01:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
}
public static function toJSON($data, $forceFlags = 0) public static function toJSON($data, $forceFlags = 0)
{ {
$flags = $forceFlags ?: (JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE); $flags = $forceFlags ?: (JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE);
@@ -884,7 +778,7 @@ abstract class Util
// handle strings prefixed with $ as js-variables // handle strings prefixed with $ as js-variables
// literal: match everything (lazy) between first pair of unescaped double quotes. First character must be $. // literal: match everything (lazy) between first pair of unescaped double quotes. First character must be $.
$json = preg_replace_callback('/(?<!\\\\)"\$(.+?)(?<!\\\\)"/i', function($m) { return str_replace('\"', '"', $m[1]); }, $json); $json = preg_replace_callback('/(?<!\\\\)"\$(.+?)(?<!\\\\)"/i', fn($m) => str_replace('\"', '"', $m[1]), $json);
return $json; return $json;
} }
@@ -1330,9 +1224,9 @@ abstract class Util
'Reply-To: ' . Cfg::get('CONTACT_EMAIL') . "\n" . 'Reply-To: ' . Cfg::get('CONTACT_EMAIL') . "\n" .
'X-Mailer: PHP/' . phpversion(); 'X-Mailer: PHP/' . phpversion();
if (Cfg::get('DEBUG') >= LOG_LEVEL_INFO && User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN)) if (Cfg::get('DEBUG') >= LOG_LEVEL_INFO)
{ {
$_SESSION['debug-mail'] = $email . "\n\n" . $subject . "\n\n" . $body; Util::addNote("Redirected from Util::sendMail:\n\nTo: " . $email . "\n\nSubject: " . $subject . "\n\n" . $body, U_GROUP_DEV | U_GROUP_ADMIN, LOG_LEVEL_INFO);
return true; return true;
} }

215
index.php
View File

@@ -8,160 +8,75 @@ if (CLI)
die("this script must not be run from CLI.\nto setup aowow use 'php aowow'\n"); die("this script must not be run from CLI.\nto setup aowow use 'php aowow'\n");
$altClass = ''; $pageCall = 'home'; // default to Homepage unless specified otherwise
switch ($pageCall) $pageParam = '';
parse_str(parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY), $query);
foreach ($query as $page => $param)
{ {
/* called by user */ $page = preg_replace('/[^\w\-]/i', '', $page);
case '': // no parameter given -> MainPage
$altClass = 'home';
case 'home':
case 'admin':
case 'account': // account management [nyi]
case 'achievement':
case 'achievements':
case 'areatrigger':
case 'areatriggers':
case 'arena-team':
case 'arena-teams':
case 'class':
case 'classes':
case 'currency':
case 'currencies':
case 'compare': // tool: item comparison
case 'emote':
case 'emotes':
case 'enchantment':
case 'enchantments':
case 'event':
case 'events':
case 'faction':
case 'factions':
case 'guide':
case 'guides':
case 'guild':
case 'guilds':
case 'icon':
case 'icons':
case 'item':
case 'items':
case 'itemset':
case 'itemsets':
case 'maps': // tool: map listing
case 'mail':
case 'mails':
case 'my-guides':
if ($pageCall == 'my-guides')
$altClass = 'guides';
case 'npc':
case 'npcs':
case 'object':
case 'objects':
case 'pet':
case 'pets':
case 'petcalc': // tool: pet talent calculator
if ($pageCall == 'petcalc')
$altClass = 'talent';
case 'profile': // character profiler [nyi]
case 'profiles': // character profile listing [nyi]
case 'profiler': // character profiler main page
case 'quest':
case 'quests':
case 'race':
case 'races':
case 'screenshot': // prepare uploaded screenshots
case 'search': // tool: searches
case 'skill':
case 'skills':
case 'sound':
case 'sounds':
case 'spell':
case 'spells':
case 'talent': // tool: talent calculator
case 'title':
case 'titles':
case 'user':
case 'video':
case 'zone':
case 'zones':
/* called by script */
case 'data': // tool: dataset-loader
case 'cookie': // lossless cookies and user settings
case 'contactus':
case 'comment':
case 'edit': // guide editor: targeted by QQ fileuploader, detail-page article editor
case 'get-description': // guide editor: shorten fulltext into description
case 'filter': // pre-evaluate filter POST-data; sanitize and forward as GET-data
case 'go-to-reply': // find page the reply is on and forward
if ($pageCall == 'go-to-reply')
$altClass = 'go-to-comment';
case 'go-to-comment': // find page the comment is on and forward
case 'locale': // subdomain-workaround, change the language
$cleanName = str_replace(['-', '_'], '', ucFirst($altClass ?: $pageCall));
try // can it be handled as ajax?
{
$out = '';
$class = __NAMESPACE__.'\\'.'Ajax'.$cleanName;
$ajax = new $class(explode('.', $pageParam));
if ($ajax->handle($out)) $pageCall = Util::lower($page);
{ $pageParam = $param ?? '';
Util::sendNoCacheHeader(); break; // only use first k/v-pair to determine page
}
if ($ajax->doRedirect) [$classMod, $file] = match (true)
header('Location: '.$out, true, 302); {
// is search ajax
isset($_GET['json']) => ['Json', $pageCall . '_json' ],
isset($_GET['opensearch']) => ['Open', $pageCall . '_open' ],
// is powered tooltip
isset($_GET['power']) => ['Power', $pageCall . '_power' ],
// is item data xml dump
isset($_GET['xml']) => ['Xml', $pageCall . '_xml' ],
// is community content feed
isset($_GET['rss']) => ['Rss', $pageCall . '_rss' ],
// is sounds playlist
isset($_GET['playlist']) => ['Playlist', $pageCall . '_playlist'],
// pageParam can be sub page
(bool)preg_match('/^[a-z\-]+$/i', $pageParam) => [Util::ucFirst(strtr($pageParam, ['-' => ''])), Util::lower($pageParam)],
// no pageParam or PageParam is param for BasePage
default => ['Base', $pageCall ]
};
// admin=X pages are mixed html and ajax on the same endpoint .. meh
if ($pageCall == 'admin' && isset($_GET['action']) && preg_match('/^[a-z]+$/', $_GET['action']))
{
$classMod .= 'Action' . Util::ucFirst($_GET['action']);
$file .= '_' . Util::lower($_GET['action']);
}
try {
$responder = new \StdClass;
// 1. try specialized response
if (file_exists('endpoints/'.$pageCall.'/'.$file.'.php'))
{
require_once 'endpoints/'.$pageCall.'/'.$file.'.php';
$class = __NAMESPACE__.'\\' . Util::ucFirst(strtr($pageCall, ['-' => ''])).$classMod.'Response';
$responder = new $class($pageParam);
}
// 2. try generalized response
else if (file_exists('endpoints/'.$pageCall.'/'.$pageCall.'.php'))
{
require_once 'endpoints/'.$pageCall.'/'.$pageCall.'.php';
$class = __NAMESPACE__.'\\' . Util::ucFirst(strtr($pageCall, ['-' => ''])).'BaseResponse';
$responder = new $class($pageParam);
}
// 3. throw .. your hands in the air and give up
if (!is_callable([$responder, 'process']))
throw new \Exception('request handler '.$pageCall.'::'.$classMod.'('.$pageParam.') not found');
$responder->process();
}
catch (\Exception $e)
{
if (isset($_GET['json']) || isset($_GET['opensearch']) || isset($_GET['power']) || isset($_GET['xml']) || isset($_GET['rss']))
(new TextResponse($pageParam))->generate404();
else else
{ (new TemplateResponse($pageParam))->generateError($pageCall);
header($ajax->getContentType());
die($out);
}
}
else
throw new \Exception('not handled as ajax');
}
catch (\Exception $e) // no, apparently not..
{
$class = __NAMESPACE__.'\\'.$cleanName.'Page';
$classInstance = new $class($pageCall, $pageParam);
if (is_callable([$classInstance, 'display']))
$classInstance->display();
else if (isset($_GET['power']))
die('$WowheadPower.register(0, '.Lang::getLocale()->value.', {})');
else // in conjunction with a proper rewriteRule in .htaccess...
(new GenericPage($pageCall))->error();
}
break;
/* other pages */
case 'whats-new':
case 'searchplugins':
case 'searchbox':
case 'tooltips':
case 'help':
case 'faq':
case 'aboutus':
case 'reputation':
case 'privilege':
case 'privileges':
case 'top-users':
(new MorePage($pageCall, $pageParam))->display();
break;
case 'latest-additions':
case 'latest-comments':
case 'latest-screenshots':
case 'latest-videos':
case 'unrated-comments':
case 'missing-screenshots':
case 'most-comments':
case 'random':
(new UtilityPage($pageCall, $pageParam))->display();
break;
default: // unk parameter given -> ErrorPage
if (isset($_GET['power']))
die('$WowheadPower.register(0, '.Lang::getLocale()->value.', {})');
else // in conjunction with a proper rewriteRule in .htaccess...
(new GenericPage($pageCall))->error();
break;
} }
?> ?>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
DELETE FROM `aowow_config` WHERE `key` IN ('rep_req_ext_links', 'gtag_measurement_id');
INSERT INTO `aowow_config` (`key`, `value`, `default`, `cat`, `flags`, `comment`) VALUES
('rep_req_ext_links', 150, 150, 5, 129, 'required reputation to link to external sites'),
('gtag_measurement_id', '', NULL, 6, 136, 'Enter your Google Tag measurement ID here to track site stats');
UPDATE `aowow_config` SET `key` = 'ua_measurement_key', `comment` = '[DEPRECATED ?] Enter your Google Universal Analytics key here to track site stats' WHERE `key` = 'analytics_user';

180
static/css/consent.css Normal file
View File

@@ -0,0 +1,180 @@
#consent-overlay .dark-filter.fade-in {
animation-name: fade-in;
animation-duration: 400ms;
animation-timing-function: ease-in-out;
}
#consent-overlay .dark-filter.hide {
display: none !important
}
#consent-overlay .dark-filter {
z-index: 2147483645;
background: rgba(0,0,0,.5);
width: 100%;
height: 100%;
overflow: hidden;
position: fixed;
top: 0;
bottom: 0;
left: 0;
}
@keyframes fade-in {
0% { opacity: 0 }
100% { opacity: 1 }
}
#consent-overlay #banner {
position: fixed;
z-index: 2147483645;
bottom: 0;
right: 0;
left: 0;
background-color: #181818;
max-height: 90%;
min-height: 125px;
overflow-x: hidden;
overflow-y: auto;
outline: none !important;
}
#consent-overlay #policy {
margin: 1.25em 0 .625em 2em;
overflow: hidden;
}
#consent-overlay #banner #policy-title {
font-size: revert;
line-height: 1.3;
margin-top: 0px;
margin-bottom: 10px;
color: #fff;
width: 50%;
}
#consent-overlay #banner #policy-text {
clear: both;
text-align: left;
line-height: 1.5;
width: 50%;
border-right: 1px solid #d8d8d8;
padding-right: 1rem;
padding-bottom: 1em
}
#consent-overlay #banner #policy-text * {
font-size: inherit;
line-height: inherit;
}
#consent-overlay #banner #policy-text a {
font-weight: bold;
margin-left: 5px;
}
#consent-overlay #banner #policy-title,
#consent-overlay #banner #policy-text {
float: left;
}
#consent-overlay #banner .columns {
width: 100%;
float: left;
box-sizing: border-box;
padding: 0;
display: initial;
margin-left: 4%;
width: 82.6666666667%;
}
#consent-overlay #banner .columns:first-child {
margin-left: 0;
}
#consent-overlay #banner #accept-btn,
#consent-overlay #banner #reject-all {
outline-offset: 1px;
}
#consent-overlay .ggl-container {
width: 45%;
padding-left: 1rem;
display: inline-block;
float: none;
}
#consent-overlay .ggl-container div,
#consent-overlay #policy-text {
border-color: #303030 !important;
}
#consent-overlay .ggl-title {
line-height: 1.7;
color: #fff;
margin-bottom: 10px;
margin-top: 0;
font-weight: 600;
font-family: inherit;
}
#consent-overlay .ggl-text {
margin-bottom: 10px;
color: #bbb;
}
#consent-overlay #banner #button-container {
left: auto;
right: 4%;
margin-left: 0;
min-height: 1px;
text-align: center;
position: absolute;
width: 13.3333333333%;
}
#consent-overlay .columns {
float: left;
box-sizing: border-box;
padding: 0;
display: initial;
}
#consent-overlay #banner #button-group {
display: inline-block;
margin-right: auto;
}
#consent-overlay #banner #button-group button {
display: block;
}
#consent-overlay #accept-btn,
#consent-overlay #reject-all {
margin-top: 1em;
margin-right: 1em;
outline-offset: 1px;
min-width: 125px;
height: auto;
white-space: normal;
word-break: break-word;
word-wrap: break-word;
padding: 12px 10px;
line-height: 1.2;
width: 100%;
background-color: #a71a19;
border-radius: 4px;
border: none;
text-decoration: none !important;
color: #fff;
font-weight: 600;
transition: 75ms;
opacity: 1 !important;
}
#consent-overlay #accept-btn:hover,
#consent-overlay #reject-all:hover {
box-shadow: none;
background-color: #da2020 !important;
border-color: #da2020 !important;
}

10
static/js/consent.js Normal file
View File

@@ -0,0 +1,10 @@
$(document).ready(function() {
$WH.qs('#consent-overlay #accept-btn').onclick = function () {
$WH.sc('consent', 1000, 1);
$WH.ge('consent-overlay').style.display = 'none';
};
$WH.qs('#consent-overlay #reject-all').onclick = function () {
$WH.sc('consent', 1000, 0);
$WH.ge('consent-overlay').style.display = 'none';
};
});

View File

@@ -1,7 +1,7 @@
<?php namespace Aowow; ?> <?php namespace Aowow; ?>
<?php <?php
if (!empty($this->contribute)): if ($this->contribute):
?> ?>
<div class="clear"></div> <div class="clear"></div>
<div class="text"> <div class="text">
@@ -11,7 +11,7 @@ if (!empty($this->contribute)):
<div class="text" style="margin-right: 310px"> <div class="text" style="margin-right: 310px">
<div class="tabbed-contents" style="clear: none"> <div class="tabbed-contents" style="clear: none">
<?php <?php
$this->localizedBrick('contrib'); $this->localizedBrick('contrib', ['coError' => $this->community['coError'], 'ssError' => $this->community['ssError'], 'viError' => $this->community['viError']]);
?> ?>
</div> </div>
</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"> <div class="footer">
<?php <?php
if (User::isInGroup(U_GROUP_EMPLOYEE) && ($this->time || isset($this->mysql) || $this->isCached)): if ($this->pageStats):
echo " <table style=\"margin:auto;\">\n"; echo " <table style=\"margin:auto;\">\n";
if (isset($this->mysql)): if ($x = $this->pageStats['sql']):
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('numSQL') .'</td><td>'.$x['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"; echo ' <tr><td style="text-align:left;">'.Lang::main('timeSQL').'</td><td>'.$x['time']."</td></tr>\n";
endif; endif;
if ($this->time): if ($x = $this->pageStats['time']):
echo ' <tr><td style="text-align:left;">Page generated in</td><td>'.Util::formatTime($this->time * 1000, true)."</td></tr>\n"; echo ' <tr><td style="text-align:left;">Page generated in</td><td>'.$x."</td></tr>\n";
endif; endif;
if ($this->cacheLoaded && $this->cacheLoaded[0] == CACHE_MODE_FILECACHE): if ($this->pageStats['cache'] && $this->pageStats['cache'][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"; echo " <tr><td style=\"text-align:left;\">Stored in filecache</td><td>".$this->pageStats['cache'][1]."</td></tr>\n";
elseif ($this->cacheLoaded && $this->cacheLoaded[0] == CACHE_MODE_MEMCACHED): elseif ($this->pageStats['cache'] && $this->pageStats['cache'][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"; echo " <tr><td style=\"text-align:left;\">Stored in Memcached</td><td>".$this->pageStats['cache'][1]."</td></tr>\n";
endif; endif;
echo " </table>\n"; echo " </table>\n";
@@ -33,15 +37,13 @@ endif;
<div id="noscript-text"><?=Lang::main('noJScript'); ?></div> <div id="noscript-text"><?=Lang::main('noJScript'); ?></div>
</noscript> </noscript>
<script type="text/javascript">DomContentLoaded.now()</script> <?=$this->localizedBrickIf($this->consentFooter, 'consent'); ?>
<?php
if (Cfg::get('DEBUG') >= LOG_LEVEL_INFO && User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN)): <?php if ($this->dbProfiles): ?>
?>
<script type="text/javascript"> <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> </script>
<?php <?php endif; ?>
endif;
?>
</body> </body>
</html> </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="Content-Type" content="text/html;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="SHORTCUT ICON" href="<?=Cfg::get('STATIC_URL'); ?>/images/logos/favicon.ico" /> <link rel="SHORTCUT ICON" href="<?=$this->gStaticUrl; ?>/images/logos/favicon.ico" />
<link rel="search" type="application/opensearchdescription+xml" href="<?=Cfg::get('STATIC_URL'); ?>/download/searchplugins/aowow.xml" title="Aowow" /> <link rel="search" type="application/opensearchdescription+xml" href="<?=$this->gStaticUrl; ?>/download/searchplugins/aowow.xml" title="<?=Lang::main('search');?>" />
<?php if (isset($this->rss)): ?> <?=$this->renderArray('css', 4); ?>
<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;
?>
<script type="text/javascript"> <script type="text/javascript">
var g_serverTime = new Date('<?=date(Util::$dateFormatInternal); ?>'); var g_serverTime = <?=$this->gServerTime; ?>;
var g_staticUrl = "<?=Cfg::get('STATIC_URL'); ?>"; var g_staticUrl = "<?=$this->gStaticUrl; ?>";
var g_host = "<?=Cfg::get('HOST_URL'); ?>"; var g_host = "<?=$this->gHost; ?>";
<?php <?php
if ($this->gDataKey): if ($this->gDataKey):
echo " var g_dataKey = '".$_SESSION['dataKey']."'\n"; echo " var g_dataKey = '".$_SESSION['dataKey']."'\n";
endif; endif;
?> ?>
</script> </script>
<?=$this->renderArray('js', 4); ?>
<script type="text/javascript">
var g_user = <?=$this->json($this->user::getUserGlobal()); ?>;
<?php <?php
foreach ($this->js as [$type, $js]): if ($fav = $this->user::getFavorites()):
if ($type == SC_JS_FILE): echo " g_favorites = ".$this->json($fav).";\n";
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";
endif; endif;
?> ?>
</script> </script>
<?php <?php if ($this->analyticsTag): ?>
if (Cfg::get('ANALYTICS_USER')): <script async src="https://www.googletagmanager.com/gtag/js?id=<?=$this->analyticsTag; ?>"></script>
?>
<script> <script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ window.dataLayer = window.dataLayer || [];
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), function gtag(){dataLayer.push(arguments);}
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) gtag('js', new Date());
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); gtag('config', '<?=$this->analyticsTag; ?>');
ga('create', '<?=Cfg::get('ANALYTICS_USER'); ?>', 'auto');
ga('send', 'pageview');
</script> </script>
<?php <?php
endif; 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 <?php
if (!empty($this->headIcons)): if ($this->headIcons):
foreach ($this->headIcons as $k => $v): foreach ($this->headIcons as $k => $v):
echo '<div id="h1-icon-'.$k."\" class=\"h1-icon\"></div>\n"; echo '<div id="h1-icon-'.$k."\" class=\"h1-icon\"></div>\n";
endforeach; endforeach;
@@ -9,7 +11,7 @@ if (!empty($this->headIcons)):
<script type="text/javascript">//<![CDATA[ <script type="text/javascript">//<![CDATA[
<?php <?php
foreach ($this->headIcons as $k => $v): 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; endforeach;
?> ?>
//]]></script> //]]></script>

View File

@@ -1,12 +1,13 @@
<?php namespace Aowow; ?> <?php
namespace Aowow\Template;
?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<?php $this->brick('head'); ?> <?php $this->brick('head'); ?>
</head> </head>
<body<?=(User::isPremium() ? ' class="premium-logo"' : null); ?>> <body<?=($this->user::isPremium() ? ' class="premium-logo"' : ''); ?>>
<div id="layers"></div> <div id="layers"></div>
<?php if ($this->headerLogo): ?> <?php if ($this->headerLogo): ?>
<style type="text/css"> <style type="text/css">
@@ -21,18 +22,18 @@
<div class="header" id="header"> <div class="header" id="header">
<div id="header-logo"> <div id="header-logo">
<a class="header-logo" href="."></a> <a class="header-logo" href="."></a>
<h1><?=Util::htmlEscape($this->name); ?></h1> <h1><?=$this->concat('title', ' - '); ?></h1>
</div> </div>
</div> </div>
<div id="wrapper" class="wrapper"> <div id="wrapper" class="wrapper">
<div class="toplinks linklist"><?php $this->brick('headerMenu'); ?></div> <div class="toplinks linklist"><?php $this->brick('headerMenu'); ?></div>
<div class="toptabs" id="toptabs"></div> <div class="toptabs" id="toptabs"></div>
<div class="topbar" id="topbar"> <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-browse" id="topbar-browse"></div>
<div class="topbar-buttons" id="topbar-buttons"></div> <div class="topbar-buttons" id="topbar-buttons"></div>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
<?=$this->writeGlobalVars(); ?> <?=$this->renderGlobalVars(12); ?>
</script> </script>

View File

@@ -1,10 +1,14 @@
<?php namespace Aowow; ?> <?php
namespace Aowow\Template;
use \Aowow\Lang;
?>
<?php <?php
if (User::isLoggedIn()): if ($this->user::isLoggedIn()):
echo '<span id="toplinks-favorites"><a class="hassubmenu"></a>|</span>'; echo '<span id="toplinks-favorites"><a class="hassubmenu"></a>|</span>';
echo '<a id="toplinks-user">'.User::$username.'</a>'; echo '<a id="toplinks-user">'.$this->user::$username.'</a>';
echo '<span id="toplinks-rep" title="'.Lang::main('reputationTip').'">(<a href="?reputation">'.User::getReputation().'</a>)</span>'; echo '<span id="toplinks-rep" title="'.Lang::main('reputationTip').'">(<a href="?reputation">'.$this->user::getReputation().'</a>)</span>';
else: else:
echo '<a href="?account=signin">'.Lang::main('signIn').'</a>'; echo '<a href="?account=signin">'.Lang::main('signIn').'</a>';
endif; 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[ <script type="text/javascript">//<![CDATA[
<?php <?php
if ($this->contribute & CONTRIBUTE_CO): if ($this->contribute & CONTRIBUTE_CO):
echo " var lv_comments = ".Util::toJSON($this->community['co']).";\n"; echo " var lv_comments = ".$this->community['co'].";\n";
endif; endif;
if ($this->contribute & CONTRIBUTE_SS): if ($this->contribute & CONTRIBUTE_SS):
echo " var lv_screenshots = ".$this->community['ss'].";\n";
echo " var lv_screenshots = ".Util::toJSON($this->community['ss']).";\n";
endif; endif;
if ($this->contribute & CONTRIBUTE_VI): if ($this->contribute & CONTRIBUTE_VI):
echo " var lv_videos = ".Util::toJSON($this->community['vi']).";\n"; echo " var lv_videos = ".$this->community['vi'].";\n";
endif; endif;
if (!empty($this->gPageInfo)): if ($this->gPageInfo):
echo " var g_pageInfo = ".Util::toJSON($this->gPageInfo).";\n"; echo " var g_pageInfo = ".$this->json('gPageInfo').";\n";
// only used by item.php // set by ItemBaseEndpoint
if (User::isLoggedIn() && isset($this->redButtons[BUTTON_EQUIP])): if ($this->user::isLoggedIn() && !empty($this->redButtons[BUTTON_EQUIP])):
echo " DomContentLoaded.addEvent(function() { pr_addEquipButton('equip-pinned-button', ".$this->typeId."); });\n"; echo " \$(document).ready(function() { pr_addEquipButton('equip-pinned-button', ".$this->typeId."); });\n";
endif; endif;
endif; endif;
if (!empty($this->pageTemplate)): if ($this->pageTemplate):
if (Lang::getLocale()->value && $this->pageTemplate['pageName'] != 'home'): if ($this->locale->value && $this->pageTemplate['pageName'] != 'home'):
echo " Locale.set(".Lang::getLocale()->value.");\n"; echo " Locale.set(".$this->locale->value.");\n";
endif; endif;
echo " PageTemplate.set(".$this->json('pageTemplate').");\n";
echo " PageTemplate.set(".Util::toJSON($this->pageTemplate).");\n"; endif;
echo " PageTemplate.init();\n"; echo " PageTemplate.init();\n";
endif;
if (isset($fiQuery) && count($fiMenuItem) > 1 && array_slice($fiMenuItem, 0, 2) == [1, 5]): 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)): 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; endif;
?> ?>
//]]></script> //]]></script>

View File

@@ -1,4 +1,8 @@
<?php namespace Aowow; ?> <?php
namespace Aowow\Template;
use \Aowow\Lang;
?>
<?php <?php
// link to wowhead // link to wowhead
@@ -18,7 +22,7 @@ endif;
// ingame-links/markdown/ect // ingame-links/markdown/ect
if (isset($this->redButtons[BUTTON_LINKS])): if (isset($this->redButtons[BUTTON_LINKS])):
if ($b = $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: 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>'; 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; endif;
@@ -27,7 +31,7 @@ endif;
// view in 3D // view in 3D
if (isset($this->redButtons[BUTTON_VIEW3D])): 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 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: 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>'; 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; endif;
@@ -36,7 +40,7 @@ endif;
// item comparison tool // item comparison tool
if (isset($this->redButtons[BUTTON_COMPARE])): if (isset($this->redButtons[BUTTON_COMPARE])):
if ($b = $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: 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>'; 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; endif;

View File

@@ -0,0 +1,25 @@
<div id="consent-overlay">
<div class="dark-filter fade-in"></div>
<div id="banner" tabindex="0">
<div id="group-container" class="columns">
<div id="policy">
<h2 id="policy-title">Privacy Notice</h2>
<div id="policy-text">
This website uses Cookies and Local Storage to manage your session and remember your user settings. We also use Google Analytics to collect anonymous usage statistics and improve our services. You may choose to opt out of Google Analytics tracking, but session management and user settings storage are required for the website to operate. For more details, please review our Privacy Policy.
</div>
<div class="ggl-container">
<h3 class="ggl-title">Google Tag Manager:</h3>
<div class="ggl-text">
Google Tag Manager is used to collect anonymous usage data to improve website performance and user experience. No personally identifiable information is stored or shared.<br /><br /><a href="https://policies.google.com/technologies/partner-sites" target="_blank">Google Privacy Policy and Terms of Use</a>
</div>
</div>
</div>
</div>
<div id="button-container" class="columns">
<div id="button-group">
<button id="accept-btn">Accept</button>
<button id="reject-all">Reject</button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,25 @@
<div id="consent-overlay">
<div class="dark-filter fade-in"></div>
<div id="banner" tabindex="0">
<div id="group-container" class="columns">
<div id="policy">
<h2 id="policy-title">Avis de confidentialité</h2>
<div id="policy-text">
Ce site utilise des cookies et le stockage local pour gérer votre session et mémoriser vos paramètres utilisateur. Nous utilisons également Google Analytics pour collecter des statistiques d'utilisation anonymes et améliorer nos services. Vous pouvez choisir de refuser le suivi Google Analytics, mais la gestion de session et le stockage des paramètres utilisateur sont nécessaires au fonctionnement du site. Pour plus de détails, veuillez consulter notre politique de confidentialité.
</div>
<div class="ggl-container">
<h3 class="ggl-title">Google Tag Manager :</h3>
<div class="ggl-text">
Google Tag Manager est utilisé pour collecter des données d'utilisation anonymes afin d'améliorer les performances et l'expérience utilisateur du site. Aucune information personnellement identifiable n'est stockée ou partagée.<br /><br /><a href="https://policies.google.com/technologies/partner-sites" target="_blank">Politique de confidentialité et conditions d'utilisation de Google</a>
</div>
</div>
</div>
</div>
<div id="button-container" class="columns">
<div id="button-group">
<button id="accept-btn">Accepter</button>
<button id="reject-all">Tout refuser</button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,25 @@
<div id="consent-overlay">
<div class="dark-filter fade-in"></div>
<div id="banner" tabindex="0">
<div id="group-container" class="columns">
<div id="policy">
<h2 id="policy-title">Datenschutzhinweis</h2>
<div id="policy-text">
Diese Website verwendet Cookies und lokalen Speicher, um Ihre Sitzung zu verwalten und Ihre Benutzereinstellungen zu speichern. Wir verwenden außerdem Google Analytics, um anonyme Nutzungsstatistiken zu sammeln und unsere Dienste zu verbessern. Sie können sich gegen das Tracking durch Google Analytics entscheiden, aber Sitzungsverwaltung und Speicherung der Benutzereinstellungen sind für den Betrieb der Website erforderlich. Weitere Informationen finden Sie in unserer Datenschutzerklärung.
</div>
<div class="ggl-container">
<h3 class="ggl-title">Google Tag Manager:</h3>
<div class="ggl-text">
Der Google Tag Manager ermöglicht es uns anonyme Nutzungsdaten zu sammeln, um die Leistung und Benutzererfahrung der Website zu verbessern. Es werden keine personenbezogenen Daten gespeichert oder weitergegeben.<br /><br /><a href="https://policies.google.com/technologies/partner-sites" target="_blank">Datenschutzerklärung & Nutzungsbedingungen von Google</a>
</div>
</div>
</div>
</div>
<div id="button-container" class="columns">
<div id="button-group">
<button id="accept-btn">Akzeptieren</button>
<button id="reject-all">Ablehnen</button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,25 @@
<div id="consent-overlay">
<div class="dark-filter fade-in"></div>
<div id="banner" tabindex="0">
<div id="group-container" class="columns">
<div id="policy">
<h2 id="policy-title">隐私声明</h2>
<div id="policy-text">
本网站使用Cookies和本地存储来管理您的会话并记住您的用户设置。我们还使用Google Analytics收集匿名使用统计数据以改善我们的服务。您可以选择拒绝Google Analytics的跟踪但会话管理和用户设置存储是网站运行所必需的。更多详情请查阅我们的隐私政策。
</div>
<div class="ggl-container">
<h3 class="ggl-title">Google Tag Manager</h3>
<div class="ggl-text">
Google Tag Manager用于收集匿名使用数据以提升网站性能和用户体验。不存储或共享任何可识别个人身份的信息。<br /><br /><a href="https://policies.google.com/technologies/partner-sites" target="_blank">Google隐私政策和使用条款</a>
</div>
</div>
</div>
</div>
<div id="button-container" class="columns">
<div id="button-group">
<button id="accept-btn">接受</button>
<button id="reject-all">全部拒绝</button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,25 @@
<div id="consent-overlay">
<div class="dark-filter fade-in"></div>
<div id="banner" tabindex="0">
<div id="group-container" class="columns">
<div id="policy">
<h2 id="policy-title">Aviso de privacidad</h2>
<div id="policy-text">
Este sitio web utiliza Cookies y almacenamiento local para gestionar su sesión y recordar sus configuraciones de usuario. También utilizamos Google Analytics para recopilar estadísticas de uso anónimas y mejorar nuestros servicios. Puede optar por rechazar el seguimiento de Google Analytics, pero la gestión de la sesión y el almacenamiento de configuraciones de usuario son necesarios para el funcionamiento del sitio web. Para más detalles, consulte nuestra Política de Privacidad.
</div>
<div class="ggl-container">
<h3 class="ggl-title">Google Tag Manager:</h3>
<div class="ggl-text">
Google Tag Manager se utiliza para recopilar datos de uso anónimos y mejorar el rendimiento y la experiencia del usuario del sitio web. No se almacena ni comparte información personal identificable.<br /><br /><a href="https://policies.google.com/technologies/partner-sites" target="_blank">Política de privacidad y términos de uso de Google</a>
</div>
</div>
</div>
</div>
<div id="button-container" class="columns">
<div id="button-group">
<button id="accept-btn">Aceptar</button>
<button id="reject-all">Rechazar todo</button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,25 @@
<div id="consent-overlay">
<div class="dark-filter fade-in"></div>
<div id="banner" tabindex="0">
<div id="group-container" class="columns">
<div id="policy">
<h2 id="policy-title">Уведомление о конфиденциальности</h2>
<div id="policy-text">
Этот сайт использует Cookies и локальное хранилище для управления вашей сессией и запоминания пользовательских настроек. Мы также используем Google Analytics для сбора анонимной статистики использования и улучшения наших сервисов. Вы можете отказаться от отслеживания Google Analytics, но управление сессией и хранение пользовательских настроек необходимы для работы сайта. Подробнее смотрите нашу Политику конфиденциальности.
</div>
<div class="ggl-container">
<h3 class="ggl-title">Google Tag Manager:</h3>
<div class="ggl-text">
Google Tag Manager используется для сбора анонимных данных об использовании сайта, чтобы повысить производительность и удобство использования. Личные данные не сохраняются и не передаются третьим лицам.<br /><br /><a href="https://policies.google.com/technologies/partner-sites" target="_blank">Политика конфиденциальности и условия использования Google</a>
</div>
</div>
</div>
</div>
<div id="button-container" class="columns">
<div id="button-group">
<button id="accept-btn">Принять</button>
<button id="reject-all">Отклонить все</button>
</div>
</div>
</div>
</div>

View File

@@ -1,4 +1,6 @@
<?php namespace Aowow; ?> <?php
namespace Aowow\Template;
?>
<div id="tab-add-your-comment" style="display: none"> <div id="tab-add-your-comment" style="display: none">
Please keep the following in mind when posting a comment: Please keep the following in mind when posting a comment:
@@ -9,9 +11,9 @@
<li><div>You might want to proof-read your comments before posting them.</div></li> <li><div>You might want to proof-read your comments before posting them.</div></li>
</ul> </ul>
<?php <?php
echo $this->coError ? ' <div class="msg-failure">'.$this->coError."</div>\n <div class=\"pad\"></div>\n" : ''; echo $coError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$coError."</div>\n" : '';
if (User::canComment()): if ($this->user::canComment()):
?> ?>
<form name="addcomment" action="?comment=add&amp;type=<?=$this->type.'&amp;typeid='.$this->typeId; ?>" method="post" onsubmit="return co_validateForm(this)"> <form name="addcomment" action="?comment=add&amp;type=<?=$this->type.'&amp;typeid='.$this->typeId; ?>" method="post" onsubmit="return co_validateForm(this)">
<div id="funcbox-generic"></div> <div id="funcbox-generic"></div>
@@ -25,7 +27,7 @@
<div class="comment-edit-body"><textarea class="comment-editbox" rows="10" cols="40" name="commentbody" disabled="disabled"></textarea></div> <div class="comment-edit-body"><textarea class="comment-editbox" rows="10" cols="40" name="commentbody" disabled="disabled"></textarea></div>
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>You are not logged in. Please <a href="?account=signin">log in</a> or <a href="?account=signup">register an account</a> to add your comment.</small> <small>You are not logged in. Please <a href="?account=signin">log in</a> or <a href="?account=signup">register an account</a> to add your comment.</small>
<?php <?php
@@ -41,9 +43,9 @@
<li><div>Be sure to read the <a href="?help=screenshots-tips-tricks" target="_blank">tips &amp; tricks</a> if you haven't before.</div></li> <li><div>Be sure to read the <a href="?help=screenshots-tips-tricks" target="_blank">tips &amp; tricks</a> if you haven't before.</div></li>
</ul> </ul>
<?php <?php
echo $this->ssError ? ' <div class="msg-failure">'.$this->ssError."</div>\n <div class=\"pad\"></div>\n" : ''; echo $ssError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$ssError."</div>\n" : '';
if (User::canUploadScreenshot()): if ($this->user::canUploadScreenshot()):
?> ?>
<form action="?screenshot=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return ss_validateForm(this)"> <form action="?screenshot=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return ss_validateForm(this)">
<input type="file" name="screenshotfile" style="width: 35%"/><br /> <input type="file" name="screenshotfile" style="width: 35%"/><br />
@@ -58,7 +60,7 @@
<input type="file" name="screenshotfile" disabled="disabled" /><br /> <input type="file" name="screenshotfile" disabled="disabled" /><br />
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>You are not signed in. Please <a href="?account=signin">sign in</a> to submit a screenshot.</small> <small>You are not signed in. Please <a href="?account=signin">sign in</a> to submit a screenshot.</small>
<?php <?php
@@ -69,7 +71,9 @@
<div id="tab-suggest-a-video" style="display: none"> <div id="tab-suggest-a-video" style="display: none">
Simply type the URL of the video in the form below. Simply type the URL of the video in the form below.
<?php <?php
if (User::canSuggestVideo()): echo $viError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$viError."</div>\n" : '';
if ($this->user::canSuggestVideo()):
?> ?>
<div class="pad2"></div> <div class="pad2"></div>
<form action="?video=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return vi_validateForm(this)"> <form action="?video=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return vi_validateForm(this)">
@@ -85,7 +89,7 @@
<input type="text" name="videourl" disabled="disabled" /><br /> <input type="text" name="videourl" disabled="disabled" /><br />
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>You are not signed in. Please <a href="?account=signin">sign in</a> to submit a video.</small> <small>You are not signed in. Please <a href="?account=signin">sign in</a> to submit a video.</small>
<?php <?php

View File

@@ -1,4 +1,6 @@
<?php namespace Aowow; ?> <?php
namespace Aowow\Template;
?>
<div id="tab-add-your-comment" style="display: none"> <div id="tab-add-your-comment" style="display: none">
Gardez à l'esprit les points suivant avant de poster un commentaire : Gardez à l'esprit les points suivant avant de poster un commentaire :
@@ -9,9 +11,9 @@
<li><div>Il serait avisé de corriger vos fautes avant de soumettre vos commentaires.</div></li> <li><div>Il serait avisé de corriger vos fautes avant de soumettre vos commentaires.</div></li>
</ul> </ul>
<?php <?php
echo $this->coError ? ' <div class="msg-failure">'.$this->coError."</div>\n <div class=\"pad\"></div>\n" : ''; echo $coError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$coError."</div>\n" : '';
if (User::canComment()): if ($this->user::canComment()):
?> ?>
<form name="addcomment" action="?comment=add&amp;type=<?=$this->type.'&amp;typeid='.$this->typeId; ?>" method="post" onsubmit="return co_validateForm(this)"> <form name="addcomment" action="?comment=add&amp;type=<?=$this->type.'&amp;typeid='.$this->typeId; ?>" method="post" onsubmit="return co_validateForm(this)">
<div id="funcbox-generic"></div> <div id="funcbox-generic"></div>
@@ -25,7 +27,7 @@
<div class="comment-edit-body"><textarea class="comment-editbox" rows="10" cols="40" name="commentbody" disabled="disabled"></textarea></div> <div class="comment-edit-body"><textarea class="comment-editbox" rows="10" cols="40" name="commentbody" disabled="disabled"></textarea></div>
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>Vous n'êtes pas connecté(e). Veuillez vous <a href="?account=signin">connecter</a> ou vous <a href="?account=signup">inscrire</a> pour ajouter votre commentaire.</small> <small>Vous n'êtes pas connecté(e). Veuillez vous <a href="?account=signin">connecter</a> ou vous <a href="?account=signup">inscrire</a> pour ajouter votre commentaire.</small>
<?php <?php
@@ -41,9 +43,9 @@
<li><div>Assurez-vous de lire les <a href="?help=screenshots-tips-tricks" target="_blank">trucs et astuces</a> si ce n'est pas déjà fait.</div></li> <li><div>Assurez-vous de lire les <a href="?help=screenshots-tips-tricks" target="_blank">trucs et astuces</a> si ce n'est pas déjà fait.</div></li>
</ul> </ul>
<?php <?php
echo $this->ssError ? ' <div class="msg-failure">'.$this->ssError."</div>\n <div class=\"pad\"></div>\n" : ''; echo $ssError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$ssError."</div>\n" : '';
if (User::canUploadScreenshot()): if ($this->user::canUploadScreenshot()):
?> ?>
<form action="?screenshot=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return ss_validateForm(this)"> <form action="?screenshot=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return ss_validateForm(this)">
<input type="file" name="screenshotfile" style="width: 35%"/><br /> <input type="file" name="screenshotfile" style="width: 35%"/><br />
@@ -58,7 +60,7 @@
<input type="file" name="screenshotfile" disabled="disabled" /><br /> <input type="file" name="screenshotfile" disabled="disabled" /><br />
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>Vous n'êtes pas connecté(e). Veuillez vous <a href="?account=signin">connecter</a> pour envoyer une capture d'écran.</small> <small>Vous n'êtes pas connecté(e). Veuillez vous <a href="?account=signin">connecter</a> pour envoyer une capture d'écran.</small>
<?php <?php
@@ -69,7 +71,9 @@
<div id="tab-suggest-a-video" style="display: none"> <div id="tab-suggest-a-video" style="display: none">
Entrez simplement l'URL du vidéo dans le formulaire ci-dessous. Entrez simplement l'URL du vidéo dans le formulaire ci-dessous.
<?php <?php
if (User::canSuggestVideo()): echo $viError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$viError."</div>\n" : '';
if ($this->user::canSuggestVideo()):
?> ?>
<div class="pad2"></div> <div class="pad2"></div>
<form action="?video=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return vi_validateForm(this)"> <form action="?video=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return vi_validateForm(this)">
@@ -85,7 +89,7 @@
<input type="text" name="videourl" disabled="disabled" /><br /> <input type="text" name="videourl" disabled="disabled" /><br />
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>Vous n'êtes pas connecté(e). Veuillez vous <a href="?account=signin">connecter</a> pour envoyer une vidéo.</small> <small>Vous n'êtes pas connecté(e). Veuillez vous <a href="?account=signin">connecter</a> pour envoyer une vidéo.</small>
<?php <?php

View File

@@ -1,4 +1,6 @@
<?php namespace Aowow; ?> <?php
namespace Aowow\Template;
?>
<div id="tab-add-your-comment" style="display: none"> <div id="tab-add-your-comment" style="display: none">
Denkt bitte an Folgendes, wenn Ihr einen Kommentar schreibt: Denkt bitte an Folgendes, wenn Ihr einen Kommentar schreibt:
@@ -9,9 +11,9 @@
<li><div>Stellt Eure Fragen bitte in unseren <a href="?forums">Foren</a>, wenn Ihr eine schnellere Antwort wünscht.</div></li> <li><div>Stellt Eure Fragen bitte in unseren <a href="?forums">Foren</a>, wenn Ihr eine schnellere Antwort wünscht.</div></li>
</ul> </ul>
<?php <?php
echo $this->coError ? ' <div class="msg-failure">'.$this->coError."</div>\n <div class=\"pad\"></div>\n" : ''; echo $coError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$coError."</div>\n" : '';
if (User::canComment()): if ($this->user::canComment()):
?> ?>
<form name="addcomment" action="?comment=add&amp;type=<?=$this->type.'&amp;typeid='.$this->typeId; ?>" method="post" onsubmit="return co_validateForm(this)"> <form name="addcomment" action="?comment=add&amp;type=<?=$this->type.'&amp;typeid='.$this->typeId; ?>" method="post" onsubmit="return co_validateForm(this)">
<div id="funcbox-generic"></div> <div id="funcbox-generic"></div>
@@ -25,7 +27,7 @@
<div class="comment-edit-body"><textarea class="comment-editbox" rows="10" cols="40" name="commentbody" disabled="disabled"></textarea></div> <div class="comment-edit-body"><textarea class="comment-editbox" rows="10" cols="40" name="commentbody" disabled="disabled"></textarea></div>
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>Ihr seid nicht angemeldet. Bitte <a href="?account=signin">meldet Euch an</a>, oder <a href="?account=signup">registriert Euch</a>, um einen Kommentar einzusenden.</small> <small>Ihr seid nicht angemeldet. Bitte <a href="?account=signin">meldet Euch an</a>, oder <a href="?account=signup">registriert Euch</a>, um einen Kommentar einzusenden.</small>
<?php <?php
@@ -41,9 +43,9 @@
<li><div>Lest Euch unbedingt die <a href="?help=screenshots-tips-tricks" target="_blank">Tipps &amp; Tricks</a> durch, wenn nicht bereits geschehen.</div></li> <li><div>Lest Euch unbedingt die <a href="?help=screenshots-tips-tricks" target="_blank">Tipps &amp; Tricks</a> durch, wenn nicht bereits geschehen.</div></li>
</ul> </ul>
<?php <?php
echo $this->ssError ? ' <div class="msg-failure">'.$this->ssError."</div>\n <div class=\"pad\"></div>\n" : ''; echo $ssError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$ssError."</div>\n" : '';
if (User::canUploadScreenshot()): if ($this->user::canUploadScreenshot()):
?> ?>
<form action="?screenshot=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return ss_validateForm(this)"> <form action="?screenshot=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return ss_validateForm(this)">
<input type="file" name="screenshotfile" style="width: 35%"/><br /> <input type="file" name="screenshotfile" style="width: 35%"/><br />
@@ -58,7 +60,7 @@
<input type="file" name="screenshotfile" disabled="disabled" /><br /> <input type="file" name="screenshotfile" disabled="disabled" /><br />
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>Ihr seid nicht angemeldet. Bitte <a href="?account=signin">meldet Euch an</a>, um einen Screenshot einzusenden.</small> <small>Ihr seid nicht angemeldet. Bitte <a href="?account=signin">meldet Euch an</a>, um einen Screenshot einzusenden.</small>
<?php <?php
@@ -69,7 +71,9 @@
<div id="tab-suggest-a-video" style="display: none"> <div id="tab-suggest-a-video" style="display: none">
Gebt einfach die URL des Videos im folgenden Formular ein. Gebt einfach die URL des Videos im folgenden Formular ein.
<?php <?php
if (User::canSuggestVideo()): echo $viError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$viError."</div>\n" : '';
if ($this->user::canSuggestVideo()):
?> ?>
<div class="pad2"></div> <div class="pad2"></div>
<form action="?video=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return vi_validateForm(this)"> <form action="?video=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return vi_validateForm(this)">
@@ -85,7 +89,7 @@
<input type="text" name="videourl" disabled="disabled" /><br /> <input type="text" name="videourl" disabled="disabled" /><br />
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>Ihr seid nicht angemeldet. Bitte <a href="?account=signin">meldet Euch an</a>, um ein Video vorzuschlagen.</small> <small>Ihr seid nicht angemeldet. Bitte <a href="?account=signin">meldet Euch an</a>, um ein Video vorzuschlagen.</small>
<?php <?php

View File

@@ -1,4 +1,6 @@
<?php namespace Aowow; ?> <?php
namespace Aowow\Template;
?>
<div id="tab-add-your-comment" style="display: none"> <div id="tab-add-your-comment" style="display: none">
发表评论时请记住以下几点: 发表评论时请记住以下几点:
@@ -9,15 +11,15 @@
<li><div>你在发表前最好先预览下你的评论。</div></li> <li><div>你在发表前最好先预览下你的评论。</div></li>
</ul> </ul>
<?php <?php
echo $this->coError ? ' <div class="msg-failure">'.$this->coError."</div>\n <div class=\"pad\"></div>\n" : ''; echo $coError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$coError."</div>\n" : '';
if (User::canComment()): if ($this->user::canComment()):
?> ?>
<form name="addcomment" action="?comment=add&amp;type=<?=$this->type.'&amp;typeid='.$this->typeId; ?>" method="post" onsubmit="return co_validateForm(this)"> <form name="addcomment" action="?comment=add&amp;type=<?=$this->type.'&amp;typeid='.$this->typeId; ?>" method="post" onsubmit="return co_validateForm(this)">
<div id="funcbox-generic"></div> <div id="funcbox-generic"></div>
<script type="text/javascript">Listview.funcBox.coEditAppend($('#funcbox-generic'), {body: ''}, 1)</script> <script type="text/javascript">Listview.funcBox.coEditAppend($('#funcbox-generic'), {body: ''}, 1)</script>
<div class="pad"></div> <div class="pad"></div>
<input type="submit" value="Submit"></input> <input type="submit" value="提交"></input>
<?php <?php
else: else:
?> ?>
@@ -25,7 +27,7 @@
<div class="comment-edit-body"><textarea class="comment-editbox" rows="10" cols="40" name="commentbody" disabled="disabled"></textarea></div> <div class="comment-edit-body"><textarea class="comment-editbox" rows="10" cols="40" name="commentbody" disabled="disabled"></textarea></div>
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>你尚未登录,请先<a href="?account=signin">登录</a>或<a href="?account=signup">注册一个账号</a> 以发表你的评论。</small> <small>你尚未登录,请先<a href="?account=signin">登录</a>或<a href="?account=signup">注册一个账号</a> 以发表你的评论。</small>
<?php <?php
@@ -41,14 +43,14 @@
<li><div>请阅读我们的<a href="?help=screenshots-tips-tricks" target="_blank">提示和技巧</a>假如你还没看过的话。</div></li> <li><div>请阅读我们的<a href="?help=screenshots-tips-tricks" target="_blank">提示和技巧</a>假如你还没看过的话。</div></li>
</ul> </ul>
<?php <?php
echo $this->ssError ? ' <div class="msg-failure">'.$this->ssError."</div>\n <div class=\"pad\"></div>\n" : ''; echo $ssError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$ssError."</div>\n" : '';
if (User::canUploadScreenshot()): if ($this->user::canUploadScreenshot()):
?> ?>
<form action="?screenshot=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return ss_validateForm(this)"> <form action="?screenshot=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return ss_validateForm(this)">
<input type="file" name="screenshotfile" style="width: 35%"/><br /> <input type="file" name="screenshotfile" style="width: 35%"/><br />
<div class="pad2"></div> <div class="pad2"></div>
<input type="submit" value="Submit" /> <input type="submit" value="提交" />
<div class="pad3"></div> <div class="pad3"></div>
<small class="q0">注意:你的截图将在审查后才会出现在站点上。</small> <small class="q0">注意:你的截图将在审查后才会出现在站点上。</small>
<?php <?php
@@ -58,7 +60,7 @@
<input type="file" name="screenshotfile" disabled="disabled" /><br /> <input type="file" name="screenshotfile" disabled="disabled" /><br />
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>你尚未登录,请先<a href="?account=signin">登录</a>以提交截图。</small> <small>你尚未登录,请先<a href="?account=signin">登录</a>以提交截图。</small>
<?php <?php
@@ -67,17 +69,19 @@
</form> </form>
</div> </div>
<div id="tab-suggest-a-video" style="display: none"> <div id="tab-suggest-a-video" style="display: none">
Simply type the URL of the video in the form below. 将视频URL输入下列表格即可。
<?php <?php
if (User::canSuggestVideo()): echo $viError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$viError."</div>\n" : '';
if ($this->user::canSuggestVideo()):
?> ?>
<div class="pad2"></div> <div class="pad2"></div>
<form action="?video=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return vi_validateForm(this)"> <form action="?video=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return vi_validateForm(this)">
<input type="text" name="videourl" style="width: 35%" /> <small>Supported: YouTube only</small> <input type="text" name="videourl" style="width: 35%" /> <small>支持:仅限 YouTube</small>
<div class="pad2"></div> <div class="pad2"></div>
<input type="submit" value="Submit" /> <input type="submit" value="提交" />
<div class="pad3"></div> <div class="pad3"></div>
<small class="q0">Note: Your video will need to be approved before appearing on the site.</small> <small class="q0">说明:您的视频需通过审核才能在站点上显示。</small>
<?php <?php
else: else:
?> ?>
@@ -85,9 +89,9 @@
<input type="text" name="videourl" disabled="disabled" /><br /> <input type="text" name="videourl" disabled="disabled" /><br />
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>You are not signed in. Please <a href="?account=signin">sign in</a> to submit a video.</small> <small>你尚未登录,请先<a href="?account=signin">登录</a>以提交视频。</small>
<?php <?php
endif; endif;
?> ?>

View File

@@ -1,4 +1,6 @@
<?php namespace Aowow; ?> <?php
namespace Aowow\Template;
?>
<div id="tab-add-your-comment" style="display: none"> <div id="tab-add-your-comment" style="display: none">
Ten en cuenta lo siguiente cuando escribas un comentario: Ten en cuenta lo siguiente cuando escribas un comentario:
@@ -9,9 +11,9 @@
<li><div>Deberías corregir tus comentarios antes de enviarlos.</div></li> <li><div>Deberías corregir tus comentarios antes de enviarlos.</div></li>
</ul> </ul>
<?php <?php
echo $this->coError ? ' <div class="msg-failure">'.$this->coError."</div>\n <div class=\"pad\"></div>\n" : ''; echo $coError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$coError."</div>\n" : '';
if (User::canComment()): if ($this->user::canComment()):
?> ?>
<form name="addcomment" action="?comment=add&amp;type=<?=$this->type.'&amp;typeid='.$this->typeId; ?>" method="post" onsubmit="return co_validateForm(this)"> <form name="addcomment" action="?comment=add&amp;type=<?=$this->type.'&amp;typeid='.$this->typeId; ?>" method="post" onsubmit="return co_validateForm(this)">
<div id="funcbox-generic"></div> <div id="funcbox-generic"></div>
@@ -25,7 +27,7 @@
<div class="comment-edit-body"><textarea class="comment-editbox" rows="10" cols="40" name="commentbody" disabled="disabled"></textarea></div> <div class="comment-edit-body"><textarea class="comment-editbox" rows="10" cols="40" name="commentbody" disabled="disabled"></textarea></div>
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>No has iniciado sesión. Por favor <a href="?account=signin">entra a tu cuenta</a> o <a href="?account=signup">registra una cuenta</a> para añadir tu comentario.</small> <small>No has iniciado sesión. Por favor <a href="?account=signin">entra a tu cuenta</a> o <a href="?account=signup">registra una cuenta</a> para añadir tu comentario.</small>
<?php <?php
@@ -41,9 +43,9 @@
<li><div>Asegurate de leer las <a href="?help=screenshots-tips-tricks" target="_blank">sugerencias y trucos</a> si no lo has hecho antes.</div></li> <li><div>Asegurate de leer las <a href="?help=screenshots-tips-tricks" target="_blank">sugerencias y trucos</a> si no lo has hecho antes.</div></li>
</ul> </ul>
<?php <?php
echo $this->ssError ? ' <div class="msg-failure">'.$this->ssError."</div>\n <div class=\"pad\"></div>\n" : ''; echo $ssError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$ssError."</div>\n" : '';
if (User::canUploadScreenshot()): if ($this->user::canUploadScreenshot()):
?> ?>
<form action="?screenshot=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return ss_validateForm(this)"> <form action="?screenshot=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return ss_validateForm(this)">
<input type="file" name="screenshotfile" style="width: 35%"/><br /> <input type="file" name="screenshotfile" style="width: 35%"/><br />
@@ -58,7 +60,7 @@
<input type="file" name="screenshotfile" disabled="disabled" /><br /> <input type="file" name="screenshotfile" disabled="disabled" /><br />
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>No has iniciado sesión. <a href="?account=signin">Inicia sesión</a> para enviar una captura de pantalla.</small> <small>No has iniciado sesión. <a href="?account=signin">Inicia sesión</a> para enviar una captura de pantalla.</small>
<?php <?php
@@ -69,7 +71,9 @@
<div id="tab-suggest-a-video" style="display: none"> <div id="tab-suggest-a-video" style="display: none">
Símplemente, escribe la URL del vídeo en el formulario. Símplemente, escribe la URL del vídeo en el formulario.
<?php <?php
if (User::canSuggestVideo()): echo $viError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$viError."</div>\n" : '';
if ($this->user::canSuggestVideo()):
?> ?>
<div class="pad2"></div> <div class="pad2"></div>
<form action="?video=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return vi_validateForm(this)"> <form action="?video=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return vi_validateForm(this)">
@@ -85,7 +89,7 @@
<input type="text" name="videourl" disabled="disabled" /><br /> <input type="text" name="videourl" disabled="disabled" /><br />
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>No has iniciado sesión. <a href="?account=signin">Inicia sesión</a> para enviar un video.</small> <small>No has iniciado sesión. <a href="?account=signin">Inicia sesión</a> para enviar un video.</small>
<?php <?php

View File

@@ -1,4 +1,6 @@
<?php namespace Aowow; ?> <?php
namespace Aowow\Template;
?>
<div id="tab-add-your-comment" style="display: none"> <div id="tab-add-your-comment" style="display: none">
При написании комментария, просим вас не забывать про следующее: При написании комментария, просим вас не забывать про следующее:
@@ -9,9 +11,9 @@
<li><div>У вас может возникнуть желание проверить написание своего комментария перед тем, как поместить его на сайт.</div></li> <li><div>У вас может возникнуть желание проверить написание своего комментария перед тем, как поместить его на сайт.</div></li>
</ul> </ul>
<?php <?php
echo $this->coError ? ' <div class="msg-failure">'.$this->coError."</div>\n <div class=\"pad\"></div>\n" : ''; echo $coError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$coError."</div>\n" : '';
if (User::canComment()): if ($this->user::canComment()):
?> ?>
<form name="addcomment" action="?comment=add&amp;type=<?=$this->type.'&amp;typeid='.$this->typeId; ?>" method="post" onsubmit="return co_validateForm(this)"> <form name="addcomment" action="?comment=add&amp;type=<?=$this->type.'&amp;typeid='.$this->typeId; ?>" method="post" onsubmit="return co_validateForm(this)">
<div id="funcbox-generic"></div> <div id="funcbox-generic"></div>
@@ -25,7 +27,7 @@
<div class="comment-edit-body"><textarea class="comment-editbox" rows="10" cols="40" name="commentbody" disabled="disabled"></textarea></div> <div class="comment-edit-body"><textarea class="comment-editbox" rows="10" cols="40" name="commentbody" disabled="disabled"></textarea></div>
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>Вы не вошли на сайт. Пожалуйста <a href="?account=signin">войдите</a> или <a href="?account=signup">зарегистрируйтесь</a>, чтобы добавлять комментарии.</small> <small>Вы не вошли на сайт. Пожалуйста <a href="?account=signin">войдите</a> или <a href="?account=signup">зарегистрируйтесь</a>, чтобы добавлять комментарии.</small>
<?php <?php
@@ -41,9 +43,9 @@
<li><div>Если вы ещё не читали, то настоятельно рекомендуем вам прочесть <a href="?help=screenshots-tips-tricks" target="_blank">советы и особенности</a> получения изображений при помощи снимков экрана.</div></li> <li><div>Если вы ещё не читали, то настоятельно рекомендуем вам прочесть <a href="?help=screenshots-tips-tricks" target="_blank">советы и особенности</a> получения изображений при помощи снимков экрана.</div></li>
</ul> </ul>
<?php <?php
echo $this->ssError ? ' <div class="msg-failure">'.$this->ssError."</div>\n <div class=\"pad\"></div>\n" : ''; echo $ssError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$ssError."</div>\n" : '';
if (User::canUploadScreenshot()): if ($this->user::canUploadScreenshot()):
?> ?>
<form action="?screenshot=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return ss_validateForm(this)"> <form action="?screenshot=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return ss_validateForm(this)">
<input type="file" name="screenshotfile" style="width: 35%"/><br /> <input type="file" name="screenshotfile" style="width: 35%"/><br />
@@ -58,7 +60,7 @@
<input type="file" name="screenshotfile" disabled="disabled" /><br /> <input type="file" name="screenshotfile" disabled="disabled" /><br />
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>Вы не вошли на сайт. Пожалуйста <a href="?account=signin">войдите</a>, чтобы отправить скриншот.</small> <small>Вы не вошли на сайт. Пожалуйста <a href="?account=signin">войдите</a>, чтобы отправить скриншот.</small>
<?php <?php
@@ -69,7 +71,9 @@
<div id="tab-suggest-a-video" style="display: none"> <div id="tab-suggest-a-video" style="display: none">
Введите URL видео на YouTube в форму ниже. Введите URL видео на YouTube в форму ниже.
<?php <?php
if (User::canSuggestVideo()): echo $viError ? " <div class=\"pad\"></div>\n <div class=\"msg-failure\">".$viError."</div>\n" : '';
if ($this->user::canSuggestVideo()):
?> ?>
<div class="pad2"></div> <div class="pad2"></div>
<form action="?video=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return vi_validateForm(this)"> <form action="?video=add&<?=$this->type.'.'.$this->typeId; ?>" method="post" enctype="multipart/form-data" onsubmit="return vi_validateForm(this)">
@@ -85,7 +89,7 @@
<input type="text" name="videourl" disabled="disabled" /><br /> <input type="text" name="videourl" disabled="disabled" /><br />
<?php <?php
endif; endif;
if (!User::isLoggedIn()): if (!$this->user::isLoggedIn()):
?> ?>
<small>Вы не вошли на сайт. Пожалуйста <a href="?account=signin">войдите</a>, чтобы отправить видео.</small> <small>Вы не вошли на сайт. Пожалуйста <a href="?account=signin">войдите</a>, чтобы отправить видео.</small>
<?php <?php

View File

@@ -5,6 +5,6 @@ Hey!
Thanks a lot for your interest in contributing to the site. Thanks a lot for your interest in contributing to the site.
Just click this link to activate your account: Just click this link to activate your account:
HOST_URL?account=signup&token=%s HOST_URL?account=activate&key=%s
The NAME_SHORT team The NAME_SHORT team

View File

@@ -5,6 +5,6 @@ Bonjour !
Nous vous remercions chaleureusement pour l'intérêt que vous portez à contribuer à notre site. Nous vous remercions chaleureusement pour l'intérêt que vous portez à contribuer à notre site.
Vous n'avez qu'à cliquer sur le lien ci-dessous pour activer votre compte. Vous n'avez qu'à cliquer sur le lien ci-dessous pour activer votre compte.
HOST_URL?account=signup&token=%s HOST_URL?account=activate&key=%s
L'équipe NAME_SHORT L'équipe NAME_SHORT

View File

@@ -5,6 +5,6 @@ Hey!
Vielen Dank für Euer Interesse an der Mitwirkung bei unserer Webseite. Vielen Dank für Euer Interesse an der Mitwirkung bei unserer Webseite.
Klickt einfach auf den folgenden Link, um Euer Konto zu aktivieren: Klickt einfach auf den folgenden Link, um Euer Konto zu aktivieren:
HOST_URL?account=signup&token=%s HOST_URL?account=activate&key=%s
Das Team von NAME_SHORT Das Team von NAME_SHORT

View File

@@ -5,6 +5,6 @@
非常感谢你有兴趣为本站做出贡献。 非常感谢你有兴趣为本站做出贡献。
只需点击此链接激活你的账户: 只需点击此链接激活你的账户:
HOST_URL?account=signup&token=%s HOST_URL?account=activate&key=%s
NAME_SHORT 团队敬上 NAME_SHORT 团队敬上

View File

@@ -5,6 +5,6 @@ Creación de cuenta
Muchas gracias por tu interés en contribuir al sitio. Muchas gracias por tu interés en contribuir al sitio.
Simplemente haz clic en este enlace para activar tu cuenta: Simplemente haz clic en este enlace para activar tu cuenta:
HOST_URL?account=signup&token=%s HOST_URL?account=activate&key=%s
El equipo de NAME_SHORT El equipo de NAME_SHORT

View File

@@ -5,6 +5,6 @@ Account Creation
Благодарим за ваш интерес по наполнению сайта. Благодарим за ваш интерес по наполнению сайта.
Для активации вашей учетной записи просто кликните по этой ссылке: Для активации вашей учетной записи просто кликните по этой ссылке:
HOST_URL?account=signup&token=%s HOST_URL?account=activate&key=%s
Команда NAME_SHORT. Команда NAME_SHORT.

View File

@@ -2,6 +2,6 @@
User Recovery User Recovery
Follow this link to log in. Follow this link to log in.
HOST_URL?account=signin&token=%s HOST_URL?account=signin&key=%s
If you did not request this mail simply ignore it. If you did not request this mail simply ignore it.

View File

@@ -2,6 +2,6 @@
Récupération d'utilisateur Récupération d'utilisateur
Suivez ce lien pour vous connecter. Suivez ce lien pour vous connecter.
HOST_URL?account=signin&token=%s HOST_URL?account=signin&key=%s
Si vous n'avez pas demandé cet e-mail, ignorez le. Si vous n'avez pas demandé cet e-mail, ignorez le.

View File

@@ -2,6 +2,6 @@
Benutzernamenanfrage Benutzernamenanfrage
Folgt diesem Link um euch anzumelden. Folgt diesem Link um euch anzumelden.
HOST_URL?account=signin&token=%s HOST_URL?account=signin&key=%s
Falls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden. Falls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden.

View File

@@ -2,6 +2,6 @@
用户恢复 用户恢复
请点击此链接登录。 请点击此链接登录。
HOST_URL?account=signin&token=%s HOST_URL?account=signin&key=%s
如果您没有请求此邮件,请忽略它。 如果您没有请求此邮件,请忽略它。

View File

@@ -2,6 +2,6 @@
Recuperacion de Usuario Recuperacion de Usuario
Siga a este enlace para ingresar. Siga a este enlace para ingresar.
HOST_URL?account=signin&token=%s HOST_URL?account=signin&key=%s
Si usted no solicitó este correo, por favor ignorelo. Si usted no solicitó este correo, por favor ignorelo.

View File

@@ -2,6 +2,6 @@
Восстановление пользователя Восстановление пользователя
Перейдите по этой ссылке, чтобы войти в систему. Перейдите по этой ссылке, чтобы войти в систему.
HOST_URL?account=signin&token=%s HOST_URL?account=signin&key=%s
Если вы не запрашивали это письмо, просто проигнорируйте его. Если вы не запрашивали это письмо, просто проигнорируйте его.

View File

@@ -2,6 +2,6 @@
Password Reset Password Reset
Follow this link to reset your password. Follow this link to reset your password.
HOST_URL?account=forgotpassword&token=%s HOST_URL?account=forgotpassword&key=%s
If you did not request this mail simply ignore it. If you did not request this mail simply ignore it.

View File

@@ -2,6 +2,6 @@
Réinitialisation du mot de passe Réinitialisation du mot de passe
Suivez ce lien pour réinitialiser votre mot de passe. Suivez ce lien pour réinitialiser votre mot de passe.
HOST_URL?account=forgotpassword&token=%s HOST_URL?account=forgotpassword&key=%s
Si vous n'avez pas fait de demande de réinitialisation, ignorez cet e-mail. Si vous n'avez pas fait de demande de réinitialisation, ignorez cet e-mail.

View File

@@ -2,6 +2,6 @@
Kennwortreset Kennwortreset
Folgt diesem Link um euer Kennwort zurückzusetzen. Folgt diesem Link um euer Kennwort zurückzusetzen.
HOST_URL?account=forgotpassword&token=%s HOST_URL?account=forgotpassword&key=%s
Falls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden. Falls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden.

View File

@@ -2,6 +2,6 @@
重置密码 重置密码
点击此链接以重置您的密码。 点击此链接以重置您的密码。
HOST_URL?account=forgotpassword&token=%s HOST_URL?account=forgotpassword&key=%s
如果您没有请求此邮件,请忽略它。 如果您没有请求此邮件,请忽略它。

View File

@@ -2,6 +2,6 @@
Reinicio de Contraseña Reinicio de Contraseña
Siga este enlace para reiniciar su contraseña. Siga este enlace para reiniciar su contraseña.
HOST_URL?account=forgotpassword&token=%s HOST_URL?account=forgotpassword&key=%s
Si usted no solicitó este correo, por favor ignorelo. Si usted no solicitó este correo, por favor ignorelo.

View File

@@ -2,6 +2,6 @@
Сброс пароля Сброс пароля
Перейдите по этой ссылке, чтобы сбросить свой пароль. Перейдите по этой ссылке, чтобы сбросить свой пароль.
HOST_URL?account=forgotpassword&token=%s HOST_URL?account=forgotpassword&key=%s
Если вы не запрашивали это письмо, просто проигнорируйте его. Если вы не запрашивали это письмо, просто проигнорируйте его.

View File

@@ -1,7 +1,10 @@
<?php namespace Aowow; ?> <?php
namespace Aowow\Template;
<?php $this->brick('header'); ?> use \Aowow\Lang;
$this->brick('header');
?>
<div class="main" id="main"> <div class="main" id="main">
<div class="main-precontents" id="main-precontents"></div> <div class="main-precontents" id="main-precontents"></div>
<div class="main-contents" id="main-contents"> <div class="main-contents" id="main-contents">
@@ -19,83 +22,39 @@
$this->brick('headIcons'); $this->brick('headIcons');
$this->brick('redButtons'); $this->brick('redButtons');
?>
<h1<?=(isset($this->expansion) ? ' class="h1-icon"><span class="icon-'.$this->expansion.'-right">'.$this->name.'</span>' : '>'.$this->name); ?></h1> if ($this->expansion && $this->h1):
echo ' <h1 class="h1-icon"><span class="icon-'.$this->expansion.'-right">'.$this->h1."</span></h1>\n";
elseif ($this->h1):
echo ' <h1>'.$this->h1."</h1>\n";
endif;
<?php $this->brick('markup', ['markup' => $this->article]);
$this->brick('article');
if (isset($this->extraText)): $this->brick('markup', ['markup' => $this->extraText]);
?>
<div id="text-generic" class="left"></div>
<script type="text/javascript">//<![CDATA[
Markup.printHtml("<?=$this->extraText; ?>", "text-generic", {
allow: Markup.CLASS_ADMIN,
dbpage: true
});
//]]></script>
<div class="pad2"></div>
<?php
endif;
$this->brick('mapper'); $this->brick('mapper');
if (!empty($this->transfer)): if ($this->transfer):
echo " <div class=\"pad\"></div>\n ".$this->transfer."\n"; echo " <div class=\"pad\"></div>\n ".$this->transfer."\n";
endif; endif;
if (isset($this->smartAI)): $this->brick('markup', ['markup' => $this->smartAI]);
?>
<div id="text-generic" class="left"></div>
<script type="text/javascript">//<![CDATA[
Markup.printHtml("<?=$this->smartAI; ?>", "text-generic", {
allow: Markup.CLASS_ADMIN,
dbpage: true
});
//]]></script>
<div class="pad2"></div> if ($this->zoneMusic):
<?php
endif;
if (!empty($this->zoneMusic)):
?> ?>
<div class="clear"> <div class="clear">
<?php <?php
if (!empty($this->zoneMusic['music'])): foreach ($this->zoneMusic as [$h3, $data, $divId, $opts]):
?> ?>
<div id="zonemusicdiv-zonemusic" style="float: left"> <div id="zonemusicdiv-<?=$divId; ?>" style="float: left">
<h3><?=Lang::sound('music'); ?></h3> <h3><?=$h3; ?></h3>
</div> </div>
<script type="text/javascript">//<![CDATA[ <script type="text/javascript">//<![CDATA[
(new AudioControls()).init(<?=Util::toJSON($this->zoneMusic['music']); ?>, $WH.ge('zonemusicdiv-zonemusic'), {loop: true}); (new AudioControls()).init(<?=$this->json($data); ?>, $WH.ge('zonemusicdiv-<?=$divId; ?>'), <?=$this->json($opts); ?>);
//]]></script> //]]></script>
<?php <?php
endif; endforeach;
if (!empty($this->zoneMusic['intro'])):
?>
<div id="zonemusicdiv-zonemusicintro" style="float: left">
<h3><?=Lang::sound('intro'); ?></h3>
</div>
<script type="text/javascript">//<![CDATA[
(new AudioControls()).init(<?=Util::toJSON($this->zoneMusic['intro']); ?>, $WH.ge('zonemusicdiv-zonemusicintro'), {});
//]]></script>
<?php
endif;
if (!empty($this->zoneMusic['ambience'])):
?>
<div id="zonemusicdiv-soundambience" style="float: left">
<h3><?=Lang::sound('ambience'); ?></h3>
</div>
<script type="text/javascript">//<![CDATA[
(new AudioControls()).init(<?=Util::toJSON($this->zoneMusic['ambience']); ?>, $WH.ge('zonemusicdiv-soundambience'), {loop: true});
//]]></script>
<?php
endif;
?> ?>
<br clear="all"/></div> <br clear="all"/></div>
<?php <?php
@@ -104,7 +63,7 @@ endif;
<h2 class="clear"><?=Lang::main('related'); ?></h2> <h2 class="clear"><?=Lang::main('related'); ?></h2>
</div> </div>
<?php <?php
$this->brick('lvTabs', ['relTabs' => true]); $this->brick('lvTabs');
$this->brick('contribute'); $this->brick('contribute');
?> ?>

View File

@@ -1,7 +1,10 @@
<?php namespace Aowow; ?> <?php
namespace Aowow\Template;
<?php $this->brick('header'); ?> use \Aowow\Lang;
$this->brick('header');
?>
<div class="main" id="main"> <div class="main" id="main">
<div class="main-precontents" id="main-precontents"></div> <div class="main-precontents" id="main-precontents"></div>
<div class="main-contents" id="main-contents"> <div class="main-contents" id="main-contents">
@@ -10,60 +13,34 @@
$this->brick('announcement'); $this->brick('announcement');
$this->brick('pageTemplate'); $this->brick('pageTemplate');
if ($this->notFound):
?>
<div class="pad3"></div>
<div class="inputbox">
<h1><?=$this->notFound['title'];?></h1>
<div id="inputbox-error"><?=$this->notFound['msg'];?></div>
<?php
else:
?> ?>
<div class="text"> <div class="text">
<?php <?php
$this->brick('redButtons'); $this->brick('redButtons');
if (!empty($this->h1Links)): if ($this->h1Link):
echo ' <div class="h1-links">'.$this->h1Links.'</div>'; echo ' <div class="h1-links"><small><a href="'.$this->h1Link.'" class="icon-rss">'.Lang::main('subscribe').'</a></small></div>';
endif; endif;
if (!empty($this->name)): if ($this->h1):
echo ' <h1>'.$this->name.'</h1>'; echo ' <h1>'.$this->h1.'</h1>';
endif; endif;
$this->brick('mapper'); $this->brick('mapper');
$this->brick('article'); $this->brick('markup', ['markup' => $this->article]);
if (isset($this->extraText)): $this->brick('markup', ['markup' => $this->extraText]);
?>
<div id="text-generic" class="left"></div>
<script type="text/javascript">//<![CDATA[
Markup.printHtml("<?=Util::jsEscape($this->extraText);?>", "text-generic", {
allow: Markup.CLASS_ADMIN,
dbpage: true
});
//]]></script>
<div class="pad2"></div> echo $this->extraHTML ?? '';
<?php
endif;
if (isset($this->extraHTML)): if ($this->tabsTitle):
echo $this->extraHTML;
endif;
endif;
if (!empty($this->tabsTitle)):
echo ' <h2 class="clear">'.$this->tabsTitle.'</h2>'; echo ' <h2 class="clear">'.$this->tabsTitle.'</h2>';
endif; endif;
?> ?>
</div> </div>
<?php <?php
if (!empty($this->lvTabs)): if ($this->lvTabs):
$this->brick('lvTabs'); $this->brick('lvTabs');
?> ?>
<div class="clear"></div> <div class="clear"></div>

View File

@@ -1,5 +1,4 @@
<?php namespace Aowow; ?> <?php namespace Aowow\Template; ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
@@ -9,9 +8,9 @@
<style type="text/css"> <style type="text/css">
body { text-align: center; font-family: Arial; background-color: black; color: white } body { text-align: center; font-family: Arial; background-color: black; color: white }
.maintenance { background: url(<?=Cfg::get('STATIC_URL'); ?>/images/logos/home.png) no-repeat center top; width: 900px; margin: 40px auto; text-align: center; padding-top: 70px; } .maintenance { background: url(<?=$this->gStaticUrl; ?>/images/logos/home.png) no-repeat center top; width: 900px; margin: 40px auto; text-align: center; padding-top: 70px; }
.maintenance div { color: #00DD00; font-weight: bold; padding: 20px } .maintenance div { color: #00DD00; font-weight: bold; padding: 20px }
.maintenance p { background: url(<?=Cfg::get('STATIC_URL'); ?>/images/maintenance/brbgnomes.jpg) no-repeat center bottom; padding-bottom: 300px } .maintenance p { background: url(<?=$this->gStaticUrl; ?>/images/maintenance/brbgnomes.jpg) no-repeat center bottom; padding-bottom: 300px }
</style> </style>
</head> </head>
<body> <body>

View File

@@ -1,71 +1,42 @@
<?php namespace Aowow; ?> <?php
namespace Aowow\Template;
<?php $this->brick('header'); ?>
$this->brick('header');
?>
<div class="main" id="main"> <div class="main" id="main">
<div class="main-precontents" id="main-precontents"></div> <div class="main-precontents" id="main-precontents"></div>
<div class="main-contents" id="main-contents"> <div class="main-contents" id="main-contents">
<?php <?php
$this->brick('announcement'); $this->brick('announcement');
$this->brick('pageTemplate'); $this->brick('pageTemplate');
if ($this->notFound): if ([$typeStr, $id] = $this->doResync):
?>
<?php
if (!empty($this->doResync)):
?> ?>
<div id="roster-status" class="profiler-message clear"></div> <div id="roster-status" class="profiler-message clear"></div>
<?php
endif;
?>
<div class="pad3"></div>
<div class="inputbox">
<h1><?=$this->notFound['title']; ?></h1>
<div id="inputbox-error"><?=$this->notFound['msg']; ?></div> <!-- style="background: no-repeat 3px 3px" -->
<?php
if (!empty($this->doResync)):
?>
<script type="text/javascript">//<![CDATA[ <script type="text/javascript">//<![CDATA[
pr_updateStatus('<?=$this->doResync[0]; ?>', $WH.ge('roster-status'), <?=$this->doResync[1]; ?>, 1); pr_updateStatus('<?=$typeStr; ?>', $WH.ge('roster-status'), <?=$id; ?>, 1);
pr_setRegionRealm($WH.gE($WH.ge('topbar'), 'form')[0], '<?=$this->region; ?>', '<?=$this->realm; ?>'); pr_setRegionRealm($WH.gE($WH.ge('topbar'), 'form')[0], '<?=$this->region; ?>', '<?=$this->realm; ?>');
//]]></script> //]]></script>
<?php <?php
endif; endif;
?>
<div class="clear"></div> if ($this->inputbox):
</div> $this->brick(...$this->inputbox); // $templateName, [$templateVars]
<?php
else: else:
?> ?>
<div class="text"> <div class="text">
<h1><?=$this->name; ?></h1> <?=($this->h1 ? ' <h1>'.$this->h1.'</h1>' : '');?>
<?php <?php
$this->brick('article'); $this->brick('markup', ['markup' => $this->article]);
if (isset($this->extraText)): $this->brick('markup', ['markup' => $this->extraText]);
echo $this->extraHTML ?? '';
?> ?>
<div id="text-generic" class="left"></div>
<script type="text/javascript">//<![CDATA[
Markup.printHtml("<?=Util::jsEscape($this->extraText); ?>", "text-generic", {
allow: Markup.CLASS_ADMIN,
dbpage: true
});
//]]></script>
<div class="pad2"></div>
</div> </div>
<?php <?php
endif;
if (isset($this->extraHTML)):
echo $this->extraHTML;
endif;
endif; endif;
?> ?>
</div><!-- main-contents --> </div><!-- main-contents -->