Compare commits

..

No commits in common. "master" and "v1.2" have entirely different histories.
master ... v1.2

1078 changed files with 91059 additions and 121871 deletions

10
.gitattributes vendored
View file

@ -1,14 +1,4 @@
* text=input
*.php text eol=lf
*.js text eol=lf
*.css text eol=lf
*.sql text eol=lf
aowow text eol=lf
prQueue text eol=lf
*.png binary
*.jpg binary
*.gif binary
*.ttf binary
*.swf binary

20
.gitignore vendored
View file

@ -1,19 +1,27 @@
# Git
*.orig
# cache
/cache/*
/cache/template/*
/setup/generated/alphaMaps/*.png
/cache/firstrun
# extract from MPQ
/setup/mpqdata/*
# 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
/static/widgets/searchbox/searchbox.html
/static/download/searchplugins/aowow.xml
/config/*
/config/config.php
/datasets/*
!/datasets/zones
# /datasets/item-scaling
# extracted sounds
/static/wowsounds/*
@ -23,7 +31,7 @@
/static/images/wow/icons/medium/*
/static/images/wow/icons/small/*
/static/images/wow/icons/tiny/*
!/static/images/wow/icons/tiny/quest_[end|start]
!/static/images/wow/icons/tiny/quest_*
/static/images/wow/hunterpettalents/*
/static/images/wow/Interface/*
/static/images/wow/loadingscreens/*
@ -42,8 +50,4 @@
/static/uploads/screenshots/*
/static/uploads/signatures/*
/static/uploads/temp/*
/static/uploads/guide/images/*
# composer
/includes/libs/*
composer.phar

View file

@ -26,10 +26,8 @@ AddDefaultCharset utf8
</IfModule>
# UHD screenshots can get pretty large (cannot be set in config)
<IfModule mod_php.c>
php_value upload_max_filesize 20M
php_value post_max_size 25M
</IfModule>
RewriteEngine on
# RewriteBase /~user/localPath/ # enable if the rules do not work, when they should

View file

@ -2,7 +2,7 @@
## Build Status
![fuck it ship it](https://forthebadge.com/badges/fuck-it-ship-it.svg)
![fuck it ship it](http://forthebadge.com/images/badges/fuck-it-ship-it.svg)
## Introduction
@ -13,22 +13,20 @@ While the first releases can be found as early as 2008, today it is impossible t
This is a complete rewrite of the serverside php code and update to the clientside javascripts from 2008 to something 2013ish.
I myself take no credit for the clientside scripting, design and layout that these php-scripts cater to.
Also, this project is not meant to be used for commercial purposes of any kind!
Also, this project is not meant to be used for commercial puposes of any kind!
## Requirements
+ Webserver running PHP ≥ 8.2 including extensions:
+ Webserver running PHP ≥ 7.4 — 8.0 including extensions:
+ [SimpleXML](https://www.php.net/manual/en/book.simplexml.php)
+ [GD](https://www.php.net/manual/en/book.image)
+ [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
+ [Composer](https://getcomposer.org/download/)
+ [TDB 335.25101](https://github.com/TrinityCore/TrinityCore/releases/tag/TDB335.25101) including updates up to [TrinityCore/TrinityCore@f3b691d](https://github.com/TrinityCore/TrinityCore/commit/f3b691dcb085014ec3f0e2c60ab94fc9c00e8aa8) (no other other providers are supported at this time)
+ MySQL ≥ 5.5.30
+ [TDB 335.21101](https://github.com/TrinityCore/TrinityCore/releases/tag/TDB335.21101)
+ WIN: php.exe needs to be added to the `PATH` system variable, if it isn't already.
+ Tools require cmake: Please refer to the individual repositories for detailed information
+ [MPQExtractor](https://github.com/Sarjuuk/MPQExtractor) / [FFmpeg](https://ffmpeg.org/download.html) / (optional: [BLPConverter](https://github.com/Sarjuuk/BLPConverter))
@ -39,9 +37,9 @@ audio processing may require [lame](https://sourceforge.net/projects/lame/files/
#### Highly Recommended
+ setting the following configuration values on your TrinityCore server (and running it once) will greatly increase the accuracy of spawn points
+ setting the following configuration values on your TrinityCore server will greatly increase the accuracy of spawn points
> Calculate.Creature.Zone.Area.Data = 1
> Calculate.Gameobject.Zone.Area.Data = 1
> Calculate.Gameoject.Zone.Area.Data = 1
## Install
@ -51,10 +49,8 @@ audio processing may require [lame](https://sourceforge.net/projects/lame/files/
`git clone git@github.com:Sarjuuk/MPQExtractor.git MPQExtractor`
#### 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 and optionally auth and characters databases you are going to reference.
Import files 01 - 03 from `setup/sql/` in order into the AoWoW database `mysql --default-character-set=utf8 -p {your-db-here} < setup/sql/01-db_structure.sql`, etc.
**Optional**: If you are using MySQL ≥ 8.4.0 and want to support fulltext search for locale zhCN, additionally import `setup/sql/04-db_optional_mysql_only.sql`. Enables this in settings after AoWoW has been set up.
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 `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
@ -76,26 +72,26 @@ Extract the following directories from the client archives into `setup/mpqdata/`
.. once is enough (still apply the localeCode though):
> \<localeCode>/Interface/TalentFrame/
> \<localeCode>/Interface/Glues/Credits/
> \<localeCode>/Interface/Icons/
> \<localeCode>/Interface/Spellbook/
> \<localeCode>/Interface/PaperDoll/
> \<localeCode>/Interface/Glues/CharacterCreate/
> \<localeCode>/Interface/GLUES/CHARACTERCREATE/
> \<localeCode>/Interface/Pictures
> \<localeCode>/Interface/PvPRankBadges
> \<localeCode>/Interface/FlavorImages
> \<localeCode>/Interface/Calendar/Holidays/
> \<localeCode>/Sound/
.. optionaly (not used in AoWoW):
> \<localeCode>/Interface/GLUES/LOADINGSCREENS/
#### 5. Reencode the audio files
WAV-files need to be reencoded as `ogg/vorbis` and some MP3s may identify themselves as `application/octet-stream` instead of `audio/mpeg`.
* [example for WIN](https://gist.github.com/Sarjuuk/d77b203f7b71d191509afddabad5fc9f)
* [example for \*nix](https://gist.github.com/Sarjuuk/1f05ef2affe49a7e7ca0fad7b01c081d)
#### 6. Install dependencies with composer
`php composer.phar install --no-dev` on a project level composer install, or
`composer install --no-dev` on a system level composer install
#### 7. Run the initial setup from the CLI
#### 6. Run the initial setup from the CLI
`php aowow --setup`.
This should guide you through with minimal input required from your end, but will take some time though, especially compiling the zone-images. Use it to familiarize yourself with the other functions this setup has. Yes, I'm dead serious: *Go read the code!* It will help you understand how to configure AoWoW and keep it in sync with your world database.
When you've created your admin account you are done.
@ -104,24 +100,24 @@ When you've created your admin account you are done.
## Troubleshooting
Q: The Page appears white, without any styles.
A: The static content is not being displayed. You are either using SSL and AoWoW is unable to detect it or STATIC_HOST is not defined properly. Either way this can be fixed via config `php aowow --configure`
A: The static content is not being displayed. You are either using SSL and AoWoW is unable to detect it or STATIC_HOST is not defined poperly. Either way this can be fixed via config `php aowow --siteconfig`
Q: Fatal error: Can't inherit abstract function \<functionName> (previously declared abstract in \<className>) in \<path>
A: You are using multiple cache optimization modules for php that are in conflict with each other. (Zend OPcache, XCache, ..) Disable all but one.
A: You are using cache optimization modules for php, that are in confict with each other. (Zend OPcache, XCache, ..) Disable all but one.
Q: Some generated images appear distorted or have alpha-channel issues.
A: Image compression is beyond my understanding, so i am unable to fix these issues within the blpReader.
BUT you can convert the affected blp file into a png file in the same directory, using the provided BLPConverter.
AoWoW will prioritize png files over blp files.
AoWoW will priorize png files over blp files.
Q: How can i get the modelviewer to work?
A: You can't anymore. Wowhead switched from Flash to WebGL (as they should) and moved or deleted the old files in the process.
Q: I'm getting random javascript errors!
A: Some server configurations or external services (like Cloudflare) come with modules, that automatically minify js and css files. Sometimes they break in the process. Disable the module in this case.
A: Some server configurations or external services (like Cloudflare) come with modules, that automaticly minify js and css files. Sometimes they break in the process. Disable the module in this case.
Q: Some search results within the profiler act rather strange. How does it work?
A: Whenever you try to view a new character, AoWoW needs to fetch it first. Since the data is structured for the needs of TrinityCore and not for easy viewing, AoWoW needs to save and restructure it locally. To this end, every char request is placed in a queue. While the queue is not empty, a single instance of `prQueue` is run in the background as not to overwhelm the characters database with requests. This also means complex search queries can't be run against the characters database and have to use the incomplete/outdated cached profiles of AoWoW.
A: Whenever you try to view a new character, AoWoW needs to fetch it first. Since the data is structured for the needs of TrinityCore and not for easy viewing, AoWoW needs to save and restructure it locally. To this end, every char request is placed in a queue. While the queue is not empty, a single instance of `prQueue` is run in the background as not to overwhelm the characters database with requests. This also means, some more exotic search queries can't be run agains the characters database and have to use the incomplete/outdated cached profiles of AoWoW.
Q: Screenshot upload fails, because the file size is too large and/or the subdirectories are visible from the web!
A: That's a web server configuration issue. If you are using Apache you may need to [enable the use of .htaccess](http://httpd.apache.org/docs/2.4/de/mod/core.html#allowoverride). Other servers require individual configuration.
@ -138,7 +134,7 @@ A: A search is only conducted against the currently used locale. You may have on
## Special Thanks
Said website with the red smiling rocket, for providing this beautiful website!
Please do not regard this project as blatant rip-off, rather as "We do really liked your presentation, but since time and content progresses, you are sadly no longer supplying the data we need".
Said website with the red smiling rocket, for providing this beautifull website!
Please do not reagard this project as blatant rip-off, rather as "We do really liked your presentation, but since time and content progresses, you are sadly no longer supplying the data we need".
![uses badges](https://forthebadge.com/badges/uses-badges.svg)
![uses badges](http://forthebadge.com/images/badges/uses-badges.svg)

20
aowow
View file

@ -1,18 +1,12 @@
#!/usr/bin/env php
<?php
if (PHP_SAPI !== 'cli')
require 'includes/shared.php';
if (!CLI)
die("this script must be run from CLI\n");
if (PHP_SAPI === 'cli' && getcwd().DIRECTORY_SEPARATOR.'aowow' != __FILE__)
die("this script must be run from the aowow root directory\n");
require_once 'includes/kernel.php';
require_once 'includes/setup/cli.class.php';
require_once 'includes/setup/timer.class.php';
require_once 'includes/setup/datatypes/primitives.php';
require_once 'includes/setup/files/binaryfile.class.php';
require_once 'includes/setup/files/dbcfile.class.php';
require_once 'setup/setup.php';
if (CLI && getcwd().DIRECTORY_SEPARATOR.'aowow' != __FILE__)
die("this script must be run from root directory\n");
else
require 'setup/setup.php';
?>

View file

@ -1,30 +0,0 @@
{
"name": "aowow/aowow",
"description": "Server and client database visualization for World of Warcraft/TrinityCore v3.3.5a, including community tools.",
"version": "2.0",
"type": "project",
"keywords": ["World of Warcraft", "wow", "database", "db", "frontend", "Wrath of the Lich King", "wotlk", "335a", "3.3.5a"],
"authors": [
{
"name": "Sarjuuk",
"role": "Developer"
}
],
"config": {
"vendor-dir": "includes/libs"
},
"require": {
"dibi/dibi": "^5.1",
"php": "8.2 - 8.4",
"ext-mbstring": "*",
"ext-simplexml": "*",
"ext-gd": "*",
"ext-mysqli": "*",
"ext-fileinfo": "*",
"ext-intl": "*"
},
"require-dev": {
"jfcherng/php-diff": "6.16",
"triggerhappy/mpq": "dev-master"
}
}

454
composer.lock generated
View file

@ -1,454 +0,0 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "25b289a987a7600746f0fd1f2491864b",
"packages": [
{
"name": "dibi/dibi",
"version": "v5.1.0",
"source": {
"type": "git",
"url": "https://github.com/dg/dibi.git",
"reference": "32b6976209859f61eb79380c5a8904ea33db47df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dg/dibi/zipball/32b6976209859f61eb79380c5a8904ea33db47df",
"reference": "32b6976209859f61eb79380c5a8904ea33db47df",
"shasum": ""
},
"require": {
"php": "8.2 - 8.5"
},
"replace": {
"dg/dibi": "*"
},
"require-dev": {
"jetbrains/phpstorm-attributes": "^1.0",
"nette/di": "^3.1",
"nette/tester": "^2.5",
"phpstan/phpstan-nette": "^2.0@stable",
"tracy/tracy": "^2.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.1-dev"
}
},
"autoload": {
"psr-4": {
"Dibi\\": "src/Dibi"
},
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause",
"GPL-2.0-only",
"GPL-3.0-only"
],
"authors": [
{
"name": "David Grudl",
"homepage": "https://davidgrudl.com"
}
],
"description": "Dibi is Database Abstraction Library for PHP",
"homepage": "https://dibiphp.com",
"keywords": [
"access",
"database",
"dbal",
"mssql",
"mysql",
"odbc",
"oracle",
"pdo",
"postgresql",
"sqlite",
"sqlsrv"
],
"support": {
"issues": "https://github.com/dg/dibi/issues",
"source": "https://github.com/dg/dibi/tree/v5.1.0"
},
"time": "2025-08-06T22:26:19+00:00"
}
],
"packages-dev": [
{
"name": "chdemko/sorted-collections",
"version": "1.0.10",
"source": {
"type": "git",
"url": "https://github.com/chdemko/php-sorted-collections.git",
"reference": "d9cf7021e6fda1eb68b9f35caf99215327f6db76"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chdemko/php-sorted-collections/zipball/d9cf7021e6fda1eb68b9f35caf99215327f6db76",
"reference": "d9cf7021e6fda1eb68b9f35caf99215327f6db76",
"shasum": ""
},
"require": {
"php": ">=8.2"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.7",
"phpbench/phpbench": "^1.3",
"phpunit/phpunit": "^11.3",
"squizlabs/php_codesniffer": "^3.10"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"chdemko\\SortedCollection\\": "src/SortedCollection"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Christophe Demko",
"email": "chdemko@gmail.com",
"homepage": "https://chdemko.pagelab.univ-lr.fr/",
"role": "Developer"
}
],
"description": "Sorted Collections for PHP >= 8.1",
"homepage": "https://php-sorted-collections.readthedocs.io/en/latest/?badge=latest",
"keywords": [
"avl",
"collection",
"iterator",
"map",
"ordered",
"set",
"sorted",
"tree",
"treemap",
"treeset"
],
"support": {
"issues": "https://github.com/chdemko/php-sorted-collections/issues",
"source": "https://github.com/chdemko/php-sorted-collections/tree/1.0.10"
},
"time": "2024-08-04T14:31:40+00:00"
},
{
"name": "jfcherng/php-color-output",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/jfcherng/php-color-output.git",
"reference": "6c7bf16686cc6a291647fcb87491640a2d5edd20"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jfcherng/php-color-output/zipball/6c7bf16686cc6a291647fcb87491640a2d5edd20",
"reference": "6c7bf16686cc6a291647fcb87491640a2d5edd20",
"shasum": ""
},
"require": {
"php": ">=7.1.3"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.19",
"liip/rmt": "^1.6",
"phan/phan": "^2 || ^3 || ^4",
"phpunit/phpunit": ">=7 <10",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Jfcherng\\Utility\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jack Cherng",
"email": "jfcherng@gmail.com"
}
],
"description": "Make your PHP command-line application colorful.",
"keywords": [
"ansi-colors",
"color",
"command-line",
"str-color"
],
"support": {
"issues": "https://github.com/jfcherng/php-color-output/issues",
"source": "https://github.com/jfcherng/php-color-output/tree/3.0.0"
},
"funding": [
{
"url": "https://www.paypal.me/jfcherng/5usd",
"type": "custom"
}
],
"time": "2021-05-27T02:45:54+00:00"
},
{
"name": "jfcherng/php-diff",
"version": "6.16.0",
"source": {
"type": "git",
"url": "https://github.com/jfcherng/php-diff.git",
"reference": "8b49edeba6e367df22977fca0f0324b4a99b78a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jfcherng/php-diff/zipball/8b49edeba6e367df22977fca0f0324b4a99b78a0",
"reference": "8b49edeba6e367df22977fca0f0324b4a99b78a0",
"shasum": ""
},
"require": {
"jfcherng/php-color-output": "^3",
"jfcherng/php-mb-string": "^1.4.6 || ^2",
"jfcherng/php-sequence-matcher": "^3.2.10 || ^4",
"php": ">=7.4"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.8",
"liip/rmt": "^1.6",
"phan/phan": "^5",
"phpunit/phpunit": "^9",
"squizlabs/php_codesniffer": "^3.6"
},
"type": "library",
"autoload": {
"psr-4": {
"Jfcherng\\Diff\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jack Cherng",
"email": "jfcherng@gmail.com"
},
{
"name": "Chris Boulton",
"email": "chris.boulton@interspire.com"
}
],
"description": "A comprehensive library for generating differences between two strings in multiple formats (unified, side by side HTML etc).",
"keywords": [
"diff",
"udiff",
"unidiff",
"unified diff"
],
"support": {
"issues": "https://github.com/jfcherng/php-diff/issues",
"source": "https://github.com/jfcherng/php-diff/tree/6.16.0"
},
"funding": [
{
"url": "https://www.paypal.me/jfcherng/5usd",
"type": "custom"
}
],
"time": "2024-03-05T08:44:05+00:00"
},
{
"name": "jfcherng/php-mb-string",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/jfcherng/php-mb-string.git",
"reference": "8407bfefde47849c9e7c9594e6de2ac85a0f845d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jfcherng/php-mb-string/zipball/8407bfefde47849c9e7c9594e6de2ac85a0f845d",
"reference": "8407bfefde47849c9e7c9594e6de2ac85a0f845d",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"php": ">=8.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3",
"phan/phan": "^5",
"phpunit/phpunit": "^9 || ^10"
},
"suggest": {
"ext-iconv": "Either \"ext-iconv\" or \"ext-mbstring\" is requried.",
"ext-mbstring": "Either \"ext-iconv\" or \"ext-mbstring\" is requried."
},
"type": "library",
"autoload": {
"psr-4": {
"Jfcherng\\Utility\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jack Cherng",
"email": "jfcherng@gmail.com"
}
],
"description": "A high performance multibytes sting implementation for frequently reading/writing operations.",
"support": {
"issues": "https://github.com/jfcherng/php-mb-string/issues",
"source": "https://github.com/jfcherng/php-mb-string/tree/2.0.1"
},
"funding": [
{
"url": "https://www.paypal.me/jfcherng/5usd",
"type": "custom"
}
],
"time": "2023-04-17T14:23:16+00:00"
},
{
"name": "jfcherng/php-sequence-matcher",
"version": "4.0.3",
"source": {
"type": "git",
"url": "https://github.com/jfcherng/php-sequence-matcher.git",
"reference": "d2038ac29627340a7458609072a8ba355e80ec5b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jfcherng/php-sequence-matcher/zipball/d2038ac29627340a7458609072a8ba355e80ec5b",
"reference": "d2038ac29627340a7458609072a8ba355e80ec5b",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3",
"phan/phan": "^5",
"phpunit/phpunit": "^9 || ^10",
"squizlabs/php_codesniffer": "^3.7"
},
"type": "library",
"autoload": {
"psr-4": {
"Jfcherng\\Diff\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jack Cherng",
"email": "jfcherng@gmail.com"
},
{
"name": "Chris Boulton",
"email": "chris.boulton@interspire.com"
}
],
"description": "A longest sequence matcher. The logic is primarily based on the Python difflib package.",
"support": {
"issues": "https://github.com/jfcherng/php-sequence-matcher/issues",
"source": "https://github.com/jfcherng/php-sequence-matcher/tree/4.0.3"
},
"funding": [
{
"url": "https://www.paypal.me/jfcherng/5usd",
"type": "custom"
}
],
"time": "2023-05-21T07:57:08+00:00"
},
{
"name": "triggerhappy/mpq",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/cipherxof/PHP-MPQ.git",
"reference": "628ca77b307d1cdf28b76da9750f3c8cbe958f49"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cipherxof/PHP-MPQ/zipball/628ca77b307d1cdf28b76da9750f3c8cbe958f49",
"reference": "628ca77b307d1cdf28b76da9750f3c8cbe958f49",
"shasum": ""
},
"require": {
"chdemko/sorted-collections": "1.0.*@dev",
"php": ">=5.4"
},
"require-dev": {
"phpunit/phpunit": "5.2.*"
},
"default-branch": true,
"type": "project",
"autoload": {
"psr-4": {
"TriggerHappy\\MPQ\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-3.0-only"
],
"description": "Handle the MPQ (MoPaQ) format natively from PHP with support for Warcraft III & Starcraft II.",
"keywords": [
"MPQ",
"archive",
"mopaq",
"php-mpq",
"phpmpq",
"triggerhappy"
],
"support": {
"issues": "https://github.com/cipherxof/PHP-MPQ/issues",
"source": "https://github.com/cipherxof/PHP-MPQ/tree/master"
},
"time": "2018-07-31T04:22:01+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"triggerhappy/mpq": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "8.2 - 8.4",
"ext-mbstring": "*",
"ext-simplexml": "*",
"ext-gd": "*",
"ext-mysqli": "*",
"ext-fileinfo": "*",
"ext-intl": "*"
},
"platform-dev": {},
"plugin-api-version": "2.9.0"
}

View file

@ -4,16 +4,13 @@ if (!defined('AOWOW_REVISION'))
die('illegal access');
function extAuth(string &$usernameOrEmail, #[\SensitiveParameter] string $password, int &$userId = 0, int &$userGroup = -1) : int
function extAuth($user, $pass, &$userId = 0, &$userGroup = -1)
{
/*
insert some auth mechanism here
set usernameOrEmail to a valid username, do not pass back the email if used for login
set userId to uid from external auth provider for identification
(optional) set userGroup to a valid userGroup (see U_GROUP_* defines)
return an AUTH_* result (see defines)
see defines for usable return values
set userId for identification
*/
return AUTH_INTERNAL_ERR;

46
datasets/zones Normal file
View file

@ -0,0 +1,46 @@
Mapper.multiLevelZones = {
206: ['206-1', '206-2', '206-3'],
209: ['209-1', '209-2', '209-3', '209-4', '209-5', '209-6', '209-7'],
616: ['616-1', '616_1', '616_2'],
719: ['719-1', '719-2', '719-3'],
721: ['721-1', '721-2', '721-3', '721-4'],
796: ['796-1', '796-2', '796-3', '796-4'],
1196: ['1196-1', '1196-2'],
1337: ['1337-1', '1337-2'],
1581: ['1581-1', '1581-2'],
1583: ['1583-1', '1583-2', '1583-3', '1583-4', '1583-5', '1583-6', '1583-7'],
1584: ['1584-1', '1584-2'],
2017: ['2017-1', '2017-2'],
2057: ['2057-1', '2057-2', '2057-3', '2057-4'],
2100: ['2100-1', '2100-2'],
2557: ['2557-1', '2557-2', '2557-3', '2557-4', '2557-5', '2557-6'],
2677: ['2677-1', '2677-2', '2677-3', '2677-4'],
3959: ['3959', '3959-1', '3959-2', '3959-3', '3959-4', '3959-5', '3959-6', '3959-7'],
3428: ['3428-1', '3428-2', '3428-3'],
3456: ['3456-1', '3456-2', '3456-3', '3456-4', '3456-5', '3456-6'],
3457: ['3457-1', '3457-2', '3457-3', '3457-4', '3457-5', '3457-6', '3457-7', '3457-8', '3457-9', '3457-10', '3457-11', '3457-12', '3457-13', '3457-14', '3457-15', '3457-16', '3457-17'],
3477: ['3477-1', '3477-2', '3477-3'],
3715: ['3715-1', '3715-2'],
3790: ['3790-1', '3790-2'],
3791: ['3791-1', '3791-2'],
3848: ['3848-1', '3848-2', '3848-3'],
3849: ['3849-1', '3849-2'],
4075: ['4075', '4075-1'],
4100: ['4100-1', '4100-2'],
4131: ['4131-1', '4131-2'],
4196: ['4196-1', '4196-2'],
4228: ['4228-1', '4228-2', '4228-3', '4228-4'],
4272: ['4272-1', '4272-2'],
4273: ['4273-0', '4273-1', '4273-2', '4273-3', '4273-4', '4273-5'],
4277: ['4277-1', '4277-2', '4277-3'],
4395: ['4395-1', '4395-2'],
4494: ['4494-1', '4494-2'],
4714: ['4714-1', '4714_1', '4714_2', '4714_3'],
4722: ['4722-1', '4722-2'],
4812: ['4812-1', '4812-2', '4812-3', '4812-4', '4812-5', '4812-6', '4812-7', '4812-8'],
};
/*
var g_zone_areas = {};
in locale files
*/

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 $rawParam)
{
parent::__construct($rawParam);
if ($rawParam)
$this->generateError();
}
protected function generate() : void
{
$this->h1 = Lang::main('moreTitles', $this->pageName);
array_unshift($this->title, $this->h1);
parent::generate();
}
}
?>

View file

@ -1,174 +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($rawParam)
{
if (!User::isLoggedIn())
$this->forwardToSignIn('account');
parent::__construct($rawParam);
}
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` = %i', User::$id);
Lang::sort('game', 'ra');
parent::generate();
/*************/
/* Ban Popup */
/*************/
$b = DB::Aowow()->selectAssoc(
'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` = %i AND ab.`typeMask` & %i 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` = %s AND `userId` = %i', '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 = DateTime::formatTimeElapsedFloat(Cfg::get('ACC_RENAME_DECAY') * 1000);
if ($user['renameCooldown'] > time())
{
$locCode = substr_replace(Lang::getLocale()->json(), '_', 2, 0); // ._.
$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'];
/* PREMIUM */
$this->premium = User::isPremium();
if (!$this->premium)
return;
// required by js to calc reputation border color in user selection
$this->reputation = User::getReputation();
// status [reviewing, ok, rejected]? (only 2: rejected processed in js)
// * 'when': uploaded timestamp expected as msec for some reason
// * 'caption': only used for getVisibleText, duplicates name?
// * 'type': always 1 ?, Dialog-popup doesn't work without it
if ($cuAvatars = DB::Aowow()->selectAssoc('SELECT `id` AS ARRAY_KEY, `id`, `name`, `name` AS "caption", `current`, `size`, `status`, `when` * 1000 AS "when", 1 AS "type" FROM ::account_avatars WHERE `userId` = %i', User::$id))
{
foreach ($cuAvatars as $id => $a)
if ($a['status'] != AvatarMgr::STATUS_REJECTED)
$this->customicons[$id] = $a['name'];
if ($id = array_find_key($cuAvatars, fn($x) => $x['current'] > 0 ))
$this->customicon = $id;
}
// 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 %in AND `token` = %s', [ACC_STATUS_NONE, ACC_STATUS_NEW], $this->_get['key']))
{
// don't remove the token yet. It's needed on signin page.
DB::Aowow()->qry('UPDATE ::account SET `status` = %i, `statusTimer` = 0, `userGroups` = %i WHERE `token` = %s', ACC_STATUS_NONE, U_GROUP_NONE, $this->_get['key']);
// fully apply block for further registration attempts from this ip
DB::Aowow()->qry('REPLACE INTO ::account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (%s, %i, %i + 1, UNIX_TIMESTAMP() + %i)',
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 $rawParam)
{
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError();
parent::__construct($rawParam);
}
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` = %i AND `statusTimer` > UNIX_TIMESTAMP() AND `token` = %s', 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` = %i AND `statusTimer` > UNIX_TIMESTAMP() AND `token` = %s', 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()->qry('UPDATE ::account SET `status` = %i, `statusTimer` = 0, `token` = "" WHERE `id` = %i', 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()->qry('DELETE FROM ::account_cookies WHERE `userId` = %i', $userId);
DB::Aowow()->qry('DELETE FROM ::account_avatars WHERE `userId` = %i', $userId);
DB::Aowow()->qry('DELETE FROM ::account_excludes WHERE `userId` = %i', $userId);
DB::Aowow()->qry('DELETE FROM ::account_favorites WHERE `userId` = %i', $userId);
DB::Aowow()->qry('DELETE FROM ::account_reputation WHERE `userId` = %i', $userId);
DB::Aowow()->qry('DELETE FROM ::account_weightscales WHERE `userId` = %i', $userId); // cascades to aowow_account_weightscale_data
// delete profiles, unlink chars
DB::Aowow()->qry('DELETE pp FROM ::profiler_profiles pp JOIN ::account_profiles ap ON ap.`profileId` = pp.`id` WHERE ap.`accountId` = %i', $userId);
// DB::Aowow()->qry('DELETE FROM ::account_profiles WHERE `accountId` = %i', $userId); // already deleted via FK?
// delete all sessions and bans
DB::Aowow()->qry('DELETE FROM ::account_banned WHERE `userId` = %i', $userId);
DB::Aowow()->qry('DELETE FROM ::account_sessions WHERE `userId` = %i', $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()->qry(
'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` = %i, `statusTimer` = 0, `token` = "", `updateValue` = "", `renameCooldown` = 0
WHERE `id` = %i',
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` = %s', $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()->qry('UPDATE ::account SET `email` = `updateValue`, `status` = %i, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = %s', 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` = %s', $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()->qry('UPDATE ::account SET `passHash` = `updateValue`, `status` = %i, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = %s', 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` = %i AND `userId` = %i', $this->_post['id'], User::$id);
if ($selected === null || $selected === false)
return;
DB::Aowow()->qry('DELETE FROM ::account_avatars WHERE `id` = %i AND `userId` = %i', $this->_post['id'], User::$id);
// if deleted avatar is also currently selected, unset
if ($selected)
DB::Aowow()->qry('UPDATE ::account SET `avatar` = 0 WHERE `id` = %i', 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 $rawParam)
{
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError();
parent::__construct($rawParam);
}
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 %in AND `statusTimer` > UNIX_TIMESTAMP() AND `id` = %i', [ACC_STATUS_NEW, ACC_STATUS_NONE, ACC_STATUS_PURGING], User::$id))
{
$token = Util::createHash(40);
DB::Aowow()->qry('UPDATE ::account SET `status` = %i, `statusTimer` = UNIX_TIMESTAMP() + %i, `token` = %s WHERE `id` = %i',
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,81 +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'] !== null) // 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` = %i AND `typeId` IN %in', $this->_post['type'], $validIds);
$insert = [];
foreach ($validIds as $typeId)
{
$insert['userId'][] = User::$id;
$insert['type'][] = $this->_post['type'];
$insert['typeId'][] = $typeId;
$insert['mode'][] = in_array($typeId, $includes) ? Profiler::COMPLETION_INCLUDE : Profiler::COMPLETION_EXCLUDE;
};
DB::Aowow()->qry('INSERT INTO ::account_excludes %m ON DUPLICATE KEY UPDATE `mode` = (`mode` ^ 0x3)', $insert);
}
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()->qry('DELETE FROM ::account_excludes WHERE `userId` = %i', User::$id);
DB::Aowow()->qry('UPDATE ::account SET `excludeGroups` = %i WHERE `id` = %i', PR_EXCLUDE_GROUP_UNAVAILABLE, User::$id);
}
private function updateGroups() : void
{
if ($this->assertPOST('groups')) // clamp to real groups
DB::Aowow()->qry('UPDATE ::account SET `excludeGroups` = %i WHERE `id` = %i', $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()->qry('DELETE FROM ::account_favorites WHERE `userId` = %i AND `type` = %i AND `typeId` = %i', 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()->qry('INSERT INTO ::account_favorites (`userId`, `type`, `typeId`) VALUES (%i, %i, %i)', 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 $rawParam)
{
// 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($rawParam);
}
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` = %s AND `type` = %i AND `count` > %i 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: '.DateTime::formatTimeElapsed($timeout * 1000).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound');
// pretend recovery started
if (!DB::Aowow()->selectCell('SELECT 1 FROM ::account WHERE `email` = %s', $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()->qry('INSERT INTO ::account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (%s, %i, %i, UNIX_TIMESTAMP() + %i) ON DUPLICATE KEY UPDATE `count` = `count` + %i, `unbanDate` = UNIX_TIMESTAMP() + %i',
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 $rawParam)
{
// 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($rawParam);
}
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` = %s AND `type` = %i AND `count` > %i 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: '.DateTime::formatTimeElapsed($timeout * 1000).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound');
// pretend recovery started
if (!DB::Aowow()->selectCell('SELECT 1 FROM ::account WHERE `email` = %s', $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()->qry('INSERT INTO ::account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (%s, %i, %i, UNIX_TIMESTAMP() + %i) ON DUPLICATE KEY UPDATE `count` = `count` + %i, `unbanDate` = UNIX_TIMESTAMP() + %i',
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()->qry('UPDATE ::account SET `avatar` = 0 WHERE `id` = %i', 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` = %s', $icon))
return Lang::account('updateMessage', 'avNotFound');
$x = DB::Aowow()->qry('UPDATE ::account SET `avatar` = 1, `wowicon` = %s WHERE `id` = %i', $icon, User::$id);
if (is_null($x))
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` = %s', $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()->qry('UPDATE ::account_avatars SET `current` = IF(`id` = %i, 1, 0) WHERE `userId` = %i AND `status` <> %i', $customIcon, User::$id, AvatarMgr::STATUS_REJECTED);
if (!is_int($x))
return Lang::main('genericError');
if (!is_int(DB::Aowow()->qry('UPDATE ::account SET `avatar` = 2 WHERE `id` = %i', 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()->qry('UPDATE ::account SET `avatarborder` = %i WHERE `id` = %i', $this->_post['avatarborder'], User::$id);
if (is_null($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()->qry('UPDATE ::account_avatars SET `name` = %s WHERE `id` = %i AND `userId` = %i', 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 $rawParam)
{
if (!Cfg::get('ACC_ALLOW_REGISTER') || Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError();
parent::__construct($rawParam);
}
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 $rawParam)
{
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($rawParam);
}
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` = %s AND `type` = %i AND `count` > %i 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: '.DateTime::formatTimeElapsed($timeout * 1000).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound');
// check email and account status
if ($token = DB::Aowow()->selectCell('SELECT `token` FROM ::account WHERE `email` = %s AND `status` = %i', $this->_post['email'], ACC_STATUS_NEW))
{
if (!Util::sendMail($this->_post['email'], 'activate-account', [$token]))
return Lang::main('intError');
DB::Aowow()->qry('INSERT INTO ::account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (%s, %i, %i, UNIX_TIMESTAMP() + %i) ON DUPLICATE KEY UPDATE `count` = `count` + %i, `unbanDate` = UNIX_TIMESTAMP() + %i',
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_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/' ]]
);
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` = %s AND `status` = %i 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` = %s AND `email` = %s AND `status` = %i 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()->qry('UPDATE ::account SET `passHash` = %s, `status` = %i WHERE `id` = %i', 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` = %s', $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()->qry('UPDATE ::account SET `status` = %i, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = %s', 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_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/'] ]
);
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 %in AND a.`token` = %s',
[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', [DateTime::formatTimeElapsedFloat(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()->qry('UPDATE ::account SET `prevIP` = IF(`curIp` = %s, `prevIP`, `curIP`), `curIP` = IF(`curIp` = %s, `curIP`, %s), `status` = IF(`status` = %i, `status`, 0), `statusTimer` = IF(`status` = %i, `statusTimer`, 0), `token` = IF(`status` = %i, `token`, "") WHERE `id` = %i',
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()->qry('DELETE FROM ::account_sessions WHERE `sessionId` = %s', $this->_get['key']);
session_regenerate_id(true); // user status changed => regenerate id
// create new session entry
DB::Aowow()->qry('INSERT INTO ::account_sessions (`userId`, `sessionId`, `created`, `expires`, `touched`, `deviceInfo`, `ip`, `status`) VALUES (%i, %s, %i, %i, %i, %s, %s, %i)',
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_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']],
'global' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet'] ]
);
public function __construct(string $rawParam)
{
// if the user not is logged in goto login page
if (!User::isLoggedIn())
$this->forwardToSignIn();
parent::__construct($rawParam);
}
protected function generate() : void
{
if ($this->_get['global'])
DB::Aowow()->qry('UPDATE ::account_sessions SET `touched` = %i, `status` = %i WHERE `userId` = %i', time(), SESSION_FORCED_LOGOUT, User::$id);
else
DB::Aowow()->qry('UPDATE ::account_sessions SET `touched` = %i, `status` = %i WHERE `sessionId` = %s', 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_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']]
);
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 $e == 1 ? Lang::account('errPassLength') : Lang::main('intError');
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` = %i AND `ip` = %s AND `count` >= %i AND `unbanDate` >= UNIX_TIMESTAMP()', IP_BAN_TYPE_REGISTRATION_ATTEMPT, User::$ip, Cfg::get('ACC_FAILED_AUTH_COUNT')))
{
DB::Aowow()->qry('UPDATE ::account_bannedips SET `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + %i WHERE `ip` = %s AND `type` = %i', Cfg::get('ACC_FAILED_AUTH_BLOCK'), User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT);
return Lang::account('inputbox', 'error', 'signupExceeded', [DateTime::formatTimeElapsedFloat(Cfg::get('ACC_FAILED_AUTH_BLOCK') * 1000)]);
}
// username / email taken
if ($inUseData = DB::Aowow()->SelectRow('SELECT `id`, `username`, `status` = %i AND `statusTimer` < UNIX_TIMESTAMP() AS "expired" FROM ::account WHERE (LOWER(`username`) = LOWER(%s) OR LOWER(`email`) = LOWER(%s))', ACC_STATUS_NEW, $this->_post['username'], $this->_post['email']))
{
if ($inUseData['expired'])
DB::Aowow()->qry('DELETE FROM ::account WHERE `id` = %i', $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()->qry('INSERT INTO ::account (`login`, `passHash`, `username`, `email`, `joindate`, `curIP`, `locale`, `userGroups`, `status`, `statusTimer`, `token`) VALUES (%s, %s, %s, %s, UNIX_TIMESTAMP(), %s, %i, %i, %i, UNIX_TIMESTAMP() + %i, %s)',
$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()->qry('INSERT INTO ::account_sessions (`userId`, `sessionId`, `created`, `expires`, `touched`, `deviceInfo`, `ip`, `status`) VALUES (%i, %s, %i, %i, %i, %s, %s, %i)',
$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()->qry('INSERT INTO ::account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (%s, %i, 1, UNIX_TIMESTAMP() + %i) ON DUPLICATE KEY UPDATE `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + %i',
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()->qry('UPDATE ::account SET `description` = %s WHERE `id` = %i', $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 $rawParam)
{
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
(new TemplateResponse())->generateError();
parent::__construct($rawParam);
}
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` = %s AND `id` <> %i', $this->_post['newemail'], User::$id))
return Lang::account('mailInUse');
$status = DB::Aowow()->selectCell('SELECT `status` FROM ::account WHERE `statusTimer` > UNIX_TIMESTAMP() AND `id` = %i', User::$id);
if ($status != ACC_STATUS_NONE && $status != ACC_STATUS_CHANGE_EMAIL)
return Lang::account('inputbox', 'error', 'isRecovering', [DateTime::formatTimeElapsedFloat(Cfg::get('ACC_RECOVERY_DECAY') * 1000)]);
$oldEmail = DB::Aowow()->selectCell('SELECT `email` FROM ::account WHERE `id` = %i', 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()->qry('UPDATE ::account SET `updateValue` = %s, `status` = %i, `statusTimer` = UNIX_TIMESTAMP() + %i, `token` = %s WHERE `id` = %i',
$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()->qry('REPLACE INTO ::account_cookies (`userId`, `name`, `data`) VALUES (%i, %s, %s)', 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()->qry('UPDATE ::account SET `debug` = %i WHERE `id` = %i', $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 $rawParam)
{
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
(new TemplateResponse())->generateError();
parent::__construct($rawParam);
}
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 $e == 1 ? Lang::account('errPassLength') : Lang::main('intError');
if ($this->_post['newPassword'] !== $this->_post['confirmPassword'])
return Lang::account('passMismatch');
$userData = DB::Aowow()->selectRow('SELECT `status`, `passHash`, `statusTimer` FROM ::account WHERE `id` = %i', User::$id);
if ($userData['status'] != ACC_STATUS_NONE && $userData['status'] != ACC_STATUS_CHANGE_PASS && $userData['statusTimer'] > time())
return Lang::account('inputbox', 'error', 'isRecovering', [DateTime::formatTimeElapsedFloat(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()->qry('UPDATE ::account SET `updateValue` = %s, `status` = %i, `statusTimer` = UNIX_TIMESTAMP() + %i, `token` = %s WHERE `id` = %i',
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` = %i', 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()->qry('UPDATE ::account_sessions SET `status` = %i, `touched` = %i WHERE `userId` = %i AND `sessionId` <> ? AND `status` = %i', 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 $rawParam)
{
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
(new TemplateResponse())->generateError();
parent::__construct($rawParam);
}
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` = %i', 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(%s)', $this->_post['newUsername']))
return Lang::account('nameInUse');
DB::Aowow()->qry('UPDATE ::account SET `username` = %s, `renameCooldown` = %i WHERE `id` = %i', $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,121 +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` = %i', User::$id);
if ($nScales >= self::MAX_SCALES)
return;
if ($id = DB::Aowow()->qry('INSERT INTO ::account_weightscales (`userId`, `name`) VALUES (%i, %s)', 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` = %i AND `id` = %i', 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()->qry('UPDATE ::account_weightscales SET `name` = %s WHERE `id` = %i', $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()->qry('DELETE FROM ::account_weightscales WHERE `id` = %i AND `userId` = %i', $this->_post['id'], User::$id);
$this->result = '';
}
private function storeScaleData(int $scaleId) : bool
{
if (!is_int(DB::Aowow()->qry('DELETE FROM ::account_weightscale_data WHERE `id` = %i', $scaleId)))
return false;
// $x['val'] is known to be a positive int due to regex check
$scaleData = array_filter($this->_post['scale'], fn($x) => Stat::getWeightJson($x['field']) && $x['val'] > 0);
array_walk($scaleData, fn(&$x) => $x['id'] = $scaleId);
foreach ($scaleData as $sd)
if (is_null(DB::Aowow()->qry('INSERT INTO ::account_weightscale_data %v', $sd)))
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) => array_combine(['field', 'val'], 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,520 +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` = %i', $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` = %i', $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` = %i, `alliance_id`, -`horde_id`) FROM player_factionchange_achievement WHERE `alliance_id` = %i OR `horde_id` = %i', $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()->selectAssoc('SELECT `criteria_id` AS ARRAY_KEY, `type` AS ARRAY_KEY2, `value1`, `value2`, `ScriptName` FROM achievement_criteria_data WHERE `type` <> %i AND `criteria_id` IN %in', 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` = %s', $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:
$extraData[] = match((int)$xData['value1'])
{
0 => Lang::maps('EasternKingdoms'),
1 => Lang::maps('Kalimdor'),
530 => Lang::maps('Outland'),
571 => Lang::maps('Northrend'),
default => (function(int $mapId) {
$z = new ZoneList(array(['mapId', $mapId]));
return '<a href="?zone='.$z->id.'">'.$z->getField('name', true).'</a>';
})($xData['value1'])
};
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` = %i AND `value1` = %i',
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` = %i', $_);
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` = %i', $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,170 +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 $rawParam)
{
$this->getCategoryFromUrl($rawParam);
parent::__construct($rawParam);
if ($this->category)
$this->subCat = '='.implode('.', $this->category);
$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 = [Listview::DEFAULT_SIZE];
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 = [Listview::DEFAULT_SIZE];
if ($fiCnd)
$conditions[] = $fiCnd;
if ($catList = DB::Aowow()->SelectCol('SELECT `id` FROM ::achievementcategory WHERE `parentCat` IN %in OR `parentCat2` IN %in ', $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() > Listview::DEFAULT_SIZE)
{
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_achievementsfound', $acvList->getMatches(), Listview::DEFAULT_SIZE);
$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` = %i', $this->_get['id']))
{
trigger_error('AdminAnnouncementsResponse::updateStatus - announcement does not exist');
return;
}
DB::Aowow()->qry('UPDATE ::announcements SET `status` = %i WHERE `id` = %i', $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()->qry('UPDATE ::comments SET `flags` = %i, `deleteUserId` = %i, `deleteDate` = %i WHERE `id` = %i', 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()->qry('UPDATE ::comments SET `flags` = `flags` & ~%i WHERE `id` = %i', 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` = %i', $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()->qry('UPDATE ::guides SET `status` = %i, `rev` = (SELECT `rev` FROM ::articles WHERE `type` = %i AND `typeId` = %i ORDER BY `rev` DESC LIMIT 1), `approveUserId` = %i, `approveDate` = %i WHERE `id` = %i', $status, Type::GUIDE, $id, User::$id, time(), $id);
else
$ok = DB::Aowow()->qry('UPDATE ::guides SET `status` = %i WHERE `id` = %i', $status, $id);
if (!$ok)
return false;
DB::Aowow()->qry('INSERT INTO ::guides_changelog (`id`, `date`, `userId`, `status`) VALUES (%i, %i, %i, %i)', $id, time(), User::$id, $status);
if ($msg)
DB::Aowow()->qry('INSERT INTO ::guides_changelog (`id`, `date`, `userId`, `msg`) VALUES (%i, %i, %i, %s)', $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` = %i AND `typeId` IN %in 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(%s)', $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,60 +0,0 @@
<?php
namespace Aowow;
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()->selectAssoc('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId` FROM ::screenshots WHERE (`status` & %i) = 0 AND `id` IN %in', 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()->qry('UPDATE ::screenshots SET `status` = %i, `userIdApprove` = %i WHERE `id` = %i', 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()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $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` & %i AND `id` = %i', CC_FLAG_DELETED, $id))
{
DB::Aowow()->qry('DELETE FROM ::screenshots WHERE `id` = %i', $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 %in GROUP BY `type`', $this->_get['id']);
DB::Aowow()->qry('UPDATE ::screenshots SET `status` = %i, `userIdDelete` = %i WHERE `id` IN %in', 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`) & %i, 1, 0) AS "hasMore" FROM ::screenshots WHERE `type` = %i AND `typeId` IN %in GROUP BY `typeId` HAVING `hasMore` = 0', CC_FLAG_APPROVED, $type, $typeIds);
if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable')))
DB::Aowow()->qry('UPDATE %n SET cuFlags = cuFlags & ~%i WHERE id IN %in', $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()->qry('UPDATE ::screenshots SET `caption` = %s WHERE `id` = %i',
$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(%s)', $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` = %i', $this->_get['id']));
$typeId = $this->_get['typeid'];
if (Type::validateIds($type, $typeId))
{
$tbl = Type::getClassAttrib($type, 'dataTable');
// move screenshot
DB::Aowow()->qry('UPDATE ::screenshots SET `typeId` = %i WHERE `id` = %i', $typeId, $this->_get['id']);
// flag target as having screenshot
DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $tbl, CUSTOM_HAS_SCREENSHOT, $typeId);
// deflag source for having had screenshots (maybe)
$ssInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~`status`) & %i, 1, 0) AS "hasMore" FROM ::screenshots WHERE `status`& %i AND `type` = %i AND `typeId` = %i', CC_FLAG_DELETED, CC_FLAG_APPROVED, $type, $oldTypeId);
if ($ssInfo || !$ssInfo['hasMore'])
DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` & ~%i WHERE `id` = %i', $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()->selectAssoc('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId`, `status` FROM ::screenshots WHERE (`status` & %i) = 0 AND `id` IN %in', 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()->qry('UPDATE ::screenshots SET `status` = %i, `userIdApprove` = %i WHERE `id` = %i', 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()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $tbl, CUSTOM_HAS_SCREENSHOT, $ssData['typeId']);
}
// reset all others
DB::Aowow()->qry('UPDATE ::screenshots a, ::screenshots b SET a.`status` = a.`status` & ~%i WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND a.`id` <> b.`id` AND b.`id` = %i', CC_FLAG_STICKY, $id);
// toggle sticky status
DB::Aowow()->qry('UPDATE ::screenshots SET `status` = IF(`status` & %i, `status` & ~%i, `status` | %i) WHERE `id` = %i AND `status` & %i', 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,105 +0,0 @@
<?php
namespace Aowow;
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()->qry('REPLACE INTO ::spawns_override (`type`, `typeGuid`, `areaId`, `floor`, `revision`) VALUES (%i, %i, %i, %i, %i)', $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)
{
if ($swp = DB::World()->selectAssoc('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` = %i AND ca.`path_id` <> 0', $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()->qry('UPDATE ::creature_waypoints SET %a WHERE `creatureOrPath` = %i AND `point` = %i', $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` = %i', $guid));
}
if (DB::Aowow()->qry('UPDATE ::spawns SET %a WHERE `type` = %i AND `guid` IN %in', $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(%s)', $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,44 +0,0 @@
<?php
namespace Aowow;
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()->selectAssoc('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId` FROM ::videos WHERE (`status` & %i) = 0 AND `id` IN %in', CC_FLAG_APPROVED, $this->_get['id']);
foreach ($viEntries as $id => $viData)
{
// set as approved in DB
DB::Aowow()->qry('UPDATE ::videos SET `status` = %i, `userIdApprove` = %i WHERE `id` = %i', 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()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $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` & %i AND `id` IN %in', 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 %in GROUP BY `type`', $this->_get['id']);
DB::Aowow()->qry('UPDATE ::videos SET `status` = %i, `userIdDelete` = %i WHERE (`status` & %i) = 0 AND `id` IN %in', 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`) & %i, 1, 0) AS "hasMore" FROM ::videos WHERE `type` = %i AND `typeId` IN %in GROUP BY `typeId` HAVING `hasMore` = 0', CC_FLAG_APPROVED, $type, $typeIds);
if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable')))
DB::Aowow()->qry('UPDATE %n SET cuFlags = cuFlags & ~%i WHERE id IN %in', $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()->qry('UPDATE ::videos SET `caption` = %s WHERE `id` = %i', $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(%s)', $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` & %i) = 0 AND b.`id` = %i 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()->qry('UPDATE ::videos SET `pos` = %i WHERE `id` = %i', $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` = %i', $id));
$typeId = $this->_get['typeid'];
if (Type::validateIds($type, $typeId))
{
$tbl = Type::getClassAttrib($type, 'dataTable');
// move video
DB::Aowow()->qry('UPDATE ::videos SET `typeId` = %i WHERE `id` = %i', $typeId, $id);
// flag target as having video
DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $tbl, CUSTOM_HAS_VIDEO, $typeId);
// deflag source for having had videos (maybe)
$viInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~`status`) & %i, 1, 0) AS "hasMore" FROM ::videos WHERE `status`& %i AND `type` = %i AND `typeId` = %i', CC_FLAG_DELETED, CC_FLAG_APPROVED, $type, $oldTypeId);
if ($viInfo || !$viInfo['hasMore'])
DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` & ~%i WHERE `id` = %i', $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()->selectAssoc('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId`, `status` FROM ::videos WHERE (`status` & %i) = 0 AND `id` IN %in', 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()->qry('UPDATE ::videos SET `status` = %i, `userIdApprove` = %i WHERE `id` = %i', 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()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $tbl, CUSTOM_HAS_VIDEO, $viData['typeId']);
}
// reset all others
DB::Aowow()->qry('UPDATE ::videos a, ::videos b SET a.`status` = a.`status` & ~%i WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND a.`id` <> b.`id` AND b.`id` = %i', CC_FLAG_STICKY, $id);
// toggle sticky status
DB::Aowow()->qry('UPDATE ::videos SET `status` = IF(`status` & %i, `status` & ~%i, `status` | %i) WHERE `id` = %i AND `status` & %i', 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()->selectAssoc('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()->qry('DELETE FROM ::account_weightscale_data WHERE `id` = %i', $this->_post['id']);
DB::Aowow()->qry('UPDATE ::account_weightscales SET `icon`= %s WHERE `id` = %i', $this->_post['__icon'], $this->_post['id']);
foreach (explode(',', $this->_post['scale']) as $s)
{
[$k, $v] = explode(':', $s);
if (!Stat::getWeightJson($k) || $v < 1)
continue;
if (DB::Aowow()->qry('INSERT INTO ::account_weightscale_data VALUES (%i, %s, %i)', $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->getBySource(Conditions::SRC_AREATRIGGER_CLIENT, entry: $this->typeId)->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 $rawParam)
{
$this->getCategoryFromUrl($rawParam);
if (isset($this->category[0]))
$this->forward('?areatriggers&filter=ty='.$this->category[0]);
parent::__construct($rawParam);
$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 = [Listview::DEFAULT_SIZE];
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() > Listview::DEFAULT_SIZE)
{
$tabData['note'] = sprintf(Util::$tryFilteringEntityString, $trigger->getMatches(), '"'.Lang::game('areatriggers').'"', Listview::DEFAULT_SIZE);
$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,148 +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` = %i AND `nameUrl` = %s', $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)->selectAssoc('SELECT at.`arenaTeamId` AS "realmGUID", at.`name`, at.`type` FROM arena_team at WHERE at.`name` = %s', $this->subjectName);
if ($subject = array_find($subjects ?: [], fn($x) => Util::lower($x['name']) === Util::lower($this->subjectName)))
{
$subject['realm'] = $this->realmId;
$subject['stub'] = 1;
$subject['nameUrl'] = Profiler::urlize($subject['name']);
// create entry from realm with basic info
DB::Aowow()->qry('INSERT IGNORE INTO ::profiler_arena_team %v', $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 $rawParam)
{
parent::__construct($rawParam);
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()->selectAssoc('SELECT `realm`, `realmGUID` FROM ::profiler_arena_team WHERE `id` IN %in', $this->_get['id']))
foreach ($teams as $t)
Profiler::scheduleResync(Type::ARENA_TEAM, $t['realm'], $t['realmGUID']);
if ($this->_get['profile'])
if ($chars = DB::Aowow()->selectAssoc('SELECT `realm`, `realmGUID` FROM ::profiler_profiles p JOIN ::profiler_arena_team_member atm ON atm.`profileId` = p.`id` WHERE atm.`arenaTeamId` IN %in', $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 $rawParam)
{
parent::__construct($rawParam);
if (!Cfg::get('PROFILER_ENABLE'))
$this->generate404();
}
protected function generate() : void
{
$this->result = Profiler::resyncStatus(Type::ARENA_TEAM, $this->_get['id']);
}
}
?>

View file

@ -1,160 +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 $rawParam)
{
if (!Cfg::get('PROFILER_ENABLE'))
$this->generateError();
$this->getSubjectFromUrl($rawParam);
parent::__construct($rawParam);
$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;
}
if ($this->category)
$this->subCat = '='.implode('.', $this->category);
$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 = [Listview::DEFAULT_SIZE];
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() > Listview::DEFAULT_SIZE)
{
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound2', $this->sumSubjects, $teams->getMatches());
$tabData['_truncated'] = 1;
}
else if ($teams->getMatches() > Listview::DEFAULT_SIZE)
$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,304 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class ClassBaseResponse extends TemplateResponse implements ICache
{
use TrDetailPage, TrCache;
private const TC_CLASS_IDS = [null, 8, 3, 1, 5, 4, 9, 6, 2, 7, null, 0]; // see TalentCalc.js
protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'detail-page-generic';
protected string $pageName = 'class';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 12];
public int $type = Type::CHR_CLASS;
public int $typeId = 0;
public ?string $expansion = null;
private CharClassList $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 CharClassList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->generateNotFound(Lang::game('class'), Lang::chrClass('notFound'));
$this->h1 = $this->subject->getField('name', true);
$this->gPageInfo += array(
'type' => $this->type,
'typeId' => $this->typeId,
'name' => $this->h1
);
/*************/
/* Menu Path */
/*************/
$this->breadcrumb[] = $this->typeId;
/**************/
/* Page Title */
/**************/
array_unshift($this->title, $this->h1, Util::ucFirst(Lang::game('class')));
/***********/
/* Infobox */
/***********/
$cl = ChrClass::from($this->typeId);
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
// hero class
if ($this->subject->getField('flags') & 0x40)
$infobox[] = '[tooltip=tooltip_heroclass]'.Lang::game('heroClass').'[/tooltip]';
// resource
if ($cl == ChrClass::DRUID) // special Druid case
$infobox[] = Lang::game('resources').
'[tooltip name=powertype1]'.Lang::game('st', 0).', '.Lang::game('st', 31).', '.Lang::game('st', 2).'[/tooltip][span class=tip tooltip=powertype1]'.Util::ucFirst(Lang::spell('powerTypes', POWER_MANA)).'[/span], '.
'[tooltip name=powertype2]'.Lang::game('st', 5).', '.Lang::game('st', 8).'[/tooltip][span class=tip tooltip=powertype2]'.Util::ucFirst(Lang::spell('powerTypes', POWER_RAGE)).'[/span], '.
'[tooltip name=powertype8]'.Lang::game('st', 1).'[/tooltip][span class=tip tooltip=powertype8]'.Util::ucFirst(Lang::spell('powerTypes', POWER_ENERGY)).'[/span]';
else if ($cl == ChrClass::DEATHKNIGHT) // special DK case
$infobox[] = Lang::game('resources').'[span]'.Util::ucFirst(Lang::spell('powerTypes', POWER_RUNE)).', '.Util::ucFirst(Lang::spell('powerTypes', $this->subject->getField('powerType'))).'[/span]';
else // regular case
$infobox[] = Lang::game('resource').'[span]'.Util::ucFirst(Lang::spell('powerTypes', $this->subject->getField('powerType'))).'[/span]';
// roles
$roles = [];
for ($i = 0; $i < 4; $i++)
if ($this->subject->getField('roles') & (1 << $i))
$roles[] = (count($roles) == 2 ? "[br]" : '').Lang::game('_roles', $i);
if ($roles)
$infobox[] = (count($roles) > 1 ? Lang::game('roles') : Lang::game('role')).implode(', ', $roles);
// specs
$specList = [];
$skills = new SkillList(array(['id', $this->subject->getField('skills')]));
foreach ($skills->iterate() as $k => $__)
$specList[$k] = '[icon name='.$skills->getField('iconString').'][url=?spells=7.'.$this->typeId.'.'.$k.']'.$skills->getField('name', true).'[/url][/icon]';
if ($specList)
$infobox[] = Lang::game('specs').'[ul][li]'.implode('[/li][li]', $specList).'[/li][/ul]';
// id
$infobox[] = Lang::chrClass('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, $_);
}
// 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 */
/****************/
$this->expansion = Util::$expansionString[$this->subject->getField('expansion')];
$this->redButtons = array(
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
BUTTON_WOWHEAD => true,
BUTTON_TALENT => ['href' => '?talent#'.Util::$tcEncoding[self::TC_CLASS_IDS[$this->typeId] * 3], 'pet' => false],
BUTTON_FORUM => false // todo (low): Cfg::get('BOARD_URL') + X
);
if ($_ = $this->subject->getField('iconString'))
$this->headIcons[] = $_;
/**************/
/* Extra Tabs */
/**************/
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
// tab: spells (grouped)
// '$LANG.tab_armorproficiencies',
// '$LANG.tab_weaponskills',
// '$LANG.tab_glyphs',
// '$LANG.tab_abilities',
// '$LANG.tab_talents',
$conditions = array(
['s.typeCat', [-13, -11, -2, 7]],
[['s.cuFlags', (SPELL_CU_TRIGGERED | CUSTOM_EXCLUDE_FOR_LISTVIEW), '&'], 0],
[
DB::OR,
// Glyphs, Proficiencies
['s.reqClassMask', $cl->toMask(), '&'],
// Abilities / Talents
['s.skillLine1', $this->subject->getField('skills')],
[DB::AND, ['s.skillLine1', 0, '>'], ['s.skillLine2OrMask', $this->subject->getField('skills')]]
],
[ // last rank or unranked
DB::OR,
['s.cuFlags', SPELL_CU_LAST_RANK, '&'],
['s.rankNo', 0]
]
);
$genSpells = new SpellList($conditions);
if (!$genSpells->error)
{
$this->extendGlobalData($genSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $genSpells->getListviewData(),
'id' => 'spells',
'name' => '$LANG.tab_spells',
'visibleCols' => ['level', 'schools', 'type', 'classes'],
'hiddenCols' => ['reagents', 'skill'],
'sort' => ['-level', 'type', 'name'],
'computeDataFunc' => '$Listview.funcBox.initSpellFilter',
'onAfterCreate' => '$Listview.funcBox.addSpellIndicator'
), SpellList::$brickFile));
}
// tab: items (grouped)
$conditions = array(
['requiredClass', $cl->toMask(), '&'],
['itemset', 0]
);
$items = new ItemList($conditions);
if (!$items->error)
{
$this->extendGlobalData($items->getJSGlobals());
$hiddenCols = null;
if ($items->hasDiffFields('requiredRace'))
$hiddenCols = ['side'];
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $items->getListviewData(),
'id' => 'items',
'name' => '$LANG.tab_items',
'visibleCols' => ['dps', 'armor', 'slot'],
'hiddenCols' => $hiddenCols,
'computeDataFunc' => '$Listview.funcBox.initSubclassFilter',
'onAfterCreate' => '$Listview.funcBox.addSubclassIndicator',
'note' => sprintf(Util::$filterResultString, '?items&filter=cr=152;crs='.$this->typeId.';crv=0'),
'_truncated' => 1
), ItemList::$brickFile));
}
// tab: quests
$conditions = array(
['reqClassMask', $cl->toMask(), '&'],
[['reqClassMask', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL, '!']
);
$quests = new QuestList($conditions);
if (!$quests->error)
{
$this->extendGlobalData($quests->getJSGlobals());
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $quests->getListviewData(),
'sort' => ['reqlevel', 'name']
), QuestList::$brickFile));
}
// tab: itemsets
$sets = new ItemsetList(array(['classMask', $cl->toMask(), '&']));
if (!$sets->error)
{
$this->extendGlobalData($sets->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $sets->getListviewData(),
'note' => sprintf(Util::$filterResultString, '?itemsets&filter=cl='.$this->typeId),
'hiddenCols' => ['classes'],
'sort' => ['-level', 'name']
), ItemsetList::$brickFile));
}
// tab: trainers
$conditions = array(
['npcflag', NPC_FLAG_TRAINER | NPC_FLAG_CLASS_TRAINER, '&'],
['trainerType', 0], // trains class spells
['trainerRequirement', $this->typeId]
);
$trainer = new CreatureList($conditions);
if (!$trainer->error)
{
$this->addDataLoader('zones');
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $trainer->getListviewData(),
'id' => 'trainers',
'name' => '$LANG.tab_trainers'
), CreatureList::$brickFile));
}
// tab: races
$races = new CharRaceList(array(['classMask', $cl->toMask(), '&']));
if (!$races->error)
$this->lvTabs->addListviewTab(new Listview(['data' => $races->getListviewData()], CharRaceList::$brickFile));
// tab: criteria-of
$conditions = array(
DB::AND,
['ac.type', ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS],
['ac.value1', $this->typeId]
);
if ($extraCrt = DB::World()->selectCol('SELECT `criteria_id` FROM achievement_criteria_data WHERE `type` IN %in AND `value1` = %i', [ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE, ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE], $this->typeId))
$conditions = [DB::OR, $conditions, ['ac.id', $extraCrt]];
$crtOf = new AchievementList($conditions);
if (!$crtOf->error)
{
$this->extendGlobalData($crtOf->getJSGlobals());
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $crtOf->getListviewData(),
'name' => '$LANG.tab_criteriaof',
'id' => 'criteria-of'
), AchievementList::$brickFile));
}
// tab: condition-for
$cnd = new Conditions();
$cnd->getByCondition(Type::CHR_CLASS, $this->typeId)->prepare();
if ($tab = $cnd->toListviewTab('condition-for', '$LANG.tab_condition_for'))
{
$this->extendGlobalData($cnd->getJsGlobals());
$this->lvTabs->addDataTab(...$tab);
}
parent::generate();
}
}
?>

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 $rawParam)
{
$this->getCategoryFromUrl($rawParam);
parent::__construct($rawParam);
}
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` = %i', $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()->qry('INSERT INTO ::comments (`userId`, `roles`, `body`, `date`, `replyTo`) VALUES (%i, %i, %s, UNIX_TIMESTAMP(), %i)', 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` = %i', $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()->qry('INSERT INTO ::comments (`type`, `typeId`, `userId`, `roles`, `body`, `date`) VALUES (%i, %i, %i, %i, %s, 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()->qry('INSERT INTO ::user_ratings (`type`, `entry`, `userId`, `value`) VALUES (%i, %i, 0, 1)', RATING_COMMENT, $postId);
// flag target with hasComment
if ($tbl = Type::getClassAttrib($this->_get['type'], 'dataTable'))
DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $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,41 +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' : '');
}
$where = [['`id` = %i', $this->_post['id']]];
if (!User::isInGroup(U_GROUP_MODERATOR))
$where[] = ['`userId` = %i', User::$id];
// flag as deleted
if (DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` | %i, `deleteUserId` = %i, `deleteDate` = UNIX_TIMESTAMP() WHERE %and', CC_FLAG_DELETED, User::$id, $where))
DB::Aowow()->qry('DELETE FROM ::user_ratings WHERE `type` = %i AND `entry` = %i', 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,50 +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)
$where = [['`id` IN %in', $this->_post['id']]];
if (!User::isInGroup(U_GROUP_MODERATOR))
$where[] = ['`userId` = %i', User::$id];
// flag as deleted; unflag subject: hasComment
if (DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` | %i, `deleteUserId` = %i, `deleteDate` = UNIX_TIMESTAMP() WHERE %and', CC_FLAG_DELETED, User::$id, $where))
{
$coInfo = DB::Aowow()->selectAssoc(
'SELECT IF(BIT_OR(~b.`flags`) & %i, 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 %in 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()->qry('UPDATE %n SET `cuFlags` = `cuFlags` & ~%i WHERE `id` = %i', $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()->qry('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` = %i', $this->_post['id']);
}
}
?>

View file

@ -1,55 +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` & %i, 1, 0) AS "deleted" FROM ::comments WHERE `id` = %i', 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');
if (is_null(DB::Aowow()->qry('INSERT INTO ::user_ratings (`type`, `entry`, `userId`, `value`) VALUES (%i, %i, %i, %i)',
RATING_COMMENT, $this->_post['id'], User::$id, User::canSupervote() ? -2 : -1
)))
{
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,65 +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` = %i AND `replyTo` = %i', $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;
$where = [['`id` = %i', $this->_post['replyId']], ['`replyTo` = %i', $this->_post['commentId']]];
if (!User::isInGroup(U_GROUP_MODERATOR))
$where[] = ['`userId` = %i', User::$id];
if (!DB::Aowow()->qry('UPDATE ::comments SET `editCount` = `editCount` + 1, %a WHERE %and', $update, $where))
{
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` = %i', $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()->qry('UPDATE ::comments SET `editCount` = `editCount` + 1, %a WHERE `id` = %i', $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` = %i', $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()->qry('UPDATE ::comments SET `flags` = `flags` | %i WHERE `id` = %i', 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()->qry('UPDATE ::comments SET `flags` = `flags` | %i WHERE `id` = %i', CC_FLAG_OUTDATED, $this->_post['id']);
else
$ok = DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` & ~%i WHERE `id` = %i', 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()->qry('UPDATE ::comments SET `flags` = `flags` | %i WHERE `id` = %i', 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` = %i AND `entry` = %i 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()->qry('UPDATE ::comments SET `flags` = `flags` | %i WHERE `id` = %i', CC_FLAG_STICKY, $this->_post['id']);
else
DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` & ~%i WHERE `id` = %i', CC_FLAG_STICKY, $this->_post['id']);
}
}
?>

View file

@ -1,49 +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)
$where = [['`id` IN %in', $this->_post['id']]];
if (!User::isInGroup(U_GROUP_MODERATOR))
{
$where[] = ['`deleteUserId` = `userId'];
$where[] = ['`deleteUserId` = %i', User::$id];
}
// unflag subject: hasComment
if (DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` & ~%i WHERE %and', CC_FLAG_DELETED, $where))
{
$coInfo = DB::Aowow()->selectAssoc('SELECT `type` AS "0", `typeId` AS "1" FROM ::comments WHERE `id` IN %in GROUP BY `type`, `typeId`', $this->_post['id']);
foreach ($coInfo as [$type, $typeId])
if ($tbl = Type::getClassAttrib($type, 'dataTable'))
DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $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,55 +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` & %i, 1, 0) AS "deleted" FROM ::comments WHERE `id` = %i', 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');
if (is_null(DB::Aowow()->qry('INSERT INTO ::user_ratings (`type`, `entry`, `userId`, `value`) VALUES (%i, %i, %i, %i)',
RATING_COMMENT, $this->_post['id'], User::$id, User::canSupervote() ? 2 : 1
)))
{
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` & %i, 1, 0) AS "deleted" FROM ::comments c LEFT JOIN ::user_ratings ur ON ur.`type` = %i AND ur.`entry` = c.id AND ur.`userId` = %i WHERE c.id = %i',
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()->qry('DELETE FROM ::user_ratings WHERE `type` = %i AND `entry` = %i AND `userId` = %i', RATING_COMMENT, $this->_get['id'], User::$id);
else // replace, because we may be overwriting an old, opposing vote
if ($ok = DB::Aowow()->qry('REPLACE INTO ::user_ratings (`type`, `entry`, `userId`, `value`) VALUES (%i, %i, %i, %i)', 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($rawParam)
{
parent::__construct($rawParam);
// 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,49 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class ContactusBaseResponse extends TextResponse
{
protected array $expectedPOST = array(
'mode' => ['filter' => FILTER_VALIDATE_INT ],
'reason' => ['filter' => FILTER_VALIDATE_INT ],
'ua' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'appname' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'page' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']],
'desc' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ],
'id' => ['filter' => FILTER_VALIDATE_INT ],
'relatedurl' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']],
'email' => ['filter' => FILTER_SANITIZE_EMAIL ]
);
/* responses
0: success
1: captcha invalid
2: description too long
3: reason missing
7: already reported
$: prints response
*/
protected function generate() : void
{
if (!$this->assertPOST('mode', 'reason'))
{
$this->result = 4;
return;
}
$report = new Report($this->_post['mode'], $this->_post['reason'], $this->_post['id']);
if ($report->create($this->_post['desc'], $this->_post['ua'], $this->_post['appname'], $this->_post['page'], $this->_post['relatedurl'], $this->_post['email']))
$this->result = 0;
else if (($e = $report->getError()) > 0)
$this->result = $e;
else
$this->result = Lang::main('intError');
}
}
?>

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()->qry('UPDATE ::account_cookies SET `data` = "purged" WHERE `userId` = %i 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()->qry('REPLACE INTO ::account_cookies VALUES (%i, %s, %s)', 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 $rawParam)
{
$this->getCategoryFromUrl($rawParam);
parent::__construct($rawParam);
}
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,267 +0,0 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class CurrencyBaseResponse extends TemplateResponse implements ICache
{
use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'detail-page-generic';
protected string $pageName = 'currency';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 15];
public int $type = Type::CURRENCY;
public int $typeId = 0;
private CurrencyList $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 CurrencyList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->generateNotFound(Lang::game('currency'), Lang::currency('notFound'));
$this->h1 = $this->subject->getField('name', true);
$this->gPageInfo += array(
'type' => $this->type,
'typeId' => $this->typeId,
'name' => $this->h1
);
$_relItemId = $this->subject->getField('itemId');
/**************/
/* Page Title */
/**************/
array_unshift($this->title, $this->h1, Util::ucFirst(Lang::game('currency')));
/*************/
/* Menu Path */
/*************/
$this->breadcrumb[] = $this->subject->getField('category');
/***********/
/* Infobox */
/**********/
$infobox = Lang::getInfoBoxForFlags(intval($this->subject->getField('cuFlags')));
// cap
if ($_ = $this->subject->getField('cap'))
$infobox[] = Lang::currency('cap').Lang::nf($_);
// id
$infobox[] = Lang::currency('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, $_);
}
// 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 */
/****************/
$hi = $this->subject->getJSGlobals()[Type::CURRENCY][$this->typeId]['icon'];
if ($hi[0] == $hi[1])
unset($hi[1]);
$this->headIcons = $hi;
$this->redButtons = array(
BUTTON_WOWHEAD => true,
BUTTON_LINKS => true
);
if ($_ = $this->subject->getField('description', true))
$this->extraText = new Markup($_, ['dbpage' => true, 'allow' => Markup::CLASS_ADMIN], 'text-generic');
/**************/
/* Extra Tabs */
/**************/
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
if ($this->typeId != CURRENCY_HONOR_POINTS && $this->typeId != CURRENCY_ARENA_POINTS)
{
// tabs: this currency is contained in..
$lootTabs = new LootByItem($_relItemId);
if ($lootTabs->getByItem())
{
$this->extendGlobalData($lootTabs->jsGlobals);
foreach ($lootTabs->iterate() as [$template, $tabData])
{
if ($template == 'npc' || $template == 'object')
$this->addDataLoader('zones');
if ($template != 'quest')
{
foreach ($tabData['data'] as &$row)
if (!empty($row['stack']))
$row['currency'] = [[$this->typeId, $row['stack'][0]]];
$tabData['extraCols'][] = '$Listview.extraCols.currency';
}
$this->lvTabs->addListviewTab(new Listview($tabData, $template));
}
}
// tab: sold by
$itemObj = new ItemList(array(['id', $_relItemId]));
if (!empty($itemObj->getExtendedCost()[$_relItemId]))
{
$vendors = $itemObj->getExtendedCost()[$_relItemId];
$this->extendGlobalData($itemObj->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
$soldBy = new CreatureList(array(['id', array_keys($vendors)]));
if (!$soldBy->error)
{
$sbData = $soldBy->getListviewData();
$extraCols = ['$Listview.extraCols.stock', "\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost', '$Listview.extraCols.condition'];
foreach ($sbData as $k => &$row)
{
$items = [];
$tokens = [];
// note: can only display one entry per row, so only use first entry of each vendor
foreach ($vendors[$k][0] as $id => $qty)
{
if (is_string($id))
continue;
if ($id > 0)
$tokens[] = [$id, $qty];
else if ($id < 0)
$items[] = [-$id, $qty];
}
if ($e = $vendors[$k][0]['event'])
if (Conditions::extendListviewRow($row, Conditions::SRC_NONE, $k, [Conditions::ACTIVE_EVENT, $e]))
$this->extendGlobalIds(Type::WORLDEVENT, $e);
$row['stock'] = $vendors[$k][0]['stock'];
$row['stack'] = $itemObj->getField('buyCount');
$row['cost'] = array(
$itemObj->getField('buyPrice'),
$items ?: null,
$tokens ?: null
);
}
// no conditions > remove conditions column
if (!array_column($sbData, 'condition'))
array_pop($extraCols);
$this->addDataLoader('zones');
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $sbData,
'name' => '$LANG.tab_soldby',
'id' => 'sold-by-npc',
'extraCols' => $extraCols,
'hiddenCols' => ['level', 'type']
), CreatureList::$brickFile));
}
}
}
// tab: created by (spell) [for items its handled in LootByItem]
if ($this->typeId == CURRENCY_HONOR_POINTS)
{
$createdBy = new SpellList(array(['effect1Id', SPELL_EFFECT_ADD_HONOR], ['effect2Id', SPELL_EFFECT_ADD_HONOR], ['effect3Id', SPELL_EFFECT_ADD_HONOR], DB::OR));
if (!$createdBy->error)
{
$this->extendGlobalData($createdBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
$tabData = array(
'data' => $createdBy->getListviewData(),
'name' => '$LANG.tab_createdby',
'id' => 'created-by',
);
if ($createdBy->hasSetFields('reagent1', 'reagent2', 'reagent3', 'reagent4', 'reagent5', 'reagent6', 'reagent7', 'reagent8'))
$tabData['visibleCols'] = ['reagents'];
$this->lvTabs->addListviewTab(new Listview($tabData, SpellList::$brickFile));
}
}
// tab: currency for
$n = $w = null;
if ($this->typeId == CURRENCY_ARENA_POINTS)
{
$n = '?items&filter=cr=145;crs=1;crv=0';
$w = '`reqArenaPoints` > 0';
}
else if ($this->typeId == CURRENCY_HONOR_POINTS)
{
$n = '?items&filter=cr=144;crs=1;crv=0';
$w = '`reqHonorPoints` > 0';
}
else
$w = '`reqItemId1` = '.$_relItemId.' OR `reqItemId2` = '.$_relItemId.' OR `reqItemId3` = '.$_relItemId.' OR `reqItemId4` = '.$_relItemId.' OR `reqItemId5` = '.$_relItemId;
if (!$n && !is_null(ItemListFilter::getCriteriaIndex(158, $_relItemId)))
$n = '?items&filter=cr=158;crs='.$_relItemId.';crv=0';
$xCosts = DB::Aowow()->selectCol('SELECT `id` FROM ::itemextendedcost WHERE '.$w);
$boughtBy = $xCosts ? DB::World()->selectCol('SELECT `item` FROM npc_vendor WHERE `extendedCost` IN %in UNION SELECT `item` FROM game_event_npc_vendor WHERE `extendedCost` IN %in', $xCosts, $xCosts) : [];
if ($boughtBy)
{
$boughtBy = new ItemList(array(['id', $boughtBy]));
if (!$boughtBy->error)
{
$tabData = array(
'data' => $boughtBy->getListviewData(ITEMINFO_VENDOR, [Type::CURRENCY => $this->typeId]),
'name' => '$LANG.tab_currencyfor',
'id' => 'currency-for',
'extraCols' => ["\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost']
);
if ($n)
$tabData['note'] = sprintf(Util::$filterResultString, $n);
$this->lvTabs->addListviewTab(new Listview($tabData, ItemList::$brickFile));
$this->extendGlobalData($boughtBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
}
}
parent::generate();
}
}
?>

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