2 Commits

Author SHA1 Message Date
Sarjuuk
ef981c93e2 DB/Fixup: database dump ddl dml inconsistency (#453, #452)
* fix: `db_structure.sql` dump missed DML change after `aowow_account.allowExpire` field removed (in `a99fff46`)
* fix: `db_structure.sql` dump missed DML change after `aowow_articles.quickInfo` field removed (in `112acb22`)

---------

Co-authored-by: Михаил Драгункин <contact@md.land>
2025-09-26 15:35:24 +02:00
Sarjuuk
10c70209e7 Markup/Fixup
* fix replacing tags with names
2025-09-25 19:23:39 +02:00
802 changed files with 51247 additions and 64389 deletions

5
.gitignore vendored
View File

@@ -11,7 +11,8 @@
# generated files
/static/js/profile_all.js
/static/js/global.js
/static/js/locale.js
/static/js/Markup.js
/static/widgets/power.js
/static/widgets/power/demo.html
/static/widgets/searchbox.js
@@ -49,4 +50,4 @@
/static/uploads/screenshots/*
/static/uploads/signatures/*
/static/uploads/temp/*
/static/uploads/guide/images/*

View File

@@ -24,7 +24,6 @@ Also, this project is not meant to be used for commercial purposes of any kind!
+ [MySQL Improved](https://www.php.net/manual/en/book.mysqli.php)
+ [Multibyte String](https://www.php.net/manual/en/book.mbstring.php)
+ [File Information](https://www.php.net/manual/en/book.fileinfo.php)
+ [Internationalization](https://www.php.net/manual/en/book.intl.php)
+ [GNU Multiple Precision](https://www.php.net/manual/en/book.gmp.php) (When using TrinityCore as auth source)
+ MySQL ≥ 5.7.0 OR MariaDB ≥ 10.6.4 OR similar
+ [TDB 335.21101](https://github.com/TrinityCore/TrinityCore/releases/tag/TDB335.21101) (no other other providers are supported at this time)
@@ -51,7 +50,7 @@ audio processing may require [lame](https://sourceforge.net/projects/lame/files/
#### 2. Prepare the database
Ensure that the account you are going to use has **full** access on the database AoWoW is going to occupy and ideally only **read** access on the world database you are going to reference.
Import files 01 - 03 from `setup/sql/` in order into the AoWoW database `mysql -p {your-db-here} < setup/sql/01-db_structure.sql`, etc.
Import `setup/db_structure.sql` into the AoWoW database `mysql -p {your-db-here} < setup/db_structure.sql`
#### 3. Server created files
See to it, that the web server is able to write the following directories and their children. If they are missing, the setup will create them with appropriate permissions

View File

@@ -4,7 +4,7 @@ if (!defined('AOWOW_REVISION'))
die('illegal access');
function extAuth(string &$usernameOrEmail, #[\SensitiveParameter] string $password, int &$userId = 0, int &$userGroup = -1) : int
function extAuth(string &$usernameOrEmail, string $password, int &$userId = 0, int &$userGroup = -1) : int
{
/*
insert some auth mechanism here

View File

@@ -1,34 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AboutusBaseResponse extends TemplateResponse
{
protected string $template = 'text-page-generic';
protected string $pageName = 'aboutus';
protected ?int $activeTab = parent::TAB_MORE;
protected array $breadcrumb = [2, 0];
public function __construct(string $pageParam)
{
parent::__construct($pageParam);
if ($pageParam)
$this->generateError();
}
protected function generate() : void
{
$this->h1 = Lang::main('moreTitles', $this->pageName);
array_unshift($this->title, $this->h1);
parent::generate();
}
}
?>

View File

@@ -1,177 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AccountBaseResponse extends TemplateResponse
{
protected string $template = 'account';
protected string $pageName = 'account';
protected array $scripts = [[SC_JS_FILE, 'js/account.js']];
// display status of executed step (forwarding back to this page)
public ?array $generalMessage = null;
public ?array $emailMessage = null;
public ?array $usernameMessage = null;
public ?array $passwordMessage = null;
public ?array $communityMessage = null;
public ?array $avatarMessage = null;
public ?array $premiumborderMessage = null;
// form fields
public int $modelrace = 0;
public int $modelgender = 0;
public int $idsInLists = 0;
public string $curEmail = '';
public string $curName = '';
public string $renameCD = '';
public string $activeCD = '';
public array $description = [];
public array $signature = [];
public int $avMode = 0;
public string $wowicon = '';
public int $customicon = 0;
public array $customicons = [];
public bool $premium = false;
public int $reputation = 0;
public ?Listview $avatarManager = null;
public ?array $bans;
public function __construct($pageParam)
{
if (!User::isLoggedIn())
$this->forwardToSignIn('account');
parent::__construct($pageParam);
}
protected function generate() : void
{
array_unshift($this->title, Lang::account('settings'));
$user = DB::Aowow()->selectRow('SELECT `debug`, `email`, `description`, `avatar`, `wowicon`, `renameCooldown` FROM ?_account WHERE `id` = ?d', User::$id);
Lang::sort('game', 'ra');
parent::generate();
/*************/
/* Ban Popup */
/*************/
$b = DB::Aowow()->select(
'SELECT ab.`end` AS "0", ab.`reason` AS "1", a.`username` AS "2"
FROM ?_account_banned ab
LEFT JOIN ?_account a ON a.`id` = ab.`staffId`
WHERE ab.`userId` = ?d AND ab.`typeMask` & ?d AND (ab.`end` = 0 OR ab.`end` > UNIX_TIMESTAMP())',
User::$id, ACC_BAN_TEMP | ACC_BAN_PERM
);
$this->bans = $b ?: null;
/*******************/
/* Status Messages */
/*******************/
if (isset($_SESSION['msg']))
{
[$var, $status, $msg] = $_SESSION['msg'];
if (property_exists($this, $var.'Message'))
$this->{$var.'Message'} = [$status, $msg];
else
trigger_error('AccountBaseResponse::generate - unknown var in $_SESSION msg: '.$var, E_USER_WARNING);
unset($_SESSION['msg']);
}
/*************/
/* Form Data */
/*************/
/* GENERAL */
// Modelviewer
if ($_ = DB::Aowow()->selectCell('SELECT `data` FROM ?_account_cookies WHERE `name` = ? AND `userId` = ?d', 'default_3dmodel', User::$id))
[$this->modelrace, $this->modelgender] = explode(',', $_);
// Lists
$this->idsInLists = $user['debug'] ? 1 : 0;
/* PERSONAL */
// Email address
$this->curEmail = $user['email'] ?? '';
// Username
$this->curName = User::$username;
$this->renameCD = Util::formatTime(Cfg::get('ACC_RENAME_DECAY') * 1000);
if ($user['renameCooldown'] > time())
{
$locCode = implode('_', str_split(Lang::getLocale()->json(), 2)); // ._.
$this->activeCD = (new \IntlDateFormatter($locCode, pattern: Lang::main('dateFmtIntl')))->format($user['renameCooldown']);
}
/* COMMUNITY */
// Public Description
$this->description = ['body' => $user['description']];
// Forum Signature
// $this->signature = ['body' => $user['signature']];
// Avatar
$this->wowicon = $user['wowicon'];
$this->avMode = $user['avatar'];
// status [reviewing, ok, rejected]? (only 2: rejected processed in js)
if (User::isPremium() && ($cuAvatars = DB::Aowow()->select('SELECT `id`, `name`, `current`, `size`, `status`, `when` FROM ?_account_avatars WHERE `userId` = ?d', User::$id)))
{
array_walk($cuAvatars, function (&$x) {
$x['when'] *= 1000; // uploaded timestamp expected as msec for some reason
$x['caption'] = $x['name']; // only used for getVisibleText, duplicates name?
$x['type'] = 1; // always 1 ?, Dialog-popup doesn't work without it
});
foreach ($cuAvatars as $a)
if ($a['status'] != AvatarMgr::STATUS_REJECTED)
$this->customicons[$a['id']] = $a['name'];
// TODO - replace with array_find in PHP 8.4
if ($x = array_filter($cuAvatars, fn($x) => $x['current'] > 0 ))
$this->customicon = array_pop($x)['id'];
}
/* PREMIUM */
$this->premium = User::isPremium();
if (!$this->premium)
return;
$this->reputation = User::getReputation();
// Avatar Manager
$this->avatarManager = new Listview([
'template' => 'avatar',
'id' => 'avatar',
'name' => '$LANG.tab_avatars',
'parent' => 'avatar-manage',
'hideNav' => 1 | 2, // top | bottom
'data' => $cuAvatars ?? [],
'note' => Lang::account('avatarSlots', [count($this->customicons), Cfg::get('acc_max_avatar_uploads')])
]);
// Premium Border Selector
// solved by js
}
}
?>

View File

@@ -1,73 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via activation email link
* empty page with status box
*/
class AccountActivateResponse extends TemplateResponse
{
protected string $template = 'text-page-generic';
protected string $pageName = 'activate';
protected array $expectedGET = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
);
private bool $success = false;
public function __construct()
{
parent::__construct();
if (!Cfg::get('ACC_ALLOW_REGISTER') || Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError();
}
protected function generate() : void
{
$this->title[] = Lang::account('title');
$msg = $this->activate();
if ($this->success)
$this->inputbox = ['inputbox-status', ['head' => Lang::account('inputbox', 'head', 'register', [2]), 'message' => $msg]];
else
{
$_SESSION['error']['activate'] = $msg;
$this->forward('?account=resend');
}
parent::generate();
}
private function activate() : string
{
if (!$this->assertGET('key'))
return Lang::main('intError');
if (DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE `status` IN (?a) AND `token` = ?', [ACC_STATUS_NONE, ACC_STATUS_NEW], $this->_get['key']))
{
// don't remove the token yet. It's needed on signin page.
DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = 0, `userGroups` = ?d WHERE `token` = ?', ACC_STATUS_NONE, U_GROUP_NONE, $this->_get['key']);
// fully apply block for further registration attempts from this ip
DB::Aowow()->query('REPLACE INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, ?d + 1, UNIX_TIMESTAMP() + ?d)',
User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
$this->success = true;
return Lang::account('inputbox', 'message', 'accActivated', [$this->_get['key']]);
}
// grace period expired and other user claimed name
return Lang::main('intError');
}
}
?>

View File

@@ -1,128 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// custom handler
class AccountConfirmdeleteResponse extends TemplateResponse
{
protected string $template = 'delete';
protected string $pageName = 'confirm-delete';
protected array $scripts = array(
[SC_CSS_FILE, 'css/delete.css'],
[SC_CSS_STRING, '[type="submit"] { margin: 0px 10px; }']
);
protected array $expectedGET = array(
'key' => [FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
);
protected array $expectedPOST = array(
'submit' => [FILTER_UNSAFE_RAW ],
'cancel' => [FILTER_UNSAFE_RAW ],
'confirm' => [FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet'] ],
'key' => [FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
);
public bool $confirm = true; // just to select the correct localized brick
public string $username = '';
public string $deleteFormTarget = '?account=confirm-delete';
public ?array $inputbox = null;
public string $key = '';
private bool $success = false;
public function __construct(string $pageParam)
{
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError();
parent::__construct($pageParam);
}
protected function generate() : void
{
array_unshift($this->title, Lang::account('accDelete'));
$this->username = User::$username;
parent::generate();
$msg = Lang::account('inputbox', 'error', 'purgeTokenUsed');
// display default confirm template
if ($this->assertGET('key') && DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP() AND `token` = ?', ACC_STATUS_PURGING, $this->_get['key']))
{
$this->key = $this->_get['key'];
return;
}
// perform action and display status
if ($this->assertPOST('key') && ($userId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP() AND `token` = ?', ACC_STATUS_PURGING, $this->_post['key'])))
{
if ($this->_post['cancel'])
$msg = $this->cancel($userId);
else if ($this->_post['submit'] && $this->_post['confirm'])
$msg = $this->purge($userId);
}
// throw error and display in status
$this->inputbox = ['inputbox-status', array(
'head' => Lang::account('inputbox', 'head', $this->success ? 'success' : 'error'),
'message' => $this->success ? $msg : '',
'error' => $this->success ? '' : $msg
)];
}
private function cancel(int $userId) : string
{
if (DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = 0, `token` = "" WHERE `id` = ?d', ACC_STATUS_NONE, $userId))
{
$this->success = true;
return Lang::account('inputbox', 'message', 'deleteCancel');
}
return Lang::main('intError');
}
private function purge(int $userId) : string
{
// empty all user settings and cookies
DB::Aowow()->query('DELETE FROM ?_account_cookies WHERE `userId` = ?d', $userId);
DB::Aowow()->query('DELETE FROM ?_account_avatars WHERE `userId` = ?d', $userId);
DB::Aowow()->query('DELETE FROM ?_account_excludes WHERE `userId` = ?d', $userId);
DB::Aowow()->query('DELETE FROM ?_account_favorites WHERE `userId` = ?d', $userId);
DB::Aowow()->query('DELETE FROM ?_account_reputation WHERE `userId` = ?d', $userId);
DB::Aowow()->query('DELETE FROM ?_account_weightscales WHERE `userId` = ?d', $userId); // cascades to aowow_account_weightscale_data
// delete profiles, unlink chars
DB::Aowow()->query('DELETE pp FROM ?_profiler_profiles pp JOIN ?_account_profiles ap ON ap.`profileId` = pp.`id` WHERE ap.`accountId` = ?d', $userId);
// DB::Aowow()->query('DELETE FROM ?_account_profiles WHERE `accountId` = ?d', $userId); // already deleted via FK?
// delete all sessions and bans
DB::Aowow()->query('DELETE FROM ?_account_banned WHERE `userId` = ?d', $userId);
DB::Aowow()->query('DELETE FROM ?_account_sessions WHERE `userId` = ?d', $userId);
// delete forum posts (msg: This post was from a user who has deleted their account. (no translations at src); comments/replies are unaffected)
// ...
// replace username with userId and empty fields
DB::Aowow()->query(
'UPDATE ?_account SET
`login` = "", `passHash` = "", `username` = `id`, `email` = NULL, `userGroups` = 0, `userPerms` = 0,
`curIp` = "", `prevIp` = "", `curLogin` = 0, `prevLogin` = 0,
`locale` = 0, `debug` = 0, `avatar` = 0, `wowicon` = "", `title` = "", `description` = "", `excludeGroups` = 0,
`status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "", `renameCooldown` = 0
WHERE `id` = ?d',
ACC_STATUS_DELETED, $userId
);
$this->success = true;
return Lang::account('inputbox', 'message', 'deleteOk');
}
}
?>

View File

@@ -1,62 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via confirmation email link
* write status to session and redirect to account settings
*/
// ?auth=email-change
class AccountConfirmemailaddressResponse extends TemplateResponse
{
protected string $template = 'text-page-generic';
protected string $pageName = 'confirm-email-address';
protected array $expectedGET = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
);
private bool $success = false;
protected function generate() : void
{
parent::generate();
if (User::isBanned())
return;
$msg = $this->change();
$this->inputbox = ['inputbox-status', array(
'head' => Lang::account('inputbox', 'head', $this->success ? 'success' : 'error'),
'message' => $this->success ? $msg : '',
'error' => $this->success ? '' : $msg,
)];
}
// this should probably leave change info intact for revert
// todo - move personal settings changes to separate table
private function change() : string
{
if (!$this->assertGET('key'))
return Lang::main('intError');
$acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ?_account WHERE `token` = ?', $this->_get['key']);
if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_EMAIL || $acc['statusTimer'] < time())
return Lang::account('inputbox', 'error', 'mailTokenUsed');
// 0 changes == error
if (!DB::Aowow()->query('UPDATE ?_account SET `email` = `updateValue`, `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = ?', ACC_STATUS_NONE, $this->_get['key']))
return Lang::main('intError');
$this->success = true;
return Lang::account('inputbox', 'message', 'mailChangeOk');
}
}
?>

View File

@@ -1,60 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via confirmation email link
* write status to session and redirect to account settings
*/
// 2025 - no longer in use?
class AccountConfirmpasswordResponse extends TemplateResponse
{
protected string $template = 'text-page-generic';
protected string $pageName = 'confirm-password';
protected array $expectedGET = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
);
private bool $success = false;
protected function generate() : void
{
parent::generate();
if (User::isBanned())
return;
$msg = $this->confirm();
$this->inputbox = ['inputbox-status', array(
'head' => Lang::account('inputbox', 'head', $this->success ? 'success' : 'error'),
'message' => $this->success ? $msg : '',
'error' => $this->success ? '' : $msg,
)];
}
private function confirm() : string
{
if (!$this->assertGET('key'))
return Lang::main('intError');
$acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ?_account WHERE `token` = ?', $this->_get['key']);
if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_PASS || $acc['statusTimer'] < time())
return Lang::account('inputbox', 'error', 'passTokenUsed');
// 0 changes == error
if (!DB::Aowow()->query('UPDATE ?_account SET `passHash` = `updateValue`, `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = ?', ACC_STATUS_NONE, $this->_get['key']))
return Lang::main('intError');
$this->success = true;
return Lang::account('inputbox', 'message', 'passChangeOk');
}
}
?>

View File

@@ -1,47 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via form button on user settings page
*/
class AccountDeleteiconResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected int $requiredUserGroup = U_GROUP_PREMIUM_PERMISSIONS;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT]
);
/*
* response not evaluated
*/
protected function generate() : void
{
if (User::isBanned() || !$this->assertPOST('id'))
return;
// non-int > error
$selected = DB::Aowow()->selectCell('SELECT `current` FROM ?_account_avatars WHERE `id` = ?d AND `userId` = ?d', $this->_post['id'], User::$id);
if ($selected === null || $selected === false)
return;
DB::Aowow()->query('DELETE FROM ?_account_avatars WHERE `id` = ?d AND `userId` = ?d', $this->_post['id'], User::$id);
// if deleted avatar is also currently selected, unset
if ($selected)
DB::Aowow()->query('UPDATE ?_account SET `avatar` = 0 WHERE `id` = ?d', User::$id);
$path = sprintf('static/uploads/avatars/%d.jpg', $this->_post['id']);
if (!unlink($path))
trigger_error('AccountDeleteiconResponse - failed to delete file: '.$path, E_USER_ERROR);
}
}
?>

View File

@@ -1,71 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via account settings link
* empty page with status box
*/
class AccountDeleteResponse extends TemplateResponse
{
protected bool $requiresLogin = true;
protected string $template = 'delete';
protected string $pageName = 'delete';
protected array $scripts = [[SC_CSS_FILE, 'css/delete.css']];
protected array $expectedPOST = array(
'proceed' => ['filter' => FILTER_UNSAFE_RAW]
);
public string $username = '';
public string $deleteFormTarget = '?account=delete';
public ?array $inputbox = null;
public function __construct(string $pageParam)
{
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError();
parent::__construct($pageParam);
}
protected function generate() : void
{
array_unshift($this->title, Lang::account('accDelete'));
parent::generate();
$this->username = User::$username;
if ($this->_post['proceed'])
{
$error = false;
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `status` NOT IN (?a) AND `statusTimer` > UNIX_TIMESTAMP() AND `id` = ?d', [ACC_STATUS_NEW, ACC_STATUS_NONE, ACC_STATUS_PURGING], User::$id))
{
$token = Util::createHash(40);
DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = UNIX_TIMESTAMP() + ?d, `token` = ? WHERE `id` = ?d',
ACC_STATUS_PURGING, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id);
Util::sendMail(User::$email, 'delete-account', [$token, User::$email, User::$username]);
}
else
$error = true;
$this->inputbox = ['inputbox-status', array(
'head' => Lang::account('inputbox', 'head', $error ? 'error' : 'success'),
'message' => $error ? '' : Lang::account('inputbox', 'message', 'deleteAccSent', [User::$email]),
'error' => $error ? Lang::account('inputbox', 'error', 'isRecovering') : ''
)];
}
}
}
?>

View File

@@ -1,76 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed from character profiles, when setting exclusions on collections
* always returns emptry string
*/
class AccountExcludeResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'mode' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 1]],
'reset' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 1]],
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdList'] ],
'type' => ['filter' => FILTER_VALIDATE_INT ],
'groups' => ['filter' => FILTER_VALIDATE_INT ]
);
protected function generate() : void
{
if (User::isBanned())
return;
if ($this->_post['mode'] == 1) // directly set exludes
$this->excludeById();
else if ($this->_post['reset'] == 1) // defaults to unavailable
$this->resetExcludes();
else if ($this->_post['groups']) // exclude by group mask
$this->updateGroups();
}
private function excludeById() : void
{
if (!$this->assertPOST('type', 'id'))
return;
if ($validIds = Type::validateIds($this->_post['type'], $this->_post['id']))
{
// ready for some bullshit? here it comes!
// we don't get signaled whether an id should be added to or removed from either includes or excludes
// so we throw everything into one table and toggle the mode if its already in here
$includes = DB::Aowow()->selectCol('SELECT `typeId` FROM ?_profiler_excludes WHERE `type` = ?d AND `typeId` IN (?a)', $this->_post['type'], $validIds);
foreach ($validIds as $typeId)
DB::Aowow()->query('INSERT INTO ?_account_excludes (`userId`, `type`, `typeId`, `mode`) VALUES (?a) ON DUPLICATE KEY UPDATE `mode` = (`mode` ^ 0x3)',
[User::$id, $this->_post['type'], $typeId, in_array($typeId, $includes) ? 2 : 1]
);
}
else
trigger_error('AccountExcludeResponse::excludeById - validation failed [type: '.$this->_post['type'].', typeId: '.implode(',', $this->_post['id']).']', E_USER_NOTICE);
}
private function resetExcludes() : void
{
DB::Aowow()->query('DELETE FROM ?_account_excludes WHERE `userId` = ?d', User::$id);
DB::Aowow()->query('UPDATE ?_account SET `excludeGroups` = ?d WHERE `id` = ?d', PR_EXCLUDE_GROUP_UNAVAILABLE, User::$id);
}
private function updateGroups() : void
{
if ($this->assertPOST('groups')) // clamp to real groups
DB::Aowow()->query('UPDATE ?_account SET `excludeGroups` = ?d WHERE `id` = ?d', $this->_post['groups'] & PR_EXCLUDE_GROUP_ANY, User::$id);
}
}
?>

View File

@@ -1,52 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed from db detail pages, when clicking on the fav star near the h1 element
* always returns emptry string
*/
class AccountFavoritesResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'add' => ['filter' => FILTER_VALIDATE_INT],
'remove' => ['filter' => FILTER_VALIDATE_INT],
'id' => ['filter' => FILTER_VALIDATE_INT],
// 'sessionKey' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']] // usage of sessionKey omitted
);
protected function generate() : void
{
if (User::isBanned())
return;
if ($this->_post['remove'])
$this->removeFavorite();
else if ($this->_post['add'])
$this->addFavorite();
}
private function removeFavorite() : void
{
if ($this->assertPOST('id', 'remove'))
DB::Aowow()->query('DELETE FROM ?_account_favorites WHERE `userId` = ?d AND `type` = ?d AND `typeId` = ?d', User::$id, $this->_post['remove'], $this->_post['id']);
}
private function addFavorite() : void
{
if ($this->assertPOST('id', 'add') && Type::validateIds($this->_post['add'], $this->_post['id']))
DB::Aowow()->query('INSERT INTO ?_account_favorites (`userId`, `type`, `typeId`) VALUES (?d, ?d, ?d)', User::$id, $this->_post['add'], $this->_post['id']);
else
trigger_error('AccountFavoritesResponse::addFavorite() - failed to add [userId: '.User::$id.', type: '.$this->_post['add'].', typeId: '.$this->_post['id'], E_USER_NOTICE);
}
}
?>

View File

@@ -1,101 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via links on signin form and from recovery email
*
* A) redirect to external page
* B) 1. click password reset link > display email form
* 2. submit email form > send mail with recovery link
* 3. click recovery link from mail > display password reset form
* 4. submit password reset form > update password
*/
class AccountforgotpasswordResponse extends TemplateResponse
{
use TrRecoveryHelper, TrGetNext;
protected string $template = 'text-page-generic';
protected string $pageName = 'forgot-password';
protected array $expectedPOST = array(
'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
);
private bool $success = false;
public function __construct(string $pageParam)
{
// don't redirect logged in users
// you can be forgetful AND logged in
if (Cfg::get('ACC_EXT_RECOVER_URL'))
$this->forward(Cfg::get('ACC_EXT_RECOVER_URL'));
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError();
parent::__construct($pageParam);
}
protected function generate() : void
{
$this->title[] = Lang::account('title');
parent::generate();
$msg = $this->processMailForm();
if ($this->success)
$this->inputbox = ['inputbox-status', ['head' => Lang::account('inputbox', 'head', 'recoverPass', [1.5]), 'message' => $msg]];
else
$this->inputbox = ['inputbox-form-email', array(
'head' => Lang::account('inputbox', 'head', 'recoverPass', [1]),
'error' => $msg,
'action' => '?account=forgot-password&next='.$this->getNext(),
'email' => $this->_post['email'] ?? ''
)];
}
private function processMailForm() : string
{
// no input yet. show clean email form
if (is_null($this->_post['email']))
return '';
// truncated due to validation fail
if (!$this->_post['email'])
return Lang::account('emailInvalid');
$timeout = DB::Aowow()->selectCell('SELECT `unbanDate` FROM ?_account_bannedips WHERE `ip` = ? AND `type` = ?d AND `count` > ?d AND `unbanDate` > UNIX_TIMESTAMP()', User::$ip, IP_BAN_TYPE_PASSWORD_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT'));
// on cooldown pretend we dont know the email address
if ($timeout && $timeout > time())
return Cfg::get('DEBUG') ? 'resend on cooldown: '.Util::formatTimeDiff($timeout).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound');
// pretend recovery started
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `email` = ?', $this->_post['email']))
{
// do not confirm or deny existence of email
$this->success = !Cfg::get('DEBUG');
return Cfg::get('DEBUG') ? Lang::account('inputbox', 'error', 'emailNotFound') : Lang::account('inputbox', 'message', 'recovPassSent', [$this->_post['email']]);
}
// recovery actually started
if ($err = $this->startRecovery(ACC_STATUS_RECOVER_PASS, 'reset-password', $this->_post['email']))
return $err;
DB::Aowow()->query('INSERT INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, ?d, UNIX_TIMESTAMP() + ?d) ON DUPLICATE KEY UPDATE `count` = `count` + ?d, `unbanDate` = UNIX_TIMESTAMP() + ?d',
User::$ip, IP_BAN_TYPE_PASSWORD_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT') + 1, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
$this->success = true;
return Lang::account('inputbox', 'message', 'recovPassSent', [$this->_post['email']]);
}
}
?>

View File

@@ -1,100 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via link on signin form
*
* A) redirect to external page
* B) 1. click password reset link > display email form
* 2. submit email form > send mail with recovery link
* ( 3. click recovery link from mail to go to signin page (so not on this page) )
*/
class AccountforgotusernameResponse extends TemplateResponse
{
use TrRecoveryHelper;
protected string $template = 'text-page-generic';
protected string $pageName = 'forgot-username';
protected array $expectedPOST = array(
'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
);
private bool $success = false;
public function __construct(string $pageParam)
{
// if the user is looged in goto account dashboard
if (User::isLoggedIn())
$this->forward('?account');
if (Cfg::get('ACC_EXT_RECOVER_URL'))
$this->forward(Cfg::get('ACC_EXT_RECOVER_URL'));
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError();
parent::__construct($pageParam);
}
protected function generate() : void
{
$this->title[] = Lang::account('title');
parent::generate();
$msg = $this->processMailForm();
if ($this->success)
$this->inputbox = ['inputbox-status', ['head' => Lang::account('inputbox', 'head', 'recoverUser'), 'message' => $msg]];
else
$this->inputbox = ['inputbox-form-email', array(
'head' => Lang::account('inputbox', 'head', 'recoverUser'),
'error' => $msg,
'action' => '?account=forgot-username'
)];
}
private function processMailForm() : string
{
// no input yet. show empty form
if (is_null($this->_post['email']))
return '';
// truncated due to validation fail
if (!$this->_post['email'])
return Lang::account('emailInvalid');
$timeout = DB::Aowow()->selectCell('SELECT `unbanDate` FROM ?_account_bannedips WHERE `ip` = ? AND `type` = ?d AND `count` > ?d AND `unbanDate` > UNIX_TIMESTAMP()', User::$ip, IP_BAN_TYPE_USERNAME_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT'));
// on cooldown pretend we dont know the email address
if ($timeout && $timeout > time())
return Cfg::get('DEBUG') ? 'resend on cooldown: '.Util::formatTimeDiff($timeout).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound');
// pretend recovery started
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `email` = ?', $this->_post['email']))
{
// do not confirm or deny existence of email
$this->success = !Cfg::get('DEBUG');
return Cfg::get('DEBUG') ? Lang::account('inputbox', 'error', 'emailNotFound') : Lang::account('inputbox', 'message', 'recovUserSent', [$this->_post['email']]);
}
// recovery actually started
if ($err = $this->startRecovery(ACC_STATUS_RECOVER_USER, 'recover-user', $this->_post['email']))
return $err;
DB::Aowow()->query('INSERT INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, ?d, UNIX_TIMESTAMP() + ?d) ON DUPLICATE KEY UPDATE `count` = `count` + ?d, `unbanDate` = UNIX_TIMESTAMP() + ?d',
User::$ip, IP_BAN_TYPE_USERNAME_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT') + 1, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
$this->success = true;
return Lang::account('inputbox', 'message', 'recovUserSent', [$this->_post['email']]);
}
}
?>

View File

@@ -1,108 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via form submit on user settings page
*/
class AccountForumavatarResponse extends TextResponse
{
protected ?string $redirectTo = '?account#community';
protected bool $requiresLogin = true;
// called via form submit
protected array $expectedPOST = array(
'avatar' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 0, 'max_range' => 2 ]],
'wowicon' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/' ]], // file name can have \W chars: inv_misc_fork&knife, achievement_dungeon_drak'tharon_heroic
'customicon' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1 ]]
);
// called via ajax
protected array $expectedGET = array(
'avatar' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 2, 'max_range' => 2]],
'customicon' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1 ]]
);
private bool $success = false;
protected function generate() : void
{
if (User::isBanned())
return;
$msg = match ($this->_post['avatar'] ?? $this->_get['avatar'])
{
0 => $this->unset(), // none
1 => $this->fromIcon(), // wow icon
2 => $this->fromUpload(!$this->_get['avatar']), // custom icon (premium feature)
default => Lang::main('genericError')
};
if ($msg)
$_SESSION['msg'] = ['avatar', $this->success, $msg];
}
private function unset() : string
{
$x = DB::Aowow()->query('UPDATE ?_account SET `avatar` = 0 WHERE `id` = ?d', User::$id);
if ($x === null || $x === false)
return Lang::main('genericError');
$this->success = true;
return Lang::account('updateMessage', $x === 0 ? 'avNoChange' : 'avSuccess');
}
private function fromIcon() : string
{
if (!$this->assertPOST('wowicon'))
return Lang::main('intError');
$icon = strtolower(trim($this->_post['wowicon']));
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_icons WHERE `name` = ?', $icon))
return Lang::account('updateMessage', 'avNotFound');
$x = DB::Aowow()->query('UPDATE ?_account SET `avatar` = 1, `wowicon` = ? WHERE `id` = ?d', strtolower($icon), User::$id);
if ($x === null || $x === false)
return Lang::main('genericError');
$this->success = true;
$msg = Lang::account('updateMessage', $x === 0 ? 'avNoChange' : 'avSuccess');
if (($qty = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_account WHERE `wowicon` = ?', $icon)) > 1)
$msg .= ' '.Lang::account('updateMessage', 'avNthUser', [$qty]);
else
$msg .= ' '.Lang::account('updateMessage', 'av1stUser');
return $msg;
}
protected function fromUpload(bool $viaPOST) : string
{
if (!User::isPremium())
return Lang::main('genericError');
if (($viaPOST && !$this->assertPOST('customicon')) || (!$viaPOST && !$this->assertGET('customicon')))
return Lang::main('intError');
$customIcon = $this->_post['customicon'] ?? $this->_get['customicon'];
$x = DB::Aowow()->query('UPDATE ?_account_avatars SET `current` = IF(`id` = ?d, 1, 0) WHERE `userId` = ?d AND `status` <> ?d', $customIcon, User::$id, AvatarMgr::STATUS_REJECTED);
if (!is_int($x))
return Lang::main('genericError');
if (!is_int(DB::Aowow()->query('UPDATE ?_account SET `avatar` = 2 WHERE `id` = ?d', User::$id)))
return Lang::main('intError');
$this->success = true;
return Lang::account('updateMessage', $x === 0 ? 'avNoChange' : 'avSuccess');
}
}
?>

View File

@@ -1,41 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via form submit on user settings page
*/
class AccountPremiumborderResponse extends TextResponse
{
protected ?string $redirectTo = '?account#premium';
protected bool $requiresLogin = true;
protected int $requiredUserGroup = U_GROUP_PREMIUM_PERMISSIONS;
protected array $expectedPOST = array(
'avatarborder' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 0, 'max_range' => 4]],
);
protected function generate() : void
{
if (User::isBanned())
return;
if (!$this->assertPOST('avatarborder'))
return;
$x = DB::Aowow()->query('UPDATE ?_account SET `avatarborder` = ?d WHERE `id` = ?d', $this->_post['avatarborder'], User::$id);
if (!is_int($x))
$_SESSION['msg'] = ['premiumborder', false, Lang::main('genericError')];
else if (!$x)
$_SESSION['msg'] = ['premiumborder', true, Lang::account('updateMessage', 'avNoChange')];
else
$_SESSION['msg'] = ['premiumborder', true, Lang::account('updateMessage', 'avSuccess')];
}
}
?>

View File

@@ -1,36 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via form button on user settings page
*/
class AccountRenameiconResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected int $requiredUserGroup = U_GROUP_PREMIUM_PERMISSIONS;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT ],
'name' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' =>'/^[a-zA-Z][a-zA-Z0-9 ]{0,19}$/']]
);
/*
* response not evaluated
*/
protected function generate() : void
{
if (User::isBanned() || !$this->assertPOST('id', 'name'))
return;
// regexp same as in account.js
DB::Aowow()->query('UPDATE ?_account_avatars SET `name` = ? WHERE `id` = ?d AND `userId` = ?d', trim($this->_post['name']), $this->_post['id'], User::$id);
}
}
?>

View File

@@ -1,52 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed after successful resend request
* empty page with status box
*/
class AccountResendsubmitResponse extends TemplateResponse
{
protected string $template = 'text-page-generic';
protected string $pageName = 'resend';
protected array $expectedPOST = array(
'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
);
public function __construct(string $pageParam)
{
if (!Cfg::get('ACC_ALLOW_REGISTER') || Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError();
parent::__construct($pageParam);
}
protected function generate() : void
{
$this->title[] = Lang::account('title');
$error = $message = '';
if ($this->assertPOST('email'))
$message = Lang::account('inputbox', 'message', 'createAccSent', [$this->_post['email']]);
else
$error = Lang::main('intError');
parent::generate();
$this->inputbox = ['inputbox-status', array(
'head' => Lang::account('inputbox', 'head', 'register', [1.5]),
'message' => $message,
'error' => $error
)];
}
}
?>

View File

@@ -1,98 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via link on login page
* empty page with status box
*/
class AccountResendResponse extends TemplateResponse
{
protected string $template = 'text-page-generic';
protected string $pageName = 'resend';
protected array $expectedPOST = array(
'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
);
private bool $success = false;
public function __construct(string $pageParam)
{
if (Cfg::get('ACC_EXT_RECOVER_URL'))
$this->forward(Cfg::get('ACC_EXT_RECOVER_URL'));
if (!Cfg::get('ACC_ALLOW_REGISTER') || Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError();
parent::__construct($pageParam);
}
protected function generate() : void
{
$this->title[] = Lang::account('title');
parent::generate();
// error from account=activate
if (isset($_SESSION['error']['activate']))
{
$msg = $_SESSION['error']['activate'];
unset($_SESSION['error']['activate']);
}
else
$msg = $this->resend();
if ($this->success)
$this->inputbox = ['inputbox-status', ['head' => Lang::account('inputbox', 'head', 'resendMail'), 'message' => $msg]];
else
$this->inputbox = ['inputbox-form-email', array(
'head' => Lang::account('inputbox', 'head', 'resendMail'),
'message' => Lang::account('inputbox', 'message', 'resendMail'),
'error' => $msg,
'action' => '?account=resend',
)];
}
private function resend() : string
{
// no input yet. show clean form
if (is_null($this->_post['email']))
return '';
// truncated due to validation fail
if (!$this->_post['email'])
return Lang::account('emailInvalid');
$timeout = DB::Aowow()->selectCell('SELECT `unbanDate` FROM ?_account_bannedips WHERE `ip` = ? AND `type` = ?d AND `count` > ?d AND `unbanDate` > UNIX_TIMESTAMP()', User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_COUNT'));
// on cooldown pretend we dont know the email address
if ($timeout && $timeout > time())
return Cfg::get('DEBUG') ? 'resend on cooldown: '.Util::formatTimeDiff($timeout).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound');
// check email and account status
if ($token = DB::Aowow()->selectCell('SELECT `token` FROM ?_account WHERE `email` = ? AND `status` = ?d', $this->_post['email'], ACC_STATUS_NEW))
{
if (!Util::sendMail($this->_post['email'], 'activate-account', [$token]))
return Lang::main('intError');
DB::Aowow()->query('INSERT INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, ?d, UNIX_TIMESTAMP() + ?d) ON DUPLICATE KEY UPDATE `count` = `count` + ?d, `unbanDate` = UNIX_TIMESTAMP() + ?d',
User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_COUNT') + 1, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
$this->success = true;
return Lang::account('inputbox', 'message', 'createAccSent', [$this->_post['email']]);
}
// pretend recovery started
// do not confirm or deny existence of email
$this->success = !Cfg::get('DEBUG');
return Cfg::get('DEBUG') ? Lang::account('inputbox', 'error', 'emailNotFound') : Lang::account('inputbox', 'message', 'createAccSent', [$this->_post['email']]);
}
}
?>

View File

@@ -1,121 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via links on signin form and from recovery email
*
* A) redirect to external page
* B) 1. click password reset link > display email form
* 2. submit email form > send mail with recovery link
* 3. click recovery link from mail > display password reset form
* 4. submit password reset form > update password
*/
class AccountresetpasswordResponse extends TemplateResponse
{
use TrRecoveryHelper, TrGetNext;
protected string $template = 'text-page-generic';
protected string $pageName = 'reset-password';
protected array $expectedGET = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']],
'next' => ['filter' => FILTER_SANITIZE_URL, 'flags' => FILTER_FLAG_STRIP_AOWOW ]
);
protected array $expectedPOST = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']],
'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW ],
'password' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'c_password' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ]
);
private bool $success = false;
public function __construct()
{
$this->title[] = Lang::account('title');
parent::__construct();
// don't redirect logged in users
// you can be forgetful AND logged in
if (Cfg::get('ACC_EXT_RECOVER_URL'))
$this->forward(Cfg::get('ACC_EXT_RECOVER_URL'));
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError();
}
protected function generate() : void
{
parent::generate();
$errMsg = '';
if (!$this->assertGET('key') && !$this->assertPOST('key'))
$errMsg = Lang::account('inputbox', 'error', 'passTokenLost');
else if ($this->_get['key'] && !DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `token` = ? AND `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP()', $this->_get['key'], ACC_STATUS_RECOVER_PASS))
$errMsg = Lang::account('inputbox', 'error', 'passTokenUsed');
if ($errMsg)
{
$this->inputbox = ['inputbox-status', array(
'head' => Lang::account('inputbox', 'head', 'error'),
'error' => $errMsg
)];
return;
}
// step "2.5"
$errMsg = $this->doResetPass();
if ($this->success)
$this->forward('?account=signin');
// step 2
$this->inputbox = ['inputbox-form-password', array(
'head' => Lang::account('inputbox', 'head', 'recoverPass', [2]),
'token' => $this->_post['key'] ?? $this->_get['key'],
'action' => '?account=reset-password&next=account=signin',
'error' => $errMsg,
)];
}
private function doResetPass() : string
{
// no input yet. show clean form
if (!$this->assertPOST('key', 'password', 'c_password') && is_null($this->_post['email']))
return '';
// truncated due to validation fail
if (!$this->_post['email'])
return Lang::account('emailInvalid');
if ($this->_post['password'] != $this->_post['c_password'])
return Lang::account('passCheckFail');
$userData = DB::Aowow()->selectRow('SELECT `id`, `passHash` FROM ?_account WHERE `token` = ? AND `email` = ? AND `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP()',
$this->_post['key'],
$this->_post['email'],
ACC_STATUS_RECOVER_PASS
);
if (!$userData)
return Lang::account('inputbox', 'error', 'emailNotFound');
if (!User::verifyCrypt($this->_post['c_password'], $userData['passHash']))
return Lang::account('newPassDiff');
if (!DB::Aowow()->query('UPDATE ?_account SET `passHash` = ?, `status` = ?d WHERE `id` = ?d', User::hashCrypt($this->_post['c_password']), ACC_STATUS_NONE, $userData['id']))
return Lang::main('intError');
$this->success = true;
return '';
}
}
?>

View File

@@ -1,62 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via revert email link
* write status to session and redirect to account settings
*/
// ?auth=email-revert
class AccountRevertemailaddressResponse extends TemplateResponse
{
protected string $template = 'text-page-generic';
protected string $pageName = 'revert-email-address';
protected array $expectedGET = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']]
);
private bool $success = false;
protected function generate() : void
{
parent::generate();
if (User::isBanned())
return;
$msg = $this->revert();
$this->inputbox = ['inputbox-status', array(
'head' => Lang::account('inputbox', 'head', $this->success ? 'success' : 'error'),
'message' => $this->success ? $msg : '',
'error' => $this->success ? '' : $msg,
)];
}
// this should probably take precedence over email-change
// todo - move personal settings changes to separate table
private function revert() : string
{
if (!$this->assertGET('key'))
return Lang::main('intError');
$acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ?_account WHERE `token` = ?', $this->_get['key']);
if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_EMAIL || $acc['statusTimer'] < time())
return Lang::account('inputbox', 'error', 'mailTokenUsed');
// 0 changes == error
if (!DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = ?', ACC_STATUS_NONE, $this->_get['key']))
return Lang::main('intError');
$this->success = true;
return Lang::account('inputbox', 'message', 'mailRevertOk');
}
}
?>

View File

@@ -1,148 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
2 modes
A) show form
B) execute login and forward to
* self on failure
* next on success
*/
class AccountSigninResponse extends TemplateResponse
{
use TrGetNext;
protected string $template = 'text-page-generic';
protected string $pageName = 'signin';
protected array $expectedPOST = array(
'username' => ['filter' => FILTER_CALLBACK, 'options' => [Util::class, 'validateLogin'] ],
'password' => ['filter' => FILTER_CALLBACK, 'options' => [Util::class, 'validatePassword']],
'remember_me' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkRememberMe'] ]
);
protected array $expectedGET = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']],
'next' => ['filter' => FILTER_SANITIZE_URL, 'flags' => FILTER_FLAG_STRIP_AOWOW ]
);
private bool $success = false;
public function __construct()
{
// if the user is logged in, goto user dashboard
if (User::isLoggedIn())
$this->forward('?user='.User::$username);
parent::__construct();
}
protected function generate() : void
{
$username =
$error = '';
$rememberMe = !!$this->_post['remember_me'];
$this->title = [Lang::account('title')];
// coming from user recovery or creation, prefill username
if ($this->_get['key'])
{
if ($userData = DB::Aowow()->selectRow('SELECT a.`login` AS "0", IF(s.`expires`, 0, 1) AS "1" FROM ?_account a LEFT JOIN ?_account_sessions s ON a.`id` = s.`userId` AND a.`token` = s.`sessionId` WHERE a.`status` IN (?a) AND a.`token` = ?',
[ACC_STATUS_RECOVER_USER, ACC_STATUS_NONE], $this->_get['key']))
[$username, $rememberMe] = $userData;
}
if ($this->doSignIn($error))
$this->forward($this->getNext(true));
if ($error)
User::destroy();
$this->inputbox = ['inputbox-form-signin', array(
'head' => Lang::account('inputbox', 'head', 'signin'),
'action' => '?account=signin&next='.$this->getNext(),
'error' => $error,
'username' => $username,
'rememberMe' => $rememberMe,
'hasRecovery' => Cfg::get('ACC_EXT_RECOVER_URL') || Cfg::get('ACC_AUTH_MODE') == AUTH_MODE_SELF,
)];
parent::generate();
}
private function doSignIn(string &$error) : bool
{
if (is_null($this->_post['username']) && is_null($this->_post['password']))
return false;
if (!$this->assertPOST('username'))
{
$error = Lang::account('userNotFound');
return false;
}
if (!$this->assertPOST('password'))
{
$error = Lang::account('wrongPass');
return false;
}
$error = match (User::authenticate($this->_post['username'], $this->_post['password']))
{
AUTH_OK, AUTH_BANNED => $this->onAuthSuccess(),
// AUTH_BANNED => Lang::account('accBanned'); // ToDo: should this return an error? the actual account functionality should be blocked elsewhere
AUTH_WRONGUSER => Lang::account('userNotFound'),
AUTH_WRONGPASS => Lang::account('wrongPass'),
AUTH_IPBANNED => Lang::account('inputbox', 'error', 'loginExceeded', [Util::formatTime(Cfg::get('ACC_FAILED_AUTH_BLOCK') * 1000)]),
AUTH_INTERNAL_ERR => Lang::main('intError'),
default => Lang::main('intError')
};
return !$error;
}
private function onAuthSuccess() : string
{
if (!User::$ip)
{
trigger_error('AccountSigninResponse::onAuthSuccess() - tried to login user without ip set', E_USER_ERROR);
return Lang::main('intError');
}
// reset account status, update expiration
$ok = DB::Aowow()->query('UPDATE ?_account SET `prevIP` = IF(`curIp` = ?, `prevIP`, `curIP`), `curIP` = IF(`curIp` = ?, `curIP`, ?), `status` = IF(`status` = ?d, `status`, 0), `statusTimer` = IF(`status` = ?d, `statusTimer`, 0), `token` = IF(`status` = ?d, `token`, "") WHERE `id` = ?d',
User::$ip, User::$ip, User::$ip,
ACC_STATUS_NEW, ACC_STATUS_NEW, ACC_STATUS_NEW,
User::$id // available after successful User:authenticate
);
if (!is_int($ok)) // num updated fields or null on fail
{
trigger_error('AccountSigninResponse::onAuthSuccess() - failed to update account status', E_USER_ERROR);
return Lang::main('intError');
}
// DELETE temp session
if ($this->_get['key'])
DB::Aowow()->query('DELETE FROM ?_account_sessions WHERE `sessionId` = ?', $this->_get['key']);
session_regenerate_id(true); // user status changed => regenerate id
// create new session entry
DB::Aowow()->query('INSERT INTO ?_account_sessions (`userId`, `sessionId`, `created`, `expires`, `touched`, `deviceInfo`, `ip`, `status`) VALUES (?d, ?, ?d, ?d, ?d, ?, ?, ?d)',
User::$id, session_id(), time(), $this->_post['remember_me'] ? 0 : time() + Cfg::get('SESSION_TIMEOUT_DELAY'), time(), User::$agent, User::$ip, SESSION_ACTIVE);
if (User::init()) // reinitialize the user
User::save();
return '';
}
}
?>

View File

@@ -1,40 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AccountSignoutResponse extends TextResponse
{
use TrGetNext;
protected array $expectedGET = array(
'next' => ['filter' => FILTER_SANITIZE_URL, 'flags' => FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH],
'global' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet'] ]
);
public function __construct(string $pageParam)
{
// if the user not is logged in goto login page
if (!User::isLoggedIn())
$this->forwardToSignIn();
parent::__construct($pageParam);
}
protected function generate() : void
{
if ($this->_get['global'])
DB::Aowow()->query('UPDATE ?_account_sessions SET `touched` = ?d, `status` = ?d WHERE `userId` = ?d', time(), SESSION_FORCED_LOGOUT, User::$id);
else
DB::Aowow()->query('UPDATE ?_account_sessions SET `touched` = ?d, `status` = ?d WHERE `sessionId` = ?', time(), SESSION_LOGOUT, session_id());
User::destroy();
$this->redirectTo = $this->getNext(true);
}
}
?>

View File

@@ -1,163 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via signup link
* self referencing
*/
class AccountSignupResponse extends TemplateResponse
{
use TrGetNext;
protected string $template = 'text-page-generic';
protected string $pageName = 'signup';
protected array $expectedPOST = array(
'username' => ['filter' => FILTER_SANITIZE_SPECIAL_CHARS, 'flags' => FILTER_FLAG_STRIP_AOWOW ],
'email' => ['filter' => FILTER_SANITIZE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW ],
'password' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'c_password' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'remember_me' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkRememberMe']]
);
protected array $expectedGET = array(
'next' => ['filter' => FILTER_SANITIZE_URL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
);
private bool $success = false;
public function __construct()
{
// if the user is logged in goto account dashboard
if (User::isLoggedIn())
$this->forward('?account');
// redirect to external registration page, if set
if (Cfg::get('ACC_EXT_CREATE_URL'))
$this->forward(Cfg::get('ACC_EXT_CREATE_URL'));
parent::__construct();
// registration not enabled on self
if (!Cfg::get('ACC_ALLOW_REGISTER'))
$this->generateError();
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError();
}
protected function generate() : void
{
$this->title[] = Lang::account('title');
// step 1 - no params > signup form
// step 2 - any param > status box
// step 3 - on ?account=activate
$message = $this->doSignUp();
if ($this->success)
{
$this->inputbox = ['inputbox-status', array(
'head' => Lang::account('inputbox', 'head', 'register', [1.5]),
'message' => Lang::account('inputbox', 'message', 'createAccSent', [$this->_post['email']])
)];
}
else
{
$this->inputbox = ['inputbox-form-signup', array(
'head' => Lang::account('inputbox', 'head', 'register', [1]),
'error' => $message,
'action' => '?account=signup&next='.$this->getNext(),
'username' => $this->_post['username'] ?? '',
'email' => $this->_post['email'] ?? '',
'rememberMe' => !!$this->_post['remember_me'],
)];
}
parent::generate();
}
private function doSignUp() : string
{
// no input yet. show clean form
if (!$this->assertPOST('username', 'password', 'c_password') && is_null($this->_post['email']))
return '';
// truncated due to validation fail
if (!$this->_post['email'])
return Lang::account('emailInvalid');
// check username
if (!Util::validateUsername($this->_post['username'], $e))
return Lang::account($e == 1 ? 'errNameLength' : 'errNameChars');
// check password
if (!Util::validatePassword($this->_post['password'], $e))
return Lang::account($e == 1 ? 'errPassLength' : 'errPassChars');
if ($this->_post['password'] !== $this->_post['c_password'])
return Lang::account('passMismatch');
// check ip
if (!User::$ip)
return Lang::main('intError');
// limit account creation
if (DB::Aowow()->selectRow('SELECT 1 FROM ?_account_bannedips WHERE `type` = ?d AND `ip` = ? AND `count` >= ?d AND `unbanDate` >= UNIX_TIMESTAMP()', IP_BAN_TYPE_REGISTRATION_ATTEMPT, User::$ip, Cfg::get('ACC_FAILED_AUTH_COUNT')))
{
DB::Aowow()->query('UPDATE ?_account_bannedips SET `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + ?d WHERE `ip` = ? AND `type` = ?d', Cfg::get('ACC_FAILED_AUTH_BLOCK'), User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT);
return Lang::account('inputbox', 'error', 'signupExceeded', [Util::formatTime(Cfg::get('ACC_FAILED_AUTH_BLOCK') * 1000)]);
}
// username / email taken
if ($inUseData = DB::Aowow()->SelectRow('SELECT `id`, `username`, `status` = ?d AND `statusTimer` < UNIX_TIMESTAMP() AS "expired" FROM ?_account WHERE (LOWER(`username`) = LOWER(?) OR LOWER(`email`) = LOWER(?))', ACC_STATUS_NEW, $this->_post['username'], $this->_post['email']))
{
if ($inUseData['expired'])
DB::Aowow()->query('DELETE FROM ?_account WHERE `id` = ?d', $inUseData['id']);
else
return Util::lower($inUseData['username']) == Util::lower($this->_post['username']) ? Lang::account('nameInUse') : Lang::account('mailInUse');
}
// create..
$token = Util::createHash();
$userId = DB::Aowow()->query('INSERT INTO ?_account (`login`, `passHash`, `username`, `email`, `joindate`, `curIP`, `locale`, `userGroups`, `status`, `statusTimer`, `token`) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(), ?, ?d, ?d, ?d, UNIX_TIMESTAMP() + ?d, ?)',
$this->_post['username'],
User::hashCrypt($this->_post['password']),
$this->_post['username'],
$this->_post['email'],
User::$ip,
Lang::getLocale()->value,
U_GROUP_PENDING,
ACC_STATUS_NEW,
Cfg::get('ACC_CREATE_SAVE_DECAY'),
$token
);
if (!$userId)
return Lang::main('intError');
// create session tied to the token to store remember_me status
DB::Aowow()->query('INSERT INTO ?_account_sessions (`userId`, `sessionId`, `created`, `expires`, `touched`, `deviceInfo`, `ip`, `status`) VALUES (?d, ?, ?d, ?d, ?d, ?, ?, ?d)',
$userId, $token, time(), $this->_post['remember_me'] ? 0 : time() + Cfg::get('SESSION_TIMEOUT_DELAY'), time(), User::$agent, User::$ip, SESSION_ACTIVE);
if (!Util::sendMail($this->_post['email'], 'activate-account', [$token], Cfg::get('ACC_CREATE_SAVE_DECAY')))
return Lang::main('intError2', ['send mail']);
// success: update ip-bans
DB::Aowow()->query('INSERT INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, 1, UNIX_TIMESTAMP() + ?d) ON DUPLICATE KEY UPDATE `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + ?d',
User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
Util::gainSiteReputation($userId, SITEREP_ACTION_REGISTER);
$this->success = true;
return '';
}
}
?>

View File

@@ -1,48 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via account settings form submit
* write status to session and redirect to account settings
*/
class AccountUpdatecommunitysettingsResponse extends TextResponse
{
protected ?string $redirectTo = '?account#community';
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'desc' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']]
);
private bool $success = false;
protected function generate() : void
{
if (User::isBanned())
return;
if ($message = $this->updateSettings())
$_SESSION['msg'] = ['community', $this->success, $message];
}
protected function updateSettings()
{
if (is_null($this->_post['desc'])) // assertPOST tests for empty string which is valid here
return Lang::main('genericError');
// description - 0 modified rows is still success
if (!is_int(DB::Aowow()->query('UPDATE ?_account SET `description` = ? WHERE `id` = ?d', $this->_post['desc'], User::$id)))
return Lang::main('genericError');
$this->success = true;
return Lang::account('updateMessage', 'community');
}
}
?>

View File

@@ -1,80 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via account settings form submit
* write status to session and redirect to account settings
*/
class AccountUpdateemailResponse extends TextResponse
{
protected ?string $redirectTo = '?account#personal';
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'newemail' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
);
private bool $success = false;
public function __construct(string $pageParam)
{
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
(new TemplateResponse())->generateError();
parent::__construct($pageParam);
}
protected function generate() : void
{
if (User::isBanned())
return;
if ($msg = $this->updateMail())
$_SESSION['msg'] = ['email', $this->success, $msg];
}
private function updateMail() : string
{
// no input yet
if (is_null($this->_post['newemail']))
return Lang::main('intError');
// truncated due to validation fail
if (!$this->_post['newemail'])
return Lang::account('emailInvalid');
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `email` = ? AND `id` <> ?d', $this->_post['newemail'], User::$id))
return Lang::account('mailInUse');
$status = DB::Aowow()->selectCell('SELECT `status` FROM ?_account WHERE `statusTimer` > UNIX_TIMESTAMP() AND `id` = ?d', User::$id);
if ($status != ACC_STATUS_NONE && $status != ACC_STATUS_CHANGE_EMAIL)
return Lang::account('isRecovering', [Util::formatTime(Cfg::get('ACC_RECOVERY_DECAY') * 1000)]);
$oldEmail = DB::Aowow()->selectCell('SELECT `email` FROM ?_account WHERE `id` = ?d', User::$id);
if ($this->_post['newemail'] == $oldEmail)
return Lang::account('newMailDiff');
$token = Util::createHash();
// store new mail in updateValue field, exchange when confirmation mail gets confirmed
if (!DB::Aowow()->query('UPDATE ?_account SET `updateValue` = ?, `status` = ?d, `statusTimer` = UNIX_TIMESTAMP() + ?d, `token` = ? WHERE `id` = ?d',
$this->_post['newemail'], ACC_STATUS_CHANGE_EMAIL, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id))
return Lang::main('intError');
if (!Util::sendMail($this->_post['newemail'], 'change-email', [$token, $this->_post['newemail']], Cfg::get('ACC_RECOVERY_DECAY')))
return Lang::main('intError2', ['send mail']);
if (!Util::sendMail($oldEmail, 'revert-email', [$token, $oldEmail], Cfg::get('ACC_RECOVERY_DECAY')))
return Lang::main('intError2', ['send mail']);
$this->success = true;
return Lang::account('updateMessage', 'personal', [$this->_post['newemail']]);
}
}
?>

View File

@@ -1,60 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via account settings form submit
* write status to session and redirect to account settings
*/
class AccountUpdategeneralsettingsResponse extends TextResponse
{
protected ?string $redirectTo = '?account#general';
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'modelrace' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['default' => 0, 'min_range' => 1, 'max_range' => 11]],
'modelgender' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['default' => 0, 'min_range' => 1, 'max_range' => 2] ],
'idsInLists' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkCheckbox'] ]
);
private bool $success = false;
protected function generate() : void
{
if (User::isBanned())
return;
if ($message = $this->updateGeneral())
$_SESSION['msg'] = ['general', $this->success, $message];
}
private function updateGeneral() : string
{
if (!$this->assertPOST('modelrace', 'modelgender'))
return Lang::main('genericError');
if ($this->_post['modelrace'] && !ChrRace::tryFrom($this->_post['modelrace']))
return Lang::main('genericError');
// js handles this as cookie, so saved as cookie; Q - also save in ?_account table?
if (!DB::Aowow()->query('REPLACE INTO ?_account_cookies (`userId`, `name`, `data`) VALUES (?d, ?, ?)', User::$id, 'default_3dmodel', $this->_post['modelrace']. ',' . $this->_post['modelgender']))
return Lang::main('genericError');
if (!setcookie('default_3dmodel', $this->_post['modelrace']. ',' . $this->_post['modelgender'], 0, '/'))
return Lang::main('intError');
// int > number of edited rows > no changes is still success
if (!is_int(DB::Aowow()->query('UPDATE ?_account SET `debug` = ?d WHERE `id` = ?d', $this->_post['idsInLists'] ? 1 : 0, User::$id)))
return Lang::main('intError');
$this->success = true;
return Lang::account('updateMessage', 'general');
}
}
?>

View File

@@ -1,86 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via account settings form submit
* write status to session and redirect to account settings
*/
class AccountUpdatepasswordResponse extends TextResponse
{
protected ?string $redirectTo = '?account#personal';
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'currentPassword' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
'newPassword' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
'confirmPassword' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
'globalLogout' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkCheckbox']]
);
private bool $success = false;
public function __construct(string $pageParam)
{
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
(new TemplateResponse())->generateError();
parent::__construct($pageParam);
}
protected function generate() : void
{
if (User::isBanned())
return;
if ($msg = $this->updatePassword())
$_SESSION['msg'] = ['password', $this->success, $msg];
}
private function updatePassword() : string
{
if (!$this->assertPOST('currentPassword', 'newPassword', 'confirmPassword'))
return Lang::main('intError');
if (!Util::validatePassword($this->_post['newPassword'], $e))
return Lang::account($e == 1 ? 'errPassLength' : 'errPassChars');
if ($this->_post['newPassword'] !== $this->_post['confirmPassword'])
return Lang::account('passMismatch');
$userData = DB::Aowow()->selectRow('SELECT `status`, `passHash`, `statusTimer` FROM ?_account WHERE `id` = ?d', User::$id);
if ($userData['status'] != ACC_STATUS_NONE && $userData['status'] != ACC_STATUS_CHANGE_PASS && $userData['statusTimer'] > time())
return Lang::account('isRecovering', [Util::formatTime(Cfg::get('ACC_RECOVERY_DECAY') * 1000)]);
if (!User::verifyCrypt($this->_post['currentPassword'], $userData['passHash']))
return Lang::account('wrongPass');
if (User::verifyCrypt($this->_post['newPassword'], $userData['passHash']))
return Lang::account('newPassDiff');
$token = Util::createHash();
// store new hash in updateValue field, exchange when confirmation mail gets confirmed
if (!DB::Aowow()->query('UPDATE ?_account SET `updateValue` = ?, `status` = ?d, `statusTimer` = UNIX_TIMESTAMP() + ?d, `token` = ? WHERE `id` = ?d',
User::hashCrypt($this->_post['newPassword']), ACC_STATUS_CHANGE_PASS, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id))
return Lang::main('intError');
$email = DB::Aowow()->selectCell('SELECT `email` FROM ?_account WHERE `id` = ?d', User::$id);
if (!Util::sendMail($email, 'update-password', [$token, $email], Cfg::get('ACC_RECOVERY_DECAY')))
return Lang::main('intError2', ['send mail']);
// logout all other active sessions
if ($this->_post['globalLogout'])
DB::Aowow()->query('UPDATE ?_account_sessions SET `status` = ?d, `touched` = ?d WHERE `userId` = ?d AND `sessionId` <> ? AND `status` = ?d', SESSION_FORCED_LOGOUT, time(), User::$id, session_id(), SESSION_ACTIVE);
$this->success = true;
return Lang::account('updateMessage', 'personal', [User::$email]);
}
}
?>

View File

@@ -1,61 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via account settings form submit
* write status to session and redirect to account settings
*/
class AccountUpdateusernameResponse extends TextResponse
{
protected ?string $redirectTo = '?account#personal';
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'newUsername' => ['filter' => FILTER_CALLBACK, 'options' => [Util::class, 'validateUsername']]
);
private bool $success = false;
public function __construct(string $pageParam)
{
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
(new TemplateResponse())->generateError();
parent::__construct($pageParam);
}
protected function generate() : void
{
if (User::isBanned())
return;
if ($msg = $this->updateUsername())
$_SESSION['msg'] = ['username', $this->success, $msg];
}
private function updateUsername() : string
{
if (!$this->assertPOST('newUsername'))
return Lang::main('intError');
if (DB::Aowow()->selectCell('SELECT `renameCooldown` FROM ?_account WHERE `id` = ?d', User::$id) > time())
return Lang::main('intError'); // should have grabbed the error response..
// yes, including your current name. you don't want to change into your current name, right?
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_post['newUsername']))
return Lang::account('nameInUse');
DB::Aowow()->query('UPDATE ?_account SET `username` = ?, `renameCooldown` = ?d WHERE `id` = ?d', $this->_post['newUsername'], time() + Cfg::get('acc_rename_decay'), User::$id);
$this->success = true;
return Lang::account('updateMessage', 'username', [User::$username, $this->_post['newUsername']]);
}
}
?>

View File

@@ -1,117 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/*
* accessed via ajax
* returns scaleId if successful, 0 if not
*/
class AccountWeightscalesResponse extends TextResponse
{
private const /* int */ MAX_SCALES = 5; // more or less hard-defined in LANG.message_weightscalesaveerror
protected bool $requiresLogin = true;
protected mixed $result = 0; // default to error
protected array $expectedPOST = array(
'save' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 1]],
'delete' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 1]],
'id' => ['filter' => FILTER_VALIDATE_INT ],
'name' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkName'] ],
'scale' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkScale'] ]
);
protected function generate() : void
{
if (User::isBanned())
return;
if ($this->_post['save'] && $this->_post['id'])
$this->updateWeights();
else if ($this->_post['save'])
$this->createWeights();
else if ($this->_post['delete'])
$this->deleteWeights();
}
private function createWeights() : void
{
if (!$this->assertPOST('name', 'scale'))
return;
$nScales = DB::Aowow()->selectCell('SELECT COUNT(`id`) FROM ?_account_weightscales WHERE `userId` = ?d', User::$id);
if ($nScales >= self::MAX_SCALES)
return;
if ($id = DB::Aowow()->query('INSERT INTO ?_account_weightscales (`userId`, `name`) VALUES (?d, ?)', User::$id, $this->_post['name']))
if ($this->storeScaleData($id))
$this->result = $id;
}
private function updateWeights() : void
{
if (!$this->assertPOST('name', 'scale', 'id'))
return;
// not in DB or not owned by user
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account_weightscales WHERE `userId` = ?d AND `id` = ?d', User::$id, $this->_post['id']))
{
trigger_error('AccountWeightscalesResponse::updateWeights - scale #'.$this->_post['id'].' not in db or not owned by user #'.User::$id, E_USER_ERROR);
return;
}
DB::Aowow()->query('UPDATE ?_account_weightscales SET `name` = ? WHERE `id` = ?d', $this->_post['name'], $this->_post['id']);
$this->storeScaleData($this->_post['id']);
// return edited id on success
$this->result = $this->_post['id'];
}
private function deleteWeights() : void
{
if ($this->assertPOST('id'))
DB::Aowow()->query('DELETE FROM ?_account_weightscales WHERE `id` = ?d AND `userId` = ?d', $this->_post['id'], User::$id);
$this->result = '';
}
private function storeScaleData(int $scaleId) : bool
{
if (!is_int(DB::Aowow()->query('DELETE FROM ?_account_weightscale_data WHERE `id` = ?d', $scaleId)))
return false;
foreach ($this->_post['scale'] as [$k, $v])
if (in_array($k, Util::$weightScales)) // $v is known to be a positive int due to regex check
if (!is_int(DB::Aowow()->query('INSERT INTO ?_account_weightscale_data VALUES (?d, ?, ?d)', $scaleId, $k, $v)))
return false;
return true;
}
/*************************************/
/* additional request data callbacks */
/*************************************/
protected static function checkScale(string $val) : array
{
if (preg_match('/^((\w+:\d+)(,\w+:\d+)*)$/', $val))
return array_map(fn($x) => explode(':', $x), explode(',', $val));
return [];
}
protected static function checkName(string $val) : string
{
return mb_substr(preg_replace('/[^[:print:]]/', '', trim(urldecode($val))), 0, 32);
}
}
?>

View File

@@ -1,511 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
/* Notes:
* can create achievement progress bars with
* g_createProgressBar(c)
* var c = {
* text: "",
* hoverText: "",
* color: "", // cssClassName rep[0-7] | ach[0|1]
* width: 0, // 0 <=> 100
* }
*/
class AchievementBaseResponse extends TemplateResponse implements ICache
{
use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'achievement';
protected string $pageName = 'achievement';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 9];
public int $type = Type::ACHIEVEMENT;
public int $typeId = 0;
public int $reqCrtQty = 0;
public ?array $mail = null;
public string $description = '';
public array $criteria = [];
public ?array $rewards = null;
private AchievementList $subject;
public function __construct(string $id)
{
parent::__construct($id);
$this->typeId = intVal($id);
$this->contribute = Type::getClassAttrib($this->type, 'contribute') ?? CONTRIBUTE_NONE;
}
protected function generate() : void
{
$this->subject = new AchievementList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->generateNotFound(Lang::game('achievement'), Lang::achievement('notFound'));
$this->extendGlobalData($this->subject->getJSGlobals(GLOBALINFO_REWARDS));
$this->h1 = $this->subject->getField('name', true);
$this->gPageInfo += array(
'type' => $this->type,
'typeId' => $this->typeId,
'name' => $this->h1
);
/*************/
/* Menu Path */
/*************/
// create page title and path
$curCat = $this->subject->getField('category');
$catPath = [];
while ($curCat > 0)
{
$catPath[] = $curCat;
$curCat = DB::Aowow()->SelectCell('SELECT `parentCat` FROM ?_achievementcategory WHERE `id` = ?d', $curCat);
}
$this->breadcrumb = array_merge($this->breadcrumb, array_reverse($catPath));
/**************/
/* Page Title */
/**************/
array_unshift($this->title, $this->subject->getField('name', true), Util::ucFirst(Lang::game('achievement')));
/***********/
/* Infobox */
/***********/
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
// points
if ($_ = $this->subject->getField('points'))
$infobox[] = Lang::achievement('points').Lang::main('colon').'[achievementpoints='.$_.']';
// location
// todo (low)
// faction
$infobox[] = Lang::main('side') . match ($this->subject->getField('faction'))
{
SIDE_ALLIANCE => '[span class=icon-alliance]'.Lang::game('si', SIDE_ALLIANCE).'[/span]',
SIDE_HORDE => '[span class=icon-horde]'.Lang::game('si', SIDE_HORDE).'[/span]',
default => Lang::game('si', SIDE_BOTH) // 0, 3
};
// id
$infobox[] = Lang::achievement('id') . $this->typeId;
// icon
if ($_ = $this->subject->getField('iconId'))
{
$infobox[] = Util::ucFirst(Lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
$this->extendGlobalIds(Type::ICON, $_);
}
// profiler relateed (note that this is part of the cache. I don't think this is important enough to calc for every view)
if (Cfg::get('PROFILER_ENABLE') && !($this->subject->getField('flags') & ACHIEVEMENT_FLAG_COUNTER))
{
$x = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_completion_achievements WHERE `achievementId` = ?d', $this->typeId);
$y = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_profiles WHERE `custom` = 0 AND `stub` = 0');
$infobox[] = Lang::profiler('attainedBy', [round(($x ?: 0) * 100 / ($y ?: 1))]);
// completion row added by InfoboxMarkup
}
// original name
if (Lang::getLocale() != Locale::EN)
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
if ($infobox)
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0', !($this->subject->getField('flags') & ACHIEVEMENT_FLAG_COUNTER));
/**********/
/* Series */
/**********/
$series = [];
if ($c = $this->subject->getField('chainId'))
{
$chainAcv = new AchievementList(array(['chainId', $c]));
foreach ($chainAcv->iterate() as $aId => $__)
{
$pos = $chainAcv->getField('chainPos');
if (!isset($series[$pos]))
$series[$pos] = [];
$series[$pos][] = array(
'side' => (int)$chainAcv->getField('faction'),
'typeStr' => Type::getFileString(Type::ACHIEVEMENT),
'typeId' => $aId,
'name' => $chainAcv->getField('name', true)
);
}
}
if ($series)
$this->series = [[array_values($series), null]];
/****************/
/* Main Content */
/****************/
$this->headIcons = [$this->subject->getField('iconString')];
$this->description = $this->subject->getField('description', true);
$this->redButtons = array(
BUTTON_WOWHEAD => !($this->subject->getField('cuFlags') & CUSTOM_SERVERSIDE),
BUTTON_LINKS => array(
'linkColor' => 'ffffff00',
'linkId' => Type::getFileString(Type::ACHIEVEMENT).':'.$this->typeId.':&quot;..UnitGUID(&quot;player&quot;)..&quot;:0:0:0:0:0:0:0:0',
'linkName' => $this->h1,
'type' => $this->type,
'typeId' => $this->typeId
)
);
$this->reqCrtQty = $this->subject->getField('reqCriteriaCount');
if ($this->createMail())
$this->addScript([SC_CSS_FILE, 'css/Book.css']);
// create rewards
$rewItems = $rewTitles = [];
if ($foo = $this->subject->getField('rewards'))
{
if ($itemRewards = array_filter($foo, fn($x) => $x[0] == Type::ITEM))
{
$bar = new ItemList(array(['i.id', array_column($itemRewards, 1)]));
foreach ($bar->iterate() as $id => $__)
$rewItems[] = new IconElement(Type::ITEM, $id, $bar->getField('name', true), quality: $bar->getField('quality'));
}
if ($titleRewards = array_filter($foo, fn($x) => $x[0] == Type::TITLE))
{
$bar = new TitleList(array(['id', array_column($titleRewards, 1)]));
foreach ($bar->iterate() as $id => $__)
$rewTitles[] = Lang::achievement('titleReward', [$id, trim(str_replace('%s', '', $bar->getField('male', true)))]);
}
}
if (($text = $this->subject->getField('reward', true)) || $rewItems || $rewTitles)
$this->rewards = [$rewItems, $rewTitles, $text];
// factionchange-equivalent
if ($pendant = DB::World()->selectCell('SELECT IF(`horde_id` = ?d, `alliance_id`, -`horde_id`) FROM player_factionchange_achievement WHERE `alliance_id` = ?d OR `horde_id` = ?d', $this->typeId, $this->typeId, $this->typeId))
{
$altAcv = new AchievementList(array(['id', abs($pendant)]));
if (!$altAcv->error)
{
$this->transfer = Lang::achievement('_transfer', array(
$altAcv->id,
ITEM_QUALITY_NORMAL,
$altAcv->getField('iconString'),
$altAcv->getField('name', true),
$pendant > 0 ? 'alliance' : 'horde',
$pendant > 0 ? Lang::game('si', SIDE_ALLIANCE) : Lang::game('si', SIDE_HORDE)
));
}
}
/*****************/
/* Criteria List */
/*****************/
// serverside extra-Data (not sure why ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE is set, let a lone a couple hundred times)
if ($crtIds = array_column($this->subject->getCriteria(), 'id'))
$crtExtraData = DB::World()->select('SELECT `criteria_id` AS ARRAY_KEY, `type` AS ARRAY_KEY2, `value1`, `value2`, `ScriptName` FROM achievement_criteria_data WHERE `type` <> ?d AND `criteria_id` IN (?a)', ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE, $crtIds);
else
$crtExtraData = [];
foreach ($this->subject->getCriteria() as $crt)
{
// hide hidden criteria for regular users (really do..?)
// if (($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_HIDDEN) && !User::isInGroup(U_GROUP_STAFF))
// continue;
// alternative display option
$crtName = Util::localizedString($crt, 'name');
$killSuffix = null;
$obj = (int)$crt['value1'];
$qty = (int)$crt['value2'];
switch ($crt['type'])
{
// link to npc
case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
$killSuffix = Lang::achievement('slain');
case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
$crtIcon = new IconElement(Type::NPC, $obj, $crtName ?: CreatureList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon', extraText: $crtName ? null : $killSuffix);
break;
// link to area (by map)
case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
$zoneId = DB::Aowow()->selectCell('SELECT `id` FROM ?_zones WHERE `mapId` = ?', $obj);
$crtIcon = new IconElement(Type::ZONE, $zoneId ?: 0, $crtName ?: ZoneList::getName($zoneId), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
break;
// link to area
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE:
case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA:
$crtIcon = new IconElement(Type::ZONE, $obj, $crtName ?: ZoneList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
break;
// link to skills
case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL:
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL:
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE:
$crtIcon = new IconElement(Type::SKILL, $obj, $crtName ?: SkillList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
$this->extendGlobalIds(Type::SKILL, $obj);
break;
// link to class
case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
$crtIcon = new IconElement(Type::CHR_CLASS, $obj, $crtName ?: CharClassList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
break;
// link to race
case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
$crtIcon = new IconElement(Type::CHR_RACE, $obj, $crtName ?: CharRaceList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
break;
// link to achivement
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
$crtIcon = new IconElement(Type::ACHIEVEMENT, $obj, $crtName ?: AchievementList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
$this->extendGlobalIds(Type::ACHIEVEMENT, $obj);
break;
// link to quest
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
$crtIcon = new IconElement(Type::QUEST, $obj, $crtName ?: QuestList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
break;
// link to spell
case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
$crtIcon = new IconElement(Type::SPELL, $obj, $crtName ?: SpellList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
$this->extendGlobalIds(Type::SPELL, $obj);
break;
// link to item
case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
$item = new ItemList([['id', $obj]]);
$crtIcon = new IconElement(Type::ITEM, $obj, $crtName ?: $item->getField('name', true), quality: $item->getField('quality'), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
$this->extendGlobalData($item->getJSGlobals());
break;
// link to faction (/w target reputation)
case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
$crtIcon = new IconElement(Type::FACTION, $obj, $crtName ?: FactionList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon', extraText: '('.Lang::getReputationLevelForPoints($qty).')');
break;
// link to GObject
case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
$crtIcon = new IconElement(Type::OBJECT, $obj, $crtName ?: GameObjectList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
break;
// link to emote
case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
$crtIcon = new IconElement(Type::EMOTE, $obj, $crtName ?: EmoteList::getName($obj), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
break;
default:
// Add a gold coin icon if required
if ($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER )
$crtIcon = new IconElement(0, 0, '', extraText: Util::formatMoney($qty));
else
$crtIcon = new IconElement(0, 0, $crtName);
break;
}
if (User::isInGroup(U_GROUP_STAFF))
$crtIcon->extraText .= ' [CriteriaId: '.$crt['id'].']';
$extraData = [];
foreach ($crtExtraData[$crt['id']] ?? [] as $xType => $xData)
{
switch ($xType)
{
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE:
$extraData[] = CreatureList::makeLink($xData['value1']);
break;
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE:
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE:
if ($xData['value1'])
$extraData[] = CharClassList::makeLink($xData['value1']);
if ($xData['value2'])
$extraData[] = CharRaceList::makeLink($xData['value2']);
break;
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA:
$extraData[] = SpellList::makeLink($xData['value1']);
break;
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA:
$extraData[] = ZoneList::makeLink($xData['value1']);
break;
case ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT:
if ($xData['ScriptName'] && User::isInGroup(U_GROUP_STAFF))
$extraData[] = 'Script '.$xData['ScriptName'];
break;
case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY:
if ($we = new WorldEventList(array(['holidayId', $xData['value1']])))
$extraData[] = '<a href="?event='.$we->id.'">'.$we->getField('name', true).'</a>';
break;
case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_ID:
if ($z = new ZoneList(array(['mapIdBak', $xData['value1']])))
$extraData[] = '<a href="?zone='.$z->id.'">'.$z->getField('name', true).'</a>';
break;
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE:
$extraData[] = TitleList::makeLink($xData['value1']);
break;
default:
if (User::isInGroup(U_GROUP_STAFF))
$extraData[] = 'has extra criteria data';
}
}
if ($extraData)
$crtIcon->extraText .= ' <br /><sup style="margin-left:8px;">('.implode(', ', $extraData).')</sup>';
$this->criteria[] = $crtIcon;
}
/**************/
/* Extra Tabs */
/**************/
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
// tab: see also
$conditions = array(
['name_loc'.Lang::getLocale()->value, $this->subject->getField('name', true)],
['id', $this->typeId, '!']
);
$saList = new AchievementList($conditions);
if (!$saList->error)
{
$this->extendGlobalData($saList->getJSGlobals());
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $saList->getListviewData(),
'id' => 'see-also',
'name' => '$LANG.tab_seealso',
'visibleCols' => ['category']
), AchievementList::$brickFile));
}
// tab: criteria of
$refs = DB::Aowow()->SelectCol('SELECT `refAchievementId` FROM ?_achievementcriteria WHERE `type` = ?d AND `value1` = ?d',
ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT,
$this->typeId
);
if (!empty($refs))
{
$coList = new AchievementList(array(['id', $refs]));
if (!$coList->error)
{
$this->extendGlobalData($coList->getJSGlobals());
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $coList->getListviewData(),
'id' => 'criteria-of',
'name' => '$LANG.tab_criteriaof',
'visibleCols' => ['category']
), AchievementList::$brickFile));
}
}
// tab: condition for
$cnd = new Conditions();
$cnd->getByCondition(Type::ACHIEVEMENT, $this->typeId)->prepare();
if ($tab = $cnd->toListviewTab('condition-for', '$LANG.tab_condition_for'))
{
$this->extendGlobalData($cnd->getJsGlobals());
$this->lvTabs->addDataTab(...$tab);
}
parent::generate();
if ($this->subject->getField('flags') & ACHIEVEMENT_FLAG_REALM_FIRST)
$this->result->registerDisplayHook('infobox', [self::class, 'infoboxHook']);
}
private function createMail() : bool
{
if ($_ = $this->subject->getField('mailTemplate'))
{
$letter = DB::Aowow()->selectRow('SELECT * FROM ?_mails WHERE `id` = ?d', $_);
if (!$letter)
return false;
$this->mail = array(
'attachments' => [],
'subject' => Util::parseHtmlText(Util::localizedString($letter, 'subject', true)),
'text' => Util::parseHtmlText(Util::localizedString($letter, 'text', true)),
'header' => [$_, null, null]
);
}
else if ($_ = Util::parseHtmlText($this->subject->getField('text', true, true)))
{
$this->mail = array(
'attachments' => [],
'subject' => Util::parseHtmlText($this->subject->getField('subject', true, true)),
'text' => $_,
'header' => [-$this->typeId, null, null]
);
}
else
return false;
if ($senderId = $this->subject->getField('sender'))
if ($senderName = CreatureList::getName($senderId))
$this->mail['header'][1] = Lang::mail('mailBy', [$senderId, $senderName]);
return true;
}
/* finalize infobox */
public static function infoboxHook(Template\PageTemplate &$pt, ?InfoboxMarkup &$markup) : void
{
// realm first still available?
if (!DB::isConnectable(DB_AUTH))
return;
$avlb = [];
foreach (Profiler::getRealms() AS $rId => $rData)
if (!DB::Characters($rId)->selectCell('SELECT 1 FROM character_achievement WHERE `achievement` = ?d', $pt->typeId))
$avlb[] = Util::ucWords($rData['name']);
if (!$avlb)
return;
$addRow = Lang::achievement('rfAvailable').implode(', ', $avlb);
if (!$markup)
$markup = new InfoboxMarkup([$addRow], ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
else
$markup->addItem($addRow);
}
}
?>

View File

@@ -1,50 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AchievementPowerResponse extends TextResponse implements ICache
{
use TrCache, TrTooltip;
private const /* string */ POWER_TEMPLATE = '$WowheadPower.registerAchievement(%d, %d, %s);';
protected int $type = Type::ACHIEVEMENT;
protected int $typeId = 0;
protected int $cacheType = CACHE_TYPE_TOOLTIP;
protected array $expectedGET = array(
'domain' => ['filter' => FILTER_CALLBACK, 'options' => [Locale::class, 'tryFromDomain']]
);
public function __construct($id)
{
parent::__construct($id);
// temp locale
if ($this->_get['domain'])
Lang::load($this->_get['domain']);
$this->typeId = intVal($id);
}
protected function generate() : void
{
$achievement = new AchievementList(array(['id', $this->typeId]));
if ($achievement->error)
$this->cacheType = CACHE_TYPE_NONE;
else
$opts = array(
'name' => $achievement->getField('name', true),
'tooltip' => $achievement->renderTooltip(),
'icon' => $achievement->getField('iconString')
);
$this->result = new Tooltip(self::POWER_TEMPLATE, $this->typeId, $opts ?? []);
}
}
?>

View File

@@ -1,168 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AchievementsBaseResponse extends TemplateResponse implements ICache
{
use TrListPage, TrCache;
protected int $type = Type::ACHIEVEMENT;
protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected string $template = 'achievements';
protected string $pageName = 'achievements';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 9];
protected array $scripts = [[SC_JS_FILE, 'js/filters.js']];
protected array $expectedGET = array(
'filter' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Filter::PATTERN_PARAM]]
);
protected array $validCats = array(
92 => true,
96 => [14861, 14862, 14863],
97 => [14777, 14778, 14779, 14780],
95 => [165, 14801, 14802, 14803, 14804, 14881, 14901, 15003],
168 => [14808, 14805, 14806, 14921, 14922, 14923, 14961, 14962, 15001, 15002, 15041, 15042],
169 => [170, 171, 172],
201 => [14864, 14865, 14866],
155 => [160, 187, 159, 163, 161, 162, 158, 14981, 156, 14941],
81 => true,
1 => array (
130 => [140, 145, 147, 191],
141 => true,
128 => [135, 136, 137],
122 => [123, 124, 125, 126, 127],
133 => true,
14807 => [14821, 14822, 14823, 14963, 15021, 15062],
132 => [178, 173],
134 => true,
131 => true,
21 => [152, 153, 154]
)
);
public function __construct(string $pageParam)
{
$this->getCategoryFromUrl($pageParam);
parent::__construct($pageParam);
$this->subCat = $pageParam !== '' ? '='.$pageParam : '';
$this->filter = new AchievementListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]);
if ($this->filter->shouldReload)
{
$_SESSION['error']['fi'] = $this->filter::class;
$get = $this->filter->buildGETParam();
$this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : ''));
}
$this->filterError = $this->filter->error;
}
protected function generate() : void
{
$this->h1 = Util::ucFirst(Lang::game('achievements'));
$conditions = [];
if (!User::isInGroup(U_GROUP_EMPLOYEE))
$conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
// include child categories if current category is empty
if ($this->category)
$conditions[] = ['category', end($this->category)];
if ($fiCnd = $this->filter->getConditions())
$conditions[] = $fiCnd;
/*************/
/* Menu Path */
/*************/
foreach ($this->category as $cat)
$this->breadcrumb[] = $cat;
/**************/
/* Page Title */
/**************/
array_unshift($this->title, Util::ucFirst(Lang::game('achievements')));
if ($this->category)
array_unshift($this->title, Lang::achievement('cat', end($this->category)));
/****************/
/* Main Content */
/****************/
// fix modern client achievement category structure: top catg [1:char, 2:statistic, 3:guild]
if ($this->category && $this->category[0] != 1)
$link = '=1.'.implode('.', $this->category);
else if ($this->category)
$link = '=2'.(count($this->category) > 1 ? '.'.implode('.', array_slice($this->category, 1)) : '');
else
$link = '';
$this->redButtons[BUTTON_WOWHEAD] = true;
$this->wowheadLink = sprintf(WOWHEAD_LINK, Lang::getLocale()->domain(), $this->pageName, $link);
if ($fiQuery = $this->filter->buildGETParam())
$this->wowheadLink .= '&filter='.$fiQuery;
$acvList = new AchievementList($conditions, ['calcTotal' => true]);
if (!$acvList->getMatches() && $this->category)
{
// ToDo - we also branch into here if the filter prohibits results. That should be skipped.
$conditions = [];
if ($fiCnd)
$conditions[] = $fiCnd;
if ($catList = DB::Aowow()->SelectCol('SELECT `id` FROM ?_achievementcategory WHERE `parentCat` IN (?a) OR `parentCat2` IN (?a) ', $this->category, $this->category))
$conditions[] = ['category', $catList];
$acvList = new AchievementList($conditions, ['calcTotal' => true]);
}
$tabData = [];
if (!$acvList->error)
{
$tabData['data'] = $acvList->getListviewData();
// fill g_items, g_titles, g_achievements
$this->extendGlobalData($acvList->getJSGlobals());
// if we are have different cats display field
if ($acvList->hasDiffFields('category'))
$tabData['visibleCols'] = ['category'];
if ($this->filter->fiExtraCols)
$tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
// create note if search limit was exceeded
if ($acvList->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
{
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_achievementsfound', $acvList->getMatches(), Cfg::get('SQL_LIMIT_DEFAULT'));
$tabData['_truncated'] = 1;
}
}
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
$this->lvTabs->addListviewTab(new Listview($tabData, AchievementList::$brickFile));
parent::generate();
$this->setOnCacheLoaded([self::class, 'onBeforeDisplay']);
}
public static function onBeforeDisplay()
{
// sort for dropdown-menus in filter
Lang::sort('game', 'si');
}
}
?>

View File

@@ -1,68 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminAnnouncementsResponse extends TemplateResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU;
protected string $template = 'text-page-generic';
protected string $pageName = 'announcements';
protected ?int $activeTab = parent::TAB_STAFF;
protected array $breadcrumb = [4, 1, 3]; // Staff > Content > Announcements
protected array $expectedGET = array(
'id' => ['filter' => FILTER_VALIDATE_INT ],
'edit' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet'] ],
'status' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 0, 'max_range' => 2]]
);
protected function generate() : void
{
if ($this->_get['id'] && isset($this->_get['status']))
{
$this->updateStatus();
$this->forward($_SERVER['HTTP_REFERER'] ?? '.');
}
else if ($this->_get['edit'])
$this->displayEditor();
else
$this->displayListing();
parent::generate();
}
private function updateStatus() : void
{
if (!$this->assertGET('status', 'id'))
{
trigger_error('AdminAnnouncementsResponse::updateStatus - error in _GET id/status');
return;
}
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_announcements WHERE `id` = ?d', $this->_get['id']))
{
trigger_error('AdminAnnouncementsResponse::updateStatus - announcement does not exist');
return;
}
DB::Aowow()->query('UPDATE ?_announcements SET `status` = ?d WHERE `id` = ?d', $this->_get['status'], $this->_get['id']);
}
private function displayEditor() : void
{
// TBD
$this->extraHTML = 'TODO - editor';
}
private function displayListing() : void
{
// TBD
// some form of listview with [NEW] button somewhere near the head i guess
$this->extraHTML = 'TODO - announcements listing';
}
}

View File

@@ -1,51 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminCommentResponse extends TextResponse
{
private const /* int */ ERR_NONE = 1;
private const /* int */ ERR_WRITE_DB = 0;
private const /* int */ ERR_MISCELLANEOUS = 999;
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_MOD;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT ],
'status' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 0, 'max_range' => 1]]
);
protected function generate() : void
{
if (!$this->assertPOST('id', 'status'))
{
trigger_error('AdminCommentResponse - malformed request received', E_USER_ERROR);
$this->result = self::ERR_MISCELLANEOUS;
return;
}
// check if is marked as outdated CC_FLAG_OUTDATED?
$ok = false;
if ($this->_post['status']) // outdated, mark as deleted and clear other flags (sticky + outdated)
{
if ($ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = ?d, `deleteUserId` = ?d, `deleteDate` = ?d WHERE `id` = ?d', CC_FLAG_DELETED, User::$id, time(), $this->_post['id']))
if ($rep = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id']))
$rep->close(Report::STATUS_CLOSED_SOLVED);
}
else // up to date
{
if ($ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id']))
if ($rep = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id']))
$rep->close(Report::STATUS_CLOSED_WONTFIX);
}
$this->result = $ok ? self::ERR_NONE : self::ERR_WRITE_DB;
}
}
?>

View File

@@ -1,81 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminGuideResponse extends TextResponse
{
private const /* int */ ERR_NONE = 0;
private const /* int */ ERR_GUIDE = 1;
private const /* int */ ERR_STATUS = 2;
private const /* int */ ERR_WRITE_DB = 3;
private const /* int */ ERR_MISCELLANEOUS = 999;
protected int $requiredUserGroup = U_GROUP_STAFF;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT ],
'status' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => GuideMgr::STATUS_APPROVED, 'max_range' => GuideMgr::STATUS_REJECTED]],
'msg' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ]
);
protected function generate() : void
{
if (!$this->assertPOST('id', 'status'))
{
trigger_error('AdminGuideResponse - malformed request received', E_USER_ERROR);
$this->result = self::ERR_MISCELLANEOUS;
return;
}
$guide = DB::Aowow()->selectRow('SELECT `userId`, `status` FROM ?_guides WHERE `id` = ?d', $this->_post['id']);
if (!$guide)
{
trigger_error('AdminGuideResponse - guide #'.$this->_post['id'].' not found', E_USER_ERROR);
$this->result = self::ERR_GUIDE;
return;
}
if ($this->_post['status'] == $guide['status'])
{
trigger_error('AdminGuideResponse - guide #'.$this->_post['id'].' already has status #'.$this->_post['status'], E_USER_ERROR);
$this->result = self::ERR_STATUS;
return;
}
// status can only be APPROVED or REJECTED due to input validation
if (!$this->update($this->_post['id'], $this->_post['status'], $this->_post['msg']))
{
trigger_error('AdminGuideResponse - write to db failed for guide #'.$this->_post['id'], E_USER_ERROR);
$this->result = self::ERR_WRITE_DB;
return;
}
if ($this->_post['status'] == GuideMgr::STATUS_APPROVED)
Util::gainSiteReputation($guide['userId'], SITEREP_ACTION_ARTICLE, ['id' => $this->_post['id']]);
$this->result = self::ERR_NONE;
}
private function update(int $id, int $status, ?string $msg = null) : bool
{
if ($status == GuideMgr::STATUS_APPROVED) // set display rev to latest
$ok = DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d, `rev` = (SELECT `rev` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d ORDER BY `rev` DESC LIMIT 1), `approveUserId` = ?d, `approveDate` = ?d WHERE `id` = ?d', $status, Type::GUIDE, $id, User::$id, time(), $id);
else
$ok = DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d WHERE `id` = ?d', $status, $id);
if (!$ok)
return false;
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `status`) VALUES (?d, ?d, ?d, ?d)', $id, time(), User::$id, $status);
if ($msg)
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `msg`) VALUES (?d, ?d, ?d, ?)', $id, time(), User::$id, $msg);
return true;
}
}
?>

View File

@@ -1,46 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminGuidesResponse extends TemplateResponse
{
protected int $requiredUserGroup = U_GROUP_STAFF;
protected string $template = 'list-page-generic';
protected string $pageName = 'guides';
protected ?int $activeTab = parent::TAB_STAFF;
protected array $breadcrumb = [4, 1, 25]; // Staff > Content > Guides Awaiting Approval
protected function generate() : void
{
$this->h1 = 'Pending Guides';
array_unshift($this->title, $this->h1);
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
parent::generate();
$pending = new GuideList([['status', GuideMgr::STATUS_REVIEW]]);
if ($pending->error)
$data = [];
else
{
$data = $pending->getListviewData();
$latest = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, MAX(`rev`) FROM ?_articles WHERE `type` = ?d AND `typeId` IN (?a) GROUP BY `rev`', Type::GUIDE, $pending->getFoundIDs());
foreach ($latest as $id => $rev)
$data[$id]['rev'] = $rev;
}
$this->lvTabs->addListviewTab(new Listview(array(
'data' => array_values($data),
'hiddenCols' => ['patch', 'comments', 'views', 'rating'],
'extraCols' => '$_'
), GuideList::$brickFile, 'guideAdminCol'));
}
}
?>

View File

@@ -1,34 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminOutofdateResponse extends TemplateResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_MOD;
protected string $template = 'list-page-generic';
protected string $pageName = 'out-of-date';
protected ?int $activeTab = parent::TAB_STAFF;
protected array $breadcrumb = [4, 1, 23]; // Staff > Content > Out of Date Comments
protected function generate() : void
{
$this->h1 = 'Out of Date Comments';
array_unshift($this->title, $this->h1);
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
parent::generate();
$this->lvTabs->addListviewTab(new Listview(array(
'data' => CommunityContent::getCommentPreviews(['flags' => CC_FLAG_OUTDATED]),
'extraCols' => '$_'
), 'commentpreview', 'commentAdminCol'));
}
}
?>

View File

@@ -1,80 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminPhpinfoResponse extends TemplateResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_DEV;
protected string $template = 'list-page-generic';
protected string $pageName = 'phpinfo';
protected ?int $activeTab = parent::TAB_STAFF;
protected array $breadcrumb = [4, 2, 21]; // Staff > Development > PHP Information
protected function generate() : void
{
$this->h1 = 'PHP Information';
array_unshift($this->title, $this->h1);
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
parent::generate();
$this->addScript([SC_CSS_STRING, <<<CSS
pre { margin: 0px; font-family: monospace; }
.d, th { border: 1px solid #000000; vertical-align: baseline; }
.p { text-align: left; }
.e { background-color: #ccccff; font-weight: bold; color: #000000; }
.h { background-color: #9999cc; font-weight: bold; color: #000000; }
.v { background-color: #cccccc; color: #000000; }
.vr { background-color: #cccccc; text-align: right; color: #000000; }
CSS]);
$bits = [INFO_GENERAL, INFO_CONFIGURATION, INFO_ENVIRONMENT, INFO_MODULES];
$names = ['General', '', '', 'Module'];
foreach ($bits as $i => $b)
{
ob_start();
phpinfo($b);
$buff = ob_get_contents();
ob_end_clean();
$buff = explode('<div class="center">', $buff)[1];
$buff = explode('</div>', $buff);
array_pop($buff); // remove last from stack
$buff = implode('</div>', $buff); // sew it together
if (strpos($buff, '<h1>'))
$buff = explode('</h1>', $buff)[1];
if (strpos($buff, '<h2>'))
{
$parts = explode('<h2>', $buff);
foreach ($parts as $p)
{
if (!preg_match('/\w/i', $p))
continue;
$p = explode('</h2>', $p);
$name = $names[$i] ? $names[$i].': ' : '';
if (preg_match('/<a[^>]*>([\w\s\d]+)<\/a>/i', $p[0], $m))
$name .= $m[1];
else
$name .= $p[0];
$this->lvTabs->addDataTab(strtolower(strtr($name, [' ' => ''])), $name, $p[1]);
}
}
else
$this->lvTabs->addDataTab(strtolower($names[$i]), $names[$i], $buff);
}
}
}
?>

View File

@@ -1,29 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminReportsResponse extends TemplateResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_EDITOR | U_GROUP_MOD | U_GROUP_LOCALIZER | U_GROUP_SCREENSHOT | U_GROUP_VIDEO;
protected string $template = 'admin/reports';
protected string $pageName = 'reports';
protected ?int $activeTab = parent::TAB_STAFF;
protected array $breadcrumb = [4, 5]; // Staff > Reports
protected function generate() : void
{
$this->h1 = 'Reports';
array_unshift($this->title, $this->h1);
$this->extraHTML = 'NYI';
parent::generate();
}
}
?>

View File

@@ -1,68 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminScreenshotsResponse extends TemplateResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
protected string $template = 'admin/screenshots';
protected string $pageName = 'screenshots';
protected ?int $activeTab = parent::TAB_STAFF;
protected array $breadcrumb = [4, 1, 5]; // Staff > Content > Screenshots
protected array $scripts = array(
[SC_JS_FILE, 'js/screenshot.js'],
[SC_CSS_STRING, '.layout {margin: 0px 25px; max-width: inherit; min-width: 1200px; }'],
[SC_CSS_STRING, '#highlightedRow { background-color: #322C1C; }']
);
protected array $expectedGET = array(
'action' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']],
'type' => ['filter' => FILTER_VALIDATE_INT ],
'typeid' => ['filter' => FILTER_VALIDATE_INT ],
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'urldecode' ]
);
public ?bool $getAll = null;
public array $ssPages = [];
public array $ssData = [];
public int $ssNFound = 0;
public array $pageTypes = [];
protected function generate() : void
{
$this->h1 = 'Screenshot Manager';
// types that can have screenshots
foreach (Type::getClassesFor(0, 'contribute', CONTRIBUTE_SS) as $type => $obj)
$this->pageTypes[$type] = Util::ucWords(Lang::game(Type::getFileString($type)));
$ssGetAll = $this->_get['all'];
$ssPages = [];
$ssData = [];
$nMatches = 0;
if ($this->_get['type'] && $this->_get['typeid'])
$ssData = ScreenshotMgr::getScreenshots($this->_get['type'], $this->_get['typeid'], nFound: $nMatches);
else if ($this->_get['user'])
{
if (mb_strlen($this->_get['user']) >= 3)
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user']))
$ssData = ScreenshotMgr::getScreenshots(userId: $uId, nFound: $nMatches);
}
else
$ssPages = ScreenshotMgr::getPages($ssGetAll, $nMatches);
$this->getAll = $ssGetAll;
$this->ssPages = $ssPages;
$this->ssData = $ssData;
$this->ssNFound = $nMatches; // ssm_numPagesFound
parent::generate();
}
}

View File

@@ -1,62 +0,0 @@
<?php
namespace Aowow;
use GdImage;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminScreenshotsActionApproveResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
);
protected function generate() : void
{
if (!$this->assertGET('id'))
{
trigger_error('AdminScreenshotsActionApproveResponse - screenshotId empty', E_USER_ERROR);
return;
}
ScreenshotMgr::init();
// create resized and thumb version of screenshot
$ssEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId` FROM ?_screenshots WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_APPROVED, $this->_get['id']);
foreach ($ssEntries as $id => $ssData)
{
if (!ScreenshotMgr::loadFile(ScreenshotMgr::PATH_PENDING, $id))
continue;
if (!ScreenshotMgr::createResized($id))
continue;
if (!ScreenshotMgr::createThumbnail($id))
continue;
// move pending > normal
if (!rename(sprintf(ScreenshotMgr::PATH_PENDING, $id), sprintf(ScreenshotMgr::PATH_NORMAL, $id)))
continue;
// set as approved in DB
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id);
// gain siterep
Util::gainSiteReputation($ssData['userIdOwner'], SITEREP_ACTION_SUBMIT_SCREENSHOT, ['id' => $id, 'what' => 1, 'date' => $ssData['date']]);
// flag DB entry as having screenshots
if ($tbl = Type::getClassAttrib($ssData['type'], 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_SCREENSHOT, $ssData['typeId']);
unset($ssEntries[$id]);
}
if (!$ssEntries)
trigger_error('AdminScreenshotsActionApproveResponse - screenshot(s) # '.implode(', ', array_keys($ssEntries)).' not in db or already approved', E_USER_WARNING);
}
}

View File

@@ -1,62 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminScreenshotsActionDeleteResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
);
// 2 steps: 1) remove from sight, 2) remove from disk
protected function generate() : void
{
if (!$this->assertGET('id'))
{
trigger_error('AdminScreenshotsActionDeleteResponse - screenshotId empty', E_USER_ERROR);
return;
}
foreach ($this->_get['id'] as $id)
{
// irrevocably purge files already flagged as deleted (should only exist as pending)
if (User::isInGroup(U_GROUP_ADMIN) && DB::Aowow()->selectCell('SELECT 1 FROM ?_screenshots WHERE `status` & ?d AND `id` = ?d', CC_FLAG_DELETED, $id))
{
DB::Aowow()->query('DELETE FROM ?_screenshots WHERE `id` = ?d', $id);
if (file_exists(sprintf(ScreenshotMgr::PATH_PENDING, $id)))
unlink(sprintf(ScreenshotMgr::PATH_PENDING, $id));
continue;
}
// move normal to pending and remove resized and thumb
if (file_exists(sprintf(ScreenshotMgr::PATH_NORMAL, $id)))
rename(sprintf(ScreenshotMgr::PATH_NORMAL, $id), sprintf(ScreenshotMgr::PATH_PENDING, $id));
if (file_exists(sprintf(ScreenshotMgr::PATH_THUMB, $id)))
unlink(sprintf(ScreenshotMgr::PATH_THUMB, $id));
if (file_exists(sprintf(ScreenshotMgr::PATH_RESIZED, $id)))
unlink(sprintf(ScreenshotMgr::PATH_RESIZED, $id));
}
// flag as deleted if not aready
$oldEntries = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, GROUP_CONCAT(`typeId`) FROM ?_screenshots WHERE `id` IN (?a) GROUP BY `type`', $this->_get['id']);
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = ?d, `userIdDelete` = ?d WHERE `id` IN (?a)', CC_FLAG_DELETED, User::$id, $this->_get['id']);
// deflag db entry as having screenshots
foreach ($oldEntries as $type => $typeIds)
{
$typeIds = explode(',', $typeIds);
$toUnflag = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, IF(BIT_OR(`status`) & ?d, 1, 0) AS "hasMore" FROM ?_screenshots WHERE `type` = ?d AND `typeId` IN (?a) GROUP BY `typeId` HAVING `hasMore` = 0', CC_FLAG_APPROVED, $type, $typeIds);
if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable')))
DB::Aowow()->query('UPDATE ?# SET cuFlags = cuFlags & ~?d WHERE id IN (?a)', $tbl, CUSTOM_HAS_SCREENSHOT, array_keys($toUnflag));
}
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminScreenshotsActionEditaltResponse extends TextResponse
{
use TrCommunityHelper;
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_VALIDATE_INT]
);
protected array $expectedPOST = array(
'alt' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']]
);
protected function generate() : void
{
if (!$this->assertGET('id'))
return;
DB::Aowow()->query('UPDATE ?_screenshots SET `caption` = ? WHERE `id` = ?d',
$this->handleCaption($this->_post['alt']),
$this->_get['id']
);
}
}

View File

@@ -1,23 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminScreenshotsActionListResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
protected array $expectedGET = array(
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
);
protected function generate() : void
{
$pages = ScreenshotMgr::getPages($this->_get['all'], $nPages);
$this->result = 'ssm_screenshotPages = '.Util::toJSON($pages).";\n";
$this->result .= 'ssm_numPagesFound = '.$nPages.';';
}
}

View File

@@ -1,31 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminScreenshotsActionManageResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
protected array $expectedGET = array(
'type' => ['filter' => FILTER_VALIDATE_INT ],
'typeid' => ['filter' => FILTER_VALIDATE_INT ],
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'urldecode']
);
protected function generate() : void
{
$res = [];
if ($this->_get['type'] && $this->_get['typeid'])
$res = ScreenshotMgr::getScreenshots($this->_get['type'], $this->_get['typeid']);
else if ($this->_get['user'])
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user']))
$res = ScreenshotMgr::getScreenshots(userId: $uId);
$this->result = 'ssm_screenshotData = '.Util::toJSON($res);
}
}

View File

@@ -1,48 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminScreenshotsActionRelocateResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_VALIDATE_INT],
'typeid' => ['filter' => FILTER_VALIDATE_INT]
// (but not type..?)
);
protected function generate() : void
{
if (!$this->assertGET('id', 'typeid'))
{
trigger_error('AdminScreenshotsActionRelocateResponse - screenshotId or typeId empty', E_USER_ERROR);
return;
}
[$type, $oldTypeId] = array_values(DB::Aowow()->selectRow('SELECT `type`, `typeId` FROM ?_screenshots WHERE `id` = ?d', $this->_get['id']));
$typeId = $this->_get['typeid'];
if (Type::validateIds($type, $typeId))
{
$tbl = Type::getClassAttrib($type, 'dataTable');
// move screenshot
DB::Aowow()->query('UPDATE ?_screenshots SET `typeId` = ?d WHERE `id` = ?d', $typeId, $this->_get['id']);
// flag target as having screenshot
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_SCREENSHOT, $typeId);
// deflag source for having had screenshots (maybe)
$ssInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~`status`) & ?d, 1, 0) AS "hasMore" FROM ?_screenshots WHERE `status`& ?d AND `type` = ?d AND `typeId` = ?d', CC_FLAG_DELETED, CC_FLAG_APPROVED, $type, $oldTypeId);
if ($ssInfo || !$ssInfo['hasMore'])
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` & ~?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_SCREENSHOT, $oldTypeId);
}
else
trigger_error('AdminScreenshotsActionRelocateResponse - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR);
}
}

View File

@@ -1,72 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminScreenshotsActionStickyResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
);
protected function generate() : void
{
if (!$this->assertGET('id'))
{
trigger_error('AdminScreenshotsActionStickyResponse - screenshotId empty', E_USER_ERROR);
return;
}
// this one is a bit strange: as far as i've seen, the only thing a 'sticky' screenshot does is show up in the infobox
// this also means, that only one screenshot per page should be sticky
// so, handle it one by one and the last one affecting one particular type/typId-key gets the cake
$ssEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId`, `status` FROM ?_screenshots WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_DELETED, $this->_get['id']);
foreach ($ssEntries as $id => $ssData)
{
// approve yet unapproved screenshots
if (!($ssData['status'] & CC_FLAG_APPROVED))
{
ScreenshotMgr::init();
if (!ScreenshotMgr::loadFile(ScreenshotMgr::PATH_PENDING, $id))
continue;
if (!ScreenshotMgr::createResized($id))
continue;
if (!ScreenshotMgr::createThumbnail($id))
continue;
// move pending > normal
if (!rename(sprintf(ScreenshotMgr::PATH_PENDING, $id), sprintf(ScreenshotMgr::PATH_NORMAL, $id)))
continue;
// set as approved in DB
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id);
// gain siterep
Util::gainSiteReputation($ssData['userIdOwner'], SITEREP_ACTION_SUBMIT_SCREENSHOT, ['id' => $id, 'what' => 1, 'date' => $ssData['date']]);
// flag DB entry as having screenshots
if ($tbl = Type::getClassAttrib($ssData['type'], 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_SCREENSHOT, $ssData['typeId']);
}
// reset all others
DB::Aowow()->query('UPDATE ?_screenshots a, ?_screenshots b SET a.`status` = a.`status` & ~?d WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND a.`id` <> b.`id` AND b.`id` = ?d', CC_FLAG_STICKY, $id);
// toggle sticky status
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = IF(`status` & ?d, `status` & ~?d, `status` | ?d) WHERE `id` = ?d AND `status` & ?d', CC_FLAG_STICKY, CC_FLAG_STICKY, CC_FLAG_STICKY, $id, CC_FLAG_APPROVED);
unset($ssEntries[$id]);
}
if ($ssEntries)
trigger_error('AdminScreenshotsActionStickyResponse - screenshot(s) # '.implode(', ', array_keys($ssEntries)).' not in db or flagged as deleted', E_USER_WARNING);
}
}

View File

@@ -1,113 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminSiteconfigResponse extends TemplateResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_DEV;
protected string $template = 'admin/siteconfig';
protected string $pageName = 'siteconfig';
protected ?int $activeTab = parent::TAB_STAFF;
protected array $breadcrumb = [4, 2, 18]; // Staff > Development > Site Configuration
protected function generate() : void
{
$this->h1 = 'Site Configuration';
array_unshift($this->title, $this->h1);
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
parent::generate();
$this->addScript([SC_CSS_STRING, <<<CSS
.grid input[type='text'], .grid input[type='number'] { width:250px; text-align:left; }
.grid input[type='button'] { width:65px; padding:2px; }
.grid a.tip { margin:0px 5px; opacity:0.8; }
.grid a.tip:hover { opacity:1; }
.grid tr { height:30px; }
.grid .disabled { opacity:0.4 !important; }
.grid .status { position:absolute; right:5px; }
CSS]);
$head = '<tr><th><b>Key</b></th><th><b>Value</b></th><th style="width:150px;"><b>Options</b></th></tr>';
foreach (Cfg::$categories as $idx => $catName)
{
$rows = '';
foreach (Cfg::forCategory($idx) as $key => [$value, $flags, , $default, $comment])
$rows .= $this->buildRow($key, $value, $flags, $default, $comment);
if ($idx == Cfg::CAT_MISCELLANEOUS)
$rows .= '<tr><td colspan="3"><a class="icon-add" onclick="cfg_add(this)">new configuration</a></td></tr>';
if (!$rows)
continue;
$this->lvTabs->addDataTab(Profiler::urlize($catName), $catName, '<table class="grid">' . $head . $rows . '</table>');
}
}
private function buildRow(string $key, string $value, int $flags, ?string $default, string $comment) : string
{
$buff = '<tr>';
$info = explode(' - ', $comment);
$key = $flags & Cfg::FLAG_PHP ? strtolower($key) : strtoupper($key);
// name
if (!empty($info[0]))
$buff .= '<td>'.sprintf(Util::$dfnString, $info[0], $key).'</td>';
else
$buff .= '<td>'.$key.'</td>';
// value
if ($flags & Cfg::FLAG_TYPE_BOOL)
$buff .= '<td><div id="'.$key.'"><input id="'.$key.'1" type="radio" name="'.$key.'" value="1" '.($value ? 'checked' : null).' /><label for="'.$key.'1">Enabled</label> <input id="'.$key.'0" type="radio" name="'.$key.'" value="0" '.($value ? null : 'checked').' /><label for="'.$key.'0">Disabled</label></div></td>';
else if ($flags & Cfg::FLAG_OPT_LIST && !empty($info[1]))
{
$buff .= '<td><select id="'.$key.'" name="'.$key.'">';
foreach (explode(', ', $info[1]) as $option)
{
[$idx, $name] = explode(':', $option);
$buff .= '<option value="'.$idx.'"'.($value == $idx ? ' selected ' : null).'>'.$name.'</option>';
}
$buff .= '</select></td>';
}
else if ($flags & Cfg::FLAG_BITMASK && !empty($info[1]))
{
$buff .= '<td><div id="'.$key.'">';
foreach (explode(', ', $info[1]) as $option)
{
[$idx, $name] = explode(':', $option);
$buff .= '<input id="'.$key.$idx.'" type="checkbox" name="'.$key.'" value="'.$idx.'"'.($value & (1 << $idx) ? ' checked ' : null).'><label for="'.$key.$idx.'">'.$name.'</label>';
}
$buff .= '</div></td>';
}
else
$buff .= '<td><input id="'.$key.'" type="'.($flags & Cfg::FLAG_TYPE_STRING ? 'text" placeholder="<empty>' : 'number'.($flags & Cfg::FLAG_TYPE_FLOAT ? '" step="any' : '')).'" name="'.$key.'" value="'.$value.'" /></td>';
// actions
$buff .= '<td style="position:relative;">';
$buff .= '<a class="icon-save tip" onclick="cfg_submit.bind(this, \''.$key.'\')()" onmouseover="$WH.Tooltip.showAtCursor(event, \'Save Changes\', 0, 0, \'q\')" onmousemove="$WH.Tooltip.cursorUpdate(event)" onmouseout="$WH.Tooltip.hide()"></a>';
if ($default)
$buff .= '|<a class="icon-refresh tip" onclick="cfg_default(\''.$key.'\', \''.$default.'\')" onmouseover="$WH.Tooltip.showAtCursor(event, \'Restore Default Value\', 0, 0, \'q\')" onmousemove="$WH.Tooltip.cursorUpdate(event)" onmouseout="$WH.Tooltip.hide()"></a>';
else
$buff .= '|<a class="icon-refresh tip disabled"></a>';
if (!($flags & Cfg::FLAG_PERSISTENT))
$buff .= '|<a class="icon-delete tip" onclick="cfg_remove.bind(this, \''.$key.'\')()" onmouseover="$WH.Tooltip.showAtCursor(event, \'Remove Setting\', 0, 0, \'q\')" onmousemove="$WH.Tooltip.cursorUpdate(event)" onmouseout="$WH.Tooltip.hide()"></a>';
$buff .= '<span class="status"></span></td></tr>';
return $buff;
}
}
?>

View File

@@ -1,34 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminSiteconfigActionAddResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_DEV | U_GROUP_ADMIN;
protected array $expectedGET = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Cfg::PATTERN_CONF_KEY_FULL]],
'val' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ]
);
protected function generate() : void
{
if (!$this->assertGET('key', 'val'))
{
trigger_error('AdminSiteconfigActionAddResponse - malformed request received', E_USER_ERROR);
$this->result = Lang::main('intError');
return;
}
$key = trim($this->_get['key']);
$val = trim(urldecode($this->_get['val']));
$this->result = Cfg::add($key, $val);
}
}
?>

View File

@@ -1,30 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminSiteconfigActionRemoveResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_DEV | U_GROUP_ADMIN;
protected array $expectedGET = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Cfg::PATTERN_CONF_KEY_FULL]]
);
protected function generate() : void
{
if (!$this->assertGET('key'))
{
trigger_error('AdminSiteconfigActionRemoveResponse - malformed request received', E_USER_ERROR);
$this->result = Lang::main('intError');
return;
}
$this->result = Cfg::delete($this->_get['key']);
}
}
?>

View File

@@ -1,34 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminSiteconfigActionUpdateResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_DEV | U_GROUP_ADMIN;
protected array $expectedGET = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Cfg::PATTERN_CONF_KEY_FULL]],
'val' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ]
);
protected function generate() : void
{
if (!$this->assertGET('key', 'val'))
{
trigger_error('AdminSiteconfigActionUpdateResponse - malformed request received', E_USER_ERROR);
$this->result = Lang::main('intError');
return;
}
$key = trim($this->_get['key']);
$val = trim(urldecode($this->_get['val']));
$this->result = Cfg::set($key, $val);
}
}
?>

View File

@@ -1,116 +0,0 @@
<?php
namespace Aowow;
use Error;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminSpawnoverrideResponse extends TextResponse
{
private const /* int */ ERR_NONE = 0;
private const /* int */ ERR_NO_POINTS = 1;
private const /* int */ ERR_WORLD_POS = 2;
private const /* int */ ERR_WRONG_TYPE = 3;
private const /* int */ ERR_WRITE_DB = 4;
private const /* int */ ERR_MISCELLANEOUS = 999;
protected int $requiredUserGroup = U_GROUP_MODERATOR;
protected array $expectedGET = array(
'type' => ['filter' => FILTER_VALIDATE_INT],
'guid' => ['filter' => FILTER_VALIDATE_INT],
'area' => ['filter' => FILTER_VALIDATE_INT],
'floor' => ['filter' => FILTER_VALIDATE_INT]
);
protected function generate() : void
{
if (!$this->assertGET('type', 'guid', 'area', 'floor'))
{
trigger_error('AdminSpawnoverrideResponse - malformed request received', E_USER_ERROR);
$this->result = self::ERR_MISCELLANEOUS;
return;
}
$guid = $this->_get['guid'];
$type = $this->_get['type'];
$area = $this->_get['area'];
$floor = $this->_get['floor'];
if (!in_array($type, [Type::NPC, Type::OBJECT, Type::SOUND, Type::AREATRIGGER, Type::ZONE]))
{
trigger_error('AdminSpawnoverrideResponse - can\'t move pip of type '.Type::getFileString($type), E_USER_ERROR);
$this->result = self::ERR_WRONG_TYPE;
return;
}
DB::Aowow()->query('REPLACE INTO ?_spawns_override (`type`, `typeGuid`, `areaId`, `floor`, `revision`) VALUES (?d, ?d, ?d, ?d, ?d)', $type, $guid, $area, $floor, AOWOW_REVISION);
$wPos = WorldPosition::getForGUID($type, $guid);
if (!$wPos)
{
$this->result = self::ERR_WORLD_POS;
return;
}
$point = WorldPosition::toZonePos($wPos[$guid]['mapId'], $wPos[$guid]['posX'], $wPos[$guid]['posY'], $area, $floor);
if (!$point)
{
$this->result = self::ERR_NO_POINTS;
return;
}
$updGUIDs = [$guid];
$newPos = array(
'posX' => $point[0]['posX'],
'posY' => $point[0]['posY'],
'areaId' => $point[0]['areaId'],
'floor' => $point[0]['floor']
);
// if creature try for waypoints
if ($type == Type::NPC)
{
$jobs = array(
'SELECT -w.`id` AS "entry", w.`point` AS "pointId", w.`position_x` AS "posX", w.`position_y` AS "posY" FROM creature_addon ca JOIN waypoint_data w ON w.`id` = ca.`path_id` WHERE ca.`guid` = ?d AND ca.`path_id` <> 0',
'SELECT `entry`, `pointId`, `location_x` AS "posX", `location_y` AS "posY" FROM `script_waypoint` WHERE `entry` = ?d',
'SELECT `entry`, `pointId`, `position_x` AS "posX", `position_y` AS "posY" FROM `waypoints` WHERE `entry` = ?d'
);
foreach ($jobs as $idx => $job)
{
if ($swp = DB::World()->select($job, $idx ? $wPos[$guid]['id'] : $guid))
{
foreach ($swp as $w)
{
if ($point = WorldPosition::toZonePos($wPos[$guid]['mapId'], $w['posX'], $w['posY'], $area, $floor))
{
$p = array(
'posX' => $point[0]['posX'],
'posY' => $point[0]['posY'],
'areaId' => $point[0]['areaId'],
'floor' => $point[0]['floor']
);
DB::Aowow()->query('UPDATE ?_creature_waypoints SET ?a WHERE `creatureOrPath` = ?d AND `point` = ?d', $p, $w['entry'], $w['pointId']);
}
}
}
}
// also move linked vehicle accessories (on the very same position)
$updGUIDs = array_merge($updGUIDs, DB::Aowow()->selectCol('SELECT s2.`guid` FROM ?_spawns s1 JOIN ?_spawns s2 ON s1.`posX` = s2.`posX` AND s1.`posY` = s2.`posY` AND
s1.`areaId` = s2.`areaId` AND s1.`floor` = s2.`floor` AND s2.`guid` < 0 WHERE s1.`guid` = ?d', $guid));
}
if (DB::Aowow()->query('UPDATE ?_spawns SET ?a WHERE `type` = ?d AND `guid` IN (?a)', $newPos, $type, $updGUIDs))
$this->result = self::ERR_NONE;
else
$this->result = self::ERR_WRITE_DB;
}
}
?>

View File

@@ -1,68 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosResponse extends TemplateResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected string $template = 'admin/videos';
protected string $pageName = 'videos';
protected ?int $activeTab = parent::TAB_STAFF;
protected array $breadcrumb = [4, 1, 17]; // Staff > Content > Videos
protected array $scripts = array(
[SC_JS_FILE, 'js/video.js'],
[SC_CSS_STRING, '.layout {margin: 0px 25px; max-width: inherit; min-width: 1200px; }'],
[SC_CSS_STRING, '#highlightedRow { background-color: #322C1C; }']
);
protected array $expectedGET = array(
'action' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']],
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']],
'type' => ['filter' => FILTER_VALIDATE_INT ],
'typeid' => ['filter' => FILTER_VALIDATE_INT ],
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'urldecode' ]
);
public ?bool $getAll = null;
public array $viPages = [];
public array $viData = [];
public int $viNFound = 0;
public array $pageTypes = [];
protected function generate() : void
{
$this->h1 = 'Video Manager';
// types that can have videos
foreach (Type::getClassesFor(0, 'contribute', CONTRIBUTE_SS) as $type => $obj)
$this->pageTypes[$type] = Util::ucWords(Lang::game(Type::getFileString($type)));
$viGetAll = $this->_get['all'];
$viPages = [];
$viData = [];
$nMatches = 0;
if ($this->_get['type'] && $this->_get['typeid'])
$viData = VideoMgr::getVideos($this->_get['type'], $this->_get['typeid'], nFound: $nMatches);
else if ($this->_get['user'])
{
if (mb_strlen($this->_get['user']) >= 3)
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user']))
$viData = VideoMgr::getVideos(userId: $uId, nFound: $nMatches);
}
else
$viPages = VideoMgr::getPages($viGetAll, $nMatches);
$this->getAll = $viGetAll;
$this->viPages = $viPages;
$this->viData = $viData;
$this->viNFound = $nMatches; // ssm_numPagesFound
parent::generate();
}
}

View File

@@ -1,46 +0,0 @@
<?php
namespace Aowow;
use GdImage;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionApproveResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
);
protected function generate() : void
{
if (!$this->assertGET('id'))
{
trigger_error('AdminVideosActionApproveResponse - videoId empty', E_USER_ERROR);
return;
}
$viEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId` FROM ?_videos WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_APPROVED, $this->_get['id']);
foreach ($viEntries as $id => $viData)
{
// set as approved in DB
DB::Aowow()->query('UPDATE ?_videos SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id);
// gain siterep
Util::gainSiteReputation($viData['userIdOwner'], SITEREP_ACTION_SUGGEST_VIDEO, ['id' => $id, 'what' => 1, 'date' => $viData['date']]);
// flag DB entry as having videos
if ($tbl = Type::getClassAttrib($viData['type'], 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $viData['typeId']);
unset($viEntries[$id]);
}
if (!$viEntries)
trigger_error('AdminVideosActionApproveResponse - video(s) # '.implode(', ', array_keys($viEntries)).' not in db or already approved', E_USER_WARNING);
}
}

View File

@@ -1,43 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionDeleteResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
);
// 2 steps: 1) remove from sight, 2) remove from disk
protected function generate() : void
{
if (!$this->assertGET('id'))
{
trigger_error('AdminVideosActionDeleteResponse - videoId empty', E_USER_ERROR);
return;
}
// irrevocably purge files already flagged as deleted (should only exist as pending)
if (User::isInGroup(U_GROUP_ADMIN))
DB::Aowow()->selectCell('SELECT 1 FROM ?_videos WHERE `status` & ?d AND `id` IN (?a)', CC_FLAG_DELETED, $this->_get['id']);
// flag as deleted if not aready
$oldEntries = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, GROUP_CONCAT(`typeId`) FROM ?_videos WHERE `id` IN (?a) GROUP BY `type`', $this->_get['id']);
DB::Aowow()->query('UPDATE ?_videos SET `status` = ?d, `userIdDelete` = ?d WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_DELETED, User::$id, CC_FLAG_DELETED, $this->_get['id']);
// deflag db entry as having videos
foreach ($oldEntries as $type => $typeIds)
{
$typeIds = explode(',', $typeIds);
$toUnflag = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, IF(BIT_OR(`status`) & ?d, 1, 0) AS "hasMore" FROM ?_videos WHERE `type` = ?d AND `typeId` IN (?a) GROUP BY `typeId` HAVING `hasMore` = 0', CC_FLAG_APPROVED, $type, $typeIds);
if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable')))
DB::Aowow()->query('UPDATE ?# SET cuFlags = cuFlags & ~?d WHERE id IN (?a)', $tbl, CUSTOM_HAS_VIDEO, array_keys($toUnflag));
}
}
}

View File

@@ -1,31 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionEdittitleResponse extends TextResponse
{
use TrCommunityHelper;
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
);
protected array $expectedPOST = array(
'title' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']]
);
protected function generate() : void
{
if (!$this->assertGET('id'))
return;
$caption = $this->handleCaption($this->_post['title']);
DB::Aowow()->query('UPDATE ?_videos SET `caption` = ? WHERE `id` = ?d', $caption, $this->_get['id'][0]);
}
}

View File

@@ -1,23 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionListResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'all' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
);
protected function generate() : void
{
$pages = VideoMgr::getPages($this->_get['all'], $nPages);
$this->result = 'vim_videoPages = '.Util::toJSON($pages).";\n";
$this->result .= 'vim_numPagesFound = '.$nPages.';';
}
}

View File

@@ -1,31 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionManageResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'type' => ['filter' => FILTER_VALIDATE_INT ],
'typeid' => ['filter' => FILTER_VALIDATE_INT ],
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'urldecode']
);
protected function generate() : void
{
$res = [];
if ($this->_get['type'] && $this->_get['typeid'])
$res = VideoMgr::getVideos($this->_get['type'], $this->_get['typeid']);
else if ($this->_get['user'])
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user']))
$res = VideoMgr::getVideos(userId: $uId);
$this->result = 'vim_videoData = '.Util::toJSON($res);
}
}

View File

@@ -1,57 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionOrderResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned'] ],
'move' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => -1, 'max_range' => 1]] // -1 = up, 1 = down
);
protected function generate() : void
{
if (!$this->assertGET('id', 'move') || $this->_get['move'] === 0)
{
trigger_error('AdminVideosActionOrderResponse - id or move empty', E_USER_ERROR);
return;
}
$id = $this->_get['id'][0];
$videos = DB::Aowow()->selectCol('SELECT a.`id` AS ARRAY_KEY, a.`pos` FROM ?_videos a, ?_videos b WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND (a.`status` & ?d) = 0 AND b.`id` = ?d ORDER BY a.`pos` ASC', CC_FLAG_DELETED, $id);
if (!$videos || count($videos) == 1)
{
trigger_error('AdminVideosActionOrderResponse - not enough videos to sort', E_USER_WARNING);
return;
}
$dir = $this->_get['move'];
$curPos = $videos[$id];
if ($dir == -1 && $curPos == 0)
{
trigger_error('AdminVideosActionOrderResponse - video #'.$id.' already in top position', E_USER_WARNING);
return;
}
if ($dir == 1 && $curPos + 1 == count($videos))
{
trigger_error('AdminVideosActionOrderResponse - video #'.$id.' already in bottom position', E_USER_WARNING);
return;
}
$oldKey = array_search($curPos + $dir, $videos);
$videos[$oldKey] -= $dir;
$videos[$id] += $dir;
foreach ($videos as $id => $pos)
DB::Aowow()->query('UPDATE ?_videos SET `pos` = ?d WHERE `id` = ?d', $pos, $id);
}
}

View File

@@ -1,49 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionRelocateResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']],
'typeid' => ['filter' => FILTER_VALIDATE_INT ]
// (but not type..?)
);
protected function generate() : void
{
if (!$this->assertGET('id', 'typeid'))
{
trigger_error('AdminVideosActionRelocateResponse - videoId or typeId empty', E_USER_ERROR);
return;
}
$id = $this->_get['id'][0];
[$type, $oldTypeId] = array_values(DB::Aowow()->selectRow('SELECT `type`, `typeId` FROM ?_videos WHERE `id` = ?d', $id));
$typeId = $this->_get['typeid'];
if (Type::validateIds($type, $typeId))
{
$tbl = Type::getClassAttrib($type, 'dataTable');
// move video
DB::Aowow()->query('UPDATE ?_videos SET `typeId` = ?d WHERE `id` = ?d', $typeId, $id);
// flag target as having video
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $typeId);
// deflag source for having had videos (maybe)
$viInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~`status`) & ?d, 1, 0) AS "hasMore" FROM ?_videos WHERE `status`& ?d AND `type` = ?d AND `typeId` = ?d', CC_FLAG_DELETED, CC_FLAG_APPROVED, $type, $oldTypeId);
if ($viInfo || !$viInfo['hasMore'])
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` & ~?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $oldTypeId);
}
else
trigger_error('AdminVideosActionRelocateResponse - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR);
}
}

View File

@@ -1,56 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminVideosActionStickyResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_VIDEO;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']]
);
protected function generate() : void
{
if (!$this->assertGET('id'))
{
trigger_error('AdminVideosActionStickyResponse - videoId empty', E_USER_ERROR);
return;
}
// this one is a bit strange: as far as i've seen, the only thing a 'sticky' video does is show up in the infobox
// this also means, that only one video per page should be sticky
// so, handle it one by one and the last one affecting one particular type/typId-key gets the cake
$viEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId`, `status` FROM ?_videos WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_DELETED, $this->_get['id']);
foreach ($viEntries as $id => $viData)
{
// approve yet unapproved videos
if (!($viData['status'] & CC_FLAG_APPROVED))
{
// set as approved in DB
DB::Aowow()->query('UPDATE ?_videos SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id);
// gain siterep
Util::gainSiteReputation($viData['userIdOwner'], SITEREP_ACTION_SUGGEST_VIDEO, ['id' => $id, 'what' => 1, 'date' => $viData['date']]);
// flag DB entry as having videos
if ($tbl = Type::getClassAttrib($viData['type'], 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $viData['typeId']);
}
// reset all others
DB::Aowow()->query('UPDATE ?_videos a, ?_videos b SET a.`status` = a.`status` & ~?d WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND a.`id` <> b.`id` AND b.`id` = ?d', CC_FLAG_STICKY, $id);
// toggle sticky status
DB::Aowow()->query('UPDATE ?_videos SET `status` = IF(`status` & ?d, `status` & ~?d, `status` | ?d) WHERE `id` = ?d AND `status` & ?d', CC_FLAG_STICKY, CC_FLAG_STICKY, CC_FLAG_STICKY, $id, CC_FLAG_APPROVED);
unset($viEntries[$id]);
}
if ($viEntries)
trigger_error('AdminVideosActionStickyResponse - video(s) # '.implode(', ', array_keys($viEntries)).' not in db or flagged as deleted', E_USER_WARNING);
}
}

View File

@@ -1,54 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminWeightpresetsResponse extends TemplateResponse
{
protected int $requiredUserGroup = U_GROUP_ADMIN | U_GROUP_DEV | U_GROUP_BUREAU;
protected string $template = 'admin/weight-presets';
protected string $pageName = 'weight-presets';
protected ?int $activeTab = parent::TAB_STAFF;
protected array $breadcrumb = [4, 2, 16]; // Staff > Development > Weight Presets
protected array $scripts = array(
[SC_JS_FILE, 'js/filters.js'],
[SC_CSS_STRING, '.wt-edit {display:inline-block; vertical-align:top; width:350px;}']
);
protected function generate() : void
{
$this->h1 = 'Weight Presets';
array_unshift($this->title, $this->h1);
$head = $body = '';
$scales = DB::Aowow()->select('SELECT `class` AS ARRAY_KEY, `id` AS ARRAY_KEY2, `name`, `icon` FROM ?_account_weightscales WHERE `userId` = 0');
$weights = DB::Aowow()->selectCol('SELECT awd.`id` AS ARRAY_KEY, awd.`field` AS ARRAY_KEY2, awd.`val` FROM ?_account_weightscale_data awd JOIN ?_account_weightscales ad ON awd.`id` = ad.`id` WHERE ad.`userId` = 0');
foreach ($scales as $cl => $data)
{
$ul = '';
foreach ($data as $id => $s)
{
$weights[$id]['__icon'] = $s['icon'];
$ul .= '[url=# onclick="loadScale.bind(this, '.$id.')();"]'.$s['name'].'[/url][br]';
}
$head .= '[td=header][class='.$cl.'][/td]';
$body .= '[td valign=top]'.$ul.'[/td]';
$this->extendGlobalIds(Type::CHR_CLASS, $cl);
}
$this->extraText = new Markup('[table class=grid][tr]'.$head.'[/tr][tr]'.$body.'[/tr][/table]', ['allow' => Markup::CLASS_ADMIN], 'text-generic');
$this->extraHTML = '<script type="text/javascript">var wt_presets = '.Util::toJSON($weights).";</script>\n\n";
parent::generate();
}
}
?>

View File

@@ -1,74 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AdminWeightpresetsActionSaveResponse extends TextResponse
{
private const /* int */ ERR_NONE = 0;
private const /* int */ ERR_WRITE_DB = 1;
private const /* int */ ERR_WRITE_FILE = 2;
private const /* int */ ERR_MISCELLANEOUS = 999;
protected int $requiredUserGroup = U_GROUP_DEV | U_GROUP_ADMIN | U_GROUP_BUREAU;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT ],
'__icon' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Cfg::PATTERN_CONF_KEY_FULL]],
'scale' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkScale'] ]
);
protected function generate() : void
{
if (!$this->assertPOST('id', '__icon', 'scale'))
{
trigger_error('AdminWeightpresetsActionSaveResponse - malformed request received', E_USER_ERROR);
$this->result = self::ERR_MISCELLANEOUS;
return;
}
// save to db
DB::Aowow()->query('DELETE FROM ?_account_weightscale_data WHERE `id` = ?d', $this->_post['id']);
DB::Aowow()->query('UPDATE ?_account_weightscales SET `icon`= ? WHERE `id` = ?d', $this->_post['__icon'], $this->_post['id']);
foreach (explode(',', $this->_post['scale']) as $s)
{
[$k, $v] = explode(':', $s);
if (!in_array($k, Util::$weightScales) || $v < 1)
continue;
if (DB::Aowow()->query('INSERT INTO ?_account_weightscale_data VALUES (?d, ?, ?d)', $this->_post['id'], $k, $v) === null)
{
trigger_error('AdminWeightpresetsActionSaveResponse - failed to write to database', E_USER_ERROR);
$this->result = self::ERR_WRITE_DB;
return;
}
}
// write dataset
exec('php aowow --build=weightPresets', $out);
foreach ($out as $o)
if (strstr($o, 'ERR'))
{
trigger_error('AdminWeightpresetsActionSaveResponse - failed to write dataset' . $o, E_USER_ERROR);
$this->result = self::ERR_WRITE_FILE;
return;
}
// all done
$this->result = self::ERR_NONE;
}
protected static function checkScale(string $val) : string
{
if (preg_match('/^((\w+:\d+)(,\w+:\d+)*)$/', $val))
return $val;
return '';
}
}
?>

View File

@@ -1,141 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AreatriggerBaseResponse extends TemplateResponse implements ICache
{
use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected int $requiredUserGroup = U_GROUP_STAFF;
protected string $template = 'detail-page-generic';
protected string $pageName = 'areatrigger';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 102];
public int $type = Type::AREATRIGGER;
public int $typeId = 0;
private AreaTriggerList $subject;
public function __construct(string $id)
{
parent::__construct($id);
$this->typeId = intVal($id);
$this->contribute = Type::getClassAttrib($this->type, 'contribute') ?? CONTRIBUTE_NONE;
}
protected function generate() : void
{
$this->subject = new AreaTriggerList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->generateNotFound(Lang::game('areatrigger'), Lang::areatrigger('notFound'));
$this->h1 = $this->subject->getField('name') ?: 'Areatrigger #'.$this->typeId;
$this->gPageInfo += array(
'type' => $this->type,
'typeId' => $this->typeId,
'name' => $this->h1
);
/*************/
/* Menu Path */
/*************/
$this->breadcrumb[] = $this->subject->getField('type');
/**************/
/* Page Title */
/**************/
array_unshift($this->title, $this->h1, Util::ucFirst(Lang::game('areatrigger')));
/****************/
/* Main Content */
/****************/
$_type = $this->subject->getField('type');
// get spawns
if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL))
{
$this->addDataLoader('zones');
$this->map = array(
['parent' => 'mapper-generic'], // Mapper
$spawns, // mapperData
null, // ShowOnMap
[Lang::areatrigger('foundIn')] // foundIn
);
foreach ($spawns as $areaId => $_)
$this->map[3][$areaId] = ZoneList::getName($areaId);
}
// Smart AI
if ($_type == AT_TYPE_SMART)
{
$sai = new SmartAI(SmartAI::SRC_TYPE_AREATRIGGER, $this->typeId, ['teleportTargetArea' => $this->subject->getField('areaId')]);
if ($sai->prepare())
{
$this->extendGlobalData($sai->getJSGlobals());
$this->smartAI = $sai->getMarkup();
}
}
$this->redButtons = array(
BUTTON_LINKS => false,
BUTTON_WOWHEAD => false
);
/**************/
/* Extra Tabs */
/**************/
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
// tab: conditions
$cnd = new Conditions();
$cnd->getBySourceEntry($this->typeId, Conditions::SRC_AREATRIGGER_CLIENT)->prepare();
if ($tab = $cnd->toListviewTab())
{
$this->extendGlobalData($cnd->getJsGlobals());
$this->lvTabs->addDataTab(...$tab);
}
if ($_type == AT_TYPE_OBJECTIVE)
{
$relQuest = new QuestList(array(['id', $this->subject->getField('quest')]));
if (!$relQuest->error)
{
$this->extendGlobalData($relQuest->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
$this->lvTabs->addListviewTab(new Listview(['data' => $relQuest->getListviewData()], QuestList::$brickFile));
}
}
else if ($_type == AT_TYPE_TELEPORT)
{
$relZone = new ZoneList(array(['id', $this->subject->getField('areaId')]));
if (!$relZone->error)
$this->lvTabs->addListviewTab(new Listview(['data' => $relZone->getListviewData()], ZoneList::$brickFile));
}
else if ($_type == AT_TYPE_SCRIPT)
{
$relTrigger = new AreaTriggerList(array(['id', $this->typeId, '!'], ['name', $this->subject->getField('name')]));
if (!$relTrigger->error)
$this->lvTabs->addListviewTab(new Listview(['data' => $relTrigger->getListviewData(), 'name' => Util::ucFirst(Lang::game('areatrigger'))]), AreaTriggerList::$brickFile, 'areatrigger');
}
parent::generate();
}
}
?>

View File

@@ -1,102 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AreatriggersBaseResponse extends TemplateResponse implements ICache
{
use TrListPage, TrCache;
protected int $type = Type::AREATRIGGER;
protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected int $requiredUserGroup = U_GROUP_STAFF;
protected string $template = 'areatriggers';
protected string $pageName = 'areatriggers';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 102];
protected array $scripts = [[SC_JS_FILE, 'js/filters.js']];
protected array $expectedGET = ['filter' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Filter::PATTERN_PARAM]]];
protected array $validCats = [0, 1, 2, 3, 4, 5];
public function __construct(string $pageParam)
{
$this->getCategoryFromUrl($pageParam);
if (isset($this->category[0]))
$this->forward('?areatriggers&filter=ty='.$this->category[0]);
parent::__construct($pageParam);
$this->filter = new AreaTriggerListFilter($this->_get['filter'] ?? '');
if ($this->filter->shouldReload)
{
$_SESSION['error']['fi'] = $this->filter::class;
$get = $this->filter->buildGETParam();
$this->forward('?' . $this->pageName . ($get ? '&filter=' . $get : ''));
}
$this->filterError = $this->filter->error;
}
protected function generate() : void
{
$this->h1 = Util::ucFirst(Lang::game('areatriggers'));
$fiForm = $this->filter->values;
/**************/
/* Page Title */
/**************/
array_unshift($this->title, $this->h1);
if (count($fiForm['ty']) == 1)
array_unshift($this->title, Lang::areatrigger('types', $fiForm['ty'][0]));
/*************/
/* Menu Path */
/*************/
if (count($fiForm['ty']) == 1)
$this->breadcrumb[] = $fiForm['ty'];
/****************/
/* Main Content */
/****************/
$this->redButtons[BUTTON_WOWHEAD] = false;
$conditions = [];
if ($_ = $this->filter->getConditions())
$conditions[] = $_;
$tabData = [];
$trigger = new AreaTriggerList($conditions, ['calcTotal' => true]);
if (!$trigger->error)
{
$tabData['data'] = $trigger->getListviewData();
// create note if search limit was exceeded; overwriting 'note' is intentional
if ($trigger->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
{
$tabData['note'] = sprintf(Util::$tryFilteringEntityString, $trigger->getMatches(), '"'.Lang::game('areatriggers').'"', Cfg::get('SQL_LIMIT_DEFAULT'));
$tabData['_truncated'] = 1;
}
}
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
$this->lvTabs->addListviewTab(new Listview($tabData, AreaTriggerList::$brickFile, 'areatrigger'));
parent::generate();
}
}
?>

View File

@@ -1,149 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class ArenateamBaseResponse extends TemplateResponse
{
use TrProfilerDetail;
protected string $template = 'roster';
protected string $pageName = 'arena-team';
protected ?int $activeTab = parent::TAB_TOOLS;
protected array $breadcrumb = [1, 5, 3]; // Tools > Profiler > Arena Team
protected array $dataLoader = ['realms', 'weight-presets'];
protected array $scripts = array(
[SC_JS_FILE, 'js/profile_all.js'],
[SC_JS_FILE, 'js/profile.js'],
[SC_CSS_FILE, 'css/Profiler.css']
);
public int $type = Type::ARENA_TEAM;
public function __construct(string $idOrProfile)
{
parent::__construct($idOrProfile);
if (!Cfg::get('PROFILER_ENABLE'))
$this->generateError();
if (!$idOrProfile)
$this->generateError();
$this->getSubjectFromUrl($idOrProfile);
// we have an ID > ok
if ($this->typeId)
return;
// param was incomplete profile > error
if (!$this->subjectName)
$this->generateError();
// 3 possibilities
// 1) already synced to aowow
if ($subject = DB::Aowow()->selectRow('SELECT `id`, `realmGUID`, `stub` FROM ?_profiler_arena_team WHERE `realm` = ?d AND `nameUrl` = ?', $this->realmId, Profiler::urlize($this->subjectName)))
{
$this->typeId = $subject['id'];
if ($subject['stub'])
$this->handleIncompleteData(Type::ARENA_TEAM, $subject['realmGUID']);
return;
}
// 2) not yet synced but exists on realm (wont work if we get passed an urlized name, but there is nothing we can do about it)
$subjects = DB::Characters($this->realmId)->select('SELECT at.`arenaTeamId` AS "realmGUID", at.`name`, at.`type` FROM arena_team at WHERE at.`name` = ?', $this->subjectName);
if ($subject = array_filter($subjects, fn($x) => Util::lower($x['name']) === Util::lower($this->subjectName)))
{
$subject = array_pop($subject);
$subject['realm'] = $this->realmId;
$subject['stub'] = 1;
$subject['nameUrl'] = Profiler::urlize($subject['name']);
// create entry from realm with basic info
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team (?#) VALUES (?a)', array_keys($subject), array_values($subject));
$this->handleIncompleteData(Type::ARENA_TEAM, $subject['realmGUID']);
return;
}
// 3) does not exist at all
$this->notFound();
}
protected function generate() : void
{
if ($this->doResync)
{
parent::generate();
return;
}
$subject = new LocalArenaTeamList(array(['at.id', $this->typeId]));
if ($subject->error)
$this->notFound();
// arena team accessed by id
if (!$this->subjectName)
$this->forward($subject->getProfileUrl());
$this->h1 = Lang::profiler('arenaRoster', [$subject->getField('name')]);
/*************/
/* Menu Path */
/*************/
$this->followBreadcrumbPath();
/**************/
/* Page Title */
/**************/
array_unshift(
$this->title,
$subject->getField('name').' ('.$this->realm.' - '.Lang::profiler('regions', $this->region).')',
Util::ucFirst(Lang::profiler('profiler'))
);
/****************/
/* Main Content */
/****************/
parent::generate();
$this->redButtons[BUTTON_RESYNC] = [$this->typeId, 'arena-team'];
// statistic calculations here
/**************/
/* Extra Tabs */
/**************/
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated');
// tab: members
$member = new LocalProfileList(array(['atm.arenaTeamId', $this->typeId]));
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $member->getListviewData(PROFILEINFO_CHARACTER | PROFILEINFO_ARENA),
'sort' => [-15],
'visibleCols' => ['race', 'classs', 'level', 'talents', 'gearscore', 'rating', 'wins', 'losses'],
'hiddenCols' => ['guild', 'location']
), ProfileList::$brickFile));
}
private function notFound() : never
{
parent::generateNotFound(Lang::game('arenateam'), Lang::profiler('notFound', 'arenateam'));
}
}
?>

View File

@@ -1,48 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class ArenaTeamResyncResponse extends TextResponse
{
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdList'] ],
'profile' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
);
public function __construct(string $pageParam)
{
parent::__construct($pageParam);
if (!Cfg::get('PROFILER_ENABLE'))
$this->generate404();
}
/* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional, not used]
profile: <empty> [optional, also get related chars]
return: 1
*/
protected function generate() : void
{
if (!$this->assertGET('id'))
return;
if ($teams = DB::Aowow()->select('SELECT `realm`, `realmGUID` FROM ?_profiler_arena_team WHERE `id` IN (?a)', $this->_get['id']))
foreach ($teams as $t)
Profiler::scheduleResync(Type::ARENA_TEAM, $t['realm'], $t['realmGUID']);
if ($this->_get['profile'])
if ($chars = DB::Aowow()->select('SELECT `realm`, `realmGUID` FROM ?_profiler_profiles p JOIN ?_profiler_arena_team_member atm ON atm.`profileId` = p.`id` WHERE atm.`arenaTeamId` IN (?a)', $this->_get['id']))
foreach ($chars as $c)
Profiler::scheduleResync(Type::PROFILE, $c['realm'], $c['realmGUID']);
$this->result = 1; // as string?
}
}
?>

View File

@@ -1,29 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class ArenaTeamStatusResponse extends TextResponse
{
protected array $expectedGET = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdList']]
);
public function __construct(string $pageParam)
{
parent::__construct($pageParam);
if (!Cfg::get('PROFILER_ENABLE'))
$this->generate404();
}
protected function generate() : void
{
$this->result = Profiler::resyncStatus(Type::ARENA_TEAM, $this->_get['id']);
}
}
?>

View File

@@ -1,158 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class ArenateamsBaseResponse extends TemplateResponse implements IProfilerList
{
use TrProfilerList, TrListPage;
protected string $template = 'arena-teams';
protected string $pageName = 'arena-teams';
protected ?int $activeTab = parent::TAB_TOOLS;
protected array $breadcrumb = [1, 5, 3]; // Tools > Profiler > Arena Teams
protected array $dataLoader = ['realms'];
protected array $scripts = array(
[SC_JS_FILE, 'js/filters.js'],
[SC_JS_FILE, 'js/profile_all.js'],
[SC_JS_FILE, 'js/profile.js']
);
protected array $expectedGET = array(
'filter' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Filter::PATTERN_PARAM]]
);
public int $type = Type::ARENA_TEAM;
private int $sumSubjects = 0;
public function __construct(string $pageParam)
{
if (!Cfg::get('PROFILER_ENABLE'))
$this->generateError();
$this->getSubjectFromUrl($pageParam);
parent::__construct($pageParam);
$realms = [];
foreach (Profiler::getRealms() as $idx => $r)
{
if ($this->region && $r['region'] != $this->region)
continue;
if ($this->realm && $r['name'] != $this->realm)
continue;
$this->sumSubjects += DB::Characters($idx)->selectCell('SELECT count(*) FROM arena_team');
$realms[] = $idx;
}
$this->subCat = $pageParam !== '' ? '='.$pageParam : '';
$this->filter = new ArenaTeamListFilter($this->_get['filter'] ?? '', ['realms' => $realms]);
if ($this->filter->shouldReload)
{
$_SESSION['error']['fi'] = $this->filter::class;
$get = $this->filter->buildGETParam();
$this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : ''));
}
$this->filterError = $this->filter->error;
}
protected function generate() : void
{
$this->h1 = Lang::game('arenateams');
/*************/
/* Menu Path */
/*************/
$this->followBreadcrumbPath();
/**************/
/* Page Title */
/**************/
if ($this->realm)
array_unshift($this->title, $this->realm,/* Cfg::get('BATTLEGROUP'),*/ Lang::profiler('regions', $this->region), Lang::game('arenateams'));
else if ($this->region)
array_unshift($this->title, Lang::profiler('regions', $this->region), Lang::game('arenateams'));
else
array_unshift($this->title, Lang::game('arenateams'));
/****************/
/* Main Content */
/****************/
$conditions = [];
if (!User::isInGroup(U_GROUP_EMPLOYEE))
$conditions[] = ['at.seasonGames', 0, '>'];
if ($_ = $this->filter->getConditions())
$conditions[] = $_;
$this->getRegions();
$tabData = array(
'id' => 'arena-teams',
'data' => [],
'hideCount' => 1,
'sort' => [-16],
'extraCols' => ['$Listview.extraCols.members'],
'visibleCols' => ['rank', 'wins', 'losses', 'rating'],
'hiddenCols' => ['arenateam', 'guild']
);
if (!$this->filter->values['sz'])
$tabData['visibleCols'][] = 'size';
if ($this->filter->values['si'])
$tabData['hiddenCols'][] = 'faction';
$miscParams = ['calcTotal' => true];
if ($this->realm)
$miscParams['sv'] = $this->realm;
if ($this->region)
$miscParams['rg'] = $this->region;
$teams = new RemoteArenaTeamList($conditions, $miscParams);
if (!$teams->error)
{
$teams->initializeLocalEntries();
$tabData['data'] = $teams->getListviewData();
// create note if search limit was exceeded
if ($this->filter->query && $teams->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
{
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound2', $this->sumSubjects, $teams->getMatches());
$tabData['_truncated'] = 1;
}
else if ($teams->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound', $this->sumSubjects, 0);
}
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated');
$this->lvTabs->addListviewTab(new Listview($tabData, ArenaTeamList::$brickFile, 'membersCol'));
parent::generate();
$this->result->registerDisplayHook('filter', [self::class, 'filterFormHook']);
}
public static function filterFormHook(Template\PageTemplate &$pt, ArenaTeamListFilter $filter) : void
{
// sort for dropdown-menus
Lang::sort('game', 'cl');
Lang::sort('game', 'ra');
}
}
?>

View File

@@ -1,48 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class ClassesBaseResponse extends TemplateResponse implements ICache
{
use TrListPage, TrCache;
protected int $type = Type::CHR_CLASS;
protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected string $template = 'list-page-generic';
protected string $pageName = 'classes';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 12];
public function __construct(string $pageParam)
{
$this->getCategoryFromUrl($pageParam);
parent::__construct($pageParam);
}
protected function generate() : void
{
$this->h1 = Util::ucFirst(Lang::game('classes'));
array_unshift($this->title, Util::ucFirst(Lang::game('classes')));
$this->redButtons[BUTTON_WOWHEAD] = true;
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
$classes = new CharClassList();
if (!$classes->error)
$this->lvTabs->addListviewTab(new Listview(['data' => $classes->getListviewData()], CharClassList::$brickFile));
parent::generate();
}
}
?>

View File

@@ -1,51 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// returns all replies on success
// must have non-200 header on error
class CommentAddreplyResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'commentId' => ['filter' => FILTER_VALIDATE_INT ],
'replyId' => ['filter' => FILTER_VALIDATE_INT ],
'body' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']]
);
protected function generate(): void
{
if (!$this->assertPOST('commentId', 'replyId', 'body'))
{
trigger_error('CommentAddreplyResponse - malformed request received', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
}
if (!User::canReply())
$this->generate404(Lang::main('cannotComment'));
if (!$this->_post['commentId'] || !DB::Aowow()->selectCell('SELECT 1 FROM ?_comments WHERE `id` = ?d', $this->_post['commentId']))
{
trigger_error('CommentAddreplyResponse - parent comment #'.$this->_post['commentId'].' does not exist', E_USER_ERROR);
$this->generate404(Lang::main('intError'));
}
if (mb_strlen($this->_post['body']) < CommunityContent::REPLY_LENGTH_MIN || mb_strlen($this->_post['body']) > CommunityContent::REPLY_LENGTH_MAX)
$this->generate404(Lang::main('textLength', [mb_strlen($this->_post['body']), CommunityContent::REPLY_LENGTH_MIN, CommunityContent::REPLY_LENGTH_MAX]));
if (!DB::Aowow()->query('INSERT INTO ?_comments (`userId`, `roles`, `body`, `date`, `replyTo`) VALUES (?d, ?d, ?, UNIX_TIMESTAMP(), ?d)', User::$id, User::$groups, $this->_post['body'], $this->_post['commentId']))
{
trigger_error('CommentAddreplyResponse - write to db failed', E_USER_ERROR);
$this->generate404(Lang::main('intError'));
}
$this->result = Util::toJSON(CommunityContent::getCommentReplies($this->_post['commentId']));
}
}
?>

View File

@@ -1,79 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class CommentAddResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'commentbody' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']]
);
protected array $expectedGET = array(
'type' => ['filter' => FILTER_VALIDATE_INT],
'typeid' => ['filter' => FILTER_VALIDATE_INT]
);
// i .. have problems believing, that everything uses nifty ajax while adding comments requires a brutal header(Loacation: <wherever>), yet, thats how it is
protected function generate() : void
{
if (!$this->assertGET('type', 'typeid') || !$this->assertPOST('commentbody') || !Type::validateIds($this->_get['type'], $this->_get['typeid']))
{
trigger_error('CommentAddResponse - malforemd request received', E_USER_ERROR);
return; // whatever, we cant even send him back
}
// we now have a valid return target
$idOrUrl = $this->_get['typeid'];
if ($this->_get['type'] == Type::GUIDE)
if ($_ = DB::Aowow()->selectCell('SELECT `url` FROM ?_guides WHERE `id` = ?d', $this->_get['typeid']))
$idOrUrl = $_;
$this->redirectTo = '?'.Type::getFileString($this->_get['type']).'='.$idOrUrl.'#comments';
// this type cannot be commented on
if (!Type::checkClassAttrib($this->_get['type'], 'contribute', CONTRIBUTE_CO))
{
trigger_error('CommentAddResponse - tried to comment on unsupported type: '.Type::getFileString($this->_get['type']), E_USER_ERROR);
$_SESSION['error']['co'] = Lang::main('intError');
return;
}
if (!User::canComment())
{
$_SESSION['error']['co'] = Lang::main('cannotComment');
return;
}
$len = mb_strlen($this->_post['commentbody']);
if ((!User::isInGroup(U_GROUP_MODERATOR) && $len < CommunityContent::COMMENT_LENGTH_MIN) || ($len > CommunityContent::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)))
{
$_SESSION['error']['co'] = Lang::main('textLength', [$len, CommunityContent::COMMENT_LENGTH_MIN, CommunityContent::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)]);
return;
}
if ($postId = DB::Aowow()->query('INSERT INTO ?_comments (`type`, `typeId`, `userId`, `roles`, `body`, `date`) VALUES (?d, ?d, ?d, ?d, ?, UNIX_TIMESTAMP())', $this->_get['type'], $this->_get['typeid'], User::$id, User::$groups, $this->_post['commentbody']))
{
Util::gainSiteReputation(User::$id, SITEREP_ACTION_COMMENT, ['id' => $postId]);
// every comment starts with a rating of +1 and i guess the simplest thing to do is create a db-entry with the system as owner
DB::Aowow()->query('INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, 0, 1)', RATING_COMMENT, $postId);
// flag target with hasComment
if ($tbl = Type::getClassAttrib($this->_get['type'], 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $this->_get['typeid']);
return;
}
trigger_error('CommentAddResponse - write to db failed', E_USER_ERROR);
$_SESSION['error']['co'] = Lang::main('intError');
}
}
?>

View File

@@ -1,44 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// expects non-200 header on error
class CommentDeletereplyResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT]
);
protected function generate() : void
{
if (!$this->assertPOST('id'))
{
trigger_error('CommentDeletereplyResponse - malformed request received', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
}
// flag as deleted (unset sticky (can a reply even be sticky?)
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d | ?d, `deleteUserId` = ?d, `deleteDate` = UNIX_TIMESTAMP() WHERE `id` = ?d { AND `userId` = ?d }',
CC_FLAG_STICKY, CC_FLAG_DELETED,
User::$id,
$this->_post['id'],
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id
);
if ($ok)
DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d', RATING_COMMENT, $this->_post['id']);
else
{
trigger_error('CommentDeletereplyResponse - deleting reply #'.$this->_post['id'].' by user #'.User::$id.' from db failed', E_USER_ERROR);
$this->generate404(Lang::main('intError'));
}
}
}
?>

View File

@@ -1,53 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class CommentDeleteResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']],
// 'username' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ]
);
protected function generate() : void
{
if (!$this->assertPOST('id'))
{
trigger_error('CommentDeleteResponse - malformed request received', E_USER_ERROR);
return;
}
// in theory, there is a username passed alongside if executed from userpage... lets just use the current user (see user.js)
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d, `deleteUserId` = ?d, `deleteDate` = UNIX_TIMESTAMP() WHERE `id` IN (?a) { AND `userId` = ?d }',
CC_FLAG_DELETED,
User::$id,
$this->_post['id'],
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id
);
// unflag subject: hasComment
if ($ok)
{
$coInfo = DB::Aowow()->select(
'SELECT IF(BIT_OR(~b.`flags`) & ?d, 1, 0) AS "0", b.`type` AS "1", b.`typeId` AS "2" FROM ?_comments a JOIN ?_comments b ON a.`type` = b.`type` AND a.`typeId` = b.`typeId` WHERE a.`id` IN (?a) GROUP BY b.`type`, b.`typeId`',
CC_FLAG_DELETED, $this->_post['id']
);
foreach ($coInfo as [$hasMore, $type, $typeId])
if (!$hasMore && ($tbl = Type::getClassAttrib($type, 'dataTable')))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` & ~?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $typeId);
return;
}
trigger_error('CommentDeleteResponse - user #'.User::$id.' could not flag comment(s) #'.implode(', ', $this->_post['id']).' as deleted', E_USER_ERROR);
}
}
?>

View File

@@ -1,30 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// expects non-200 header on error
class CommentDetachreplyResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_MODERATOR;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT]
);
protected function generate() : void
{
if (!$this->assertPOST('id'))
{
trigger_error('CommentDetachreplyResponse - malformed request received', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
}
DB::Aowow()->query('UPDATE ?_comments c1, ?_comments c2 SET c1.`replyTo` = 0, c1.`type` = c2.`type`, c1.`typeId` = c2.`typeId` WHERE c1.`replyTo` = c2.`id` AND c1.`id` = ?d', $this->_post['id']);
}
}
?>

View File

@@ -1,61 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// expects non-200 header on error
class CommentDownvotereplyResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT]
);
protected function generate() : void
{
if (!$this->assertPOST('id'))
{
trigger_error('CommentDownvotereplyResponse - malformed request received', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
}
if (!User::canDownvote())
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'cannot downvote' : '');
$comment = DB::Aowow()->selectRow('SELECT `userId`, IF(`flags` & ?d, 1, 0) AS "deleted" FROM ?_comments WHERE `id` = ?d', CC_FLAG_DELETED, $this->_post['id']);
if (!$comment)
{
trigger_error('CommentDownvotereplyResponse - comment #'.$this->_post['id'].' not found in db', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'replyID not found' : '');
}
if (User::$id == $comment['userId']) // not worth logging?
$this->generate404('LANG.voteself_tip');
if ($comment['deleted'])
$this->generate404('LANG.votedeleted_tip');
$ok = DB::Aowow()->query(
'INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)',
RATING_COMMENT,
$this->_post['id'],
User::$id,
User::canSupervote() ? -2 : -1
);
if (!is_int($ok))
{
trigger_error('CommentDownvotereplyResponse - write to db failed', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'write to db failed' : '');
}
Util::gainSiteReputation($comment['userId'], SITEREP_ACTION_DOWNVOTED, ['id' => $this->_post['id'], 'voterId' => User::$id]);
User::decrementDailyVotes();
}
}
?>

View File

@@ -1,62 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// returns all replies on success
// must have non-200 header on error
class CommentEditreplyResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'commentId' => ['filter' => FILTER_VALIDATE_INT ],
'replyId' => ['filter' => FILTER_VALIDATE_INT ],
'body' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']]
);
protected function generate() : void
{
if (!$this->assertPOST('commentId', 'replyId', 'body'))
{
trigger_error('CommentEditreplyResponse - malformed request received', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
}
$ownerId = DB::Aowow()->selectCell('SELECT `userId` FROM ?_comments WHERE `id` = ?d AND `replyTo` = ?d', $this->_post['replyId'], $this->_post['commentId']);
if (!User::canReply() || (User::$id != $ownerId && !User::isInGroup(U_GROUP_MODERATOR)))
$this->generate404(Lang::main('cannotComment'));
if (!$ownerId)
{
trigger_error('CommentEditreplyResponse - comment #'.$this->_post['commentId'].' or reply #'.$this->_post['replyId'].' does not exist', E_USER_ERROR);
$this->generate404(Lang::main('intError'));
}
if (mb_strlen($this->_post['body']) < CommunityContent::REPLY_LENGTH_MIN || mb_strlen($this->_post['body']) > CommunityContent::REPLY_LENGTH_MAX)
$this->generate404(Lang::main('textLength', [mb_strlen($this->_post['body']), CommunityContent::REPLY_LENGTH_MIN, CommunityContent::REPLY_LENGTH_MAX]));
$update = array(
'body' => $this->_post['body'],
'editUserId' => User::$id,
'editDate' => time()
);
if (User::$id == $ownerId)
$update['roles'] = User::$groups;
if (!DB::Aowow()->query('UPDATE ?_comments SET `editCount` = `editCount` + 1, ?a WHERE `id` = ?d AND `replyTo` = ?d { AND `userId` = ?d }',
$update, $this->_post['replyId'], $this->_post['commentId'], User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id))
{
trigger_error('CommentEditreplyResponse - write to db failed', E_USER_ERROR);
$this->generate404(Lang::main('intError'));
}
$this->result = Util::toJSON(CommunityContent::getCommentReplies($this->_post['commentId']));
}
}
?>

View File

@@ -1,63 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class CommentEditResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'body' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']],
'response' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']]
);
protected array $expectedGET = array(
'id' => ['filter' => FILTER_VALIDATE_INT]
);
protected function generate() : void
{
if (!$this->assertGET('id') || !$this->assertPOST('body'))
{
trigger_error('CommentEditResponse - malforemd request received', E_USER_ERROR);
return;
}
$ownerId = DB::Aowow()->selectCell('SELECT `userId` FROM ?_comments WHERE `id` = ?d', $this->_get['id']);
if (!User::canComment() || (User::$id != $ownerId && !User::isInGroup(U_GROUP_MODERATOR)))
{
trigger_error('CommentEditResponse - user #'.User::$id.' not allowed to edit comment #'.$this->_get['id'], E_USER_ERROR);
return;
}
if (!User::isInGroup(U_GROUP_MODERATOR) && mb_strlen($this->_post['body']) < CommunityContent::COMMENT_LENGTH_MIN)
return; // no point in reporting this trifle
// trim to max length
if (!User::isInGroup(U_GROUP_MODERATOR))
$this->_post['body'] = mb_substr($this->_post['body'], 0, (CommunityContent::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)));
$update = array(
'body' => $this->_post['body'],
'editUserId' => User::$id,
'editDate' => time()
);
if (User::$id == $ownerId)
$update['roles'] = User::$groups;
if (User::isInGroup(U_GROUP_MODERATOR))
{
$update['responseBody'] = $this->_post['response'] ?? '';
$update['responseUserId'] = User::$id;
$update['responseRoles'] = User::$groups;
}
DB::Aowow()->query('UPDATE ?_comments SET `editCount` = `editCount` + 1, ?a WHERE `id` = ?d', $update, $this->_get['id']);
}
}
?>

View File

@@ -1,45 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// expects non-200 header on error
class CommentFlagreplyResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT]
);
protected function generate() : void
{
if (!$this->assertPOST('id'))
{
trigger_error('CommentFlagreplyResponse - malformed request received', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
}
$replyOwner = DB::Aowow()->selectCell('SELECT `userId` FROM ?_commments WHERE `id` = ?d', $this->_post['id']);
if (!$replyOwner)
{
trigger_error('CommentFlagreplyResponse - reply not found', E_USER_ERROR);
$this->generate404(Lang::main('intError'));
}
// ui element should not be present
if ($replyOwner == User::$id)
$this->generate404();
$report = new Report(Report::MODE_COMMENT, Report::CO_INAPPROPRIATE, $this->_post['id']);
if (!$report->create('Report Reply Button Click'))
$this->generate404('LANG.ct_resp_error'.$report->getError());
else if (count($report->getSimilar()) >= CommunityContent::REPORT_THRESHOLD_AUTO_DELETE)
DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d WHERE `id` = ?d', CC_FLAG_DELETED, $this->_post['id']);
}
}
?>

View File

@@ -1,58 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// toggle flag
class CommentOutofdateResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT ],
'remove' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 1]],
'reason' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ]
);
protected function generate() : void
{
if (!$this->assertPOST('id'))
{
trigger_error('CommentOutofdateResponse - malformed request received', E_USER_ERROR);
if (User::isInGroup(U_GROUP_STAFF))
$this->result = 'malformed request received';
}
$ok = false;
if (User::isInGroup(U_GROUP_MODERATOR)) // directly mark as outdated
{
if (!$this->_post['remove'])
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id']);
else
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id']);
}
else // try to report as outdated
{
$report = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id']);
if (!$report->create($this->_post['reason']))
$this->result = Lang::main('intError');
if (count($report->getSimilar()) >= CommunityContent::REPORT_THRESHOLD_AUTO_OUT_OF_DATE)
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id']);
}
if (!$ok)
{
trigger_error('CommentOutofdateResponse - failed to update comment in db', E_USER_ERROR);
$this->result = Lang::main('intError');
return;
}
$this->result = 'ok'; // the js expects the actual characters 'ok' on success, not some json string like '"ok"'
}
}
?>

View File

@@ -1,31 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// up/down - distribution
class CommentRatingResponse extends TextResponse
{
protected array $expectedGET = array(
'id' => ['filter' => FILTER_VALIDATE_INT]
);
protected function generate() : void
{
if (!$this->assertGET('id'))
{
$this->result = Util::toJSON(['success' => 0]);
return;
}
if ($votes = DB::Aowow()->selectRow('SELECT 1 AS "success", SUM(IF(`value` > 0, `value`, 0)) AS "up", SUM(IF(`value` < 0, -`value`, 0)) AS "down" FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND `userId` <> 0 GROUP BY `entry`', RATING_COMMENT, $this->_get['id']))
$this->result = Util::toJSON($votes);
else
$this->result = Util::toJSON(['success' => 1, 'up' => 0, 'down' => 0]);
}
}
?>

View File

@@ -1,24 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class CommentShowrepliesResponse extends TextResponse
{
protected array $expectedGET = array(
'id' => ['filter' => FILTER_VALIDATE_INT]
);
protected function generate() : void
{
if (!$this->assertGET('id'))
$this->result = Util::toJSON([]);
else
$this->result = Util::toJSON(CommunityContent::getCommentReplies($this->_get['id']));
}
}
?>

View File

@@ -1,34 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// toggle flag
class CommentStickyResponse extends TextResponse
{
protected int $requiredUserGroup = U_GROUP_MODERATOR;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT ],
'sticky' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 0, 'max_range' => 1]]
);
protected function generate() : void
{
if (!$this->assertPOST('id', 'sticky'))
{
trigger_error('CommentStickyResponse - malformed request received', E_USER_ERROR);
return;
}
if ($this->_post['sticky'])
DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d WHERE `id` = ?d', CC_FLAG_STICKY, $this->_post['id']);
else
DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` = ?d', CC_FLAG_STICKY, $this->_post['id']);
}
}
?>

View File

@@ -1,48 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class CommentUndeleteResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdListUnsigned']],
// 'username' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ]
);
protected function generate() : void
{
if (!$this->assertPOST('id'))
{
trigger_error('CommentUndeleteResponse - malformed request received', E_USER_ERROR);
return;
}
// in theory, there is a username passed alongside if executed from userpage... lets just use the current user (see user.js)
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` IN (?a) { AND `userId` = `deleteUserId` AND `deleteUserId` = ?d }',
CC_FLAG_DELETED,
$this->_post['id'],
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id
);
// unflag subject: hasComment
if ($ok)
{
$coInfo = DB::Aowow()->select('SELECT `type` AS "0", `typeId` AS "1" FROM ?_comments WHERE `id` IN (?a) GROUP BY `type`, `typeId`', $this->_post['id']);
foreach ($coInfo as [$type, $typeId])
if ($tbl = Type::getClassAttrib($type, 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $typeId);
return;
}
trigger_error('CommentUndeleteResponse - user #'.User::$id.' could not unflag comment(s) #'.implode(', ', $this->_post['id']).' from deleted', E_USER_ERROR);
}
}
?>

View File

@@ -1,61 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// expects non-200 header on error
class CommentUpvotereplyResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedPOST = array(
'id' => ['filter' => FILTER_VALIDATE_INT]
);
protected function generate() : void
{
if (!$this->assertPOST('id'))
{
trigger_error('CommentUpvotereplyResponse - malformed request received', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
}
if (!User::canUpvote())
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'cannot upvote' : '');
$comment = DB::Aowow()->selectRow('SELECT `userId`, IF(`flags` & ?d, 1, 0) AS "deleted" FROM ?_comments WHERE `id` = ?d', CC_FLAG_DELETED, $this->_post['id']);
if (!$comment)
{
trigger_error('CommentUpvotereplyResponse - comment #'.$this->_post['id'].' not found in db', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'replyID not found' : '');
}
if (User::$id == $comment['userId']) // not worth logging?
$this->generate404('LANG.voteself_tip');
if ($comment['deleted'])
$this->generate404('LANG.votedeleted_tip');
$ok = DB::Aowow()->query(
'INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)',
RATING_COMMENT,
$this->_post['id'],
User::$id,
User::canSupervote() ? 2 : 1
);
if (!is_int($ok))
{
trigger_error('CommentUpvotereplyResponse - write to db failed', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'write to db failed' : '');
}
Util::gainSiteReputation($comment['userId'], SITEREP_ACTION_UPVOTED, ['id' => $this->_post['id'], 'voterId' => User::$id]);
User::decrementDailyVotes();
}
}
?>

View File

@@ -1,84 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// up, down and remove
class CommentVoteResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedGET = array(
'id' => ['filter' => FILTER_VALIDATE_INT ],
'rating' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => -2, 'max_range' => 2]]
);
protected function generate(): void
{
if (!$this->assertGET('id', 'rating'))
{
trigger_error('CommentVoteResponse - malformed request received', E_USER_ERROR);
$this->result = Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
return;
}
if (User::getCurrentDailyVotes() <= 0)
{
$this->result = Util::toJSON(['error' => 1, 'message' => Lang::main('tooManyVotes')]);
return;
}
$target = DB::Aowow()->selectRow(
'SELECT c.`userId` AS "owner", ur.`value`, IF(c.`flags` & ?d, 1, 0) AS "deleted" FROM ?_comments c LEFT JOIN ?_user_ratings ur ON ur.`type` = ?d AND ur.`entry` = c.id AND ur.`userId` = ?d WHERE c.id = ?d',
CC_FLAG_DELETED, RATING_COMMENT, User::$id, $this->_get['id']
);
if (!$target)
{
trigger_error('CommentVoteResponse - target comment #'.$this->_get['id'].' not found', E_USER_ERROR);
$this->result = Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
return;
}
$val = User::canSupervote() ? 2 : 1;
if ($this->_get['rating'] < 0)
$val *= -1;
if (User::$id == $target['owner'] || $val != $this->_get['rating'] || $target['deleted'])
{
// circumvented the checks in JS
$this->result = Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
return;
}
if (($val > 0 && !User::canUpvote()) || ($val < 0 && !User::canDownvote()))
{
$this->result = Util::toJSON(['error' => 1, 'message' => Lang::main('bannedRating')]);
return;
}
$ok = false;
// old and new have same sign; undo vote (user may have gained/lost access to superVote in the meantime)
if ($target['value'] && ($target['value'] < 0) == ($val < 0))
$ok = DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND `userId` = ?d', RATING_COMMENT, $this->_get['id'], User::$id);
else // replace, because we may be overwriting an old, opposing vote
if ($ok = DB::Aowow()->query('REPLACE INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)', RATING_COMMENT, $this->_get['id'], User::$id, $val))
User::decrementDailyVotes(); // do not refund retracted votes!
if ($ok)
{
if ($val > 0) // gain rep
Util::gainSiteReputation($target['owner'], SITEREP_ACTION_UPVOTED, ['id' => $this->_get['id'], 'voterId' => User::$id]);
else if ($val < 0)
Util::gainSiteReputation($target['owner'], SITEREP_ACTION_DOWNVOTED, ['id' => $this->_get['id'], 'voterId' => User::$id]);
$this->result = Util::toJSON(['error' => 0]);
}
else
$this->result = Util::toJSON(['error' => 1, 'message' => Lang::main('intError')]);
}
}
?>

View File

@@ -1,116 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
// tabId 1: Tools g_initHeader()
class CompareBaseResponse extends TemplateResponse
{
protected string $template = 'compare';
protected string $pageName = 'compare';
protected ?int $activeTab = parent::TAB_TOOLS;
protected array $breadcrumb = [1, 3];
protected array $dataLoader = ['weight-presets', 'gems', 'enchants', 'itemsets'];
protected array $scripts = array(
[SC_JS_FILE, 'js/profile.js'],
[SC_JS_FILE, 'js/Draggable.js'],
[SC_JS_FILE, 'js/filters.js'],
[SC_JS_FILE, 'js/Summary.js'],
[SC_CSS_FILE, 'css/Summary.css']
);
protected array $expectedGET = array(
'compare' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkCompareString']]
);
protected array $expectedCOOKIE = array(
'compare_groups' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkCompareString']]
);
public Summary $summary;
public array $cmpItems = [];
private string $compareString = '';
public function __construct($pageParam)
{
parent::__construct($pageParam);
// prefer GET over COOKIE
if ($this->_get['compare'])
$this->compareString = $this->_get['compare'];
else if ($this->_cookie['compare_groups'])
$this->compareString = $this->_cookie['compare_groups'];
}
protected function generate() : void
{
$this->h1 = Lang::main('compareTool');
array_unshift($this->title, $this->h1);
$this->summary = new Summary(array(
'template' => 'compare',
'id' => 'compare',
'parent' => 'compare-generic'
));
if ($this->compareString)
{
$items = [];
foreach (explode(';', $this->compareString) as $itemsString)
{
$suGroup = [];
foreach (explode(':', $itemsString) as $itemDef)
{
// [itemId, subItem, permEnch, tempEnch, gem1, gem2, gem3, gem4]
$params = array_pad(array_map('intVal', explode('.', $itemDef)), 8, 0);
$items[] = $params[0];
$suGroup[] = $params;
}
$this->summary->addGroup($suGroup);
}
$iList = new ItemList(array(['i.id', $items]));
$data = $iList->getListviewData(ITEMINFO_SUBITEMS | ITEMINFO_JSON);
foreach ($iList->iterate() as $itemId => $__)
{
if (empty($data[$itemId]))
continue;
if (!empty($data[$itemId]['subitems']))
foreach ($data[$itemId]['subitems'] as &$si)
{
$si['enchantment'] = implode(', ', $si['enchantment']);
unset($si['chance']);
}
$this->cmpItems[$itemId] = [
'name_'.Lang::getLocale()->json() => $iList->getField('name', true),
'quality' => $iList->getField('quality'),
'icon' => $iList->getField('iconString'),
'jsonequip' => $data[$itemId]
];
}
}
parent::generate();
}
protected static function checkCompareString(string $val) : string
{
$val = urldecode($val);
if (preg_match('/[^-?\d\.:;]/', $val))
return '';
return $val;
}
}
?>

View File

@@ -1,54 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class CookieBaseResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedGET = array(
'purge' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
);
public function __construct(private string $param)
{
// note that parent::__construct has to come after this
if ($param && preg_match('/^[\w-]+$/i', $param))
$this->expectedGET = [$param => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']]];
// NOW we know, what to expect and sanitize
parent::__construct($param);
}
/* responses
0: success
$: silent error
*/
protected function generate() : void
{
if (!$this->param && $this->_get['purge'])
{
if (User::$id && DB::Aowow()->query('UPDATE ?_account_cookies SET `data` = "purged" WHERE `userId` = ?d AND `name` LIKE "announcement-%"', User::$id) !== null)
$this->result = 0;
return;
}
if (!$this->param || !$this->assertGET($this->param))
{
trigger_error('CookieBaseResponse - malformed request received', E_USER_ERROR);
return;
}
if (DB::Aowow()->query('REPLACE INTO ?_account_cookies VALUES (?d, ?, ?)', User::$id, $this->param, $this->_get[$this->param]))
$this->result = 0;
else
trigger_error('CookieBaseResponse - write to db failed', E_USER_ERROR);
}
}
?>

View File

@@ -1,76 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class CurrenciesBaseResponse extends TemplateResponse implements ICache
{
use TrListPage, TrCache;
protected int $type = Type::CURRENCY;
protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected string $template = 'list-page-generic';
protected string $pageName = 'currencies';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 15];
protected array $validCats = [1, 2, 3, 22];
public function __construct(string $pageParam)
{
$this->getCategoryFromUrl($pageParam);
parent::__construct($pageParam);
}
protected function generate() : void
{
$this->h1 = Util::ucFirst(Lang::game('currencies'));
/**************/
/* Page Title */
/**************/
array_unshift($this->title, $this->h1);
if ($this->category)
array_unshift($this->title, Lang::currency('cat', $this->category[0]));
/*************/
/* Menu Path */
/*************/
if ($this->category)
$this->breadcrumb[] = $this->category[0];
/****************/
/* Main Content */
/****************/
$this->redButtons[BUTTON_WOWHEAD] = true;
$conditions = [];
if (!User::isInGroup(U_GROUP_EMPLOYEE))
$conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
if ($this->category)
$conditions[] = ['category', $this->category[0]];
$money = new CurrencyList($conditions);
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
$this->lvTabs->addListviewTab(new Listview(['data' => $money->getListviewData()], CurrencyList::$brickFile));
parent::generate();
}
}
?>

View File

@@ -1,50 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class CurrencyPowerResponse extends TextResponse implements ICache
{
use TrTooltip, TrCache;
private const /* string */ POWER_TEMPLATE = '$WowheadPower.registerCurrency(%d, %d, %s);';
protected int $type = Type::CURRENCY;
protected int $typeId = 0;
protected int $cacheType = CACHE_TYPE_TOOLTIP;
protected array $expectedGET = array(
'domain' => ['filter' => FILTER_CALLBACK, 'options' => [Locale::class, 'tryFromDomain']]
);
public function __construct(string $id)
{
parent::__construct($id);
// temp locale
if ($this->_get['domain'])
Lang::load($this->_get['domain']);
$this->typeId = intVal($id);
}
protected function generate() : void
{
$currency = new CurrencyList(array(['id', $this->typeId]));
if ($currency->error)
$this->cacheType = CACHE_TYPE_NONE;
else
$opts = array(
'name' => $currency->getField('name', true),
'tooltip' => $currency->renderTooltip(),
'icon' => $currency->getField('iconString')
);
$this->result = new Tooltip(self::POWER_TEMPLATE, $this->typeId, $opts ?? []);
}
}
?>

View File

@@ -1,149 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class DataBaseResponse extends TextResponse
{
protected array $expectedGET = array(
'locale' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkLocale' ]],
't' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine' ]],
'catg' => ['filter' => FILTER_VALIDATE_INT ],
'skill' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkSkill' ]],
'class' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 11]],
'callback' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkCallback' ]]
);
public function __construct(string $pageParam)
{
parent::__construct($pageParam);
if ($this->_get['locale']?->validate())
Lang::load($this->_get['locale']);
}
protected function generate() : void
{
// different data can be strung together
foreach ($this->params as $set)
{
// requires valid token to hinder automated access
if ($set != 'item-scaling' && (!$this->_get['t'] || empty($_SESSION['dataKey']) || $this->_get['t'] != $_SESSION['dataKey']))
{
trigger_error('DataBaseResponse::generate - session data key empty or expired', E_USER_ERROR);
continue;
}
/* issue on no initial data:
when we loadOnDemand, the jScript tries to generate the catg-tree before it is initialized
it cant be initialized, without loading the data as empty catg are omitted
loading the data triggers the generation of the catg-tree
*/
$this->result .= match($set)
{
'factions' => $this->loadProfilerData($set),
'mounts' => $this->loadProfilerData($set, SKILL_MOUNTS),
'companions' => $this->loadProfilerData($set, SKILL_COMPANIONS),
'quests' => $this->loadProfilerQuests($set, $this->_get['catg']),
'recipes' => $this->loadProfilerRecipes(),
// locale independent
'quick-excludes',
'weight-presets',
'item-scaling',
'realms',
'statistics' => $this->loadAgnosticFile($set),
// localized
'talents',
'achievements',
'pet-talents',
'glyphs',
'gems',
'enchants',
'itemsets',
'pets',
'zones' => $this->loadLocalizedFile($set),
default => (function($x) { trigger_error('DataBaseResponse::generate - invalid file "'.$x.'" in request', E_USER_ERROR); })($set),
};
}
}
private function loadProfilerRecipes() : string
{
if (!$this->_get['callback'] || !$this->_get['skill'])
return '';
$result = '';
foreach ($this->_get['skill'] as $s)
Util::loadStaticFile('p-recipes-'.$s, $result, true);
Util::loadStaticFile('p-recipes-sec', $result, true);
$result .= "\n\$WowheadProfiler.loadOnDemand('recipes', null);\n";
return $result;
}
private function loadProfilerQuests(string $file, ?string $catg = null) : string
{
$result = '';
if ($catg === null)
Util::loadStaticFile('p-'.$file, $result, false);
else
Util::loadStaticFile('p-'.$file.'-'.$catg, $result, true);
$result .= "\n\$WowheadProfiler.loadOnDemand('".$file."', ".($catg ?? 'null').");\n";
return $result;
}
private function loadProfilerData(string $file, ?string $catg = null) : string
{
$result = '';
if ($this->_get['callback'])
if (Util::loadStaticFile('p-'.$file, $result, true))
$result .= "\n\$WowheadProfiler.loadOnDemand('".$file."', ".($catg ?? 'null').");\n";
return $result;
}
private function loadAgnosticFile(string $file) : string
{
$result = '';
if (!Util::loadStaticFile($file, $result) && Cfg::get('DEBUG'))
$result .= "alert('could not fetch static data: ".$file."');";
return $result . "\n\n";
}
private function loadLocalizedFile(string $file) : string
{
$result = '';
if ($file == 'talents' && ($_ = $this->_get['class']))
$file .= "-".$_;
if (!Util::loadStaticFile($file, $result, true) && Cfg::get('DEBUG'))
$result .= "alert('could not fetch static data: ".$file." for locale: ".Lang::getLocale()->json()."');";
return $result . "\n\n";
}
protected static function checkSkill(string $val) : array
{
return array_intersect(array_merge(SKILLS_TRADE_PRIMARY, [SKILL_FIRST_AID, SKILL_COOKING, SKILL_FISHING]), explode(',', $val));
}
protected static function checkCallback(string $val) : bool
{
return substr($val, 0, 29) === '$WowheadProfiler.loadOnDemand';
}
}
?>

View File

@@ -1,48 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class EditImageResponse extends TextResponse
{
protected bool $requiresLogin = true;
protected array $expectedGET = array(
'qqfile' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'guide' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 1]]
);
/*
success: bool
id: image enumerator
type: 3 ? png : jpg
name: old filename
error: errString
*/
protected function generate() : void
{
if (!$this->assertGET('qqfile', 'guide'))
{
$this->result = Util::toJSON(['success' => false, 'error' => Lang::main('genericError')]);
return;
}
if (!User::canWriteGuide())
{
$this->result = Util::toJSON(['success' => false, 'error' => Lang::main('genericError')]);
return;
}
$this->result = GuideMgr::handleUpload();
if (isset($this->result['success']))
$this->result += ['name' => $this->_get['qqfile']];
$this->result = Util::toJSON($this->result);
}
}
?>

View File

@@ -1,61 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class EmotesBaseResponse extends TemplateResponse implements ICache
{
use TrListPage, TrCache;
protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected int $type = Type::EMOTE;
protected string $template = 'list-page-generic';
protected string $pageName = 'emotes';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 100];
public function __construct(string $pageParam)
{
$this->getCategoryFromUrl($pageParam);
parent::__construct($pageParam);
}
protected function generate() : void
{
$this->h1 = Util::ucFirst(Lang::game('emotes'));
/**************/
/* Page Title */
/**************/
array_unshift($this->title, $this->h1);
/****************/
/* Main Content */
/****************/
$cnd = [Cfg::get('SQL_LIMIT_NONE')];
if (!User::isInGroup(U_GROUP_STAFF))
$cnd[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
$tabData = array(
'data' => (new EmoteList($cnd))->getListviewData(),
'name' => Util::ucFirst(Lang::game('emotes'))
);
$this->lvTabs->addListviewTab(new Listview($tabData, EmoteList::$brickFile, 'emote'));
parent::generate();
}
}
?>

View File

@@ -1,129 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class EnchantmentsBaseResponse extends TemplateResponse implements ICache
{
use TrListPage, TrCache;
protected int $type = Type::ENCHANTMENT;
protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected string $template = 'enchantments';
protected string $pageName = 'enchantments';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 101];
protected array $scripts = [[SC_JS_FILE, 'js/filters.js']];
protected array $expectedGET = array(
'filter' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Filter::PATTERN_PARAM]]
);
protected array $validCats = [1, 2, 3, 4, 5, 6, 7, 8];
public function __construct(string $pageParam)
{
$this->getCategoryFromUrl($pageParam);
parent::__construct($pageParam);
if ($this->category)
$this->forward('?enchantments&filter=ty='.$this->category[0]);
$this->subCat = $pageParam !== '' ? '='.$pageParam : '';
$this->filter = new EnchantmentListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]);
if ($this->filter->shouldReload)
{
$_SESSION['error']['fi'] = $this->filter::class;
$get = $this->filter->buildGETParam();
$this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : ''));
}
$this->filterError = $this->filter->error;
}
protected function generate() : void
{
$this->h1 = Util::ucFirst(Lang::game('enchantments'));
$conditions = [];
if (!User::isInGroup(U_GROUP_EMPLOYEE))
$conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
if ($_ = $this->filter->getConditions())
$conditions[] = $_;
/**************/
/* Page Title */
/**************/
$fiForm = $this->filter->values;
array_unshift($this->title, $this->h1);
if (isset($fiForm['ty']) && count($fiForm['ty']) == 1 && $fiForm['ty'][0] > ENCHANTMENT_TYPE_NONE && $fiForm['ty'][0] <= ENCHANTMENT_TYPE_PRISMATIC_SOCKET)
array_unshift($this->title, Lang::enchantment('types', $fiForm['ty'][0]));
/*************/
/* Menu Path */
/*************/
if (isset($fiForm['ty']) && count($fiForm['ty']) == 1)
$this->breadcrumb[] = $fiForm['ty'][0];
/****************/
/* Main Content */
/****************/
$this->redButtons[BUTTON_WOWHEAD] = false;
$tabData = array(
'data' => [],
'name' => Util::ucFirst(Lang::game('enchantments'))
);
$ench = new EnchantmentList($conditions, ['calcTotal' => true]);
$tabData['data'] = $ench->getListviewData();
$this->extendGlobalData($ench->getJSGlobals());
$xCols = [];
foreach (Stat::getFilterCriteriumIdFor() as $idx => $fiId)
if (array_filter(array_column($tabData['data'], Stat::getJsonString($idx))))
$xCols[] = $fiId;
// some kind of declaration conflict going on here..., expects colId for WEAPON_DAMAGE_MAX but jsonString is WEAPON_DAMAGE
if (array_filter(array_column($tabData['data'], 'dmg')))
$xCols[] = Stat::getFilterCriteriumId(Stat::WEAPON_DAMAGE_MAX);
if ($xCols)
$this->filter->fiExtraCols = array_merge($this->filter->fiExtraCols, $xCols);
if ($this->filter->fiExtraCols)
$tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
if (array_filter(array_column($tabData['data'], 'spells')))
$tabData['visibleCols'] = ['trigger'];
if (!$ench->hasSetFields('skillLine'))
$tabData['hiddenCols'] = ['skill'];
if ($ench->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
{
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_enchantmentsfound', $ench->getMatches(), Cfg::get('SQL_LIMIT_DEFAULT'));
$tabData['_truncated'] = 1;
}
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
$this->lvTabs->addListviewTab(new Listview($tabData, EnchantmentList::$brickFile, 'enchantment'));
parent::generate();
}
}
?>

View File

@@ -1,344 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class EventBaseResponse extends TemplateResponse implements ICache
{
use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'detail-page-generic';
protected string $pageName = 'event';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 11];
public int $type = Type::WORLDEVENT;
public int $typeId = 0;
public array $dates = [];
private WorldEventList $subject;
public function __construct(string $id)
{
parent::__construct($id);
$this->typeId = intVal($id);
$this->contribute = Type::getClassAttrib($this->type, 'contribute') ?? CONTRIBUTE_NONE;
}
protected function generate() : void
{
$this->subject = new WorldEventList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->generateNotFound(Lang::game('event'), Lang::event('notFound'));
$this->h1 = $this->subject->getField('name', true);
$this->dates = array(
'firstDate' => $this->subject->getField('startTime'),
'lastDate' => $this->subject->getField('endTime'),
'length' => $this->subject->getField('length'),
'rec' => $this->subject->getField('occurence')
);
$this->gPageInfo += array(
'type' => $this->type,
'typeId' => $this->typeId,
'name' => $this->h1
);
$_holidayId = $this->subject->getField('holidayId');
/*************/
/* Menu Path */
/*************/
$this->breadcrumb[] = match ($this->subject->getField('scheduleType'))
{
-1 => 1,
0, 1 => 2,
2 => 3,
'' => 0,
default => 0
};
/**************/
/* Page Title */
/**************/
array_unshift($this->title, $this->h1, Util::ucWords(Lang::game('event')));
/***********/
/* Infobox */
/***********/
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
// boss
if ($_ = $this->subject->getField('bossCreature'))
{
$this->extendGlobalIds(Type::NPC, $_);
$infobox[] = Lang::npc('rank', 3).Lang::main('colon').'[npc='.$_.']';
}
// id
$infobox[] = Lang::event('id') . $this->typeId;
// display holiday id to staff
if ($_holidayId && User::isInGroup(U_GROUP_STAFF))
$infobox[] = 'Holiday ID'.Lang::main('colon').$_holidayId;
// original name
if (Lang::getLocale() != Locale::EN)
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
if ($infobox)
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
/****************/
/* Main Content */
/****************/
// no entry in ?_articles? use default HolidayDescription
if ($_holidayId && empty($this->article))
$this->article = new Markup($this->subject->getField('description', true), ['dbpage' => true]);
if ($_holidayId)
$this->wowheadLink = sprintf(WOWHEAD_LINK, Lang::getLocale()->domain(), 'event=', $_holidayId);
$this->headIcons = [$this->subject->getField('iconString')];
$this->redButtons = array(
BUTTON_WOWHEAD => $_holidayId > 0,
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId]
);
parent::generate();
/**************/
/* Extra Tabs */
/**************/
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
// tab: npcs
if ($npcIds = DB::World()->selectCol('SELECT `id` AS ARRAY_KEY, IF(ec.`eventEntry` > 0, 1, 0) AS "added" FROM creature c, game_event_creature ec WHERE ec.`guid` = c.`guid` AND ABS(ec.`eventEntry`) = ?d', $this->typeId))
{
$creatures = new CreatureList(array(['id', array_keys($npcIds)]));
if (!$creatures->error)
{
$data = $creatures->getListviewData();
foreach ($data as &$d)
$d['method'] = $npcIds[$d['id']];
$tabData = ['data' => $data];
if ($_holidayId && CreatureListFilter::getCriteriaIndex(38, $_holidayId))
$tabData['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=38;crs='.$_holidayId.';crv=0');
$this->result->addDataLoader('zones'); // req. by secondary tooltip in this tab
$this->lvTabs->addListviewTab(new Listview($tabData, CreatureList::$brickFile));
}
}
// tab: objects
if ($objectIds = DB::World()->selectCol('SELECT `id` AS ARRAY_KEY, IF(eg.`eventEntry` > 0, 1, 0) AS "added" FROM gameobject g, game_event_gameobject eg WHERE eg.`guid` = g.`guid` AND ABS(eg.`eventEntry`) = ?d', $this->typeId))
{
$objects = new GameObjectList(array(['id', array_keys($objectIds)]));
if (!$objects->error)
{
$data = $objects->getListviewData();
foreach ($data as &$d)
$d['method'] = $objectIds[$d['id']];
$tabData = ['data' => $data];
if ($_holidayId && GameObjectListFilter::getCriteriaIndex(16, $_holidayId))
$tabData['note'] = sprintf(Util::$filterResultString, '?objects&filter=cr=16;crs='.$_holidayId.';crv=0');
$this->result->addDataLoader('zones'); // req. by secondary tooltip in this tab
$this->lvTabs->addListviewTab(new Listview($tabData, GameObjectList::$brickFile));
}
}
// tab: achievements
if ($_ = $this->subject->getField('achievementCatOrId'))
{
$condition = $_ > 0 ? [['category', $_]] : [['id', -$_]];
$acvs = new AchievementList($condition);
if (!$acvs->error)
{
$this->extendGlobalData($acvs->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
$tabData = array(
'data' => $acvs->getListviewData(),
'visibleCols' => ['category']
);
if ($_holidayId && AchievementListFilter::getCriteriaIndex(11, $_holidayId))
$tabData['note'] = sprintf(Util::$filterResultString, '?achievements&filter=cr=11;crs='.$_holidayId.';crv=0');
$this->lvTabs->addListviewTab(new Listview($tabData, AchievementList::$brickFile));
}
}
$itemCnd = [];
if ($_holidayId)
{
$itemCnd = array(
'OR',
['eventId', $this->typeId], // direct requirement on item
);
// tab: quests (by table, go & creature)
$quests = new QuestList(array(['eventId', $this->typeId]));
if (!$quests->error)
{
$this->extendGlobalData($quests->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
$tabData = ['data'=> $quests->getListviewData()];
if (QuestListFilter::getCriteriaIndex(33, $_holidayId))
$tabData['note'] = sprintf(Util::$filterResultString, '?quests&filter=cr=33;crs='.$_holidayId.';crv=0');
$this->lvTabs->addListviewTab(new Listview($tabData, QuestList::$brickFile));
$questItems = [];
foreach (array_column($quests->rewards, Type::ITEM) as $arr)
$questItems = array_merge($questItems, array_keys($arr));
foreach (array_column($quests->choices, Type::ITEM) as $arr)
$questItems = array_merge($questItems, array_keys($arr));
foreach (array_column($quests->requires, Type::ITEM) as $arr)
$questItems = array_merge($questItems, $arr);
if ($questItems)
$itemCnd[] = ['id', $questItems];
}
}
// items from creature
if ($npcIds && !$creatures->error)
{
// vendor
$cIds = $creatures->getFoundIDs();
if ($sells = DB::World()->selectCol(
'SELECT `item` FROM npc_vendor nv WHERE `entry` IN (?a) UNION
SELECT nv1.`item` FROM npc_vendor nv1 JOIN npc_vendor nv2 ON -nv1.`entry` = nv2.`item` WHERE nv2.`entry` IN (?a) UNION
SELECT `item` FROM game_event_npc_vendor genv JOIN creature c ON genv.`guid` = c.`guid` WHERE c.`id` IN (?a)',
$cIds, $cIds, $cIds
))
$itemCnd[] = ['id', $sells];
}
// tab: items
// not checking for loot ... cant distinguish between eventLoot and fillerCrapLoot
if ($itemCnd)
{
$eventItems = new ItemList($itemCnd);
if (!$eventItems->error)
{
$this->extendGlobalData($eventItems->getJSGlobals(GLOBALINFO_SELF));
$tabData = ['data'=> $eventItems->getListviewData()];
if ($_holidayId && ItemListFilter::getCriteriaIndex(160, $_holidayId))
$tabData['note'] = sprintf(Util::$filterResultString, '?items&filter=cr=160;crs='.$_holidayId.';crv=0');
$this->lvTabs->addListviewTab(new Listview($tabData, ItemList::$brickFile));
}
}
// tab: see also (event conditions)
if ($rel = DB::World()->selectCol('SELECT IF(`eventEntry` = `prerequisite_event`, NULL, IF(`eventEntry` = ?d, `prerequisite_event`, -`eventEntry`)) FROM game_event_prerequisite WHERE `prerequisite_event` = ?d OR `eventEntry` = ?d', $this->typeId, $this->typeId, $this->typeId))
{
if (array_filter($rel, fn($x) => $x === null))
trigger_error('game_event_prerequisite: this event has itself as prerequisite', E_USER_WARNING);
if ($seeAlso = array_filter($rel, fn($x) => $x > 0))
{
$relEvents = new WorldEventList(array(['id', $seeAlso]));
$this->extendGlobalData($relEvents->getJSGlobals());
$relData = $relEvents->getListviewData();
foreach ($relEvents->getFoundIDs() as $id)
Conditions::extendListviewRow($relData[$id], Conditions::SRC_NONE, $this->typeId, [-Conditions::ACTIVE_EVENT, $this->typeId]);
$this->extendGlobalData($this->subject->getJSGlobals());
$d = $this->subject->getListviewData();
foreach ($rel as $r)
if ($r > 0)
if (Conditions::extendListviewRow($d[$this->typeId], Conditions::SRC_NONE, $this->typeId, [-Conditions::ACTIVE_EVENT, $r]))
$this->extendGlobalIds(Type::WORLDEVENT, $r);
$tabData = array(
'data' => array_merge($relData, $d),
'id' => 'see-also',
'name' => '$LANG.tab_seealso',
'hiddenCols' => ['date'],
'extraCols' => ['$Listview.extraCols.condition']
);
$this->lvTabs->addListviewTab(new Listview($tabData, WorldEventList::$brickFile));
}
}
// tab: condition for
$cnd = new Conditions();
$cnd->getByCondition(Type::WORLDEVENT, $this->typeId)->prepare();
if ($tab = $cnd->toListviewTab('condition-for', '$LANG.tab_condition_for'))
{
$this->extendGlobalData($cnd->getJsGlobals());
$this->lvTabs->addDataTab(...$tab);
}
$this->result->registerDisplayHook('lvTabs', [self::class, 'tabsHook']);
$this->result->registerDisplayHook('infobox', [self::class, 'infoboxHook']);
}
// update dates to now()
public static function tabsHook(Template\PageTemplate &$pt, Tabs &$lvTabs) : void
{
foreach ($lvTabs->iterate() as &$listview)
if (is_object($listview) && $listview?->getTemplate() == 'holiday')
WorldEventList::updateListview($listview);
}
/* finalize infobox */
public static function infoboxHook(Template\PageTemplate &$pt, ?InfoboxMarkup &$markup) : void
{
WorldEventList::updateDates($pt->dates, $start, $end, $rec);
$infobox = [];
// start
if ($start)
$infobox[] = Lang::event('start').date(Lang::main('dateFmtLong'), $start);
// end
if ($end)
$infobox[] = Lang::event('end').date(Lang::main('dateFmtLong'), $end);
// interval
if ($rec > 0)
$infobox[] = Lang::event('interval').Util::formatTime($rec * 1000);
// in progress
if ($start < time() && $end > time())
$infobox[] = '[span class=q2]'.Lang::event('inProgress').'[/span]';
if ($infobox && !$markup)
$markup = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
else if ($markup)
foreach ($infobox as $ib)
$markup->addItem($ib);
}
}
?>

View File

@@ -1,79 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class EventPowerResponse extends TextResponse implements ICache
{
use TrCache, TrTooltip;
private const /* string */ POWER_TEMPLATE = '$WowheadPower.registerHoliday(%d, %d, %s);';
protected int $type = Type::WORLDEVENT;
protected int $typeId = 0;
protected int $cacheType = CACHE_TYPE_TOOLTIP;
protected array $expectedGET = array(
'domain' => ['filter' => FILTER_CALLBACK, 'options' => [Locale::class, 'tryFromDomain']]
);
private array $dates = [];
public function __construct(string $id)
{
parent::__construct($id);
// temp locale
if ($this->_get['domain'])
Lang::load($this->_get['domain']);
$this->typeId = intVal($id);
}
protected function generate() : void
{
$worldevent = new WorldEventList(array(['id', $this->typeId]));
if ($worldevent->error)
$this->cacheType = CACHE_TYPE_NONE;
else
{
$icon = $worldevent->getField('iconString');
if ($icon == 'trade_engineering')
$icon = null;
$opts = array(
'name' => $worldevent->getField('name', true),
'tooltip' => $worldevent->renderTooltip(),
'icon' => $icon
);
$this->dates = array(
'firstDate' => $worldevent->getField('startTime'),
'lastDate' => $worldevent->getField('endTime'),
'length' => $worldevent->getField('length'),
'rec' => $worldevent->getField('occurence')
);
$this->setOnCacheLoaded([self::class, 'onBeforeDisplay'], $this->dates);
}
$this->result = new Tooltip(self::POWER_TEMPLATE, $this->typeId, $opts ?? []);
}
public static function onBeforeDisplay(string $tooltip, array $dates) : string
{
// update dates to now()
WorldEventList::updateDates($dates, $start, $end);
return sprintf(
$tooltip,
$start ? date(Lang::main('dateFmtLong'), $start) : null,
$end ? date(Lang::main('dateFmtLong'), $end) : null
);
}
}
?>

View File

@@ -1,96 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class EventsBaseResponse extends TemplateResponse implements ICache
{
use TrListPage, TrCache;
protected int $type = Type::WORLDEVENT;
protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected string $template = 'list-page-generic';
protected string $pageName = 'events';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 11];
protected array $validCats = [0, 1, 2, 3];
public function __construct(string $pageParam)
{
$this->getCategoryFromUrl($pageParam);
parent::__construct($pageParam);
}
protected function generate() : void
{
$this->h1 = Util::ucWords(Lang::game('events'));
/**************/
/* Page Title */
/**************/
array_unshift($this->title, $this->h1);
if ($this->category)
array_unshift($this->title, Lang::event('category')[$this->category[0]]);
/*************/
/* Menu Path */
/*************/
if ($this->category)
$this->breadcrumb[] = $this->category[0];
/****************/
/* Main Content */
/****************/
$this->redButtons[BUTTON_WOWHEAD] = true;
$condition = [];
if (!User::isInGroup(U_GROUP_EMPLOYEE))
$condition[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
if ($this->category)
$condition[] = match ($this->category[0])
{
1 => ['h.scheduleType', -1],
2 => ['h.scheduleType', [0, 1]],
3 => ['h.scheduleType', 2],
default => ['e.holidayId', 0] // also cat 0
};
$events = new WorldEventList($condition);
$this->extendGlobalData($events->getJSGlobals());
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"]);
$this->lvTabs->addListviewTab(new Listview(['data' => $events->getListviewData()], WorldEventList::$brickFile));
if ($_ = array_filter($events->getListviewData(), fn($x) => $x['category'] > 0))
$this->lvTabs->addListviewTab(new Listview(['data' => $_, 'hideCount' => 1], 'calendar'));
parent::generate();
$this->result->registerDisplayHook('lvTabs', [self::class, 'tabsHook']);
}
// recalculate dates with now()
public static function tabsHook(Template\PageTemplate &$pt, Tabs &$lvTabs) : void
{
foreach ($lvTabs->iterate() as &$listview)
if (is_object($listview) && ($listview?->getTemplate() == 'holiday' || $listview?->getTemplate() == 'holidaycal'))
WorldEventList::updateListview($listview);
}
}
?>

View File

@@ -1,361 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class FactionBaseResponse extends TemplateResponse implements ICache
{
use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'detail-page-generic';
protected string $pageName = 'faction';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 7];
public int $type = Type::FACTION;
public int $typeId = 0;
private FactionList $subject;
public function __construct(string $id)
{
parent::__construct($id);
$this->typeId = intVal($id);
$this->contribute = Type::getClassAttrib($this->type, 'contribute') ?? CONTRIBUTE_NONE;
}
protected function generate() : void
{
$this->subject = new FactionList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->generateNotFound(Lang::game('faction'), Lang::faction('notFound'));
$this->h1 = $this->subject->getField('name', true);
$this->gPageInfo += array(
'type' => $this->type,
'typeId' => $this->typeId,
'name' => $this->h1
);
/*************/
/* Menu Path */
/*************/
array_unshift($this->title, $this->h1, Util::ucFirst(Lang::game('faction')));
/**************/
/* Page Title */
/**************/
if ($foo = $this->subject->getField('cat'))
{
if ($bar = $this->subject->getField('cat2'))
$this->breadcrumb[] = $bar;
$this->breadcrumb[] = $foo;
}
/***********/
/* Infobox */
/***********/
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
// Quartermaster if any
if ($ids = $this->subject->getField('qmNpcIds'))
{
$this->extendGlobalIds(Type::NPC, ...$ids);
$qmStr = Lang::faction('quartermaster');
if (count($ids) == 1)
$qmStr .= '[npc='.$ids[0].']';
else if (count($ids) > 1)
{
$qmStr .= '[ul]';
foreach ($ids as $id)
$qmStr .= '[li][npc='.$id.'][/li]';
$qmStr .= '[/ul]';
}
$infobox[] = $qmStr;
}
// side if any
if ($_ = $this->subject->getField('side'))
$infobox[] = Lang::main('side').'[span class=icon-'.($_ == SIDE_ALLIANCE ? 'alliance' : 'horde').']'.Lang::game('si', $_).'[/span]';
// id
$infobox[] = Lang::faction('id') . $this->typeId;
// profiler relateed (note that this is part of the cache. I don't think this is important enough to calc for every view)
if (Cfg::get('PROFILER_ENABLE') && !($this->subject->getField('cuFlags') & CUSTOM_EXCLUDE_FOR_LISTVIEW))
{
$x = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_completion_reputation WHERE `exalted` = 1 AND `factionId` = ?d', $this->typeId);
$y = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_profiles WHERE `custom` = 0 AND `stub` = 0');
$infobox[] = Lang::profiler('attainedBy', [round(($x ?: 0) * 100 / ($y ?: 1))]);
// completion row added by InfoboxMarkup
}
// original name
if (Lang::getLocale() != Locale::EN)
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
if ($infobox) // unsure if this should be tracked (needs data dump in User::getCompletion())
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0', 0);
/****************/
/* Main Content */
/****************/
$this->redButtons = array(
BUTTON_WOWHEAD => true,
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId]
);
// Spillover Effects
/* todo (low): also check on reputation_spillover_template (but its data is identical to calculation below
$rst = DB::World()->selectRow('SELECT
CONCAT_WS(" ", faction1, faction2, faction3, faction4) AS faction,
CONCAT_WS(" ", rate_1, rate_2, rate_3, rate_4) AS rate,
CONCAT_WS(" ", rank_1, rank_2, rank_3, rank_4) AS rank
FROM reputation_spillover_template WHERE faction = ?d', $this->typeId);
*/
$conditions = array(
['id', $this->typeId, '!'], // not self
['repIdx', -1, '!'] // only gainable
);
if ($p = $this->subject->getField('parentFactionId')) // linked via parent
$conditions[] = ['OR', ['id', $p], ['parentFactionId', $p]];
else // self as parent
$conditions[] = ['parentFactionId', $this->typeId];
$spillover = new FactionList($conditions);
$this->extendGlobalData($spillover->getJSGlobals());
$buff = '';
foreach ($spillover->iterate() as $spillId => $__)
if ($val = ($spillover->getField('spilloverRateIn') * $this->subject->getField('spilloverRateOut') * 100))
$buff .= '[tr][td][faction='.$spillId.'][/td][td][span class=q'.($val > 0 ? '2]+' : '10]').$val.'%[/span][/td][td]'.Lang::game('rep', $spillover->getField('spilloverMaxRank')).'[/td][/tr]';
if ($buff)
$this->extraText = new Markup(
'[h3 class=clear]'.Lang::faction('spillover').'[/h3][div margin=15px]'.Lang::faction('spilloverDesc').'[/div][table class=grid width=400px][tr][td width=150px][b]'.Util::ucFirst(Lang::game('faction')).'[/b][/td][td width=100px][b]'.Lang::spell('_value').'[/b][/td][td width=150px][b]'.Lang::faction('maxStanding').'[/b][/td][/tr]'.$buff.'[/table]',
['dbpage' => true, 'allow' => Markup::CLASS_ADMIN],
'text-generic'
);
// reward rates (ultimately this should be calculated into each reward display)
if ($rates = DB::World()->selectRow('SELECT `quest_rate`, `quest_daily_rate`, `quest_weekly_rate`, `quest_monthly_rate`, `quest_repeatable_rate`, `creature_rate`, `spell_rate` FROM reputation_reward_rate WHERE `faction` = ?d', $this->typeId))
{
$buff = '';
foreach ($rates as $k => $v)
{
if ($v == 1)
continue;
$head = match ($k)
{
'quest_rate' => Lang::game('quests'),
'quest_daily_rate' => Lang::game('quests').' ('.Lang::quest('daily').')',
'quest_weekly_rate' => Lang::game('quests').' ('.Lang::quest('weekly').')',
'quest_monthly_rate' => Lang::game('quests').' ('.Lang::quest('monthly').')',
'quest_repeatable_rate' => Lang::game('quests').' ('.Lang::quest('repeatable').')',
'creature_rate' => Lang::game('npcs'),
'spell_rate' => Lang::game('spells')
};
$buff .= '[tr][td]'.$head.Lang::main('colon').'[/td][td width=35px align=right][span class=q'.($v < 1 ? '10]' : '2]+').intVal(($v - 1) * 100).'%[/span][/td][/tr]';
}
if ($buff && $this->extraText)
$this->extraText->append('[h3 class=clear]'.Lang::faction('customRewRate').'[/h3][table class=grid width=250px]'.$buff.'[/table]');
else if ($buff)
$this->extraText = new Markup('[h3 class=clear]'.Lang::faction('customRewRate').'[/h3][table class=grid width=250px]'.$buff.'[/table]', ['dbpage' => true, 'allow' => Markup::CLASS_ADMIN], 'text-generic');
}
// factionchange-equivalent
if ($pendant = DB::World()->selectCell('SELECT IF(`horde_id` = ?d, `alliance_id`, -`horde_id`) FROM player_factionchange_reputations WHERE `alliance_id` = ?d OR `horde_id` = ?d', $this->typeId, $this->typeId, $this->typeId))
{
$altFac = new FactionList(array(['id', abs($pendant)]));
if (!$altFac->error)
{
$this->transfer = Lang::faction('_transfer', array(
$altFac->id,
$altFac->getField('name', true),
$pendant > 0 ? 'alliance' : 'horde',
$pendant > 0 ? Lang::game('si', SIDE_ALLIANCE) : Lang::game('si', SIDE_HORDE)
));
}
}
/**************/
/* Extra Tabs */
/**************/
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
// tab: items
$items = new ItemList(array(['requiredFaction', $this->typeId]), ['calcTotal' => true]);
if (!$items->error)
{
$this->extendGlobalData($items->getJSGlobals(GLOBALINFO_SELF));
$tabData = array(
'data' => $items->getListviewData(),
'extraCols' => '$_',
'sort' => ['standing', 'name']
);
if ($items->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
if (!is_null(ItemListFilter::getCriteriaIndex(17, $this->typeId)))
$tabData['note'] = sprintf(Util::$filterResultString, '?items&filter=cr=17;crs='.$this->typeId.';crv=0');
$this->lvTabs->addListviewTab(new Listview($tabData, ItemList::$brickFile, 'itemStandingCol'));
}
// tab: creatures with onKill reputation
// only if you can actually gain reputation by kills
if ($this->subject->getField('reputationIndex') != -1)
{
// inherit siblings/children from $spillover
$cRep = DB::World()->selectCol('SELECT DISTINCT `creature_id` AS ARRAY_KEY, `qty` FROM (
SELECT `creature_id`, `RewOnKillRepValue1` as "qty" FROM creature_onkill_reputation WHERE `RewOnKillRepValue1` > 0 AND (`RewOnKillRepFaction1` = ?d { OR (`RewOnKillRepFaction1` IN (?a) AND `IsTeamAward1` <> 0) } ) UNION
SELECT `creature_id`, `RewOnKillRepValue2` as "qty" FROM creature_onkill_reputation WHERE `RewOnKillRepValue2` > 0 AND (`RewOnKillRepFaction2` = ?d { OR (`RewOnKillRepFaction2` IN (?a) AND `IsTeamAward2` <> 0) } )
) x',
$this->typeId, $spillover->getFoundIDs() ?: DBSIMPLE_SKIP,
$this->typeId, $spillover->getFoundIDs() ?: DBSIMPLE_SKIP
);
if ($cRep)
{
$killCreatures = new CreatureList(array(['id', array_keys($cRep)]), ['calcTotal' => true]);
if (!$killCreatures->error)
{
$data = $killCreatures->getListviewData();
foreach ($data as $id => &$d)
$d['reputation'] = $cRep[$id];
$tabData = array(
'data' => $data,
'extraCols' => '$_',
'sort' => ['-reputation', 'name']
);
if ($killCreatures->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
if (!is_null(CreatureListFilter::getCriteriaIndex(42, $this->typeId)))
$tabData['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=42;crs='.$this->typeId.';crv=0');
$this->addDataLoader('zones');
$this->lvTabs->addListviewTab(new Listview($tabData, CreatureList::$brickFile, 'npcRepCol'));
}
}
}
// tab: members
if ($_ = $this->subject->getField('templateIds'))
{
$members = new CreatureList(array(['faction', $_]), ['calcTotal' => true]);
if (!$members->error)
{
$tabData = array(
'data' => $members->getListviewData(),
'id' => 'member',
'name' => '$LANG.tab_members'
);
if ($members->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
if (!is_null(CreatureListFilter::getCriteriaIndex(3, $this->typeId)))
$tabData['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=3;crs='.$this->typeId.';crv=0');
$this->addDataLoader('zones');
$this->lvTabs->addListviewTab(new Listview($tabData, CreatureList::$brickFile));
}
}
// tab: objects
if ($_ = $this->subject->getField('templateIds'))
{
$objects = new GameObjectList(array(['faction', $_]));
if (!$objects->error)
{
$this->addDataLoader('zones');
$this->lvTabs->addListviewTab(new Listview(['data' => $objects->getListviewData()], GameObjectList::$brickFile));
}
}
// tab: quests
$conditions = array(
['AND', ['rewardFactionId1', $this->typeId], ['rewardFactionValue1', 0, '>']],
['AND', ['rewardFactionId2', $this->typeId], ['rewardFactionValue2', 0, '>']],
['AND', ['rewardFactionId3', $this->typeId], ['rewardFactionValue3', 0, '>']],
['AND', ['rewardFactionId4', $this->typeId], ['rewardFactionValue4', 0, '>']],
['AND', ['rewardFactionId5', $this->typeId], ['rewardFactionValue5', 0, '>']],
'OR'
);
$quests = new QuestList($conditions, ['calcTotal' => true]);
if (!$quests->error)
{
$this->extendGlobalData($quests->getJSGlobals(GLOBALINFO_ANY));
$tabData = array(
'data' => $quests->getListviewData($this->typeId),
'extraCols' => '$_'
);
if ($quests->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT'))
if (!is_null(QuestListFilter::getCriteriaIndex(1, $this->typeId)))
$tabData['note'] = sprintf(Util::$filterResultString, '?quests&filter=cr=1;crs='.$this->typeId.';crv=0');
$this->lvTabs->addListviewTab(new Listview($tabData, QuestList::$brickFile, 'questRepCol'));
}
// tab: achievements
$conditions = array(
['ac.type', ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION],
['ac.value1', $this->typeId]
);
$acvs = new AchievementList($conditions);
if (!$acvs->error)
{
$this->extendGlobalData($acvs->getJSGlobals(GLOBALINFO_ANY));
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $acvs->getListviewData(),
'id' => 'criteria-of',
'name' => '$LANG.tab_criteriaof',
'visibleCols' => ['category']
), AchievementList::$brickFile));
}
// tab: condition-for
$cnd = new Conditions();
$cnd->getByCondition(Type::FACTION, $this->typeId)->prepare();
if ($tab = $cnd->toListviewTab('condition-for', '$LANG.tab_condition_for'))
{
$this->extendGlobalData($cnd->getJsGlobals());
$this->lvTabs->addDataTab(...$tab);
}
parent::generate();
}
}
?>

Some files were not shown because too many files have changed in this diff Show More