1 Commits
v1.2 ... v1.0

Author SHA1 Message Date
Sarjuuk
e7d49befdf Updated README with installation instructions
thats it folks .. lets call it v1.0
2015-06-27 21:40:43 +02:00
509 changed files with 26666 additions and 74098 deletions

4
.gitattributes vendored
View File

@@ -1,4 +0,0 @@
*.php text eol=lf
*.js text eol=lf
*.css text eol=lf
*.sql text eol=lf

View File

@@ -1,22 +0,0 @@
---
name: Bug report
about: issue template
title: ''
labels: ''
assignees: ''
---
**Describe the bug and how to reproduce it**
additionally paste relevant lines from db table `aowow_errors`
or your browsers console here.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**System:**
- OS: [e.g. Win10]
- PHP version:
- revision used:
- Browser (in case of JavaScript / display errors):
- AzerothCore: yes/no

18
.gitignore vendored
View File

@@ -1,44 +1,30 @@
# Git
*.orig
# cache # cache
/cache/template/* /cache/template/*
/setup/generated/alphaMaps/*.png /setup/generated/alphaMaps/*.png
/cache/firstrun
# extract from MPQ
/setup/mpqdata/*
# generated files # generated files
/static/js/profile_all.js /static/js/profile_all.js
/static/js/locale.js /static/js/locale.js
/static/js/Markup.js
/static/widgets/power.js /static/widgets/power.js
/static/widgets/power/demo.html /static/widgets/power/demo.html
/static/widgets/searchbox.js /static/widgets/searchbox.js
/static/widgets/searchbox/searchbox.html /static/widgets/searchbox/searchbox.html
/static/download/searchplugins/aowow.xml /static/download/searchplugins/aowow.xml
/config/config.php /config/config.php
/datasets/*
!/datasets/zones
# /datasets/item-scaling # /datasets/item-scaling
# extracted sounds
/static/wowsounds/*
# extracted images # extracted images
/static/images/wow/icons/large/* /static/images/wow/icons/large/*
/static/images/wow/icons/medium/* /static/images/wow/icons/medium/*
/static/images/wow/icons/small/* /static/images/wow/icons/small/*
/static/images/wow/icons/tiny/* /static/images/wow/icons/tiny/*
!/static/images/wow/icons/tiny/quest_* !/static/images/wow/icons/tiny/quest_*
/static/images/wow/hunterpettalents/* /static/images/wow/hunterpettalents/icons*
/static/images/wow/Interface/* /static/images/wow/interface/*
/static/images/wow/loadingscreens/* /static/images/wow/loadingscreens/*
/static/images/wow/maps/* /static/images/wow/maps/*
!/static/images/wow/maps/overlay* !/static/images/wow/maps/overlay*
/static/images/wow/talents/icons/* /static/images/wow/talents/icons/*
/static/images/wow/talents/backgrounds/*
# modelviewer (~7GB data) # modelviewer (~7GB data)
/static/modelviewer/models/* /static/modelviewer/models/*

6
.gitmodules vendored Normal file
View File

@@ -0,0 +1,6 @@
[submodule "includes/tools/MPQExtractor"]
path = includes/tools/MPQExtractor
url = https://github.com/iamcal/MPQExtractor.git
[submodule "includes/tools/BLPConverter"]
path = includes/tools/BLPConverter
url = https://github.com/Kanma/BLPConverter.git

View File

@@ -1,19 +1,11 @@
Order Deny,Allow Order Deny,Allow
<FilesMatch "\.(conf|php|in)$"> <FilesMatch "\.(conf|php|in)|aowow$">
Deny from all Deny from all
</FilesMatch> </FilesMatch>
<FilesMatch "^(index)\.php$"> <FilesMatch "^(index)\.php$">
Allow from all Allow from all
</FilesMatch> </FilesMatch>
<Files "aowow">
ForceType application/x-httpd-php
</Files>
<Files "prQueue">
ForceType application/x-httpd-php
</Files>
# Block view of some folders # Block view of some folders
Options -Indexes Options -Indexes
DirectoryIndex index.php DirectoryIndex index.php
@@ -21,13 +13,13 @@ DirectoryIndex index.php
# Support for UTF8 # Support for UTF8
AddDefaultCharset utf8 AddDefaultCharset utf8
<IfModule mod_charset.c> <IfModule mod_charset.c>
CharsetDisable on CharsetDisable on
CharsetRecodeMultipartForms Off CharsetRecodeMultipartForms Off
</IfModule> </IfModule>
# UHD screenshots can get pretty large (cannot be set in config) # 5MB should be enough for the largest screenshots in the land
php_value upload_max_filesize 20M php_value default_charset UTF-8
php_value post_max_size 25M php_value upload_max_filesize 5M
RewriteEngine on RewriteEngine on
# RewriteBase /~user/localPath/ # enable if the rules do not work, when they should # RewriteBase /~user/localPath/ # enable if the rules do not work, when they should

BIN
README Normal file

Binary file not shown.

140
README.md
View File

@@ -1,140 +0,0 @@
![logo](static/images/logos/home.png)
## Build Status
![fuck it ship it](http://forthebadge.com/images/badges/fuck-it-ship-it.svg)
## Introduction
AoWoW is a Database tool for World of Warcraft v3.3.5 (build 12340)
It is based upon the other famous Database tool for WoW, featuring the red smiling rocket.
While the first releases can be found as early as 2008, today it is impossible to say who created this project.
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 puposes of any kind!
## Requirements
+ 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)
+ [GNU Multiple Precision](https://www.php.net/manual/en/book.gmp.php) (When using TrinityCore as auth source)
+ 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))
+ WIN users may find it easier to use these alternatives
+ [MPQEditor](http://www.zezula.net/en/mpq/download.html) / [FFmpeg](http://ffmpeg.zeranoe.com/builds/) / (optional: [BLPConverter](https://github.com/PatrickCyr/BLPConverter))
audio processing may require [lame](https://sourceforge.net/projects/lame/files/lame/3.99/) or [vorbis-tools](https://www.xiph.org/downloads/) (which may require libvorbis (which may require libogg))
#### Highly Recommended
+ setting the following configuration values on your TrinityCore server will greatly increase the accuracy of spawn points
> Calculate.Creature.Zone.Area.Data = 1
> Calculate.Gameoject.Zone.Area.Data = 1
## Install
#### 1. Acquire the required repositories
`git clone git@github.com:Sarjuuk/aowow.git aowow`
`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 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
* `cache/`
* `config/`
* `static/download/`
* `static/widgets/`
* `static/js/`
* `static/uploads/`
* `static/images/wow/`
* `datasets/`
#### 4. Extract the client archives (MPQs)
Extract the following directories from the client archives into `setup/mpqdata/`, while maintaining patch order (base mpq -> patch-mpq: 1 -> 9 -> A -> Z). The required paths are scattered across the archives. Overwrite older files if asked to.
.. for every locale you are going to use:
> \<localeCode>/DBFilesClient/
> \<localeCode>/Interface/WorldMap/
> \<localeCode>/Interface/FrameXML/GlobalStrings.lua
.. 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/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. 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.
## 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 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 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 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 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, 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.
Q: An Item, Quest or NPC i added or edited can't be searched. Why?
A: A search is only conducted against the currently used locale. You may have only edited the name field in the base table instead of adding multiple strings into the appropriate \*_locale tables. In this case searches in a non-english locale are run against an empty name field.
## Thanks
@mix: for providing the php-script to parse .blp and .dbc into usable images and tables
@LordJZ: the wrapper-class for DBSimple; the basic idea for the user-class
@kliver: basic implementation of screenshot uploads
@Sarjuuk: maintainer of the project
## Special Thanks
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](http://forthebadge.com/images/badges/uses-badges.svg)

24
aowow Executable file → Normal file
View File

@@ -1,12 +1,12 @@
<?php <?php
require 'includes/shared.php'; require 'includes/shared.php';
if (!CLI) if (!CLI)
die("this script must be run from CLI\n"); die("this script must be run from CLI\n");
if (CLI && getcwd().DIRECTORY_SEPARATOR.'aowow' != __FILE__) if (CLI && getcwd().DIRECTORY_SEPARATOR.'aowow' != __FILE__)
die("this script must be run from root directory\n"); die("this script must be run from root directory\n");
else else
require 'setup/setup.php'; require 'setup/setup.php';
?> ?>

49
config/config.php.in Normal file
View File

@@ -0,0 +1,49 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// use as example. File is generated from setup.
// -- Aowow Database --
// contains world-data, user-data and logs
$AoWoWconf['aowow'] = array(
'host' => '127.0.0.1',
'user' => '<user>',
'pass' => '<pass>',
'db' => 'world',
'prefix' => 'aowow_'
);
// -- World Database --
// used to generate data-tables
$AoWoWconf['world'] = array(
'host' => '127.0.0.1',
'user' => '<user>',
'pass' => '<pass>',
'db' => 'world',
'prefix' => ''
);
// -- Auth Database --
// used to generate user-tables
$AoWoWconf['auth'] = array(
'host' => '127.0.0.1',
'user' => '<user>',
'pass' => '<pass>',
'db' => 'auth',
'prefix' => ''
);
// -- Characters Database --
// used to display profiles
$AoWoWconf['characters'][<realmId>] = array(
'host' => '127.0.0.1',
'user' => '<user>',
'pass' => '<pass>',
'db' => 'characters',
'prefix' => ''
);
?>

View File

@@ -4,7 +4,7 @@ if (!defined('AOWOW_REVISION'))
die('illegal access'); die('illegal access');
function extAuth($user, $pass, &$userId = 0, &$userGroup = -1) function extAuth($user, $pass, &$userId = 0)
{ {
/* /*
insert some auth mechanism here insert some auth mechanism here

74
datasets/weight-presets Normal file
View File

@@ -0,0 +1,74 @@
var wt_presets = {
1: {
pve: {
arms: {__icon:'ability_rogue_eviscerate'},
fury: {__icon:'ability_warrior_innerrage',exprtng:100,str:82,critstrkrtng:66,agi:53,armorpenrtng:52,hitrtng:48,hastertng:36,atkpwr:31,armor:5},
prot: {__icon:'ability_warrior_defensivestance',sta:100,dodgertng:90,defrtng:86,block:81,agi:67,parryrtng:67,blockrtng:48,str:48,exprtng:19,hitrtng:10,armorpenrtng:10,critstrkrtng:7,armor:6,hastertng:1,atkpwr:1}
}
},
2: {
pve: {
holy: {__icon:'spell_holy_holybolt',int:100,manargn:88,splpwr:58,critstrkrtng:46,hastertng:35},
prot: {__icon:'ability_paladin_shieldofthetemplar',sta:100,dodgertng:94,block:86,defrtng:86,exprtng:79,agi:76,parryrtng:76,hitrtng:58,blockrtng:52,str:50,armor:6,atkpwr:6,splpwr:4,critstrkrtng:3},
retrib: {__icon:'spell_holy_auraoflight',mledps:470,hitrtng:100,str:80,exprtng:66,critstrkrtng:40,atkpwr:34,agi:32,hastertng:30,armorpenrtng:22,splpwr:9}
}
},
3: {
pve: {
beast: {__icon:'ability_hunter_beasttaming',rgddps:213,hitrtng:100,agi:58,critstrkrtng:40,int:37,atkpwr:30,armorpenrtng:28,hastertng:21},
marks: {__icon:'ability_marksmanship',rgddps:379,hitrtng:100,agi:74,critstrkrtng:57,armorpenrtng:40,int:39,atkpwr:32,hastertng:24},
surv: {__icon:'ability_hunter_swiftstrike',rgddps:181,hitrtng:100,agi:76,critstrkrtng:42,int:35,hastertng:31,atkpwr:29,armorpenrtng:26}
}
},
4: {
pve: {
assas: {__icon:'ability_rogue_eviscerate',mledps:170,agi:100,exprtng:87,hitrtng:83,critstrkrtng:81,atkpwr:65,armorpenrtng:65,hastertng:64,str:55},
combat: {__icon:'ability_backstab',mledps:220,armorpenrtng:100,agi:100,exprtng:82,hitrtng:80,critstrkrtng:75,hastertng:73,str:55,atkpwr:50},
subtle: {__icon:'ability_stealth',mledps:228,exprtng:100,agi:100,hitrtng:80,armorpenrtng:75,critstrkrtng:75,hastertng:75,str:55,atkpwr:50}
}
},
5: {
pve: {
disc: {__icon:'spell_holy_wordfortitude',splpwr:100,manargn:67,int:65,hastertng:59,critstrkrtng:48,spi:22},
holy: {__icon:'spell_holy_guardianspirit',manargn:100,int:69,splpwr:60,spi:52,critstrkrtng:38,hastertng:31},
shadow: {__icon:'spell_shadow_shadowwordpain',hitrtng:100,shasplpwr:76,splpwr:76,critstrkrtng:54,hastertng:50,spi:16,int:16}
}
},
6: {
pve: {
blooddps: {__icon:'spell_deathknight_bloodpresence',mledps:360,armorpenrtng:100,str:99,hitrtng:91,exprtng:90,critstrkrtng:57,hastertng:55,atkpwr:36,armor:1},
frostdps: {__icon:'spell_deathknight_frostpresence',mledps:337,hitrtng:100,str:97,exprtng:81,armorpenrtng:61,critstrkrtng:45,atkpwr:35,hastertng:28,armor:1},
frosttank: {__icon:'spell_deathknight_frostpresence',mledps:419,parryrtng:100,hitrtng:97,str:96,defrtng:85,exprtng:69,dodgertng:61,agi:61,sta:61,critstrkrtng:49,atkpwr:41,armorpenrtng:31,armor:5},
unholydps: {__icon:'spell_deathknight_unholypresence',mledps:209,str:100,hitrtng:66,exprtng:51,hastertng:48,critstrkrtng:45,atkpwr:34,armorpenrtng:32,armor:1}
}
},
7: {
pve: {
elem: {__icon:'spell_nature_lightning',hitrtng:100,splpwr:60,hastertng:56,critstrkrtng:40,int:11},
enhance: {__icon:'spell_nature_lightningshield',mledps:135,hitrtng:100,exprtng:84,agi:55,int:55,critstrkrtng:55,hastertng:42,str:35,atkpwr:32,splpwr:29,armorpenrtng:26},
resto: {__icon:'spell_nature_magicimmunity',manargn:100,int:85,splpwr:77,critstrkrtng:62,hastertng:35}
}
},
8: {
pve: {
arcane: {__icon:'spell_holy_magicalsentry',hitrtng:100,hastertng:54,arcsplpwr:49,splpwr:49,critstrkrtng:37,int:34,frosplpwr:24,firsplpwr:24,spi:14},
fire: {__icon:'spell_fire_firebolt02',hitrtng:100,hastertng:53,firsplpwr:46,splpwr:46,critstrkrtng:43,frosplpwr:23,arcsplpwr:23,int:13},
frost: {__icon:'spell_frost_frostbolt02',hitrtng:100,hastertng:42,frosplpwr:39,splpwr:39,arcsplpwr:19,firsplpwr:19,critstrkrtng:19,int:6}
}
},
9: {
pve: {
afflic: {__icon:'spell_shadow_deathcoil',hitrtng:100,shasplpwr:72,splpwr:72,hastertng:61,critstrkrtng:38,firsplpwr:36,spi:34,int:15},
demo: {__icon:'spell_shadow_metamorphosis',hitrtng:100,hastertng:50,firsplpwr:45,shasplpwr:45,splpwr:45,critstrkrtng:31,spi:29,int:13},
destro: {__icon:'spell_shadow_rainoffire',hitrtng:100,firsplpwr:47,splpwr:47,hastertng:46,spi:26,shasplpwr:23,critstrkrtng:16,int:13}
}
},
11: {
pve: {
balance: {__icon:'spell_nature_starfall',hitrtng:100,splpwr:66,hastertng:54,critstrkrtng:43,spi:22,int:22},
feraltank: {__icon:'ability_racial_bearform',agi:100,sta:75,dodgertng:65,defrtng:60,exprtng:16,str:10,armor:10,hitrtng:8,hastertng:5,atkpwr:4,feratkpwr:4,critstrkrtng:3},
resto: {__icon:'spell_nature_healingtouch',splpwr:100,manargn:73,hastertng:57,int:51,spi:32,critstrkrtng:11},
feraldps: {__icon:'ability_druid_catform',agi:100,armorpenrtng:90,str:80,critstrkrtng:55,exprtng:50,hitrtng:50,feratkpwr:40,atkpwr:40,hastertng:35}
}
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,178 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxAccount extends AjaxHandler
{
protected $validParams = ['exclude', 'weightscales', 'favorites'];
protected $_post = array(
'groups' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'save' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'delete' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkIdList'],
'name' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxAccount::checkName' ],
'scale' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxAccount::checkScale' ],
'reset' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'mode' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'type' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'add' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'remove' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
// 'sessionKey' => ['filter' => FILTER_SANITIZE_NUMBER_INT]
);
protected $_get = array(
'locale' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkLocale']
);
public function __construct(array $params)
{
parent::__construct($params);
if (is_numeric($this->_get['locale']))
User::useLocale($this->_get['locale']);
if (!$this->params || !User::$id)
return;
// select handler
if ($this->params[0] == 'exclude')
$this->handler = 'handleExclude';
else if ($this->params[0] == 'weightscales')
$this->handler = 'handleWeightscales';
else if ($this->params[0] == 'favorites')
$this->handler = 'handleFavorites';
}
protected function handleExclude() : void
{
if ($this->_post['mode'] == 1) // directly set exludes
{
$type = $this->_post['type'];
$ids = $this->_post['id'];
if (!Type::exists($type) || empty($ids))
{
trigger_error('AjaxAccount::handleExclude - invalid type #'.$type.(empty($ids) ? ' or id-list empty' : ''), E_USER_ERROR);
return;
}
// ready for some bullshit? here it comes!
// we don't get signaled whether an id should be added to or removed from either includes or excludes
// so we throw everything into one table and toggle the mode if its already in here
$includes = DB::Aowow()->selectCol('SELECT typeId FROM ?_profiler_excludes WHERE type = ?d AND typeId IN (?a)', $type, $ids);
foreach ($ids as $typeId)
DB::Aowow()->query('INSERT INTO ?_account_excludes (`userId`, `type`, `typeId`, `mode`) VALUES (?a) ON DUPLICATE KEY UPDATE mode = (mode ^ 0x3)', array(
User::$id, $type, $typeId, in_array($includes, $typeId) ? 2 : 1
));
return;
}
else if ($this->_post['reset'] == 1) // defaults to unavailable
{
$mask = PR_EXCLUDE_GROUP_UNAVAILABLE;
DB::Aowow()->query('DELETE FROM ?_account_excludes WHERE userId = ?d', User::$id);
}
else // clamp to real groups
$mask = $this->_post['groups'] & PR_EXCLUDE_GROUP_ANY;
DB::Aowow()->query('UPDATE ?_account SET excludeGroups = ?d WHERE id = ?d', $mask, User::$id);
}
protected function handleWeightscales() : string
{
if ($this->_post['save'])
{
if (!$this->_post['scale'])
{
trigger_error('AjaxAccount::handleWeightscales - scaleId empty', E_USER_ERROR);
return '0';
}
$id = 0;
if ($this->_post['id'] && ($id = $this->_post['id'][0]))
{
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account_weightscales WHERE userId = ?d AND id = ?d', User::$id, $id))
{
trigger_error('AjaxAccount::handleWeightscales - scale #'.$id.' not in db or owned by user #'.User::$id, E_USER_ERROR);
return '0';
}
DB::Aowow()->query('UPDATE ?_account_weightscales SET `name` = ? WHERE id = ?d', $this->_post['name'], $id);
}
else
{
$nScales = DB::Aowow()->selectCell('SELECT COUNT(id) FROM ?_account_weightscales WHERE userId = ?d', User::$id);
if ($nScales >= 5) // more or less hard-defined in LANG.message_weightscalesaveerror
return '0';
$id = DB::Aowow()->query('INSERT INTO ?_account_weightscales (`userId`, `name`) VALUES (?d, ?)', User::$id, $this->_post['name']);
}
DB::Aowow()->query('DELETE FROM ?_account_weightscale_data WHERE id = ?d', $id);
foreach (explode(',', $this->_post['scale']) as $s)
{
[$k, $v] = explode(':', $s);
if (!in_array($k, Util::$weightScales) || $v < 1)
continue;
DB::Aowow()->query('INSERT INTO ?_account_weightscale_data VALUES (?d, ?, ?d)', $id, $k, $v);
}
return (string)$id;
}
else if ($this->_post['delete'] && $this->_post['id'] && $this->_post['id'][0])
DB::Aowow()->query('DELETE FROM ?_account_weightscales WHERE id = ?d AND userId = ?d', $this->_post['id'][0], User::$id);
else
{
trigger_error('AjaxAccount::handleWeightscales - malformed request received', E_USER_ERROR);
return '0';
}
}
protected function handleFavorites() : void
{
// omit usage of sessionKey
if (count($this->_post['id']) != 1 || empty($this->_post['id'][0]))
{
trigger_error('AjaxAccount::handleFavorites - malformed request received', E_USER_ERROR);
return;
}
$typeId = $this->_post['id'][0];
if ($type = $this->_post['add'])
{
$tc = Type::newList($type, [['id', $typeId]]);
if (!$tc || $tc->error)
{
trigger_error('AjaxAccount::handleFavorites - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR);
return;
}
DB::Aowow()->query('INSERT INTO ?_account_favorites (`userId`, `type`, `typeId`) VALUES (?d, ?d, ?d)', User::$id, $type, $typeId);
}
else if ($type = $this->_post['remove'])
DB::Aowow()->query('DELETE FROM ?_account_favorites WHERE `userId` = ?d AND `type` = ?d AND `typeId` = ?d', User::$id, $type, $typeId);
}
protected static function checkScale(string $val) : string
{
if (preg_match('/^((\w+:\d+)(,\w+:\d+)*)$/', $val))
return $val;
return '';
}
protected static function checkName(string $val) : string
{
$var = trim(urldecode($val));
return filter_var($var, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_AOWOW);
}
}
?>

View File

@@ -1,629 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxAdmin extends AjaxHandler
{
protected $validParams = ['screenshots', 'siteconfig', 'weight-presets', 'spawn-override', 'guide'];
protected $_get = array(
'action' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW ],
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkIdListUnsigned'],
'key' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxAdmin::checkKey' ],
'all' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkFulltext' ],
'type' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt' ],
'typeid' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt' ],
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxAdmin::checkUser' ],
'val' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkFulltext' ],
'guid' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt' ],
'area' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt' ],
'floor' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt' ]
);
protected $_post = array(
'alt' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW],
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt'],
'scale' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxAdmin::checkScale'],
'__icon' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxAdmin::checkKey' ],
'status' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt'],
'msg' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW]
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$this->params)
return;
if ($this->params[0] == 'screenshots' && $this->_get['action'])
{
if (!User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT))
return;
if ($this->_get['action'] == 'list')
$this->handler = 'ssList';
else if ($this->_get['action'] == 'manage')
$this->handler = 'ssManage';
else if ($this->_get['action'] == 'editalt')
$this->handler = 'ssEditAlt';
else if ($this->_get['action'] == 'approve')
$this->handler = 'ssApprove';
else if ($this->_get['action'] == 'sticky')
$this->handler = 'ssSticky';
else if ($this->_get['action'] == 'delete')
$this->handler = 'ssDelete';
else if ($this->_get['action'] == 'relocate')
$this->handler = 'ssRelocate';
}
else if ($this->params[0] == 'siteconfig' && $this->_get['action'])
{
if (!User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN))
return;
if ($this->_get['action'] == 'add')
$this->handler = 'confAdd';
else if ($this->_get['action'] == 'remove')
$this->handler = 'confRemove';
else if ($this->_get['action'] == 'update')
$this->handler = 'confUpdate';
}
else if ($this->params[0] == 'weight-presets' && $this->_get['action'])
{
if (!User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN | U_GROUP_BUREAU))
return;
if ($this->_get['action'] == 'save')
$this->handler = 'wtSave';
}
else if ($this->params[0] == 'spawn-override')
{
if (!User::isInGroup(U_GROUP_MODERATOR))
return;
$this->handler = 'spawnPosFix';
}
else if ($this->params[0] == 'guide')
{
if (!User::isInGroup(U_GROUP_STAFF))
return;
$this->handler = 'guideManage';
}
}
// get all => null (optional)
// evaled response .. UNK
protected function ssList() : string
{
// ssm_screenshotPages
// ssm_numPagesFound
$pages = CommunityContent::getScreenshotPagesForManager($this->_get['all'], $nPages);
$buff = 'ssm_screenshotPages = '.Util::toJSON($pages).";\n";
$buff .= 'ssm_numPagesFound = '.$nPages.';';
return $buff;
}
// get: [type => type, typeId => typeId] || [user => username]
// evaled response .. UNK
protected function ssManage() : string
{
$res = [];
if ($this->_get['type'] && $this->_get['type'] && $this->_get['typeid'] && $this->_get['typeid'])
$res = CommunityContent::getScreenshotsForManager($this->_get['type'], $this->_get['typeid']);
else if ($this->_get['user'])
if ($uId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE displayName = ?', $this->_get['user']))
$res = CommunityContent::getScreenshotsForManager(0, 0, $uId);
return 'ssm_screenshotData = '.Util::toJSON($res);
}
// get: id => SSid
// resp: ''
protected function ssEditAlt() : void
{
// doesn't need to be htmlEscaped, ths javascript does that
if ($this->_get['id'] && $this->_post['alt'] !== null)
DB::Aowow()->query('UPDATE ?_screenshots SET caption = ? WHERE id = ?d', trim($this->_post['alt']), $this->_get['id'][0]);
}
// get: id => comma-separated SSids
// resp: ''
protected function ssApprove() : void
{
if (!$this->reqGET('id'))
{
trigger_error('AjaxAdmin::ssApprove - screenshotId empty', E_USER_ERROR);
return;
}
// create resized and thumb version of screenshot
$resized = [772, 618];
$thumb = [150, 150];
$path = 'static/uploads/screenshots/%s/%d.jpg';
foreach ($this->_get['id'] as $id)
{
// must not be already approved
if ($ssEntry = DB::Aowow()->selectRow('SELECT userIdOwner, date, type, typeId FROM ?_screenshots WHERE (status & ?d) = 0 AND id = ?d', CC_FLAG_APPROVED, $id))
{
// should also error-log
if (!file_exists(sprintf($path, 'pending', $id)))
{
trigger_error('AjaxAdmin::ssApprove - screenshot #'.$id.' exists in db but not as file', E_USER_ERROR);
continue;
}
$srcImg = imagecreatefromjpeg(sprintf($path, 'pending', $id));
$srcW = imagesx($srcImg);
$srcH = imagesy($srcImg);
// write thumb
$scale = min(1.0, min($thumb[0] / $srcW, $thumb[1] / $srcH));
$destW = $srcW * $scale;
$destH = $srcH * $scale;
$destImg = imagecreatetruecolor($destW, $destH);
imagefill($destImg, 0, 0, imagecolorallocate($destImg, 255, 255, 255));
imagecopyresampled($destImg, $srcImg, 0, 0, 0, 0, $destW, $destH, $srcW, $srcH);
imagejpeg($destImg, sprintf($path, 'thumb', $id), 100);
// write resized (only if required)
if ($srcW > $resized[0] || $srcH > $resized[1])
{
$scale = min(1.0, min($resized[0] / $srcW, $resized[1] / $srcH));
$destW = $srcW * $scale;
$destH = $srcH * $scale;
$destImg = imagecreatetruecolor($destW, $destH);
imagefill($destImg, 0, 0, imagecolorallocate($destImg, 255, 255, 255));
imagecopyresampled($destImg, $srcImg, 0, 0, 0, 0, $destW, $destH, $srcW, $srcH);
imagejpeg($destImg, sprintf($path, 'resized', $id), 100);
}
imagedestroy($srcImg);
// move screenshot from pending to normal
rename(sprintf($path, 'pending', $id), sprintf($path, 'normal', $id));
// set as approved in DB and gain rep (once!)
DB::Aowow()->query('UPDATE ?_screenshots SET status = ?d, userIdApprove = ?d WHERE id = ?d', CC_FLAG_APPROVED, User::$id, $id);
Util::gainSiteReputation($ssEntry['userIdOwner'], SITEREP_ACTION_UPLOAD, ['id' => $id, 'what' => 1, 'date' => $ssEntry['date']]);
// flag DB entry as having screenshots
if ($tbl = Type::getClassAttrib($ssEntry['type'], 'dataTable'))
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags | ?d WHERE id = ?d', CUSTOM_HAS_SCREENSHOT, $ssEntry['typeId']);
}
else
trigger_error('AjaxAdmin::ssApprove - screenshot #'.$id.' not in db or already approved', E_USER_ERROR);
}
return;
}
// get: id => comma-separated SSids
// resp: ''
protected function ssSticky() : void
{
if (!$this->reqGET('id'))
{
trigger_error('AjaxAdmin::ssSticky - screenshotId empty', E_USER_ERROR);
return;
}
// approve soon to be sticky screenshots
$this->ssApprove();
// 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
foreach ($this->_get['id'] as $id)
{
// reset all others
DB::Aowow()->query('UPDATE ?_screenshots a, ?_screenshots b SET a.status = a.status & ~?d WHERE a.type = b.type AND a.typeId = b.typeId AND a.id <> b.id AND b.id = ?d', CC_FLAG_STICKY, $id);
// toggle sticky status
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = IF(`status` & ?d, `status` & ~?d, `status` | ?d) WHERE id = ?d AND `status` & ?d', CC_FLAG_STICKY, CC_FLAG_STICKY, CC_FLAG_STICKY, $id, CC_FLAG_APPROVED);
}
}
// get: id => comma-separated SSids
// resp: ''
// 2 steps: 1) remove from sight, 2) remove from disk
protected function ssDelete() : void
{
if (!$this->reqGET('id'))
{
trigger_error('AjaxAdmin::ssDelete - screenshotId empty', E_USER_ERROR);
return;
}
$path = 'static/uploads/screenshots/%s/%d.jpg';
foreach ($this->_get['id'] as $id)
{
// irrevocably remove already deleted files
if (User::isInGroup(U_GROUP_ADMIN) && DB::Aowow()->selectCell('SELECT 1 FROM ?_screenshots WHERE status & ?d AND id = ?d', CC_FLAG_DELETED, $id))
{
DB::Aowow()->query('DELETE FROM ?_screenshots WHERE id = ?d', $id);
if (file_exists(sprintf($path, 'pending', $id)))
unlink(sprintf($path, 'pending', $id));
continue;
}
// move pending or normal to pending
if (file_exists(sprintf($path, 'normal', $id)))
rename(sprintf($path, 'normal', $id), sprintf($path, 'pending', $id));
// remove resized and thumb
if (file_exists(sprintf($path, 'thumb', $id)))
unlink(sprintf($path, 'thumb', $id));
if (file_exists(sprintf($path, 'resized', $id)))
unlink(sprintf($path, 'resized', $id));
}
// flag as deleted if not aready
$oldEntries = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, GROUP_CONCAT(typeId) FROM ?_screenshots WHERE id IN (?a) GROUP BY `type`', $this->_get['id']);
DB::Aowow()->query('UPDATE ?_screenshots SET status = ?d, userIdDelete = ?d WHERE id IN (?a)', CC_FLAG_DELETED, User::$id, $this->_get['id']);
// deflag db entry as having screenshots
foreach ($oldEntries as $type => $typeIds)
{
$typeIds = explode(',', $typeIds);
$toUnflag = DB::Aowow()->selectCol('SELECT typeId AS ARRAY_KEY, IF(BIT_OR(`status`) & ?d, 1, 0) AS hasMore FROM ?_screenshots WHERE `type` = ?d AND typeId IN (?a) GROUP BY typeId HAVING hasMore = 0', CC_FLAG_APPROVED, $type, $typeIds);
if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable')))
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags & ~?d WHERE id IN (?a)', CUSTOM_HAS_SCREENSHOT, array_keys($toUnflag));
}
}
// get: id => ssId, typeid => typeId (but not type..?)
// resp: ''
protected function ssRelocate() : void
{
if (!$this->reqGET('id', 'typeid'))
{
trigger_error('AjaxAdmin::ssRelocate - screenshotId or typeId empty', E_USER_ERROR);
return;
}
$id = $this->_get['id'][0];
[$type, $oldTypeId] = array_values(DB::Aowow()->selectRow('SELECT type, typeId FROM ?_screenshots WHERE id = ?d', $id));
$typeId = (int)$this->_get['typeid'];
$tc = Type::newList($type, [['id', $typeId]]);
if ($tc && !$tc->error)
{
// move screenshot
DB::Aowow()->query('UPDATE ?_screenshots SET typeId = ?d WHERE id = ?d', $typeId, $id);
// flag target as having screenshot
DB::Aowow()->query('UPDATE '.$tc::$dataTable.' SET cuFlags = cuFlags | ?d WHERE id = ?d', CUSTOM_HAS_SCREENSHOT, $typeId);
// deflag source for having had screenshots (maybe)
$ssInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~status) & ?d, 1, 0) AS hasMore FROM ?_screenshots WHERE `status`& ?d AND `type` = ?d AND typeId = ?d', CC_FLAG_DELETED, CC_FLAG_APPROVED, $type, $oldTypeId);
if($ssInfo || !$ssInfo['hasMore'])
DB::Aowow()->query('UPDATE '.$tc::$dataTable.' SET cuFlags = cuFlags & ~?d WHERE id = ?d', CUSTOM_HAS_SCREENSHOT, $oldTypeId);
}
else
trigger_error('AjaxAdmin::ssRelocate - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR);
}
protected function confAdd() : string
{
$key = trim($this->_get['key']);
$val = trim(urldecode($this->_get['val']));
if ($key === null)
return 'empty option name given';
if (!strlen($key))
return 'invalid chars in option name: [a-z 0-9 _ . -] are allowed';
if (ini_get($key) === false || ini_set($key, $val) === false)
return 'this configuration option cannot be set';
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_config WHERE `flags` & ?d AND `key` = ?', CON_FLAG_PHP, $key))
return 'this configuration option is already in use';
DB::Aowow()->query('INSERT IGNORE INTO ?_config (`key`, `value`, `cat`, `flags`) VALUES (?, ?, 0, ?d)', $key, $val, CON_FLAG_TYPE_STRING | CON_FLAG_PHP);
return '';
}
protected function confRemove() : string
{
if (!$this->reqGET('key'))
return 'invalid configuration option given';
if (DB::Aowow()->query('DELETE FROM ?_config WHERE `key` = ? AND (`flags` & ?d) = 0', $this->_get['key'], CON_FLAG_PERSISTENT))
return '';
else
return 'option name is either protected or was not found';
}
protected function confUpdate() : string
{
$key = trim($this->_get['key']);
$val = trim(urldecode($this->_get['val']));
$msg = '';
if (!strlen($key))
return 'empty option name given';
$cfg = DB::Aowow()->selectRow('SELECT `flags`, `value` FROM ?_config WHERE `key` = ?', $key);
if (!$cfg)
return 'configuration option not found';
if (!($cfg['flags'] & CON_FLAG_TYPE_STRING) && !strlen($val))
return 'empty value given';
else if ($cfg['flags'] & CON_FLAG_TYPE_INT && !preg_match('/^-?\d+$/i', $val))
return "value must be integer";
else if ($cfg['flags'] & CON_FLAG_TYPE_FLOAT && !preg_match('/^-?\d*(,|.)?\d+$/i', $val))
return "value must be float";
else if ($cfg['flags'] & CON_FLAG_TYPE_BOOL && $val != '1')
$val = '0';
DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $val, $key);
if (!$this->confOnChange($key, $val, $msg))
DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $cfg['value'], $key);
return $msg;
}
protected function wtSave() : string
{
if (!$this->reqPOST('id', '__icon'))
return '3';
// save to db
DB::Aowow()->query('DELETE FROM ?_account_weightscale_data WHERE id = ?d', $this->_post['id']);
DB::Aowow()->query('UPDATE ?_account_weightscales SET `icon`= ? WHERE `id` = ?d', $this->_post['__icon'], $this->_post['id']);
foreach (explode(',', $this->_post['scale']) as $s)
{
[$k, $v] = explode(':', $s);
if (!in_array($k, Util::$weightScales) || $v < 1)
continue;
if (DB::Aowow()->query('INSERT INTO ?_account_weightscale_data VALUES (?d, ?, ?d)', $this->_post['id'], $k, $v) === null)
return '1';
}
// write dataset
exec('php aowow --build=weightPresets', $out);
foreach ($out as $o)
if (strstr($o, 'ERR'))
return '2';
// all done
return '0';
}
protected function spawnPosFix() : string
{
if (!$this->reqGET('type', 'guid', 'area', 'floor'))
return '-4';
$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]))
return '-3';
DB::Aowow()->query('REPLACE INTO ?_spawns_override VALUES (?d, ?d, ?d, ?d, ?d)', $type, $guid, $area, $floor, AOWOW_REVISION);
if ($wPos = Game::getWorldPosForGUID($type, $guid))
{
if ($point = Game::worldPosToZonePos($wPos[$guid]['mapId'], $wPos[$guid]['posX'], $wPos[$guid]['posY'], $area, $floor))
{
$updGUIDs = [$guid];
$newPos = array(
'posX' => $point[0]['posX'],
'posY' => $point[0]['posY'],
'areaId' => $point[0]['areaId'],
'floor' => $point[0]['floor']
);
// if creature try for waypoints
if ($type == Type::NPC)
{
$jobs = array(
'SELECT -w.id AS `entry`, w.point AS `pointId`, w.position_y AS `posX`, w.position_x AS `posY` FROM creature_addon ca JOIN waypoint_data w ON w.id = ca.path_id WHERE ca.guid = ?d AND ca.path_id <> 0',
'SELECT `entry`, `pointId`, `location_y` AS `posX`, `location_x` AS `posY` FROM `script_waypoint` WHERE `entry` = ?d',
'SELECT `entry`, `pointId`, `position_y` AS `posX`, `position_x` AS `posY` FROM `waypoints` WHERE `entry` = ?d'
);
foreach ($jobs as $idx => $job)
{
if ($swp = DB::World()->select($job, $idx ? $wPos[$guid]['id'] : $guid))
{
foreach ($swp as $w)
{
if ($point = Game::worldPosToZonePos($wPos[$guid]['mapId'], $w['posX'], $w['posY'], $area, $floor))
{
$p = array(
'posX' => $point[0]['posX'],
'posY' => $point[0]['posY'],
'areaId' => $point[0]['areaId'],
'floor' => $point[0]['floor']
);
}
DB::Aowow()->query('UPDATE ?_creature_waypoints SET ?a WHERE `creatureOrPath` = ?d AND `point` = ?d', $p, $w['entry'], $w['pointId']);
}
}
}
// also move linked vehicle accessories (on the very same position)
$updGUIDs = array_merge($updGUIDs, DB::Aowow()->selectCol('SELECT s2.guid FROM ?_spawns s1 JOIN ?_spawns s2 ON s1.posX = s2.posX AND s1.posY = s2.posY AND
s1.areaId = s2.areaId AND s1.floor = s2.floor AND s2.guid < 0 WHERE s1.guid = ?d', $guid));
}
DB::Aowow()->query('UPDATE ?_spawns SET ?a WHERE `type` = ?d AND `guid` IN (?a)', $newPos, $type, $updGUIDs);
return '1';
}
return '-2';
}
return '-1';
}
protected function guideManage() : string
{
$update = function (int $id, int $status, ?string $msg = null) : bool
{
if (!DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d WHERE `id` = ?d', $status, $id))
return false;
// set display rev to latest
if ($status == GUIDE_STATUS_APPROVED)
DB::Aowow()->query('UPDATE ?_guides SET `rev` = (SELECT `rev` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d ORDER BY `rev` DESC LIMIT 1) WHERE `id` = ?d', Type::GUIDE, $id, $id);
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `status`) VALUES (?d, ?d, ?d, ?d)', $id, time(), User::$id, $status);
if ($msg)
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `msg`) VALUES (?d, ?d, ?d, ?)' , $id, time(), User::$id, $msg);
return true;
};
if (!$this->_post['id'])
trigger_error('AjaxHander::guideManage - malformed request: id: '.$this->_post['id'].', status: '.$this->_post['status']);
else
{
$guide = DB::Aowow()->selectRow('SELECT `userId`, `status` FROM ?_guides WHERE `id` = ?d', $this->_post['id']);
if (!$guide)
trigger_error('AjaxHander::guideManage - guide #'.$this->_post['id'].' not found');
else
{
if ($this->_post['status'] == $guide['status'])
trigger_error('AjaxHander::guideManage - guide #'.$this->_post['id'].' already has status #'.$this->_post['status']);
else
{
if ($this->_post['status'] == GUIDE_STATUS_APPROVED)
{
if ($update($this->_post['id'], GUIDE_STATUS_APPROVED, $this->_post['msg']))
{
Util::gainSiteReputation($guide['userId'], SITEREP_ACTION_ARTICLE, ['id' => $this->_post['id']]);
return '1';
}
else
return '-2';
}
else if ($this->_post['status'] == GUIDE_STATUS_REJECTED)
return $update($this->_post['id'], GUIDE_STATUS_REJECTED, $this->_post['msg']) ? '1' : '-2';
else
trigger_error('AjaxHander::guideManage - unhandled status change request');
}
}
}
return '-1';
}
/***************************/
/* additional input filter */
/***************************/
protected static function checkKey(string $val) : string
{
// expecting string
if (preg_match('/[^a-z0-9_\.\-]/i', $val))
return '';
return strtolower($val);
}
protected static function checkUser($val) : string
{
$n = Util::lower(trim(urldecode($val)));
if (User::isValidName($n))
return $n;
return '';
}
protected static function checkScale($val) : string
{
if (preg_match('/^((\w+:\d+)(,\w+:\d+)*)$/', $val))
return $val;
return '';
}
/**********/
/* helper */
/**********/
private static function confOnChange(string $key, string $val, string &$msg) : bool
{
$fn = $buildList = null;
switch ($key)
{
case 'battlegroup':
$buildList = 'realms,realmMenu';
break;
case 'name_short':
$buildList = 'searchboxBody,demo,searchplugin';
break;
case 'site_host':
$buildList = 'searchplugin,demo,power,searchboxBody';
break;
case 'static_host':
$buildList = 'searchplugin,power,searchboxBody,searchboxScript';
break;
case 'contact_email':
$buildList = 'markup';
break;
case 'locales':
$buildList = 'locales';
$msg .= ' * remember to rebuild all static files for the language you just added.<br />';
$msg .= ' * you can speed this up by supplying the regionCode to the setup: <pre class="q1">--locales=<regionCodes,> -f</pre>';
break;
case 'profiler_enable':
$buildList = 'realms,realmMenu';
$fn = function($x) use (&$msg) {
if (!$x)
return true;
return Profiler::queueStart($msg);
};
break;
case 'acc_auth_mode':
$fn = function($x) use (&$msg) {
if ($x == 1 && !extension_loaded('gmp'))
{
$msg .= 'PHP extension GMP is required to use TrinityCore as auth source, but is not currently enabled.<br />';
return false;
}
return true;
};
break;
default: // nothing to do, everything is fine
return true;
}
if ($buildList)
{
// we need to use exec as build() can only be run from CLI
exec('php aowow --build='.$buildList, $out);
foreach ($out as $o)
if (strstr($o, 'ERR'))
$msg .= explode('0m]', $o)[1]."<br />\n";
}
return $fn ? $fn($val) : true;
}
}
?>

View File

@@ -1,82 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('invalid access');
class AjaxArenaTeam extends AjaxHandler
{
protected $validParams = ['resync', 'status'];
protected $_get = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkIdList' ],
'profile' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkEmptySet'],
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$this->params)
return;
switch ($this->params[0])
{
case 'resync':
$this->handler = 'handleResync';
break;
case 'status':
$this->handler = 'handleStatus';
break;
}
}
/* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional, not used]
profile: <empty> [optional, also get related chars]
return: 1
*/
protected function handleResync() : string
{
if ($teams = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_arena_team WHERE id IN (?a)', $this->_get['id']))
foreach ($teams as $t)
Profiler::scheduleResync(Type::ARENA_TEAM, $t['realm'], $t['realmGUID']);
if ($this->_get['profile'])
if ($chars = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_profiles p JOIN ?_profiler_arena_team_member atm ON atm.profileId = p.id WHERE atm.arenaTeamId IN (?a)', $this->_get['id']))
foreach ($chars as $c)
Profiler::scheduleResync(Type::PROFILE, $c['realm'], $c['realmGUID']);
return '1';
}
/* params
id: <prId1,prId2,..,prIdN>
return
<status object>
[
nQueueProcesses,
[statusCode, timeToRefresh, curQueuePos, errorCode, nResyncTries],
[<anotherStatus>]
...
]
not all fields are required, if zero they are omitted
statusCode:
0: end the request
1: waiting
2: working...
3: ready; click to view
4: error / retry
errorCode:
0: unk error
1: char does not exist
2: armory gone
*/
protected function handleStatus() : string
{
$response = Profiler::resyncStatus(Type::ARENA_TEAM, $this->_get['id']);
return Util::toJSON($response);
}
}
?>

View File

@@ -1,475 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxComment extends AjaxHandler
{
const COMMENT_LENGTH_MIN = 10;
const COMMENT_LENGTH_MAX = 7500;
const REPLY_LENGTH_MIN = 15;
const REPLY_LENGTH_MAX = 600;
protected $_post = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkIdListUnsigned'],
'body' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkFulltext' ],
'commentbody' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkFulltext' ],
'response' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW ],
'reason' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW ],
'remove' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'commentId' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'replyId' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'sticky' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
// 'username' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW ]
);
protected $_get = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt'],
'type' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt'],
'typeid' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt'],
'rating' => ['filter' => FILTER_SANITIZE_NUMBER_INT]
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$this->params || count($this->params) != 1)
return;
// note: return values must be formated as STRICT json!
// select handler
if ($this->params[0] == 'add')
$this->handler = 'handleCommentAdd';
else if ($this->params[0] == 'edit')
$this->handler = 'handleCommentEdit';
else if ($this->params[0] == 'delete')
$this->handler = 'handleCommentDelete';
else if ($this->params[0] == 'undelete')
$this->handler = 'handleCommentUndelete';
else if ($this->params[0] == 'rating') // up/down - distribution
$this->handler = 'handleCommentRating';
else if ($this->params[0] == 'vote') // up, down and remove
$this->handler = 'handleCommentVote';
else if ($this->params[0] == 'sticky') // toggle flag
$this->handler = 'handleCommentSticky';
else if ($this->params[0] == 'out-of-date') // toggle flag
$this->handler = 'handleCommentOutOfDate';
else if ($this->params[0] == 'show-replies')
$this->handler = 'handleCommentShowReplies';
else if ($this->params[0] == 'add-reply') // also returns all replies on success
$this->handler = 'handleReplyAdd';
else if ($this->params[0] == 'edit-reply') // also returns all replies on success
$this->handler = 'handleReplyEdit';
else if ($this->params[0] == 'detach-reply')
$this->handler = 'handleReplyDetach';
else if ($this->params[0] == 'delete-reply')
$this->handler = 'handleReplyDelete';
else if ($this->params[0] == 'flag-reply')
$this->handler = 'handleReplyFlag';
else if ($this->params[0] == 'upvote-reply')
$this->handler = 'handleReplyUpvote';
else if ($this->params[0] == 'downvote-reply')
$this->handler = 'handleReplyDownvote';
}
// 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 handleCommentAdd() : string
{
if (!$this->_get['typeid'] || !$this->_get['type'] || !Type::exists($this->_get['type']))
{
trigger_error('AjaxComment::handleCommentAdd - malforemd request received', E_USER_ERROR);
return ''; // whatever, we cant even send him back
}
// this type cannot be commented on
if (!Type::checkClassAttrib($this->_get['type'], 'contribute', CONTRIBUTE_CO))
{
trigger_error('AjaxComment::handleCommentAdd - tried to comment on unsupported type #'.$this->_get['type'], E_USER_ERROR);
return '';
}
// trim to max length
if (!User::isInGroup(U_GROUP_MODERATOR) && mb_strlen($this->_post['commentbody']) > (self::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)))
$this->_post['commentbody'] = mb_substr($this->_post['commentbody'], 0, (self::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)));
if (User::canComment())
{
if (!empty($this->_post['commentbody']) && mb_strlen($this->_post['commentbody']) >= self::COMMENT_LENGTH_MIN)
{
if ($postIdx = DB::Aowow()->query('INSERT INTO ?_comments (type, typeId, userId, roles, body, date) VALUES (?d, ?d, ?d, ?d, ?, UNIX_TIMESTAMP())', $this->_get['type'], $this->_get['typeid'], User::$id, User::$groups, $this->_post['commentbody']))
{
Util::gainSiteReputation(User::$id, SITEREP_ACTION_COMMENT, ['id' => $postIdx]);
// every comment starts with a rating of +1 and i guess the simplest thing to do is create a db-entry with the system as owner
DB::Aowow()->query('INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, 0, 1)', RATING_COMMENT, $postIdx);
// flag target with hasComment
if ($tbl = Type::getClassAttrib($this->_get['type'], 'dataTable'))
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags | ?d WHERE id = ?d', CUSTOM_HAS_COMMENT, $this->_get['typeid']);
}
else
{
$_SESSION['error']['co'] = Lang::main('intError');
trigger_error('AjaxComment::handleCommentAdd - write to db failed', E_USER_ERROR);
}
}
else
$_SESSION['error']['co'] = Lang::main('textLength', [mb_strlen($this->_post['commentbody']), self::COMMENT_LENGTH_MIN, self::COMMENT_LENGTH_MAX]);
}
else
$_SESSION['error']['co'] = Lang::main('cannotComment');
$this->doRedirect = true;
$idOrUrl = $this->_get['typeid'];
if ($this->_get['type'] == Type::GUIDE)
if ($_ = DB::Aowow()->selectCell('SELECT `url` FROM ?_guides WHERE `id` = ?d', $this->_get['typeid']))
$idOrUrl = $_;
return '?'.Type::getFileString($this->_get['type']).'='.$idOrUrl.'#comments';
}
protected function handleCommentEdit() : void
{
if (!User::canComment() && !User::isInGroup(U_GROUP_MODERATOR))
{
trigger_error('AjaxComment::handleCommentEdit - user #'.User::$id.' not allowed to edit', E_USER_ERROR);
return;
}
if (!$this->_get['id'] || !$this->_post['body'])
{
trigger_error('AjaxComment::handleCommentEdit - malforemd request received', E_USER_ERROR);
return;
}
if (mb_strlen($this->_post['body']) < self::COMMENT_LENGTH_MIN)
return; // no point in reporting this trifle
// trim to max length
if (!User::isInGroup(U_GROUP_MODERATOR) && mb_strlen($this->_post['body']) > (self::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)))
$this->_post['body'] = mb_substr($this->_post['body'], 0, (self::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)));
$update = array(
'body' => $this->_post['body'],
'editUserId' => User::$id,
'editDate' => time()
);
if (User::isInGroup(U_GROUP_MODERATOR))
{
$update['responseBody'] = !$this->_post['response'] ? '' : $this->_post['response'];
$update['responseUserId'] = !$this->_post['response'] ? 0 : User::$id;
$update['responseRoles'] = !$this->_post['response'] ? 0 : User::$groups;
}
DB::Aowow()->query('UPDATE ?_comments SET editCount = editCount + 1, ?a WHERE id = ?d', $update, $this->_get['id']);
}
protected function handleCommentDelete() : void
{
if (!$this->_post['id'] || !User::$id)
{
trigger_error('AjaxComment::handleCommentDelete - commentId empty or user not logged in', E_USER_ERROR);
return;
}
// in theory, there is a username passed alongside... lets just use the current user (see user.js)
$ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags | ?d, deleteUserId = ?d, deleteDate = UNIX_TIMESTAMP() WHERE id IN (?a){ AND userId = ?d}',
CC_FLAG_DELETED,
User::$id,
$this->_post['id'],
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id
);
// deflag hasComment
if ($ok)
{
$coInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~b.flags) & ?d, 1, 0) as hasMore, b.type, b.typeId FROM ?_comments a JOIN ?_comments b ON a.type = b.type AND a.typeId = b.typeId WHERE a.id = ?d',
CC_FLAG_DELETED,
$this->_post['id']
);
if (!$coInfo['hasMore'] && ($tbl = Type::getClassAttrib($coInfo['type'], 'dataTable')))
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags & ~?d WHERE id = ?d', CUSTOM_HAS_COMMENT, $coInfo['typeId']);
}
else
trigger_error('AjaxComment::handleCommentDelete - user #'.User::$id.' could not flag comment #'.$this->_post['id'].' as deleted', E_USER_ERROR);
}
protected function handleCommentUndelete() : void
{
if (!$this->_post['id'] || !User::$id)
{
trigger_error('AjaxComment::handleCommentUndelete - commentId empty or user not logged in', E_USER_ERROR);
return;
}
// in theory, there is a username passed alongside... lets just use the current user (see user.js)
$ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags & ~?d WHERE id IN (?a){ AND userId = deleteUserId AND deleteUserId = ?d}',
CC_FLAG_DELETED,
$this->_post['id'],
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id
);
// reflag hasComment
if ($ok)
{
$coInfo = DB::Aowow()->selectRow('SELECT type, typeId FROM ?_comments WHERE id = ?d', $this->_post['id']);
if ($tbl = Type::getClassAttrib($coInfo['type'], 'dataTable'))
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags | ?d WHERE id = ?d', CUSTOM_HAS_COMMENT, $coInfo['typeId']);
}
else
trigger_error('AjaxComment::handleCommentUndelete - user #'.User::$id.' could not unflag comment #'.$this->_post['id'].' as deleted', E_USER_ERROR);
}
protected function handleCommentRating() : string
{
if (!$this->_get['id'])
return Util::toJSON(['success' => 0]);
if ($votes = DB::Aowow()->selectRow('SELECT 1 AS success, SUM(IF(`value` > 0, `value`, 0)) AS up, SUM(IF(`value` < 0, -`value`, 0)) AS down FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND userId <> 0 GROUP BY `entry`', RATING_COMMENT, $this->_get['id']))
return Util::toJSON($votes);
else
return Util::toJSON(['success' => 1, 'up' => 0, 'down' => 0]);
}
protected function handleCommentVote() : string
{
if (!User::$id || !$this->_get['id'] || !$this->_get['rating'])
return Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
$target = DB::Aowow()->selectRow('SELECT c.`userId` AS owner, ur.`value` FROM ?_comments c LEFT JOIN ?_user_ratings ur ON ur.`type` = ?d AND ur.`entry` = c.id AND ur.`userId` = ?d WHERE c.id = ?d', RATING_COMMENT, User::$id, $this->_get['id']);
$val = User::canSupervote() ? 2 : 1;
if ($this->_get['rating'] < 0)
$val *= -1;
if (User::getCurDailyVotes() <= 0)
return Util::toJSON(['error' => 1, 'message' => Lang::main('tooManyVotes')]);
else if (!$target || $val != $this->_get['rating'])
return Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
else if (($val > 0 && !User::canUpvote()) || ($val < 0 && !User::canDownvote()))
return Util::toJSON(['error' => 1, 'message' => Lang::main('bannedRating')]);
$ok = false;
// old and new have same sign; undo vote (user may have gained/lost access to superVote in the meantime)
if ($target['value'] && ($target['value'] < 0) == ($val < 0))
$ok = DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND `userId` = ?d', RATING_COMMENT, $this->_get['id'], User::$id);
else // replace, because we may be overwriting an old, opposing vote
if ($ok = DB::Aowow()->query('REPLACE INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)', RATING_COMMENT, (int)$this->_get['id'], User::$id, $val))
User::decrementDailyVotes(); // do not refund retracted votes!
if (!$ok)
return Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
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]);
return Util::toJSON(['error' => 0]);
}
protected function handleCommentSticky() : void
{
if (!$this->_post['id'] || !User::isInGroup(U_GROUP_MODERATOR))
{
trigger_error('AjaxComment::handleCommentSticky - commentId empty or user #'.User::$id.' not moderator', E_USER_ERROR);
return;
}
if ($this->_post['sticky'])
DB::Aowow()->query('UPDATE ?_comments SET flags = flags | ?d WHERE id = ?d', CC_FLAG_STICKY, $this->_post['id'][0]);
else
DB::Aowow()->query('UPDATE ?_comments SET flags = flags & ~?d WHERE id = ?d', CC_FLAG_STICKY, $this->_post['id'][0]);
}
protected function handleCommentOutOfDate() : string
{
$this->contentType = MIME_TYPE_TEXT;
if (!$this->_post['id'])
{
trigger_error('AjaxComment::handleCommentOutOfDate - commentId empty', E_USER_ERROR);
return Lang::main('intError');
}
$ok = false;
if (User::isInGroup(U_GROUP_MODERATOR)) // directly mark as outdated
{
if (!$this->_post['remove'])
$ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags | 0x4 WHERE id = ?d', $this->_post['id'][0]);
else
$ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags & ~0x4 WHERE id = ?d', $this->_post['id'][0]);
}
else if (DB::Aowow()->selectCell('SELECT 1 FROM ?_reports WHERE `mode` = ?d AND `reason`= ?d AND `subject` = ?d AND `userId` = ?d', 1, 17, $this->_post['id'][0], User::$id))
return Lang::main('alreadyReport');
else if (User::$id && !$this->_post['reason'] || mb_strlen($this->_post['reason']) < self::REPLY_LENGTH_MIN)
return Lang::main('textTooShort');
else if (User::$id) // only report as outdated
$ok = Util::createReport(1, 17, $this->_post['id'][0], '[Outdated Comment] '.$this->_post['reason']);
if ($ok) // this one is very special; as in: completely retarded
return 'ok'; // the script expects the actual characters 'ok' not some string like "ok"
else
trigger_error('AjaxComment::handleCommentOutOfDate - failed to update comment in db', E_USER_ERROR);
return Lang::main('intError');
}
protected function handleCommentShowReplies() : string
{
return Util::toJSON(!$this->_get['id'] ? [] : CommunityContent::getCommentReplies($this->_get['id']));
}
protected function handleReplyAdd() : string
{
$this->contentType = MIME_TYPE_TEXT;
if (!User::canComment())
return Lang::main('cannotComment');
if (!$this->_post['commentId'] || !DB::Aowow()->selectCell('SELECT 1 FROM ?_comments WHERE id = ?d', $this->_post['commentId']))
{
trigger_error('AjaxComment::handleReplyAdd - comment #'.$this->_post['commentId'].' does not exist', E_USER_ERROR);
return Lang::main('intError');
}
if (!$this->_post['body'] || mb_strlen($this->_post['body']) < self::REPLY_LENGTH_MIN || mb_strlen($this->_post['body']) > self::REPLY_LENGTH_MAX)
return Lang::main('textLength', [mb_strlen($this->_post['body']), self::REPLY_LENGTH_MIN, self::REPLY_LENGTH_MAX]);
if (DB::Aowow()->query('INSERT INTO ?_comments (`userId`, `roles`, `body`, `date`, `replyTo`) VALUES (?d, ?d, ?, UNIX_TIMESTAMP(), ?d)', User::$id, User::$groups, $this->_post['body'], $this->_post['commentId']))
return Util::toJSON(CommunityContent::getCommentReplies($this->_post['commentId']));
trigger_error('AjaxComment::handleReplyAdd - write to db failed', E_USER_ERROR);
return Lang::main('intError');
}
protected function handleReplyEdit() : string
{
$this->contentType = MIME_TYPE_TEXT;
if (!User::canComment())
return Lang::main('cannotComment');
if ((!$this->_post['replyId'] || !$this->_post['commentId']) && DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_comments WHERE id IN (?a)', [$this->_post['replyId'], $this->_post['commentId']]))
{
trigger_error('AjaxComment::handleReplyEdit - comment #'.$this->_post['commentId'].' or reply #'.$this->_post['replyId'].' does not exist', E_USER_ERROR);
return Lang::main('intError');
}
if (!$this->_post['body'] || mb_strlen($this->_post['body']) < self::REPLY_LENGTH_MIN || mb_strlen($this->_post['body']) > self::REPLY_LENGTH_MAX)
return Lang::main('textLength', [mb_strlen($this->_post['body']), self::REPLY_LENGTH_MIN, self::REPLY_LENGTH_MAX]);
if (DB::Aowow()->query('UPDATE ?_comments SET body = ?, editUserId = ?d, editDate = UNIX_TIMESTAMP(), editCount = editCount + 1 WHERE id = ?d AND replyTo = ?d{ AND userId = ?d}',
$this->_post['body'], User::$id, $this->_post['replyId'], $this->_post['commentId'], User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id))
return Util::toJSON(CommunityContent::getCommentReplies($this->_post['commentId']));
trigger_error('AjaxComment::handleReplyEdit - write to db failed', E_USER_ERROR);
return Lang::main('intError');
}
protected function handleReplyDetach() : void
{
if (!$this->_post['id'] || !User::isInGroup(U_GROUP_MODERATOR))
{
trigger_error('AjaxComment::handleReplyDetach - commentId empty or user #'.User::$id.' not moderator', E_USER_ERROR);
return;
}
DB::Aowow()->query('UPDATE ?_comments c1, ?_comments c2 SET c1.replyTo = 0, c1.type = c2.type, c1.typeId = c2.typeId WHERE c1.replyTo = c2.id AND c1.id = ?d', $this->_post['id'][0]);
}
protected function handleReplyDelete() : void
{
if (!User::$id || !$this->_post['id'])
{
trigger_error('AjaxComment::handleReplyDelete - commentId empty or user not logged in', E_USER_ERROR);
return;
}
if (DB::Aowow()->query('DELETE FROM ?_comments WHERE id = ?d{ AND userId = ?d}', $this->_post['id'][0], User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id))
DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d', RATING_COMMENT, $this->_post['id'][0]);
else
trigger_error('AjaxComment::handleReplyDelete - deleting comment #'.$this->_post['id'][0].' by user #'.User::$id.' from db failed', E_USER_ERROR);
}
protected function handleReplyFlag() : void
{
if (!User::$id || !$this->_post['id'])
{
trigger_error('AjaxComment::handleReplyFlag - commentId empty or user not logged in', E_USER_ERROR);
return;
}
Util::createReport(1, 19, $this->_post['id'][0], '[General Reply Report]');
}
protected function handleReplyUpvote() : void
{
if (!$this->_post['id'] || !User::canUpvote())
{
trigger_error('AjaxComment::handleReplyUpvote - commentId empty or user not allowed to vote', E_USER_ERROR);
return;
}
$owner = DB::Aowow()->selectCell('SELECT userId FROM ?_comments WHERE id = ?d', $this->_post['id'][0]);
if (!$owner)
{
trigger_error('AjaxComment::handleReplyUpvote - comment #'.$this->_post['id'][0].' not found in db', E_USER_ERROR);
return;
}
$ok = DB::Aowow()->query(
'INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)',
RATING_COMMENT,
$this->_post['id'][0],
User::$id,
User::canSupervote() ? 2 : 1
);
if ($ok)
{
Util::gainSiteReputation($owner, SITEREP_ACTION_UPVOTED, ['id' => $this->_post['id'][0], 'voterId' => User::$id]);
User::decrementDailyVotes();
}
else
trigger_error('AjaxComment::handleReplyUpvote - write to db failed', E_USER_ERROR);
}
protected function handleReplyDownvote() : void
{
if (!$this->_post['id'] || !User::canDownvote())
{
trigger_error('AjaxComment::handleReplyDownvote - commentId empty or user not allowed to vote', E_USER_ERROR);
return;
}
$owner = DB::Aowow()->selectCell('SELECT userId FROM ?_comments WHERE id = ?d', $this->_post['id'][0]);
if (!$owner)
{
trigger_error('AjaxComment::handleReplyDownvote - comment #'.$this->_post['id'][0].' not found in db', E_USER_ERROR);
return;
}
$ok = DB::Aowow()->query(
'INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)',
RATING_COMMENT,
$this->_post['id'][0],
User::$id,
User::canSupervote() ? -2 : -1
);
if ($ok)
{
Util::gainSiteReputation($owner, SITEREP_ACTION_DOWNVOTED, ['id' => $this->_post['id'][0], 'voterId' => User::$id]);
User::decrementDailyVotes();
}
else
trigger_error('AjaxComment::handleReplyDownvote - write to db failed', E_USER_ERROR);
}
}
?>

View File

@@ -1,93 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxContactus extends AjaxHandler
{
protected $_post = array(
'mode' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'reason' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'ua' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW],
'appname' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW],
'page' => ['filter' => FILTER_SANITIZE_URL],
'desc' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW],
'id' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'relatedurl' => ['filter' => FILTER_SANITIZE_URL],
'email' => ['filter' => FILTER_SANITIZE_EMAIL]
);
public function __construct(array $params)
{
parent::__construct($params);
// always this one
$this->handler = 'handleContactUs';
}
/* responses
0: success
1: captcha invalid
2: description too long
3: reason missing
7: already reported
$: prints response
*/
protected function handleContactUs() : string
{
$mode = $this->_post['mode'];
$rsn = $this->_post['reason'];
$ua = $this->_post['ua'];
$app = $this->_post['appname'];
$url = $this->_post['page'];
$desc = $this->_post['desc'];
$subj = $this->_post['id'];
$contexts = array(
[1, 2, 3, 4, 5, 6, 7, 8],
[15, 16, 17, 18, 19, 20],
[30, 31, 32, 33, 34, 35, 36, 37],
[45, 46, 47, 48],
[60, 61],
[45, 46, 47, 48],
[45, 46, 48]
);
if ($mode === null || $rsn === null || $ua === null || $app === null || $url === null)
{
trigger_error('AjaxContactus::handleContactUs - malformed contact request received', E_USER_ERROR);
return Lang::main('intError');
}
if (!isset($contexts[$mode]) || !in_array($rsn, $contexts[$mode]))
{
trigger_error('AjaxContactus::handleContactUs - report has invalid context (mode:'.$mode.' / reason:'.$rsn.')', E_USER_ERROR);
return Lang::main('intError');
}
if (!$desc)
return 3;
if (mb_strlen($desc) > 500)
return 2;
if (!User::$id && !User::$ip)
{
trigger_error('AjaxContactus::handleContactUs - could not determine IP for anonymous user', E_USER_ERROR);
return Lang::main('intError');
}
// check already reported
$field = User::$id ? 'userId' : 'ip';
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_reports WHERE `mode` = ?d AND `reason`= ?d AND `subject` = ?d AND ?# = ?', $mode, $rsn, $subj, $field, User::$id ?: User::$ip))
return 7;
if (Util::createReport($mode, $rsn, $subj, $desc, $ua, $app, $url, $this->_post['relatedurl'], $this->_post['email']))
return 0;
trigger_error('AjaxContactus::handleContactUs - write to db failed', E_USER_ERROR);
return Lang::main('intError');
}
}
?>

View File

@@ -1,45 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxCookie extends AjaxHandler
{
public function __construct(array $params)
{
// note that parent::__construct has to come after this
if (!$params || !User::$id)
return;
$this->_get = array(
$params[0] => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW],
);
// NOW we know, what to expect and sanitize
parent::__construct($params);
// always this one
$this->handler = 'handleCookie';
}
/* responses
0: success
$: silent error
*/
protected function handleCookie() : string
{
if (User::$id && $this->params && $this->_get[$this->params[0]])
{
if (DB::Aowow()->query('REPLACE INTO ?_account_cookies VALUES (?d, ?, ?)', User::$id, $this->params[0], $this->_get[$this->params[0]]))
return '0';
else
trigger_error('AjaxCookie::handleCookie - write to db failed', E_USER_ERROR);
}
else
trigger_error('AjaxCookie::handleCookie - malformed request received', E_USER_ERROR);
return '';
}
}
?>

View File

@@ -1,142 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxData extends AjaxHandler
{
protected $_get = array(
'locale' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkLocale'],
't' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW ],
'catg' => ['filter' => FILTER_SANITIZE_NUMBER_INT ],
'skill' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxData::checkSkill' ],
'class' => ['filter' => FILTER_SANITIZE_NUMBER_INT ],
'callback' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxData::checkCallback' ]
);
public function __construct(array $params)
{
parent::__construct($params);
if (is_numeric($this->_get['locale']))
User::useLocale($this->_get['locale']);
// always this one
$this->handler = 'handleData';
}
/* responses
<string>
*/
protected function handleData() : string
{
$result = '';
// different data can be strung together
foreach ($this->params as $set)
{
// requires valid token to hinder automated access
if ($set != 'item-scaling' && (!$this->_get['t'] || empty($_SESSION['dataKey']) || $this->_get['t'] != $_SESSION['dataKey']))
{
trigger_error('AjaxData::handleData - session data key empty or expired', E_USER_ERROR);
continue;
}
switch ($set)
{
/* issue on no initial data:
when we loadOnDemand, the jScript tries to generate the catg-tree before it is initialized
it cant be initialized, without loading the data as empty catg are omitted
loading the data triggers the generation of the catg-tree
*/
case 'factions':
$result .= $this->loadProfilerData($set);
break;
case 'companions':
$result .= $this->loadProfilerData($set, '778');
break;
case 'mounts':
$result .= $this->loadProfilerData($set, '777');
break;
case 'quests':
// &partial: im not doing this right
// it expects a full quest dump on first lookup but will query subCats again if clicked..?
// for now omiting the detail clicks with empty results and just set catg update
$catg = isset($this->_get['catg']) ? $this->_get['catg'] : 'null';
if ($catg == 'null')
$result .= $this->loadProfilerData($set);
else if ($this->_get['callback'])
$result .= "\n\$WowheadProfiler.loadOnDemand('quests', ".$catg.");\n";
break;
case 'recipes':
if (!$this->_get['callback'] || !$this->_get['skill'])
break;
foreach ($this->_get['skill'] as $s)
Util::loadStaticFile('p-recipes-'.$s, $result, true);
Util::loadStaticFile('p-recipes-sec', $result, true);
$result .= "\n\$WowheadProfiler.loadOnDemand('recipes', null);\n";
break;
// locale independant
case 'quick-excludes':
case 'zones':
case 'weight-presets':
case 'item-scaling':
case 'realms':
case 'statistics':
if (!Util::loadStaticFile($set, $result) && CFG_DEBUG)
$result .= "alert('could not fetch static data: ".$set."');";
$result .= "\n\n";
break;
// localized
case 'talents':
if ($_ = $this->_get['class'])
$set .= "-".$_;
case 'achievements':
case 'pet-talents':
case 'glyphs':
case 'gems':
case 'enchants':
case 'itemsets':
case 'pets':
if (!Util::loadStaticFile($set, $result, true) && CFG_DEBUG)
$result .= "alert('could not fetch static data: ".$set." for locale: ".User::$localeString."');";
$result .= "\n\n";
break;
default:
trigger_error('AjaxData::handleData - invalid file "'.$set.'" in request', E_USER_ERROR);
break;
}
}
return $result;
}
protected static function checkSkill(string $val) : array
{
return array_intersect([171, 164, 333, 202, 182, 773, 755, 165, 186, 393, 197, 185, 129, 356], explode(',', $val));
}
protected static function checkCallback(string $val) : bool
{
return substr($val, 0, 29) === '$WowheadProfiler.loadOnDemand';
}
private function loadProfilerData(string $file, string $catg = 'null') : string
{
$result = '';
if ($this->_get['callback'])
if (Util::loadStaticFile('p-'.$file, $result, true))
$result .= "\n\$WowheadProfiler.loadOnDemand('".$file."', ".$catg.");\n";
return $result;
}
}
?>

View File

@@ -1,80 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxEdit extends AjaxHandler
{
protected $_get = array(
'qqfile' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW],
'guide' => ['filter' => FILTER_SANITIZE_NUMBER_INT]
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$params)
return;
if ($params[0] == 'image')
$this->handler = 'handleUpload';
else if ($params[0] == 'article') // has it's own editor page
$this->handler = null;
}
/*
success: bool
id: image enumerator
type: 3 ? png : jpg
name: old filename
error: errString
*/
protected function handleUpload() : string
{
if (!User::$id || $this->_get['guide'] != 1)
return Util::toJSON(['success' => false, 'error' => '']);
require_once('includes/libs/qqFileUploader.class.php');
$targetPath = 'static/uploads/guide/images/';
$tmpPath = 'static/uploads/temp/';
$tmpFile = User::$displayName.'-'.Type::GUIDE.'-0-'.Util::createHash(16);
$uploader = new qqFileUploader(['jpg', 'jpeg', 'png'], 10 * 1024 * 1024);
$result = $uploader->handleUpload($tmpPath, $tmpFile, true);
if (isset($result['success']))
{
$finfo = new finfo(FILEINFO_MIME);
$mime = $finfo->file($tmpPath.$result['newFilename']);
if (preg_match('/^image\/(png|jpe?g)/i', $mime, $m))
{
$i = 1; // image index
if ($files = scandir($targetPath, SCANDIR_SORT_DESCENDING))
if (rsort($files, SORT_NATURAL) && $files[0] != '.' && $files[0] != '..')
$i = explode('.', $files[0])[0] + 1;
$targetFile = $i . ($m[1] == 'png' ? '.png' : '.jpg');
// move to final location
if (!rename($tmpPath.$result['newFilename'], $targetPath.$targetFile))
return Util::toJSON(['error' => Lang::main('intError')]);
// send success
return Util::toJSON(array(
'success' => true,
'id' => $i,
'type' => $m[1] == 'png' ? 3 : 2,
'name' => $this->_get['qqfile']
));
}
return Util::toJSON(['error' => Lang::screenshot('error', 'unkFormat')]);
}
return Util::toJSON($result);
}
}
?>

View File

@@ -1,111 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxFilter extends AjaxHandler
{
public $doRedirect = true;
private $cat = [];
private $page = '';
private $filter = null;
public function __construct(array $params)
{
if (!$params)
return;
$p = explode('=', $params[0]);
$this->page = $p[0];
if (isset($p[1]))
$this->cat[] = $p[1];
if (count($params) > 1)
for ($i = 1; $i < count($params); $i++)
$this->cat[] = $params[$i];
$opts = ['parentCats' => $this->cat];
switch ($p[0])
{
case 'achievements':
$this->filter = (new AchievementListFilter(true, $opts));
break;
case 'areatriggers':
$this->filter = (new AreaTriggerListFilter(true, $opts));
break;
case 'enchantments':
$this->filter = (new EnchantmentListFilter(true, $opts));
break;
case 'icons':
$this->filter = (new IconListFilter(true, $opts));
break;
case 'items':
$this->filter = (new ItemListFilter(true, $opts));
break;
case 'itemsets':
$this->filter = (new ItemsetListFilter(true, $opts));
break;
case 'npcs':
$this->filter = (new CreatureListFilter(true, $opts));
break;
case 'objects':
$this->filter = (new GameObjectListFilter(true, $opts));
break;
case 'quests':
$this->filter = (new QuestListFilter(true, $opts));
break;
case 'sounds':
$this->filter = (new SoundListFilter(true, $opts));
break;
case 'spells':
$this->filter = (new SpellListFilter(true, $opts));
break;
case 'profiles':
$this->filter = (new ProfileListFilter(true, $opts));
break;
case 'guilds':
$this->filter = (new GuildListFilter(true, $opts));
break;
case 'arena-teams':
$this->filter = (new ArenaTeamListFilter(true, $opts));
break;
default:
return;
}
parent::__construct($params);
// always this one
$this->handler = 'handleFilter';
}
protected function handleFilter() : string
{
$url = '?'.$this->page;
$this->filter->mergeCat($this->cat);
if ($this->cat)
$url .= '='.implode('.', $this->cat);
$fi = [];
if ($x = $this->filter->getFilterString())
$url .= '&filter='.$x;
if ($this->filter->error)
$_SESSION['fiError'] = get_class($this->filter);
if ($fi)
$url .= '&filter='.implode(';', $fi);
// do get request
return $url;
}
}
?>

View File

@@ -1,35 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxGetdescription extends AjaxHandler
{
protected $_post = array(
'description' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkFulltext']]
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$params || $params[0]) // should be empty
return;
$this->handler = 'handleDescription';
}
protected function handleDescription() : string
{
$this->contentType = MIME_TYPE_TEXT;
if (!User::$id)
return '';
$desc = (new Markup($this->_post['description']))->stripTags();
return Lang::trimTextClean($desc, 120);
}
}
?>

View File

@@ -1,38 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxGotocomment extends AjaxHandler
{
protected $_get = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkInt']
);
public function __construct(array $params)
{
parent::__construct($params);
// always this one
$this->handler = 'handleGoToComment';
$this->doRedirect = true;
}
/* responses
header()
*/
protected function handleGoToComment() : string
{
if (!$this->_get['id'])
return '.'; // go home
if ($_ = DB::Aowow()->selectRow('SELECT IFNULL(c2.id, c1.id) AS id, IFNULL(c2.type, c1.type) AS type, IFNULL(c2.typeId, c1.typeId) AS typeId FROM ?_comments c1 LEFT JOIN ?_comments c2 ON c1.replyTo = c2.id WHERE c1.id = ?d', $this->_get['id']))
return '?'.Type::getFileString(intVal($_['type'])).'='.$_['typeId'].'#comments:id='.$_['id'].($_['id'] != $this->_get['id'] ? ':reply='.$this->_get['id'] : null);
else
trigger_error('AjaxGotocomment::handleGoToComment - could not find comment #'.$this->get['id'], E_USER_ERROR);
return '.';
}
}
?>

View File

@@ -1,61 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxGuide extends AjaxHandler
{
protected $_post = array(
'id' => [FILTER_SANITIZE_NUMBER_INT, null],
'rating' => [FILTER_SANITIZE_NUMBER_INT, null]
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$this->params || count($this->params) != 1)
return;
$this->contentType = MIME_TYPE_TEXT;
// select handler
if ($this->params[0] == 'vote')
$this->handler = 'voteGuide';
}
protected function voteGuide() : string
{
if (!$this->_post['id'] || $this->_post['rating'] < 0 || $this->_post['rating'] > 5)
{
header('HTTP/1.0 404 Not Found', true, 404);
return '';
}
else if (!User::canUpvote() || !User::canDownvote()) // same logic as comments?
{
header('HTTP/1.0 403 Forbidden', true, 403);
return '';
}
// by id, not own, published
if ($g = DB::Aowow()->selectRow('SELECT `userId`, `cuFlags` FROM ?_guides WHERE `id` = ?d AND (`status` = ?d OR `rev` > 0)', $this->_post['id'], GUIDE_STATUS_APPROVED))
{
if ($g['cuFlags'] & GUIDE_CU_NO_RATING || $g['userId'] == User::$id)
{
header('HTTP/1.0 403 Forbidden', true, 403);
return '';
}
if (!$this->_post['rating'])
DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND `userId` = ?d', RATING_GUIDE, $this->_post['id'], User::$id);
else
DB::Aowow()->query('REPLACE INTO ?_user_ratings VALUES (?d, ?d, ?d, ?d)', RATING_GUIDE, $this->_post['id'], User::$id, $this->_post['rating']);
$res = DB::Aowow()->selectRow('SELECT IFNULL(SUM(`value`), 0) AS `t`, IFNULL(COUNT(*), 0) AS `n` FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d', RATING_GUIDE, $this->_post['id']);
return Util::toJSON($res['n'] ? ['rating' => $res['t'] / $res['n'], 'nvotes' => $res['n']] : ['rating' => 0, 'nvotes' => 0]);
}
return Util::toJSON(['rating' => 0, 'nvotes' => 0]);
}
}
?>

View File

@@ -1,82 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('invalid access');
class AjaxGuild extends AjaxHandler
{
protected $validParams = ['resync', 'status'];
protected $_get = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkIdList' ],
'profile' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkEmptySet'],
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$this->params)
return;
switch ($this->params[0])
{
case 'resync':
$this->handler = 'handleResync';
break;
case 'status':
$this->handler = 'handleStatus';
break;
}
}
/* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional, not used]
profile: <empty> [optional, also get related chars]
return: 1
*/
protected function handleResync() : string
{
if ($guilds = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_guild WHERE id IN (?a)', $this->_get['id']))
foreach ($guilds as $g)
Profiler::scheduleResync(Type::GUILD, $g['realm'], $g['realmGUID']);
if ($this->_get['profile'])
if ($chars = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_profiles WHERE guild IN (?a)', $this->_get['id']))
foreach ($chars as $c)
Profiler::scheduleResync(Type::PROFILE, $c['realm'], $c['realmGUID']);
return '1';
}
/* params
id: <prId1,prId2,..,prIdN>
return
<status object>
[
nQueueProcesses,
[statusCode, timeToRefresh, curQueuePos, errorCode, nResyncTries],
[<anotherStatus>]
...
]
not all fields are required, if zero they are omitted
statusCode:
0: end the request
1: waiting
2: working...
3: ready; click to view
4: error / retry
errorCode:
0: unk error
1: char does not exist
2: armory gone
*/
protected function handleStatus() : string
{
$response = Profiler::resyncStatus(Type::GUILD, $this->_get['id']);
return Util::toJSON($response);
}
}
?>

View File

@@ -1,33 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxLocale extends AjaxHandler
{
protected $_get = array(
'locale' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkLocale']
);
public function __construct(array $params)
{
parent::__construct($params);
// always this one
$this->handler = 'handleLocale';
$this->doRedirect = true;
}
/* responses
header()
*/
protected function handleLocale() : string
{
User::setLocale($this->_get['locale']);
User::save();
return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '.';
}
}
?>

View File

@@ -1,767 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxProfile extends AjaxHandler
{
private $undo = false;
protected $validParams = ['link', 'unlink', 'pin', 'unpin', 'public', 'private', 'avatar', 'resync', 'status', 'save', 'delete', 'purge', 'summary', 'load'];
protected $_get = array(
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkIdList' ],
'items' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxProfile::checkItemList'],
'size' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW ],
'guild' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkEmptySet'],
'arena-team' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkEmptySet'],
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxProfile::checkUser' ]
);
protected $_post = array(
'name' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkFulltext'],
'level' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'class' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'race' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'gender' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'nomodel' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'talenttree1' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'talenttree2' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'talenttree3' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'activespec' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'talentbuild1' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW],
'glyphs1' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW],
'talentbuild2' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW],
'glyphs2' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW],
'icon' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW],
'description' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkFulltext'],
'source' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'copy' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'public' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'gearscore' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
'inv' => ['filter' => FILTER_CALLBACK, 'options' => 'AjaxHandler::checkIdListUnsigned', 'flags' => FILTER_REQUIRE_ARRAY],
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$this->params)
return;
if (!CFG_PROFILER_ENABLE)
return;
switch ($this->params[0])
{
case 'unlink':
$this->undo = true;
case 'link':
$this->handler = 'handleLink'; // always returns null
break;
case 'unpin':
$this->undo = true;
case 'pin':
$this->handler = 'handlePin'; // always returns null
break;
case 'private':
$this->undo = true;
case 'public':
$this->handler = 'handlePrivacy'; // always returns null
break;
case 'avatar':
$this->handler = 'handleAvatar'; // sets an image header
break; // so it has to die here or another header will be set
case 'resync':
$this->handler = 'handleResync'; // always returns "1"
break;
case 'status':
$this->handler = 'handleStatus'; // returns status object
break;
case 'save':
$this->handler = 'handleSave';
break;
case 'delete':
$this->handler = 'handleDelete';
break;
case 'purge':
$this->handler = 'handlePurge';
break;
case 'summary': // page is generated by jScript
die(); // just be empty
case 'load':
$this->handler = 'handleLoad';
break;
}
}
/* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional]
return: null
*/
protected function handleLink() : void // links char with account
{
if (!User::$id || empty($this->_get['id']))
{
trigger_error('AjaxProfile::handleLink - profileId empty or user not logged in', E_USER_ERROR);
return;
}
$uid = User::$id;
if ($this->_get['user'] && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
{
if (!($uid = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE user = ?', $this->_get['user'])))
{
trigger_error('AjaxProfile::handleLink - user "'.$this->_get['user'].'" does not exist', E_USER_ERROR);
return;
}
}
if ($this->undo)
DB::Aowow()->query('DELETE FROM ?_account_profiles WHERE accountId = ?d AND profileId IN (?a)', $uid, $this->_get['id']);
else
{
foreach ($this->_get['id'] as $prId) // only link characters, not custom profiles
{
if ($prId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_profiles WHERE id = ?d AND realm IS NOT NULL', $prId))
DB::Aowow()->query('INSERT IGNORE INTO ?_account_profiles VALUES (?d, ?d, 0)', $uid, $prId);
else
{
trigger_error('AjaxProfile::handleLink - profile #'.$prId.' is custom or does not exist', E_USER_ERROR);
return;
}
}
}
}
/* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional]
return: null
*/
protected function handlePin() : void // (un)favorite
{
if (!User::$id || empty($this->_get['id'][0]))
{
trigger_error('AjaxProfile::handlePin - profileId empty or user not logged in', E_USER_ERROR);
return;
}
$uid = User::$id;
if ($this->_get['user'] && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
{
if (!($uid = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE user = ?', $this->_get['user'])))
{
trigger_error('AjaxProfile::handlePin - user "'.$this->_get['user'].'" does not exist', E_USER_ERROR);
return;
}
}
// since only one character can be pinned at a time we can reset everything
DB::Aowow()->query('UPDATE ?_account_profiles SET extraFlags = extraFlags & ?d WHERE accountId = ?d', ~PROFILER_CU_PINNED, $uid);
// and set a single char if necessary
if (!$this->undo)
DB::Aowow()->query('UPDATE ?_account_profiles SET extraFlags = extraFlags | ?d WHERE profileId = ?d AND accountId = ?d', PROFILER_CU_PINNED, $this->_get['id'][0], $uid);
}
/* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional]
return: null
*/
protected function handlePrivacy() : void // public visibility
{
if (!User::$id || empty($this->_get['id'][0]))
{
trigger_error('AjaxProfile::handlePrivacy - profileId empty or user not logged in', E_USER_ERROR);
return;
}
$uid = User::$id;
if ($this->_get['user'] && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
{
if (!($uid = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE user = ?', $this->_get['user'])))
{
trigger_error('AjaxProfile::handlePrivacy - user "'.$this->_get['user'].'" does not exist', E_USER_ERROR);
return;
}
}
if ($this->undo)
{
DB::Aowow()->query('UPDATE ?_account_profiles SET extraFlags = extraFlags & ?d WHERE profileId IN (?a) AND accountId = ?d', ~PROFILER_CU_PUBLISHED, $this->_get['id'], $uid);
DB::Aowow()->query('UPDATE ?_profiler_profiles SET cuFlags = cuFlags & ?d WHERE id IN (?a) AND user = ?d', ~PROFILER_CU_PUBLISHED, $this->_get['id'], $uid);
}
else
{
DB::Aowow()->query('UPDATE ?_account_profiles SET extraFlags = extraFlags | ?d WHERE profileId IN (?a) AND accountId = ?d', PROFILER_CU_PUBLISHED, $this->_get['id'], $uid);
DB::Aowow()->query('UPDATE ?_profiler_profiles SET cuFlags = cuFlags | ?d WHERE id IN (?a) AND user = ?d', PROFILER_CU_PUBLISHED, $this->_get['id'], $uid);
}
}
/* params
id: <prId>
size: <string> [optional]
return: image-header
*/
protected function handleAvatar() : void // image
{
// something happened in the last years: those textures do not include tiny icons
$sizes = [/* 'tiny' => 15, */'small' => 18, 'medium' => 36, 'large' => 56];
$aPath = 'uploads/avatars/%d.jpg';
$s = $this->_get['size'] ?: 'medium';
if (!$this->_get['id'] || !preg_match('/^([0-9]+)\.(jpg|gif)$/', $this->_get['id'][0], $matches) || !in_array($s, array_keys($sizes)))
{
trigger_error('AjaxProfile::handleAvatar - malformed request received', E_USER_ERROR);
return;
}
$this->contentType = $matches[2] == 'png' ? MIME_TYPE_PNG : MIME_TYPE_JPEG;
$id = $matches[1];
$dest = imageCreateTruecolor($sizes[$s], $sizes[$s]);
if (file_exists(sprintf($aPath, $id)))
{
$offsetX = $offsetY = 0;
switch ($s)
{
case 'tiny':
$offsetX += $sizes['small'];
case 'small':
$offsetY += $sizes['medium'];
case 'medium':
$offsetX += $sizes['large'];
}
$src = imageCreateFromJpeg(printf($aPath, $id));
imagecopymerge($dest, $src, 0, 0, $offsetX, $offsetY, $sizes[$s], $sizes[$s], 100);
}
else
trigger_error('AjaxProfile::handleAvatar - avatar file #'.$id.' not found', E_USER_ERROR);
if ($matches[2] == 'gif')
imageGif($dest);
else
imageJpeg($dest);
}
/* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional, not used]
return: 1
*/
protected function handleResync() : string
{
if ($chars = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_profiles WHERE id IN (?a)', $this->_get['id']))
{
foreach ($chars as $c)
Profiler::scheduleResync(Type::PROFILE, $c['realm'], $c['realmGUID']);
}
else
trigger_error('AjaxProfile::handleResync - profiles '.implode(', ', $this->_get['id']).' not found in db', E_USER_ERROR);
return '1';
}
/* params
id: <prId1,prId2,..,prIdN>
return
<status object>
[
nQueueProcesses,
[statusCode, timeToRefresh, curQueuePos, errorCode, nResyncTries],
[<anotherStatus>]
...
]
not all fields are required, if zero they are omitted
statusCode:
0: end the request
1: waiting
2: working...
3: ready; click to view
4: error / retry
errorCode:
0: unk error
1: char does not exist
2: armory gone
*/
protected function handleStatus() : string
{
// roster resync for this guild was requested -> get char list
if ($this->_get['guild'])
$ids = DB::Aowow()->selectCol('SELECT id FROM ?_profiler_profiles WHERE guild IN (?a)', $this->_get['id']);
else if ($this->_get['arena-team'])
$ids = DB::Aowow()->selectCol('SELECT profileId FROM ?_profiler_arena_team_member WHERE arenaTeamId IN (?a)', $this->_get['id']);
else
$ids = $this->_get['id'];
if (!$ids)
{
trigger_error('AjaxProfile::handleStatus - no profileIds to resync'.($this->_get['guild'] ? ' for guild #'.$this->_get['guild'] : ($this->_get['arena-team'] ? ' for areana team #'.$this->_get['arena-team'] : '')), E_USER_ERROR);
return Util::toJSON([1, [PR_QUEUE_STATUS_ERROR, 0, 0, PR_QUEUE_ERROR_CHAR]]);
}
$response = Profiler::resyncStatus(Type::PROFILE, $ids);
return Util::toJSON($response);
}
/* params (get))
id: <prId1,0> [0: new profile]
params (post)
<various char data> [see below]
return:
proileId [onSuccess]
-1 [onError]
*/
protected function handleSave() : string // unKill a profile
{
// todo (med): detail check this post-data
$cuProfile = array(
'user' => User::$id,
// 'userName' => User::$displayName,
'name' => $this->_post['name'],
'level' => $this->_post['level'],
'class' => $this->_post['class'],
'race' => $this->_post['race'],
'gender' => $this->_post['gender'],
'nomodelMask' => $this->_post['nomodel'],
'talenttree1' => $this->_post['talenttree1'],
'talenttree2' => $this->_post['talenttree2'],
'talenttree3' => $this->_post['talenttree3'],
'talentbuild1' => $this->_post['talentbuild1'],
'talentbuild2' => $this->_post['talentbuild2'],
'activespec' => $this->_post['activespec'],
'glyphs1' => $this->_post['glyphs1'],
'glyphs2' => $this->_post['glyphs2'],
'gearscore' => $this->_post['gearscore'],
'icon' => $this->_post['icon'],
'cuFlags' => PROFILER_CU_PROFILE | ($this->_post['public'] ? PROFILER_CU_PUBLISHED : 0)
);
if (strstr($cuProfile['icon'], 'profile=avatar')) // how the profiler is supposed to handle icons is beyond me
$cuProfile['icon'] = '';
if ($_ = $this->_post['description'])
$cuProfile['description'] = $_;
if ($_ = $this->_post['source']) // should i also set sourcename?
$cuProfile['sourceId'] = $_;
if ($_ = $this->_post['copy']) // gets set to source profileId when "save as" is clicked. Whats the difference to 'source' though?
{
// get character origin info if possible
if ($r = DB::Aowow()->selectCell('SELECT realm FROM ?_profiler_profiles WHERE id = ?d AND realm IS NOT NULL', $_))
$cuProfile['realm'] = $r;
$cuProfile['sourceId'] = $_;
}
if ($cuProfile['sourceId'])
$cuProfile['sourceName'] = DB::Aowow()->selectCell('SELECT name FROM ?_profiler_profiles WHERE id = ?d', $cuProfile['sourceId']);
$charId = -1;
if ($id = $this->_get['id'][0]) // update
{
if ($charId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_profiles WHERE id = ?d', $id))
DB::Aowow()->query('UPDATE ?_profiler_profiles SET ?a WHERE id = ?d', $cuProfile, $id);
}
else // new
{
$nProfiles = DB::Aowow()->selectCell('SELECT COUNT(*) FROM ?_profiler_profiles WHERE user = ?d AND (cuFlags & ?d) = 0 AND realmGUID IS NULL', User::$id, PROFILER_CU_DELETED);
if ($nProfiles < 10 || User::isPremium())
if ($newId = DB::Aowow()->query('INSERT INTO ?_profiler_profiles (?#) VALUES (?a)', array_keys($cuProfile), array_values($cuProfile)))
$charId = $newId;
}
// update items
if ($charId != -1)
{
// ok, 'funny' thing: wether an item has en extra prismatic sockel is determined contextual
// either the socket is -1 or it has an itemId in a socket where there shouldn't be one
$keys = ['id', 'slot', 'item', 'subitem', 'permEnchant', 'tempEnchant', 'gem1', 'gem2', 'gem3', 'gem4'];
// validate Enchantments
$enchIds = array_merge(
array_column($this->_post['inv'], 3), // perm enchantments
array_column($this->_post['inv'], 4) // temp enchantments (not used..?)
);
$enchs = new EnchantmentList(array(['id', $enchIds]));
// validate items
$itemIds = array_merge(
array_column($this->_post['inv'], 1), // base item
array_column($this->_post['inv'], 5), // gem slot 1
array_column($this->_post['inv'], 6), // gem slot 2
array_column($this->_post['inv'], 7), // gem slot 3
array_column($this->_post['inv'], 8) // gem slot 4
);
$items = new ItemList(array(['id', $itemIds]));
if (!$items->error)
{
foreach ($this->_post['inv'] as $slot => $itemData)
{
if ($slot + 1 == array_sum($itemData)) // only slot definition set => empty slot
{
DB::Aowow()->query('DELETE FROM ?_profiler_items WHERE id = ?d AND slot = ?d', $charId, $itemData[0]);
continue;
}
// item does not exist
if (!$items->getEntry($itemData[1]))
continue;
// sub-item check
if (!$items->getRandEnchantForItem($itemData[1]))
$itemData[2] = 0;
// item sockets are fubar
$nSockets = $items->json[$itemData[1]]['nsockets'];
$nSockets += in_array($slot, [SLOT_WAIST, SLOT_WRISTS, SLOT_HANDS]) ? 1 : 0;
for ($i = 5; $i < 9; $i++)
if ($itemData[$i] > 0 && (!$items->getEntry($itemData[$i]) || $i >= (5 + $nSockets)))
$itemData[$i] = 0;
// item enchantments are borked
if ($itemData[3] && !$enchs->getEntry($itemData[3]))
$itemData[3] = 0;
if ($itemData[4] && !$enchs->getEntry($itemData[4]))
$itemData[4] = 0;
// looks good
array_unshift($itemData, $charId);
DB::Aowow()->query('REPLACE INTO ?_profiler_items (?#) VALUES (?a)', $keys, $itemData);
}
}
}
return (string)$charId;
}
/* params
id: <prId1,prId2,..,prIdN>
return
null
*/
protected function handleDelete() : void // kill a profile
{
if (!User::$id || !$this->_get['id'])
{
trigger_error('AjaxProfile::handleDelete - profileId empty or user not logged in', E_USER_ERROR);
return;
}
// only flag as deleted; only custom profiles
DB::Aowow()->query(
'UPDATE ?_profiler_profiles SET cuFlags = cuFlags | ?d WHERE id IN (?a) AND cuFlags & ?d {AND user = ?d}',
PROFILER_CU_DELETED,
$this->_get['id'],
PROFILER_CU_PROFILE,
User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU) ? DBSIMPLE_SKIP : User::$id
);
}
/* params
id: profileId
items: string [itemIds.join(':')]
unnamed: unixtime [only to force the browser to reload instead of cache]
return
lots...
*/
protected function handleLoad() : string
{
// titles, achievements, characterData, talents, pets
// and some onLoad-hook to .. load it registerProfile($data)
// everything else goes through data.php .. strangely enough
if (!$this->_get['id'])
{
trigger_error('AjaxProfile::handleLoad - profileId empty', E_USER_ERROR);
return '';
}
$pBase = DB::Aowow()->selectRow('SELECT pg.name AS guildname, p.* FROM ?_profiler_profiles p LEFT JOIN ?_profiler_guild pg ON pg.id = p.guild WHERE p.id = ?d', $this->_get['id'][0]);
if (!$pBase)
{
trigger_error('Profiler::handleLoad - called with invalid profileId #'.$this->_get['id'][0], E_USER_WARNING);
return '';
}
if (($pBase['cuFlags'] & PROFILER_CU_DELETED) && !User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
return '';
$rData = [];
foreach (Profiler::getRealms() as $rId => $rData)
if ($rId == $pBase['realm'])
break;
$profile = array(
'id' => $pBase['id'],
'source' => $pBase['id'],
'level' => $pBase['level'],
'classs' => $pBase['class'],
'race' => $pBase['race'],
'faction' => Game::sideByRaceMask(1 << ($pBase['race'] - 1)) - 1,
'gender' => $pBase['gender'],
'skincolor' => $pBase['skincolor'],
'hairstyle' => $pBase['hairstyle'],
'haircolor' => $pBase['haircolor'],
'facetype' => $pBase['facetype'],
'features' => $pBase['features'],
'title' => $pBase['title'],
'name' => $pBase['name'],
'guild' => "$'".$pBase['guildname']."'",
'published' => !!($pBase['cuFlags'] & PROFILER_CU_PUBLISHED),
'pinned' => !!($pBase['cuFlags'] & PROFILER_CU_PINNED),
'nomodel' => $pBase['nomodelMask'],
'playedtime' => $pBase['playedtime'],
'lastupdated' => $pBase['lastupdated'] * 1000,
'talents' => array(
'builds' => array( // notice the bullshit to prevent the talent-string from becoming a float! NOTICE IT!!
['talents' => '$"'.$pBase['talentbuild1'].'"', 'glyphs' => $pBase['glyphs1']],
['talents' => '$"'.$pBase['talentbuild2'].'"', 'glyphs' => $pBase['glyphs2']]
),
'active' => $pBase['activespec']
),
// set later
'inventory' => [],
'bookmarks' => [], // list of userIds who claimed this profile (claiming and owning are two different things)
// completion lists: [subjectId => amount/timestamp/1]
'skills' => [], // skillId => [curVal, maxVal]
'reputation' => [], // factionId => curVal
'titles' => [], // titleId => 1
'spells' => [], // spellId => 1; recipes, vanity pets, mounts
'achievements' => [], // achievementId => timestamp
'quests' => [], // questId => 1
'achievementpoints' => 0, // max you have
'statistics' => [], // all raid activity [achievementId => killCount]
'activity' => [], // recent raid activity [achievementId => 1] (is a subset of statistics)
);
if ($pBase['cuFlags'] & PROFILER_CU_PROFILE)
{
// this parameter is _really_ strange .. probably still not doing this right
$profile['source'] = $pBase['realm'] ? $pBase['sourceId'] : 0;
$profile['sourcename'] = $pBase['sourceName'];
$profile['description'] = $pBase['description'];
$profile['user'] = $pBase['user'];
$profile['username'] = DB::Aowow()->selectCell('SELECT displayName FROM ?_account WHERE id = ?d', $pBase['user']);
}
// custom profiles inherit this when copied from real char :(
if ($pBase['realm'])
{
$profile['region'] = [$rData['region'], Lang::profiler('regions', $rData['region'])];
$profile['battlegroup'] = [Profiler::urlize(CFG_BATTLEGROUP), CFG_BATTLEGROUP];
$profile['realm'] = [Profiler::urlize($rData['name'], true), $rData['name']];
}
// bookmarks
if ($_ = DB::Aowow()->selectCol('SELECT accountId FROM ?_account_profiles WHERE profileId = ?d', $pBase['id']))
$profile['bookmarks'] = $_;
// arena teams - [size(2|3|5) => DisplayName]; DisplayName gets urlized to use as link
if ($at = DB::Aowow()->selectCol('SELECT type AS ARRAY_KEY, name FROM ?_profiler_arena_team at JOIN ?_profiler_arena_team_member atm ON atm.arenaTeamId = at.id WHERE atm.profileId = ?d', $pBase['id']))
$profile['arenateams'] = $at;
// pets if hunter fields: [name:name, family:petFamily, npc:npcId, displayId:modelId, talents:talentString]
if ($pets = DB::Aowow()->select('SELECT name, family, npc, displayId, talents FROM ?_profiler_pets WHERE owner = ?d', $pBase['id']))
$profile['pets'] = $pets;
// source for custom profiles; profileId => [name, ownerId, iconString(optional)]
if ($customs = DB::Aowow()->select('SELECT id AS ARRAY_KEY, name, user, icon FROM ?_profiler_profiles WHERE sourceId = ?d AND sourceId <> id {AND (cuFlags & ?d) = 0}', $pBase['id'], User::isInGroup(U_GROUP_STAFF) ? DBSIMPLE_SKIP : PROFILER_CU_DELETED))
{
foreach ($customs as $id => $cu)
{
if (!$cu['icon'])
unset($cu['icon']);
$profile['customs'][$id] = array_values($cu);
}
}
/* $profile[]
// CUSTOM
'auras' => [], // custom list of buffs, debuffs [spellId]
// UNUSED
'glyphs' => [], // provided list of already known glyphs (post cataclysm feature)
*/
$completion = DB::Aowow()->select('SELECT type AS ARRAY_KEY, typeId AS ARRAY_KEY2, cur, max FROM ?_profiler_completion WHERE id = ?d', $pBase['id']);
foreach ($completion as $type => $data)
{
switch ($type)
{
case Type::FACTION: // factionId => amount
$profile['reputation'] = array_combine(array_keys($data), array_column($data, 'cur'));
break;
case Type::TITLE:
foreach ($data as &$d)
$d = 1;
$profile['titles'] = $data;
break;
case Type::QUEST:
foreach ($data as &$d)
$d = 1;
$profile['quests'] = $data;
break;
case Type::SPELL:
foreach ($data as &$d)
$d = 1;
$profile['spells'] = $data;
break;
case Type::ACHIEVEMENT:
$achievements = array_filter($data, function ($x) { return $x['max'] === null; });
$statistics = array_filter($data, function ($x) { return $x['max'] !== null; });
// achievements
$profile['achievements'] = array_combine(array_keys($achievements), array_column($achievements, 'cur'));
$profile['achievementpoints'] = DB::Aowow()->selectCell('SELECT SUM(points) FROM ?_achievement WHERE id IN (?a)', array_keys($achievements));
// raid progression
$activity = array_filter($statistics, function ($x) { return $x['cur'] > (time() - MONTH); });
foreach ($activity as &$r)
$r = 1;
// ony .. subtract 10-man from 25-man
$profile['statistics'] = array_combine(array_keys($statistics), array_column($statistics, 'max'));
$profile['activity'] = $activity;
break;
case Type::SKILL:
foreach ($data as &$d)
$d = [$d['cur'], $d['max']];
$profile['skills'] = $data;
break;
}
}
$buff = '';
$usedSlots = [];
if ($this->_get['items'])
{
$phItems = new ItemList(array(['id', $this->_get['items']], ['slot', INVTYPE_NON_EQUIP, '!']));
if (!$phItems->error)
{
$data = $phItems->getListviewData(ITEMINFO_JSON | ITEMINFO_SUBITEMS);
foreach ($phItems->iterate() as $iId => $__)
{
$sl = $phItems->getField('slot');
foreach (Profiler::$slot2InvType as $slot => $invTypes)
{
if (in_array($sl, $invTypes) && !in_array($slot, $usedSlots))
{
// get and apply inventory
$buff .= 'g_items.add('.$iId.', {name_'.User::$localeString.":'".Util::jsEscape($phItems->getField('name', true))."', quality:".$phItems->getField('quality').", icon:'".$phItems->getField('iconString')."', jsonequip:".Util::toJSON($data[$iId])."});\n";
$profile['inventory'][$slot] = [$iId, 0, 0, 0, 0, 0, 0, 0];
$usedSlots[] = $slot;
break;
}
}
}
}
}
if ($items = DB::Aowow()->select('SELECT * FROM ?_profiler_items WHERE id = ?d', $pBase['id']))
{
$itemz = new ItemList(array(['id', array_column($items, 'item')], CFG_SQL_LIMIT_NONE));
if (!$itemz->error)
{
$data = $itemz->getListviewData(ITEMINFO_JSON | ITEMINFO_SUBITEMS);
foreach ($items as $i)
{
if ($itemz->getEntry($i['item']) && !in_array($i['slot'], $usedSlots))
{
// get and apply inventory
$buff .= 'g_items.add('.$i['item'].', {name_'.User::$localeString.":'".Util::jsEscape($itemz->getField('name', true))."', quality:".$itemz->getField('quality').", icon:'".$itemz->getField('iconString')."', jsonequip:".Util::toJSON($data[$i['item']])."});\n";
$profile['inventory'][$i['slot']] = [$i['item'], $i['subItem'], $i['permEnchant'], $i['tempEnchant'], $i['gem1'], $i['gem2'], $i['gem3'], $i['gem4']];
}
}
}
}
if ($buff)
$buff .= "\n";
// if ($au = $char->getField('auras'))
// {
// $auraz = new SpellList(array(['id', $char->getField('auras')], CFG_SQL_LIMIT_NONE));
// $dataz = $auraz->getListviewData();
// $modz = $auraz->getProfilerMods();
// // get and apply aura-mods
// foreach ($dataz as $id => $data)
// {
// $mods = [];
// if (!empty($modz[$id]))
// {
// foreach ($modz[$id] as $k => $v)
// {
// if (is_array($v))
// $mods[] = $v;
// else if ($str = @Game::$itemMods[$k])
// $mods[$str] = $v;
// }
// }
// $buff .= 'g_spells.add('.$id.", {id:".$id.", name:'".Util::jsEscape(mb_substr($data['name'], 1))."', icon:'".$data['icon']."', modifier:".Util::toJSON($mods)."});\n";
// }
// $buff .= "\n";
// }
// load available titles
Util::loadStaticFile('p-titles-'.$pBase['gender'], $buff, true);
// add profile to buffer
$buff .= "\n\n\$WowheadProfiler.registerProfile(".Util::toJSON($profile).");";
return $buff."\n";
}
/* params
id: <prId>
data: <mode> [string, tabName]
return
null
*/
protected function handlePurge() : void { } // removes completion data (as uploaded by the wowhead client) Just fail silently if someone triggers this manually
protected static function checkItemList($val) : array
{
// expecting item-list
if (preg_match('/\d+(:\d+)*/', $val))
return array_map('intVal', explode(':', $val));
return [];
}
protected static function checkUser(string $val) : string
{
if (User::isValidName($val))
return $val;
return '';
}
}
?>

View File

@@ -18,19 +18,19 @@ if (!defined('AOWOW_REVISION'))
class CommunityContent class CommunityContent
{ {
private static array $jsGlobals = []; private static $jsGlobals = [];
private static array $subjCache = []; private static $subjCache = [];
private static string $coQuery = ' private static $commentQuery = '
SELECT SELECT
c.*, c.*,
a1.displayName AS user, a1.displayName AS user,
a2.displayName AS editUser, a2.displayName AS editUser,
a3.displayName AS deleteUser, a3.displayName AS deleteUser,
a4.displayName AS responseUser, a4.displayName AS responseUser,
IFNULL(SUM(ur.value), 0) AS rating, IFNULL(SUM(cr.value), 0) AS rating,
SUM(IF(ur.userId > 0 AND ur.userId = ?d, ur.value, 0)) AS userRating, SUM(IF (cr.userId = ?d, value, 0)) AS userRating,
SUM(IF( r.userId > 0 AND r.userId = ?d, 1, 0)) AS userReported SUM(IF (r.userId = ?d, 1, 0)) AS userReported
FROM FROM
?_comments c ?_comments c
JOIN JOIN
@@ -42,7 +42,7 @@ class CommunityContent
LEFT JOIN LEFT JOIN
?_account a4 ON c.responseUserId = a4.id ?_account a4 ON c.responseUserId = a4.id
LEFT JOIN LEFT JOIN
?_user_ratings ur ON c.id = ur.entry AND ur.type = ?d ?_comments_rates cr ON c.id = cr.commentId
LEFT JOIN LEFT JOIN
?_reports r ON r.subject = c.id AND r.mode = 1 AND r.reason = 19 ?_reports r ON r.subject = c.id AND r.mode = 1 AND r.reason = 19
WHERE WHERE
@@ -54,41 +54,24 @@ class CommunityContent
rating ASC rating ASC
'; ';
private static string $ssQuery = ' private static $previewQuery = '
SELECT s.id AS ARRAY_KEY, s.id, a.displayName AS user, s.date, s.width, s.height, s.caption, IF(s.status & ?d, 1, 0) AS "sticky", s.type, s.typeId
FROM ?_screenshots s
LEFT JOIN ?_account a ON s.userIdOwner = a.id
WHERE {s.userIdOwner = ?d AND }{s.type = ? AND }{s.typeId = ? AND }s.status & ?d AND (s.status & ?d) = 0
{ORDER BY ?# DESC}
{LIMIT ?d}
';
private static string $viQuery = '
SELECT v.id AS ARRAY_KEY, v.id, a.displayName AS user, v.date, v.videoId, v.caption, IF(v.status & ?d, 1, 0) AS "sticky", v.type, v.typeId
FROM ?_videos v
LEFT JOIN ?_account a ON v.userIdOwner = a.id
WHERE {v.userIdOwner = ?d AND }{v.type = ? AND }{v.typeId = ? AND }v.status & ?d AND (v.status & ?d) = 0
{ORDER BY ?# DESC}
{LIMIT ?d}
';
private static string $previewQuery = '
SELECT SELECT
c.id, c.id,
c.body AS preview, c.body AS preview,
c.date, c.date,
c.replyTo AS commentid, c.replyTo AS commentid,
UNIX_TIMESTAMP() - c.date AS elapsed,
IF(c.flags & ?d, 1, 0) AS deleted, IF(c.flags & ?d, 1, 0) AS deleted,
IF(c.type <> 0, c.type, c2.type) AS type, IF(c.type <> 0, c.type, c2.type) AS type,
IF(c.typeId <> 0, c.typeId, c2.typeId) AS typeId, IF(c.typeId <> 0, c.typeId, c2.typeId) AS typeId,
IFNULL(SUM(ur.value), 0) AS rating, IFNULL(SUM(cr.value), 0) AS rating,
a.displayName AS user a.displayName AS user
FROM FROM
?_comments c ?_comments c
JOIN JOIN
?_account a ON c.userId = a.id ?_account a ON c.userId = a.id
LEFT JOIN LEFT JOIN
?_user_ratings ur ON ur.entry = c.id AND ur.userId <> 0 AND ur.`type` = 1 ?_comments_rates cr ON cr.commentId = c.id
LEFT JOIN LEFT JOIN
?_comments c2 ON c.replyTo = c2.id ?_comments c2 ON c.replyTo = c2.id
WHERE WHERE
@@ -104,13 +87,13 @@ class CommunityContent
?d ?d
'; ';
private static function addSubject(int $type, int $typeId) : void private static function addSubject($type, $typeId)
{ {
if (!isset(self::$subjCache[$type][$typeId])) if (!isset(self::$subjCache[$type][$typeId]))
self::$subjCache[$type][$typeId] = 0; self::$subjCache[$type][$typeId] = 0;
} }
private static function getSubjects() : void private static function getSubjects()
{ {
foreach (self::$subjCache as $type => $ids) foreach (self::$subjCache as $type => $ids)
{ {
@@ -118,16 +101,35 @@ class CommunityContent
if (!$_) if (!$_)
continue; continue;
$obj = Type::newList($type, [CFG_SQL_LIMIT_NONE, ['id', $_]]); $cnd = [CFG_SQL_LIMIT_NONE, ['id', $_]];
if (!$obj)
continue; switch ($type)
{
case TYPE_NPC: $obj = new CreatureList($cnd); break;
case TYPE_OBJECT: $obj = new GameobjectList($cnd); break;
case TYPE_ITEM: $obj = new ItemList($cnd); break;
case TYPE_ITEMSET: $obj = new ItemsetList($cnd); break;
case TYPE_QUEST: $obj = new QuestList($cnd); break;
case TYPE_SPELL: $obj = new SpellList($cnd); break;
case TYPE_ZONE: $obj = new ZoneList($cnd); break;
case TYPE_FACTION: $obj = new FactionList($cnd); break;
case TYPE_PET: $obj = new PetList($cnd); break;
case TYPE_ACHIEVEMENT: $obj = new AchievementList($cnd); break;
case TYPE_TITLE: $obj = new TitleList($cnd); break;
case TYPE_WORLDEVENT: $obj = new WorldEventList($cnd); break;
case TYPE_CLASS: $obj = new CharClassList($cnd); break;
case TYPE_RACE: $obj = new CharRaceList($cnd); break;
case TYPE_SKILL: $obj = new SkillList($cnd); break;
case TYPE_CURRENCY: $obj = new CurrencyList($cnd); break;
default: continue;
}
foreach ($obj->iterate() as $id => $__) foreach ($obj->iterate() as $id => $__)
self::$subjCache[$type][$id] = $obj->getField('name', true); self::$subjCache[$type][$id] = $obj->getField('name', true);
} }
} }
public static function getCommentPreviews(array $params = [], ?int &$nFound = 0, bool $dateFmt = true) : array public static function getCommentPreviews($params = [], &$nFound = 0)
{ {
/* /*
purged:0, <- doesnt seem to be used anymore purged:0, <- doesnt seem to be used anymore
@@ -160,18 +162,35 @@ class CommunityContent
$c['subject'] = self::$subjCache[$c['type']][$c['typeId']]; $c['subject'] = self::$subjCache[$c['type']][$c['typeId']];
// format date // format date
$c['date'] = $dateFmt ? date(Util::$dateFormatInternal, $c['date']) : intVal($c['date']); $c['date'] = date(Util::$dateFormatInternal, $c['date']);
// remove commentid if not looking for replies // remove commentid if not looking for replies
if (empty($params['replies'])) if (empty($params['replies']))
unset($c['commentid']); unset($c['commentid']);
// format text for listview // remove line breaks
$c['preview'] = Lang::trimTextClean($c['preview']); $c['preview'] = strtr($c['preview'], ["\n" => ' ', "\r" => ' ']);
// limit whitespaces to one at a time
$c['preview'] = preg_replace('/\s+/', ' ', $c['preview']);
// limit previews to 100 chars + whatever it takes to make the last word full
if (strlen($c['preview']) > 100)
{
$n = 0;
$b = [];
$parts = explode(' ', $c['preview']);
while ($n < 100 && $parts)
{
$_ = array_shift($parts);
$n += strlen($_);
$b[] = $_;
}
$c['preview'] = implode(' ', $b).'…';
}
} }
else else
{ {
trigger_error('Comment '.$c['id'].' belongs to nonexistant subject.', E_USER_NOTICE); Util::addNote(U_GROUP_STAFF, 'CommunityClass::getCommentPreviews - comment '.$c['id'].' belongs to nonexistant subject');
unset($comments[$idx]); unset($comments[$idx]);
} }
} }
@@ -179,13 +198,13 @@ class CommunityContent
return $comments; return $comments;
} }
public static function getCommentReplies(int $commentId, int $limit = 0, ?int &$nFound = 0) : array public static function getCommentReplies($commentId, $limit = 0, &$nFound = 0)
{ {
$replies = []; $replies = [];
$query = $limit > 0 ? self::$coQuery.' LIMIT '.$limit : self::$coQuery; $query = $limit > 0 ? self::$commentQuery.' LIMIT '.$limit : self::$commentQuery;
// get replies // get replies
$results = DB::Aowow()->selectPage($nFound, $query, User::$id, User::$id, RATING_COMMENT, $commentId, 0, 0, CC_FLAG_DELETED, User::$id, User::isInGroup(U_GROUP_COMMENTS_MODERATOR)); $results = DB::Aowow()->selectPage($nFound, $query, User::$id, User::$id, $commentId, 0, 0, CC_FLAG_DELETED, User::$id, User::isInGroup(U_GROUP_COMMENTS_MODERATOR));
foreach ($results as $r) foreach ($results as $r)
{ {
(new Markup($r['body']))->parseGlobalsFromText(self::$jsGlobals); (new Markup($r['body']))->parseGlobalsFromText(self::$jsGlobals);
@@ -219,7 +238,7 @@ class CommunityContent
{ {
$screenshots = DB::Aowow()->select(' $screenshots = DB::Aowow()->select('
SELECT s.id, a.displayName AS user, s.date, s.width, s.height, s.type, s.typeId, s.caption, s.status, s.status AS "flags" SELECT s.id, a.displayName AS user, s.date, s.width, s.height, s.type, s.typeId, s.caption, s.status, s.status AS "flags"
FROM ?_screenshots s FROM ?_screenshots s,
LEFT JOIN ?_account a ON s.userIdOwner = a.id LEFT JOIN ?_account a ON s.userIdOwner = a.id
WHERE WHERE
{ s.type = ?d} { s.type = ?d}
@@ -287,20 +306,21 @@ class CommunityContent
public static function getScreenshotPagesForManager($all, &$nFound) public static function getScreenshotPagesForManager($all, &$nFound)
{ {
// i GUESS .. ss_getALL ? everything : pending // i GUESS .. ss_getALL ? everything : unapproved
$nFound = 0; $nFound = 0;
$pages = DB::Aowow()->select(' $pages = DB::Aowow()->select('
SELECT s.`type`, s.`typeId`, count(1) AS "count", MIN(s.`date`) AS "date" SELECT s.`type`, s.`typeId`, count(1) AS "count", MIN(s.`date`) AS "date"
FROM ?_screenshots s FROM ?_screenshots s
{WHERE (s.status & ?d) = 0} {WHERE (s.status & ?d) = 0}
GROUP BY s.`type`, s.`typeId`', GROUP BY s.`type`, s.`typeId`',
$all ? DBSIMPLE_SKIP : CC_FLAG_APPROVED | CC_FLAG_DELETED $all ? DBSIMPLE_SKIP : CC_FLAG_APPROVED
); );
if ($pages) if ($pages)
{ {
// limit to one actually existing type each // limit to one actually existing type each
foreach (array_unique(array_column($pages, 'type')) as $t) $types = array_intersect(array_unique(array_column($pages, 'type')), array_keys(Util::$typeClasses));
foreach ($types as $t)
{ {
$ids = []; $ids = [];
foreach ($pages as $row) foreach ($pages as $row)
@@ -310,21 +330,22 @@ class CommunityContent
if (!$ids) if (!$ids)
continue; continue;
$obj = Type::newList($t, [CFG_SQL_LIMIT_NONE, ['id', $ids]]); $cnd = [['id', $ids]];
if (!$obj || $obj->error) if ($t == TYPE_WORLDEVENT) // FKIN HOLIDAYS
continue; array_push($cnd, ['holidayId', $ids], 'OR');
$tClass = new Util::$typeClasses[$t]($cnd);
foreach ($pages as &$p) foreach ($pages as &$p)
if ($p['type'] == $t) if ($p['type'] == $t)
if ($obj->getEntry($p['typeId'])) if ($tClass->getEntry($p['typeId']))
$p['name'] = $obj->getField('name', true); $p['name'] = $tClass->getField('name', true);
} }
foreach ($pages as &$p) foreach ($pages as &$p)
{ {
if (empty($p['name'])) if (empty($p['name']))
{ {
trigger_error('Screenshot linked to nonexistant type/typeId combination: '.$p['type'].'/'.$p['typeId'], E_USER_NOTICE); Util::addNote(U_GROUP_STAFF | U_GROUP_SCREENSHOT, 'AdminPage::handleScreenshots() - Screenshot linked to nonexistant type/typeId combination '.$p['type'].'/'.$p['typeId']);
unset($p); unset($p);
} }
else else
@@ -338,10 +359,10 @@ class CommunityContent
return $pages; return $pages;
} }
public static function getComments(int $type, int $typeId) : array private static function getComments($type, $typeId)
{ {
$results = DB::Aowow()->query(self::$coQuery, User::$id, User::$id, RATING_COMMENT, 0, $type, $typeId, CC_FLAG_DELETED, User::$id, (int)User::isInGroup(U_GROUP_COMMENTS_MODERATOR)); $results = DB::Aowow()->query(self::$commentQuery, User::$id, User::$id, 0, $type, $typeId, CC_FLAG_DELETED, User::$id, (int)User::isInGroup(U_GROUP_COMMENTS_MODERATOR));
$comments = []; $comments = [];
// additional informations // additional informations
@@ -350,7 +371,7 @@ class CommunityContent
{ {
(new Markup($r['body']))->parseGlobalsFromText(self::$jsGlobals); (new Markup($r['body']))->parseGlobalsFromText(self::$jsGlobals);
self::$jsGlobals[Type::USER][$r['userId']] = $r['userId']; self::$jsGlobals[TYPE_USER][$r['userId']] = $r['userId'];
$c = array( $c = array(
'commentv2' => 1, // always 1.. enables some features i guess..? 'commentv2' => 1, // always 1.. enables some features i guess..?
@@ -396,20 +417,22 @@ class CommunityContent
return $comments; return $comments;
} }
public static function getVideos(int $typeOrUser = 0, int $typeId = 0, int &$nFound = 0, bool $dateFmt = true) : array public static function getVideos($typeOrUser, $typeId = 0, &$nFound = 0)
{ {
$videos = DB::Aowow()->selectPage($nFound, self::$viQuery, $videos = DB::Aowow()->selectPage($nFound, "
SELECT v.id, a.displayName AS user, v.date, v.videoId, v.caption, IF(v.status & ?d, 1, 0) AS 'sticky', v.type, v.typeId
FROM ?_videos v
LEFT JOIN ?_account a ON v.userIdOwner = a.id
WHERE {v.userIdOwner = ?d }{v.type = ? }{AND v.typeId = ? }AND v.status & ?d AND (v.status & ?d) = 0",
CC_FLAG_STICKY, CC_FLAG_STICKY,
$typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP, $typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP,
$typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP, $typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP,
$typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP, $typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP,
CC_FLAG_APPROVED, CC_FLAG_APPROVED,
CC_FLAG_DELETED, CC_FLAG_DELETED
!$typeOrUser ? 'date' : DBSIMPLE_SKIP,
!$typeOrUser ? CFG_SQL_LIMIT_SEARCH : DBSIMPLE_SKIP
); );
if ($typeOrUser <= 0) // not for search by type/typeId if ($typeOrUser < 0) // only for user page
{ {
foreach ($videos as $v) foreach ($videos as $v)
self::addSubject($v['type'], $v['typeId']); self::addSubject($v['type'], $v['typeId']);
@@ -420,7 +443,7 @@ class CommunityContent
// format data to meet requirements of the js // format data to meet requirements of the js
foreach ($videos as &$v) foreach ($videos as &$v)
{ {
if ($typeOrUser <= 0) // not for search by type/typeId if ($typeOrUser < 0) // only for user page
{ {
if (!empty(self::$subjCache[$v['type']][$v['typeId']]) && !is_numeric(self::$subjCache[$v['type']][$v['typeId']])) if (!empty(self::$subjCache[$v['type']][$v['typeId']]) && !is_numeric(self::$subjCache[$v['type']][$v['typeId']]))
$v['subject'] = self::$subjCache[$v['type']][$v['typeId']]; $v['subject'] = self::$subjCache[$v['type']][$v['typeId']];
@@ -428,8 +451,8 @@ class CommunityContent
$v['subject'] = Lang::user('removed'); $v['subject'] = Lang::user('removed');
} }
$v['date'] = $dateFmt ? date(Util::$dateFormatInternal, $v['date']) : intVal($v['date']); $v['date'] = date(Util::$dateFormatInternal, $v['date']);
$v['videoType'] = 1; // always youtube $v['videoType'] = 1; // always youtube
if (!$v['sticky']) if (!$v['sticky'])
unset($v['sticky']); unset($v['sticky']);
@@ -441,20 +464,22 @@ class CommunityContent
return $videos; return $videos;
} }
public static function getScreenshots(int $typeOrUser = 0, int $typeId = 0, int &$nFound = 0, bool $dateFmt = true) : array public static function getScreenshots($typeOrUser, $typeId = 0, &$nFound = 0)
{ {
$screenshots = DB::Aowow()->selectPage($nFound, self::$ssQuery, $screenshots = DB::Aowow()->selectPage($nFound, "
SELECT s.id, a.displayName AS user, s.date, s.width, s.height, s.caption, IF(s.status & ?d, 1, 0) AS 'sticky', s.type, s.typeId
FROM ?_screenshots s
LEFT JOIN ?_account a ON s.userIdOwner = a.id
WHERE {s.userIdOwner = ?d }{s.type = ? }{AND s.typeId = ? }AND s.status & ?d AND (s.status & ?d) = 0",
CC_FLAG_STICKY, CC_FLAG_STICKY,
$typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP, $typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP,
$typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP, $typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP,
$typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP, $typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP,
CC_FLAG_APPROVED, CC_FLAG_APPROVED,
CC_FLAG_DELETED, CC_FLAG_DELETED
!$typeOrUser ? 'date' : DBSIMPLE_SKIP,
!$typeOrUser ? CFG_SQL_LIMIT_SEARCH : DBSIMPLE_SKIP
); );
if ($typeOrUser <= 0) // not for search by type/typeId if ($typeOrUser < 0) // only for user page
{ {
foreach ($screenshots as $s) foreach ($screenshots as $s)
self::addSubject($s['type'], $s['typeId']); self::addSubject($s['type'], $s['typeId']);
@@ -465,7 +490,7 @@ class CommunityContent
// format data to meet requirements of the js // format data to meet requirements of the js
foreach ($screenshots as &$s) foreach ($screenshots as &$s)
{ {
if ($typeOrUser <= 0) // not for search by type/typeId if ($typeOrUser < 0) // only for user page
{ {
if (!empty(self::$subjCache[$s['type']][$s['typeId']]) && !is_numeric(self::$subjCache[$s['type']][$s['typeId']])) if (!empty(self::$subjCache[$s['type']][$s['typeId']]) && !is_numeric(self::$subjCache[$s['type']][$s['typeId']]))
$s['subject'] = self::$subjCache[$s['type']][$s['typeId']]; $s['subject'] = self::$subjCache[$s['type']][$s['typeId']];
@@ -473,7 +498,7 @@ class CommunityContent
$s['subject'] = Lang::user('removed'); $s['subject'] = Lang::user('removed');
} }
$s['date'] = $dateFmt ? date(Util::$dateFormatInternal, $s['date']) : intVal($s['date']); $s['date'] = date(Util::$dateFormatInternal, $s['date']);
if (!$s['sticky']) if (!$s['sticky'])
unset($s['sticky']); unset($s['sticky']);
@@ -485,11 +510,11 @@ class CommunityContent
return $screenshots; return $screenshots;
} }
public static function getAll(int $type, int $typeId, array &$jsg) : array public static function getAll($type, $typeId, &$jsg)
{ {
$result = array( $result = array(
'vi' => self::getVideos($type, $typeId), 'vi' => self::getVideos($type, $typeId),
'ss' => self::getScreenshots($type, $typeId), 'sc' => self::getScreenshots($type, $typeId),
'co' => self::getComments($type, $typeId) 'co' => self::getComments($type, $typeId)
); );
@@ -497,10 +522,5 @@ class CommunityContent
return $result; return $result;
} }
public static function getJSGlobals() : array
{
return self::$jsGlobals;
}
} }
?> ?>

View File

@@ -15,8 +15,6 @@ class DB
private static $optionsCache = []; private static $optionsCache = [];
private static $connectionCache = []; private static $connectionCache = [];
private static $logs = [];
private static function createConnectSyntax(&$options) private static function createConnectSyntax(&$options)
{ {
return 'mysqli://'.$options['user'].':'.$options['pass'].'@'.$options['host'].'/'.$options['db']; return 'mysqli://'.$options['user'].':'.$options['pass'].'@'.$options['host'].'/'.$options['db'];
@@ -31,43 +29,17 @@ class DB
$interface = DbSimple_Generic::connect(self::createConnectSyntax($options)); $interface = DbSimple_Generic::connect(self::createConnectSyntax($options));
if (!$interface || $interface->error) if (!$interface || $interface->error)
die('Failed to connect to database on index #'.$idx.".\n"); die('Failed to connect to database.');
$interface->setErrorHandler(['DB', 'errorHandler']); $interface->setErrorHandler(['DB', 'errorHandler']);
$interface->query('SET NAMES ?', 'utf8mb4'); $interface->query('SET NAMES ?', 'utf8');
if ($options['prefix']) if ($options['prefix'])
$interface->setIdentPrefix($options['prefix']); $interface->setIdentPrefix($options['prefix']);
// disable STRICT_TRANS_TABLES and STRICT_ALL_TABLES off. It prevents usage of implicit default values.
if ($idx == DB_AOWOW)
$interface->query("SET SESSION sql_mode = 'NO_ENGINE_SUBSTITUTION'");
// disable ONLY_FULL_GROUP_BY (Allows for non-aggregated selects in a group-by query)
else
$interface->query("SET SESSION sql_mode = ''");
self::$interfaceCache[$idx] = &$interface; self::$interfaceCache[$idx] = &$interface;
self::$connectionCache[$idx] = true; self::$connectionCache[$idx] = true;
} }
public static function test(array $options, ?string &$err = '') : bool
{
$defPort = ini_get('mysqli.default_port');
$port = 0;
if (strstr($options['host'], ':'))
[$options['host'], $port] = explode(':', $options['host']);
try {
$link = @mysqli_connect($options['host'], $options['user'], $options['pass'], $options['db'], $port ?: $defPort);
mysqli_close($link);
}
catch (Exception $e)
{
$err = '['.mysqli_connect_errno().'] '.mysqli_connect_error();
return false;
}
return true;
}
public static function errorHandler($message, $data) public static function errorHandler($message, $data)
{ {
if (!error_reporting()) if (!error_reporting())
@@ -79,35 +51,6 @@ class DB
exit; exit;
} }
public static function logger($self, $query, $trace)
{
if ($trace) // actual query
self::$logs[] = [substr(str_replace("\n", ' ', $query), 0, 200)];
else // the statistics
{
end(self::$logs);
self::$logs[key(self::$logs)][] = substr(explode(';', $query)[0], 5);
}
}
public static function getLogs()
{
$out = '<pre><table style="font-size:12;"><tr><th></th><th>Time</th><th>Query</th></tr>';
foreach (self::$logs as $i => [$l, $t])
{
$c = 'inherit';
preg_match('/(\d+)/', $t, $m);
if ($m[1] > 100)
$c = '#FFA0A0';
else if ($m[1] > 20)
$c = '#FFFFA0';
$out .= '<tr><td>'.$i.'.</td><td style="background-color:'.$c.';">'.$t.'</td><td>'.$l.'</td></tr>';
}
return Util::jsEscape($out).'</table></pre>';
}
public static function getDB($idx) public static function getDB($idx)
{ {
return self::$interfaceCache[$idx]; return self::$interfaceCache[$idx];

File diff suppressed because it is too large Load Diff

View File

@@ -1,490 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class Game
{
public static $resistanceFields = array(
null, 'resHoly', 'resFire', 'resNature', 'resFrost', 'resShadow', 'resArcane'
);
public static $rarityColorStings = array( // zero-indexed
'9d9d9d', 'ffffff', '1eff00', '0070dd', 'a335ee', 'ff8000', 'e5cc80', 'e6cc80'
);
public static $specIconStrings = array(
-1 => 'inv_misc_questionmark',
0 => 'spell_nature_elementalabsorption',
6 => ['spell_deathknight_bloodpresence', 'spell_deathknight_frostpresence', 'spell_deathknight_unholypresence' ],
11 => ['spell_nature_starfall', 'ability_racial_bearform', 'spell_nature_healingtouch' ],
3 => ['ability_hunter_beasttaming', 'ability_marksmanship', 'ability_hunter_swiftstrike' ],
8 => ['spell_holy_magicalsentry', 'spell_fire_firebolt02', 'spell_frost_frostbolt02' ],
2 => ['spell_holy_holybolt', 'spell_holy_devotionaura', 'spell_holy_auraoflight' ],
5 => ['spell_holy_wordfortitude', 'spell_holy_holybolt', 'spell_shadow_shadowwordpain' ],
4 => ['ability_rogue_eviscerate', 'ability_backstab', 'ability_stealth' ],
7 => ['spell_nature_lightning', 'spell_nature_lightningshield', 'spell_nature_magicimmunity' ],
9 => ['spell_shadow_deathcoil', 'spell_shadow_metamorphosis', 'spell_shadow_rainoffire' ],
1 => ['ability_rogue_eviscerate', 'ability_warrior_innerrage', 'ability_warrior_defensivestance' ]
);
public static $classFileStrings = array(
null, 'warrior', 'paladin', 'hunter', 'rogue', 'priest', 'deathknight', 'shaman', 'mage', 'warlock', null, 'druid'
);
private static $combatRatingToItemMod = array( // zero-indexed idx:CR; val:Mod
null, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28,
29, 30, null, null, null, 37, 44
);
public static $lvlIndepRating = array( // rating doesn't scale with level
ITEM_MOD_MANA, ITEM_MOD_HEALTH, ITEM_MOD_ATTACK_POWER, ITEM_MOD_MANA_REGENERATION, ITEM_MOD_SPELL_POWER,
ITEM_MOD_HEALTH_REGEN, ITEM_MOD_SPELL_PENETRATION, ITEM_MOD_BLOCK_VALUE
);
public static $questClasses = array(
-2 => [ 0],
0 => [ 1, 3, 4, 8, 9, 10, 11, 12, 25, 28, 33, 36, 38, 40, 41, 44, 45, 46, 47, 51, 85, 130, 132, 139, 154, 267, 1497, 1519, 1537, 2257, 3430, 3431, 3433, 3487, 4080, 4298],
1 => [ 14, 15, 16, 17, 141, 148, 188, 215, 220, 331, 357, 361, 363, 400, 405, 406, 440, 490, 493, 618, 1377, 1637, 1638, 1657, 1769, 3524, 3525, 3526, 3557],
2 => [ 206, 209, 491, 717, 718, 719, 721, 722, 796, 1176, 1196, 1337, 1417, 1581, 1583, 1584, 1941, 2017, 2057, 2100, 2366, 2367, 2437, 2557, 3535, 3562, 3688, 3713, 3714, 3715, 3716, 3717, 3789, 3790, 3791, 3792, 3842, 3847, 3848, 3849, 3905, 4100, 4131, 4196, 4228, 4264, 4265, 4272, 4277, 4415, 4416, 4494, 4522, 4723, 4809, 4813, 4820],
3 => [ 1977, 2159, 2677, 2717, 3428, 3429, 3456, 3457, 3606, 3607, 3805, 3836, 3845, 3923, 3959, 4075, 4273, 4493, 4500, 4603, 4722, 4812, 4987],
4 => [ -372, -263, -262, -261, -162, -161, -141, -82, -81, -61],
5 => [ -373, -371, -324, -304, -264, -201, -182, -181, -121, -101, -24],
6 => [ -25, 2597, 3277, 3358, 3820, 4384, 4710],
7 => [-1010, -368, -367, -365, -344, -241, -1],
8 => [ 3483, 3518, 3519, 3520, 3521, 3522, 3523, 3679, 3703],
9 => [-1005, -1003, -1002, -1001, -376, -375, -374, -370, -369, -366, -364, -41, -22], // 22: seasonal
10 => [ 65, 66, 67, 210, 394, 495, 2817, 3537, 3711, 4024, 4197, 4395, 4742]
);
/* why:
Because petSkills (and ranged weapon skills) are the only ones with more than two skillLines attached. Because Left Joining ?_spell with ?_skillLineability causes more trouble than it has uses.
Because this is more or less the only reaonable way to fit all that information into one database field, so..
.. the indizes of this array are bits of skillLine2OrMask in ?_spell if skillLineId1 is negative
*/
public static $skillLineMask = array( // idx => [familyId, skillLineId]
-1 => array( // Pets (Hunter)
[ 1, 208], [ 2, 209], [ 3, 203], [ 4, 210], [ 5, 211], [ 6, 212], [ 7, 213], // Wolf, Cat, Spider, Bear, Boar, Crocolisk, Carrion Bird
[ 8, 214], [ 9, 215], [11, 217], [12, 218], [20, 236], [21, 251], [24, 653], // Crab, Gorilla, Raptor, Tallstrider, Scorpid, Turtle, Bat
[25, 654], [26, 655], [27, 656], [30, 763], [31, 767], [32, 766], [33, 765], // Hyena, Bird of Prey, Wind Serpent, Dragonhawk, Ravager, Warp Stalker, Sporebat
[34, 764], [35, 768], [37, 775], [38, 780], [39, 781], [41, 783], [42, 784], // Nether Ray, Serpent, Moth, Chimaera, Devilsaur, Silithid, Worm
[43, 786], [44, 785], [45, 787], [46, 788] // Rhino, Wasp, Core Hound, Spirit Beast
),
-2 => array( // Pets (Warlock)
[15, 189], [16, 204], [17, 205], [19, 207], [23, 188], [29, 761] // Felhunter, Voidwalker, Succubus, Doomguard, Imp, Felguard
),
-3 => array( // Ranged Weapons
[null, 45], [null, 46], [null, 226] // Bow, Gun, Crossbow
)
);
public static $sockets = array( // jsStyle Strings
'meta', 'red', 'yellow', 'blue'
);
// 'replicates' $WH.g_statToJson
public static $itemMods = array( // zero-indexed; "mastrtng": unused mastery; _[a-z] => taken mods..
'dmg', 'mana', 'health', 'agi', 'str', 'int', 'spi',
'sta', 'energy', 'rage', 'focus', 'runicpwr', 'defrtng', 'dodgertng',
'parryrtng', 'blockrtng', 'mlehitrtng', 'rgdhitrtng', 'splhitrtng', 'mlecritstrkrtng', 'rgdcritstrkrtng',
'splcritstrkrtng', '_mlehitrtng', '_rgdhitrtng', '_splhitrtng', '_mlecritstrkrtng', '_rgdcritstrkrtng', '_splcritstrkrtng',
'mlehastertng', 'rgdhastertng', 'splhastertng', 'hitrtng', 'critstrkrtng', '_hitrtng', '_critstrkrtng',
'resirtng', 'hastertng', 'exprtng', 'atkpwr', 'rgdatkpwr', 'feratkpwr', 'splheal',
'spldmg', 'manargn', 'armorpenrtng', 'splpwr', 'healthrgn', 'splpen', 'block', // ITEM_MOD_BLOCK_VALUE
'mastrtng', 'armor', 'firres', 'frores', 'holres', 'shares', 'natres',
'arcres', 'firsplpwr', 'frosplpwr', 'holsplpwr', 'shasplpwr', 'natsplpwr', 'arcsplpwr'
);
public static $class2SpellFamily = array(
// null Warrior Paladin Hunter Rogue Priest DK Shaman Mage Warlock null Druid
null, 4, 10, 9, 8, 6, 15, 11, 3, 5, null, 7
);
public static $areaFloors = array(
206 => 3, 209 => 7, 719 => 3, 721 => 4, 796 => 4, 1196 => 2, 1337 => 2, 1581 => 2, 1583 => 7, 1584 => 2,
2017 => 2, 2057 => 4, 2100 => 2, 2557 => 6, 2677 => 4, 3428 => 3, 3457 => 17, 3790 => 2, 3791 => 2, 3959 => 8,
3456 => 6, 3715 => 2, 3848 => 3, 3849 => 2, 4075 => 2, 4100 => 2, 4131 => 2, 4196 => 2, 4228 => 4, 4272 => 2,
4273 => 6, 4277 => 3, 4395 => 2, 4494 => 2, 4722 => 2, 4812 => 8
);
public static function itemModByRatingMask($mask)
{
if (($mask & 0x1C000) == 0x1C000) // special case resilience
return ITEM_MOD_RESILIENCE_RATING;
if (($mask & 0x00E0) == 0x00E0) // hit rating - all subcats (mle, rgd, spl)
return ITEM_MOD_HIT_RATING;
if (($mask & 0x0700) == 0x0700) // crit rating - all subcats (mle, rgd, spl)
return ITEM_MOD_CRIT_RATING;
for ($j = 0; $j < count(self::$combatRatingToItemMod); $j++)
{
if (!self::$combatRatingToItemMod[$j])
continue;
if (!($mask & (1 << $j)))
continue;
return self::$combatRatingToItemMod[$j];
}
return 0;
}
public static function sideByRaceMask($race)
{
// Any
if (!$race || ($race & RACE_MASK_ALL) == RACE_MASK_ALL)
return SIDE_BOTH;
// Horde
if ($race & RACE_MASK_HORDE && !($race & RACE_MASK_ALLIANCE))
return SIDE_HORDE;
// Alliance
if ($race & RACE_MASK_ALLIANCE && !($race & RACE_MASK_HORDE))
return SIDE_ALLIANCE;
return SIDE_BOTH;
}
public static function getReputationLevelForPoints($pts)
{
if ($pts >= 41999)
return REP_EXALTED;
else if ($pts >= 20999)
return REP_REVERED;
else if ($pts >= 8999)
return REP_HONORED;
else if ($pts >= 2999)
return REP_FRIENDLY;
else if ($pts >= 0)
return REP_NEUTRAL;
else if ($pts >= -3000)
return REP_UNFRIENDLY;
else if ($pts >= -6000)
return REP_HOSTILE;
else
return REP_HATED;
}
public static function getTaughtSpells(&$spell)
{
$extraIds = [-1]; // init with -1 to prevent empty-array errors
$lookup = [-1];
switch (gettype($spell))
{
case 'object':
if (get_class($spell) != 'SpellList')
return [];
$lookup[] = $spell->id;
foreach ($spell->canTeachSpell() as $idx)
$extraIds[] = $spell->getField('effect'.$idx.'TriggerSpell');
break;
case 'integer':
$lookup[] = $spell;
break;
case 'array':
$lookup = $spell;
break;
default:
return [];
}
// note: omits required spell and chance in skill_discovery_template
$data = array_merge(
DB::World()->selectCol('SELECT spellId FROM spell_learn_spell WHERE entry IN (?a)', $lookup),
DB::World()->selectCol('SELECT spellId FROM skill_discovery_template WHERE reqSpell IN (?a)', $lookup),
$extraIds
);
// return list of integers, not strings
$data = array_map('intVal', $data);
return $data;
}
public static function getPageText($ptId)
{
$pages = [];
while ($ptId)
{
if ($row = DB::World()->selectRow('SELECT ptl.Text AS Text_loc?d, pt.* FROM page_text pt LEFT JOIN page_text_locale ptl ON pt.ID = ptl.ID AND locale = ? WHERE pt.ID = ?d', User::$localeId, User::$localeString, $ptId))
{
$ptId = $row['NextPageID'];
$pages[] = Util::parseHtmlText(Util::localizedString($row, 'Text'));
}
else
{
trigger_error('Referenced PageTextId #'.$ptId.' is not in DB', E_USER_WARNING);
break;
}
}
return $pages;
}
/*********************/
/* World Pos. Checks */
/*********************/
private static $alphaMapCache = [];
private static function alphaMapCheck(int $areaId, array &$set) : bool
{
$file = 'setup/generated/alphaMaps/'.$areaId.'.png';
if (!file_exists($file)) // file does not exist (probably instanced area)
return false;
// invalid and corner cases (literally)
if (!is_array($set) || empty($set['posX']) || empty($set['posY']) || $set['posX'] >= 100 || $set['posY'] >= 100)
{
$set = null;
return true;
}
if (empty(self::$alphaMapCache[$areaId]))
self::$alphaMapCache[$areaId] = imagecreatefrompng($file);
// alphaMaps are 1000 x 1000, adapt points [black => valid point]
if (!imagecolorat(self::$alphaMapCache[$areaId], $set['posX'] * 10, $set['posY'] * 10))
$set = null;
return true;
}
public static function checkCoords(array $points) : array
{
$result = [];
$capitals = array( // capitals take precedence over their surroundings
1497, 1637, 1638, 3487, // Undercity, Ogrimmar, Thunder Bluff, Silvermoon City
1519, 1537, 1657, 3557, // Stormwind City, Ironforge, Darnassus, The Exodar
3703, 4395 // Shattrath City, Dalaran
);
foreach ($points as $res)
{
if (self::alphaMapCheck($res['areaId'], $res))
{
if (!$res)
continue;
// some rough measure how central the spawn is on the map (the lower the number, the better)
// 0: perfect center; 1: touches a border
$q = abs( (($res['posX'] - 50) / 50) * (($res['posY'] - 50) / 50) );
if (empty($result) || $result[0] > $q)
$result = [$q, $res];
}
else if (in_array($res['areaId'], $capitals)) // capitals (auto-discovered) and no hand-made alphaMap available
return $res;
else if (empty($result)) // add with lowest quality if alpha map is missing
$result = [1.0, $res];
}
// spawn does not really match on a map, but we need at least one result
if (!$result)
{
usort($points, function ($a, $b) { return ($a['dist'] < $b['dist']) ? -1 : 1; });
$result = [1.0, $points[0]];
}
return $result[1];
}
public static function getWorldPosForGUID(int $type, int ...$guids) : array
{
$result = [];
switch ($type)
{
case Type::NPC:
$result = DB::World()->select('SELECT `guid` AS ARRAY_KEY, `id`, `map` AS `mapId`, `position_y` AS `posX`, `position_x` AS `posY` FROM creature WHERE `guid` IN (?a)', $guids);
break;
case Type::OBJECT:
$result = DB::World()->select('SELECT `guid` AS ARRAY_KEY, `id`, `map` AS `mapId`, `position_y` AS `posX`, `position_x` AS `posY` FROM gameobject WHERE `guid` IN (?a)', $guids);
break;
case Type::SOUND:
$result = DB::AoWoW()->select('SELECT `soundId` AS ARRAY_KEY, `soundId` AS `id`, `mapId`, `posX`, `posY` FROM dbc_soundemitters WHERE `soundId` IN (?a)', $guids);
break;
case Type::AREATRIGGER:
$result = DB::AoWoW()->select('SELECT `id` AS ARRAY_KEY, `id`, `mapId`, `posX`, `posY` FROM dbc_areatrigger WHERE `id` IN (?a)', $guids);
break;
default:
trigger_error('Game::getWorldPosForGUID - instanced with unsupported TYPE '.$type, E_USER_WARNING);
}
return $result;
}
public static function worldPosToZonePos(int $mapId, float $posX, float $posY, int $areaId = 0, int $floor = -1) : array
{
if (!$mapId < 0)
return [];
$query = 'SELECT
dm.id,
wma.areaId,
IFNULL(dm.floor, 0) AS floor,
100 - ROUND(IF(dm.id IS NOT NULL, (?f - dm.minY) * 100 / (dm.maxY - dm.minY), (?f - wma.right) * 100 / (wma.left - wma.right)), 1) AS `posX`,
100 - ROUND(IF(dm.id IS NOT NULL, (?f - dm.minX) * 100 / (dm.maxX - dm.minX), (?f - wma.bottom) * 100 / (wma.top - wma.bottom)), 1) AS `posY`,
SQRT(POWER(abs(IF(dm.id IS NOT NULL, (?f - dm.minY) * 100 / (dm.maxY - dm.minY), (?f - wma.right) * 100 / (wma.left - wma.right)) - 50), 2) +
POWER(abs(IF(dm.id IS NOT NULL, (?f - dm.minX) * 100 / (dm.maxX - dm.minX), (?f - wma.bottom) * 100 / (wma.top - wma.bottom)) - 50), 2)) AS `dist`
FROM
dbc_worldmaparea wma
LEFT JOIN
dbc_dungeonmap dm ON dm.mapId = IF(?d AND (wma.mapId NOT IN (0, 1, 530, 571) OR wma.areaId = 4395), wma.mapId, -1)
WHERE
wma.mapId = ?d AND IF(?d, wma.areaId = ?d, wma.areaId <> 0){ AND IF(dm.floor IS NULL, 1, dm.floor = ?d)}
HAVING
(`posX` BETWEEN 0.1 AND 99.9 AND `posY` BETWEEN 0.1 AND 99.9)
ORDER BY
`dist` ASC';
// dist BETWEEN 0 (center) AND 70.7 (corner)
$points = DB::Aowow()->select($query, $posX, $posX, $posY, $posY, $posX, $posX, $posY, $posY, 1, $mapId, $areaId, $areaId, $floor < 0 ? DBSIMPLE_SKIP : $floor);
if (!$points) // retry: TC counts pre-instance subareas as instance-maps .. which have no map file
$points = DB::Aowow()->select($query, $posX, $posX, $posY, $posY, $posX, $posX, $posY, $posY, 0, $mapId, 0, 0, DBSIMPLE_SKIP);
if (!is_array($points))
{
trigger_error('Game::worldPosToZonePos - dbc query failed', E_USER_ERROR);
return [];
}
return $points;
}
public static function getQuotesForCreature(int $creatureId, bool $asHTML = false, string $talkSource = '') : array
{
$nQuotes = 0;
$quotes = [];
$soundIds = [];
$quoteSrc = DB::World()->select('
SELECT
ct.GroupID AS ARRAY_KEY, ct.ID as ARRAY_KEY2,
ct.`Type` AS `talkType`,
ct.TextRange AS `range`,
IFNULL(bct.`LanguageID`, ct.`Language`) AS lang,
IFNULL(NULLIF(bct.Text, ""), IFNULL(NULLIF(bct.Text1, ""), IFNULL(ct.`Text`, ""))) AS text_loc0,
{IFNULL(NULLIF(bctl.Text, ""), IFNULL(NULLIF(bctl.Text1, ""), IFNULL(ctl.Text, ""))) AS text_loc?d,}
IF(bct.SoundEntriesID > 0, bct.SoundEntriesID, ct.Sound) AS soundId
FROM
creature_text ct
{LEFT JOIN
creature_text_locale ctl ON ct.CreatureID = ctl.CreatureID AND ct.GroupID = ctl.GroupID AND ct.ID = ctl.ID AND ctl.Locale = ?}
LEFT JOIN
broadcast_text bct ON ct.BroadcastTextId = bct.ID
{LEFT JOIN
broadcast_text_locale bctl ON ct.BroadcastTextId = bctl.ID AND bctl.locale = ?}
WHERE
ct.CreatureID = ?d',
User::$localeId ?: DBSIMPLE_SKIP,
User::$localeId ? Util::$localeStrings[User::$localeId] : DBSIMPLE_SKIP,
User::$localeId ? Util::$localeStrings[User::$localeId] : DBSIMPLE_SKIP,
$creatureId
);
foreach ($quoteSrc as $grp => $text)
{
$group = [];
foreach ($text as $t)
{
if ($t['soundId'])
$soundIds[] = $t['soundId'];
$msg = Util::localizedString($t, 'text');
if (!$msg)
continue;
// fixup .. either set %s for emotes or dont >.<
if (in_array($t['talkType'], [2, 16]) && strpos($msg, '%s') === false)
$msg = '%s '.$msg;
// fixup: bad case-insensivity
$msg = Util::parseHtmlText(str_replace('%S', '%s', htmlentities($msg)), !$asHTML);
if ($talkSource)
$msg = sprintf($msg, $talkSource);
// make type css compatible
switch ($t['talkType'])
{
case 1: // yell:
case 14: $t['talkType'] = 1; break; // - dark red
case 2: // emote:
case 16: // "
case 3: // boss emote:
case 41: $t['talkType'] = 4; break; // - orange
case 4: // whisper:
case 15: // "
case 5: // boss whisper:
case 42: $t['talkType'] = 3; break; // - pink-ish
default: $t['talkType'] = 2; // [type: 0, 12] say: yellow-ish
}
// prefix
$pre = '';
if ($t['talkType'] != 4)
$pre = ($talkSource ?: '%s').' '.Lang::npc('textTypes', $t['talkType']).Lang::main('colon').($t['lang'] ? '['.Lang::game('languages', $t['lang']).'] ' : null);
if ($asHTML)
$msg = '<div><span class="s'.$t['talkType'].'">%s'.($t['range'] ? sprintf(Util::$dfnString, Lang::npc('textRanges', $t['range']), $msg) : $msg).'</span></div>';
else
$msg = '[div][span class=s'.$t['talkType'].']%s'.html_entity_decode($msg).'[/span][/div]';
$line = array(
'range' => $t['range'],
'text' => $msg,
'prefix' => $pre
);
$nQuotes++;
$group[] = $line;
}
if ($group)
$quotes[$grp] = $group;
}
return [$quotes, $nQuotes, $soundIds];
}
public static function getBreakpointsForSkill(int $skillId, int $reqLevel) : array
{
switch ($skillId)
{
case SKILL_HERBALISM:
case SKILL_LOCKPICKING:
case SKILL_JEWELCRAFTING:
case SKILL_INSCRIPTION:
case SKILL_SKINNING:
case SKILL_MINING:
$points = [$reqLevel]; // red/orange
if ($reqLevel + 25 <= MAX_SKILL) // orange/yellow
$points[] = $reqLevel + 25;
if ($reqLevel + 50 <= MAX_SKILL) // yellow/green
$points[] = $reqLevel + 50;
if ($reqLevel + 100 <= MAX_SKILL) // green/grey
$points[] = $reqLevel + 100;
return $points;
default:
return [$reqLevel];
}
}
}
?>

View File

@@ -9,31 +9,22 @@ if (file_exists('config/config.php'))
else else
$AoWoWconf = []; $AoWoWconf = [];
mb_internal_encoding('UTF-8');
define('OS_WIN', substr(PHP_OS, 0, 3) == 'WIN');
require_once 'includes/defines.php'; require_once 'includes/defines.php';
require_once 'includes/libs/DbSimple/Generic.php'; // Libraray: http://en.dklab.ru/lib/DbSimple (using variant: https://github.com/ivan1986/DbSimple/tree/master) require_once 'includes/libs/DbSimple/Generic.php'; // Libraray: http://en.dklab.ru/lib/DbSimple (using variant: https://github.com/ivan1986/DbSimple/tree/master)
require_once 'includes/utilities.php'; // helper functions require_once 'includes/utilities.php'; // misc™ data 'n func
require_once 'includes/game.php'; // game related data & functions require_once 'includes/ajaxHandler.class.php'; // handles ajax and jsonp requests
require_once 'includes/profiler.class.php';
require_once 'includes/user.class.php'; require_once 'includes/user.class.php';
require_once 'includes/markup.class.php'; // manipulate markup text require_once 'includes/markup.class.php'; // manipulate markup text
require_once 'includes/database.class.php'; // wrap DBSimple require_once 'includes/database.class.php'; // wrap DBSimple
require_once 'includes/community.class.php'; // handle comments, screenshots and videos require_once 'includes/community.class.php'; // handle comments, screenshots and videos
require_once 'includes/loot.class.php'; // build lv-tabs containing loot-information require_once 'includes/loot.class.php'; // build lv-tabs containing loot-information
require_once 'includes/smartAI.class.php';
require_once 'localization/lang.class.php'; require_once 'localization/lang.class.php';
require_once 'pages/genericPage.class.php'; require_once 'pages/genericPage.class.php';
// autoload List-classes, associated filters and pages // autoload List-classes, associated filters and pages
spl_autoload_register(function ($class) { spl_autoload_register(function ($class) {
$class = strtolower(str_replace('ListFilter', 'List', $class)); $class = strtolower(str_replace('Filter', '', $class));
if (class_exists($class)) // already registered if (class_exists($class)) // already registered
return; return;
@@ -41,37 +32,18 @@ spl_autoload_register(function ($class) {
if (preg_match('/[^\w]/i', $class)) // name should contain only letters if (preg_match('/[^\w]/i', $class)) // name should contain only letters
return; return;
if (stripos($class, 'list')) if (strpos($class, 'list'))
{ {
require_once 'includes/basetype.class.php'; if (!class_exists('BaseType'))
require_once 'includes/types/basetype.class.php';
$cl = strtr($class, ['list' => '']); if (file_exists('includes/types/'.strtr($class, ['list' => '']).'.class.php'))
if ($cl == 'remoteprofile' || $cl == 'localprofile') require_once 'includes/types/'.strtr($class, ['list' => '']).'.class.php';
$cl = 'profile';
if ($cl == 'remotearenateam' || $cl == 'localarenateam')
$cl = 'arenateam';
if ($cl == 'remoteguild' || $cl == 'localguild')
$cl = 'guild';
if (file_exists('includes/types/'.$cl.'.class.php'))
require_once 'includes/types/'.$cl.'.class.php';
else
throw new Exception('could not register type class: '.$cl);
return; return;
} }
else if (stripos($class, 'ajax') === 0)
{
require_once 'includes/ajaxHandler.class.php'; // handles ajax and jsonp requests
if (file_exists('includes/ajaxHandler/'.strtr($class, ['ajax' => '']).'.class.php')) if (file_exists('pages/'.strtr($class, ['page' => '']).'.php'))
require_once 'includes/ajaxHandler/'.strtr($class, ['ajax' => '']).'.class.php';
else
throw new Exception('could not register ajaxHandler class: '.$class);
return;
}
else if (file_exists('pages/'.strtr($class, ['page' => '']).'.php'))
require_once 'pages/'.strtr($class, ['page' => '']).'.php'; require_once 'pages/'.strtr($class, ['page' => '']).'.php';
}); });
@@ -93,56 +65,41 @@ if (!empty($AoWoWconf['characters']))
// load config to constants // load config to constants
function loadConfig(bool $noPHP = false) : void $sets = DB::isConnectable(DB_AOWOW) ? DB::Aowow()->select('SELECT `key` AS ARRAY_KEY, `value`, `flags` FROM ?_config') : [];
foreach ($sets as $k => $v)
{ {
$sets = DB::isConnectable(DB_AOWOW) ? DB::Aowow()->select('SELECT `key` AS ARRAY_KEY, `value`, `flags` FROM ?_config') : []; // this should not have been possible
foreach ($sets as $k => $v) if (!strlen($v['value']))
continue;
$php = $v['flags'] & CON_FLAG_PHP;
if ($v['flags'] & CON_FLAG_TYPE_INT)
$val = intVal($v['value']);
else if ($v['flags'] & CON_FLAG_TYPE_FLOAT)
$val = floatVal($v['value']);
else if ($v['flags'] & CON_FLAG_TYPE_BOOL)
$val = (bool)$v['value'];
else if ($v['flags'] & CON_FLAG_TYPE_STRING)
$val = preg_replace('/[^\p{L}0-9~\s_\-\'\/\.:,]/ui', '', $v['value']);
else
{ {
$php = $v['flags'] & CON_FLAG_PHP; Util::addNote(U_GROUP_ADMIN | U_GROUP_DEV, 'Kernel: '.($php ? 'PHP' : 'Aowow').' config value '.($php ? strtolower($k) : 'CFG_'.strtoupper($k)).' has no type set. Value forced to 0!');
if ($php && $noPHP) $val = 0;
continue;
// this should not have been possible
if (!strlen($v['value']) && !($v['flags'] & CON_FLAG_TYPE_STRING) && !$php)
{
trigger_error('Aowow config value CFG_'.strtoupper($k).' is empty - config will not be used!', E_USER_ERROR);
continue;
}
if ($v['flags'] & CON_FLAG_TYPE_INT)
$val = intVal($v['value']);
else if ($v['flags'] & CON_FLAG_TYPE_FLOAT)
$val = floatVal($v['value']);
else if ($v['flags'] & CON_FLAG_TYPE_BOOL)
$val = (bool)$v['value'];
else if ($v['flags'] & CON_FLAG_TYPE_STRING)
$val = preg_replace("/[\p{C}]/ui", '', $v['value']);
else if ($php)
{
trigger_error('PHP config value '.strtolower($k).' has no type set - config will not be used!', E_USER_ERROR);
continue;
}
else // if (!$php)
{
trigger_error('Aowow config value CFG_'.strtoupper($k).' has no type set - value forced to 0!', E_USER_ERROR);
$val = 0;
}
if ($php)
ini_set(strtolower($k), $val);
else if (!defined('CFG_'.strtoupper($k)))
define('CFG_'.strtoupper($k), $val);
} }
if ($php)
ini_set(strtolower($k), $val);
else
define('CFG_'.strtoupper($k), $val);
} }
loadConfig();
// handle non-fatal errors and notices
error_reporting(!empty($AoWoWconf['aowow']) && CFG_DEBUG ? E_AOWOW : 0); // handle occuring errors
set_error_handler(function($errNo, $errStr, $errFile, $errLine) error_reporting(!empty($AoWoWconf['aowow']) && CFG_DEBUG ? (E_ALL & ~(E_DEPRECATED | E_USER_DEPRECATED | E_STRICT)) : 0);
{ $errHandled = false;
set_error_handler(function($errNo, $errStr, $errFile, $errLine) use (&$errHandled) {
$errName = 'unknown error'; // errors not in this list can not be handled by set_error_handler (as per documentation) or are ignored $errName = 'unknown error'; // errors not in this list can not be handled by set_error_handler (as per documentation) or are ignored
$uGroup = U_GROUP_EMPLOYEE;
if ($errNo == E_WARNING) // 0x0002 if ($errNo == E_WARNING) // 0x0002
$errName = 'E_WARNING'; $errName = 'E_WARNING';
else if ($errNo == E_PARSE) // 0x0004 else if ($errNo == E_PARSE) // 0x0004
@@ -154,60 +111,29 @@ set_error_handler(function($errNo, $errStr, $errFile, $errLine)
else if ($errNo == E_USER_WARNING) // 0x0200 else if ($errNo == E_USER_WARNING) // 0x0200
$errName = 'E_USER_WARNING'; $errName = 'E_USER_WARNING';
else if ($errNo == E_USER_NOTICE) // 0x0400 else if ($errNo == E_USER_NOTICE) // 0x0400
{
$errName = 'E_USER_NOTICE'; $errName = 'E_USER_NOTICE';
$uGroup = U_GROUP_STAFF;
}
else if ($errNo == E_RECOVERABLE_ERROR) // 0x1000 else if ($errNo == E_RECOVERABLE_ERROR) // 0x1000
$errName = 'E_RECOVERABLE_ERROR'; $errName = 'E_RECOVERABLE_ERROR';
Util::addNote($uGroup, $errName.' - '.$errStr.' @ '.$errFile. ':'.$errLine); if (User::isInGroup(U_GROUP_STAFF))
if (CLI)
CLI::write($errName.' - '.$errStr.' @ '.$errFile. ':'.$errLine, $errNo & 0x40A ? CLI::LOG_WARN : CLI::LOG_ERROR);
if (DB::isConnectable(DB_AOWOW))
DB::Aowow()->query('INSERT INTO ?_errors (`date`, `version`, `phpError`, `file`, `line`, `query`, `userGroups`, `message`) VALUES (UNIX_TIMESTAMP(), ?d, ?d, ?, ?d, ?, ?d, ?) ON DUPLICATE KEY UPDATE `date` = UNIX_TIMESTAMP()',
AOWOW_REVISION, $errNo, $errFile, $errLine, CLI ? 'CLI' : ($_SERVER['QUERY_STRING'] ?? ''), User::$groups, $errStr
);
return true;
}, E_AOWOW);
// handle exceptions
set_exception_handler(function ($ex)
{
Util::addNote(U_GROUP_EMPLOYEE, 'Exception - '.$ex->getMessage().' @ '.$ex->getFile(). ':'.$ex->getLine()."\n".$ex->getTraceAsString());
if (DB::isConnectable(DB_AOWOW))
DB::Aowow()->query('INSERT INTO ?_errors (`date`, `version`, `phpError`, `file`, `line`, `query`, `userGroups`, `message`) VALUES (UNIX_TIMESTAMP(), ?d, ?d, ?, ?d, ?, ?d, ?) ON DUPLICATE KEY UPDATE `date` = UNIX_TIMESTAMP()',
AOWOW_REVISION, $ex->getCode(), $ex->getFile(), $ex->getLine(), CLI ? 'CLI' : ($_SERVER['QUERY_STRING'] ?? ''), User::$groups, $ex->getMessage()
);
if (!CLI)
(new GenericPage())->error();
else
echo 'Exception - '.$ex->getMessage()."\n ".$ex->getFile(). '('.$ex->getLine().")\n".$ex->getTraceAsString()."\n";
});
// handle fatal errors
register_shutdown_function(function()
{
if (($e = error_get_last()) && $e['type'] & (E_ERROR | E_COMPILE_ERROR | E_CORE_ERROR))
{ {
Util::addNote(U_GROUP_EMPLOYEE, 'Fatal Error - '.$e['message'].' @ '.$e['file']. ':'.$e['line']); if (!$errHandled)
{
Util::addNote(U_GROUP_STAFF, 'one or more php related error occured, while generating this page.');
$errHandled = true;
}
if (DB::isConnectable(DB_AOWOW)) Util::addNote(U_GROUP_STAFF, $errName.' - '.$errStr.' @ '.$errFile. ':'.$errLine);
DB::Aowow()->query('INSERT INTO ?_errors (`date`, `version`, `phpError`, `file`, `line`, `query`, `userGroups`, `message`) VALUES (UNIX_TIMESTAMP(), ?d, ?d, ?, ?d, ?, ?d, ?) ON DUPLICATE KEY UPDATE `date` = UNIX_TIMESTAMP()',
AOWOW_REVISION, $e['type'], $e['file'], $e['line'], CLI ? 'CLI' : ($_SERVER['QUERY_STRING'] ?? ''), User::$groups, $e['message']
);
if (CLI)
echo 'Fatal Error - '.$e['message'].' @ '.$e['file']. ':'.$e['line']."\n";
// cant generate a page for web view :(
die();
} }
});
if (DB::isConnectable(DB_AOWOW))
DB::Aowow()->query('INSERT INTO ?_errors (`date`, `version`, `phpError`, `file`, `line`, `query`, `userGroups`, `message`) VALUES (UNIX_TIMESTAMP(), ?d, ?d, ?, ?d, ?, ?d, ?) ON DUPLICATE KEY UPDATE `date` = UNIX_TIMESTAMP()',
AOWOW_REVISION, $errNo, $errFile, $errLine, CLI ? 'CLI' : $_SERVER['QUERY_STRING'], User::$groups, $errStr
);
return !((User::isInGroup(U_GROUP_STAFF) && defined('CFG_DEBUG') && CFG_DEBUG) || CLI);
}, E_ALL & ~(E_DEPRECATED | E_USER_DEPRECATED | E_STRICT));
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || (!empty($AoWoWconf['aowow']) && CFG_FORCE_SSL); $secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || (!empty($AoWoWconf['aowow']) && CFG_FORCE_SSL);
if (defined('CFG_STATIC_HOST')) // points js to images & scripts if (defined('CFG_STATIC_HOST')) // points js to images & scripts
@@ -219,54 +145,45 @@ if (defined('CFG_SITE_HOST')) // points js to exec
if (!CLI) if (!CLI)
{ {
if (!defined('CFG_SITE_HOST') || !defined('CFG_STATIC_HOST'))
die('error: SITE_HOST or STATIC_HOST not configured');
// Setup Session // Setup Session
if (CFG_SESSION_CACHE_DIR && Util::writeDir(CFG_SESSION_CACHE_DIR))
session_save_path(getcwd().'/'.CFG_SESSION_CACHE_DIR);
session_set_cookie_params(15 * YEAR, '/', '', $secure, true); session_set_cookie_params(15 * YEAR, '/', '', $secure, true);
session_cache_limiter('private'); session_cache_limiter('private');
if (!session_start()) session_start();
{
trigger_error('failed to start session', E_USER_ERROR);
exit;
}
if (!empty($AoWoWconf['aowow']) && User::init()) if (!empty($AoWoWconf['aowow']) && User::init())
User::save(); // save user-variables in session User::save(); // save user-variables in session
// set up some logging (~10 queries will execute before we init the user and load the config) // todo: (low) - move to setup web-interface (when it begins its existance)
if (CFG_DEBUG && User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN)) if (!defined('CFG_SITE_HOST') || !defined('CFG_STATIC_HOST'))
{ {
DB::Aowow()->setLogger(['DB', 'logger']); $host = substr($_SERVER['SERVER_NAME'].strtr($_SERVER['SCRIPT_NAME'], ['index.php' => '']), 0, -1);
DB::World()->setLogger(['DB', 'logger']);
if (DB::isConnected(DB_AUTH))
DB::Auth()->setLogger(['DB', 'logger']);
if (!empty($AoWoWconf['characters'])) define('HOST_URL', ($secure ? 'https://' : 'http://').$host);
foreach ($AoWoWconf['characters'] as $idx => $__) define('STATIC_URL', ($secure ? 'https://' : 'http://').$host.'/static');
if (DB::isConnected(DB_CHARACTERS . $idx))
DB::Characters($idx)->setLogger(['DB', 'logger']); if (User::isInGroup(U_GROUP_ADMIN)) // initial set
{
DB::Aowow()->query('REPLACE INTO ?_config VALUES (?a)',
[['site_host', $host, CON_FLAG_TYPE_STRING | CON_FLAG_PERSISTENT, 'default: '.$host.' - points js to executable files (automaticly set on first run)'],
['static_host', $host.'/static', CON_FLAG_TYPE_STRING | CON_FLAG_PERSISTENT, 'default: '.$host.'/static - points js to images & scripts (automaticly set on first run)']]
);
}
} }
// hard-override locale for this call (should this be here..?) // hard-override locale for this call (should this be here..?)
// all strings attached.. // all strings attached..
if (!empty($AoWoWconf['aowow'])) if (!empty($AoWoWconf['aowow']))
{ {
if (isset($_GET['locale']) && (int)$_GET['locale'] <= MAX_LOCALES && (int)$_GET['locale'] >= 0) if (isset($_GET['locale']) && (CFG_LOCALES & (1 << (int)$_GET['locale'])))
if (CFG_LOCALES & (1 << $_GET['locale'])) User::useLocale($_GET['locale']);
User::useLocale($_GET['locale']);
Lang::load(User::$localeString); Lang::load(User::$localeString);
} }
// parse page-parameters .. sanitize before use! // parse page-parameters .. sanitize before use!
$str = explode('&', mb_strtolower($_SERVER['QUERY_STRING'] ?? ''), 2)[0]; $str = explode('&', $_SERVER['QUERY_STRING'], 2)[0];
$_ = explode('=', $str, 2); $_ = explode('=', $str, 2);
$pageCall = $_[0]; $pageCall = $_[0];
$pageParam = $_[1] ?? ''; $pageParam = isset($_[1]) ? $_[1] : null;
Util::$wowheadLink = 'http://'.Util::$subDomains[User::$localeId].'.wowhead.com/'.$str; Util::$wowheadLink = 'http://'.Util::$subDomains[User::$localeId].'.wowhead.com/'.$str;
} }

View File

@@ -159,8 +159,9 @@ class DbSimple_Connect
* *
* @param string $query запрос * @param string $query запрос
*/ */
public function addInit(...$args) public function addInit($query)
{ {
$args = func_get_args();
if ($this->DbSimple !== null) if ($this->DbSimple !== null)
return call_user_func_array(array(&$this->DbSimple, 'query'), $args); return call_user_func_array(array(&$this->DbSimple, 'query'), $args);
$this->init[] = $args; $this->init[] = $args;

View File

@@ -144,8 +144,9 @@ abstract class DbSimple_Database extends DbSimple_LastError
* mixed select(string $query [, $arg1] [,$arg2] ...) * mixed select(string $query [, $arg1] [,$arg2] ...)
* Execute query and return the result. * Execute query and return the result.
*/ */
public function select(...$args) public function select($query)
{ {
$args = func_get_args();
$total = false; $total = false;
return $this->_query($args, $total); return $this->_query($args, $total);
} }
@@ -156,8 +157,10 @@ abstract class DbSimple_Database extends DbSimple_LastError
* Total number of found rows (independent to LIMIT) is returned in $total * Total number of found rows (independent to LIMIT) is returned in $total
* (in most cases second query is performed to calculate $total). * (in most cases second query is performed to calculate $total).
*/ */
public function selectPage(&$total, ...$args) public function selectPage(&$total, $query)
{ {
$args = func_get_args();
array_shift($args);
$total = true; $total = true;
return $this->_query($args, $total); return $this->_query($args, $total);
} }
@@ -170,8 +173,9 @@ abstract class DbSimple_Database extends DbSimple_LastError
* because PHP DOES NOT generates notice on $row['abc'] if $row === null * because PHP DOES NOT generates notice on $row['abc'] if $row === null
* or $row === false (but, if $row is empty array, notice is generated). * or $row === false (but, if $row is empty array, notice is generated).
*/ */
public function selectRow(...$args) public function selectRow()
{ {
$args = func_get_args();
$total = false; $total = false;
$rows = $this->_query($args, $total); $rows = $this->_query($args, $total);
if (!is_array($rows)) return $rows; if (!is_array($rows)) return $rows;
@@ -184,8 +188,9 @@ abstract class DbSimple_Database extends DbSimple_LastError
* array selectCol(string $query [, $arg1] [,$arg2] ...) * array selectCol(string $query [, $arg1] [,$arg2] ...)
* Return the first column of query result as array. * Return the first column of query result as array.
*/ */
public function selectCol(...$args) public function selectCol()
{ {
$args = func_get_args();
$total = false; $total = false;
$rows = $this->_query($args, $total); $rows = $this->_query($args, $total);
if (!is_array($rows)) return $rows; if (!is_array($rows)) return $rows;
@@ -198,8 +203,9 @@ abstract class DbSimple_Database extends DbSimple_LastError
* Return the first cell of the first column of query result. * Return the first cell of the first column of query result.
* If no one row selected, return null. * If no one row selected, return null.
*/ */
public function selectCell(...$args) public function selectCell()
{ {
$args = func_get_args();
$total = false; $total = false;
$rows = $this->_query($args, $total); $rows = $this->_query($args, $total);
if (!is_array($rows)) return $rows; if (!is_array($rows)) return $rows;
@@ -215,8 +221,9 @@ abstract class DbSimple_Database extends DbSimple_LastError
* mixed query(string $query [, $arg1] [,$arg2] ...) * mixed query(string $query [, $arg1] [,$arg2] ...)
* Alias for select(). May be used for INSERT or UPDATE queries. * Alias for select(). May be used for INSERT or UPDATE queries.
*/ */
public function query(...$args) public function query()
{ {
$args = func_get_args();
$total = false; $total = false;
return $this->_query($args, $total); return $this->_query($args, $total);
} }
@@ -239,8 +246,9 @@ abstract class DbSimple_Database extends DbSimple_LastError
* Нужно для сложных запросов, состоящих из кусков, которые полезно сохранить * Нужно для сложных запросов, состоящих из кусков, которые полезно сохранить
* *
*/ */
public function subquery(...$args) public function subquery()
{ {
$args = func_get_args();
$this->_expandPlaceholders($args,$this->_placeholderNativeArgs !== null); $this->_expandPlaceholders($args,$this->_placeholderNativeArgs !== null);
return new DbSimple_SubQuery($args); return new DbSimple_SubQuery($args);
} }

View File

@@ -30,7 +30,7 @@ class DbSimple_Mysqli extends DbSimple_Database
* constructor(string $dsn) * constructor(string $dsn)
* Connect to MySQL server. * Connect to MySQL server.
*/ */
function __construct($dsn) function DbSimple_Mysqli($dsn)
{ {
if (!is_callable("mysqli_connect")) if (!is_callable("mysqli_connect"))
@@ -159,7 +159,6 @@ class DbSimple_Mysqli extends DbSimple_Database
{ {
$this->_lastQuery = $queryMain; $this->_lastQuery = $queryMain;
$this->_expandPlaceholders($queryMain, false); $this->_expandPlaceholders($queryMain, false);
mysqli_ping($this->link);
$result = mysqli_query($this->link, $queryMain[0]); $result = mysqli_query($this->link, $queryMain[0]);
if ($result === false) if ($result === false)
return $this->_setDbError($queryMain[0]); return $this->_setDbError($queryMain[0]);
@@ -179,7 +178,7 @@ class DbSimple_Mysqli extends DbSimple_Database
protected function _performFetch($result) protected function _performFetch($result)
{ {
$row = mysqli_fetch_assoc($result); $row = mysqli_fetch_assoc($result);
if (mysqli_error($this->link)) return $this->_setDbError($this->_lastQuery); if (mysql_error()) return $this->_setDbError($this->_lastQuery);
if ($row === false) return null; if ($row === false) return null;
return $row; return $row;
} }

View File

@@ -1,187 +0,0 @@
<?php
/****************************************
Example of how to use this uploader class...
You can uncomment the following lines (minus the require) to use these as your defaults.
// list of valid extensions, ex. array("jpeg", "xml", "bmp")
$allowedExtensions = array();
// max file size in bytes
$sizeLimit = 10 * 1024 * 1024;
require('valums-file-uploader/server/php.php');
$uploader = new qqFileUploader($allowedExtensions, $sizeLimit);
// Call handleUpload() with the name of the folder, relative to PHP's getcwd()
$result = $uploader->handleUpload('uploads/');
// to pass data through iframe you will need to encode all html tags
echo htmlspecialchars(json_encode($result), ENT_NOQUOTES);
/******************************************/
/**
* Handle file uploads via XMLHttpRequest
*/
class qqUploadedFileXhr
{
/**
* Save the file to the specified path
* @return boolean TRUE on success
*/
function save(string $path) : bool
{
$input = fopen("php://input", "r");
$temp = tmpfile();
$realSize = stream_copy_to_stream($input, $temp);
fclose($input);
if ($realSize != $this->getSize())
return false;
$target = fopen($path, "w");
fseek($temp, 0, SEEK_SET);
stream_copy_to_stream($temp, $target);
fclose($target);
return true;
}
function getName() : string
{
return $_GET['qqfile'];
}
function getSize(): int
{
if (isset($_SERVER["CONTENT_LENGTH"]))
return (int)$_SERVER["CONTENT_LENGTH"];
throw new Exception('Getting content length is not supported.');
return 0;
}
}
/**
* Handle file uploads via regular form post (uses the $_FILES array)
*/
class qqUploadedFileForm
{
/**
* Save the file to the specified path
* @return boolean TRUE on success
*/
function save(string $path) : bool
{
if(!move_uploaded_file($_FILES['qqfile']['tmp_name'], $path))
return false;
return true;
}
function getName() : string
{
return $_FILES['qqfile']['name'];
}
function getSize() : int
{
return $_FILES['qqfile']['size'];
}
}
class qqFileUploader
{
private $allowedExtensions = array();
private $sizeLimit = 10485760;
private $file;
public function __construct(array $allowedExtensions = array(), $sizeLimit = 10485760)
{
$this->allowedExtensions = array_map("strtolower", $allowedExtensions);
$this->sizeLimit = $sizeLimit;
$this->checkServerSettings();
if (isset($_GET['qqfile']))
$this->file = new qqUploadedFileXhr();
else if (isset($_FILES['qqfile']))
$this->file = new qqUploadedFileForm();
else
$this->file = null;
}
public function getName() : string
{
return $this->file?->getName() ?? '';
}
private function checkServerSettings() : void
{
$postSize = $this->toBytes(ini_get('post_max_size'));
$uploadSize = $this->toBytes(ini_get('upload_max_filesize'));
if ($postSize < $this->sizeLimit || $uploadSize < $this->sizeLimit)
{
$size = max(1, $this->sizeLimit / 1024 / 1024) . 'M';
die("{'error':'increase post_max_size and upload_max_filesize to $size'}");
}
}
private function toBytes(string $str) : int
{
$val = trim($str);
$last = strtolower(substr($str, -1, 1));
switch ($last)
{
case 'g': $val *= 1024;
case 'm': $val *= 1024;
case 'k': $val *= 1024;
}
return $val;
}
/**
* Returns array('success' => true, 'newFilename' => 'myDoc123.doc') or array('error' => 'error message')
*/
function handleUpload(string $uploadDirectory, string $newName = '', bool $replaceOldFile = FALSE) : array
{
if (!is_writable($uploadDirectory))
return ['error' => "Server error. Upload directory isn't writable."];
if (!$this->file)
return ['error' => 'No files were uploaded.'];
$size = $this->file->getSize();
if ($size == 0)
return ['error' => 'File is empty'];
if ($size > $this->sizeLimit)
return ['error' => 'File is too large'];
$pathinfo = pathinfo($this->getName());
$filename = $newName ?: $pathinfo['filename'];
//$filename = md5(uniqid());
$ext = @$pathinfo['extension']; // hide notices if extension is empty
if ($this->allowedExtensions && !in_array(strtolower($ext), $this->allowedExtensions))
{
$these = implode(', ', $this->allowedExtensions);
return ['error' => 'File has an invalid extension, it should be one of '. $these . '.'];
}
// don't overwrite previous files that were uploaded
if (!$replaceOldFile)
while (file_exists($uploadDirectory . $filename . '.' . $ext))
$filename .= rand(10, 99);
if ($this->file->save($uploadDirectory . $filename . '.' . $ext))
return ['success' => true, 'newFilename' => $filename . '.' . $ext];
else
return ['error' => 'Could not save uploaded file. The upload was cancelled, or server error encountered'];
}
}

View File

@@ -1,7 +1,7 @@
<?php <?php
if (!defined('AOWOW_REVISION')) if (!defined('AOWOW_REVISION'))
die('illegal access'); die('invalid access');
/* from TC wiki /* from TC wiki
@@ -25,7 +25,6 @@ class Loot
private $entry = 0; // depending on the lookup itemId oder templateId private $entry = 0; // depending on the lookup itemId oder templateId
private $results = []; private $results = [];
private $chanceMods = [];
private $lootTemplates = array( private $lootTemplates = array(
LOOT_REFERENCE, // internal LOOT_REFERENCE, // internal
LOOT_ITEM, // item LOOT_ITEM, // item
@@ -41,33 +40,33 @@ class Loot
LOOT_SPELL // spell LOOT_SPELL // spell
); );
public function &iterate() : iterable public function &iterate()
{ {
reset($this->results); reset($this->results);
foreach ($this->results as $k => $__) while (list($k, $__) = each($this->results))
yield $k => $this->results[$k]; yield $k => $this->results[$k];
} }
public function getResult() : array public function getResult()
{ {
return $this->results; return $this->results;
} }
private function createStack(array $l) : string // issue: TC always has an equal distribution between min/max private function createStack($l) // issue: TC always has an equal distribution between min/max
{ {
if (empty($l['min']) || empty($l['max']) || $l['max'] <= $l['min']) if (empty($l['min']) || empty($l['max']) || $l['max'] <= $l['min'])
return ''; return null;
$stack = []; $stack = [];
for ($i = $l['min']; $i <= $l['max']; $i++) for ($i = $l['min']; $i <= $l['max']; $i++)
$stack[$i] = round(100 / (1 + $l['max'] - $l['min']), 3); $stack[$i] = round(100 / (1 + $l['max'] - $l['min']), 3);
// yes, it wants a string .. how weired is that.. // yes, it wants a string .. how weired is that..
return json_encode($stack, JSON_NUMERIC_CHECK); // do not replace with Util::toJSON ! return json_encode($stack, JSON_NUMERIC_CHECK);
} }
private function storeJSGlobals(array $data) : void private function storeJSGlobals($data)
{ {
foreach ($data as $type => $jsData) foreach ($data as $type => $jsData)
{ {
@@ -82,62 +81,7 @@ class Loot
} }
} }
private function calcChance(array $refs, array $parents = []) : array private function getByContainerRecursive($tableName, $lootId, &$handledRefs, $groupId = 0, $baseChance = 1.0)
{
$retData = [];
$retKeys = [];
foreach ($refs as $rId => $ref)
{
// check for possible database inconsistencies
if (!$ref['chance'] && !$ref['isGrouped'])
trigger_error('Loot by Item: Ungrouped Item/Ref '.$ref['item'].' has 0% chance assigned!', E_USER_WARNING);
if ($ref['isGrouped'] && $ref['sumChance'] > 100)
trigger_error('Loot by Item: Group with Item/Ref '.$ref['item'].' has '.number_format($ref['sumChance'], 2).'% total chance! Some items cannot drop!', E_USER_WARNING);
if ($ref['isGrouped'] && $ref['sumChance'] >= 100 && !$ref['chance'])
trigger_error('Loot by Item: Item/Ref '.$ref['item'].' with adaptive chance cannot drop. Group already at 100%!', E_USER_WARNING);
$chance = abs($ref['chance'] ?: (100 - $ref['sumChance']) / $ref['nZeroItems']) / 100;
// apply inherited chanceMods
if (isset($this->chanceMods[$ref['item']]))
{
$chance *= $this->chanceMods[$ref['item']][0];
$chance = 1 - pow(1 - $chance, $this->chanceMods[$ref['item']][1]);
}
// save chance for parent-ref
$this->chanceMods[$rId] = [$chance, $ref['multiplier']];
// refTemplate doesn't point to a new ref -> we are done
if (!in_array($rId, $parents))
{
$data = array(
'percent' => $chance,
'stack' => [$ref['min'], $ref['max']],
'count' => 1 // ..and one for the sort script
);
if ($_ = self::createStack($ref))
$data['pctstack'] = $_;
// sort highest chances first
$i = 0;
for (; $i < count($retData); $i++)
if ($retData[$i]['percent'] < $data['percent'])
break;
array_splice($retData, $i, 0, [$data]);
array_splice($retKeys, $i, 0, [$rId]);
}
}
return array_combine($retKeys, $retData);
}
private function getByContainerRecursive(string $tableName, int $lootId, array &$handledRefs, int $groupId = 0, float $baseChance = 1.0) : ?array
{ {
$loot = []; $loot = [];
$rawItems = []; $rawItems = [];
@@ -157,8 +101,7 @@ class Loot
'quest' => $entry['QuestRequired'], 'quest' => $entry['QuestRequired'],
'group' => $entry['GroupId'], 'group' => $entry['GroupId'],
'parentRef' => $tableName == LOOT_REFERENCE ? $lootId : 0, 'parentRef' => $tableName == LOOT_REFERENCE ? $lootId : 0,
'realChanceMod' => $baseChance, 'realChanceMod' => $baseChance
'groupChance' => 0
); );
// if ($entry['LootMode'] > 1) // if ($entry['LootMode'] > 1)
@@ -191,7 +134,7 @@ class Loot
// bandaid.. remove when propperly handling lootmodes // bandaid.. remove when propperly handling lootmodes
if (!in_array($entry['Reference'], $handledRefs)) if (!in_array($entry['Reference'], $handledRefs))
{ // todo (high): find out, why i used this in the first place. (don't do drugs, kids) { // todo (high): find out, why i used this in the first place. (don't do drugs, kids)
[$data, $raw] = self::getByContainerRecursive(LOOT_REFERENCE, $entry['Reference'], $handledRefs, /*$entry['GroupId'],*/ 0, $entry['Chance'] / 100); list($data, $raw) = self::getByContainerRecursive(LOOT_REFERENCE, $entry['Reference'], $handledRefs, /*$entry['GroupId'],*/ 0, $entry['Chance'] / 100);
$handledRefs[] = $entry['Reference']; $handledRefs[] = $entry['Reference'];
@@ -224,19 +167,15 @@ class Loot
} }
else if ($entry['GroupId'] && $entry['Chance']) else if ($entry['GroupId'] && $entry['Chance'])
{ {
if (empty($groupChances[$entry['GroupId']]))
$groupChances[$entry['GroupId']] = 0;
$groupChances[$entry['GroupId']] += $entry['Chance'];
$set['groupChance'] = $entry['Chance']; $set['groupChance'] = $entry['Chance'];
if (!$entry['Reference'])
{
if (empty($groupChances[$entry['GroupId']]))
$groupChances[$entry['GroupId']] = 0;
$groupChances[$entry['GroupId']] += $entry['Chance'];
}
} }
else // shouldn't have happened else // shouldn't have happened
{ {
trigger_error('Unhandled case in calculating chance for item '.$entry['Item'].'!', E_USER_WARNING); Util::addNote(U_GROUP_EMPLOYEE, 'Loot::getByContainerRecursive: unhandled case in calculating chance for item '.$entry['Item'].'!');
continue; continue;
} }
@@ -250,22 +189,24 @@ class Loot
$sum = 0; $sum = 0;
else if ($sum >= 100.01) else if ($sum >= 100.01)
{ {
trigger_error('Loot entry '.$lootId.' / group '.$k.' has a total chance of '.number_format($sum, 2).'%. Some items cannot drop!', E_USER_WARNING); Util::addNote(U_GROUP_EMPLOYEE, 'Loot::getByContainerRecursive: entry '.$lootId.' / group '.$k.' has a total chance of '.number_format($sum, 2).'%. Some items cannot drop!');
$sum = 100; $sum = 100;
} }
// is applied as backReference to items with 0-chance
$groupChances[$k] = (100 - $sum) / ($nGroupEquals[$k] ?: 1); $cnt = empty($nGroupEquals[$k]) ? 1 : $nGroupEquals[$k];
$groupChances[$k] = (100 - $sum) / $cnt; // is applied as backReference to items with 0-chance
} }
return [$loot, array_unique($rawItems)]; return [$loot, array_unique($rawItems)];
} }
public function getByContainer(string $table, int $entry): bool public function getByContainer($table, $entry)
{ {
$this->entry = intVal($entry); $this->entry = intVal($entry);
if (!in_array($table, $this->lootTemplates) || !$this->entry) if (!in_array($table, $this->lootTemplates) || !$this->entry)
return false; return null;
/* /*
todo (high): implement conditions on loot (and conditions in general) todo (high): implement conditions on loot (and conditions in general)
@@ -328,7 +269,7 @@ class Loot
); );
$this->results[] = array_merge($base, $data); $this->results[] = array_merge($base, $data);
$this->jsGlobals[Type::ITEM][$loot['reference']] = $data; $this->jsGlobals[TYPE_ITEM][$loot['reference']] = $data;
} }
} }
@@ -371,16 +312,16 @@ class Loot
break; break;
} }
$this->extraCols[] = "\$Listview.funcBox.createSimpleCol('group', 'Group', '7%', 'group')"; $this->extraCols[] = "Listview.funcBox.createSimpleCol('group', 'Group', '7%', 'group')";
foreach ($fields as $idx => $field) foreach ($fields as $idx => $field)
if ($set & (1 << $idx)) if ($set & (1 << $idx))
$this->extraCols[] = "\$Listview.funcBox.createSimpleCol('".$field."', '".Util::ucFirst($field)."', '7%', '".$field."')"; $this->extraCols[] = "Listview.funcBox.createSimpleCol('".$field."', '".Util::ucFirst($field)."', '7%', '".$field."')";
} }
return true; return true;
} }
public function getByItem(int $entry, int $maxResults = CFG_SQL_LIMIT_DEFAULT, array $lootTableList = []) : bool public function getByItem($entry, $maxResults = CFG_SQL_LIMIT_DEFAULT, $lootTableList = [])
{ {
$this->entry = intVal($entry); $this->entry = intVal($entry);
@@ -409,12 +350,13 @@ class Loot
['achievement', [], '$LANG.tab_rewardfrom', 'reward-from-achievement', [], [], []] ['achievement', [], '$LANG.tab_rewardfrom', 'reward-from-achievement', [], [], []]
); );
$refResults = []; $refResults = [];
$chanceMods = [];
$query = 'SELECT $query = 'SELECT
lt1.entry AS ARRAY_KEY, lt1.entry AS ARRAY_KEY,
IF(lt1.reference = 0, lt1.item, lt1.reference) AS item, IF(lt1.reference = 0, lt1.item, lt1.reference) AS item,
lt1.chance, lt1.chance,
SUM(IF(lt2.chance = 0, 1, 0)) AS nZeroItems, SUM(IF(lt2.chance = 0, 1, 0)) AS nZeroItems,
SUM(IF(lt2.reference = 0, lt2.chance, 0)) AS sumChance, SUM(lt2.chance) AS sumChance,
IF(lt1.groupid > 0, 1, 0) AS isGrouped, IF(lt1.groupid > 0, 1, 0) AS isGrouped,
IF(lt1.reference = 0, lt1.mincount, 1) AS min, IF(lt1.reference = 0, lt1.mincount, 1) AS min,
IF(lt1.reference = 0, lt1.maxcount, 1) AS max, IF(lt1.reference = 0, lt1.maxcount, 1) AS max,
@@ -427,6 +369,61 @@ class Loot
%s %s
GROUP BY lt2.entry, lt2.groupid'; GROUP BY lt2.entry, lt2.groupid';
$calcChance = function ($refs, $parents = []) use (&$chanceMods)
{
$retData = [];
$retKeys = [];
foreach ($refs as $rId => $ref)
{
// check for possible database inconsistencies
if (!$ref['chance'] && !$ref['isGrouped'])
Util::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: ungrouped Item/Ref '.$ref['item'].' has 0% chance assigned!');
if ($ref['isGrouped'] && $ref['sumChance'] > 100)
Util::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: group with Item/Ref '.$ref['item'].' has '.number_format($ref['sumChance'], 2).'% total chance! Some items cannot drop!');
if ($ref['isGrouped'] && $ref['sumChance'] >= 100 && !$ref['chance'])
Util::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: Item/Ref '.$ref['item'].' with adaptive chance cannot drop. Group already at 100%!');
$chance = abs($ref['chance'] ?: (100 - $ref['sumChance']) / $ref['nZeroItems']) / 100;
// apply inherited chanceMods
if (isset($chanceMods[$ref['item']]))
{
$chance *= $chanceMods[$ref['item']][0];
$chance = 1 - pow(1 - $chance, $chanceMods[$ref['item']][1]);
}
// save chance for parent-ref
$chanceMods[$rId] = [$chance, $ref['multiplier']];
// refTemplate doesn't point to a new ref -> we are done
if (!in_array($rId, $parents))
{
$data = array(
'percent' => $chance,
'stack' => [$ref['min'], $ref['max']],
'count' => 1 // ..and one for the sort script
);
if ($_ = self::createStack($ref))
$data['pctstack'] = $_;
// sort highest chances first
$i = 0;
for (; $i < count($retData); $i++)
if ($retData[$i]['percent'] < $data['percent'])
break;
array_splice($retData, $i, 0, [$data]);
array_splice($retKeys, $i, 0, [$rId]);
}
}
return array_combine($retKeys, $retData);
};
/* /*
get references containing the item get references containing the item
*/ */
@@ -445,7 +442,7 @@ class Loot
array_keys($curRefs) array_keys($curRefs)
); );
$refResults += $this->calcChance($curRefs, array_column($newRefs, 'item')); $refResults += $calcChance($curRefs, array_column($newRefs, 'item'));
} }
/* /*
@@ -456,7 +453,7 @@ class Loot
if ($lootTableList && !in_array($this->lootTemplates[$i], $lootTableList)) if ($lootTableList && !in_array($this->lootTemplates[$i], $lootTableList))
continue; continue;
$result = $this->calcChance(DB::World()->select( $result = $calcChance(DB::World()->select(
sprintf($query, '{lt1.reference IN (?a) OR }(lt1.reference = 0 AND lt1.item = ?d)'), sprintf($query, '{lt1.reference IN (?a) OR }(lt1.reference = 0 AND lt1.item = ?d)'),
$this->lootTemplates[$i], $this->lootTemplates[$i], $this->lootTemplates[$i], $this->lootTemplates[$i],
$refResults ? array_keys($refResults) : DBSIMPLE_SKIP, $refResults ? array_keys($refResults) : DBSIMPLE_SKIP,
@@ -497,7 +494,7 @@ class Loot
$srcData = $srcObj->getListviewData(); $srcData = $srcObj->getListviewData();
foreach ($srcObj->iterate() as $curTpl) foreach ($srcObj->iterate() as $__id => $curTpl)
{ {
switch ($curTpl['typeCat']) switch ($curTpl['typeCat'])
{ {
@@ -508,7 +505,7 @@ class Loot
} }
$tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField('lootId')]); $tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField('lootId')]);
$tabsFinal[$tabId][4][] = '$Listview.extraCols.percent'; $tabsFinal[$tabId][4][] = 'Listview.extraCols.percent';
if ($tabId != 15) if ($tabId != 15)
$tabsFinal[$tabId][6][] = 'skill'; $tabsFinal[$tabId][6][] = 'skill';
} }
@@ -533,7 +530,7 @@ class Loot
// achievement part // achievement part
$conditions = array(['itemExtra', $this->entry]); $conditions = array(['itemExtra', $this->entry]);
if ($ar = DB::World()->selectCol('SELECT ID FROM achievement_reward WHERE ItemID = ?d{ OR MailTemplateID IN (?a)}', $this->entry, $ids ?: DBSIMPLE_SKIP)) if ($ar = DB::World()->selectCol('SELECT entry FROM achievement_reward WHERE item = ?d{ OR mailTemplate IN (?a)}', $this->entry, $ids ?: DBSIMPLE_SKIP))
array_push($conditions, ['id', $ar], 'OR'); array_push($conditions, ['id', $ar], 'OR');
$srcObj = new AchievementList($conditions); $srcObj = new AchievementList($conditions);
@@ -566,7 +563,7 @@ class Loot
$srcData = $srcObj->getListviewData(); $srcData = $srcObj->getListviewData();
if (!empty($result)) if (!empty($result))
$tabsFinal[16][4][] = '$Listview.extraCols.percent'; $tabsFinal[16][4][] = 'Listview.extraCols.percent';
if ($srcObj->hasSetFields(['reagent1'])) if ($srcObj->hasSetFields(['reagent1']))
$tabsFinal[16][6][] = 'reagents'; $tabsFinal[16][6][] = 'reagents';
@@ -604,35 +601,17 @@ class Loot
$tabId = abs($tabId); // general case (skinning) $tabId = abs($tabId); // general case (skinning)
$tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField($field)]); $tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField($field)]);
$tabsFinal[$tabId][4][] = '$Listview.extraCols.percent'; $tabsFinal[$tabId][4][] = 'Listview.extraCols.percent';
} }
} }
break; break;
} }
} }
foreach ($tabsFinal as $tabId => $data) $this->results = $tabsFinal;
{
$tabData = array(
'data' => $data[1],
'name' => $data[2],
'id' => $data[3]
);
if ($data[4])
$tabData['extraCols'] = array_unique($data[4]);
if ($data[5])
$tabData['hiddenCols'] = array_unique($data[5]);
if ($data[6])
$tabData['visibleCols'] = array_unique($data[6]);
$this->results[$tabId] = [$data[0], $tabData];
}
return true; return true;
} }
} }
?> ?>

View File

@@ -1,7 +1,7 @@
<?php <?php
if (!defined('AOWOW_REVISION')) if (!defined('AOWOW_REVISION'))
die('illegal access'); die('invalid access');
/* /*
this is just a skeleton for now this is just a skeleton for now
@@ -14,8 +14,6 @@ class Markup
private $text = ''; private $text = '';
private $jsGlobals = []; private $jsGlobals = [];
private static $dbTagPattern = '/(?<!\\\\)\[(npc|object|item|itemset|quest|spell|zone|faction|pet|achievement|statistic|title|event|class|race|skill|currency|emote|enchantment|money|sound|icondb)=(-?\d+)[^\]]*\]/i';
public function __construct($text) public function __construct($text)
{ {
$this->text = $text; $this->text = $text;
@@ -23,38 +21,14 @@ class Markup
public function parseGlobalsFromText(&$jsg = []) public function parseGlobalsFromText(&$jsg = [])
{ {
if (preg_match_all(self::$dbTagPattern, $this->text, $matches, PREG_SET_ORDER)) if (preg_match_all('/(?<!\\\\)\[(npc|object|item|itemset|quest|spell|zone|faction|pet|achievement|statistic|title|event|class|race|skill|currency)=(\d+)[^\]]*\]/i', $this->text, $matches, PREG_SET_ORDER))
{ {
foreach ($matches as $match) foreach ($matches as $match)
{ {
if ($match[1] == 'statistic') if ($match[1] == 'statistic')
$match[1] = 'achievement'; $match[1] = 'achievement';
else if ($match[1] == 'icondb')
$match[1] = 'icon';
if ($match[1] == 'money') if ($type = array_search($match[1], Util::$typeStrings))
{
if (stripos($match[0], 'items'))
{
if (preg_match('/items=([0-9,]+)/i', $match[0], $submatch))
{
$sm = explode(',', $submatch[1]);
for ($i = 0; $i < count($sm); $i+=2)
$this->jsGlobals[Type::ITEM][$sm[$i]] = $sm[$i];
}
}
if (stripos($match[0], 'currency'))
{
if (preg_match('/currency=([0-9,]+)/i', $match[0], $submatch))
{
$sm = explode(',', $submatch[1]);
for ($i = 0; $i < count($sm); $i+=2)
$this->jsGlobals[Type::CURRENCY][$sm[$i]] = $sm[$i];
}
}
}
else if ($type = Type::getIndexFrom(Type::IDX_FILE_STR, $match[1]))
$this->jsGlobals[$type][$match[2]] = $match[2]; $this->jsGlobals[$type][$match[2]] = $match[2];
} }
} }
@@ -64,78 +38,6 @@ class Markup
return $this->jsGlobals; return $this->jsGlobals;
} }
public function stripTags($globals = [])
{
// since this is an article the db-tags should already be parsed
$text = preg_replace_callback(self::$dbTagPattern, function ($match) use ($globals) {
if ($match[1] == 'statistic')
$match[1] = 'achievement';
else if ($match[1] == 'icondb')
$match[1] = 'icon';
else if ($match[1] == 'money')
{
$moneys = [];
if (stripos($match[0], 'items'))
{
if (preg_match('/items=([0-9,]+)/i', $match[0], $submatch))
{
$sm = explode(',', $submatch[1]);
for ($i = 0; $i < count($sm); $i += 2)
{
if (!empty($globals[Type::ITEM][1][$sm[$i]]))
$moneys[] = $globals[Type::ITEM][1][$sm[$i]]['name'];
else
$moneys[] = Util::ucFirst(Lang::game('item')).' #'.$sm[$i];
}
}
}
if (stripos($match[0], 'currency'))
{
if (preg_match('/currency=([0-9,]+)/i', $match[0], $submatch))
{
$sm = explode(',', $submatch[1]);
for ($i = 0; $i < count($sm); $i += 2)
{
if (!empty($globals[Type::CURRENCY][1][$sm[$i]]))
$moneys[] = $globals[Type::CURRENCY][1][$sm[$i]]['name'];
else
$moneys[] = Util::ucFirst(Lang::game('curency')).' #'.$sm[$i];
}
}
}
return Lang::concat($moneys);
}
if ($type = Type::getIndexFrom(Type::IDX_FILE_STR, $match[1]))
{
if (!empty($globals[$type][1][$match[2]]))
return $globals[$type][1][$match[2]]['name'];
else
return Util::ucFirst(Lang::game($match[1])).' #'.$match[2];
}
trigger_error('Markup::stripTags() - encountered unhandled db-tag: '.var_export($match));
return '';
}, $this->text);
$text = str_replace('[br]', "\n", $text);
$stripped = '';
$inTag = false;
for ($i = 0; $i < strlen($text); $i++)
{
if ($text[$i] == '[')
$inTag = true;
if (!$inTag)
$stripped .= $text[$i];
if ($text[$i] == ']')
$inTag = false;
}
return $stripped;
}
public function fromHtml() public function fromHtml()
{ {
} }

View File

@@ -1,921 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class Profiler
{
const PID_FILE = 'config/pr-queue-pid';
const CHAR_GMFLAGS = 0x1 | 0x8 | 0x10 | 0x20; // PLAYER_EXTRA_ :: GM_ON | TAXICHEAT | GM_INVISIBLE | GM_CHAT
private static $realms = [];
public static $slot2InvType = array(
1 => [INVTYPE_HEAD], // head
2 => [INVTYPE_NECK], // neck
3 => [INVTYPE_SHOULDERS], // shoulder
4 => [INVTYPE_BODY], // shirt
5 => [INVTYPE_CHEST, INVTYPE_ROBE], // chest
6 => [INVTYPE_WAIST], // waist
7 => [INVTYPE_LEGS], // legs
8 => [INVTYPE_FEET], // feet
9 => [INVTYPE_WRISTS], // wrists
10 => [INVTYPE_HANDS], // hands
11 => [INVTYPE_FINGER], // finger1
12 => [INVTYPE_FINGER], // finger2
13 => [INVTYPE_TRINKET], // trinket1
14 => [INVTYPE_TRINKET], // trinket2
15 => [INVTYPE_CLOAK], // chest
16 => [INVTYPE_WEAPONMAINHAND, INVTYPE_WEAPON, INVTYPE_2HWEAPON], // mainhand
17 => [INVTYPE_WEAPONOFFHAND, INVTYPE_WEAPON, INVTYPE_HOLDABLE, INVTYPE_SHIELD], // offhand
18 => [INVTYPE_RANGED, INVTYPE_THROWN, INVTYPE_RELIC], // ranged + relic
19 => [INVTYPE_TABARD], // tabard
);
public static $raidProgression = array( // statisticAchievement => relevantCriterium ; don't forget to enable this in /js/Profiler.js as well
1361 => 5100, 1362 => 5101, 1363 => 5102, 1365 => 5104, 1366 => 5108, 1364 => 5110, 1369 => 5112, 1370 => 5113, 1371 => 5114, 1372 => 5117, 1373 => 5119, 1374 => 5120, 1375 => 7805, 1376 => 5122, 1377 => 5123, // Naxxramas 10
1367 => 5103, 1368 => 5111, 1378 => 5124, 1379 => 5125, 1380 => 5126, 1381 => 5127, 1382 => 5128, 1383 => 7806, 1384 => 5130, 1385 => 5131, 1386 => 5132, 1387 => 5133, 1388 => 5134, 1389 => 5135, 1390 => 5136, // Naxxramas 25
2856 => 9938, 2857 => 9939, 2858 => 9940, 2859 => 9941, 2861 => 9943, 2865 => 9947, 2866 => 9948, 2868 => 9950, 2869 => 9951, 2870 => 9952, 2863 => 10558, 2864 => 10559, 2862 => 10560, 2867 => 10565, 2860 => 10580, // Ulduar 10
2872 => 9954, 2873 => 9955, 2874 => 9956, 2884 => 9957, 2875 => 9959, 2879 => 9963, 2880 => 9964, 2882 => 9966, 2883 => 9967, 3236 => 10542, 3257 => 10561, 3256 => 10562, 3258 => 10563, 2881 => 10566, 2885 => 10581, // Ulduar 25
1098 => 3271, // Onyxia's Lair 10
1756 => 13345, // Onyxia's Lair 25
4031 => 12230, 4034 => 12234, 4038 => 12238, 4042 => 12242, 4046 => 12246, // Trial of the Crusader 25 nh
4029 => 12231, 4035 => 12235, 4039 => 12239, 4043 => 12243, 4047 => 12247, // Trial of the Crusader 25 hc
4030 => 12229, 4033 => 12233, 4037 => 12237, 4041 => 12241, 4045 => 12245, // Trial of the Crusader 10 hc
4028 => 12228, 4032 => 12232, 4036 => 12236, 4040 => 12240, 4044 => 12244, // Trial of the Crusader 10 nh
4642 => 13091, 4656 => 13106, 4661 => 13111, 4664 => 13114, 4667 => 13117, 4670 => 13120, 4673 => 13123, 4676 => 13126, 4679 => 13129, 4682 => 13132, 4685 => 13135, 4688 => 13138, // Icecrown Citadel 25 hc
4641 => 13092, 4655 => 13105, 4660 => 13109, 4663 => 13112, 4666 => 13115, 4669 => 13118, 4672 => 13121, 4675 => 13124, 4678 => 13127, 4681 => 13130, 4683 => 13133, 4687 => 13136, // Icecrown Citadel 25 nh
4640 => 13090, 4654 => 13104, 4659 => 13110, 4662 => 13113, 4665 => 13116, 4668 => 13119, 4671 => 13122, 4674 => 13125, 4677 => 13128, 4680 => 13131, 4684 => 13134, 4686 => 13137, // Icecrown Citadel 10 hc
4639 => 13089, 4643 => 13093, 4644 => 13094, 4645 => 13095, 4646 => 13096, 4647 => 13097, 4648 => 13098, 4649 => 13099, 4650 => 13100, 4651 => 13101, 4652 => 13102, 4653 => 13103, // Icecrown Citadel 10 nh
4823 => 13467, // Ruby Sanctum 25 hc
4820 => 13465, // Ruby Sanctum 25 nh
4822 => 13468, // Ruby Sanctum 10 hc
4821 => 13466, // Ruby Sanctum 10 nh
);
public static function getBuyoutForItem($itemId)
{
if (!$itemId)
return 0;
// try, when having filled char-DB at hand
// return DB::Characters()->selectCell('SELECT SUM(a.buyoutprice) / SUM(ii.count) FROM auctionhouse a JOIN item_instance ii ON ii.guid = a.itemguid WHERE ii.itemEntry = ?d', $itemId);
return 0;
}
public static function queueStart(&$msg = '')
{
$queuePID = self::queueStatus();
if ($queuePID)
{
$msg = 'queue already running';
return true;
}
if (OS_WIN) // here be gremlins! .. suggested was "start /B php prQueue" as background process. but that closes itself
pclose(popen('start php prQueue --log=cache/profiling.log', 'r'));
else
exec('php prQueue --log=cache/profiling.log > /dev/null 2>/dev/null &');
usleep(500000);
if (self::queueStatus())
return true;
else
{
$msg = 'failed to start queue';
return false;
}
}
public static function queueStatus()
{
if (!file_exists(self::PID_FILE))
return 0;
$pid = file_get_contents(self::PID_FILE);
$cmd = OS_WIN ? 'tasklist /NH /FO CSV /FI "PID eq %d"' : 'ps --no-headers p %d';
exec(sprintf($cmd, $pid), $out);
if ($out && stripos($out[0], $pid) !== false)
return $pid;
// have pidFile but no process with this pid
self::queueFree();
return 0;
}
public static function queueLock($pid)
{
$queuePID = self::queueStatus();
if ($queuePID && $queuePID != $pid)
{
trigger_error('pSync - another queue with PID #'.$queuePID.' is already running', E_USER_ERROR);
return false;
}
// no queue running; create or overwrite pidFile
$ok = false;
if ($fh = fopen(self::PID_FILE, 'w'))
{
if (fwrite($fh, $pid))
$ok = true;
fclose($fh);
}
return $ok;
}
public static function queueFree()
{
unlink(self::PID_FILE);
}
public static function urlize($str, $allowLocales = false, $profile = false)
{
$search = ['<', '>', ' / ', "'"];
$replace = ['&lt;', '&gt;', '-', '' ];
$str = str_replace($search, $replace, $str);
if ($profile)
{
$str = str_replace(['(', ')'], ['', ''], $str);
$accents = array(
"ß" => "ss",
"á" => "a", "ä" => "a", "à" => "a", "â" => "a",
"è" => "e", "ê" => "e", "é" => "e", "ë" => "e",
"í" => "i", "î" => "i", "ì" => "i", "ï" => "i",
"ñ" => "n",
"ò" => "o", "ó" => "o", "ö" => "o", "ô" => "o",
"ú" => "u", "ü" => "u", "û" => "u", "ù" => "u",
"œ" => "oe",
"Á" => "A", "Ä" => "A", "À" => "A", "Â" => "A",
"È" => "E", "Ê" => "E", "É" => "E", "Ë" => "E",
"Í" => "I", "Î" => "I", "Ì" => "I", "Ï" => "I",
"Ñ" => "N",
"Ò" => "O", "Ó" => "O", "Ö" => "O", "Ô" => "O",
"Ú" => "U", "Ü" => "U", "Û" => "U", "Ù" => "U",
"Œ" => "Oe"
);
$str = strtr($str, $accents);
}
$str = trim($str);
if ($allowLocales)
$str = str_replace(' ', '-', $str);
else
$str = preg_replace('/[^a-z0-9]/i', '-', $str);
$str = str_replace('--', '-', $str);
$str = str_replace('--', '-', $str);
$str = rtrim($str, '-');
$str = strtolower($str);
return $str;
}
public static function getRealms()
{
if (DB::isConnectable(DB_AUTH) && !self::$realms)
{
self::$realms = DB::Auth()->select('SELECT
id AS ARRAY_KEY,
`name`,
CASE
WHEN timezone IN (2, 3, 4) THEN "us"
WHEN timezone IN (8, 9, 10, 11, 12) THEN "eu"
WHEN timezone = 6 THEN "kr"
WHEN timezone = 14 THEN "tw"
WHEN timezone = 16 THEN "cn"
END AS region
FROM
realmlist
WHERE
allowedSecurityLevel = 0 AND
gamebuild = ?d',
WOW_BUILD
);
foreach (self::$realms as $rId => $rData)
{
if (DB::isConnectable(DB_CHARACTERS . $rId))
continue;
// realm in db but no connection info set
unset(self::$realms[$rId]);
}
}
return self::$realms;
}
private static function queueInsert($realmId, $guid, $type, $localId)
{
if ($rData = DB::Aowow()->selectRow('SELECT requestTime AS time, status FROM ?_profiler_sync WHERE realm = ?d AND realmGUID = ?d AND `type` = ?d AND typeId = ?d AND status <> ?d', $realmId, $guid, $type, $localId, PR_QUEUE_STATUS_WORKING))
{
// not on already scheduled - recalc time and set status to PR_QUEUE_STATUS_WAITING
if ($rData['status'] != PR_QUEUE_STATUS_WAITING)
{
$newTime = CFG_DEBUG ? time() : max($rData['time'] + CFG_PROFILER_RESYNC_DELAY, time());
DB::Aowow()->query('UPDATE ?_profiler_sync SET requestTime = ?d, status = ?d, errorCode = 0 WHERE realm = ?d AND realmGUID = ?d AND `type` = ?d AND typeId = ?d', $newTime, PR_QUEUE_STATUS_WAITING, $realmId, $guid, $type, $localId);
}
}
else
DB::Aowow()->query('REPLACE INTO ?_profiler_sync (realm, realmGUID, `type`, typeId, requestTime, status, errorCode) VALUES (?d, ?d, ?d, ?d, UNIX_TIMESTAMP(), ?d, 0)', $realmId, $guid, $type, $localId, PR_QUEUE_STATUS_WAITING);
}
public static function scheduleResync($type, $realmId, $guid)
{
$newId = 0;
switch ($type)
{
case Type::PROFILE:
if ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID = ?d', $realmId, $guid))
self::queueInsert($realmId, $guid, Type::PROFILE, $newId);
break;
case Type::GUILD:
if ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_guild WHERE realm = ?d AND realmGUID = ?d', $realmId, $guid))
self::queueInsert($realmId, $guid, Type::GUILD, $newId);
break;
case Type::ARENA_TEAM:
if ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_arena_team WHERE realm = ?d AND realmGUID = ?d', $realmId, $guid))
self::queueInsert($realmId, $guid, Type::ARENA_TEAM, $newId);
break;
default:
trigger_error('scheduling resync for unknown type #'.$type.' omiting..', E_USER_WARNING);
return 0;
}
if (!$newId)
trigger_error('Profiler::scheduleResync() - tried to resync type #'.$type.' guid #'.$guid.' from realm #'.$realmId.' without preloaded data', E_USER_ERROR);
else if (!self::queueStart($msg))
trigger_error('Profiler::scheduleResync() - '.$msg, E_USER_ERROR);
return $newId;
}
public static function resyncStatus($type, array $subjectGUIDs)
{
$response = [CFG_PROFILER_ENABLE ? 2 : 0]; // in theory you could have multiple queues; used as divisor for: (15 / x) + 2
if (!$subjectGUIDs)
$response[] = [PR_QUEUE_STATUS_ENDED, 0, 0, PR_QUEUE_ERROR_CHAR];
else
{
// error out all profiles with status WORKING, that are older than 60sec
DB::Aowow()->query('UPDATE ?_profiler_sync SET status = ?d, errorCode = ?d WHERE status = ?d AND requestTime < ?d', PR_QUEUE_STATUS_ERROR, PR_QUEUE_ERROR_UNK, PR_QUEUE_STATUS_WORKING, time() - MINUTE);
$subjectStatus = DB::Aowow()->select('SELECT typeId AS ARRAY_KEY, status, realm, errorCode FROM ?_profiler_sync WHERE `type` = ?d AND typeId IN (?a)', $type, $subjectGUIDs);
$queue = DB::Aowow()->selectCol('SELECT CONCAT(type, ":", typeId) FROM ?_profiler_sync WHERE status = ?d AND requestTime < UNIX_TIMESTAMP() ORDER BY requestTime ASC', PR_QUEUE_STATUS_WAITING);
foreach ($subjectGUIDs as $guid)
{
if (empty($subjectStatus[$guid])) // whelp, thats some error..
$response[] = [PR_QUEUE_STATUS_ERROR, 0, 0, PR_QUEUE_ERROR_UNK];
else if ($subjectStatus[$guid]['status'] == PR_QUEUE_STATUS_ERROR)
$response[] = [PR_QUEUE_STATUS_ERROR, 0, 0, $subjectStatus[$guid]['errorCode']];
else
$response[] = array(
$subjectStatus[$guid]['status'],
$subjectStatus[$guid]['status'] != PR_QUEUE_STATUS_READY ? CFG_PROFILER_RESYNC_PING : 0,
array_search($type.':'.$guid, $queue) + 1,
0,
1 // nResycTries - unsure about this one
);
}
}
return $response;
}
public static function getCharFromRealm($realmId, $charGuid)
{
$char = DB::Characters($realmId)->selectRow('SELECT c.* FROM characters c WHERE c.guid = ?d', $charGuid);
if (!$char)
return false;
// reminder: this query should not fail: a placeholder entry is created as soon as a char listview is created or profile detail page is called
$profile = DB::Aowow()->selectRow('SELECT id, lastupdated FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID = ?d', $realmId, $char['guid']);
if (!$profile)
return false; // well ... it failed
$profileId = $profile['id'];
CLI::write('fetching char '.$char['name'].' (#'.$charGuid.') from realm #'.$realmId);
if (!$char['online'] && $char['logout_time'] <= $profile['lastupdated'])
{
DB::Aowow()->query('UPDATE ?_profiler_profiles SET lastupdated = ?d WHERE id = ?d', time(), $profileId);
CLI::write('char did not log in since last update. skipping...');
return true;
}
CLI::write('writing...');
$ra = (1 << ($char['race'] - 1));
$cl = (1 << ($char['class'] - 1));
/*************/
/* equipment */
/*************/
/* enchantment-Indizes
* 0: permEnchant
* 3: tempEnchant
* 6: gem1
* 9: gem2
* 12: gem3
* 15: socketBonus [not used]
* 18: extraSocket [only check existance]
* 21 - 30: randomProp enchantments
*/
DB::Aowow()->query('DELETE FROM ?_profiler_items WHERE id = ?d', $profileId);
$items = DB::Characters($realmId)->select('SELECT ci.slot AS ARRAY_KEY, ii.itemEntry, ii.enchantments, ii.randomPropertyId FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ?d AND bag = 0 AND slot BETWEEN 0 AND 18', $char['guid']);
$gemItems = [];
$permEnch = [];
$mhItem = 0;
$ohItem = 0;
foreach ($items as $slot => $item)
{
$ench = explode(' ', $item['enchantments']);
$gEnch = [];
foreach ([6, 9, 12] as $idx)
if ($ench[$idx])
$gEnch[$idx] = $ench[$idx];
if ($gEnch)
{
$gi = DB::Aowow()->selectCol('SELECT gemEnchantmentId AS ARRAY_KEY, id FROM ?_items WHERE class = 3 AND gemEnchantmentId IN (?a)', $gEnch);
foreach ($gEnch as $eId)
{
if (isset($gemItems[$eId]))
$gemItems[$eId][1]++;
else
$gemItems[$eId] = [$gi[$eId], 1];
}
}
if ($slot + 1 == 16)
$mhItem = $item['itemEntry'];
if ($slot + 1 == 17)
$ohItem = $item['itemEntry'];
if ($ench[0])
$permEnch[$slot] = $ench[0];
$data = array(
'id' => $profileId,
'slot' => $slot + 1,
'item' => $item['itemEntry'],
'subItem' => $item['randomPropertyId'],
'permEnchant' => $ench[0],
'tempEnchant' => $ench[3],
'extraSocket' => (int)!!$ench[18],
'gem1' => isset($gemItems[$ench[6]]) ? $gemItems[$ench[6]][0] : 0,
'gem2' => isset($gemItems[$ench[9]]) ? $gemItems[$ench[9]][0] : 0,
'gem3' => isset($gemItems[$ench[12]]) ? $gemItems[$ench[12]][0] : 0,
'gem4' => 0 // serverside items cant have more than 3 sockets. (custom profile thing)
);
DB::Aowow()->query('INSERT INTO ?_profiler_items (?#) VALUES (?a)', array_keys($data), array_values($data));
}
CLI::write(' ..inventory');
/**************/
/* basic info */
/**************/
$data = array(
'realm' => $realmId,
'realmGUID' => $charGuid,
'name' => $char['name'],
'renameItr' => 0,
'race' => $char['race'],
'class' => $char['class'],
'level' => $char['level'],
'gender' => $char['gender'],
'skincolor' => $char['skin'],
'facetype' => $char['face'], // maybe features
'hairstyle' => $char['hairStyle'],
'haircolor' => $char['hairColor'],
'features' => $char['facialStyle'], // maybe facetype
'title' => $char['chosenTitle'] ? DB::Aowow()->selectCell('SELECT id FROM ?_titles WHERE bitIdx = ?d', $char['chosenTitle']) : 0,
'playedtime' => $char['totaltime'],
'nomodelMask' => ($char['playerFlags'] & 0x400 ? (1 << SLOT_HEAD) : 0) | ($char['playerFlags'] & 0x800 ? (1 << SLOT_BACK) : 0),
'talenttree1' => 0,
'talenttree2' => 0,
'talenttree3' => 0,
'talentbuild1' => '',
'talentbuild2' => '',
'glyphs1' => '',
'glyphs2' => '',
'activespec' => $char['activeTalentGroup'],
'guild' => null,
'guildRank' => null,
'gearscore' => 0,
'achievementpoints' => 0
);
// char is flagged for rename
if ($char['at_login'] & 0x1)
{
$ri = DB::Aowow()->selectCell('SELECT MAX(renameItr) FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID = ?d AND name = ?', $realmId, $charGuid, $char['name']);
$data['renameItr'] = $ri ? ++$ri : 1;
}
/********************/
/* talents + glyphs */
/********************/
$t = DB::Characters($realmId)->selectCol('SELECT talentGroup AS ARRAY_KEY, spell AS ARRAY_KEY2, spell FROM character_talent WHERE guid = ?d', $char['guid']);
$g = DB::Characters($realmId)->select('SELECT talentGroup AS ARRAY_KEY, glyph1 AS g1, glyph2 AS g4, glyph3 AS g5, glyph4 AS g2, glyph5 AS g3, glyph6 AS g6 FROM character_glyphs WHERE guid = ?d', $char['guid']);
for ($i = 0; $i < 2; $i++)
{
// talents
for ($j = 0; $j < 3; $j++)
{
$_ = DB::Aowow()->selectCol('SELECT spell AS ARRAY_KEY, MAX(IF(spell IN (?a), `rank`, 0)) FROM ?_talents WHERE class = ?d AND tab = ?d GROUP BY id ORDER BY `row`, `col` ASC', !empty($t[$i]) ? $t[$i] : [0], $char['class'], $j);
$data['talentbuild'.($i + 1)] .= implode('', $_);
if ($data['activespec'] == $i)
$data['talenttree'.($j + 1)] = array_sum($_);
}
// glyphs
if (isset($g[$i]))
{
$gProps = [];
for ($j = 1; $j <= 6; $j++)
if ($g[$i]['g'.$j])
$gProps[$j] = $g[$i]['g'.$j];
if ($gProps)
if ($gItems = DB::Aowow()->selectCol('SELECT i.id FROM ?_glyphproperties gp JOIN ?_spell s ON s.effect1MiscValue = gp.id AND s.effect1Id = 74 JOIN ?_items i ON i.class = 16 AND i.spellId1 = s.id WHERE gp.id IN (?a)', $gProps))
$data['glyphs'.($i + 1)] = implode(':', $gItems);
}
}
$t = array(
'spent' => [$data['talenttree1'], $data['talenttree2'], $data['talenttree3']],
'spec' => 0
);
if ($t['spent'][0] > $t['spent'][1] && $t['spent'][0] > $t['spent'][2])
$t['spec'] = 1;
else if ($t['spent'][1] > $t['spent'][0] && $t['spent'][1] > $t['spent'][2])
$t['spec'] = 2;
else if ($t['spent'][2] > $t['spent'][1] && $t['spent'][2] > $t['spent'][0])
$t['spec'] = 3;
// calc gearscore
if ($items)
$data['gearscore'] += (new ItemList(array(['id', array_column($items, 'itemEntry')])))->getScoreTotal($data['class'], $t, $mhItem, $ohItem);
if ($gemItems)
{
$gemScores = new ItemList(array(['id', array_column($gemItems, 0)]));
foreach ($gemItems as [$itemId, $mult])
if (isset($gemScores->json[$itemId]['gearscore']))
$data['gearscore'] += $gemScores->json[$itemId]['gearscore'] * $mult;
}
if ($permEnch) // fuck this shit .. we are guestimating this!
{
// enchantId => multiple spells => multiple items with varying itemlevels, quality, whatevs
// cant reasonably get to the castItem from enchantId and slot
$profSpec = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, skillLevel AS "1", skillLine AS "0" FROM ?_itemenchantment WHERE id IN (?a)', $permEnch);
foreach ($permEnch as $eId)
{
if ($x = Util::getEnchantmentScore(0, 0, !!$profSpec[$eId][1], $eId))
$data['gearscore'] += $x;
else if ($profSpec[$eId][0] != 776) // not runeforging
$data['gearscore'] += 17; // assume high quality enchantment for unknown cases
}
}
$data['lastupdated'] = time();
CLI::write(' ..basic info');
/***************/
/* hunter pets */
/***************/
if ($cl == CLASS_HUNTER)
{
DB::Aowow()->query('DELETE FROM ?_profiler_pets WHERE owner = ?d', $profileId);
$pets = DB::Characters($realmId)->select('SELECT id AS ARRAY_KEY, id, entry, modelId, name FROM character_pet WHERE owner = ?d', $charGuid);
foreach ($pets as $petGuid => $petData)
{
$morePet = DB::Aowow()->selectRow('SELECT p.`type`, c.family FROM ?_pet p JOIN ?_creature c ON c.family = p.id WHERE c.id = ?d', $petData['entry']);
$petSpells = DB::Characters($realmId)->selectCol('SELECT spell FROM pet_spell WHERE guid = ?d', $petGuid);
$_ = DB::Aowow()->selectCol('SELECT spell AS ARRAY_KEY, MAX(IF(spell IN (?a), `rank`, 0)) FROM ?_talents WHERE class = 0 AND petTypeMask = ?d GROUP BY id ORDER BY row, col ASC', $petSpells ?: [0], 1 << $morePet['type']);
$pet = array(
'id' => $petGuid,
'owner' => $profileId,
'name' => $petData['name'],
'family' => $morePet['family'],
'npc' => $petData['entry'],
'displayId' => $petData['modelId'],
'talents' => implode('', $_)
);
DB::Aowow()->query('INSERT INTO ?_profiler_pets (?#) VALUES (?a)', array_keys($pet), array_values($pet));
}
CLI::write(' ..hunter pets');
}
/*******************/
/* completion data */
/*******************/
DB::Aowow()->query('DELETE FROM ?_profiler_completion WHERE id = ?d', $profileId);
// done quests
if ($quests = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, quest AS typeId FROM character_queststatus_rewarded WHERE guid = ?d', $profileId, Type::QUEST, $char['guid']))
foreach (Util::createSqlBatchInsert($quests) as $q)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$q, array_keys($quests[0]));
CLI::write(' ..quests');
// known skills (professions only)
$skAllowed = DB::Aowow()->selectCol('SELECT id FROM ?_skillline WHERE typeCat IN (9, 11) AND (cuFlags & ?d) = 0', CUSTOM_EXCLUDE_FOR_LISTVIEW);
$skills = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, skill AS typeId, `value` AS cur, max FROM character_skills WHERE guid = ?d AND skill IN (?a)', $profileId, Type::SKILL, $char['guid'], $skAllowed);
// manually apply racial profession bonuses
foreach ($skills as &$sk)
{
// Blood Elves - Arcane Affinity
if ($sk['typeId'] == 333 && $char['race'] == 10)
{
$sk['cur'] += 10;
$sk['max'] += 10;
}
// Draenei - Gemcutting
if ($sk['typeId'] == 755 && $char['race'] == 11)
{
$sk['cur'] += 5;
$sk['max'] += 5;
}
// Tauren - Cultivation
// Gnomes - Engineering Specialization
if (($sk['typeId'] == 182 && $char['race'] == 6) ||
($sk['typeId'] == 202 && $char['race'] == 7))
{
$sk['cur'] += 15;
$sk['max'] += 15;
}
}
unset($sk);
if ($skills)
{
// apply auto-learned trade skills
DB::Aowow()->query('
INSERT INTO ?_profiler_completion
SELECT ?d, ?d, spellId, NULL, NULL
FROM dbc_skilllineability
WHERE skillLineId IN (?a) AND
acquireMethod = 1 AND
(reqRaceMask = 0 OR reqRaceMask & ?d) AND
(reqClassMask = 0 OR reqClassMask & ?d)',
$profileId, Type::SPELL,
array_column($skills, 'typeId'),
1 << ($char['race'] - 1),
1 << ($char['class'] - 1)
);
foreach (Util::createSqlBatchInsert($skills) as $sk)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$sk, array_keys($skills[0]));
}
CLI::write(' ..professions');
// reputation
// get base values for this race/class
$reputation = [];
$baseRep = DB::Aowow()->selectCol('
SELECT id AS ARRAY_KEY, baseRepValue1 FROM aowow_factions WHERE baseRepValue1 && (baseRepRaceMask1 & ?d || (!baseRepRaceMask1 AND baseRepClassMask1)) &&
((baseRepClassMask1 & ?d) || !baseRepClassMask1) UNION
SELECT id AS ARRAY_KEY, baseRepValue2 FROM aowow_factions WHERE baseRepValue2 && (baseRepRaceMask2 & ?d || (!baseRepRaceMask2 AND baseRepClassMask2)) &&
((baseRepClassMask2 & ?d) || !baseRepClassMask2) UNION
SELECT id AS ARRAY_KEY, baseRepValue3 FROM aowow_factions WHERE baseRepValue3 && (baseRepRaceMask3 & ?d || (!baseRepRaceMask3 AND baseRepClassMask3)) &&
((baseRepClassMask3 & ?d) || !baseRepClassMask3) UNION
SELECT id AS ARRAY_KEY, baseRepValue4 FROM aowow_factions WHERE baseRepValue4 && (baseRepRaceMask4 & ?d || (!baseRepRaceMask4 AND baseRepClassMask4)) &&
((baseRepClassMask4 & ?d) || !baseRepClassMask4)
', $ra, $cl, $ra, $cl, $ra, $cl, $ra, $cl);
if ($reputation = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, faction AS typeId, standing AS cur FROM character_reputation WHERE guid = ?d AND (flags & 0x4) = 0', $profileId, Type::FACTION, $char['guid']))
{
// merge back base values for encountered factions
foreach ($reputation as &$set)
{
if (empty($baseRep[$set['typeId']]))
continue;
$set['cur'] += $baseRep[$set['typeId']];
unset($baseRep[$set['typeId']]);
}
}
// insert base values for not yet encountered factions
foreach ($baseRep as $id => $val)
$reputation[] = array(
'id' => $profileId,
'type' => Type::FACTION,
'typeId' => $id,
'cur' => $val
);
foreach (Util::createSqlBatchInsert($reputation) as $rep)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$rep, array_keys($reputation[0]));
CLI::write(' ..reputation');
// known titles
$tBlocks = explode(' ', $char['knownTitles']);
$indizes = [];
for ($i = 0; $i < 6; $i++)
for ($j = 0; $j < 32; $j++)
if ($tBlocks[$i] & (1 << $j))
$indizes[] = $j + ($i * 32);
if ($indizes)
DB::Aowow()->query('INSERT INTO ?_profiler_completion SELECT ?d, ?d, id, NULL, NULL FROM ?_titles WHERE bitIdx IN (?a)', $profileId, Type::TITLE, $indizes);
CLI::write(' ..titles');
// achievements
if ($achievements = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, achievement AS typeId, date AS cur FROM character_achievement WHERE guid = ?d', $profileId, Type::ACHIEVEMENT, $char['guid']))
{
foreach (Util::createSqlBatchInsert($achievements) as $a)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$a, array_keys($achievements[0]));
$data['achievementpoints'] = DB::Aowow()->selectCell('SELECT SUM(points) FROM ?_achievement WHERE id IN (?a)', array_column($achievements, 'typeId'));
}
CLI::write(' ..achievements');
// raid progression
if ($progress = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, criteria AS typeId, date AS cur, counter AS `max` FROM character_achievement_progress WHERE guid = ?d AND criteria IN (?a)', $profileId, Type::ACHIEVEMENT, $char['guid'], self::$raidProgression))
{
array_walk($progress, function (&$val) { $val['typeId'] = array_search($val['typeId'], self::$raidProgression); });
foreach (Util::createSqlBatchInsert($progress) as $p)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$p, array_keys($progress[0]));
}
CLI::write(' ..raid progression');
// known spells
if ($spells = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, spell AS typeId FROM character_spell WHERE guid = ?d AND disabled = 0', $profileId, Type::SPELL, $char['guid']))
foreach (Util::createSqlBatchInsert($spells) as $s)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$s, array_keys($spells[0]));
CLI::write(' ..known spells (vanity pets & mounts)');
/****************/
/* related data */
/****************/
// guilds
if ($guild = DB::Characters($realmId)->selectRow('SELECT g.name AS name, g.guildid AS id, gm.rank FROM guild_member gm JOIN guild g ON g.guildid = gm.guildid WHERE gm.guid = ?d', $char['guid']))
{
$guildId = 0;
if (!($guildId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_guild WHERE realm = ?d AND realmGUID = ?d', $realmId, $guild['id'])))
{
$gData = array( // only most basic data
'realm' => $realmId,
'realmGUID' => $guild['id'],
'name' => $guild['name'],
'nameUrl' => self::urlize($guild['name']),
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
$guildId = DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_guild (?#) VALUES (?a)', array_keys($gData), array_values($gData));
}
$data['guild'] = $guildId;
$data['guildRank'] = $guild['rank'];
}
// arena teams
$teams = DB::Characters($realmId)->select('SELECT at.arenaTeamId AS ARRAY_KEY, at.name, at.type, IF(at.captainGuid = atm.guid, 1, 0) AS captain, atm.* FROM arena_team at JOIN arena_team_member atm ON atm.arenaTeamId = at.arenaTeamId WHERE atm.guid = ?d', $char['guid']);
foreach ($teams as $rGuid => $t)
{
$teamId = 0;
if (!($teamId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_arena_team WHERE realm = ?d AND realmGUID = ?d', $realmId, $rGuid)))
{
$team = array( // only most basic data
'realm' => $realmId,
'realmGUID' => $rGuid,
'name' => $t['name'],
'nameUrl' => self::urlize($t['name']),
'type' => $t['type'],
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
$teamId = DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team (?#) VALUES (?a)', array_keys($team), array_values($team));
}
$member = array(
'arenaTeamId' => $teamId,
'profileId' => $profileId,
'captain' => $t['captain'],
'weekGames' => $t['weekGames'],
'weekWins' => $t['weekWins'],
'seasonGames' => $t['seasonGames'],
'seasonWins' => $t['seasonWins'],
'personalRating' => $t['personalRating']
);
DB::Aowow()->query('INSERT INTO ?_profiler_arena_team_member (?#) VALUES (?a) ON DUPLICATE KEY UPDATE ?a', array_keys($member), array_values($member), array_slice($member, 2));
}
CLI::write(' ..associated arena teams');
/*********************/
/* mark char as done */
/*********************/
if (DB::Aowow()->query('UPDATE ?_profiler_profiles SET ?a WHERE realm = ?d AND realmGUID = ?d', $data, $realmId, $charGuid) !== null)
DB::Aowow()->query('UPDATE ?_profiler_profiles SET cuFlags = cuFlags & ?d WHERE id = ?d', ~PROFILER_CU_NEEDS_RESYNC, $profileId);
return true;
}
public static function getGuildFromRealm($realmId, $guildGuid)
{
$guild = DB::Characters($realmId)->selectRow('SELECT guildId, name, createDate, info, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor FROM guild WHERE guildId = ?d', $guildGuid);
if (!$guild)
return false;
// reminder: this query should not fail: a placeholder entry is created as soon as a team listview is created or team detail page is called
$guildId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_guild WHERE realm = ?d AND realmGUID = ?d', $realmId, $guild['guildId']);
CLI::write('fetching guild #'.$guildGuid.' from realm #'.$realmId);
CLI::write('writing...');
/**************/
/* Guild Data */
/**************/
unset($guild['guildId']);
$guild['nameUrl'] = self::urlize($guild['name']);
DB::Aowow()->query('UPDATE ?_profiler_guild SET ?a WHERE realm = ?d AND realmGUID = ?d', $guild, $realmId, $guildGuid);
// ranks
DB::Aowow()->query('DELETE FROM ?_profiler_guild_rank WHERE guildId = ?d', $guildId);
if ($ranks = DB::Characters($realmId)->select('SELECT ?d AS guildId, rid AS `rank`, rname AS name FROM guild_rank WHERE guildid = ?d', $guildId, $guildGuid))
foreach (Util::createSqlBatchInsert($ranks) as $r)
DB::Aowow()->query('INSERT INTO ?_profiler_guild_rank (?#) VALUES '.$r, array_keys(reset($ranks)));
CLI::write(' ..guild data');
/***************/
/* Member Data */
/***************/
$conditions = array(
['g.guildid', $guildGuid],
['deleteInfos_Account', null],
['level', MAX_LEVEL, '<='], // prevents JS errors
[['extra_flags', self::CHAR_GMFLAGS, '&'], 0] // not a staff char
);
// this here should all happen within ProfileList
$members = new RemoteProfileList($conditions, ['sv' => $realmId]);
if (!$members->error)
$members->initializeLocalEntries();
else
return false;
CLI::write(' ..guild members');
/*********************/
/* mark guild as done */
/*********************/
DB::Aowow()->query('UPDATE ?_profiler_guild SET cuFlags = cuFlags & ?d WHERE id = ?d', ~PROFILER_CU_NEEDS_RESYNC, $guildId);
return true;
}
public static function getArenaTeamFromRealm($realmId, $teamGuid)
{
$team = DB::Characters($realmId)->selectRow('SELECT arenaTeamId, name, type, captainGuid, rating, seasonGames, seasonWins, weekGames, weekWins, `rank`, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor FROM arena_team WHERE arenaTeamId = ?d', $teamGuid);
if (!$team)
return false;
// reminder: this query should not fail: a placeholder entry is created as soon as a team listview is created or team detail page is called
$teamId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_arena_team WHERE realm = ?d AND realmGUID = ?d', $realmId, $team['arenaTeamId']);
CLI::write('fetching arena team #'.$teamGuid.' from realm #'.$realmId);
CLI::write('writing...');
/*************/
/* Team Data */
/*************/
$captain = $team['captainGuid'];
unset($team['captainGuid']);
unset($team['arenaTeamId']);
$team['nameUrl'] = self::urlize($team['name']);
DB::Aowow()->query('UPDATE ?_profiler_arena_team SET ?a WHERE realm = ?d AND realmGUID = ?d', $team, $realmId, $teamGuid);
CLI::write(' ..team data');
/***************/
/* Member Data */
/***************/
$members = DB::Characters($realmId)->select('
SELECT
atm.guid AS ARRAY_KEY, atm.arenaTeamId, atm.weekGames, atm.weekWins, atm.seasonGames, atm.seasonWins, atm.personalrating
FROM
arena_team_member atm
JOIN
characters c ON c.guid = atm.guid AND
c.deleteInfos_Account IS NULL AND
c.level <= ?d AND
(c.extra_flags & ?d) = 0
WHERE
arenaTeamId = ?d',
MAX_LEVEL,
self::CHAR_GMFLAGS,
$teamGuid
);
$conditions = array(
['c.guid', array_keys($members)],
['deleteInfos_Account', null],
['level', MAX_LEVEL, '<='], // prevents JS errors
[['extra_flags', self::CHAR_GMFLAGS, '&'], 0] // not a staff char
);
$mProfiles = new RemoteProfileList($conditions, ['sv' => $realmId]);
if (!$mProfiles->error)
{
$mProfiles->initializeLocalEntries();
foreach ($mProfiles->iterate() as $__)
{
$mGuid = $mProfiles->getField('guid');
$members[$mGuid]['arenaTeamId'] = $teamId;
$members[$mGuid]['captain'] = (int)($mGuid == $captain);
$members[$mGuid]['profileId'] = $mProfiles->getField('id');
}
DB::Aowow()->query('DELETE FROM ?_profiler_arena_team_member WHERE arenaTeamId = ?d', $teamId);
foreach (Util::createSqlBatchInsert($members) as $m)
DB::Aowow()->query('INSERT INTO ?_profiler_arena_team_member (?#) VALUES '.$m, array_keys(reset($members)));
}
else
return false;
CLI::write(' ..team members');
/*********************/
/* mark team as done */
/*********************/
DB::Aowow()->query('UPDATE ?_profiler_arena_team SET cuFlags = cuFlags & ?d WHERE id = ?d', ~PROFILER_CU_NEEDS_RESYNC, $teamId);
return true;
}
}
?>

View File

@@ -1,26 +1,26 @@
<?php <?php
define('AOWOW_REVISION', 33); define('AOWOW_REVISION', 1);
define('CLI', PHP_SAPI === 'cli'); define('CLI', PHP_SAPI === 'cli');
$reqExt = ['SimpleXML', 'gd', 'mysqli', 'mbstring', 'fileinfo'/*, 'gmp'*/]; $reqExt = ['SimpleXML', 'gd', 'mysqli', 'mbstring'];
$error = ''; $error = '';
foreach ($reqExt as $r) foreach ($reqExt as $r)
if (!extension_loaded($r)) if (!extension_loaded($r))
$error .= 'Required Extension <b>'.$r."</b> was not found. Please check if it should exist, using \"<i>php -m</i>\"\n\n"; $error .= 'Required Extension <b>'.$r."</b> was not found. Please check if it should exist, using \"<i>php -m</i>\"\n\n";
if (version_compare(PHP_VERSION, '7.4.0') < 0) if (version_compare(PHP_VERSION, '5.5.0') < 0)
$error .= 'PHP Version <b>7.4</b> or higher required! Your version is <b>'.PHP_VERSION."</b>.\nCore functions are unavailable!\n"; $error .= 'PHP Version <b>5.5.0</b> or higher required! Your version is <b>'.PHP_VERSION."</b>.\nCore functions are unavailable!\n";
if ($error) if ($error)
{ {
echo CLI ? strip_tags($error) : $error; echo CLI ? strip_tags($error) : $error;
die(); die();
} }
// include all necessities, set up basics // include all necessities, set up basics
require_once 'includes/kernel.php'; require_once 'includes/kernel.php';
?> ?>

File diff suppressed because it is too large Load Diff

View File

@@ -8,18 +8,17 @@ class AchievementList extends BaseType
{ {
use listviewHelper; use listviewHelper;
public static $type = Type::ACHIEVEMENT; public static $type = TYPE_ACHIEVEMENT;
public static $brickFile = 'achievement'; public static $brickFile = 'achievement';
public static $dataTable = '?_achievement';
public $criteria = []; public $criteria = [];
protected $queryBase = 'SELECT `a`.*, `a`.`id` AS ARRAY_KEY FROM ?_achievement a'; protected $queryBase = 'SELECT `a`.*, `a`.`id` AS ARRAY_KEY FROM ?_achievement a';
protected $queryOpts = array( protected $queryOpts = array(
'a' => [['ic'], 'o' => 'orderInGroup ASC'], 'a' => [['si'], 'o' => 'orderInGroup ASC'],
'ic' => ['j' => ['?_icons ic ON ic.id = a.iconId', true], 's' => ', ic.name AS iconString'], 'si' => ['j' => ['?_icons si ON si.id = a.iconId', true], 's' => ', si.iconString'],
'ac' => ['j' => ['?_achievementcriteria AS `ac` ON `ac`.`refAchievementId` = `a`.`id`', true], 'g' => '`a`.`id`'] 'ac' => ['j' => ['?_achievementcriteria AS `ac` ON `ac`.`refAchievementId` = `a`.`id`', true], 'g' => '`a`.`id`']
); );
/* /*
todo: evaluate TC custom-data-tables: a*_criteria_data should be merged on installation todo: evaluate TC custom-data-tables: a*_criteria_data should be merged on installation
@@ -34,24 +33,7 @@ class AchievementList extends BaseType
// post processing // post processing
$rewards = DB::World()->select(' $rewards = DB::World()->select('
SELECT SELECT ar.entry AS ARRAY_KEY, ar.*, lar.* FROM achievement_reward ar LEFT JOIN locales_achievement_reward lar ON lar.entry = ar.entry WHERE ar.entry IN (?a)',
ar.ID AS ARRAY_KEY, ar.TitleA, ar.TitleH, ar.ItemID, ar.Sender AS sender, ar.MailTemplateID,
ar.Subject AS subject_loc0, IFNULL(arl2.Subject, "") AS subject_loc2, IFNULL(arl3.Subject, "") AS subject_loc3, IFNULL(arl4.Subject, "") AS subject_loc4, IFNULL(arl6.Subject, "") AS subject_loc6, IFNULL(arl8.Subject, "") AS subject_loc8,
ar.Body AS text_loc0, IFNULL(arl2.Body, "") AS text_loc2, IFNULL(arl3.Body, "") AS text_loc3, IFNULL(arl4.Body, "") AS text_loc4, IFNULL(arl6.Body, "") AS text_loc6, IFNULL(arl8.Body, "") AS text_loc8
FROM
achievement_reward ar
LEFT JOIN
achievement_reward_locale arl2 ON arl2.ID = ar.ID AND arl2.Locale = "frFR"
LEFT JOIN
achievement_reward_locale arl3 ON arl3.ID = ar.ID AND arl3.Locale = "deDE"
LEFT JOIN
achievement_reward_locale arl4 ON arl4.ID = ar.ID AND arl4.Locale = "zhCN"
LEFT JOIN
achievement_reward_locale arl6 ON arl6.ID = ar.ID AND arl6.Locale = "esES"
LEFT JOIN
achievement_reward_locale arl8 ON arl8.ID = ar.ID AND arl8.Locale = "ruRU"
WHERE
ar.ID IN (?a)',
$this->getFoundIDs() $this->getFoundIDs()
); );
@@ -63,33 +45,31 @@ class AchievementList extends BaseType
{ {
$_curTpl = array_merge($rewards[$_id], $_curTpl); $_curTpl = array_merge($rewards[$_id], $_curTpl);
$_curTpl['mailTemplate'] = $rewards[$_id]['MailTemplateID']; if ($rewards[$_id]['mailTemplate'])
if ($rewards[$_id]['MailTemplateID'])
{ {
// using class Loot creates an inifinite loop cirling between Loot, ItemList and SpellList or something // using class Loot creates an inifinite loop cirling between Loot, ItemList and SpellList or something
// $mailSrc = new Loot(); // $mailSrc = new Loot();
// $mailSrc->getByContainer(LOOT_MAIL, $rewards[$_id]['MailTemplateID']); // $mailSrc->getByContainer(LOOT_MAIL, $rewards[$_id]['mailTemplate']);
// foreach ($mailSrc->iterate() as $loot) // foreach ($mailSrc->iterate() as $loot)
// $_curTpl['rewards'][] = [Type::ITEM, $loot['id']]; // $_curTpl['rewards'][] = [TYPE_ITEM, $loot['id']];
// lets just assume for now, that mailRewards for achievements do not contain references // lets just assume for now, that mailRewards for achievements do not contain references
$mailRew = DB::World()->selectCol('SELECT Item FROM mail_loot_template WHERE Reference <= 0 AND entry = ?d', $rewards[$_id]['MailTemplateID']); $mailRew = DB::World()->selectCol('SELECT Item FROM mail_loot_template WHERE Reference <= 0 AND entry = ?d', $rewards[$_id]['mailTemplate']);
foreach ($mailRew AS $mr) foreach ($mailRew AS $mr)
$_curTpl['rewards'][] = [Type::ITEM, $mr]; $_curTpl['rewards'][] = [TYPE_ITEM, $mr];
} }
} }
//"rewards":[[11,137],[3,138]] [type, typeId] //"rewards":[[11,137],[3,138]] [type, typeId]
if (!empty($_curTpl['ItemID'])) if (!empty($_curTpl['item']))
$_curTpl['rewards'][] = [Type::ITEM, $_curTpl['ItemID']]; $_curTpl['rewards'][] = [TYPE_ITEM, $_curTpl['item']];
if (!empty($_curTpl['itemExtra'])) if (!empty($_curTpl['itemExtra']))
$_curTpl['rewards'][] = [Type::ITEM, $_curTpl['itemExtra']]; $_curTpl['rewards'][] = [TYPE_ITEM, $_curTpl['itemExtra']];
if (!empty($_curTpl['TitleA'])) if (!empty($_curTpl['title_A']))
$_curTpl['rewards'][] = [Type::TITLE, $_curTpl['TitleA']]; $_curTpl['rewards'][] = [TYPE_TITLE, $_curTpl['title_A']];
if (!empty($_curTpl['TitleH'])) if (!empty($_curTpl['title_H']))
if (empty($_curTpl['TitleA']) || $_curTpl['TitleA'] != $_curTpl['TitleH']) if (empty($_curTpl['title_A']) || $_curTpl['title_A'] != $_curTpl['title_H'])
$_curTpl['rewards'][] = [Type::TITLE, $_curTpl['TitleH']]; $_curTpl['rewards'][] = [TYPE_TITLE, $_curTpl['title_H']];
// icon // icon
$_curTpl['iconString'] = $_curTpl['iconString'] ?: 'trade_engineering'; $_curTpl['iconString'] = $_curTpl['iconString'] ?: 'trade_engineering';
@@ -103,7 +83,7 @@ class AchievementList extends BaseType
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
{ {
if ($addMask & GLOBALINFO_SELF) if ($addMask & GLOBALINFO_SELF)
$data[Type::ACHIEVEMENT][$this->id] = ['icon' => $this->curTpl['iconString'], 'name' => $this->getField('name', true)]; $data[TYPE_ACHIEVEMENT][$this->id] = ['icon' => $this->curTpl['iconString'], 'name' => $this->getField('name', true)];
if ($addMask & GLOBALINFO_REWARDS) if ($addMask & GLOBALINFO_REWARDS)
foreach ($this->curTpl['rewards'] as $_) foreach ($this->curTpl['rewards'] as $_)
@@ -151,7 +131,7 @@ class AchievementList extends BaseType
if (isset($this->criteria[$this->id])) if (isset($this->criteria[$this->id]))
return $this->criteria[$this->id]; return $this->criteria[$this->id];
$result = DB::Aowow()->Select('SELECT * FROM ?_achievementcriteria WHERE `refAchievementId` = ?d ORDER BY `order` ASC', $this->curTpl['refAchievement'] ?: $this->id); $result = DB::Aowow()->Select('SELECT * FROM ?_achievementcriteria WHERE `refAchievementId` = ?d ORDER BY `order` ASC', $this->id);
if (!$result) if (!$result)
return []; return [];
@@ -226,25 +206,23 @@ class AchievementList extends BaseType
break; break;
} }
$criteria .= '<!--cr'.$crt['id'].':'.$crt['type'].':'.$crt['value1'].'-->- '.$crtName;
if ($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER) if ($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER)
$criteria .= '&nbsp;<span class="moneygold">'.Lang::nf($crt['value2' ] / 10000).'</span>'; $criteria .= '- '.Util::jsEscape($crtName).' <span class="moneygold">'.number_format($crt['value2' ] / 10000).'</span><br />';
else
$criteria .= '<br />'; $criteria .= '- '.Util::jsEscape($crtName).'<br />';
if (++$i == round(count($rows)/2)) if (++$i == round(count($rows)/2))
$criteria .= '</small></td><th class="q0" style="white-space: nowrap; text-align: left"><small>'; $criteria .= '</small></td><th class="q0" style="white-space: nowrap; text-align: left"><small>';
} }
$x = '<table><tr><td><b class="q">'; $x = '<table><tr><td><b class="q">';
$x .= $name; $x .= Util::jsEscape($name);
$x .= '</b></td></tr></table>'; $x .= '</b></td></tr></table>';
if ($description || $criteria) if ($description || $criteria)
$x .= '<table><tr><td>'; $x .= '<table><tr><td>';
if ($description) if ($description)
$x .= '<br />'.$description.'<br />'; $x .= '<br />'.Util::jsEscape($description).'<br />';
if ($criteria) if ($criteria)
{ {
@@ -266,7 +244,7 @@ class AchievementList extends BaseType
$data[$this->id] = array( $data[$this->id] = array(
"n" => $this->getField('name', true), "n" => $this->getField('name', true),
"s" => $this->curTpl['faction'], "s" => $this->curTpl['faction'],
"t" => Type::ACHIEVEMENT, "t" => TYPE_ACHIEVEMENT,
"ti" => $this->id "ti" => $this->id
); );
} }
@@ -300,42 +278,62 @@ class AchievementListFilter extends Filter
321 => -1, 424 => -1, 301 => -1 321 => -1, 424 => -1, 301 => -1
) )
); );
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
2 => [FILTER_CR_BOOLEAN, 'reward_loc0', true ], // givesreward 2 => [FILTER_CR_BOOLEAN, 'reward_loc0', true ], // givesreward
3 => [FILTER_CR_STRING, 'reward', STR_LOCALIZED ], // rewardtext 3 => [FILTER_CR_STRING, 'reward', true ], // rewardtext
4 => [FILTER_CR_NYI_PH, null, 1, ], // location [enum] 7 => [FILTER_CR_BOOLEAN, 'chainId', ], // partseries
5 => [FILTER_CR_CALLBACK, 'cbSeries', ACHIEVEMENT_CU_FIRST_SERIES, null], // first in series [yn] 9 => [FILTER_CR_NUMERIC, 'id', null, true], // id
6 => [FILTER_CR_CALLBACK, 'cbSeries', ACHIEVEMENT_CU_LAST_SERIES, null], // last in series [yn] 10 => [FILTER_CR_STRING, 'si.iconString', ], // icon
7 => [FILTER_CR_BOOLEAN, 'chainId', ], // partseries 18 => [FILTER_CR_STAFFFLAG, 'flags', ], // flags
9 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id 14 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
10 => [FILTER_CR_STRING, 'ic.name', ], // icon 15 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
11 => [FILTER_CR_CALLBACK, 'cbRelEvent', null, null], // related event [enum] 16 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
14 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
15 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
16 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
18 => [FILTER_CR_STAFFFLAG, 'flags', ] // flags
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_RANGE, [2, 18], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 99999]], true ], // criteria operators
'crv' => [FILTER_V_REGEX, '/[\p{C};:%\\\\]/ui', true ], // criteria values - only printable chars, no delimiters
'na' => [FILTER_V_REGEX, '/[\p{C};%\\\\]/ui', false], // name / description - only printable chars, no delimiter
'ex' => [FILTER_V_EQUAL, 'on', false], // extended name search
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'si' => [FILTER_V_LIST, [1, 2, 3, -1, -2], false], // side
'minpt' => [FILTER_V_RANGE, [1, 99], false], // required level min
'maxpt' => [FILTER_V_RANGE, [1, 99], false] // required level max
); );
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCr = $this->genericCriterion($cr)) if ($genCr = $this->genericCriterion($cr))
return $genCr; return $genCr;
unset($cr);
$this->error = true;
return [1];
}
switch ($cr[0])
{
case 4: // location [enum]
/* todo */ return [1]; // no plausible locations parsed yet
case 5: // first in series [yn]
if ($this->int2Bool($cr[1]))
return $cr[1] ? ['AND', ['chainId', 0, '!'], ['cuFlags', ACHIEVEMENT_CU_FIRST_SERIES, '&']] : ['AND', ['chainId', 0, '!'], [['cuFlags', ACHIEVEMENT_CU_FIRST_SERIES, '&'], 0]];
break;
case 6: // last in series [yn]
if ($this->int2Bool($cr[1]))
return $cr[1] ? ['AND', ['chainId', 0, '!'], ['cuFlags', ACHIEVEMENT_CU_LAST_SERIES, '&']] : ['AND', ['chainId', 0, '!'], [['cuFlags', ACHIEVEMENT_CU_LAST_SERIES, '&'], 0]];
break;
case 11: // Related Event [enum]
$_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null;
if ($_ !== null)
{
if (is_int($_))
return ($_ > 0) ? ['category', $_] : ['id', abs($_)];
else
{
$ids = array_filter($this->enums[$cr[0]], function($x) {
return is_int($x) && $x > 0;
});
return ['category', $ids, $_ ? null : '!'];
}
}
break;
}
unset($cr); unset($cr);
$this->error = true; $this->error = true;
return [1]; return [1];
@@ -361,57 +359,45 @@ class AchievementListFilter extends Filter
// points min // points min
if (isset($_v['minpt'])) if (isset($_v['minpt']))
$parts[] = ['points', $_v['minpt'], '>=']; {
if ($this->isSaneNumeric($_v['minpt']))
$parts[] = ['points', $_v['minpt'], '>='];
else
unset($_v['minpt']);
}
// points max // points max
if (isset($_v['maxpt'])) if (isset($_v['maxpt']))
$parts[] = ['points', $_v['maxpt'], '<=']; {
if ($this->isSaneNumeric($_v['maxpt']))
$parts[] = ['points', $_v['maxpt'], '<='];
else
unset($_v['maxpt']);
}
// faction (side) // faction (side)
if (isset($_v['si'])) if (isset($_v['si']))
{ {
switch ($_v['si']) switch ($_v['si'])
{ {
case 3: // both
$parts[] = ['faction', 0];
break;
case -1: // faction, exclusive both case -1: // faction, exclusive both
case -2: case -2:
$parts[] = ['faction', -$_v['si']]; $parts[] = ['faction', -$_v['si']];
break; break;
case 1: // faction, inclusive both case 1: // faction, inclusive both
case 2: case 2:
case 3: // both $parts[] = ['OR', ['faction', 0], ['faction', $_v['si']]];
$parts[] = ['faction', $_v['si'], '&'];
break; break;
default:
unset($_v['si']);
} }
} }
return $parts; return $parts;
} }
protected function cbRelEvent($cr, $value)
{
if (!isset($this->enums[$cr[0]][$cr[1]]))
return false;
$_ = $this->enums[$cr[0]][$cr[1]];
if (is_int($_))
return ($_ > 0) ? ['category', $_] : ['id', abs($_)];
else
{
$ids = array_filter($this->enums[$cr[0]], function($x) { return is_int($x) && $x > 0; });
return ['category', $ids, $_ ? null : '!'];
}
return false;
}
protected function cbSeries($cr, $value)
{
if ($this->int2Bool($cr[1]))
return $cr[1] ? ['AND', ['chainId', 0, '!'], ['cuFlags', $value, '&']] : ['AND', ['chainId', 0, '!'], [['cuFlags', $value, '&'], 0]];
return false;
}
} }
?> ?>

View File

@@ -1,102 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AreaTriggerList extends BaseType
{
use spawnHelper;
public static $type = Type::AREATRIGGER;
public static $brickFile = 'areatrigger';
public static $dataTable = '?_areatrigger';
protected $queryBase = 'SELECT a.*, a.id AS ARRAY_KEY FROM ?_areatrigger a';
protected $queryOpts = array(
'a' => [['s']],
's' => ['j' => ['?_spawns s ON s.type = 503 AND s.typeId = a.id', true], 's' => ', s.areaId']
);
public function __construct($conditions)
{
parent::__construct($conditions);
foreach ($this->iterate() as $id => &$_curTpl)
if (!$_curTpl['name'])
$_curTpl['name'] = 'Unnamed Areatrigger #' . $id;
}
public function getListviewData() : array
{
$data = [];
foreach ($this->iterate() as $__)
{
$data[$this->id] = array(
'id' => $this->curTpl['id'],
'type' => $this->curTpl['type'],
'name' => $this->curTpl['name'],
);
if ($_ = $this->curTpl['areaId'])
$data[$this->id]['location'] = [$_];
}
return $data;
}
public function getJSGlobals($addMask = GLOBALINFO_ANY)
{
return [];
}
public function renderTooltip() { }
}
class AreaTriggerListFilter extends Filter
{
protected $genericFilter = array(
2 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT] // id
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_LIST, [2], true ], // criteria ids
'crs' => [FILTER_V_RANGE, [1, 6], true ], // criteria operators
'crv' => [FILTER_V_RANGE, [0, 99999], true ], // criteria values - all criteria are numeric here
'na' => [FILTER_V_REGEX, '/[\p{C};\\\\]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'ty' => [FILTER_V_RANGE, [0, 5], true ] // types
);
protected function createSQLForCriterium(&$cr)
{
if (in_array($cr[0], array_keys($this->genericFilter)))
if ($genCr = $this->genericCriterion($cr))
return $genCr;
unset($cr);
$this->error = true;
return [1];
}
protected function createSQLForValues()
{
$parts = [];
$_v = &$this->fiData['v'];
// name [str]
if (isset($_v['na']))
if ($_ = $this->modularizeString(['name']))
$parts[] = $_;
// type [list]
if (isset($_v['ty']))
$parts[] = ['type', $_v['ty']];
return $parts;
}
}
?>

View File

@@ -1,346 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class ArenaTeamList extends BaseType
{
use profilerHelper, listviewHelper;
private $rankOrder = [];
public function getListviewData()
{
$data = [];
foreach ($this->iterate() as $__)
{
$data[$this->id] = array(
'name' => $this->curTpl['name'],
'realm' => Profiler::urlize($this->curTpl['realmName'], true),
'realmname' => $this->curTpl['realmName'],
// 'battlegroup' => Profiler::urlize($this->curTpl['battlegroup']), // was renamed to subregion somewhere around cata release
// 'battlegroupname' => $this->curTpl['battlegroup'],
'region' => Profiler::urlize($this->curTpl['region']),
'faction' => $this->curTpl['faction'],
'size' => $this->curTpl['type'],
'rank' => $this->curTpl['rank'],
'wins' => $this->curTpl['seasonWins'],
'games' => $this->curTpl['seasonGames'],
'rating' => $this->curTpl['rating'],
'members' => $this->curTpl['members']
);
}
return array_values($data);
}
public function renderTooltip() {}
public function getJSGlobals($addMask = 0) {}
}
class ArenaTeamListFilter extends Filter
{
public $extraOpts = [];
protected $genericFilter = [];
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'na' => [FILTER_V_REGEX, '/[\p{C};%\\\\]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'ex' => [FILTER_V_EQUAL, 'on', false], // only match exact
'si' => [FILTER_V_LIST, [1, 2], false], // side
'sz' => [FILTER_V_LIST, [2, 3, 5], false], // tema size
'rg' => [FILTER_V_CALLBACK, 'cbRegionCheck', false], // region
'sv' => [FILTER_V_CALLBACK, 'cbServerCheck', false], // server
);
protected function createSQLForCriterium(&$cr) { }
protected function createSQLForValues()
{
$parts = [];
$_v = $this->fiData['v'];
// region (rg), battlegroup (bg) and server (sv) are passed to ArenaTeamList as miscData and handled there
// name [str]
if (!empty($_v['na']))
if ($_ = $this->modularizeString(['at.name'], $_v['na'], !empty($_v['ex']) && $_v['ex'] == 'on'))
$parts[] = $_;
// side [list]
if (!empty($_v['si']))
{
if ($_v['si'] == 1)
$parts[] = ['c.race', [1, 3, 4, 7, 11]];
else if ($_v['si'] == 2)
$parts[] = ['c.race', [2, 5, 6, 8, 10]];
}
// size [int]
if (!empty($_v['sz']))
$parts[] = ['at.type', $_v['sz']];
return $parts;
}
protected function cbRegionCheck(&$v)
{
if (in_array($v, Util::$regions))
{
$this->parentCats[0] = $v; // directly redirect onto this region
$v = ''; // remove from filter
return true;
}
return false;
}
protected function cbServerCheck(&$v)
{
foreach (Profiler::getRealms() as $realm)
if ($realm['name'] == $v)
{
$this->parentCats[1] = Profiler::urlize($v);// directly redirect onto this server
$v = ''; // remove from filter
return true;
}
return false;
}
}
class RemoteArenaTeamList extends ArenaTeamList
{
protected $queryBase = 'SELECT `at`.*, `at`.`arenaTeamId` AS ARRAY_KEY FROM arena_team at';
protected $queryOpts = array(
'at' => [['atm', 'c'], 'g' => 'ARRAY_KEY', 'o' => 'rating DESC'],
'atm' => ['j' => 'arena_team_member atm ON atm.arenaTeamId = at.arenaTeamId'],
'c' => ['j' => 'characters c ON c.guid = atm.guid AND c.deleteInfos_Account IS NULL AND c.level <= 80 AND (c.extra_flags & '.Profiler::CHAR_GMFLAGS.') = 0', 's' => ', BIT_OR(IF(c.race IN (1, 3, 4, 7, 11), 1, 2)) - 1 AS faction']
);
private $members = [];
public function __construct($conditions = [], $miscData = null)
{
// select DB by realm
if (!$this->selectRealms($miscData))
{
trigger_error('no access to auth-db or table realmlist is empty', E_USER_WARNING);
return;
}
parent::__construct($conditions, $miscData);
if ($this->error)
return;
// ranks in DB are inaccurate. recalculate from rating (fetched as DESC from DB)
foreach ($this->dbNames as $rId => $__)
foreach ([2, 3, 5] as $type)
$this->rankOrder[$rId][$type] = DB::Characters($rId)->selectCol('SELECT arenaTeamId FROM arena_team WHERE `type` = ?d ORDER BY rating DESC', $type);
reset($this->dbNames); // only use when querying single realm
$realmId = key($this->dbNames);
$realms = Profiler::getRealms();
$distrib = [];
// post processing
foreach ($this->iterate() as $guid => &$curTpl)
{
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
// realm, rank
$r = explode(':', $guid);
if (!empty($realms[$r[0]]))
{
$curTpl['realm'] = $r[0];
$curTpl['realmName'] = $realms[$r[0]]['name'];
$curTpl['region'] = $realms[$r[0]]['region'];
$curTpl['rank'] = array_search($curTpl['arenaTeamId'], $this->rankOrder[$r[0]][$curTpl['type']]) + 1;
}
else
{
trigger_error('arena team "'.$curTpl['name'].'" belongs to nonexistant realm #'.$r, E_USER_WARNING);
unset($this->templates[$guid]);
continue;
}
// team members
$this->members[$r[0]][$r[1]] = $r[1];
// equalize distribution
if (empty($distrib[$curTpl['realm']]))
$distrib[$curTpl['realm']] = 1;
else
$distrib[$curTpl['realm']]++;
}
// get team members
foreach ($this->members as $realmId => &$teams)
$teams = DB::Characters($realmId)->select('
SELECT
at.arenaTeamId AS ARRAY_KEY, c.guid AS ARRAY_KEY2, c.name AS "0", c.class AS "1", IF(at.captainguid = c.guid, 1, 0) AS "2"
FROM
arena_team at
JOIN
arena_team_member atm ON atm.arenaTeamId = at.arenaTeamId JOIN characters c ON c.guid = atm.guid
WHERE
at.arenaTeamId IN (?a) AND
c.deleteInfos_Account IS NULL AND
c.level <= ?d AND
(c.extra_flags & ?d) = 0',
$teams,
MAX_LEVEL,
Profiler::CHAR_GMFLAGS
);
// equalize subject distribution across realms
$limit = CFG_SQL_LIMIT_DEFAULT;
foreach ($conditions as $c)
if (is_int($c))
$limit = $c;
$total = array_sum($distrib);
foreach ($distrib as &$d)
$d = ceil($limit * $d / $total);
foreach ($this->iterate() as $guid => &$curTpl)
{
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
{
unset($this->templates[$guid]);
continue;
}
$r = explode(':', $guid);
if (isset($this->members[$r[0]][$r[1]]))
$curTpl['members'] = array_values($this->members[$r[0]][$r[1]]); // [name, classId, isCaptain]
$distrib[$curTpl['realm']]--;
$limit--;
}
}
public function initializeLocalEntries()
{
$profiles = [];
// init members for tooltips
foreach ($this->members as $realmId => $teams)
{
$gladiators = [];
foreach ($teams as $team)
$gladiators = array_merge($gladiators, array_keys($team));
$profiles[$realmId] = new RemoteProfileList(array(['c.guid', $gladiators], CFG_SQL_LIMIT_NONE), ['sv' => $realmId]);
if (!$profiles[$realmId]->error)
$profiles[$realmId]->initializeLocalEntries();
}
$data = [];
foreach ($this->iterate() as $guid => $__)
{
$data[$guid] = array(
'realm' => $this->getField('realm'),
'realmGUID' => $this->getField('arenaTeamId'),
'name' => $this->getField('name'),
'nameUrl' => Profiler::urlize($this->getField('name')),
'type' => $this->getField('type'),
'rating' => $this->getField('rating'),
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
}
// basic arena team data
foreach (Util::createSqlBatchInsert($data) as $ins)
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team (?#) VALUES '.$ins, array_keys(reset($data)));
// merge back local ids
$localIds = DB::Aowow()->selectCol(
'SELECT CONCAT(realm, ":", realmGUID) AS ARRAY_KEY, id FROM ?_profiler_arena_team WHERE realm IN (?a) AND realmGUID IN (?a)',
array_column($data, 'realm'),
array_column($data, 'realmGUID')
);
foreach ($this->iterate() as $guid => &$_curTpl)
if (isset($localIds[$guid]))
$_curTpl['id'] = $localIds[$guid];
// profiler_arena_team_member requires profiles and arena teams to be filled
foreach ($this->members as $realmId => $teams)
{
if (empty($profiles[$realmId]))
continue;
$memberData = [];
foreach ($teams as $teamId => $team)
foreach ($team as $memberId => $member)
$memberData[] = array(
'arenaTeamId' => $localIds[$realmId.':'.$teamId],
'profileId' => $profiles[$realmId]->getEntry($realmId.':'.$memberId)['id'],
'captain' => $member[2]
);
foreach (Util::createSqlBatchInsert($memberData) as $ins)
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team_member (?#) VALUES '.$ins, array_keys(reset($memberData)));
}
}
}
class LocalArenaTeamList extends ArenaTeamList
{
protected $queryBase = 'SELECT at.*, at.id AS ARRAY_KEY FROM ?_profiler_arena_team at';
public function __construct($conditions = [], $miscData = null)
{
parent::__construct($conditions, $miscData);
if ($this->error)
return;
$realms = Profiler::getRealms();
// post processing
$members = DB::Aowow()->selectCol('SELECT *, arenaTeamId AS ARRAY_KEY, profileId AS ARRAY_KEY2 FROM ?_profiler_arena_team_member WHERE arenaTeamId IN (?a)', $this->getFoundIDs());
foreach ($this->iterate() as $id => &$curTpl)
{
if ($curTpl['realm'] && !isset($realms[$curTpl['realm']]))
continue;
if (isset($realms[$curTpl['realm']]))
{
$curTpl['realmName'] = $realms[$curTpl['realm']]['name'];
$curTpl['region'] = $realms[$curTpl['realm']]['region'];
}
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
$curTpl['members'] = $members[$id];
}
}
public function getProfileUrl()
{
$url = '?arena-team=';
return $url.implode('.', array(
Profiler::urlize($this->getField('region')),
Profiler::urlize($this->getField('realmName')),
Profiler::urlize($this->getField('name'))
));
}
}
?>

View File

@@ -6,11 +6,10 @@ if (!defined('AOWOW_REVISION'))
class CharClassList extends BaseType class CharClassList extends BaseType
{ {
public static $type = Type::CHR_CLASS; public static $type = TYPE_CLASS;
public static $brickFile = 'class'; public static $brickFile = 'class';
public static $dataTable = '?_classes';
protected $queryBase = 'SELECT c.*, id AS ARRAY_KEY FROM ?_classes c'; protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_classes c';
public function __construct($conditions = []) public function __construct($conditions = [])
{ {

View File

@@ -6,11 +6,10 @@ if (!defined('AOWOW_REVISION'))
class CharRaceList extends BaseType class CharRaceList extends BaseType
{ {
public static $type = Type::CHR_RACE; public static $type = TYPE_RACE;
public static $brickFile = 'race'; public static $brickFile = 'race';
public static $dataTable = '?_races';
protected $queryBase = 'SELECT r.*, id AS ARRAY_KEY FROM ?_races r'; protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_races r';
public function getListviewData() public function getListviewData()
{ {
@@ -40,7 +39,7 @@ class CharRaceList extends BaseType
$data = []; $data = [];
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
$data[Type::CHR_RACE][$this->id] = ['name' => $this->getField('name', true)]; $data[TYPE_RACE][$this->id] = ['name' => $this->getField('name', true)];
return $data; return $data;
} }

View File

@@ -8,13 +8,12 @@ class CreatureList extends BaseType
{ {
use spawnHelper; use spawnHelper;
public static $type = Type::NPC; public static $type = TYPE_NPC;
public static $brickFile = 'creature'; public static $brickFile = 'creature';
public static $dataTable = '?_creature';
protected $queryBase = 'SELECT ct.*, ct.id AS ARRAY_KEY FROM ?_creature ct'; protected $queryBase = 'SELECT ct.*, ct.id AS ARRAY_KEY FROM ?_creature ct';
public $queryOpts = array( public $queryOpts = array(
'ct' => [['ft', 'qse', 'dct1', 'dct2', 'dct3'], 's' => ', IFNULL(dct1.id, IFNULL(dct2.id, IFNULL(dct3.id, 0))) AS parentId, IFNULL(dct1.name_loc0, IFNULL(dct2.name_loc0, IFNULL(dct3.name_loc0, ""))) AS parent_loc0, IFNULL(dct1.name_loc2, IFNULL(dct2.name_loc2, IFNULL(dct3.name_loc2, ""))) AS parent_loc2, IFNULL(dct1.name_loc3, IFNULL(dct2.name_loc3, IFNULL(dct3.name_loc3, ""))) AS parent_loc3, IFNULL(dct1.name_loc4, IFNULL(dct2.name_loc4, IFNULL(dct3.name_loc4, ""))) AS parent_loc4, IFNULL(dct1.name_loc6, IFNULL(dct2.name_loc6, IFNULL(dct3.name_loc6, ""))) AS parent_loc6, IFNULL(dct1.name_loc8, IFNULL(dct2.name_loc8, IFNULL(dct3.name_loc8, ""))) AS parent_loc8, IF(dct1.difficultyEntry1 = ct.id, 1, IF(dct2.difficultyEntry2 = ct.id, 2, IF(dct3.difficultyEntry3 = ct.id, 3, 0))) AS difficultyMode'], 'ct' => [['ft', 'qse', 'dct1', 'dct2', 'dct3'], 's' => ', IFNULL(dct1.id, IFNULL(dct2.id, IFNULL(dct3.id, 0))) AS parentId, IFNULL(dct1.name_loc0, IFNULL(dct2.name_loc0, IFNULL(dct3.name_loc0, ""))) AS parent_loc0, IFNULL(dct1.name_loc2, IFNULL(dct2.name_loc2, IFNULL(dct3.name_loc2, ""))) AS parent_loc2, IFNULL(dct1.name_loc3, IFNULL(dct2.name_loc3, IFNULL(dct3.name_loc3, ""))) AS parent_loc3, IFNULL(dct1.name_loc6, IFNULL(dct2.name_loc6, IFNULL(dct3.name_loc6, ""))) AS parent_loc6, IFNULL(dct1.name_loc8, IFNULL(dct2.name_loc8, IFNULL(dct3.name_loc8, ""))) AS parent_loc8, IF(dct1.difficultyEntry1 = ct.id, 1, IF(dct2.difficultyEntry2 = ct.id, 2, IF(dct3.difficultyEntry3 = ct.id, 3, 0))) AS difficultyMode'],
'dct1' => ['j' => ['?_creature dct1 ON ct.cuFlags & 0x02 AND dct1.difficultyEntry1 = ct.id', true]], 'dct1' => ['j' => ['?_creature dct1 ON ct.cuFlags & 0x02 AND dct1.difficultyEntry1 = ct.id', true]],
'dct2' => ['j' => ['?_creature dct2 ON ct.cuFlags & 0x02 AND dct2.difficultyEntry2 = ct.id', true]], 'dct2' => ['j' => ['?_creature dct2 ON ct.cuFlags & 0x02 AND dct2.difficultyEntry2 = ct.id', true]],
'dct3' => ['j' => ['?_creature dct3 ON ct.cuFlags & 0x02 AND dct3.difficultyEntry3 = ct.id', true]], 'dct3' => ['j' => ['?_creature dct3 ON ct.cuFlags & 0x02 AND dct3.difficultyEntry3 = ct.id', true]],
@@ -49,7 +48,7 @@ class CreatureList extends BaseType
public static function getName($id) public static function getName($id)
{ {
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_creature WHERE id = ?d', $id); $n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_creature WHERE id = ?d', $id);
return Util::localizedString($n, 'name'); return Util::localizedString($n, 'name');
} }
@@ -77,14 +76,13 @@ class CreatureList extends BaseType
if ($type) if ($type)
$row3[] = Lang::game('ct', $type); $row3[] = Lang::game('ct', $type);
if ($_ = Lang::npc('rank', $this->curTpl['rank'])) $row3[] = '('.Lang::npc('rank', $this->curTpl['rank']).')';
$row3[] = '('.$_.')';
$x = '<table>'; $x = '<table>';
$x .= '<tr><td><b class="q">'.Util::htmlEscape($this->getField('name', true)).'</b></td></tr>'; $x .= '<tr><td><b class="q">'.$this->getField('name', true).'</b></td></tr>';
if ($sn = $this->getField('subname', true)) if ($sn = $this->getField('subname', true))
$x .= '<tr><td>'.Util::htmlEscape($sn).'</td></tr>'; $x .= '<tr><td>'.$sn.'</td></tr>';
$x .= '<tr><td>'.implode(' ', $row3).'</td></tr>'; $x .= '<tr><td>'.implode(' ', $row3).'</td></tr>';
@@ -102,22 +100,27 @@ class CreatureList extends BaseType
public function getRandomModelId() public function getRandomModelId()
{ {
// dwarf?? [null, 30754, 30753, 30755, 30736]
// totems use hardcoded models, tauren model is base // totems use hardcoded models, tauren model is base
$totems = [null, 4589, 4588, 4587, 4590]; // slot => modelId $totems = array( // tauren => [orc, dwarf(?!), troll, tauren, draenei]
$data = []; 4589 => [30758, 30754, 30762, 4589, 19074], // fire
4588 => [30757, 30753, 30761, 4588, 19073], // earth
4587 => [30759, 30755, 30763, 4587, 19075], // water
4590 => [30756, 30736, 30760, 4590, 19071], // air
);
$data = [];
for ($i = 1; $i < 5; $i++) for ($i = 1; $i < 5; $i++)
if ($_ = $this->curTpl['displayId'.$i]) if ($_ = $this->curTpl['displayId'.$i])
$data[] = $_; $data[] = $_;
if (count($data) == 1 && ($slotId = array_search($data[0], $totems))) if (count($data) == 1 && in_array($data[0], array_keys($totems)))
$data = DB::World()->selectCol('SELECT DisplayId FROM player_totem_model WHERE TotemSlot = ?d', $slotId); $data = $totems[$data[0]];
return !$data ? 0 : $data[array_rand($data)]; return !$data ? 0 : $data[array_rand($data)];
} }
public function getBaseStats(string $type) : array public function getBaseStats($type)
{ {
// i'm aware of the BaseVariance/RangedVariance fields ... i'm just totaly unsure about the whole damage calculation // i'm aware of the BaseVariance/RangedVariance fields ... i'm just totaly unsure about the whole damage calculation
switch ($type) switch ($type)
@@ -142,14 +145,8 @@ class CreatureList extends BaseType
$rngMin = ($this->getField('dmgMin') + ($this->getField('rngAtkPwrMin') / 14)) * $this->getField('dmgMultiplier') * $this->getField('rngAtkSpeed'); $rngMin = ($this->getField('dmgMin') + ($this->getField('rngAtkPwrMin') / 14)) * $this->getField('dmgMultiplier') * $this->getField('rngAtkSpeed');
$rngMax = ($this->getField('dmgMax') * 1.5 + ($this->getField('rngAtkPwrMax') / 14)) * $this->getField('dmgMultiplier') * $this->getField('rngAtkSpeed'); $rngMax = ($this->getField('dmgMax') * 1.5 + ($this->getField('rngAtkPwrMax') / 14)) * $this->getField('dmgMultiplier') * $this->getField('rngAtkSpeed');
return [$rngMin, $rngMax]; return [$rngMin, $rngMax];
case 'resistance':
$r = [];
for ($i = SPELL_SCHOOL_HOLY; $i < SPELL_SCHOOL_ARCANE+1; $i++)
$r[$i] = $this->getField('resistance'.$i);
return $r;
default: default:
return []; return [0, 0];
} }
} }
@@ -164,22 +161,9 @@ class CreatureList extends BaseType
* *
* NPCINFO_TAMEABLE (0x1): include texture & react * NPCINFO_TAMEABLE (0x1): include texture & react
* NPCINFO_MODEL (0x2): * NPCINFO_MODEL (0x2):
* NPCINFO_REP (0x4): include repreward
*/ */
$data = []; $data = [];
$rewRep = [];
if ($addInfoMask & NPCINFO_REP && $this->getFoundIDs())
{
$rewRep = DB::World()->selectCol('
SELECT creature_id AS ARRAY_KEY, RewOnKillRepFaction1 AS ARRAY_KEY2, RewOnKillRepValue1 FROM creature_onkill_reputation WHERE creature_id IN (?a) AND RewOnKillRepFaction1 > 0 UNION
SELECT creature_id AS ARRAY_KEY, RewOnKillRepFaction2 AS ARRAY_KEY2, RewOnKillRepValue2 FROM creature_onkill_reputation WHERE creature_id IN (?a) AND RewOnKillRepFaction2 > 0',
$this->getFoundIDs(),
$this->getFoundIDs()
);
}
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
{ {
@@ -223,7 +207,6 @@ class CreatureList extends BaseType
'react' => [$this->curTpl['A'], $this->curTpl['H']], 'react' => [$this->curTpl['A'], $this->curTpl['H']],
); );
if ($this->getField('startsQuests')) if ($this->getField('startsQuests'))
$data[$this->id]['hasQuests'] = 1; $data[$this->id]['hasQuests'] = 1;
@@ -232,14 +215,6 @@ class CreatureList extends BaseType
if ($addInfoMask & NPCINFO_TAMEABLE) // only first skin of first model ... we're omitting potentially 11 skins here .. but the lv accepts only one .. w/e if ($addInfoMask & NPCINFO_TAMEABLE) // only first skin of first model ... we're omitting potentially 11 skins here .. but the lv accepts only one .. w/e
$data[$this->id]['skin'] = $this->curTpl['textureString']; $data[$this->id]['skin'] = $this->curTpl['textureString'];
if ($addInfoMask & NPCINFO_REP)
{
$data[$this->id]['reprewards'] = [];
if ($rewRep[$this->id])
foreach ($rewRep[$this->id] as $fac => $val)
$data[$this->id]['reprewards'][] = [$fac, $val];
}
} }
} }
@@ -252,7 +227,7 @@ class CreatureList extends BaseType
$data = []; $data = [];
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
$data[Type::NPC][$this->id] = ['name' => $this->getField('name', true)]; $data[TYPE_NPC][$this->id] = ['name' => $this->getField('name', true)];
return $data; return $data;
} }
@@ -265,7 +240,7 @@ class CreatureList extends BaseType
{ {
$data[$this->id] = array( $data[$this->id] = array(
'n' => $this->getField('parentId') ? $this->getField('parent', true) : $this->getField('name', true), 'n' => $this->getField('parentId') ? $this->getField('parent', true) : $this->getField('name', true),
't' => Type::NPC, 't' => TYPE_NPC,
'ti' => $this->getField('parentId') ?: $this->id, 'ti' => $this->getField('parentId') ?: $this->id,
// 'bd' => (int)($this->curTpl['cuFlags'] & NPC_CU_INSTANCE_BOSS || ($this->curTpl['typeFlags'] & 0x4 && $this->curTpl['rank'])) // 'bd' => (int)($this->curTpl['cuFlags'] & NPC_CU_INSTANCE_BOSS || ($this->curTpl['typeFlags'] & 0x4 && $this->curTpl['rank']))
// 'z' where am i spawned // 'z' where am i spawned
@@ -278,7 +253,6 @@ class CreatureList extends BaseType
public function addRewardsToJScript(&$refs) { } public function addRewardsToJScript(&$refs) { }
} }
@@ -294,66 +268,228 @@ class CreatureListFilter extends Filter
// cr => [type, field, misc, extraCol] // cr => [type, field, misc, extraCol]
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
1 => [FILTER_CR_CALLBACK, 'cbHealthMana', 'healthMax', 'healthMin'], // health [num] 5 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_REPAIRER ], // canrepair
2 => [FILTER_CR_CALLBACK, 'cbHealthMana', 'manaMin', 'manaMax' ], // mana [num] 6 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin
3 => [FILTER_CR_CALLBACK, 'cbFaction', null, null ], // faction [enum] 9 => [FILTER_CR_BOOLEAN, 'lootId', ], // lootable
5 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_REPAIRER ], // canrepair 11 => [FILTER_CR_BOOLEAN, 'pickpocketLootId', ], // pickpocketable
6 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin 18 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_AUCTIONEER ], // auctioneer
7 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [enum] 19 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker
8 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [enum] 20 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BATTLEMASTER ], // battlemaster
9 => [FILTER_CR_BOOLEAN, 'lootId', ], // lootable 21 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_FLIGHT_MASTER ], // flightmaster
10 => [FILTER_CR_CALLBACK, 'cbRegularSkinLoot', NPC_TYPEFLAG_SPECIALLOOT ], // skinnable [yn] 22 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // guildmaster
11 => [FILTER_CR_BOOLEAN, 'pickpocketLootId', ], // pickpocketable 23 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_INNKEEPER ], // innkeeper
12 => [FILTER_CR_CALLBACK, 'cbMoneyDrop', null, null ], // averagemoneydropped [op] [int] 24 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_CLASS_TRAINER ], // talentunlearner
15 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_HERBLOOT, null ], // gatherable [yn] 25 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // tabardvendor
16 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_MININGLOOT, null ], // minable [yn] 27 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_STABLE_MASTER ], // stablemaster
18 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_AUCTIONEER ], // auctioneer 28 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_TRAINER ], // trainer
19 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker 29 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_VENDOR ], // vendor
20 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BATTLEMASTER ], // battlemaster 19 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker
21 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_FLIGHT_MASTER ], // flightmaster 37 => [FILTER_CR_NUMERIC, 'id', null, true], // id
22 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // guildmaster 35 => [FILTER_CR_STRING, 'textureString' ], // useskin
23 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_INNKEEPER ], // innkeeper 32 => [FILTER_CR_FLAG, 'cuFlags', NPC_CU_INSTANCE_BOSS ], // instanceboss
24 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_CLASS_TRAINER ], // talentunlearner 33 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
25 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // tabardvendor 31 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
27 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_STABLE_MASTER ], // stablemaster 40 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
28 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_TRAINER ], // trainer
29 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_VENDOR ], // vendor
31 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
32 => [FILTER_CR_FLAG, 'cuFlags', NPC_CU_INSTANCE_BOSS ], // instanceboss
33 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
34 => [FILTER_CR_NYI_PH, 1, null ], // usemodel [str] - displayId -> id:creatureDisplayInfo.dbc/model -> id:cratureModelData.dbc/modelPath
35 => [FILTER_CR_STRING, 'textureString' ], // useskin
37 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true ], // id
38 => [FILTER_CR_CALLBACK, 'cbRelEvent', null, null ], // relatedevent [enum]
40 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
41 => [FILTER_CR_NYI_PH, 1, null ], // haslocation [yn] [staff]
42 => [FILTER_CR_CALLBACK, 'cbReputation', '>', null ], // increasesrepwith [enum]
43 => [FILTER_CR_CALLBACK, 'cbReputation', '<', null ], // decreasesrepwith [enum]
44 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_ENGINEERLOOT, null ] // salvageable [yn]
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_LIST, [[1, 3],[5, 12], 15, 16, [18, 25], [27, 29], [31, 35], 37, 38, [40, 44]], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 9999]], true ], // criteria operators
'crv' => [FILTER_V_REGEX, '/[\p{C}:;%\\\\]/ui', true ], // criteria values - only printable chars, no delimiter
'na' => [FILTER_V_REGEX, '/[\p{C};%\\\\]/ui', false], // name / subname - only printable chars, no delimiter
'ex' => [FILTER_V_EQUAL, 'on', false], // also match subname
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'fa' => [FILTER_V_CALLBACK, 'cbPetFamily', true ], // pet family [list] - cat[0] == 1
'minle' => [FILTER_V_RANGE, [1, 99], false], // min level [int]
'maxle' => [FILTER_V_RANGE, [1, 99], false], // max level [int]
'cl' => [FILTER_V_RANGE, [0, 4], true ], // classification [list]
'ra' => [FILTER_V_LIST, [-1, 0, 1], false], // react alliance [int]
'rh' => [FILTER_V_LIST, [-1, 0, 1], false] // react horde [int]
); );
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCr = $this->genericCriterion($cr)) if ($genCr = $this->genericCriterion($cr))
return $genCr; return $genCr;
unset($cr);
$this->error = true;
return [1];
}
switch ($cr[0])
{
case 1: // health [num]
if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1]))
break;
// remap OP for this special case
switch ($cr[1])
{
case '=': // min > max is totally possible
$this->extraOpts['ct']['h'][] = 'healthMin = healthMax AND healthMin = '.$cr[2];
break;
case '>':
$this->extraOpts['ct']['h'][] = 'IF(healthMin > healthMax, healthMax, healthMin) > '.$cr[2];
break;
case '>=':
$this->extraOpts['ct']['h'][] = 'IF(healthMin > healthMax, healthMax, healthMin) >= '.$cr[2];
break;
case '<':
$this->extraOpts['ct']['h'][] = 'IF(healthMin > healthMax, healthMin, healthMax) < '.$cr[2];
break;
case '<=':
$this->extraOpts['ct']['h'][] = 'IF(healthMin > healthMax, healthMin, healthMax) <= '.$cr[2];
break;
}
return [1]; // always true, use post-filter
case 2: // mana [num]
if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1]))
break;
// remap OP for this special case
switch ($cr[1])
{
case '=':
$this->extraOpts['ct']['h'][] = 'manaMin = manaMax AND manaMin = '.$cr[2];
break;
case '>':
$this->extraOpts['ct']['h'][] = 'IF(manaMin > manaMax, manaMin, manaMax) > '.$cr[2];
break;
case '>=':
$this->extraOpts['ct']['h'][] = 'IF(manaMin > manaMax, manaMin, manaMax) >= '.$cr[2];
break;
case '<':
$this->extraOpts['ct']['h'][] = 'IF(manaMin > manaMax, manaMax, manaMin) < '.$cr[2];
break;
case '<=':
$this->extraOpts['ct']['h'][] = 'IF(manaMin > manaMax, manaMax, manaMin) <= '.$cr[2];
break;
}
return [1]; // always true, use post-filter
case 7: // startsquest [enum]
switch ($cr[1])
{
case 1: // any
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!']];
case 2: // alliance
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']];
case 3: // horde
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']];
case 4: // both
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
case 5: // none
$this->extraOpts['ct']['h'][] = 'startsQuests = 0';
return [1];
}
break;
case 8: // endsquest [enum]
switch ($cr[1])
{
case 1: // any
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!']];
case 2: // alliance
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']];
case 3: // horde
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']];
case 4: // both
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
case 5: // none
$this->extraOpts['ct']['h'][] = 'endsQuests = 0';
return [1];
}
break;
case 3: // faction [enum]
if (in_array($cr[1], $this->enums[$cr[0]]))
{
$facTpls = [];
$facs = new FactionList(array('OR', ['parentFactionId', $cr[1]], ['id', $cr[1]]));
foreach ($facs->iterate() as $__)
$facTpls = array_merge($facTpls, $facs->getField('templateIds'));
if (!$facTpls)
return [0];
return ['faction', $facTpls];
}
break;
case 38; // relatedevent
if (!$this->isSaneNumeric($cr[1]))
break;
if ($cr[1] == FILTER_ENUM_ANY)
{
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0');
$cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds);
return ['s.guid', $cGuids];
}
else if ($cr[1] == FILTER_ENUM_NONE)
{
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0');
$cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds);
return ['s.guid', $cGuids, '!'];
}
else if ($cr[1])
{
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId = ?d', $cr[1]);
$cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds);
return ['s.guid', $cGuids];
}
break;
case 42: // increasesrepwith [enum]
if (in_array($cr[1], $this->enums[3])) // reuse
{
if ($cIds = DB::World()->selectCol('SELECT creature_id FROM creature_onkill_reputation WHERE (RewOnKillRepFaction1 = ?d AND RewOnKillRepValue1 > 0) OR (RewOnKillRepFaction2 = ?d AND RewOnKillRepValue2 > 0)', $cr[1], $cr[1]))
return ['id', $cIds];
else
return [0];
}
break;
case 43: // decreasesrepwith [enum]
if (in_array($cr[1], $this->enums[3])) // reuse
{
if ($cIds = DB::World()->selectCol('SELECT creature_id FROM creature_onkill_reputation WHERE (RewOnKillRepFaction1 = ?d AND RewOnKillRepValue1 < 0) OR (RewOnKillRepFaction2 = ?d AND RewOnKillRepValue2 < 0)', $cr[1], $cr[1]))
return ['id', $cIds];
else
return [0];
}
break;
case 12: // averagemoneydropped [op] [int]
if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1]))
break;
return ['AND', ['((minGold + maxGold) / 2)', $cr[2], $cr[1]]];
case 15: // gatherable [yn]
if ($this->int2Bool($cr[1]))
{
if ($cr[1])
return ['AND', ['skinLootId', 0, '>'], ['typeFlags', NPC_TYPEFLAG_HERBLOOT, '&']];
else
return ['OR', ['skinLootId', 0], [['typeFlags', NPC_TYPEFLAG_HERBLOOT, '&'], 0]];
}
break;
case 44: // salvageable [yn]
if ($this->int2Bool($cr[1]))
{
if ($cr[1])
return ['AND', ['skinLootId', 0, '>'], ['typeFlags', NPC_TYPEFLAG_ENGINEERLOOT, '&']];
else
return ['OR', ['skinLootId', 0], [['typeFlags', NPC_TYPEFLAG_ENGINEERLOOT, '&'], 0]];
}
break;
case 16: // minable [yn]
if ($this->int2Bool($cr[1]))
{
if ($cr[1])
return ['AND', ['skinLootId', 0, '>'], ['typeFlags', NPC_TYPEFLAG_MININGLOOT, '&']];
else
return ['OR', ['skinLootId', 0], [['typeFlags', NPC_TYPEFLAG_MININGLOOT, '&'], 0]];
}
break;
case 10: // skinnable [yn]
if ($this->int2Bool($cr[1]))
{
if ($cr[1])
return ['AND', ['skinLootId', 0, '>'], [['typeFlags', NPC_TYPEFLAG_SPECIALLOOT, '&'], 0]];
else
return ['OR', ['skinLootId', 0], [['typeFlags', NPC_TYPEFLAG_SPECIALLOOT, '&'], 0, '!']];
}
break;
case 34: // usemodel [str] // displayId -> id:creatureDisplayInfo.dbc/model -> id:cratureModelData.dbc/modelPath
case 41: // haslocation [yn] [staff]
/* todo */ return [1];
}
unset($cr); unset($cr);
$this->error = true; $this->error = true;
return [1]; return [1];
@@ -379,176 +515,65 @@ class CreatureListFilter extends Filter
// pet family [list] // pet family [list]
if (isset($_v['fa'])) if (isset($_v['fa']))
$parts[] = ['family', $_v['fa']]; {
$_ = (array)$_v['fa'];
if (!array_diff($_, [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 20, 21, 24, 25, 26, 27, 30, 31, 32, 33, 34, 35, 37, 38, 39, 41, 42, 43, 44, 45, 46]))
$parts[] = ['family', $_];
else
unset($_v['cl']);
}
// creatureLevel min [int] // creatureLevel min [int]
if (isset($_v['minle'])) if (isset($_v['minle']))
$parts[] = ['minLevel', $_v['minle'], '>=']; {
if (is_int($_v['minle']) && $_v['minle'] > 0)
$parts[] = ['minLevel', $_v['minle'], '>='];
else
unset($_v['minle']);
}
// creatureLevel max [int] // creatureLevel max [int]
if (isset($_v['maxle'])) if (isset($_v['maxle']))
$parts[] = ['maxLevel', $_v['maxle'], '<=']; {
if (is_int($_v['maxle']) && $_v['maxle'] > 0)
$parts[] = ['maxLevel', $_v['maxle'], '<='];
else
unset($_v['maxle']);
}
// classification [list] // classification [list]
if (isset($_v['cl'])) if (isset($_v['cl']))
$parts[] = ['rank', $_v['cl']]; {
$_ = (array)$_v['cl'];
if (!array_diff($_, [0, 1, 2, 3, 4]))
$parts[] = ['rank', $_];
else
unset($_v['cl']);
}
// react Alliance [int] // react Alliance [int]
if (isset($_v['ra'])) if (isset($_v['ra']))
$parts[] = ['ft.A', $_v['ra']]; {
$_ = (int)$_v['ra'];
if (in_array($_, [-1, 0, 1]))
$parts[] = ['ft.A', $_];
else
unset($_v['ra']);
}
// react Horde [int] // react Horde [int]
if (isset($_v['rh'])) if (isset($_v['rh']))
$parts[] = ['ft.H', $_v['rh']]; {
$_ = (int)$_v['rh'];
if (in_array($_, [-1, 0, 1]))
$parts[] = ['ft.H', $_];
else
unset($_v['rh']);
}
return $parts; return $parts;
} }
protected function cbPetFamily(&$val)
{
if (!$this->parentCats || $this->parentCats[0] != 1)
return false;
if (!Util::checkNumeric($val, NUM_REQ_INT))
return false;
$type = FILTER_V_LIST;
$valid = [[1, 9], 11, 12, 20, 21, [24, 27], [30, 35], [37, 39], [41, 46]];
return $this->checkInput($type, $valid, $val);
}
protected function cbRelEvent($cr)
{
if (!Util::checkNumeric($cr[1], NUM_REQ_INT))
return false;
if ($cr[1] == FILTER_ENUM_ANY)
{
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0');
$cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds);
return ['s.guid', $cGuids];
}
else if ($cr[1] == FILTER_ENUM_NONE)
{
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0');
$cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds);
return ['s.guid', $cGuids, '!'];
}
else if ($cr[1])
{
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId = ?d', $cr[1]);
$cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds);
return ['s.guid', $cGuids];
}
return false;
}
protected function cbMoneyDrop($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
return ['AND', ['((minGold + maxGold) / 2)', $cr[2], $cr[1]]];
}
protected function cbQuestRelation($cr, $field, $val)
{
switch ($cr[1])
{
case 1: // any
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!']];
case 2: // alliance
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']];
case 3: // horde
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']];
case 4: // both
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
case 5: // none
$this->extraOpts['ct']['h'][] = $field.' = 0';
return [1];
}
return false;
}
protected function cbHealthMana($cr, $minField, $maxField)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
// remap OP for this special case
switch ($cr[1])
{
case '=': // min > max is totally possible
$this->extraOpts['ct']['h'][] = $minField.' = '.$maxField.' AND '.$minField.' = '.$cr[2];
break;
case '>':
case '>=':
case '<':
case '<=':
$this->extraOpts['ct']['h'][] = 'IF('.$minField.' > '.$maxField.', '.$maxField.', '.$minField.') '.$cr[1].' '.$cr[2];
break;
}
return [1]; // always true, use post-filter
}
protected function cbSpecialSkinLoot($cr, $typeFlag)
{
if (!$this->int2Bool($cr[1]))
return false;
if ($cr[1])
return ['AND', ['skinLootId', 0, '>'], ['typeFlags', $typeFlag, '&']];
else
return ['OR', ['skinLootId', 0], [['typeFlags', $typeFlag, '&'], 0]];
}
protected function cbRegularSkinLoot($cr, $typeFlag)
{
if (!$this->int2Bool($cr[1]))
return false;
if ($cr[1])
return ['AND', ['skinLootId', 0, '>'], [['typeFlags', $typeFlag, '&'], 0]];
else
return ['OR', ['skinLootId', 0], ['typeFlags', $typeFlag, '&']];
}
protected function cbReputation($cr, $op)
{
if (!in_array($cr[1], $this->enums[3])) // reuse
return false;
if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE id = ?d', $cr[1]))
$this->formData['reputationCols'][] = [$cr[1], Util::localizedString($_, 'name')];
if ($cIds = DB::World()->selectCol('SELECT creature_id FROM creature_onkill_reputation WHERE (RewOnKillRepFaction1 = ?d AND RewOnKillRepValue1 '.$op.' 0) OR (RewOnKillRepFaction2 = ?d AND RewOnKillRepValue2 '.$op.' 0)', $cr[1], $cr[1]))
return ['id', $cIds];
else
return [0];
}
protected function cbFaction($cr)
{
if (!Util::checkNumeric($cr[1], NUM_REQ_INT))
return false;
if (!in_array($cr[1], $this->enums[$cr[0]]))
return false;
$facTpls = [];
$facs = new FactionList(array('OR', ['parentFactionId', $cr[1]], ['id', $cr[1]]));
foreach ($facs->iterate() as $__)
$facTpls = array_merge($facTpls, $facs->getField('templateIds'));
return $facTpls ? ['faction', $facTpls] : [0];
}
} }
?> ?>

View File

@@ -6,25 +6,14 @@ if (!defined('AOWOW_REVISION'))
class CurrencyList extends BaseType class CurrencyList extends BaseType
{ {
public static $type = Type::CURRENCY; public static $type = TYPE_CURRENCY;
public static $brickFile = 'currency'; public static $brickFile = 'currency';
public static $dataTable = '?_currencies';
protected $queryBase = 'SELECT c.*, c.id AS ARRAY_KEY FROM ?_currencies c';
protected $queryOpts = array(
'c' => [['ic']],
'ic' => ['j' => ['?_icons ic ON ic.id = c.iconId', true], 's' => ', ic.name AS iconString']
);
public function __construct($conditions = [])
{
parent::__construct($conditions);
foreach ($this->iterate() as &$_curTpl)
if (!$_curTpl['iconString'])
$_curTpl['iconString'] = 'inv_misc_questionmark';
}
protected $queryBase = 'SELECT *, c.id AS ARRAY_KEY FROM ?_currencies c';
protected $queryOpts = array(
'c' => [['ic']],
'ic' => ['j' => ['?_icons ic ON ic.id = c.iconId', true], 's' => ', ic.iconString']
);
public function getListviewData() public function getListviewData()
{ {
@@ -57,31 +46,13 @@ class CurrencyList extends BaseType
else else
$icon = [$this->curTpl['iconString'], $this->curTpl['iconString']]; $icon = [$this->curTpl['iconString'], $this->curTpl['iconString']];
$data[Type::CURRENCY][$this->id] = ['name' => $this->getField('name', true), 'icon' => $icon]; $data[TYPE_CURRENCY][$this->id] = ['name' => $this->getField('name', true), 'icon' => $icon];
} }
return $data; return $data;
} }
public function renderTooltip() public function renderTooltip() { }
{
if (!$this->curTpl)
return array();
$x = '<table><tr><td>';
$x .= '<b>'.$this->getField('name', true).'</b><br>';
// cata+ (or go fill it by hand)
if ($_ = $this->getField('description', true))
$x .= '<div style="max-width: 300px" class="q">'.$_.'</div>';
if ($_ = $this->getField('cap'))
$x .= '<br><span class="q">'.Lang::currency('cap').Lang::main('colon').'</span>'.Lang::nf($_).'<br>';
$x .= '</td></tr></table>';
return $x;
}
} }
?> ?>

View File

@@ -1,58 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class EmoteList extends BaseType
{
public static $type = Type::EMOTE;
public static $brickFile = 'emote';
public static $dataTable = '?_emotes';
protected $queryBase = 'SELECT e.*, e.id AS ARRAY_KEY FROM ?_emotes e';
public function __construct($conditions = [])
{
parent::__construct($conditions);
// post processing
foreach ($this->iterate() as &$curTpl)
{
// remap for generic access
$curTpl['name'] = $curTpl['cmd'];
}
}
public function getListviewData()
{
$data = [];
foreach ($this->iterate() as $__)
{
$data[$this->id] = array(
'id' => $this->curTpl['id'],
'name' => $this->curTpl['cmd'],
'preview' => $this->getField('self', true) ?: ($this->getField('noTarget', true) ?: $this->getField('target', true))
);
// [nyi] sounds
}
return $data;
}
public function getJSGlobals($addMask = GLOBALINFO_ANY)
{
$data = [];
foreach ($this->iterate() as $__)
$data[Type::EMOTE][$this->id] = ['name' => $this->getField('cmd')];
return $data;
}
public function renderTooltip() { }
}
?>

View File

@@ -1,347 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class EnchantmentList extends BaseType
{
use listviewHelper;
public static $type = Type::ENCHANTMENT;
public static $brickFile = 'enchantment';
public static $dataTable = '?_itemenchantment';
private $jsonStats = [];
private $relSpells = [];
private $triggerIds = [];
protected $queryBase = 'SELECT ie.*, ie.id AS ARRAY_KEY FROM ?_itemenchantment ie';
protected $queryOpts = array( // 502 => Type::ENCHANTMENT
'ie' => [['is']],
'is' => ['j' => ['?_item_stats `is` ON `is`.`type` = 502 AND `is`.`typeId` = `ie`.`id`', true], 's' => ', `is`.*'],
);
public function __construct($conditions = [])
{
parent::__construct($conditions);
// post processing
foreach ($this->iterate() as &$curTpl)
{
$curTpl['spells'] = []; // [spellId, triggerType, charges, chanceOrPpm]
for ($i = 1; $i <=3; $i++)
{
if ($curTpl['object'.$i] <= 0)
continue;
switch ($curTpl['type'.$i])
{
case 1:
$proc = -$this->getField('ppmRate') ?: ($this->getField('procChance') ?: $this->getField('amount'.$i));
$curTpl['spells'][$i] = [$curTpl['object'.$i], 2, $curTpl['charges'], $proc];
$this->relSpells[] = $curTpl['object'.$i];
break;
case 3:
$curTpl['spells'][$i] = [$curTpl['object'.$i], 1, $curTpl['charges'], 0];
$this->relSpells[] = $curTpl['object'.$i];
break;
case 7:
$curTpl['spells'][$i] = [$curTpl['object'.$i], 0, $curTpl['charges'], 0];
$this->relSpells[] = $curTpl['object'.$i];
break;
}
}
// floats are fetched as string from db :<
$curTpl['dmg'] = floatVal($curTpl['dmg']);
$curTpl['dps'] = floatVal($curTpl['dps']);
// remove zero-stats
foreach (Game::$itemMods as $str)
if ($curTpl[$str] == 0) // empty(0.0f) => true .. yeah, sure
unset($curTpl[$str]);
if ($curTpl['dps'] == 0)
unset($curTpl['dps']);
}
if ($this->relSpells)
$this->relSpells = new SpellList(array(['id', $this->relSpells]));
}
// use if you JUST need the name
public static function getName($id)
{
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_itemenchantment WHERE id = ?d', $id );
return Util::localizedString($n, 'name');
}
// end static use
public function getListviewData($addInfoMask = 0x0)
{
$data = [];
foreach ($this->iterate() as $__)
{
$data[$this->id] = array(
'id' => $this->id,
'name' => $this->getField('name', true),
'spells' => []
);
if ($this->curTpl['skillLine'] > 0)
$data[$this->id]['reqskill'] = $this->curTpl['skillLine'];
if ($this->curTpl['skillLevel'] > 0)
$data[$this->id]['reqskillrank'] = $this->curTpl['skillLevel'];
if ($this->curTpl['requiredLevel'] > 0)
$data[$this->id]['reqlevel'] = $this->curTpl['requiredLevel'];
foreach ($this->curTpl['spells'] as $s)
{
// enchant is procing or onUse
if ($s[1] == 2 || $s[1] == 0)
$data[$this->id]['spells'][$s[0]] = $s[2];
// spell is procing
else if ($this->relSpells && $this->relSpells->getEntry($s[0]) && ($_ = $this->relSpells->canTriggerSpell()))
{
foreach ($_ as $idx)
{
$this->triggerIds[] = $this->relSpells->getField('effect'.$idx.'TriggerSpell');
$data[$this->id]['spells'][$this->relSpells->getField('effect'.$idx.'TriggerSpell')] = $s[2];
}
}
}
if (!$data[$this->id]['spells'])
unset($data[$this->id]['spells']);
Util::arraySumByKey($data[$this->id], $this->getStatGain());
}
return $data;
}
public function getStatGain($addScalingKeys = false)
{
$data = [];
foreach (Game::$itemMods as $str)
if (isset($this->curTpl[$str]))
$data[$str] = $this->curTpl[$str];
if (isset($this->curTpl['dps']))
$data['dps'] = $this->curTpl['dps'];
// scaling enchantments are saved as 0 to item_stats, thus return empty
if ($addScalingKeys)
{
$spellStats = [];
if ($this->relSpells)
$spellStats = $this->relSpells->getStatGain();
for ($h = 1; $h <= 3; $h++)
{
$obj = (int)$this->curTpl['object'.$h];
switch ($this->curTpl['type'.$h])
{
case 3: // TYPE_EQUIP_SPELL Spells from ObjectX (use of amountX?)
if (!empty($spellStats[$obj]))
foreach ($spellStats[$obj] as $mod => $_)
if ($str = Game::$itemMods[$mod])
Util::arraySumByKey($data, [$str => 0]);
$obj = null;
break;
case 4: // TYPE_RESISTANCE +AmountX resistance for ObjectX School
switch ($obj)
{
case 0: // Physical
$obj = ITEM_MOD_ARMOR;
break;
case 1: // Holy
$obj = ITEM_MOD_HOLY_RESISTANCE;
break;
case 2: // Fire
$obj = ITEM_MOD_FIRE_RESISTANCE;
break;
case 3: // Nature
$obj = ITEM_MOD_NATURE_RESISTANCE;
break;
case 4: // Frost
$obj = ITEM_MOD_FROST_RESISTANCE;
break;
case 5: // Shadow
$obj = ITEM_MOD_SHADOW_RESISTANCE;
break;
case 6: // Arcane
$obj = ITEM_MOD_ARCANE_RESISTANCE;
break;
default:
$obj = null;
}
break;
case 5: // TYPE_STAT +AmountX for Statistic by type of ObjectX
if ($obj < 2) // [mana, health] are on [0, 1] respectively and are expected on [1, 2] ..
$obj++; // 0 is weaponDmg .. ehh .. i messed up somewhere
break; // stats are directly assigned below
default: // TYPE_NONE dnd stuff; skip assignment below
$obj = null;
}
if ($obj !== null)
if ($str = Game::$itemMods[$obj]) // check if we use these mods
Util::arraySumByKey($data, [$str => 0]);
}
}
return $data;
}
public function getRelSpell($id)
{
if ($this->relSpells)
return $this->relSpells->getEntry($id);
return null;
}
public function getJSGlobals($addMask = GLOBALINFO_ANY)
{
$data = [];
if ($addMask & GLOBALINFO_SELF)
foreach ($this->iterate() as $__)
$data[Type::ENCHANTMENT][$this->id] = ['name' => $this->getField('name', true)];
if ($addMask & GLOBALINFO_RELATED)
{
if ($this->relSpells)
$data = $this->relSpells->getJSGlobals(GLOBALINFO_SELF);
foreach ($this->triggerIds as $tId)
if (empty($data[Type::SPELL][$tId]))
$data[Type::SPELL][$tId] = $tId;
}
return $data;
}
public function renderTooltip() { }
}
class EnchantmentListFilter extends Filter
{
protected $enums = array(
3 => array( // requiresprof
null, 171, 164, 185, 333, 202, 129, 755, 165, 186, 197, true, false, 356, 182, 773
)
);
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
2 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
3 => [FILTER_CR_ENUM, 'skillLine' ], // requiresprof
4 => [FILTER_CR_NUMERIC, 'skillLevel', NUM_CAST_INT ], // reqskillrank
5 => [FILTER_CR_BOOLEAN, 'conditionId' ], // hascondition
10 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
11 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
12 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
20 => [FILTER_CR_NUMERIC, 'is.str', NUM_CAST_INT, true], // str
21 => [FILTER_CR_NUMERIC, 'is.agi', NUM_CAST_INT, true], // agi
22 => [FILTER_CR_NUMERIC, 'is.sta', NUM_CAST_INT, true], // sta
23 => [FILTER_CR_NUMERIC, 'is.int', NUM_CAST_INT, true], // int
24 => [FILTER_CR_NUMERIC, 'is.spi', NUM_CAST_INT, true], // spi
25 => [FILTER_CR_NUMERIC, 'is.arcres', NUM_CAST_INT, true], // arcres
26 => [FILTER_CR_NUMERIC, 'is.firres', NUM_CAST_INT, true], // firres
27 => [FILTER_CR_NUMERIC, 'is.natres', NUM_CAST_INT, true], // natres
28 => [FILTER_CR_NUMERIC, 'is.frores', NUM_CAST_INT, true], // frores
29 => [FILTER_CR_NUMERIC, 'is.shares', NUM_CAST_INT, true], // shares
30 => [FILTER_CR_NUMERIC, 'is.holres', NUM_CAST_INT, true], // holres
32 => [FILTER_CR_NUMERIC, 'is.dps', NUM_CAST_FLOAT, true], // dps
34 => [FILTER_CR_NUMERIC, 'is.dmg', NUM_CAST_FLOAT, true], // dmg
37 => [FILTER_CR_NUMERIC, 'is.mleatkpwr', NUM_CAST_INT, true], // mleatkpwr
38 => [FILTER_CR_NUMERIC, 'is.rgdatkpwr', NUM_CAST_INT, true], // rgdatkpwr
39 => [FILTER_CR_NUMERIC, 'is.rgdhitrtng', NUM_CAST_INT, true], // rgdhitrtng
40 => [FILTER_CR_NUMERIC, 'is.rgdcritstrkrtng', NUM_CAST_INT, true], // rgdcritstrkrtng
41 => [FILTER_CR_NUMERIC, 'is.armor' , NUM_CAST_INT, true], // armor
42 => [FILTER_CR_NUMERIC, 'is.defrtng', NUM_CAST_INT, true], // defrtng
43 => [FILTER_CR_NUMERIC, 'is.block', NUM_CAST_INT, true], // block
44 => [FILTER_CR_NUMERIC, 'is.blockrtng', NUM_CAST_INT, true], // blockrtng
45 => [FILTER_CR_NUMERIC, 'is.dodgertng', NUM_CAST_INT, true], // dodgertng
46 => [FILTER_CR_NUMERIC, 'is.parryrtng', NUM_CAST_INT, true], // parryrtng
48 => [FILTER_CR_NUMERIC, 'is.splhitrtng', NUM_CAST_INT, true], // splhitrtng
49 => [FILTER_CR_NUMERIC, 'is.splcritstrkrtng', NUM_CAST_INT, true], // splcritstrkrtng
50 => [FILTER_CR_NUMERIC, 'is.splheal', NUM_CAST_INT, true], // splheal
51 => [FILTER_CR_NUMERIC, 'is.spldmg', NUM_CAST_INT, true], // spldmg
52 => [FILTER_CR_NUMERIC, 'is.arcsplpwr', NUM_CAST_INT, true], // arcsplpwr
53 => [FILTER_CR_NUMERIC, 'is.firsplpwr', NUM_CAST_INT, true], // firsplpwr
54 => [FILTER_CR_NUMERIC, 'is.frosplpwr', NUM_CAST_INT, true], // frosplpwr
55 => [FILTER_CR_NUMERIC, 'is.holsplpwr', NUM_CAST_INT, true], // holsplpwr
56 => [FILTER_CR_NUMERIC, 'is.natsplpwr', NUM_CAST_INT, true], // natsplpwr
57 => [FILTER_CR_NUMERIC, 'is.shasplpwr', NUM_CAST_INT, true], // shasplpwr
60 => [FILTER_CR_NUMERIC, 'is.healthrgn', NUM_CAST_INT, true], // healthrgn
61 => [FILTER_CR_NUMERIC, 'is.manargn', NUM_CAST_INT, true], // manargn
77 => [FILTER_CR_NUMERIC, 'is.atkpwr', NUM_CAST_INT, true], // atkpwr
78 => [FILTER_CR_NUMERIC, 'is.mlehastertng', NUM_CAST_INT, true], // mlehastertng
79 => [FILTER_CR_NUMERIC, 'is.resirtng', NUM_CAST_INT, true], // resirtng
84 => [FILTER_CR_NUMERIC, 'is.mlecritstrkrtng', NUM_CAST_INT, true], // mlecritstrkrtng
94 => [FILTER_CR_NUMERIC, 'is.splpen', NUM_CAST_INT, true], // splpen
95 => [FILTER_CR_NUMERIC, 'is.mlehitrtng', NUM_CAST_INT, true], // mlehitrtng
96 => [FILTER_CR_NUMERIC, 'is.critstrkrtng', NUM_CAST_INT, true], // critstrkrtng
97 => [FILTER_CR_NUMERIC, 'is.feratkpwr', NUM_CAST_INT, true], // feratkpwr
101 => [FILTER_CR_NUMERIC, 'is.rgdhastertng', NUM_CAST_INT, true], // rgdhastertng
102 => [FILTER_CR_NUMERIC, 'is.splhastertng', NUM_CAST_INT, true], // splhastertng
103 => [FILTER_CR_NUMERIC, 'is.hastertng', NUM_CAST_INT, true], // hastertng
114 => [FILTER_CR_NUMERIC, 'is.armorpenrtng', NUM_CAST_INT, true], // armorpenrtng
115 => [FILTER_CR_NUMERIC, 'is.health', NUM_CAST_INT, true], // health
116 => [FILTER_CR_NUMERIC, 'is.mana', NUM_CAST_INT, true], // mana
117 => [FILTER_CR_NUMERIC, 'is.exprtng', NUM_CAST_INT, true], // exprtng
119 => [FILTER_CR_NUMERIC, 'is.hitrtng', NUM_CAST_INT, true], // hitrtng
123 => [FILTER_CR_NUMERIC, 'is.splpwr', NUM_CAST_INT, true] // splpwr
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_RANGE, [2, 123], true ], // criteria ids
'crs' => [FILTER_V_RANGE, [1, 15], true ], // criteria operators
'crv' => [FILTER_V_RANGE, [0, 99999], true ], // criteria values - only numerals
'na' => [FILTER_V_REGEX, '/[\p{C};%\\\\]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'ty' => [FILTER_V_RANGE, [1, 8], true ] // types
);
protected function createSQLForCriterium(&$cr)
{
if (in_array($cr[0], array_keys($this->genericFilter)))
if ($genCr = $this->genericCriterion($cr))
return $genCr;
unset($cr);
$this->error = true;
return [1];
}
protected function createSQLForValues()
{
$parts = [];
$_v = &$this->fiData['v'];
//string
if (isset($_v['na']))
if ($_ = $this->modularizeString(['name_loc'.User::$localeId]))
$parts[] = $_;
// type
if (isset($_v['ty']))
$parts[] = ['OR', ['type1', $_v['ty']], ['type2', $_v['ty']], ['type3', $_v['ty']]];
return $parts;
}
}
?>

View File

@@ -6,16 +6,15 @@ if (!defined('AOWOW_REVISION'))
class FactionList extends BaseType class FactionList extends BaseType
{ {
public static $type = Type::FACTION; public static $type = TYPE_FACTION;
public static $brickFile = 'faction'; public static $brickFile = 'faction';
public static $dataTable = '?_factions';
protected $queryBase = 'SELECT f.*, f.parentFactionId AS cat, f.id AS ARRAY_KEY FROM ?_factions f'; protected $queryBase = 'SELECT f.*, f.parentFactionId AS cat, f.id AS ARRAY_KEY FROM ?_factions f';
protected $queryOpts = array( protected $queryOpts = array(
'f' => [['f2']], 'f' => [['f2']],
'f2' => ['j' => ['?_factions f2 ON f.parentFactionId = f2.id', true], 's' => ', IFNULL(f2.parentFactionId, 0) AS cat2'], 'f2' => ['j' => ['?_factions f2 ON f.parentFactionId = f2.id', true], 's' => ', IFNULL(f2.parentFactionId, 0) AS cat2'],
'ft' => ['j' => '?_factiontemplate ft ON ft.factionId = f.id'] 'ft' => ['j' => '?_factiontemplate ft ON ft.factionId = f.id']
); );
public function __construct($conditions = []) public function __construct($conditions = [])
{ {
@@ -37,7 +36,7 @@ class FactionList extends BaseType
public static function getName($id) public static function getName($id)
{ {
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_factions WHERE id = ?d', $id); $n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_factions WHERE id = ?d', $id);
return Util::localizedString($n, 'name'); return Util::localizedString($n, 'name');
} }
@@ -75,7 +74,7 @@ class FactionList extends BaseType
$data = []; $data = [];
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
$data[Type::FACTION][$this->id] = ['name' => $this->getField('name', true)]; $data[TYPE_FACTION][$this->id] = ['name' => $this->getField('name', true)];
return $data; return $data;
} }

View File

@@ -8,12 +8,11 @@ class GameObjectList extends BaseType
{ {
use listviewHelper, spawnHelper; use listviewHelper, spawnHelper;
public static $type = Type::OBJECT; public static $type = TYPE_OBJECT;
public static $brickFile = 'object'; public static $brickFile = 'object';
public static $dataTable = '?_objects';
protected $queryBase = 'SELECT o.*, o.id AS ARRAY_KEY FROM ?_objects o'; protected $queryBase = 'SELECT o.*, o.id AS ARRAY_KEY FROM ?_objects o';
protected $queryOpts = array( protected $queryOpts = array(
'o' => [['ft', 'qse']], 'o' => [['ft', 'qse']],
'ft' => ['j' => ['?_factiontemplate ft ON ft.id = o.faction', true], 's' => ', ft.factionId, ft.A, ft.H'], 'ft' => ['j' => ['?_factiontemplate ft ON ft.id = o.faction', true], 's' => ', ft.factionId, ft.A, ft.H'],
'qse' => ['j' => ['?_quests_startend qse ON qse.type = 2 AND qse.typeId = o.id', true], 's' => ', IF(min(qse.method) = 1 OR max(qse.method) = 3, 1, 0) AS startsQuests, IF(min(qse.method) = 2 OR max(qse.method) = 3, 1, 0) AS endsQuests', 'g' => 'o.id'], 'qse' => ['j' => ['?_quests_startend qse ON qse.type = 2 AND qse.typeId = o.id', true], 's' => ', IF(min(qse.method) = 1 OR max(qse.method) = 3, 1, 0) AS startsQuests, IF(min(qse.method) = 2 OR max(qse.method) = 3, 1, 0) AS endsQuests', 'g' => 'o.id'],
@@ -31,9 +30,6 @@ class GameObjectList extends BaseType
// post processing // post processing
foreach ($this->iterate() as $_id => &$curTpl) foreach ($this->iterate() as $_id => &$curTpl)
{ {
if (!$curTpl['name_loc0'])
$curTpl['name_loc0'] = 'Unnamed Object #' . $_id;
// unpack miscInfo // unpack miscInfo
$curTpl['lootStack'] = []; $curTpl['lootStack'] = [];
$curTpl['spells'] = []; $curTpl['spells'] = [];
@@ -62,7 +58,7 @@ class GameObjectList extends BaseType
public static function getName($id) public static function getName($id)
{ {
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_objects WHERE id = ?d', $id); $n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_objects WHERE id = ?d', $id);
return Util::localizedString($n, 'name'); return Util::localizedString($n, 'name');
} }
@@ -96,14 +92,13 @@ class GameObjectList extends BaseType
$x = '<table>'; $x = '<table>';
$x .= '<tr><td><b class="q">'.$this->getField('name', true).'</b></td></tr>'; $x .= '<tr><td><b class="q">'.$this->getField('name', true).'</b></td></tr>';
if ($this->curTpl['typeCat']) if ($_ = Lang::gameObject('type', $this->curTpl['typeCat']))
if ($_ = Lang::gameObject('type', $this->curTpl['typeCat'])) $x .= '<tr><td>'.$_.'</td></tr>';
$x .= '<tr><td>'.$_.'</td></tr>';
if (isset($this->curTpl['lockId'])) if (isset($this->curTpl['lockId']))
if ($locks = Lang::getLocks($this->curTpl['lockId'])) if ($locks = Lang::getLocks($this->curTpl['lockId']))
foreach ($locks as $l) foreach ($locks as $l)
$x .= '<tr><td>'.sprintf(Lang::game('requires'), $l).'</td></tr>'; $x .= '<tr><td>'.$l.'</td></tr>';
$x .= '</table>'; $x .= '</table>';
@@ -115,7 +110,7 @@ class GameObjectList extends BaseType
$data = []; $data = [];
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
$data[Type::OBJECT][$this->id] = ['name' => $this->getField('name', true)]; $data[TYPE_OBJECT][$this->id] = ['name' => $this->getField('name', true)];
return $data; return $data;
} }
@@ -128,7 +123,7 @@ class GameObjectList extends BaseType
{ {
$data[$this->id] = array( $data[$this->id] = array(
'n' => $this->getField('name', true), 'n' => $this->getField('name', true),
't' => Type::OBJECT, 't' => TYPE_OBJECT,
'ti' => $this->id 'ti' => $this->id
// 'bd' => bossdrop // 'bd' => bossdrop
// 'dd' => dungeondifficulty // 'dd' => dungeondifficulty
@@ -143,48 +138,100 @@ class GameObjectList extends BaseType
class GameObjectListFilter extends Filter class GameObjectListFilter extends Filter
{ {
public $extraOpts = []; public $extraOpts = [];
protected $enums = array(
50 => array(
null, 1, 2, 3, 4,
663 => 663,
883 => 883,
FILTER_ENUM_ANY => true,
FILTER_ENUM_NONE => false
)
);
protected $genericFilter = array( protected $genericFilter = array(
1 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin 1 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin
2 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [side] 7 => [FILTER_CR_NUMERIC, 'reqSkill', null ], // requiredskilllevel
3 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [side] 11 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT], // hasscreenshots
4 => [FILTER_CR_CALLBACK, 'cbOpenable', null, null], // openable [yn] 13 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
5 => [FILTER_CR_NYI_PH, null, null ], // averagemoneycontained [op] [int] - GOs don't contain money, match against 0 15 => [FILTER_CR_NUMERIC, 'id', null ], // id
7 => [FILTER_CR_NUMERIC, 'reqSkill', NUM_CAST_INT ], // requiredskilllevel 18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
11 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
13 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
15 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT ], // id
16 => [FILTER_CR_CALLBACK, 'cbRelEvent', null, null], // relatedevent (ignore removed by event)
18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
50 => [FILTER_CR_ENUM, 'spellFocusId', null, ], // SpellFocus
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_LIST, [[1, 5], 7, 11, 13, 15, 16, 18, 50], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 5000]], true ], // criteria operators
'crv' => [FILTER_V_RANGE, [0, 99999], true ], // criteria values - only numeric input values expected
'na' => [FILTER_V_REGEX, '/[\p{C};%\\\\]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false] // match any / all filter
); );
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCR = $this->genericCriterion($cr)) if ($genCR = $this->genericCriterion($cr))
return $genCR; return $genCR;
unset($cr);
$this->error = true;
return [1];
}
switch ($cr[0])
{
case 4:
if (!$this->int2Bool($cr[1]))
break;
return $cr[1] ? ['OR', ['flags', 0x2, '&'], ['type', 3]] : ['AND', [['flags', 0x2, '&'], 0], ['type', 3, '!']];
case 5: // averagemoneycontained [op] [int] GOs don't contain money .. eval to 0 == true
if (!$this->isSaneNumeric($cr[2], false) || !$this->int2Op($cr[1]))
break;
return eval('return ('.$cr[2].' '.$cr[1].' 0)') ? [1] : [0];
case 2: // startsquest [side]
switch ($cr[1])
{
case 1: // any
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!']];
case 2: // alliance only
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']];
case 3: // horde only
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']];
case 4: // both
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
case 5: // none
$this->extraOpts['o']['h'][] = 'startsQuests = 0';
return [1];
}
break;
case 3: // endsquest [side]
switch ($cr[1])
{
case 1: // any
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!']];
case 2: // alliance only
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']];
case 3: // horde only
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']];
case 4: // both
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
case 5: // none todo: broken, if entry starts and ends quests...
$this->extraOpts['o']['h'][] = 'endsQuests = 0';
return [1];
}
break;
case 16; // relatedevent (ignore removed by event)
if (!$this->isSaneNumeric($cr[1]))
break;
if ($cr[1] == FILTER_ENUM_ANY)
{
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0');
$goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds);
return ['s.guid', $goGuids];
}
else if ($cr[1] == FILTER_ENUM_NONE)
{
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0');
$goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds);
return ['s.guid', $goGuids, '!'];
}
else if ($cr[1])
{
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId = ?d', $cr[1]);
$goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds);
return ['s.guid', $goGuids];
}
break;
}
unset($cr); unset($cr);
$this->error = true; $this->error = 1;
return [1]; return [1];
} }
@@ -200,61 +247,6 @@ class GameObjectListFilter extends Filter
return $parts; return $parts;
} }
protected function cbOpenable($cr)
{
if ($this->int2Bool($cr[1]))
return $cr[1] ? ['OR', ['flags', 0x2, '&'], ['type', 3]] : ['AND', [['flags', 0x2, '&'], 0], ['type', 3, '!']];
return false;
}
protected function cbQuestRelation($cr, $field, $value)
{
switch ($cr[1])
{
case 1: // any
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!']];
case 2: // alliance only
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']];
case 3: // horde only
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']];
case 4: // both
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
case 5: // none todo (low): broken, if entry starts and ends quests...
$this->extraOpts['o']['h'][] = $field.' = 0';
return [1];
}
return false;
}
protected function cbRelEvent($cr)
{
if (!Util::checkNumeric($cr[1], NUM_REQ_INT))
return false;;
if ($cr[1] == FILTER_ENUM_ANY)
{
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0');
$goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds);
return ['s.guid', $goGuids];
}
else if ($cr[1] == FILTER_ENUM_NONE)
{
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0');
$goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds);
return ['s.guid', $goGuids, '!'];
}
else if ($cr[1])
{
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId = ?d', $cr[1]);
$goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds);
return ['s.guid', $goGuids];
}
return false;
}
} }
?> ?>

View File

@@ -1,167 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GuideList extends BaseType
{
use ListviewHelper;
public const STATUS_COLORS = array(
GUIDE_STATUS_DRAFT => '#71D5FF',
GUIDE_STATUS_REVIEW => '#FFFF00',
GUIDE_STATUS_APPROVED => '#1EFF00',
GUIDE_STATUS_REJECTED => '#FF4040',
GUIDE_STATUS_ARCHIVED => '#FFD100'
);
public static $type = Type::GUIDE;
public static $brickFile = 'guide';
public static $dataTable = '?_guides';
private $article = [];
private $jsGlobals = [];
protected $queryBase = 'SELECT g.*, g.id AS ARRAY_KEY FROM ?_guides g';
protected $queryOpts = array(
'g' => [['a', 'c'], 'g' => 'g.`id`'],
'a' => ['j' => ['?_account a ON a.id = g.userId', true], 's' => ', IFNULL(a.displayName, "") AS author'],
'c' => ['j' => ['?_comments c ON c.`type` = '.Type::GUIDE.' AND c.`typeId` = g.`id` AND (c.`flags` & '.CC_FLAG_DELETED.') = 0', true], 's' => ', COUNT(c.`id`) AS `comments`']
);
public function __construct($conditions = [])
{
parent::__construct($conditions);
if ($this->error)
return;
$ratings = DB::Aowow()->select('SELECT `entry` AS ARRAY_KEY, IFNULL(SUM(`value`), 0) AS `t`, IFNULL(COUNT(*), 0) AS `n`, IFNULL(MAX(IF(`userId` = ?d, `value`, 0)), 0) AS `s` FROM ?_user_ratings WHERE `type` = ?d AND `entry` IN (?a)', User::$id, RATING_GUIDE, $this->getFoundIDs());
// post processing
foreach ($this->iterate() as $id => &$_curTpl)
{
if (isset($ratings[$id]))
{
$_curTpl['nvotes'] = $ratings[$id]['n'];
$_curTpl['rating'] = $ratings[$id]['n'] < 5 ? -1 : $ratings[$id]['t'] / $ratings[$id]['n'];
$_curTpl['_self'] = $ratings[$id]['s'];
}
else
{
$_curTpl['nvotes'] = 0;
$_curTpl['rating'] = -1;
}
}
}
public function getArticle(int $rev = -1) : string
{
if ($rev < -1)
$rev = -1;
if (empty($this->article[$rev]))
{
$a = DB::Aowow()->selectRow('SELECT `article`, `rev` FROM ?_articles WHERE ((`type` = ?d AND `typeId` = ?d){ OR `url` = ?}){ AND `rev`= ?d} ORDER BY `rev` DESC LIMIT 1',
Type::GUIDE, $this->id, $this->getField('url') ?: DBSIMPLE_SKIP, $rev < 0 ? DBSIMPLE_SKIP : $rev);
$this->article[$a['rev']] = $a['article'];
if ($this->article[$a['rev']])
{
(new Markup($this->article[$a['rev']]))->parseGlobalsFromText($this->jsGlobals);
return $this->article[$a['rev']];
}
else
trigger_error('GuideList::getArticle - linked article is missing');
}
return $this->article[$rev] ?? '';
}
public function getListviewData(bool $addDescription = false) : array
{
$data = [];
foreach ($this->iterate() as $__)
{
$data[$this->id] = array(
'id' => $this->id,
'category' => $this->getField('category'),
'title' => $this->getField('title'),
'description' => $this->getField('description'),
'sticky' => !!($this->getField('cuFlags') & CC_FLAG_STICKY),
'nvotes' => $this->getField('nvotes'),
'url' => '/?guide=' . ($this->getField('url') ?: $this->id),
'status' => $this->getField('status'),
'author' => $this->getField('author'),
'authorroles' => $this->getField('roles'),
'rating' => $this->getField('rating'),
'views' => $this->getField('views'),
'comments' => $this->getField('comments'),
// 'patch' => $this->getField(''), // 30305 - patch is pointless, use date instead
'date' => $this->getField('date'), // ok
'when' => date(Util::$dateFormatInternal, $this->getField('date'))
);
}
return $data;
}
public function userCanView() : bool
{
// is owner || is staff
return $this->getField('userId') == User::$id || User::isInGroup(U_GROUP_STAFF);
}
public function canBeViewed() : bool
{
// currently approved || has prev. approved version
return $this->getField('status') == GUIDE_STATUS_APPROVED || $this->getField('rev') > 0;
}
public function canBeReported() : bool
{
// not own guide && is not archived
return $this->getField('userId') != User::$id && $this->getField('status') != GUIDE_STATUS_ARCHIVED;
}
public function getJSGlobals($addMask = GLOBALINFO_ANY) : array
{
return $this->jsGlobals;
}
public function renderTooltip() : string
{
$specStr = '';
if ($this->getField('classId') && $this->getField('category') == 1)
{
$c = $this->getField('classId');
if (($s = $this->getField('specId')) > -1)
{
$i = Game::$specIconStrings[$c][$s];
$n = Lang::game('classSpecs', $c, $s);
}
else
{
$i = 'class_'.Game::$classFileStrings[$c];
$n = Lang::game('cl', $c);
}
$specStr = '&nbsp;&nbsp;&nbsp;&nbsp;<span class="icontiny c'.$c.'" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/'.$i.'.gif)">'.$n.'</span>';
}
$tt = '<table><tr><td><div style="max-width: 320px"><b class="q">'.$this->getField('title').'</b><br>';
$tt .= '<table width="100%"><tr><td>'.Lang::guide('guide').'</td><th>'.Lang::guide('byAuthor', [$this->getField('author')]).'</th></tr></table>';
$tt .= '<table width="100%"><tr><td>'.Lang::guide('category', $this->getField('category')).$specStr.'</td><th>'.Lang::guide('patch').' 3.3.5</th></tr></table>';
$tt .= '<div class="q" style="margin: 0.25em 0">'.$this->getField('description').'</div>';
$tt .= '</div></td></tr></table>';
return $tt;
}
}
?>

View File

@@ -1,307 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GuildList extends BaseType
{
use profilerHelper, listviewHelper;
public function getListviewData()
{
$this->getGuildScores();
$data = [];
foreach ($this->iterate() as $__)
{
$data[$this->id] = array(
'name' => '$"'.str_replace ('"', '', $this->curTpl['name']).'"', // MUST be a string, omit any quotes in name
'members' => $this->curTpl['members'],
'faction' => $this->curTpl['faction'],
'achievementpoints' => $this->getField('achievementpoints'),
'gearscore' => $this->getField('gearscore'),
'realm' => Profiler::urlize($this->curTpl['realmName'], true),
'realmname' => $this->curTpl['realmName'],
// 'battlegroup' => Profiler::urlize($this->curTpl['battlegroup']), // was renamed to subregion somewhere around cata release
// 'battlegroupname' => $this->curTpl['battlegroup'],
'region' => Profiler::urlize($this->curTpl['region'])
);
}
return array_values($data);
}
private function getGuildScores()
{
/*
Guild gear scores and achievement points are derived using a weighted average of all of the known characters in that guild.
Guilds with at least 25 level 80 players receive full benefit of the top 25 characters' gear scores, while guilds with at least 10 level 80 characters receive a slight penalty,
at least 1 level 80 a moderate penalty, and no level 80 characters a severe penalty. [...]
Instead of being based on level, achievement point averages are based around 1,500 points, but the same penalties apply.
*/
$guilds = array_column($this->templates, 'id');
if (!$guilds)
return;
$stats = DB::Aowow()->select('SELECT guild AS ARRAY_KEY, id AS ARRAY_KEY2, level, gearscore, achievementpoints, IF(cuFlags & ?d, 0, 1) AS synced FROM ?_profiler_profiles WHERE guild IN (?a) ORDER BY gearscore DESC', PROFILER_CU_NEEDS_RESYNC, $guilds);
foreach ($this->iterate() as &$_curTpl)
{
$id = $_curTpl['id'];
if (empty($stats[$id]))
continue;
$guildStats = array_filter($stats[$id], function ($x) { return $x['synced']; } );
if (!$guildStats)
continue;
$nMaxLevel = count(array_filter($stats[$id], function ($x) { return $x['level'] >= MAX_LEVEL; } ));
$levelMod = 1.0;
if ($nMaxLevel < 25)
$levelMod = 0.85;
if ($nMaxLevel < 10)
$levelMod = 0.66;
if ($nMaxLevel < 1)
$levelMod = 0.20;
$totalGS = $totalAP = $nMembers = 0;
foreach ($guildStats as $gs)
{
$totalGS += $gs['gearscore'] * $levelMod * min($gs['level'], MAX_LEVEL) / MAX_LEVEL;
$totalAP += $gs['achievementpoints'] * $levelMod * min($gs['achievementpoints'], 1500) / 1500;
$nMembers += min($gs['level'], MAX_LEVEL) / MAX_LEVEL;
}
$_curTpl['gearscore'] = intval($totalGS / $nMembers);
$_curTpl['achievementpoints'] = intval($totalAP / $nMembers);
}
}
public function renderTooltip() {}
public function getJSGlobals($addMask = 0) {}
}
class GuildListFilter extends Filter
{
public $extraOpts = [];
protected $genericFilter = [];
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'na' => [FILTER_V_REGEX, '/[\p{C};%\\\\]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'ex' => [FILTER_V_EQUAL, 'on', false], // only match exact
'si' => [FILTER_V_LIST, [1, 2], false], // side
'rg' => [FILTER_V_CALLBACK, 'cbRegionCheck', false], // region
'sv' => [FILTER_V_CALLBACK, 'cbServerCheck', false], // server
);
protected function createSQLForCriterium(&$cr) { }
protected function createSQLForValues()
{
$parts = [];
$_v = $this->fiData['v'];
// region (rg), battlegroup (bg) and server (sv) are passed to GuildList as miscData and handled there
// name [str]
if (!empty($_v['na']))
if ($_ = $this->modularizeString(['g.name'], $_v['na'], !empty($_v['ex']) && $_v['ex'] == 'on'))
$parts[] = $_;
// side [list]
if (!empty($_v['si']))
{
if ($_v['si'] == 1)
$parts[] = ['c.race', [1, 3, 4, 7, 11]];
else if ($_v['si'] == 2)
$parts[] = ['c.race', [2, 5, 6, 8, 10]];
}
return $parts;
}
protected function cbRegionCheck(&$v)
{
if (in_array($v, Util::$regions))
{
$this->parentCats[0] = $v; // directly redirect onto this region
$v = ''; // remove from filter
return true;
}
return false;
}
protected function cbServerCheck(&$v)
{
foreach (Profiler::getRealms() as $realm)
if ($realm['name'] == $v)
{
$this->parentCats[1] = Profiler::urlize($v);// directly redirect onto this server
$v = ''; // remove from filter
return true;
}
return false;
}
}
class RemoteGuildList extends GuildList
{
protected $queryBase = 'SELECT `g`.*, `g`.`guildid` AS ARRAY_KEY FROM guild g';
protected $queryOpts = array(
'g' => [['gm', 'c'], 'g' => 'ARRAY_KEY'],
'gm' => ['j' => 'guild_member gm ON gm.guildid = g.guildid', 's' => ', COUNT(1) AS members'],
'c' => ['j' => 'characters c ON c.guid = gm.guid', 's' => ', BIT_OR(IF(c.race IN (1, 3, 4, 7, 11), 1, 2)) - 1 AS faction']
);
public function __construct($conditions = [], $miscData = null)
{
// select DB by realm
if (!$this->selectRealms($miscData))
{
trigger_error('no access to auth-db or table realmlist is empty', E_USER_WARNING);
return;
}
parent::__construct($conditions, $miscData);
if ($this->error)
return;
reset($this->dbNames); // only use when querying single realm
$realmId = key($this->dbNames);
$realms = Profiler::getRealms();
$distrib = [];
// post processing
foreach ($this->iterate() as $guid => &$curTpl)
{
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
$r = explode(':', $guid)[0];
if (!empty($realms[$r]))
{
$curTpl['realm'] = $r;
$curTpl['realmName'] = $realms[$r]['name'];
$curTpl['region'] = $realms[$r]['region'];
}
else
{
trigger_error('character "'.$curTpl['name'].'" belongs to nonexistant realm #'.$r, E_USER_WARNING);
unset($this->templates[$guid]);
continue;
}
// equalize distribution
if (empty($distrib[$curTpl['realm']]))
$distrib[$curTpl['realm']] = 1;
else
$distrib[$curTpl['realm']]++;
}
$limit = CFG_SQL_LIMIT_DEFAULT;
foreach ($conditions as $c)
if (is_int($c))
$limit = $c;
$total = array_sum($distrib);
foreach ($distrib as &$d)
$d = ceil($limit * $d / $total);
foreach ($this->iterate() as $guid => &$curTpl)
{
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
{
unset($this->templates[$guid]);
continue;
}
$distrib[$curTpl['realm']]--;
$limit--;
}
}
public function initializeLocalEntries()
{
$data = [];
foreach ($this->iterate() as $guid => $__)
{
$data[$guid] = array(
'realm' => $this->getField('realm'),
'realmGUID' => $this->getField('guildid'),
'name' => $this->getField('name'),
'nameUrl' => Profiler::urlize($this->getField('name')),
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
}
// basic guild data
foreach (Util::createSqlBatchInsert($data) as $ins)
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_guild (?#) VALUES '.$ins, array_keys(reset($data)));
// merge back local ids
$localIds = DB::Aowow()->selectCol(
'SELECT CONCAT(realm, ":", realmGUID) AS ARRAY_KEY, id FROM ?_profiler_guild WHERE realm IN (?a) AND realmGUID IN (?a)',
array_column($data, 'realm'),
array_column($data, 'realmGUID')
);
foreach ($this->iterate() as $guid => &$_curTpl)
if (isset($localIds[$guid]))
$_curTpl['id'] = $localIds[$guid];
}
}
class LocalGuildList extends GuildList
{
protected $queryBase = 'SELECT g.*, g.id AS ARRAY_KEY FROM ?_profiler_guild g';
public function __construct($conditions = [], $miscData = null)
{
parent::__construct($conditions, $miscData);
if ($this->error)
return;
$realms = Profiler::getRealms();
foreach ($this->iterate() as $id => &$curTpl)
{
if ($curTpl['realm'] && !isset($realms[$curTpl['realm']]))
continue;
if (isset($realms[$curTpl['realm']]))
{
$curTpl['realmName'] = $realms[$curTpl['realm']]['name'];
$curTpl['region'] = $realms[$curTpl['realm']]['region'];
}
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
}
}
public function getProfileUrl()
{
$url = '?guild=';
return $url.implode('.', array(
Profiler::urlize($this->getField('region')),
Profiler::urlize($this->getField('realmName')),
Profiler::urlize($this->getField('name'))
));
}
}
?>

View File

@@ -1,237 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class IconList extends BaseType
{
use listviewHelper;
public static $type = Type::ICON;
public static $brickFile = 'icon';
public static $dataTable = '?_icons';
public static $contribute = CONTRIBUTE_CO;
private $pseudoQry = 'SELECT iconId AS ARRAY_KEY, COUNT(*) FROM ?# WHERE iconId IN (?a) GROUP BY iconId';
private $pseudoJoin = array(
'nItems' => '?_items',
'nSpells' => '?_spell',
'nAchievements' => '?_achievement',
'nCurrencies' => '?_currencies',
'nPets' => '?_pet'
);
protected $queryBase = 'SELECT ic.*, ic.id AS ARRAY_KEY FROM ?_icons ic';
/* this works, but takes ~100x more time than i'm comfortable with .. kept as reference
protected $queryOpts = array( // 29 => Type::ICON
'ic' => [['s', 'i', 'a', 'c', 'p'], 'g' => 'ic.id'],
'i' => ['j' => ['?_items `i` ON `i`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `i`.`id`) AS nItems'],
's' => ['j' => ['?_spell `s` ON `s`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `s`.`id`) AS nSpells'],
'a' => ['j' => ['?_achievement `a` ON `a`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `a`.`id`) AS nAchievements'],
'c' => ['j' => ['?_currencies `c` ON `c`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `c`.`id`) AS nCurrencies'],
'p' => ['j' => ['?_pet `p` ON `p`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `p`.`id`) AS nPets']
);
*/
public function __construct($conditions)
{
parent::__construct($conditions);
if (!$this->getFoundIDs())
return;
foreach ($this->pseudoJoin as $var => $tbl)
{
$res = DB::Aowow()->selectCol($this->pseudoQry, $tbl, $this->getFoundIDs());
foreach ($res as $icon => $qty)
$this->templates[$icon][$var] = $qty;
}
}
// use if you JUST need the name
public static function getName($id)
{
$n = DB::Aowow()->SelectRow('SELECT name FROM ?_icons WHERE id = ?d', $id );
return Util::localizedString($n, 'name');
}
// end static use
public function getListviewData($addInfoMask = 0x0)
{
$data = [];
foreach ($this->iterate() as $__)
{
$data[$this->id] = array(
'id' => $this->id,
'name' => $this->getField('name', true, true),
'icon' => $this->getField('name', true, true),
'itemcount' => (int)$this->getField('nItems'),
'spellcount' => (int)$this->getField('nSpells'),
'achievementcount' => (int)$this->getField('nAchievements'),
'npccount' => 0, // UNUSED
'petabilitycount' => 0, // UNUSED
'currencycount' => (int)$this->getField('nCurrencies'),
'missionabilitycount' => 0, // UNUSED
'buildingcount' => 0, // UNUSED
'petcount' => (int)$this->getField('nPets'),
'threatcount' => 0, // UNUSED
'classcount' => 0 // class icons are hardcoded and not referenced in dbc
);
}
return $data;
}
public function getJSGlobals($addMask = GLOBALINFO_ANY)
{
$data = [];
foreach ($this->iterate() as $__)
$data[Type::ICON][$this->id] = ['name' => $this->getField('name', true, true), 'icon' => $this->getField('name', true, true)];
return $data;
}
public function renderTooltip() { }
}
class IconListFilter extends Filter
{
public $extraOpts = null;
// cr => [type, field, misc, extraCol]
private $criterion2field = array(
1 => '?_items', // items [num]
2 => '?_spell', // spells [num]
3 => '?_achievement', // achievements [num]
// 4 => '', // battlepets [num]
// 5 => '', // battlepetabilities [num]
6 => '?_currencies', // currencies [num]
// 7 => '', // garrisonabilities [num]
// 8 => '', // garrisonbuildings [num]
9 => '?_pet', // hunterpets [num]
// 10 => '', // garrisonmissionthreats [num]
11 => '', // classes [num]
13 => '' // used [num]
);
private $totalUses = [];
protected $genericFilter = array(
1 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // items [num]
2 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // spells [num]
3 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // achievements [num]
6 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // currencies [num]
9 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // hunterpets [num]
11 => [FILTER_CR_NYI_PH, null, null], // classes [num]
13 => [FILTER_CR_CALLBACK, 'cbUseAll' ] // used [num]
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_LIST, [1, 2, 3, 6, 9, 11, 13], true ], // criteria ids
'crs' => [FILTER_V_RANGE, [1, 6], true ], // criteria operators
'crv' => [FILTER_V_RANGE, [0, 99999], true ], // criteria values - all criteria are numeric here
'na' => [FILTER_V_REGEX, '/[\p{C};%\\\\]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false] // match any / all filter
);
private function _getCnd($op, $val, $tbl)
{
switch ($op)
{
case '>':
case '>=':
case '=':
$ids = DB::Aowow()->selectCol('SELECT iconId AS ARRAY_KEY, COUNT(*) AS n FROM ?# GROUP BY iconId HAVING n '.$op.' '.$val, $tbl);
return $ids ? ['id', array_keys($ids)] : [1];
case '<=':
if ($val)
$op = '>';
break;
case '<':
if ($val)
$op = '>=';
break;
case '!=':
if ($val)
$op = '=';
break;
}
$ids = DB::Aowow()->selectCol('SELECT iconId AS ARRAY_KEY, COUNT(*) AS n FROM ?# GROUP BY iconId HAVING n '.$op.' '.$val, $tbl);
return $ids ? ['id', array_keys($ids), '!'] : [1];
}
protected function createSQLForCriterium(&$cr)
{
if (in_array($cr[0], array_keys($this->genericFilter)))
if ($genCr = $this->genericCriterion($cr))
return $genCr;
unset($cr);
$this->error = true;
return [1];
}
protected function createSQLForValues()
{
$parts = [];
$_v = &$this->fiData['v'];
//string
if (isset($_v['na']))
if ($_ = $this->modularizeString(['name']))
$parts[] = $_;
return $parts;
}
protected function cbUseAny($cr, $value)
{
if (Util::checkNumeric($cr[2], NUM_CAST_INT) && $this->int2Op($cr[1]))
return $this->_getCnd($cr[1], $cr[2], $this->criterion2field[$cr[0]]);
return false;
}
protected function cbUseAll($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
if (!$this->totalUses)
{
foreach ($this->criterion2field as $tbl)
{
if (!$tbl)
continue;
$res = DB::Aowow()->selectCol('SELECT iconId AS ARRAY_KEY, COUNT(*) AS n FROM ?# GROUP BY iconId', $tbl);
Util::arraySumByKey($this->totalUses, $res);
}
}
if ($cr[1] == '=')
$cr[1] = '==';
$op = $cr[1];
if ($cr[1] == '<=' && $cr[2])
$op = '>';
else if ($cr[1] == '<' && $cr[2])
$op = '>=';
else if ($cr[1] == '!=' && $cr[2])
$op = '==';
$ids = array_filter($this->totalUses, function ($x) use ($op, $cr) { return eval('return '.$x.' '.$op.' '.$cr[2].';'); });
if ($cr[1] != $op)
return $ids ? ['id', array_keys($ids), '!'] : [1];
else
return $ids ? ['id', array_keys($ids)] : ['id', array_keys($this->totalUses), '!'];
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -8,18 +8,14 @@ class ItemsetList extends BaseType
{ {
use ListviewHelper; use ListviewHelper;
public static $type = Type::ITEMSET; public static $type = TYPE_ITEMSET;
public static $brickFile = 'itemset'; public static $brickFile = 'itemset';
public static $dataTable = '?_itemset';
public $pieceToSet = []; // used to build g_items and search public $pieceToSet = []; // used to build g_items and search
private $classes = []; // used to build g_classes private $classes = []; // used to build g_classes
protected $queryBase = 'SELECT `set`.*, `set`.id AS ARRAY_KEY FROM ?_itemset `set`'; protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_itemset `set`';
protected $queryOpts = array( protected $queryOpts = ['set' => ['o' => 'maxlevel DESC']];
'set' => ['o' => 'maxlevel DESC'],
'e' => ['j' => ['?_events e ON e.id = `set`.eventId', true], 's' => ', e.holidayId']
);
public function __construct($conditions = []) public function __construct($conditions = [])
{ {
@@ -80,91 +76,19 @@ class ItemsetList extends BaseType
$data = []; $data = [];
if ($this->classes && ($addMask & GLOBALINFO_RELATED)) if ($this->classes && ($addMask & GLOBALINFO_RELATED))
$data[Type::CHR_CLASS] = array_combine($this->classes, $this->classes); $data[TYPE_CLASS] = array_combine($this->classes, $this->classes);
if ($this->pieceToSet && ($addMask & GLOBALINFO_SELF)) if ($this->pieceToSet && ($addMask & GLOBALINFO_SELF))
$data[Type::ITEM] = array_combine(array_keys($this->pieceToSet), array_keys($this->pieceToSet)); $data[TYPE_ITEM] = array_combine(array_keys($this->pieceToSet), array_keys($this->pieceToSet));
if ($addMask & GLOBALINFO_SELF) if ($addMask & GLOBALINFO_SELF)
foreach ($this->iterate() as $id => $__) foreach ($this->iterate() as $id => $__)
$data[Type::ITEMSET][$id] = ['name' => $this->getField('name', true)]; $data[TYPE_ITEMSET][$id] = ['name' => $this->getField('name', true)];
return $data; return $data;
} }
public function renderTooltip() public function renderTooltip() { }
{
if (!$this->curTpl)
return array();
$x = '<table><tr><td>';
$x .= '<span class="q'.$this->getField('quality').'">'.$this->getField('name', true).'</span><br />';
$nCl = 0;
if ($_ = $this->getField('classMask'))
{
$jsg = [];
$cl = Lang::getClassString($_, $jsg);
$nCl = count($jsg);
$x .= Util::ucFirst($nCl > 1 ? Lang::game('classes') : Lang::game('class')).Lang::main('colon').$cl.'<br />';
}
if ($_ = $this->getField('contentGroup'))
$x .= Lang::itemset('notes', $_).($this->getField('heroic') ? ' <i class="q2">('.Lang::item('heroic').')</i>' : '').'<br />';
if (!$nCl || !$this->getField('contentGroup'))
$x.= Lang::itemset('types', $this->getField('type')).'<br />';
if ($bonuses = $this->getBonuses())
{
$x .= '<span>';
foreach ($bonuses as $b)
$x .= '<br /><span class="q13">'.$b['bonus'].' '.Lang::itemset('_pieces').Lang::main('colon').'</span>'.$b['desc'];
$x .= '</span>';
}
$x .= '</td></tr></table>';
return $x;
}
public function getBonuses()
{
$spells = [];
for ($i = 1; $i < 9; $i++)
{
$spl = $this->getField('spell'.$i);
$qty = $this->getField('bonus'.$i);
// cant use spell as index, would change order
if ($spl && $qty)
$spells[] = ['id' => $spl, 'bonus' => $qty];
}
// sort by required pieces ASC
usort($spells, function($a, $b) {
if ($a['bonus'] == $b['bonus'])
return 0;
return ($a['bonus'] > $b['bonus']) ? 1 : -1;
});
$setSpells = new SpellList(array(['s.id', array_column($spells, 'id')]));
foreach ($setSpells->iterate() as $spellId => $__)
{
foreach ($spells as &$s)
{
if ($spellId != $s['id'])
continue;
$s['desc'] = $setSpells->parseText('description', $this->getField('reqLevel') ?: MAX_LEVEL)[0];
}
}
return $spells;
}
} }
@@ -173,40 +97,34 @@ class ItemsetListFilter extends Filter
{ {
// cr => [type, field, misc, extraCol] // cr => [type, field, misc, extraCol]
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
2 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id 2 => [FILTER_CR_NUMERIC, 'id', null, true], // id
3 => [FILTER_CR_NUMERIC, 'npieces', NUM_CAST_INT ], // pieces 3 => [FILTER_CR_NUMERIC, 'npieces', ], // pieces
4 => [FILTER_CR_STRING, 'bonusText', STR_LOCALIZED ], // bonustext 4 => [FILTER_CR_STRING, 'bonusText', true ], // bonustext
5 => [FILTER_CR_BOOLEAN, 'heroic', ], // heroic 5 => [FILTER_CR_BOOLEAN, 'heroic', ], // heroic
6 => [FILTER_CR_ENUM, 'e.holidayId', ], // relatedevent 6 => [FILTER_CR_ENUM, 'holidayId', ], // relatedevent
8 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments 8 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
9 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots 9 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
10 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos 10 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
12 => [FILTER_CR_NYI_PH, null, 1 ] // available to players [yn] - ugh .. scan loot, quest and vendor templates and write to ?_itemset
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_RANGE, [2, 12], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 424]], true ], // criteria operators
'crv' => [FILTER_V_REGEX, '/[\p{C};:%\\\\]/ui', true ], // criteria values - only printable chars, no delimiters
'na' => [FILTER_V_REGEX, '/[\p{C};%\\\\]/ui', false], // name / description - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'qu' => [FILTER_V_RANGE, [0, 7], true ], // quality
'ty' => [FILTER_V_RANGE, [1, 12], true ], // set type
'minle' => [FILTER_V_RANGE, [1, 999], false], // min item level
'maxle' => [FILTER_V_RANGE, [1, 999], false], // max itemlevel
'minrl' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // min required level
'maxrl' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // max required level
'cl' => [FILTER_V_LIST, [[1, 9], 11], false], // class
'ta' => [FILTER_V_RANGE, [1, 30], false] // tag / content group
); );
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCR = $this->genericCriterion($cr)) if ($genCR = $this->genericCriterion($cr))
return $genCR; return $genCR;
unset($cr);
$this->error = true;
return [1];
}
switch ($cr[0])
{
case 12: // available to players [yn] ugh .. scan loot, quest and vendor templates and write to ?_itemset
/* todo */ return [1];
}
unset($cr); unset($cr);
$this->error = true; $this->error = true;
return [1]; return [1];
@@ -224,35 +142,65 @@ class ItemsetListFilter extends Filter
// quality [enum] // quality [enum]
if (isset($_v['qu'])) if (isset($_v['qu']))
$parts[] = ['quality', $_v['qu']]; $parts[] = ['quality', (array)$_v['qu']];
// type [enum] // type [enum]
if (isset($_v['ty'])) if (isset($_v['ty']))
$parts[] = ['type', $_v['ty']]; $parts[] = ['type', (array)$_v['ty']];
// itemLevel min [int] // itemLevel min [int]
if (isset($_v['minle'])) if (isset($_v['minle']))
$parts[] = ['minLevel', $_v['minle'], '>=']; {
if (is_int($_v['minle']) && $_v['minle'] > 0)
$parts[] = ['minLevel', $_v['minle'], '>='];
else
unset($_v['minle']);
}
// itemLevel max [int] // itemLevel max [int]
if (isset($_v['maxle'])) if (isset($_v['maxle']))
$parts[] = ['maxLevel', $_v['maxle'], '<=']; {
if (is_int($_v['maxle']) && $_v['maxle'] > 0)
$parts[] = ['maxLevel', $_v['maxle'], '<='];
else
unset($_v['maxle']);
}
// reqLevel min [int] // reqLevel min [int]
if (isset($_v['minrl'])) if (isset($_v['minrl']))
$parts[] = ['reqLevel', $_v['minrl'], '>=']; {
if (is_int($_v['minrl']) && $_v['minrl'] > 0)
$parts[] = ['reqLevel', $_v['minrl'], '>='];
else
unset($_v['minrl']);
}
// reqLevel max [int] // reqLevel max [int]
if (isset($_v['maxrl'])) if (isset($_v['maxrl']))
$parts[] = ['reqLevel', $_v['maxrl'], '<=']; {
if (is_int($_v['maxrl']) && $_v['maxrl'] > 0)
$parts[] = ['reqLevel', $_v['maxrl'], '<='];
else
unset($_v['maxrl']);
}
// class [enum] // class [enum]
if (isset($_v['cl'])) if (isset($_v['cl']))
$parts[] = ['classMask', $this->list2Mask([$_v['cl']]), '&']; {
if (in_array($_v['cl'], [1, 2, 3, 4, 5, 6, 7, 8, 9, 11]))
$parts[] = ['classMask', $this->list2Mask($_v['cl']), '&'];
else
unset($_v['cl']);
}
// tag [enum] // tag [enum]
if (isset($_v['ta'])) if (isset($_v['ta']))
$parts[] = ['contentGroup', intVal($_v['ta'])]; {
if ($_v['ta'] > 0 && $_v['ta'] < 31)
$parts[] = ['contentGroup', intVal($_v['ta'])];
else
unset($_v['ta']);
}
return $parts; return $parts;
} }

View File

@@ -1,74 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class MailList extends BaseType
{
public static $type = Type::MAIL;
public static $brickFile = 'mail';
public static $dataTable = '?_mails';
protected $queryBase = 'SELECT m.*, m.id AS ARRAY_KEY FROM ?_mails m';
protected $queryOpts = [];
public function __construct($conditions = [])
{
parent::__construct($conditions);
if ($this->error)
return;
// post processing
foreach ($this->iterate() as $_id => &$_curTpl)
{
$_curTpl['name'] = Util::localizedString($_curTpl, 'subject', true);
if (!$_curTpl['name'])
{
$_curTpl['name'] = sprintf(Lang::mail('untitled'), $_id);
$_curTpl['subject_loc0'] = $_curTpl['name'];
}
}
}
public static function getName($id)
{
$n = DB::Aowow()->SelectRow('SELECT subject_loc0, subject_loc2, subject_loc3, subject_loc4, subject_loc6, subject_loc8 FROM ?_mails WHERE id = ?d', $id);
return Util::localizedString($n, 'subject');
}
public function getListviewData()
{
$data = [];
foreach ($this->iterate() as $__)
{
$body = str_replace('[br]', ' ', Util::parseHtmlText($this->getField('text', true), true));
$data[$this->id] = array(
'id' => $this->id,
'subject' => $this->getField('subject', true),
'body' => Lang::trimTextClean($body),
'attachments' => [$this->getField('attachment')]
);
}
return $data;
}
public function getJSGlobals($addMask = 0)
{
$data = [];
foreach ($this->iterate() as $__)
if ($a = $this->curTpl['attachment'])
$data[Type::ITEM][$a] = $a;
return $data;
}
public function renderTooltip() { }
}
?>

View File

@@ -8,15 +8,10 @@ class PetList extends BaseType
{ {
use ListviewHelper; use ListviewHelper;
public static $type = Type::PET; public static $type = TYPE_PET;
public static $brickFile = 'pet'; public static $brickFile = 'pet';
public static $dataTable = '?_pet';
protected $queryBase = 'SELECT p.*, p.id AS ARRAY_KEY FROM ?_pet p'; protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_pet p';
protected $queryOpts = array(
'p' => [['ic']],
'ic' => ['j' => ['?_icons ic ON p.iconId = ic.id', true], 's' => ', ic.name AS iconString'],
);
public function getListviewData() public function getListviewData()
{ {
@@ -59,10 +54,10 @@ class PetList extends BaseType
if ($addMask & GLOBALINFO_RELATED) if ($addMask & GLOBALINFO_RELATED)
for ($i = 1; $i <= 4; $i++) for ($i = 1; $i <= 4; $i++)
if ($this->curTpl['spellId'.$i] > 0) if ($this->curTpl['spellId'.$i] > 0)
$data[Type::SPELL][$this->curTpl['spellId'.$i]] = $this->curTpl['spellId'.$i]; $data[TYPE_SPELL][$this->curTpl['spellId'.$i]] = $this->curTpl['spellId'.$i];
if ($addMask & GLOBALINFO_SELF) if ($addMask & GLOBALINFO_SELF)
$data[Type::PET][$this->id] = ['icon' => $this->curTpl['iconString']]; $data[TYPE_PET][$this->id] = ['icon' => $this->curTpl['iconString']];
} }
return $data; return $data;

View File

@@ -4,329 +4,210 @@ if (!defined('AOWOW_REVISION'))
die('illegal access'); die('illegal access');
// class CharacterList extends BaseType // new profiler-related parent: ProfilerType?; maybe a trait is enough => use ProfileHelper;
// class GuildList extends BaseType
// class ArenaTeamList extends BaseType
class ProfileList extends BaseType class ProfileList extends BaseType
{ {
use profilerHelper, listviewHelper; public static $type = 0; // profiles dont actually have one
public static $brickFile = 'profile';
public function getListviewData($addInfo = 0, array $reqCols = []) protected $queryBase = ''; // SELECT p.*, p.id AS ARRAY_KEY FROM ?_profiles p';
protected $queryOpts = array(
'p' => [['pa', 'pg']],
'pam' => [['?_profiles_arenateam_member pam ON pam.memberId = p.id', true], 's' => ', pam.status'],
'pa' => ['?_profiles_arenateam pa ON pa.id = pam.teamId', 's' => ', pa.mode, pa.name'],
'pgm' => [['?_profiles_guid_member pgm ON pgm.memberId = p.Id', true], 's' => ', pgm.rankId'],
'pg' => ['?_profiles_guild pg ON pg.if = pgm.guildId', 's' => ', pg.name']
);
public function __construct($conditions = [], $miscData = null)
{
$character = array(
'id' => 2,
'name' => 'CharName',
'region' => ['eu', 'Europe'],
'battlegroup' => ['pure-pwnage', 'Pure Pwnage'],
'realm' => ['dafuque', 'da\'Fuqúe'],
'level' => 80,
'classs' => 11,
'race' => 6,
'faction' => 1, // 0:alliance; 1:horde
'gender' => 1, // 0:male, 1:female
'skincolor' => 0, // playerbytes % 256
'hairstyle' => 0, // (playerbytes >> 16) % 256
'haircolor' => 0, // (playerbytes >> 24) % 256
'facetype' => 0, // (playerbytes >> 8) % 256 [maybe features]
'features' => 0, // playerBytes2 % 256 [maybe facetype]
'source' => 2, // source: used if you create a profile from a genuine character. It inherites region, realm and bGroup
'sourcename' => 'SourceCharName', // > if these three are false we get a 'genuine' profile [0 for genuine characters..?]
'user' => 1, // > 'genuine' is the parameter for _isArmoryProfile(allowCustoms) ['' for genuine characters..?]
'username' => 'TestUser', // > also, if 'source' <> 0, the char-icon is requestet via profile.php?avatar
'published' => 1, // public / private
'pinned' => 1, // usable for some utility funcs on site
'nomodel' => 0x0, // unchecks DisplayOnCharacter by (1 << slotId - 1)
'title' => 0, // titleId currently in use or null
'guild' => 'GuildName', // only on chars; id or null
'description' => 'this is a profile', // only on custom profiles
'arenateams' => [], // [size(2|3|5) => DisplayName]; DisplayName gets urlized to use as link
'playedtime' => 0, // exact to the day
'lastupdated' => 0, // timestamp in ms
'achievementpoints' => 0, // max you have
'talents' => array(
'builds' => array(
['talents' => '', 'glyphs' => ''], // talents:string of 0-5 points; glyphs: itemIds.join(':')
),
'active' => 1 // 1|2
),
'customs' => [], // custom profiles created from this char; profileId => [name, ownerId, iconString(optional)]
'skills' => [], // skillId => [curVal, maxVal]; can contain anything, should be limited to prim/sec professions
'inventory' => [], // slotId => [itemId, subItemId, permEnchantId, tempEnchantId, gemItemId1, gemItemId2, gemItemId3, gemItemId4]
'auras' => [], // custom list of buffs, debuffs [spellId]
// completion lists: [subjectId => amount/timestamp/1]
'reputation' => [], // factionId => amount
'titles' => [], // titleId => 1
'spells' => [], // spellId => 1; recipes, pets, mounts
'achievements' => [], // achievementId => timestamp
'quests' => [], // questId => 1
// UNKNOWN
'bookmarks' => [2], // UNK pinned or claimed userId => profileIds..?
'statistics' => [], // UNK all statistics? [achievementId => killCount]
'activity' => [], // UNK recent achievements? [achievementId => killCount]
'glyphs' => [], // not really used .. i guess..?
'pets' => array( // UNK
[], // one array per pet, structure UNK
),
);
// parent::__construct($conditions, $miscData);
@include('datasets/ProfilerExampleChar'); // tmp char data
$this->templates[2] = $character;
$this->curTpl = $character;
if ($this->error)
return;
// post processing
// foreach ($this->iterate() as $_id => &$curTpl)
// {
// }
}
public function getListviewData()
{ {
$data = []; $data = [];
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
{ {
if (!$this->isVisibleToUser()) $tDistrib = $this->getTalentDistribution();
continue;
if (($addInfo & PROFILEINFO_PROFILE) && !$this->isCustom())
continue;
if (($addInfo & PROFILEINFO_CHARACTER) && $this->isCustom())
continue;
$data[$this->id] = array( $data[$this->id] = array(
'id' => $this->getField('id'), 'id' => 0,
'name' => $this->getField('name'), 'name' => $this->curTpl['name'],
'race' => $this->getField('race'), 'achievementpoints' => $this->curTpl['achievementpoints'],
'classs' => $this->getField('class'), 'guild' => $this->curTpl['guild'], // 0 if none
'gender' => $this->getField('gender'), 'guildRank' => -1,
'level' => $this->getField('level'), 'realm' => $this->curTpl['realm'][0],
'faction' => (1 << ($this->getField('race') - 1)) & RACE_MASK_ALLIANCE ? 0 : 1, 'realmname' => $this->curTpl['realm'][1],
'talenttree1' => $this->getField('talenttree1'), 'battlegroup' => $this->curTpl['battlegroup'][0],
'talenttree2' => $this->getField('talenttree2'), 'battlegroupname' => $this->curTpl['battlegroup'][0],
'talenttree3' => $this->getField('talenttree3'), 'region' => $this->curTpl['region'][0],
'talentspec' => $this->getField('activespec') + 1, // 0 => 1; 1 => 2 'level' => $this->curTpl['level'],
'achievementpoints' => $this->getField('achievementpoints'), 'race' => $this->curTpl['race'],
'guild' => '$"'.str_replace ('"', '', $this->curTpl['guildname']).'"',// force this to be a string 'gender' => $this->curTpl['gender'],
'guildrank' => $this->getField('guildrank'), 'classs' => $this->curTpl['classs'],
'realm' => Profiler::urlize($this->getField('realmName'), true), 'faction' => $this->curTpl['faction'],
'realmname' => $this->getField('realmName'), 'talenttree1' => $tDistrib[0],
// 'battlegroup' => Profiler::urlize($this->getField('battlegroup')), // was renamed to subregion somewhere around cata release 'talenttree2' => $tDistrib[1],
// 'battlegroupname' => $this->getField('battlegroup'), 'talenttree3' => $tDistrib[2],
'gearscore' => $this->getField('gearscore') 'talentspec' => $this->curTpl['talents']['active']
); );
if ($addInfo & PROFILEINFO_USER) if (!empty($this->curTpl['description']))
$data[$this->id]['published'] = (int)!!($this->getField('cuFlags') & PROFILER_CU_PUBLISHED); $data[$this->id]['description'] = $this->curTpl['description'];
// for the lv this determins if the link is profile=<id> or profile=<region>.<realm>.<name> if (!empty($this->curTpl['icon']))
if (!$this->isCustom()) $data[$this->id]['icon'] = $this->curTpl['icon'];
$data[$this->id]['region'] = Profiler::urlize($this->getField('region'));
if ($addInfo & PROFILEINFO_ARENA) if ($this->curTpl['cuFlags'] & PROFILE_CU_PUBLISHED)
{ $data[$this->id]['published'] = 1;
$data[$this->id]['rating'] = $this->getField('rating');
$data[$this->id]['captain'] = $this->getField('captain');
$data[$this->id]['games'] = $this->getField('seasonGames');
$data[$this->id]['wins'] = $this->getField('seasonWins');
}
// Filter asked for skills - add them if ($this->curTpl['cuFlags'] & PROFILE_CU_PINNED)
foreach ($reqCols as $col)
$data[$this->id][$col] = $this->getField($col);
if ($addInfo & PROFILEINFO_PROFILE)
{
if ($_ = $this->getField('description'))
$data[$this->id]['description'] = $_;
if ($_ = $this->getField('icon'))
$data[$this->id]['icon'] = $_;
}
if ($addInfo & PROFILEINFO_CHARACTER)
if ($_ = $this->getField('renameItr'))
$data[$this->id]['renameItr'] = $_;
if ($this->getField('cuFlags') & PROFILER_CU_PINNED)
$data[$this->id]['pinned'] = 1; $data[$this->id]['pinned'] = 1;
if ($this->getField('cuFlags') & PROFILER_CU_DELETED) if ($this->curTpl['cuFlags'] & PROFILE_CU_DELETED)
$data[$this->id]['deleted'] = 1; $data[$this->id]['deleted'] = 1;
} }
return array_values($data);
}
public function renderTooltip()
{
if (!$this->curTpl)
return [];
$title = '';
$name = $this->getField('name');
if ($_ = $this->getField('title'))
$title = (new TitleList(array(['id', $_])))->getField($this->getField('gender') ? 'female' : 'male', true);
if ($this->isCustom())
$name .= Lang::profiler('customProfile');
else if ($title)
$name = sprintf($title, $name);
$x = '<table>';
$x .= '<tr><td><b class="q">'.$name.'</b></td></tr>';
if ($g = $this->getField('guildname'))
$x .= '<tr><td>&lt;'.$g.'&gt;</td></tr>';
else if ($d = $this->getField('description'))
$x .= '<tr><td>'.$d.'</td></tr>';
$x .= '<tr><td>'.Lang::game('level').' '.$this->getField('level').' '.Lang::game('ra', $this->getField('race')).' '.Lang::game('cl', $this->getField('class')).'</td></tr>';
$x .= '</table>';
return $x;
}
public function getJSGlobals($addMask = 0)
{
$data = [];
$realms = Profiler::getRealms();
foreach ($this->iterate() as $id => $__)
{
if (($addMask & PROFILEINFO_PROFILE) && $this->isCustom())
{
$profile = array(
'id' => $this->getField('id'),
'name' => $this->getField('name'),
'race' => $this->getField('race'),
'classs' => $this->getField('class'),
'level' => $this->getField('level'),
'gender' => $this->getField('gender')
);
if ($_ = $this->getField('icon'))
$profile['icon'] = $_;
$data[] = $profile;
continue;
}
if ($addMask & PROFILEINFO_CHARACTER && !$this->isCustom())
{
if (!isset($realms[$this->getField('realm')]))
continue;
$data[] = array(
'id' => $this->getField('id'),
'name' => $this->getField('name'),
'realmname' => $realms[$this->getField('realm')]['name'],
'region' => $realms[$this->getField('realm')]['region'],
'realm' => Profiler::urlize($realms[$this->getField('realm')]['name']),
'race' => $this->getField('race'),
'classs' => $this->getField('class'),
'level' => $this->getField('level'),
'gender' => $this->getField('gender'),
'pinned' => $this->getField('cuFlags') & PROFILER_CU_PINNED ? 1 : 0
);
}
}
return $data; return $data;
} }
public function isCustom() public function renderTooltip($interactive = false)
{ {
return $this->getField('cuFlags') & PROFILER_CU_PROFILE; if (!$this->curTpl)
return [];
$x = '<table>';
$x .= '<tr><td><b class="q">'.$this->getField('name').'</b></td></tr>';
if ($g = $this->getField('name'))
$x .= '<tr><td>&lt;'.$g.'&gt; ('.$this->getField('guildrank').')</td></tr>';
else if ($d = $this->getField('description'))
$x .= '<tr><td>'.$d.'</td></tr>';
$x .= '<tr><td>'.Lang::game('level').' '.$this->getField('level').' '.Lang::game('ra', $this->curTpl['race']).' '.Lang::game('cl', $this->curTpl['classs']).'</td></tr>';
$x .= '</table>';
return $x;
} }
public function isVisibleToUser() public function getJSGlobals($addMask = 0) {}
private function getTalentDistribution()
{ {
if (!$this->isCustom() || User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU)) if (!empty($this->tDistribution))
return true; $this->tDistribution[$this->curTpl['classId']] = DB::Aowow()->selectCol('SELECT COUNT(t.id) FROM dbc_talent t JOIN dbc_talenttab tt ON t.tabId = tt.id WHERE tt.classMask & ?d GROUP BY tt.id ORDER BY tt.tabNumber ASC', 1 << ($this->curTpl['classId'] - 1));
if ($this->getField('cuFlags') & PROFILER_CU_DELETED) $result = [];
return false; $start = 0;
foreach ($this->tDistribution[$this->curTpl['classId']] as $len)
if (User::$id == $this->getField('user'))
return true;
return (bool)($this->getField('cuFlags') & PROFILER_CU_PUBLISHED);
}
public function getIcon()
{
if ($_ = $this->getField('icon'))
return $_;
$str = 'chr_';
switch ($this->getField('race'))
{ {
case 1: $str .= 'human_'; break; $result[] = array_sum(str_split(substr($this->curTpl['talentString'], $start, $len)));
case 2: $str .= 'orc_'; break; $start += $len;
case 3: $str .= 'dwarf_'; break;
case 4: $str .= 'nightelf_'; break;
case 5: $str .= 'scourge_'; break;
case 6: $str .= 'tauren_'; break;
case 7: $str .= 'gnome_'; break;
case 8: $str .= 'troll_'; break;
case 10: $str .= 'bloodelf_'; break;
case 11: $str .= 'draenei_'; break;
} }
switch ($this->getField('gender')) return $result;
{
case 0: $str .= 'male_'; break;
case 1: $str .= 'female_'; break;
}
switch ($this->getField('class'))
{
case 1: $str .= 'warrior0'; break;
case 2: $str .= 'paladin0'; break;
case 3: $str .= 'hunter0'; break;
case 4: $str .= 'rogue0'; break;
case 5: $str .= 'priest0'; break;
case 6: $str .= 'deathknight0'; break;
case 7: $str .= 'shaman0'; break;
case 8: $str .= 'mage0'; break;
case 9: $str .= 'warlock0'; break;
case 11: $str .= 'druid0'; break;
}
$level = $this->getField('level');
if ($level > 59)
$str .= floor(($level - 60) / 10) + 2;
else
$str .= 1;
return $str;
} }
} }
class ProfileListFilter extends Filter class ProfileListFilter extends Filter
{ {
public $useLocalList = false; public $extraOpts = [];
public $extraOpts = [];
private $realms = []; protected $genericFilter = array(
protected $enums = array(
-1 => array( // arena team sizes
// by name by rating by contrib
12 => 2, 13 => 2, 14 => 2,
15 => 3, 16 => 3, 17 => 3,
18 => 5, 19 => 5, 20 => 5
)
); );
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
2 => [FILTER_CR_NUMERIC, 'gearscore', NUM_CAST_INT ], // gearscore [num]
3 => [FILTER_CR_CALLBACK, 'cbAchievs', null, null], // achievementpoints [num]
5 => [FILTER_CR_NUMERIC, 'talenttree1', NUM_CAST_INT ], // talenttree1 [num]
6 => [FILTER_CR_NUMERIC, 'talenttree2', NUM_CAST_INT ], // talenttree2 [num]
7 => [FILTER_CR_NUMERIC, 'talenttree3', NUM_CAST_INT ], // talenttree3 [num]
9 => [FILTER_CR_STRING, 'g.name', ], // guildname
10 => [FILTER_CR_CALLBACK, 'cbHasGuildRank', null, null], // guildrank
12 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname2v2
15 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname3v3
18 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname5v5
13 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng2v2
16 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng3v3
19 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng5v5
14 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib2v2 [num]
17 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib3v3 [num]
20 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib5v5 [num]
21 => [FILTER_CR_CALLBACK, 'cbWearsItems', null, null], // wearingitem [str]
23 => [FILTER_CR_CALLBACK, 'cbCompletedAcv', null, null], // completedachievement
25 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_ALCHEMY, null], // alchemy [num]
26 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_BLACKSMITHING, null], // blacksmithing [num]
27 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_ENCHANTING, null], // enchanting [num]
28 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_ENGINEERING, null], // engineering [num]
29 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_HERBALISM, null], // herbalism [num]
30 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_INSCRIPTION, null], // inscription [num]
31 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_JEWELCRAFTING, null], // jewelcrafting [num]
32 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_LEATHERWORKING, null], // leatherworking [num]
33 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_MINING, null], // mining [num]
34 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_SKINNING, null], // skinning [num]
35 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_TAILORING, null], // tailoring [num]
36 => [FILTER_CR_CALLBACK, 'cbHasGuild', null, null] // hasguild [yn]
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_RANGE, [1, 36], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 5000]], true ], // criteria operators
'crv' => [FILTER_V_REGEX, '/[\p{C}:;%\\\\]/ui', true ], // criteria values
'na' => [FILTER_V_REGEX, '/[\p{C};%\\\\]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'ex' => [FILTER_V_EQUAL, 'on', false], // only match exact
'si' => [FILTER_V_LIST, [1, 2], false], // side
'ra' => [FILTER_V_LIST, [[1, 8], 10, 11], true ], // race
'cl' => [FILTER_V_LIST, [[1, 9], 11], true ], // class
'minle' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // min level
'maxle' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // max level
'rg' => [FILTER_V_CALLBACK, 'cbRegionCheck', false], // region
'sv' => [FILTER_V_CALLBACK, 'cbServerCheck', false], // server
);
/* heads up!
a couple of filters are too complex to be run against the characters database
if they are selected, force useage of LocalProfileList
*/
public function __construct($fromPOST = false, $opts = [])
{
if (!empty($opts['realms']))
$this->realms = $opts['realms'];
else
$this->realms = array_keys(Profiler::getRealms());
parent::__construct($fromPOST, $opts);
if (!empty($this->fiData['c']['cr']))
if (array_intersect($this->fiData['c']['cr'], [2, 5, 6, 7, 21]))
$this->useLocalList = true;
}
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCR = $this->genericCriterion($cr)) if ($genCR = $this->genericCriterion($cr))
return $genCR; return $genCR;
unset($cr);
$this->error = true;
return [1];
}
switch ($cr[0])
{
default:
break;
}
unset($cr); unset($cr);
$this->error = true; $this->error = 1;
return [1]; return [1];
} }
@@ -335,459 +216,13 @@ class ProfileListFilter extends Filter
$parts = []; $parts = [];
$_v = $this->fiData['v']; $_v = $this->fiData['v'];
// region (rg), battlegroup (bg) and server (sv) are passed to ProflieList as miscData and handled there // name
if (isset($_v['na']))
// table key differs between remote and local :< if ($_ = $this->modularizeString(['name_loc'.User::$localeId]))
$k = $this->useLocalList ? 'p' : 'c'; $parts[] = $_;
// name [str] - the table is case sensitive. Since i down't want to destroy indizes, lets alter the search terms
if (!empty($_v['na']))
{
$lower = $this->modularizeString([$k.'.name'], Util::lower($_v['na']), !empty($_v['ex']) && $_v['ex'] == 'on', true);
$proper = $this->modularizeString([$k.'.name'], Util::ucWords($_v['na']), !empty($_v['ex']) && $_v['ex'] == 'on', true);
$parts[] = ['OR', $lower, $proper];
}
// side [list]
if (!empty($_v['si']))
{
if ($_v['si'] == 1)
$parts[] = [$k.'.race', [1, 3, 4, 7, 11]];
else if ($_v['si'] == 2)
$parts[] = [$k.'.race', [2, 5, 6, 8, 10]];
}
// race [list]
if (!empty($_v['ra']))
$parts[] = [$k.'.race', $_v['ra']];
// class [list]
if (!empty($_v['cl']))
$parts[] = [$k.'.class', $_v['cl']];
// min level [int]
if (isset($_v['minle']))
$parts[] = [$k.'.level', $_v['minle'], '>='];
// max level [int]
if (isset($_v['maxle']))
$parts[] = [$k.'.level', $_v['maxle'], '<='];
return $parts; return $parts;
} }
protected function cbRegionCheck(&$v)
{
if (in_array($v, Util::$regions))
{
$this->parentCats[0] = $v; // directly redirect onto this region
$v = ''; // remove from filter
return true;
}
return false;
}
protected function cbServerCheck(&$v)
{
foreach (Profiler::getRealms() as $realm)
if ($realm['name'] == $v)
{
$this->parentCats[1] = Profiler::urlize($v);// directly redirect onto this server
$v = ''; // remove from filter
return true;
}
return false;
}
protected function cbProfession($cr, $skillId)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return;
$k = 'sk_'.Util::createHash(12);
$col = 'skill'.$skillId;
$this->formData['extraCols'][$skillId] = $col;
if ($this->useLocalList)
{
$this->extraOpts[$k] = array(
'j' => ['?_profiler_completion '.$k.' ON '.$k.'.id = p.id AND '.$k.'.`type` = '.Type::SKILL.' AND '.$k.'.typeId = '.$skillId.' AND '.$k.'.cur '.$cr[1].' '.$cr[2], true],
's' => [', '.$k.'.cur AS '.$col]
);
return [$k.'.typeId', null, '!'];
}
else
{
$this->extraOpts[$k] = array(
'j' => ['character_skills '.$k.' ON '.$k.'.guid = c.guid AND '.$k.'.skill = '.$skillId.' AND '.$k.'.value '.$cr[1].' '.$cr[2], true],
's' => [', '.$k.'.value AS '.$col]
);
return [$k.'.skill', null, '!'];
}
}
protected function cbCompletedAcv($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT))
return false;
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_achievement WHERE id = ?d', $cr[2]))
return false;
$k = 'acv_'.Util::createHash(12);
if ($this->useLocalList)
{
$this->extraOpts[$k] = ['j' => ['?_profiler_completion '.$k.' ON '.$k.'.id = p.id AND '.$k.'.`type` = '.Type::ACHIEVEMENT.' AND '.$k.'.typeId = '.$cr[2], true]];
return [$k.'.typeId', null, '!'];
}
else
{
$this->extraOpts[$k] = ['j' => ['character_achievement '.$k.' ON '.$k.'.guid = c.guid AND '.$k.'.achievement = '.$cr[2], true]];
return [$k.'.achievement', null, '!'];
}
}
protected function cbWearsItems($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT))
return false;
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_items WHERE id = ?d', $cr[2]))
return false;
$k = 'i_'.Util::createHash(12);
$this->extraOpts[$k] = ['j' => ['?_profiler_items '.$k.' ON '.$k.'.id = p.id AND '.$k.'.item = '.$cr[2], true]];
return [$k.'.item', null, '!'];
}
protected function cbHasGuild($cr)
{
if (!$this->int2Bool($cr[1]))
return false;
if ($this->useLocalList)
return ['p.guild', null, $cr[1] ? '!' : null];
else
return ['gm.guildId', null, $cr[1] ? '!' : null];
}
protected function cbHasGuildRank($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
if ($this->useLocalList)
return ['p.guildrank', $cr[2], $cr[1]];
else
return ['gm.rank', $cr[2], $cr[1]];
}
protected function cbTeamName($cr)
{
if ($_ = $this->modularizeString(['at.name'], $cr[2]))
return ['AND', ['at.type', $this->enums[-1][$cr[0]]], $_];
return false;
}
protected function cbTeamRating($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
return ['AND', ['at.type', $this->enums[-1][$cr[0]]], ['at.rating', $cr[2], $cr[1]]];
}
protected function cbAchievs($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
if ($this->useLocalList)
return ['p.achievementpoints', $cr[2], $cr[1]];
else
return ['cap.counter', $cr[2], $cr[1]];
}
} }
class RemoteProfileList extends ProfileList
{
protected $queryBase = 'SELECT `c`.*, `c`.`guid` AS ARRAY_KEY FROM characters c';
protected $queryOpts = array(
'c' => [['gm', 'g', 'cap']], // 12698: use criteria of Achievement 4496 as shortcut to get total achievement points
'cap' => ['j' => ['character_achievement_progress cap ON cap.guid = c.guid AND cap.criteria = 12698', true], 's' => ', IFNULL(cap.counter, 0) AS achievementpoints'],
'gm' => ['j' => ['guild_member gm ON gm.guid = c.guid', true], 's' => ', gm.rank AS guildrank'],
'g' => ['j' => ['guild g ON g.guildid = gm.guildid', true], 's' => ', g.guildid AS guild, g.name AS guildname'],
'atm' => ['j' => ['arena_team_member atm ON atm.guid = c.guid', true], 's' => ', atm.personalRating AS rating'],
'at' => [['atm'], 'j' => 'arena_team at ON atm.arenaTeamId = at.arenaTeamId', 's' => ', at.name AS arenateam, IF(at.captainGuid = c.guid, 1, 0) AS captain']
);
public function __construct($conditions = [], $miscData = null)
{
// select DB by realm
if (!$this->selectRealms($miscData))
{
trigger_error('no access to auth-db or table realmlist is empty', E_USER_WARNING);
return;
}
parent::__construct($conditions, $miscData);
if ($this->error)
return;
reset($this->dbNames); // only use when querying single realm
$realmId = key($this->dbNames);
$realms = Profiler::getRealms();
$talentSpells = [];
$talentLookup = [];
$distrib = null;
$limit = CFG_SQL_LIMIT_DEFAULT;
foreach ($conditions as $c)
if (is_int($c))
$limit = $c;
// post processing
foreach ($this->iterate() as $guid => &$curTpl)
{
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
// realm
[$r, $g] = explode(':', $guid);
if (!empty($realms[$r]))
{
$curTpl['realm'] = $r;
$curTpl['realmName'] = $realms[$r]['name'];
$curTpl['region'] = $realms[$r]['region'];
}
else
{
trigger_error('character "'.$curTpl['name'].'" belongs to nonexistant realm #'.$r, E_USER_WARNING);
unset($this->templates[$guid]);
continue;
}
// temp id
$curTpl['id'] = 0;
// talent points pre
$talentLookup[$r][$g] = [];
$talentSpells[] = $curTpl['class'];
$curTpl['activespec'] = $curTpl['activeTalentGroup'];
// equalize distribution
if ($limit != CFG_SQL_LIMIT_NONE)
{
if (empty($distrib[$curTpl['realm']]))
$distrib[$curTpl['realm']] = 1;
else
$distrib[$curTpl['realm']]++;
}
// char is pending rename
if ($curTpl['at_login'] & 0x1)
{
if (!isset($this->rnItr[$curTpl['name']]))
$this->rnItr[$curTpl['name']] = DB::Aowow()->selectCell('SELECT MAX(renameItr) FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID IS NOT NULL AND name = ?', $r, $curTpl['name']) ?: 0;
// already saved as "pending rename"
if ($rnItr = DB::Aowow()->selectCell('SELECT renameItr FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID = ?d', $r, $g))
$curTpl['renameItr'] = $rnItr;
// not yet recognized: get max itr
else
$curTpl['renameItr'] = ++$this->rnItr[$curTpl['name']];
}
else
$curTpl['renameItr'] = 0;
$curTpl['cuFlags'] = 0;
}
foreach ($talentLookup as $realm => $chars)
$talentLookup[$realm] = DB::Characters($realm)->selectCol('SELECT guid AS ARRAY_KEY, spell AS ARRAY_KEY2, talentGroup FROM character_talent ct WHERE guid IN (?a)', array_keys($chars));
$talentSpells = DB::Aowow()->select('SELECT spell AS ARRAY_KEY, tab, `rank` FROM ?_talents WHERE class IN (?a)', array_unique($talentSpells));
if ($distrib !== null)
{
$total = array_sum($distrib);
foreach ($distrib as &$d)
$d = ceil($limit * $d / $total);
}
foreach ($this->iterate() as $guid => &$curTpl)
{
if ($distrib !== null)
{
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
{
unset($this->templates[$guid]);
continue;
}
$distrib[$curTpl['realm']]--;
$limit--;
}
[$r, $g] = explode(':', $guid);
// talent points post
$curTpl['talenttree1'] = 0;
$curTpl['talenttree2'] = 0;
$curTpl['talenttree3'] = 0;
if (!empty($talentLookup[$r][$g]))
{
$talents = array_filter($talentLookup[$r][$g], function($v) use ($curTpl) { return $curTpl['activespec'] == $v; } );
foreach (array_intersect_key($talentSpells, $talents) as $spell => $data)
$curTpl['talenttree'.($data['tab'] + 1)] += $data['rank'];
}
}
}
public function getListviewData($addInfoMask = 0, array $reqCols = [])
{
$data = parent::getListviewData($addInfoMask, $reqCols);
// not wanted on server list
foreach ($data as &$d)
unset($d['published']);
return $data;
}
public function initializeLocalEntries()
{
$baseData = $guildData = [];
foreach ($this->iterate() as $guid => $__)
{
$baseData[$guid] = array(
'realm' => $this->getField('realm'),
'realmGUID' => $this->getField('guid'),
'name' => $this->getField('name'),
'renameItr' => $this->getField('renameItr'),
'race' => $this->getField('race'),
'class' => $this->getField('class'),
'level' => $this->getField('level'),
'gender' => $this->getField('gender'),
'guild' => $this->getField('guild') ?: null,
'guildrank' => $this->getField('guild') ? $this->getField('guildrank') : null,
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
if ($this->getField('guild'))
$guildData[] = array(
'realm' => $this->getField('realm'),
'realmGUID' => $this->getField('guild'),
'name' => $this->getField('guildname'),
'nameUrl' => Profiler::urlize($this->getField('guildname')),
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
}
// basic guild data (satisfying table constraints)
if ($guildData)
{
foreach (Util::createSqlBatchInsert($guildData) as $ins)
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_guild (?#) VALUES '.$ins, array_keys(reset($guildData)));
// merge back local ids
$localGuilds = DB::Aowow()->selectCol('SELECT realm AS ARRAY_KEY, realmGUID AS ARRAY_KEY2, id FROM ?_profiler_guild WHERE realm IN (?a) AND realmGUID IN (?a)',
array_column($guildData, 'realm'), array_column($guildData, 'realmGUID')
);
foreach ($baseData as &$bd)
if ($bd['guild'])
$bd['guild'] = $localGuilds[$bd['realm']][$bd['guild']];
}
// basic char data (enough for tooltips)
if ($baseData)
{
foreach (Util::createSqlBatchInsert($baseData) as $ins)
DB::Aowow()->query('INSERT INTO ?_profiler_profiles (?#) VALUES '.$ins.' ON DUPLICATE KEY UPDATE name = VALUES(name), renameItr = VALUES(renameItr)', array_keys(reset($baseData)));
// merge back local ids
$localIds = DB::Aowow()->select(
'SELECT CONCAT(realm, ":", realmGUID) AS ARRAY_KEY, id, gearscore FROM ?_profiler_profiles WHERE (cuFlags & ?d) = 0 AND realm IN (?a) AND realmGUID IN (?a)',
PROFILER_CU_PROFILE,
array_column($baseData, 'realm'),
array_column($baseData, 'realmGUID')
);
foreach ($this->iterate() as $guid => &$_curTpl)
if (isset($localIds[$guid]))
$_curTpl = array_merge($_curTpl, $localIds[$guid]);
}
}
}
class LocalProfileList extends ProfileList
{
protected $queryBase = 'SELECT p.*, p.id AS ARRAY_KEY FROM ?_profiler_profiles p';
protected $queryOpts = array(
'p' => [['g'], 'g' => 'p.id'],
'ap' => ['j' => ['?_account_profiles ap ON ap.profileId = p.id', true], 's' => ', (IFNULL(ap.ExtraFlags, 0) | p.cuFlags) AS cuFlags'],
'atm' => ['j' => ['?_profiler_arena_team_member atm ON atm.profileId = p.id', true], 's' => ', atm.captain, atm.personalRating AS rating, atm.seasonGames, atm.seasonWins'],
'at' => [['atm'], 'j' => ['?_profiler_arena_team at ON at.id = atm.arenaTeamId', true], 's' => ', at.type'],
'g' => ['j' => ['?_profiler_guild g ON g.id = p.guild', true], 's' => ', g.name AS guildname']
);
public function __construct($conditions = [], $miscData = null)
{
parent::__construct($conditions, $miscData);
if ($this->error)
return;
$realms = Profiler::getRealms();
// post processing
$acvPoints = DB::Aowow()->selectCol('SELECT pc.id AS ARRAY_KEY, SUM(a.points) FROM ?_profiler_completion pc LEFT JOIN ?_achievement a ON a.id = pc.typeId WHERE pc.`type` = ?d AND pc.id IN (?a) GROUP BY pc.id', Type::ACHIEVEMENT, $this->getFoundIDs());
foreach ($this->iterate() as $id => &$curTpl)
{
if ($curTpl['realm'] && !isset($realms[$curTpl['realm']]))
continue;
if (isset($realms[$curTpl['realm']]))
{
$curTpl['realmName'] = $realms[$curTpl['realm']]['name'];
$curTpl['region'] = $realms[$curTpl['realm']]['region'];
}
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
$curTpl['achievementpoints'] = isset($acvPoints[$id]) ? $acvPoints[$id] : 0;
}
}
public function getProfileUrl()
{
$url = '?profile=';
if ($this->isCustom())
return $url.$this->getField('id');
return $url.implode('.', array(
Profiler::urlize($this->getField('region')),
Profiler::urlize($this->getField('realmName')),
urlencode($this->getField('name'))
));
}
}
?> ?>

View File

@@ -6,9 +6,8 @@ if (!defined('AOWOW_REVISION'))
class QuestList extends BaseType class QuestList extends BaseType
{ {
public static $type = Type::QUEST; public static $type = TYPE_QUEST;
public static $brickFile = 'quest'; public static $brickFile = 'quest';
public static $dataTable = '?_quests';
public $requires = []; public $requires = [];
public $rewards = []; public $rewards = [];
@@ -19,7 +18,6 @@ class QuestList extends BaseType
'q' => [], 'q' => [],
'rsc' => ['j' => '?_spell rsc ON q.rewardSpellCast = rsc.id'], // limit rewardSpellCasts 'rsc' => ['j' => '?_spell rsc ON q.rewardSpellCast = rsc.id'], // limit rewardSpellCasts
'qse' => ['j' => '?_quests_startend qse ON q.id = qse.questId', 's' => ', qse.method'], // groupConcat..? 'qse' => ['j' => '?_quests_startend qse ON q.id = qse.questId', 's' => ', qse.method'], // groupConcat..?
'e' => ['j' => ['?_events e ON e.id = `q`.eventId', true], 's' => ', e.holidayId']
); );
public function __construct($conditions = [], $miscData = null) public function __construct($conditions = [], $miscData = null)
@@ -35,7 +33,7 @@ class QuestList extends BaseType
$_curTpl['cat1'] = $_curTpl['zoneOrSort']; // should probably be in a method... $_curTpl['cat1'] = $_curTpl['zoneOrSort']; // should probably be in a method...
$_curTpl['cat2'] = 0; $_curTpl['cat2'] = 0;
foreach (Game::$questClasses as $k => $arr) foreach (Util::$questClasses as $k => $arr)
{ {
if (in_array($_curTpl['cat1'], $arr)) if (in_array($_curTpl['cat1'], $arr))
{ {
@@ -49,18 +47,18 @@ class QuestList extends BaseType
for ($i = 1; $i < 7; $i++) for ($i = 1; $i < 7; $i++)
{ {
if ($_ = $_curTpl['reqItemId'.$i]) if ($_ = $_curTpl['reqItemId'.$i])
$requires[Type::ITEM][] = $_; $requires[TYPE_ITEM][] = $_;
if ($i > 4) if ($i > 4)
continue; continue;
if ($_curTpl['reqNpcOrGo'.$i] > 0) if ($_curTpl['reqNpcOrGo'.$i] > 0)
$requires[Type::NPC][] = $_curTpl['reqNpcOrGo'.$i]; $requires[TYPE_NPC][] = $_curTpl['reqNpcOrGo'.$i];
else if ($_curTpl['reqNpcOrGo'.$i] < 0) else if ($_curTpl['reqNpcOrGo'.$i] < 0)
$requires[Type::OBJECT][] = -$_curTpl['reqNpcOrGo'.$i]; $requires[TYPE_OBJECT][] = -$_curTpl['reqNpcOrGo'.$i];
if ($_ = $_curTpl['reqSourceItemId'.$i]) if ($_ = $_curTpl['reqSourceItemId'.$i])
$requires[Type::ITEM][] = $_; $requires[TYPE_ITEM][] = $_;
} }
if ($requires) if ($requires)
$this->requires[$id] = $requires; $this->requires[$id] = $requires;
@@ -70,24 +68,24 @@ class QuestList extends BaseType
$choices = []; $choices = [];
if ($_ = $_curTpl['rewardTitleId']) if ($_ = $_curTpl['rewardTitleId'])
$rewards[Type::TITLE][] = $_; $rewards[TYPE_TITLE][] = $_;
if ($_ = $_curTpl['rewardHonorPoints']) if ($_ = $_curTpl['rewardHonorPoints'])
$rewards[Type::CURRENCY][104] = $_; $rewards[TYPE_CURRENCY][104] = $_;
if ($_ = $_curTpl['rewardArenaPoints']) if ($_ = $_curTpl['rewardArenaPoints'])
$rewards[Type::CURRENCY][103] = $_; $rewards[TYPE_CURRENCY][103] = $_;
for ($i = 1; $i < 7; $i++) for ($i = 1; $i < 7; $i++)
{ {
if ($_ = $_curTpl['rewardChoiceItemId'.$i]) if ($_ = $_curTpl['rewardChoiceItemId'.$i])
$choices[Type::ITEM][$_] = $_curTpl['rewardChoiceItemCount'.$i]; $choices[TYPE_ITEM][$_] = $_curTpl['rewardChoiceItemCount'.$i];
if ($i > 5) if ($i > 5)
continue; continue;
if ($_ = $_curTpl['rewardFactionId'.$i]) if ($_ = $_curTpl['rewardFactionId'.$i])
$rewards[Type::FACTION][$_] = $_curTpl['rewardFactionValue'.$i]; $rewards[TYPE_FACTION][$_] = $_curTpl['rewardFactionValue'.$i];
if ($i > 4) if ($i > 4)
continue; continue;
@@ -96,9 +94,9 @@ class QuestList extends BaseType
{ {
$qty = $_curTpl['rewardItemCount'.$i]; $qty = $_curTpl['rewardItemCount'.$i];
if (in_array($_, $currencies)) if (in_array($_, $currencies))
$rewards[Type::CURRENCY][array_search($_, $currencies)] = $qty; $rewards[TYPE_CURRENCY][array_search($_, $currencies)] = $qty;
else else
$rewards[Type::ITEM][$_] = $qty; $rewards[TYPE_ITEM][$_] = $qty;
} }
} }
if ($rewards) if ($rewards)
@@ -112,7 +110,7 @@ class QuestList extends BaseType
// static use START // static use START
public static function getName($id) public static function getName($id)
{ {
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_quests WHERE id = ?d', $id); $n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_quests WHERE id = ?d', $id);
return Util::localizedString($n, 'name'); return Util::localizedString($n, 'name');
} }
// static use END // static use END
@@ -122,18 +120,12 @@ class QuestList extends BaseType
return $this->curTpl['flags'] & QUEST_FLAG_REPEATABLE || $this->curTpl['specialFlags'] & QUEST_FLAG_SPECIAL_REPEATABLE; return $this->curTpl['flags'] & QUEST_FLAG_REPEATABLE || $this->curTpl['specialFlags'] & QUEST_FLAG_SPECIAL_REPEATABLE;
} }
public function isDaily() public function isDaily($strict = false)
{ {
if ($this->curTpl['flags'] & QUEST_FLAG_DAILY) if ($strict)
return 1; return $this->curTpl['flags'] & QUEST_FLAG_DAILY;
else
if ($this->curTpl['flags'] & QUEST_FLAG_WEEKLY) return $this->curTpl['flags'] & (QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY) || $this->curTpl['specialFlags'] & QUEST_FLAG_SPECIAL_MONTHLY;
return 2;
if ($this->curTpl['specialFlags'] & QUEST_FLAG_SPECIAL_MONTHLY)
return 3;
return 0;
} }
// using reqPlayerKills and rewardHonor as a crutch .. has TC this even implemented..? // using reqPlayerKills and rewardHonor as a crutch .. has TC this even implemented..?
@@ -156,7 +148,7 @@ class QuestList extends BaseType
{ {
$data[$this->id] = array( $data[$this->id] = array(
"n" => $this->getField('name', true), "n" => $this->getField('name', true),
"t" => Type::QUEST, "t" => TYPE_QUEST,
"ti" => $this->id, "ti" => $this->id,
"c" => $this->curTpl['cat1'], "c" => $this->curTpl['cat1'],
"c2" => $this->curTpl['cat2'] "c2" => $this->curTpl['cat2']
@@ -172,10 +164,10 @@ class QuestList extends BaseType
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
{ {
if (!(Game::sideByRaceMask($this->curTpl['reqRaceMask']) & $side)) if (!(Util::sideByRaceMask($this->curTpl['reqRaceMask']) & $side))
continue; continue;
[$series, $first] = DB::Aowow()->SelectRow( list($series, $first) = DB::Aowow()->SelectRow(
'SELECT IF(prev.id OR cur.nextQuestIdChain, 1, 0) AS "0", IF(prev.id IS NULL AND cur.nextQuestIdChain, 1, 0) AS "1" FROM ?_quests cur LEFT JOIN ?_quests prev ON prev.nextQuestIdChain = cur.id WHERE cur.id = ?d', 'SELECT IF(prev.id OR cur.nextQuestIdChain, 1, 0) AS "0", IF(prev.id IS NULL AND cur.nextQuestIdChain, 1, 0) AS "1" FROM ?_quests cur LEFT JOIN ?_quests prev ON prev.nextQuestIdChain = cur.id WHERE cur.id = ?d',
$this->id $this->id
); );
@@ -209,21 +201,21 @@ class QuestList extends BaseType
'level' => $this->curTpl['level'], 'level' => $this->curTpl['level'],
'reqlevel' => $this->curTpl['minLevel'], 'reqlevel' => $this->curTpl['minLevel'],
'name' => $this->getField('name', true), 'name' => $this->getField('name', true),
'side' => Game::sideByRaceMask($this->curTpl['reqRaceMask']), 'side' => Util::sideByRaceMask($this->curTpl['reqRaceMask']),
'wflags' => 0x0, 'wflags' => 0x0,
'xp' => $this->curTpl['rewardXP'] 'xp' => $this->curTpl['rewardXP']
); );
if (!empty($this->rewards[$this->id][Type::CURRENCY])) if (!empty($this->rewards[$this->id][TYPE_CURRENCY]))
foreach ($this->rewards[$this->id][Type::CURRENCY] as $iId => $qty) foreach ($this->rewards[$this->id][TYPE_CURRENCY] as $iId => $qty)
$data[$this->id]['currencyrewards'][] = [$iId, $qty]; $data[$this->id]['currencyrewards'][] = [$iId, $qty];
if (!empty($this->rewards[$this->id][Type::ITEM])) if (!empty($this->rewards[$this->id][TYPE_ITEM]))
foreach ($this->rewards[$this->id][Type::ITEM] as $iId => $qty) foreach ($this->rewards[$this->id][TYPE_ITEM] as $iId => $qty)
$data[$this->id]['itemrewards'][] = [$iId, $qty]; $data[$this->id]['itemrewards'][] = [$iId, $qty];
if (!empty($this->choices[$this->id][Type::ITEM])) if (!empty($this->choices[$this->id][TYPE_ITEM]))
foreach ($this->choices[$this->id][Type::ITEM] as $iId => $qty) foreach ($this->choices[$this->id][TYPE_ITEM] as $iId => $qty)
$data[$this->id]['itemchoices'][] = [$iId, $qty]; $data[$this->id]['itemchoices'][] = [$iId, $qty];
if ($_ = $this->curTpl['rewardTitleId']) if ($_ = $this->curTpl['rewardTitleId'])
@@ -249,8 +241,6 @@ class QuestList extends BaseType
// if ($this->isRepeatable()) // dafuque..? says repeatable and is used as 'disabled'..? // if ($this->isRepeatable()) // dafuque..? says repeatable and is used as 'disabled'..?
// $data[$this->id]['wflags'] |= QUEST_CU_REPEATABLE; // $data[$this->id]['wflags'] |= QUEST_CU_REPEATABLE;
if ($this->curTpl['cuFlags'] & (CUSTOM_UNAVAILABLE | CUSTOM_DISABLED))
$data[$this->id]['wflags'] |= QUEST_CU_REPEATABLE;
if ($this->curTpl['flags'] & QUEST_FLAG_DAILY) if ($this->curTpl['flags'] & QUEST_FLAG_DAILY)
{ {
@@ -313,7 +303,7 @@ class QuestList extends BaseType
if (!$this->curTpl) if (!$this->curTpl)
return null; return null;
$title = htmlentities($this->getField('name', true)); $title = Util::jsEscape($this->getField('name', true));
$level = $this->curTpl['level']; $level = $this->curTpl['level'];
if ($level < 0) if ($level < 0)
$level = 0; $level = 0;
@@ -332,7 +322,7 @@ class QuestList extends BaseType
$x .= '<table><tr><td><b class="q">'.$title.'</b></td></tr></table>'; $x .= '<table><tr><td><b class="q">'.$title.'</b></td></tr></table>';
$x .= '<table><tr><td><br />'.$this->parseText('objectives', false); $x .= '<table><tr><td><br />'.$this->parseText('objectives');
$xReq = ''; $xReq = '';
@@ -350,7 +340,7 @@ class QuestList extends BaseType
else else
$name = $rng > 0 ? CreatureList::getName($rng) : GameObjectList::getName(-$rng); $name = $rng > 0 ? CreatureList::getName($rng) : GameObjectList::getName(-$rng);
$xReq .= '<br /> - '.$name.($rngQty > 1 ? ' x '.$rngQty : null); $xReq .= '<br /> - '.Util::jsEscape($name).($rngQty > 1 ? ' x '.$rngQty : null);
} }
for ($i = 1; $i < 7; $i++) for ($i = 1; $i < 7; $i++)
@@ -361,11 +351,11 @@ class QuestList extends BaseType
if (!$ri || $riQty < 1) if (!$ri || $riQty < 1)
continue; continue;
$xReq .= '<br /> - '.ItemList::getName($ri).($riQty > 1 ? ' x '.$riQty : null); $xReq .= '<br /> - '.Util::jsEscape(ItemList::getName($ri)).($riQty > 1 ? ' x '.$riQty : null);
} }
if ($et = $this->getField('end', true)) if ($et = $this->getField('end', true))
$xReq .= '<br /> - '.$et; $xReq .= '<br /> - '.Util::jsEscape($et);
if ($_ = $this->getField('rewardOrReqMoney')) if ($_ = $this->getField('rewardOrReqMoney'))
if ($_ < 0) if ($_ < 0)
@@ -390,31 +380,33 @@ class QuestList extends BaseType
// items // items
for ($i = 1; $i < 5; $i++) for ($i = 1; $i < 5; $i++)
if ($this->curTpl['rewardItemId'.$i] > 0) if ($this->curTpl['rewardItemId'.$i] > 0)
$data[Type::ITEM][$this->curTpl['rewardItemId'.$i]] = $this->curTpl['rewardItemId'.$i]; $data[TYPE_ITEM][$this->curTpl['rewardItemId'.$i]] = $this->curTpl['rewardItemId'.$i];
for ($i = 1; $i < 7; $i++) for ($i = 1; $i < 7; $i++)
if ($this->curTpl['rewardChoiceItemId'.$i] > 0) if ($this->curTpl['rewardChoiceItemId'.$i] > 0)
$data[Type::ITEM][$this->curTpl['rewardChoiceItemId'.$i]] = $this->curTpl['rewardChoiceItemId'.$i]; $data[TYPE_ITEM][$this->curTpl['rewardChoiceItemId'.$i]] = $this->curTpl['rewardChoiceItemId'.$i];
// spells // spells
if ($this->curTpl['rewardSpell'] > 0) if ($this->curTpl['rewardSpell'] > 0)
$data[Type::SPELL][$this->curTpl['rewardSpell']] = $this->curTpl['rewardSpell']; $data[TYPE_SPELL][$this->curTpl['rewardSpell']] = $this->curTpl['rewardSpell'];
if ($this->curTpl['rewardSpellCast'] > 0) if ($this->curTpl['rewardSpellCast'] > 0)
$data[Type::SPELL][$this->curTpl['rewardSpellCast']] = $this->curTpl['rewardSpellCast']; $data[TYPE_SPELL][$this->curTpl['rewardSpellCast']] = $this->curTpl['rewardSpellCast'];
// titles // titles
if ($this->curTpl['rewardTitleId'] > 0) if ($this->curTpl['rewardTitleId'] > 0)
$data[Type::TITLE][$this->curTpl['rewardTitleId']] = $this->curTpl['rewardTitleId']; $data[TYPE_TITLE][$this->curTpl['rewardTitleId']] = $this->curTpl['rewardTitleId'];
// currencies // currencies
if (!empty($this->rewards[$this->id][Type::CURRENCY])) if (!empty($this->rewards[$this->id][TYPE_CURRENCY]))
foreach ($this->rewards[$this->id][Type::CURRENCY] as $id => $__) {
$data[Type::CURRENCY][$id] = $id; $_ = $this->rewards[$this->id][TYPE_CURRENCY];
$data[TYPE_CURRENCY] = array_combine(array_keys($_), array_keys($_));
}
} }
if ($addMask & GLOBALINFO_SELF) if ($addMask & GLOBALINFO_SELF)
$data[Type::QUEST][$this->id] = ['name' => $this->getField('name', true)]; $data[TYPE_QUEST][$this->id] = ['name' => $this->getField('name', true)];
} }
return $data; return $data;
@@ -430,64 +422,189 @@ class QuestListFilter extends Filter
38 => [null, 1, 2, 3, 4, 5, 6, 7, 8, null, 10, 11, true, false], 38 => [null, 1, 2, 3, 4, 5, 6, 7, 8, null, 10, 11, true, false],
); );
protected $genericFilter = array( protected $genericFilter = array(
1 => [FILTER_CR_CALLBACK, 'cbReputation', '>', null], // increasesrepwith
2 => [FILTER_CR_NUMERIC, 'rewardXP', NUM_CAST_INT ], // experiencegained
3 => [FILTER_CR_NUMERIC, 'rewardOrReqMoney', NUM_CAST_INT ], // moneyrewarded
4 => [FILTER_CR_CALLBACK, 'cbSpellRewards', null, null], // spellrewarded [yn]
5 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_SHARABLE ], // sharable
6 => [FILTER_CR_NUMERIC, 'timeLimit', NUM_CAST_INT ], // timer
7 => [FILTER_CR_NYI_PH, null, 1 ], // firstquestseries
9 => [FILTER_CR_CALLBACK, 'cbEarnReputation', null, null], // objectiveearnrepwith [enum]
10 => [FILTER_CR_CALLBACK, 'cbReputation', '<', null], // decreasesrepwith
11 => [FILTER_CR_NUMERIC, 'suggestedPlayers', NUM_CAST_INT ], // suggestedplayers
15 => [FILTER_CR_NYI_PH, null, 1 ], // lastquestseries
16 => [FILTER_CR_NYI_PH, null, 1 ], // partseries
18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
19 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 0x1, null], // startsfrom [enum]
21 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 0x2, null], // endsat [enum]
22 => [FILTER_CR_CALLBACK, 'cbItemRewards', null, null], // itemrewards [op] [int]
23 => [FILTER_CR_CALLBACK, 'cbItemChoices', null, null], // itemchoices [op] [int]
24 => [FILTER_CR_CALLBACK, 'cbLacksStartEnd', null, null], // lacksstartend [yn]
25 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
27 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_DAILY ], // daily 27 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_DAILY ], // daily
28 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_WEEKLY ], // weekly 28 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_WEEKLY ], // weekly
29 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_REPEATABLE ], // repeatable 29 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_REPEATABLE ], // repeatable
30 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id 30 => [FILTER_CR_NUMERIC, 'id', null, true], // id
33 => [FILTER_CR_ENUM, 'e.holidayId' ], // relatedevent 5 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_SHARABLE ], // sharable
34 => [FILTER_CR_CALLBACK, 'cbAvailable', null, null], // availabletoplayers [yn] 11 => [FILTER_CR_NUMERIC, 'suggestedPlayers', ], // suggestedplayers
6 => [FILTER_CR_NUMERIC, 'timeLimit', ], // timer
42 => [FILTER_CR_STAFFFLAG, 'flags', ], // flags
45 => [FILTER_CR_BOOLEAN, 'rewardTitleId', ], // titlerewarded
2 => [FILTER_CR_NUMERIC, 'rewardXP', ], // experiencegained
3 => [FILTER_CR_NUMERIC, 'rewardOrReqMoney', ], // moneyrewarded
33 => [FILTER_CR_ENUM, 'holidayId', ], // relatedevent
25 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
36 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos 36 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
37 => [FILTER_CR_CALLBACK, 'cbClassSpec', null, null], // classspecific [enum]
38 => [FILTER_CR_CALLBACK, 'cbRaceSpec', null, null], // racespecific [enum]
42 => [FILTER_CR_STAFFFLAG, 'flags' ], // flags
43 => [FILTER_CR_CALLBACK, 'cbCurrencyReward', null, null], // currencyrewarded [enum]
44 => [FILTER_CR_CALLBACK, 'cbLoremaster', null, null], // countsforloremaster_stc [yn]
45 => [FILTER_CR_BOOLEAN, 'rewardTitleId' ] // titlerewarded
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_RANGE, [1, 45], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 99999]], true ], // criteria operators
'crv' => [FILTER_V_REGEX, '/\D/', true ], // criteria values - only numerals
'na' => [FILTER_V_REGEX, '/[\p{C};%\\\\]/ui', false], // name / text - only printable chars, no delimiter
'ex' => [FILTER_V_EQUAL, 'on', false], // also match subname
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'minle' => [FILTER_V_RANGE, [1, 99], false], // min quest level
'maxle' => [FILTER_V_RANGE, [1, 99], false], // max quest level
'minrl' => [FILTER_V_RANGE, [1, 99], false], // min required level
'maxrl' => [FILTER_V_RANGE, [1, 99], false], // max required level
'si' => [FILTER_V_LIST, [-2, -1, 1, 2, 3], false], // siede
'ty' => [FILTER_V_LIST, [0, 1, 21, 41, 62, [81, 85], 88, 89], true ] // type
); );
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCr = $this->genericCriterion($cr)) if ($genCr = $this->genericCriterion($cr))
return $genCr; return $genCr;
unset($cr);
$this->error = true;
return [1];
}
switch ($cr[0])
{
case 1: // increasesrepwith
if ($this->isSaneNumeric($cr[1]) && $cr[1] > 0)
{
return [
'OR',
['AND', ['rewardFactionId1', $cr[1]], ['rewardFactionValue1', 0, '>']],
['AND', ['rewardFactionId2', $cr[1]], ['rewardFactionValue2', 0, '>']],
['AND', ['rewardFactionId3', $cr[1]], ['rewardFactionValue3', 0, '>']],
['AND', ['rewardFactionId4', $cr[1]], ['rewardFactionValue4', 0, '>']],
['AND', ['rewardFactionId5', $cr[1]], ['rewardFactionValue5', 0, '>']]
];
}
break;
case 10: // decreasesrepwith
if ($this->isSaneNumeric($cr[1]) && $cr[1] > 0)
{
return [
'OR',
['AND', ['rewardFactionId1', $cr[1]], ['rewardFactionValue1', 0, '<']],
['AND', ['rewardFactionId2', $cr[1]], ['rewardFactionValue2', 0, '<']],
['AND', ['rewardFactionId3', $cr[1]], ['rewardFactionValue3', 0, '<']],
['AND', ['rewardFactionId4', $cr[1]], ['rewardFactionValue4', 0, '<']],
['AND', ['rewardFactionId5', $cr[1]], ['rewardFactionValue5', 0, '<']]
];
}
break;
case 43: // currencyrewarded
if ($this->isSaneNumeric($cr[1]) && $cr[1] > 0)
{
return [
'OR',
['rewardItemId1', $cr[1]], ['rewardItemId2', $cr[1]], ['rewardItemId3', $cr[1]], ['rewardItemId4', $cr[1]],
['rewardChoiceItemId1', $cr[1]], ['rewardChoiceItemId2', $cr[1]], ['rewardChoiceItemId3', $cr[1]], ['rewardChoiceItemId4', $cr[1]], ['rewardChoiceItemId5', $cr[1]], ['rewardChoiceItemId6', $cr[1]]
];
}
break;
case 34: // availabletoplayers
if ($this->int2Bool($cr[1]))
{
if ($cr[1])
return ['AND', [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0], [['flags', QUEST_FLAG_UNAVAILABLE, '&'], 0]];
else
return ['OR', ['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], ['flags', QUEST_FLAG_UNAVAILABLE, '&']];
}
break;
case 23: // itemchoices [op] [int]
if (!$this->isSaneNumeric($cr[2], false) || !$this->int2Op($cr[1]))
break;
$this->extraOpts['q']['s'][] = ', (IF(rewardChoiceItemId1, 1, 0) + IF(rewardChoiceItemId2, 1, 0) + IF(rewardChoiceItemId3, 1, 0) + IF(rewardChoiceItemId4, 1, 0) + IF(rewardChoiceItemId5, 1, 0) + IF(rewardChoiceItemId6, 1, 0)) as numChoices';
$this->extraOpts['q']['h'][] = 'numChoices '.$cr[1].' '.$cr[2];
return [1];
case 22: // itemrewards [op] [int]
if (!$this->isSaneNumeric($cr[2], false) || !$this->int2Op($cr[1]))
break;
$this->extraOpts['q']['s'][] = ', (IF(rewardItemId1, 1, 0) + IF(rewardItemId2, 1, 0) + IF(rewardItemId3, 1, 0) + IF(rewardItemId4, 1, 0)) as numRewards';
$this->extraOpts['q']['h'][] = 'numRewards '.$cr[1].' '.$cr[2];
return [1];
case 44: // countsforloremaster_stc [bool]
if ($this->int2Bool($cr[1]))
{
if ($cr[1])
return ['AND', ['zoneOrSort', 0, '>'], [['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE , '&'], 0], [['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY , '&'], 0]];
else
return ['OR', ['zoneOrSort', 0, '<'], ['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE , '&'], ['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY , '&']];;
}
break;
case 4: // spellrewarded [bool]
if ($this->int2Bool($cr[1]))
{
if ($cr[1])
return ['OR', ['sourceSpellId', 0, '>'], ['rewardSpell', 0, '>'], ['rsc.effect1Id', SpellList::$effects['teach']], ['rsc.effect2Id', SpellList::$effects['teach']], ['rsc.effect3Id', SpellList::$effects['teach']]];
else
return ['AND', ['sourceSpellId', 0], ['rewardSpell', 0], ['rewardSpellCast', 0]];
}
break;
case 9: // objectiveearnrepwith [enum]
$_ = intVal($cr[1]);
if ($_ > 0)
return ['OR', ['reqFactionId1', $_], ['reqFactionId2', $_]];
else if ($cr[1] == FILTER_ENUM_ANY) // any
return ['OR', ['reqFactionId1', 0, '>'], ['reqFactionId2', 0, '>']];
else if ($cr[1] == FILTER_ENUM_NONE) // none
return ['AND', ['reqFactionId1', 0], ['reqFactionId2', 0]];
break;
case 37: // classspecific [enum]
$_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null;
if ($_ !== null)
{
if ($_ === true)
return ['AND', ['reqClassMask', 0, '!'], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!']];
else if ($_ === false)
return ['OR', ['reqClassMask', 0], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL]];
else if (is_int($_))
return ['AND', ['reqClassMask', (1 << ($_ - 1)), '&'], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!']];
}
break;
case 38: // racespecific [enum]
$_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null;
if ($_ !== null)
{
if ($_ === true)
return ['AND', ['reqRaceMask', 0, '!'], [['reqRaceMask', RACE_MASK_ALL, '&'], RACE_MASK_ALL, '!'], [['reqRaceMask', RACE_MASK_ALLIANCE, '&'], RACE_MASK_ALLIANCE, '!'], [['reqRaceMask', RACE_MASK_HORDE, '&'], RACE_MASK_HORDE, '!']];
else if ($_ === false)
return ['OR', ['reqRaceMask', 0], ['reqRaceMask', RACE_MASK_ALL], ['reqRaceMask', RACE_MASK_ALLIANCE], ['reqRaceMask', RACE_MASK_HORDE]];
else if (is_int($_))
return ['AND', ['reqRaceMask', (1 << ($_ - 1)), '&'], [['reqRaceMask', RACE_MASK_ALLIANCE, '&'], RACE_MASK_ALLIANCE, '!'], [['reqRaceMask', RACE_MASK_HORDE, '&'], RACE_MASK_HORDE, '!']];
}
break;
case 19: // startsfrom [enum]
switch ($cr[1])
{
case 1: // npc
return ['AND', ['qse.type', TYPE_NPC], ['qse.method', 0x1, '&']];
case 2: // object
return ['AND', ['qse.type', TYPE_OBJECT], ['qse.method', 0x1, '&']];
case 3: // item
return ['AND', ['qse.type', TYPE_ITEM], ['qse.method', 0x1, '&']];
}
break;
case 21: // endsat [enum]
switch ($cr[1])
{
case 1: // npc
return ['AND', ['qse.type', TYPE_NPC], ['qse.method', 0x2, '&']];
case 2: // object
return ['AND', ['qse.type', TYPE_OBJECT], ['qse.method', 0x2, '&']];
}
break;
case 24: // lacksstartend [bool]
$missing = DB::Aowow()->selectCol('SELECT questId, max(method) a, min(method) b FROM ?_quests_startend GROUP BY questId HAVING (a | b) <> 3');
if ($this->int2Bool($cr[1]))
{
if ($cr[1])
return ['id', $missing];
else
return ['id', $missing, '!'];
}
break;
case 7: // firstquestseries
case 15: // lastquestseries
case 16: // partseries
/* todo */ return [1]; // self-joining eats substential amounts of time: should restructure that and also incorporate reqQ and openQ cases from infobox
default:
break;
}
unset($cr); unset($cr);
$this->error = true; $this->error = 1;
return [1]; return [1];
} }
@@ -511,19 +628,39 @@ class QuestListFilter extends Filter
// level min // level min
if (isset($_v['minle'])) if (isset($_v['minle']))
$parts[] = ['level', $_v['minle'], '>=']; // not considering quests that are always at player level (-1) {
if (is_int($_v['minle']) && $_v['minle'] > 0)
$parts[] = ['level', $_v['minle'], '>=']; // not considering quests that are always at player level (-1)
else
unset($_v['minle']);
}
// level max // level max
if (isset($_v['maxle'])) if (isset($_v['maxle']))
$parts[] = ['level', $_v['maxle'], '<=']; {
if (is_int($_v['maxle']) && $_v['maxle'] > 0)
$parts[] = ['level', $_v['maxle'], '<='];
else
unset($_v['maxle']);
}
// reqLevel min // reqLevel min
if (isset($_v['minrl'])) if (isset($_v['minrl']))
$parts[] = ['minLevel', $_v['minrl'], '>=']; // ignoring maxLevel {
if (is_int($_v['minrl']) && $_v['minrl'] > 0)
$parts[] = ['minLevel', $_v['minrl'], '>='];// ignoring maxLevel
else
unset($_v['minrl']);
}
// reqLevel max // reqLevel max
if (isset($_v['maxrl'])) if (isset($_v['maxrl']))
$parts[] = ['minLevel', $_v['maxrl'], '<=']; // ignoring maxLevel {
if (is_int($_v['maxrl']) && $_v['maxrl'] > 0)
$parts[] = ['minLevel', $_v['maxrl'], '<='];// ignoring maxLevel
else
unset($_v['maxrl']);
}
// side // side
if (isset($_v['si'])) if (isset($_v['si']))
@@ -548,171 +685,22 @@ class QuestListFilter extends Filter
case -1: case -1:
$parts[] = ['AND', $ex, ['reqRaceMask', RACE_MASK_ALLIANCE, '&']]; $parts[] = ['AND', $ex, ['reqRaceMask', RACE_MASK_ALLIANCE, '&']];
break; break;
default:
unset($_v['si']);
} }
} }
// type [list] // type [list]
if (isset($_v['ty'])) if (isset($_v['ty']))
$parts[] = ['type', $_v['ty']];
return $parts;
}
protected function cbReputation($cr, $sign)
{
if (!Util::checkNumeric($cr[1], NUM_REQ_INT) || $cr[1] <= 0)
return false;
if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE id = ?d', $cr[1]))
$this->formData['reputationCols'][] = [$cr[1], Util::localizedString($_, 'name')];
return [
'OR',
['AND', ['rewardFactionId1', $cr[1]], ['rewardFactionValue1', 0, $sign]],
['AND', ['rewardFactionId2', $cr[1]], ['rewardFactionValue2', 0, $sign]],
['AND', ['rewardFactionId3', $cr[1]], ['rewardFactionValue3', 0, $sign]],
['AND', ['rewardFactionId4', $cr[1]], ['rewardFactionValue4', 0, $sign]],
['AND', ['rewardFactionId5', $cr[1]], ['rewardFactionValue5', 0, $sign]]
];
}
protected function cbQuestRelation($cr, $flags)
{
switch ($cr[1])
{ {
case 1: // npc $_ = (array)$_v['ty'];
return ['AND', ['qse.type', Type::NPC], ['qse.method', $flags, '&']]; if (!array_diff($_, [0, 1, 21, 41, 62, 81, 82, 83, 84, 85, 88, 89]))
case 2: // object $parts[] = ['type', $_];
return ['AND', ['qse.type', Type::OBJECT], ['qse.method', $flags, '&']]; else
case 3: // item unset($_v['ty']);
return ['AND', ['qse.type', Type::ITEM], ['qse.method', $flags, '&']];
} }
return false; return $parts;
}
protected function cbCurrencyReward($cr)
{
if (!Util::checkNumeric($cr[1], NUM_REQ_INT) || $cr[1] <= 0)
return false;
return [
'OR',
['rewardItemId1', $cr[1]], ['rewardItemId2', $cr[1]], ['rewardItemId3', $cr[1]], ['rewardItemId4', $cr[1]],
['rewardChoiceItemId1', $cr[1]], ['rewardChoiceItemId2', $cr[1]], ['rewardChoiceItemId3', $cr[1]], ['rewardChoiceItemId4', $cr[1]], ['rewardChoiceItemId5', $cr[1]], ['rewardChoiceItemId6', $cr[1]]
];
}
protected function cbAvailable($cr)
{
if (!$this->int2Bool($cr[1]))
return false;
if ($cr[1])
return [['cuFlags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'], 0];
else
return ['cuFlags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'];
}
protected function cbItemChoices($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
$this->extraOpts['q']['s'][] = ', (IF(rewardChoiceItemId1, 1, 0) + IF(rewardChoiceItemId2, 1, 0) + IF(rewardChoiceItemId3, 1, 0) + IF(rewardChoiceItemId4, 1, 0) + IF(rewardChoiceItemId5, 1, 0) + IF(rewardChoiceItemId6, 1, 0)) as numChoices';
$this->extraOpts['q']['h'][] = 'numChoices '.$cr[1].' '.$cr[2];
return [1];
}
protected function cbItemRewards($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
$this->extraOpts['q']['s'][] = ', (IF(rewardItemId1, 1, 0) + IF(rewardItemId2, 1, 0) + IF(rewardItemId3, 1, 0) + IF(rewardItemId4, 1, 0)) as numRewards';
$this->extraOpts['q']['h'][] = 'numRewards '.$cr[1].' '.$cr[2];
return [1];
}
protected function cbLoremaster($cr)
{
if (!$this->int2Bool($cr[1]))
return false;
if ($cr[1])
return ['AND', ['zoneOrSort', 0, '>'], [['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE , '&'], 0], [['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY , '&'], 0]];
else
return ['OR', ['zoneOrSort', 0, '<'], ['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE , '&'], ['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY , '&']];;
}
protected function cbSpellRewards($cr)
{
if (!$this->int2Bool($cr[1]))
return false;
if ($cr[1])
return ['OR', ['sourceSpellId', 0, '>'], ['rewardSpell', 0, '>'], ['rsc.effect1Id', SpellList::$effects['teach']], ['rsc.effect2Id', SpellList::$effects['teach']], ['rsc.effect3Id', SpellList::$effects['teach']]];
else
return ['AND', ['sourceSpellId', 0], ['rewardSpell', 0], ['rewardSpellCast', 0]];
}
protected function cbEarnReputation($cr)
{
if (!Util::checkNumeric($cr[1], NUM_REQ_INT))
return false;
if ($cr[1] > 0)
return ['OR', ['reqFactionId1', $cr[1]], ['reqFactionId2', $cr[1]]];
else if ($cr[1] == FILTER_ENUM_ANY) // any
return ['OR', ['reqFactionId1', 0, '>'], ['reqFactionId2', 0, '>']];
else if ($cr[1] == FILTER_ENUM_NONE) // none
return ['AND', ['reqFactionId1', 0], ['reqFactionId2', 0]];
return false;
}
protected function cbClassSpec($cr)
{
if (!isset($this->enums[$cr[0]][$cr[1]]))
return false;
$_ = $this->enums[$cr[0]][$cr[1]];
if ($_ === true)
return ['AND', ['reqClassMask', 0, '!'], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!']];
else if ($_ === false)
return ['OR', ['reqClassMask', 0], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL]];
else if (is_int($_))
return ['AND', ['reqClassMask', (1 << ($_ - 1)), '&'], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!']];
return false;
}
protected function cbRaceSpec($cr)
{
if (!isset($this->enums[$cr[0]][$cr[1]]))
return false;
$_ = $this->enums[$cr[0]][$cr[1]];
if ($_ === true)
return ['AND', ['reqRaceMask', 0, '!'], [['reqRaceMask', RACE_MASK_ALL, '&'], RACE_MASK_ALL, '!'], [['reqRaceMask', RACE_MASK_ALLIANCE, '&'], RACE_MASK_ALLIANCE, '!'], [['reqRaceMask', RACE_MASK_HORDE, '&'], RACE_MASK_HORDE, '!']];
else if ($_ === false)
return ['OR', ['reqRaceMask', 0], ['reqRaceMask', RACE_MASK_ALL], ['reqRaceMask', RACE_MASK_ALLIANCE], ['reqRaceMask', RACE_MASK_HORDE]];
else if (is_int($_))
return ['AND', ['reqRaceMask', (1 << ($_ - 1)), '&'], [['reqRaceMask', RACE_MASK_ALLIANCE, '&'], RACE_MASK_ALLIANCE, '!'], [['reqRaceMask', RACE_MASK_HORDE, '&'], RACE_MASK_HORDE, '!']];
return false;
}
protected function cbLacksStartEnd($cr)
{
if (!$this->int2Bool($cr[1]))
return false;
$missing = DB::Aowow()->selectCol('SELECT questId, max(method) a, min(method) b FROM ?_quests_startend GROUP BY questId HAVING (a | b) <> 3');
if ($cr[1])
return ['id', $missing];
else
return ['id', $missing, '!'];
} }
} }

View File

@@ -6,14 +6,13 @@ if (!defined('AOWOW_REVISION'))
class SkillList extends BaseType class SkillList extends BaseType
{ {
public static $type = Type::SKILL; public static $type = TYPE_SKILL;
public static $brickFile = 'skill'; public static $brickFile = 'skill';
public static $dataTable = '?_skillline';
protected $queryBase = 'SELECT sl.*, sl.id AS ARRAY_KEY FROM ?_skillline sl'; protected $queryBase = 'SELECT *, sl.id AS ARRAY_KEY FROM ?_skillline sl';
protected $queryOpts = array( protected $queryOpts = array(
'sl' => [['ic']], 'sl' => [['si']],
'ic' => ['j' => ['?_icons ic ON ic.id = sl.iconId', true], 's' => ', ic.name AS iconString'], 'si' => ['j' => '?_icons si ON si.id = sl.iconId', 's' => ', si.iconString'],
); );
public function __construct($conditions = []) public function __construct($conditions = [])
@@ -32,15 +31,12 @@ class SkillList extends BaseType
while (count($_) < 5) while (count($_) < 5)
$_[] = 0; $_[] = 0;
} }
if (!$_curTpl['iconId'])
$_curTpl['iconString'] = 'inv_misc_questionmark';
} }
} }
public static function getName($id) public static function getName($id)
{ {
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_skillline WHERE id = ?d', $id); $n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_skillline WHERE id = ?d', $id);
return Util::localizedString($n, 'name'); return Util::localizedString($n, 'name');
} }
@@ -54,11 +50,11 @@ class SkillList extends BaseType
'category' => $this->curTpl['typeCat'], 'category' => $this->curTpl['typeCat'],
'categorybak' => $this->curTpl['categoryId'], 'categorybak' => $this->curTpl['categoryId'],
'id' => $this->id, 'id' => $this->id,
'name' => $this->getField('name', true), 'name' => Util::jsEscape($this->getField('name', true)),
'profession' => $this->curTpl['professionMask'], 'profession' => $this->curTpl['professionMask'],
'recipeSubclass' => $this->curTpl['recipeSubClass'], 'recipeSubclass' => $this->curTpl['recipeSubClass'],
'specializations' => Util::toJSON($this->curTpl['specializations'], JSON_NUMERIC_CHECK), 'specializations' => Util::toJSON($this->curTpl['specializations']),
'icon' => $this->curTpl['iconString'] 'icon' => Util::jsEscape($this->curTpl['iconString'])
); );
} }
@@ -70,7 +66,7 @@ class SkillList extends BaseType
$data = []; $data = [];
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
$data[self::$type][$this->id] = ['name' => $this->getField('name', true), 'icon' => $this->curTpl['iconString']]; $data[self::$type][$this->id] = ['name' => Util::jsEscape($this->getField('name', true)), 'icon' => Util::jsEscape($this->curTpl['iconString'])];
return $data; return $data;
} }

View File

@@ -1,131 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class SoundList extends BaseType
{
use spawnHelper;
public static $type = Type::SOUND;
public static $brickFile = 'sound';
public static $dataTable = '?_sounds';
public static $contribute = CONTRIBUTE_CO;
protected $queryBase = 'SELECT s.*, s.id AS ARRAY_KEY FROM ?_sounds s';
private $fileBuffer = [];
private static $fileTypes = array(
SOUND_TYPE_OGG => 'audio/ogg; codecs="vorbis"',
SOUND_TYPE_MP3 => 'audio/mpeg'
);
public function __construct($conditions = [])
{
parent::__construct($conditions);
// post processing
foreach ($this->iterate() as $id => &$_curTpl)
{
$_curTpl['files'] = [];
for ($i = 1; $i < 11; $i++)
{
if ($_curTpl['soundFile'.$i])
{
$this->fileBuffer[$_curTpl['soundFile'.$i]] = null;
$_curTpl['files'][] = &$this->fileBuffer[$_curTpl['soundFile'.$i]];
}
unset($_curTpl['soundFile'.$i]);
}
}
if ($this->fileBuffer)
{
$files = DB::Aowow()->select('SELECT id AS ARRAY_KEY, `id`, `file` AS title, `type`, `path` FROM ?_sounds_files sf WHERE id IN (?a)', array_keys($this->fileBuffer));
foreach ($files as $id => $data)
{
// 3.3.5 bandaid - need fullpath to play via wow API, remove for cata and later
$data['path'] = str_replace('\\', '\\\\', $data['path'] ? $data['path'] . '\\' . $data['title'] : $data['title']);
// skipp file extension
$data['title'] = substr($data['title'], 0, -4);
// enum to string
$data['type'] = self::$fileTypes[$data['type']];
// get real url
$data['url'] = STATIC_URL . '/wowsounds/' . $data['id'];
// v push v
$this->fileBuffer[$id] = $data;
}
}
}
public function getListviewData()
{
$data = [];
foreach ($this->iterate() as $__)
{
$data[$this->id] = array(
'id' => $this->id,
'type' => $this->getField('cat'),
'name' => $this->getField('name'),
'files' => array_values(array_filter($this->getField('files')))
);
}
return $data;
}
public function getJSGlobals($addMask = 0)
{
$data = [];
foreach ($this->iterate() as $__)
$data[self::$type][$this->id] = array(
'name' => $this->getField('name', true),
'type' => $this->getField('cat'),
'files' => array_values(array_filter($this->getField('files')))
);
return $data;
}
public function renderTooltip() { }
}
class SoundListFilter extends Filter
{
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'na' => [FILTER_V_REGEX, '/[\p{C};%\\\\]/ui', false], // name - only printable chars, no delimiter
'ty' => [FILTER_V_LIST, [[1, 4], 6, 9, 10, 12, 13, 14, 16, 17, [19, 23], [25, 31], 50, 52, 53], true ] // type
);
// we have no criteria for this one...
protected function createSQLForCriterium(&$cr)
{
unset($cr);
$this->error = true;
return [1];
}
protected function createSQLForValues()
{
$parts = [];
$_v = &$this->fiData['v'];
// name [str]
if (isset($_v['na']))
if ($_ = $this->modularizeString(['name']))
$parts[] = $_;
// type [list]
if (isset($_v['ty']))
$parts[] = ['cat', $_v['ty']];
return $parts;
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -8,17 +8,16 @@ class TitleList extends BaseType
{ {
use listviewHelper; use listviewHelper;
public static $type = Type::TITLE; public static $type = TYPE_TITLE;
public static $brickFile = 'title'; public static $brickFile = 'title';
public static $dataTable = '?_titles';
public $sources = []; public $sources = [];
protected $queryBase = 'SELECT t.*, id AS ARRAY_KEY FROM ?_titles t'; protected $queryBase = 'SELECT t.*, id AS ARRAY_KEY FROM ?_titles t';
protected $queryOpts = array( protected $queryOpts = array(
't' => [['src']], // 11: Type::TITLE 't' => [['src']], // 11: TYPE_TITLE
'src' => ['j' => ['?_source src ON type = 11 AND typeId = t.id', true], 's' => ', src13, moreType, moreTypeId'] 'src' => ['j' => ['?_source src ON type = 11 AND typeId = t.id', true], 's' => ', src13, moreType, moreTypeId']
); );
public function __construct($conditions = []) public function __construct($conditions = [])
{ {
@@ -28,9 +27,9 @@ class TitleList extends BaseType
foreach ($this->iterate() as $id => &$_curTpl) foreach ($this->iterate() as $id => &$_curTpl)
{ {
// preparse sources - notice: under this system titles can't have more than one source (or two for achivements), which is enough for standard TC cases but may break custom cases // preparse sources - notice: under this system titles can't have more than one source (or two for achivements), which is enough for standard TC cases but may break custom cases
if ($_curTpl['moreType'] == Type::ACHIEVEMENT) if ($_curTpl['moreType'] == TYPE_ACHIEVEMENT)
$this->sources[$this->id][12][] = $_curTpl['moreTypeId']; $this->sources[$this->id][12][] = $_curTpl['moreTypeId'];
else if ($_curTpl['moreType'] == Type::QUEST) else if ($_curTpl['moreType'] == TYPE_QUEST)
$this->sources[$this->id][4][] = $_curTpl['moreTypeId']; $this->sources[$this->id][4][] = $_curTpl['moreTypeId'];
else if ($_curTpl['src13']) else if ($_curTpl['src13'])
$this->sources[$this->id][13][] = $_curTpl['src13']; $this->sources[$this->id][13][] = $_curTpl['src13'];
@@ -81,10 +80,10 @@ class TitleList extends BaseType
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
{ {
$data[Type::TITLE][$this->id]['name'] = $this->getField('male', true); $data[TYPE_TITLE][$this->id]['name'] = $this->getField('male', true);
if ($_ = $this->getField('female', true)) if ($_ = $this->getField('female', true))
$data[Type::TITLE][$this->id]['namefemale'] = $_; $data[TYPE_TITLE][$this->id]['namefemale'] = $_;
} }
return $data; return $data;
@@ -115,6 +114,9 @@ class TitleList extends BaseType
if (!empty($sources[12])) if (!empty($sources[12]))
$sources[12] = (new AchievementList(array(['id', $sources[12]])))->getSourceData(); $sources[12] = (new AchievementList(array(['id', $sources[12]])))->getSourceData();
if (!empty($sources[13]))
$sources[13] = DB::Aowow()->SELECT('SELECT *, Id AS ARRAY_KEY FROM ?_sourcestrings WHERE Id IN (?a)', $sources[13]);
foreach ($this->sources as $Id => $src) foreach ($this->sources as $Id => $src)
{ {
$tmp = []; $tmp = [];
@@ -145,7 +147,7 @@ class TitleList extends BaseType
// other source (only one item possible, so no iteration needed) // other source (only one item possible, so no iteration needed)
if (isset($src[13])) if (isset($src[13]))
$tmp[13] = [Lang::game('pvpSources', $this->sources[$Id][13][0])]; $tmp[13] = [Util::localizedString($sources[13][$this->sources[$Id][13][0]], 'source')];
$this->templates[$Id]['source'] = $tmp; $this->templates[$Id]['source'] = $tmp;
} }

View File

@@ -6,17 +6,16 @@ if (!defined('AOWOW_REVISION'))
class UserList extends BaseType class UserList extends BaseType
{ {
public static $type = Type::USER; public static $type = TYPE_USER;
public static $brickFile = 'user'; public static $brickFile = 'user';
public static $dataTable = ''; // doesn't have community content
public $sources = []; public $sources = [];
protected $queryBase = 'SELECT *, a.id AS ARRAY_KEY FROM ?_account a'; protected $queryBase = 'SELECT *, a.id AS ARRAY_KEY FROM ?_account a';
protected $queryOpts = array( protected $queryOpts = array(
'a' => [['r']], 'a' => [['r']],
'r' => ['j' => ['?_account_reputation r ON r.userId = a.id', true], 's' => ', IFNULL(SUM(r.amount), 0) AS reputation', 'g' => 'a.id'] 'r' => ['j' => ['?_account_reputation r ON r.userId = a.id', true], 's' => ', IFNULL(SUM(r.amount), 0) AS reputation', 'g' => 'a.id']
); );
public function getListviewData() { } public function getListviewData() { }
@@ -52,7 +51,7 @@ class UserList extends BaseType
// border: seen as null|1|3 .. changes the border around the avatar (i suspect its meaning changed and got decupled from premium-status with the introduction of patron-status) // border: seen as null|1|3 .. changes the border around the avatar (i suspect its meaning changed and got decupled from premium-status with the introduction of patron-status)
} }
return [Type::USER => $data]; return [TYPE_USER => $data];
} }
public function renderTooltip() { } public function renderTooltip() { }

View File

@@ -6,15 +6,14 @@ if (!defined('AOWOW_REVISION'))
class WorldEventList extends BaseType class WorldEventList extends BaseType
{ {
public static $type = Type::WORLDEVENT; public static $type = TYPE_WORLDEVENT;
public static $brickFile = 'event'; public static $brickFile = 'event';
public static $dataTable = '?_events';
protected $queryBase = 'SELECT e.*, h.*, e.description AS nameINT, e.id AS id, e.id AS ARRAY_KEY FROM ?_events e'; protected $queryBase = 'SELECT *, -e.id as id, -e.id AS ARRAY_KEY FROM ?_events e';
protected $queryOpts = array( protected $queryOpts = array(
'e' => [['h']], 'e' => [['h']],
'h' => ['j' => ['?_holidays h ON e.holidayId = h.id', true], 'o' => '-e.id ASC'] 'h' => ['j' => ['?_holidays h ON e.holidayId = h.id', true], 'o' => '-e.id ASC']
); );
public function __construct($conditions = []) public function __construct($conditions = [])
{ {
@@ -41,18 +40,22 @@ class WorldEventList extends BaseType
if ($this->curTpl['requires']) if ($this->curTpl['requires'])
$this->curTpl['requires'] = explode(' ', $this->curTpl['requires']); $this->curTpl['requires'] = explode(' ', $this->curTpl['requires']);
$this->curTpl['eventBak'] = -$this->curTpl['id'];
// change Ids if holiday is set // change Ids if holiday is set
if ($this->curTpl['holidayId'] > 0) if ($this->curTpl['holidayId'] > 0)
{ {
$this->curTpl['id'] = $this->curTpl['holidayId'];
$this->curTpl['name'] = $this->getField('name', true); $this->curTpl['name'] = $this->getField('name', true);
$replace[$this->id] = $this->curTpl; $replace[$this->id] = $this->curTpl;
unset($this->curTpl['description']);
} }
else // set a name if holiday is missing else // set a name if holiday is missing
{ {
// template // template
$this->curTpl['name_loc0'] = $this->curTpl['nameINT']; $this->curTpl['name_loc0'] = $this->curTpl['description'];
$this->curTpl['iconString'] = 'trade_engineering'; $this->curTpl['iconString'] = 'trade_engineering';
$this->curTpl['name'] = '(SERVERSIDE) '.$this->getField('nameINT', true); $this->curTpl['name'] = '(SERVERSIDE) '.$this->getField('description', true);
$replace[$this->id] = $this->curTpl; $replace[$this->id] = $this->curTpl;
} }
} }
@@ -66,22 +69,10 @@ class WorldEventList extends BaseType
public static function getName($id) public static function getName($id)
{ {
$row = DB::Aowow()->SelectRow(' if ($id > 0)
SELECT $row = DB::Aowow()->SelectRow('SELECT * FROM ?_holidays WHERE Id = ?d', intVal($id));
IFNULL(h.name_loc0, e.description) AS name_loc0, else
h.name_loc2, $row = DB::Aowow()->SelectRow('SELECT description as name FROM ?_events WHERE Id = ?d', intVal(-$id));
h.name_loc3,
h.name_loc4,
h.name_loc6,
h.name_loc8
FROM
?_events e
LEFT JOIN
?_holidays h ON e.holidayId = h.id
WHERE
e.id = ?d',
$id
);
return Util::localizedString($row, 'name'); return Util::localizedString($row, 'name');
} }
@@ -162,36 +153,12 @@ class WorldEventList extends BaseType
$data = []; $data = [];
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
$data[Type::WORLDEVENT][$this->id] = ['name' => $this->getField('name', true), 'icon' => $this->curTpl['iconString']]; $data[TYPE_WORLDEVENT][$this->id] = ['name' => $this->getField('name', true), 'icon' => $this->curTpl['iconString']];
return $data; return $data;
} }
public function renderTooltip() public function renderTooltip() { }
{
if (!$this->curTpl)
return null;
$x = '<table><tr><td>';
// head v that extra % is nesecary because we are using sprintf later on
$x .= '<table width="100%%"><tr><td><b>'.$this->getField('name', true).'</b></td><th><b class="q0">'.Lang::event('category', $this->getField('category')).'</b></th></tr></table>';
// use string-placeholder for dates
// start
$x .= Lang::event('start').Lang::main('colon').'%s<br>';
// end
$x .= Lang::event('end').Lang::main('colon').'%s';
$x .= '</td></tr></table>';
// desc
if ($this->getField('holidayId'))
if ($_ = $this->getField('description', true))
$x .= '<table><tr><td><span class="q">'.$_.'</span></td></tr></table>';
return $x;
}
} }
?> ?>

View File

@@ -8,11 +8,10 @@ class ZoneList extends BaseType
{ {
use listviewHelper; use listviewHelper;
public static $type = Type::ZONE; public static $type = TYPE_ZONE;
public static $brickFile = 'zone'; public static $brickFile = 'zone';
public static $dataTable = '?_zones';
protected $queryBase = 'SELECT z.*, id AS ARRAY_KEY FROM ?_zones z'; protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_zones z';
public function __construct($conditions = [], $miscData = null) public function __construct($conditions = [], $miscData = null)
{ {
@@ -54,7 +53,7 @@ class ZoneList extends BaseType
// use if you JUST need the name // use if you JUST need the name
public static function getName($id) public static function getName($id)
{ {
$n = DB::Aowow()->selectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_zones WHERE id = ?d', $id ); $n = DB::Aowow()->selectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_zones WHERE id = ?d', $id );
return Util::localizedString($n, 'name'); return Util::localizedString($n, 'name');
} }
@@ -100,7 +99,7 @@ class ZoneList extends BaseType
$data = []; $data = [];
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
$data[Type::ZONE][$this->id] = ['name' => $this->getField('name', true)]; $data[TYPE_ZONE][$this->id] = ['name' => $this->getField('name', true)];
return $data; return $data;
} }

View File

@@ -17,12 +17,10 @@ class User
public static $dailyVotes = 0; public static $dailyVotes = 0;
public static $ip = null; public static $ip = null;
private static $reputation = 0; private static $reputation = 0;
private static $dataKey = ''; private static $dataKey = '';
private static $expires = false; private static $expires = false;
private static $passHash = ''; private static $passHash = '';
private static $excludeGroups = 1;
private static $profiles = null;
public static function init() public static function init()
{ {
@@ -41,7 +39,7 @@ class User
// check IP bans // check IP bans
if ($ipBan = DB::Aowow()->selectRow('SELECT count, unbanDate FROM ?_account_bannedips WHERE ip = ? AND type = 0', self::$ip)) if ($ipBan = DB::Aowow()->selectRow('SELECT count, unbanDate FROM ?_account_bannedips WHERE ip = ? AND type = 0', self::$ip))
{ {
if ($ipBan['count'] > CFG_ACC_FAILED_AUTH_COUNT && $ipBan['unbanDate'] > time()) if ($ipBan['count'] > CFG_FAILED_AUTH_COUNT && $ipBan['unbanDate'] > time())
return false; return false;
else if ($ipBan['unbanDate'] <= time()) else if ($ipBan['unbanDate'] <= time())
DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE ip = ?', self::$ip); DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE ip = ?', self::$ip);
@@ -56,7 +54,7 @@ class User
return false; return false;
$query = DB::Aowow()->SelectRow(' $query = DB::Aowow()->SelectRow('
SELECT a.id, a.passHash, a.displayName, a.locale, a.userGroups, a.userPerms, a.allowExpire, BIT_OR(ab.typeMask) AS bans, IFNULL(SUM(r.amount), 0) as reputation, a.avatar, a.dailyVotes, a.excludeGroups SELECT a.id, a.passHash, a.displayName, a.locale, a.userGroups, a.userPerms, a.allowExpire, BIT_OR(ab.typeMask) AS bans, IFNULL(SUM(r.amount), 0) as reputation, a.avatar, a.dailyVotes
FROM ?_account a FROM ?_account a
LEFT JOIN ?_account_banned ab ON a.id = ab.userId AND ab.end > UNIX_TIMESTAMP() LEFT JOIN ?_account_banned ab ON a.id = ab.userId AND ab.end > UNIX_TIMESTAMP()
LEFT JOIN ?_account_reputation r ON a.id = r.userId LEFT JOIN ?_account_reputation r ON a.id = r.userId
@@ -75,26 +73,15 @@ class User
return false; return false;
} }
self::$id = intval($query['id']); self::$id = intval($query['id']);
self::$displayName = $query['displayName']; self::$displayName = $query['displayName'];
self::$passHash = $query['passHash']; self::$passHash = $query['passHash'];
self::$expires = (bool)$query['allowExpire']; self::$expires = (bool)$query['allowExpire'];
self::$reputation = $query['reputation']; self::$reputation = $query['reputation'];
self::$banStatus = $query['bans']; self::$banStatus = $query['bans'];
self::$groups = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userGroups']); self::$groups = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userGroups']);
self::$perms = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userPerms']); self::$perms = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userPerms']);
self::$dailyVotes = $query['dailyVotes']; self::$dailyVotes = $query['dailyVotes'];
self::$excludeGroups = $query['excludeGroups'];
$conditions = array(
[['cuFlags', PROFILER_CU_DELETED, '&'], 0],
['OR', ['user', self::$id], ['ap.accountId', self::$id]]
);
if (self::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
array_shift($conditions);
self::$profiles = (new LocalProfileList($conditions));
if ($query['avatar']) if ($query['avatar'])
self::$avatar = $query['avatar']; self::$avatar = $query['avatar'];
@@ -125,7 +112,7 @@ class User
); );
// gain rep for daily visit // gain rep for daily visit
if (!(self::$banStatus & (ACC_BAN_TEMP | ACC_BAN_PERM)) && !self::isInGroup(U_GROUP_PENDING)) if (!(self::$banStatus & (ACC_BAN_TEMP | ACC_BAN_PERM)))
Util::gainSiteReputation(self::$id, SITEREP_ACTION_DAILYVISIT); Util::gainSiteReputation(self::$id, SITEREP_ACTION_DAILYVISIT);
// increment consecutive visits (next day or first of new month and not more than 48h) // increment consecutive visits (next day or first of new month and not more than 48h)
@@ -153,11 +140,11 @@ class User
$rawIp = explode(',', $rawIp)[0]; // [ip, proxy1, proxy2] $rawIp = explode(',', $rawIp)[0]; // [ip, proxy1, proxy2]
// check IPv4 // check IPv4
if ($ipAddr = filter_var($rawIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) if ($ipAddr = filter_var($rawIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))
break; break;
// check IPv6 // check IPv6
if ($ipAddr = filter_var($rawIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) if ($ipAddr = filter_var($rawIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))
break; break;
} }
} }
@@ -183,27 +170,17 @@ class User
{ {
$loc = strtolower(substr($_SERVER["HTTP_ACCEPT_LANGUAGE"], 0, 2)); $loc = strtolower(substr($_SERVER["HTTP_ACCEPT_LANGUAGE"], 0, 2));
switch ($loc) { switch ($loc) {
case 'fr': $loc = LOCALE_FR; break;
case 'de': $loc = LOCALE_DE; break;
case 'zh': $loc = LOCALE_CN; break; // may cause issues in future with zh-tw
case 'es': $loc = LOCALE_ES; break;
case 'ru': $loc = LOCALE_RU; break; case 'ru': $loc = LOCALE_RU; break;
case 'es': $loc = LOCALE_ES; break;
case 'de': $loc = LOCALE_DE; break;
case 'fr': $loc = LOCALE_FR; break;
default: $loc = LOCALE_EN; default: $loc = LOCALE_EN;
} }
} }
// check; pick first viable if failed // check
if (CFG_LOCALES && !(CFG_LOCALES & (1 << $loc))) if ($loc != LOCALE_EN && !(CFG_LOCALES & (1 << $loc)))
{ $loc = LOCALE_EN;
foreach (Util::$localeStrings as $idx => $__)
{
if (CFG_LOCALES & (1 << $idx))
{
$loc = $idx;
break;
}
}
}
// set // set
if (self::$id) if (self::$id)
@@ -236,7 +213,7 @@ class User
$user = 0; $user = 0;
$hash = ''; $hash = '';
switch (CFG_ACC_AUTH_MODE) switch (CFG_AUTH_MODE)
{ {
case AUTH_MODE_SELF: case AUTH_MODE_SELF:
{ {
@@ -246,11 +223,11 @@ class User
// handle login try limitation // handle login try limitation
$ip = DB::Aowow()->selectRow('SELECT ip, count, unbanDate FROM ?_account_bannedips WHERE type = 0 AND ip = ?', self::$ip); $ip = DB::Aowow()->selectRow('SELECT ip, count, unbanDate FROM ?_account_bannedips WHERE type = 0 AND ip = ?', self::$ip);
if (!$ip || $ip['unbanDate'] < time()) // no entry exists or time expired; set count to 1 if (!$ip || $ip['unbanDate'] < time()) // no entry exists or time expired; set count to 1
DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 0, 1, UNIX_TIMESTAMP() + ?d)', self::$ip, CFG_ACC_FAILED_AUTH_BLOCK); DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 0, 1, UNIX_TIMESTAMP() + ?d)', self::$ip, CFG_FAILED_AUTH_EXCLUSION);
else // entry already exists; increment count else // entry already exists; increment count
DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ?', CFG_ACC_FAILED_AUTH_BLOCK, self::$ip); DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ?', CFG_FAILED_AUTH_EXCLUSION, self::$ip);
if ($ip && $ip['count'] >= CFG_ACC_FAILED_AUTH_COUNT && $ip['unbanDate'] >= time()) if ($ip && $ip['count'] >= CFG_FAILED_AUTH_COUNT && $ip['unbanDate'] >= time())
return AUTH_IPBANNED; return AUTH_IPBANNED;
$query = DB::Aowow()->SelectRow(' $query = DB::Aowow()->SelectRow('
@@ -268,6 +245,9 @@ class User
if (!self::verifyCrypt($pass)) if (!self::verifyCrypt($pass))
return AUTH_WRONGPASS; return AUTH_WRONGPASS;
if ($query['status'] & ACC_STATUS_NEW)
return AUTH_ACC_INACTIVE;
// successfull auth; clear bans for this IP // successfull auth; clear bans for this IP
DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE type = 0 AND ip = ?', self::$ip); DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE type = 0 AND ip = ?', self::$ip);
@@ -283,11 +263,12 @@ class User
if (!DB::isConnectable(DB_AUTH)) if (!DB::isConnectable(DB_AUTH))
return AUTH_INTERNAL_ERR; return AUTH_INTERNAL_ERR;
$wow = DB::Auth()->selectRow('SELECT a.id, a.salt, a.verifier, ab.active AS hasBan FROM account a LEFT JOIN account_banned ab ON ab.id = a.id AND active <> 0 WHERE username = ? LIMIT 1', $name); $wow = DB::Auth()->selectRow('SELECT a.id, a.sha_pass_hash, ab.active AS hasBan FROM account a LEFT JOIN account_banned ab ON ab.id = a.id AND active <> 0 WHERE username = ? LIMIT 1', $name);
if (!$wow) if (!$wow)
return AUTH_WRONGUSER; return AUTH_WRONGUSER;
if (!self::verifySRP6($name, $pass, $wow['salt'], $wow['verifier'])) self::$passHash = $wow['sha_pass_hash'];
if (!self::verifySHA1($name, $pass))
return AUTH_WRONGPASS; return AUTH_WRONGPASS;
if ($wow['hasBan']) if ($wow['hasBan'])
@@ -306,13 +287,11 @@ class User
return AUTH_INTERNAL_ERR; return AUTH_INTERNAL_ERR;
require 'config/extAuth.php'; require 'config/extAuth.php';
$result = extAuth($name, $pass, $extId);
$extGroup = -1;
$result = extAuth($name, $pass, $extId, $extGroup);
if ($result == AUTH_OK && $extId) if ($result == AUTH_OK && $extId)
{ {
if ($_ = self::checkOrCreateInDB($extId, $name, $extGroup)) if ($_ = self::checkOrCreateInDB($extId, $name))
$user = $_; $user = $_;
else else
return AUTH_INTERNAL_ERR; return AUTH_INTERNAL_ERR;
@@ -335,28 +314,18 @@ class User
} }
// create a linked account for our settings if nessecary // create a linked account for our settings if nessecary
private static function checkOrCreateInDB($extId, $name, $userGroup = -1) private static function checkOrCreateInDB($extId, $name)
{ {
if (!intVal($extId))
return 0;
$userGroup = intVal($userGroup);
if ($_ = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE extId = ?d', $extId)) if ($_ = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE extId = ?d', $extId))
{
if ($userGroup >= U_GROUP_NONE)
DB::Aowow()->query('UPDATE ?_account SET userGroups = ?d WHERE extId = ?d', $userGroup, $extId);
return $_; return $_;
}
$newId = DB::Aowow()->query('INSERT IGNORE INTO ?_account (extId, user, displayName, joinDate, prevIP, prevLogin, locale, status, userGroups) VALUES (?d, ?, ?, UNIX_TIMESTAMP(), ?, UNIX_TIMESTAMP(), ?d, ?d, ?d)', $newId = DB::Aowow()->query('INSERT IGNORE INTO ?_account (extId, user, displayName, joinDate, prevIP, prevLogin, locale, status) VALUES (?d, ?, ?, UNIX_TIMESTAMP(), ?, UNIX_TIMESTAMP(), ?d, ?d)',
$extId, $extId,
$name, $name,
Util::ucFirst($name), Util::ucFirst($name),
isset($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : '', isset($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : '',
User::$localeId, User::$localeId,
ACC_STATUS_OK, ACC_STATUS_OK
$userGroup >= U_GROUP_NONE ? $userGroup : U_GROUP_NONE
); );
if ($newId) if ($newId)
@@ -383,43 +352,27 @@ class User
public static function verifyCrypt($pass, $hash = '') public static function verifyCrypt($pass, $hash = '')
{ {
$_ = $hash ?: self::$passHash; $_ = $hash ?: self::$passHash;
return $_ === crypt($pass, $_); return $_ == crypt($pass, $_);
} }
private static function verifySRP6($user, $pass, $salt, $verifier) // sha1 used by TC / MaNGOS
private static function hashSHA1($name, $pass)
{ {
$g = gmp_init(7); return sha1(strtoupper($name).':'.strtoupper($pass));
$N = gmp_init('894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7', 16); }
$x = gmp_import(
sha1($salt . sha1(strtoupper($user . ':' . $pass), TRUE), TRUE), private static function verifySHA1($name, $pass)
1, {
GMP_LSW_FIRST return self::$passHash == self::hashSHA1($name, $pass);
);
$v = gmp_powm($g, $x, $N);
return ($verifier === str_pad(gmp_export($v, 1, GMP_LSW_FIRST), 32, chr(0), STR_PAD_RIGHT));
} }
public static function isValidName($name, &$errCode = 0) public static function isValidName($name, &$errCode = 0)
{ {
$errCode = 0; $errCode = 0;
// different auth modes require different usernames if (strlen($name) < 4 || strlen($name) > 16)
$min = 0; // external case
$max = 0;
if (CFG_ACC_AUTH_MODE == AUTH_MODE_SELF)
{
$min = 4;
$max = 16;
}
else if (CFG_ACC_AUTH_MODE == AUTH_MODE_REALM)
{
$min = 3;
$max = 32;
}
if (($min && mb_strlen($name) < $min) || ($max && mb_strlen($name) > $max))
$errCode = 1; $errCode = 1;
else if (preg_match('/[^\w\d\-]/i', $name)) else if (preg_match('/[^\w\d]/i', $name))
$errCode = 2; $errCode = 2;
return $errCode == 0; return $errCode == 0;
@@ -429,8 +382,7 @@ class User
{ {
$errCode = 0; $errCode = 0;
// only enforce for own passwords if (strlen($pass) < 6 || strlen($pass) > 16)
if (mb_strlen($pass) < 6 && CFG_ACC_AUTH_MODE == AUTH_MODE_SELF)
$errCode = 1; $errCode = 1;
// else if (preg_match('/[^\w\d!"#\$%]/', $pass)) // such things exist..? :o // else if (preg_match('/[^\w\d!"#\$%]/', $pass)) // such things exist..? :o
// $errCode = 2; // $errCode = 2;
@@ -478,14 +430,6 @@ class User
return self::$perms || self::$reputation >= CFG_REP_REQ_COMMENT; return self::$perms || self::$reputation >= CFG_REP_REQ_COMMENT;
} }
public static function canReply()
{
if (!self::$id || self::$banStatus & (ACC_BAN_COMMENT | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
return self::$perms || self::$reputation >= CFG_REP_REQ_REPLY;
}
public static function canUpvote() public static function canUpvote()
{ {
if (!self::$id || self::$banStatus & (ACC_BAN_COMMENT | ACC_BAN_PERM | ACC_BAN_TEMP)) if (!self::$id || self::$banStatus & (ACC_BAN_COMMENT | ACC_BAN_PERM | ACC_BAN_TEMP))
@@ -510,30 +454,6 @@ class User
return self::$reputation >= CFG_REP_REQ_SUPERVOTE; return self::$reputation >= CFG_REP_REQ_SUPERVOTE;
} }
public static function canUploadScreenshot()
{
if (!self::$id || self::$banStatus & (ACC_BAN_SCREENSHOT | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
return true;
}
public static function canWriteGuide()
{
if (!self::$id || self::$banStatus & (ACC_BAN_GUIDE | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
return true;
}
public static function canSuggestVideo()
{
if (!self::$id || self::$banStatus & (ACC_BAN_VIDEO | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
return true;
}
public static function isPremium() public static function isPremium()
{ {
return self::isInGroup(U_GROUP_PREMIUM) || self::$reputation >= CFG_REP_REQ_PREMIUM; return self::isInGroup(U_GROUP_PREMIUM) || self::$reputation >= CFG_REP_REQ_PREMIUM;
@@ -580,26 +500,18 @@ class User
if (!self::$id || self::$banStatus & (ACC_BAN_TEMP | ACC_BAN_PERM)) if (!self::$id || self::$banStatus & (ACC_BAN_TEMP | ACC_BAN_PERM))
return $gUser; return $gUser;
$gUser['commentban'] = !self::canComment(); $gUser['commentban'] = (bool)(self::$banStatus & ACC_BAN_COMMENT);
$gUser['canUpvote'] = self::canUpvote(); $gUser['canUpvote'] = self::canUpvote();
$gUser['canDownvote'] = self::canDownvote(); $gUser['canDownvote'] = self::canDownvote();
$gUser['canPostReplies'] = self::canReply(); $gUser['canPostReplies'] = self::canComment();
$gUser['superCommentVotes'] = self::canSupervote(); $gUser['superCommentVotes'] = self::canSupervote();
$gUser['downvoteRep'] = CFG_REP_REQ_DOWNVOTE; $gUser['downvoteRep'] = CFG_REP_REQ_DOWNVOTE;
$gUser['upvoteRep'] = CFG_REP_REQ_UPVOTE; $gUser['upvoteRep'] = CFG_REP_REQ_UPVOTE;
$gUser['characters'] = self::getCharacters(); $gUser['characters'] = self::getCharacters();
$gUser['excludegroups'] = self::$excludeGroups;
$gUser['settings'] = (new StdClass); // profiler requires this to be set; has property premiumborder (NYI)
if ($_ = self::getProfilerExclusions())
$gUser = array_merge($gUser, $_);
if ($_ = self::getProfiles()) if ($_ = self::getProfiles())
$gUser['profiles'] = $_; $gUser['profiles'] = $_;
if ($_ = self::getGuides())
$gUser['guides'] = $_;
if ($_ = self::getWeightScales()) if ($_ = self::getWeightScales())
$gUser['weightscales'] = $_; $gUser['weightscales'] = $_;
@@ -611,59 +523,63 @@ class User
public static function getWeightScales() public static function getWeightScales()
{ {
$result = []; $data = [];
$res = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, name FROM ?_account_weightscales WHERE userId = ?d', self::$id); $res = DB::Aowow()->select('SELECT * FROM ?_account_weightscales WHERE userId = ?d', self::$id);
if (!$res) foreach ($res as $i)
return $result; {
$set = array (
'name' => $i['name'],
'id' => $i['id']
);
$weights = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, `field` AS ARRAY_KEY2, val FROM ?_account_weightscale_data WHERE id IN (?a)', array_keys($res)); $weights = explode(',', $i['weights']);
foreach ($weights as $id => $data) foreach ($weights as $weight)
$result[] = array_merge(['name' => $res[$id], 'id' => $id], $data); {
$w = explode(':', $weight);
return $result; if ($w[1] === 'undefined')
} $w[1] = 0;
public static function getProfilerExclusions() $set[$w[0]] = $w[1];
{ }
$result = [];
$modes = [1 => 'excludes', 2 => 'includes'];
foreach ($modes as $mode => $field)
if ($ex = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, typeId AS ARRAY_KEY2, typeId FROM ?_account_excludes WHERE mode = ?d AND userId = ?d', $mode, self::$id))
foreach ($ex as $type => $ids)
$result[$field][$type] = array_values($ids);
return $result; $data[] = $set;
}
return $data;
} }
public static function getCharacters() public static function getCharacters()
{ {
if (!self::$profiles) // existing chars on realm(s)
return []; $characters = array(
// array(
// 'id' => 22,
// 'name' => 'Example Char',
// 'realmname' => 'Example Realm',
// 'region' => 'eu',
// 'realm' => 'example-realm',
// 'race' => 6,
// 'classs' => 11,
// 'level' => 80,
// 'gender' => 1,
// 'pinned' => 1
// )
);
return self::$profiles->getJSGlobals(PROFILEINFO_CHARACTER); return $characters;
} }
public static function getProfiles() public static function getProfiles()
{ {
if (!self::$profiles) // chars build in profiler
return []; $profiles = array(
// array('id' => 21, 'name' => 'Example Profile 1', 'race' => 4, 'classs' => 5, 'level' => 72, 'gender' => 1, 'icon' => 'inv_axe_04'),
// array('id' => 23, 'name' => 'Example Profile 2', 'race' => 11, 'classs' => 3, 'level' => 17, 'gender' => 0)
);
return self::$profiles->getJSGlobals(PROFILEINFO_PROFILE); return $profiles;
}
public static function getGuides()
{
$result = [];
if ($guides = DB::Aowow()->select('SELECT `id`, `title`, `url` FROM ?_guides WHERE `userId` = ?d AND `status` <> ?d', self::$id, GUIDE_STATUS_ARCHIVED))
{
// fix url
array_walk($guides, fn(&$x) => $x['url'] = '/?guide='.($x['url'] ?? $x['id']));
$result = $guides;
}
return $result;
} }
public static function getCookies() public static function getCookies()
@@ -675,33 +591,6 @@ class User
return $data; return $data;
} }
public static function getFavorites()
{
if (!self::$id)
return [];
$res = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, `typeId` AS ARRAY_KEY2, `typeId` FROM ?_account_favorites WHERE `userId` = ?d', self::$id);
if (!$res)
return [];
$data = [];
foreach ($res as $type => $ids)
{
$tc = Type::newList($type, [['id', array_values($ids)]]);
if (!$tc || $tc->error)
continue;
$entities = [];
foreach ($tc->iterate() as $id => $__)
$entities[] = [$id, $tc->getField('name', true, true)];
if ($entities)
$data[] = ['id' => $type, 'entities' => $entities];
}
return $data;
}
} }
?> ?>

File diff suppressed because it is too large Load Diff

View File

@@ -22,39 +22,24 @@ switch ($pageCall)
case 'account': // account management [nyi] case 'account': // account management [nyi]
case 'achievement': case 'achievement':
case 'achievements': case 'achievements':
case 'areatrigger': // case 'arena-team':
case 'areatriggers': // case 'arena-teams':
case 'arena-team':
case 'arena-teams':
case 'class': case 'class':
case 'classes': case 'classes':
case 'currency': case 'currency':
case 'currencies': case 'currencies':
case 'compare': // tool: item comparison case 'compare': // tool: item comparison
case 'emote':
case 'emotes':
case 'enchantment':
case 'enchantments':
case 'event': case 'event':
case 'events': case 'events':
case 'faction': case 'faction':
case 'factions': case 'factions':
case 'guide': // case 'guild':
case 'guides': // case 'guilds':
case 'guild':
case 'guilds':
case 'icon':
case 'icons':
case 'item': case 'item':
case 'items': case 'items':
case 'itemset': case 'itemset':
case 'itemsets': case 'itemsets':
case 'maps': // tool: map listing case 'maps': // tool: map listing
case 'mail':
case 'mails':
case 'my-guides':
if ($pageCall == 'my-guides')
$altClass = 'guides';
case 'npc': case 'npc':
case 'npcs': case 'npcs':
case 'object': case 'object':
@@ -75,8 +60,8 @@ switch ($pageCall)
case 'search': // tool: searches case 'search': // tool: searches
case 'skill': case 'skill':
case 'skills': case 'skills':
case 'sound': // case 'sound': // db: sounds for zone, creature, spell, ...
case 'sounds': // case 'sounds':
case 'spell': case 'spell':
case 'spells': case 'spells':
case 'talent': // tool: talent calculator case 'talent': // tool: talent calculator
@@ -86,51 +71,17 @@ switch ($pageCall)
case 'video': case 'video':
case 'zone': case 'zone':
case 'zones': case 'zones':
/* called by script */ if (in_array($pageCall, ['admin', 'account', 'profile']))
case 'data': // tool: dataset-loader
case 'cookie': // lossless cookies and user settings
case 'contactus':
case 'comment':
case 'edit': // guide editor: targeted by QQ fileuploader, detail-page article editor
case 'get-description': // guide editor: shorten fulltext into description
case 'filter': // pre-evaluate filter POST-data; sanitize and forward as GET-data
case 'go-to-comment': // find page the comment is on and forward
case 'locale': // subdomain-workaround, change the language
$cleanName = str_replace(['-', '_'], '', ucFirst($altClass ?: $pageCall));
try // can it be handled as ajax?
{ {
$out = ''; if (($_ = (new AjaxHandler($pageParam))->handle($pageCall)) !== null)
$class = 'Ajax'.$cleanName;
$ajax = new $class(explode('.', $pageParam));
if ($ajax->handle($out))
{ {
Util::sendNoCacheHeader(); header('Content-type: application/x-javascript; charset=utf-8');
die((string)$_);
if ($ajax->doRedirect)
header('Location: '.$out, true, 302);
else
{
header($ajax->getContentType());
die($out);
}
} }
else
throw new Exception('not handled as ajax');
}
catch (Exception $e) // no, apparently not..
{
$class = $cleanName.'Page';
$classInstance = new $class($pageCall, $pageParam);
if (is_callable([$classInstance, 'display']))
$classInstance->display();
else if (isset($_GET['power']))
die('$WowheadPower.register(0, '.User::$localeId.', {})');
else // in conjunction with a proper rewriteRule in .htaccess...
(new GenericPage($pageCall))->error();
} }
$_ = ($altClass ?: $pageCall).'Page';
(new $_($pageCall, $pageParam))->display();
break; break;
/* other pages */ /* other pages */
case 'whats-new': case 'whats-new':
@@ -140,13 +91,10 @@ switch ($pageCall)
case 'help': case 'help':
case 'faq': case 'faq':
case 'aboutus': case 'aboutus':
case 'reputation':
case 'privilege':
case 'privileges':
case 'top-users':
(new MorePage($pageCall, $pageParam))->display(); (new MorePage($pageCall, $pageParam))->display();
break; break;
case 'latest-additions': case 'latest-additions':
case 'latest-articles':
case 'latest-comments': case 'latest-comments':
case 'latest-screenshots': case 'latest-screenshots':
case 'latest-videos': case 'latest-videos':
@@ -156,6 +104,19 @@ switch ($pageCall)
case 'random': case 'random':
(new UtilityPage($pageCall, $pageParam))->display(); (new UtilityPage($pageCall, $pageParam))->display();
break; break;
/* called by script */
case 'data': // tool: dataset-loader
case 'cookie': // lossless cookies and user settings
case 'contactus':
case 'comment':
case 'go-to-comment': // find page the comment is on and forward
case 'locale': // subdomain-workaround, change the language
if (($_ = (new AjaxHandler($pageParam))->handle($pageCall)) !== null)
{
header('Content-type: application/x-javascript; charset=utf-8');
die((string)$_);
}
break;
default: // unk parameter given -> ErrorPage default: // unk parameter given -> ErrorPage
if (isset($_GET['power'])) if (isset($_GET['power']))
die('$WowheadPower.register(0, '.User::$localeId.', {})'); die('$WowheadPower.register(0, '.User::$localeId.', {})');

View File

@@ -6,48 +6,26 @@ class Lang
private static $main; private static $main;
private static $account; private static $account;
private static $user; private static $user;
private static $mail;
private static $game; private static $game;
private static $maps;
private static $profiler;
private static $screenshot;
private static $privileges;
private static $smartAI;
private static $unit;
// types
private static $achievement; private static $achievement;
private static $areatrigger;
private static $chrClass; private static $chrClass;
private static $currency; private static $currency;
private static $event; private static $event;
private static $faction; private static $faction;
private static $gameObject; private static $gameObject;
private static $icon;
private static $item; private static $item;
private static $itemset; private static $itemset;
private static $mail; private static $maps;
private static $npc; private static $npc;
private static $pet; private static $pet;
private static $quest; private static $quest;
private static $race; private static $race;
private static $skill; private static $skill;
private static $sound;
private static $spell; private static $spell;
private static $title; private static $title;
private static $zone; private static $zone;
private static $guide;
private static $emote;
private static $enchantment;
private static $locales = array(
LOCALE_EN => 'English',
LOCALE_FR => 'Français',
LOCALE_DE => 'Deutsch',
LOCALE_CN => '简体中文',
LOCALE_ES => 'Español',
LOCALE_RU => 'Русский'
);
public static function load($loc) public static function load($loc)
{ {
@@ -59,136 +37,33 @@ class Lang
foreach ($lang as $k => $v) foreach ($lang as $k => $v)
self::$$k = $v; self::$$k = $v;
// *cough* .. reuse-hacks (because copy-pastaing text for 5 locales sucks) // *cough* .. reuse-hack
self::$item['cat'][2] = [self::$item['cat'][2], self::$spell['weaponSubClass']]; self::$item['cat'][2] = [self::$item['cat'][2], self::$spell['weaponSubClass']];
self::$item['cat'][2][1][14] .= ' ('.self::$item['cat'][2][0].')'; self::$item['cat'][2][1][14] .= ' ('.self::$item['cat'][2][0].')';
self::$main['moreTitles']['privilege'] = self::$privileges['_privileges'];
} }
// todo: make static props private and access through this
public static function __callStatic($prop, $args) public static function __callStatic($prop, $args)
{ {
if (!isset(self::$$prop)) if (!isset(self::$$prop))
{ {
$dbt = debug_backtrace()[0]; Util::addNote(U_GROUP_STAFF, 'Lang::__callStatic() - tried to use undefined property Lang::$'.$prop);
$file = explode(DIRECTORY_SEPARATOR, $dbt['file']);
trigger_error('Lang - tried to use undefined property Lang::$'.$prop.', called in '.array_pop($file).':'.$dbt['line'], E_USER_WARNING);
return null; return null;
} }
$vspfArgs = [];
$var = self::$$prop; $var = self::$$prop;
foreach ($args as $arg) foreach ($args as $key)
{ {
if (is_array($arg)) if (!isset($var[$key]))
{ {
$vspfArgs = $arg; Util::addNote(U_GROUP_STAFF, 'Lang::__callStatic() - undefined key "'.$key.'" in property Lang::$'.$prop.'[\''.implode('\'][\'', $args).'\']');
continue;
}
else if (!isset($var[$arg]))
{
$dbt = debug_backtrace()[0];
$file = explode(DIRECTORY_SEPARATOR, $dbt['file']);
trigger_error('Lang - undefined property Lang::$'.$prop.'[\''.implode('\'][\'', $args).'\'], called in '.array_pop($file).':'.$dbt['line'], E_USER_WARNING);
return null; return null;
} }
$var = $var[$arg]; $var = $var[$key];
} }
// meh :x return $var;
if ($var === null && $prop == 'spell' && count($args) == 1)
{
if ($args[0] == 'effects')
$var = self::$$prop['unkEffect'];
else if ($args[0] == 'auras')
$var = self::$$prop['unkAura'];
}
return self::vspf($var, $vspfArgs);
}
public static function concat($args, $useAnd = true, $callback = null)
{
$b = '';
$i = 0;
$n = count($args);
foreach ($args as $k => $arg)
{
if (is_callable($callback))
$b .= $callback($arg, $k);
else
$b .= $arg;
if ($n > 1 && $i < ($n - 2))
$b .= ', ';
else if ($n > 1 && $i == $n - 2)
$b .= Lang::main($useAnd ? 'and' : 'or');
$i++;
}
return $b;
}
// truncate string after X chars. If X is inside a word truncate behind it.
public static function trimTextClean(string $text, int $len = 100) : string
{
// remove line breaks
$text = strtr($text, ["\n" => ' ', "\r" => ' ']);
// limit whitespaces to one at a time
$text = preg_replace('/\s+/', ' ', trim($text));
if ($len > 0 && mb_strlen($text) > $len)
{
$n = 0;
$b = [];
$parts = explode(' ', $text);
while ($n < $len && $parts)
{
$_ = array_shift($parts);
$n += mb_strlen($_);
$b[] = $_;
}
$text = implode(' ', $b).'…';
}
return $text;
}
// add line breaks to string after X chars. If X is inside a word break behind it.
public static function breakTextClean(string $text, int $len = 30, bool $asHTML = true) : string
{
// remove line breaks
$text = strtr($text, ["\n" => ' ', "\r" => ' ']);
// limit whitespaces to one at a time
$text = preg_replace('/\s+/', ' ', trim($text));
$row = [];
if ($len > 0 && mb_strlen($text) > $len)
{
$i = 0;
$n = 0;
$parts = explode(' ', $text);
foreach ($parts as $p)
{
$row[$i][] = $p;
$n += (mb_strlen($p) + 1);
if ($n < $len)
continue;
$n = 0;
$i++;
}
foreach ($row as &$r)
$r = implode(' ', $r);
}
return implode($asHTML ? '<br />' : '[br]', $row);
} }
public static function sort($prop, $group, $method = SORT_NATURAL) public static function sort($prop, $group, $method = SORT_NATURAL)
@@ -196,14 +71,14 @@ class Lang
if (!isset(self::$$prop)) if (!isset(self::$$prop))
{ {
trigger_error('Lang::sort - tried to use undefined property Lang::$'.$prop, E_USER_WARNING); Util::addNote(U_GROUP_STAFF, 'Lang::sort() - tried to use undefined property Lang::$'.$prop);
return null; return null;
} }
$var = &self::$$prop; $var = &self::$$prop;
if (!isset($var[$group])) if (!isset($var[$group]))
{ {
trigger_error('Lang::sort - tried to use undefined property Lang::$'.$prop.'[\''.$group.'\']', E_USER_WARNING); Util::addNote(U_GROUP_STAFF, 'Lang::sort() - tried to use undefined property Lang::$'.$prop.'[\''.$group.'\']');
return null; return null;
} }
@@ -230,10 +105,9 @@ class Lang
return $tmp; return $tmp;
} }
public static function getLocks(int $lockId, ?array &$ids = [], bool $interactive = false, bool $asHTML = false) : array public static function getLocks($lockId, $interactive = false)
{ {
$locks = []; $locks = [];
$ids = [];
$lock = DB::Aowow()->selectRow('SELECT * FROM ?_lock WHERE id = ?d', $lockId); $lock = DB::Aowow()->selectRow('SELECT * FROM ?_lock WHERE id = ?d', $lockId);
if (!$lock) if (!$lock)
return $locks; return $locks;
@@ -244,71 +118,47 @@ class Lang
$rank = $lock['reqSkill'.$i]; $rank = $lock['reqSkill'.$i];
$name = ''; $name = '';
if ($lock['type'.$i] == LOCK_TYPE_ITEM) if ($lock['type'.$i] == 1) // opened by item
{ {
$name = ItemList::getName($prop); $name = ItemList::getName($prop);
if (!$name) if (!$name)
continue; continue;
if ($interactive && $asHTML) if ($interactive)
$name = '<a class="q1" href="?item='.$prop.'">'.$name.'</a>'; $name = '<a class="q1" href="?item='.$prop.'">'.$name.'</a>';
else if ($interactive && !$asHTML)
{
$name = '[item='.$prop.']';
$ids[Type::ITEM][] = $prop;
}
} }
else if ($lock['type'.$i] == LOCK_TYPE_SKILL) else if ($lock['type'.$i] == 2) // opened by skill
{ {
// exclude unusual stuff
if (!in_array($prop, [1, 2, 3, 4, 9, 16, 20]))
continue;
$name = self::spell('lockType', $prop); $name = self::spell('lockType', $prop);
if (!$name) if (!$name)
continue; continue;
// skills if ($interactive)
if (in_array($prop, [1, 2, 3, 20]))
{ {
$skills = array( $skill = 0;
1 => SKILL_LOCKPICKING, switch ($prop)
2 => SKILL_HERBALISM,
3 => SKILL_MINING,
20 => SKILL_INSCRIPTION
);
if ($interactive && $asHTML)
$name = '<a href="?skill='.$skills[$prop].'">'.$name.'</a>';
else if ($interactive && !$asHTML)
{ {
$name = '[skill='.$skills[$prop].']'; case 1: $skill = 633; break; // Lockpicking
$ids[Type::SKILL][] = $skills[$prop]; case 2: $skill = 182; break; // Herbing
case 3: $skill = 186; break; // Mining
case 20: $skill = 773; break; // Scribing
} }
if ($rank > 0) if ($skill)
$name .= ' ('.$rank.')'; $name = '<a href="?skill='.$skill.'">'.$name.'</a>';
} }
// Lockpicking
else if ($prop == 4) if ($rank > 0)
{ $name .= ' ('.$rank.')';
if ($interactive && $asHTML)
$name = '<a href="?spell=1842">'.$name.'</a>';
else if ($interactive && !$asHTML)
{
$name = '[spell=1842]';
$ids[Type::SPELL][] = 1842;
}
}
// exclude unusual stuff
else if (User::isInGroup(U_GROUP_STAFF))
{
if ($rank > 0)
$name .= ' ('.$rank.')';
}
else
continue;
} }
else else
continue; continue;
$locks[$lock['type'.$i] == LOCK_TYPE_ITEM ? $prop : -$prop] = $name; $locks[$lock['type'.$i] == 1 ? $prop : -$prop] = sprintf(self::game('requires'), $name);
} }
return $locks; return $locks;
@@ -316,7 +166,7 @@ class Lang
public static function getReputationLevelForPoints($pts) public static function getReputationLevelForPoints($pts)
{ {
$_ = Game::getReputationLevelForPoints($pts); $_ = Util::getReputationLevelForPoints($pts);
return self::game('rep', $_); return self::game('rep', $_);
} }
@@ -347,7 +197,7 @@ class Lang
} }
if ($class == ITEM_CLASS_MISC) // yeah hardcoded.. sue me! if ($class == ITEM_CLASS_MISC) // yeah hardcoded.. sue me!
return self::spell('cat', -5, 0); return self::spell('cat', -5);
$tmp = []; $tmp = [];
$strs = self::spell($class == ITEM_CLASS_ARMOR ? 'armorSubClass' : 'weaponSubClass'); $strs = self::spell($class == ITEM_CLASS_ARMOR ? 'armorSubClass' : 'weaponSubClass');
@@ -355,17 +205,12 @@ class Lang
if ($mask & (1 << $k) && $str) if ($mask & (1 << $k) && $str)
$tmp[] = $str; $tmp[] = $str;
if (!$tmp && $class == ITEM_CLASS_ARMOR) return implode(', ', $tmp);
return self::spell('cat', -11, 8);
else if (!$tmp && $class == ITEM_CLASS_WEAPON)
return self::spell('cat', -11, 6);
else
return implode(', ', $tmp);
} }
public static function getStances($stanceMask) public static function getStances($stanceMask)
{ {
$stanceMask &= 0xFF37F6FF; // clamp to available stances/forms.. $stanceMask &= 0xFC27909F; // clamp to available stances/forms..
$tmp = []; $tmp = [];
$i = 1; $i = 1;
@@ -402,7 +247,7 @@ class Lang
return implode(', ', $tmp); return implode(', ', $tmp);
} }
public static function getClassString(int $classMask, array &$ids = [], bool $asHTML = true) : string public static function getClassString($classMask, &$ids = [], &$n = 0, $asHTML = true)
{ {
$classMask &= CLASS_MASK_ALL; // clamp to available classes.. $classMask &= CLASS_MASK_ALL; // clamp to available classes..
@@ -424,26 +269,36 @@ class Lang
$i++; $i++;
} }
$n = count($tmp);
$ids = array_keys($tmp); $ids = array_keys($tmp);
return implode(', ', $tmp); return implode(', ', $tmp);
} }
public static function getRaceString(int $raceMask, array &$ids = [], bool $asHTML = true) : string public static function getRaceString($raceMask, &$side = 0, &$ids = [], &$n = 0, $asHTML = true)
{ {
$raceMask &= RACE_MASK_ALL; // clamp to available races.. $raceMask &= RACE_MASK_ALL; // clamp to available races..
if ($raceMask == RACE_MASK_ALL) // available to all races (we don't display 'both factions') if ($raceMask == RACE_MASK_ALL) // available to all races (we don't display 'both factions')
return false; return false;
if (!$raceMask)
return false;
$tmp = []; $tmp = [];
$i = 1; $i = 1;
$base = $asHTML ? '<a href="?race=%d" class="q1">%s</a>' : '[race=%d]'; $base = $asHTML ? '<a href="?race=%d" class="q1">%s</a>' : '[race=%d]';
$br = $asHTML ? '' : '[br]'; $br = $asHTML ? '' : '[br]';
if (!$raceMask)
{
$side |= SIDE_BOTH;
return self::game('ra', 0);
}
if ($raceMask & RACE_MASK_HORDE)
$side |= SIDE_HORDE;
if ($raceMask & RACE_MASK_ALLIANCE)
$side |= SIDE_ALLIANCE;
if ($raceMask == RACE_MASK_HORDE) if ($raceMask == RACE_MASK_HORDE)
return self::game('ra', -2); return self::game('ra', -2);
@@ -460,138 +315,11 @@ class Lang
$i++; $i++;
} }
$n = count($tmp);
$ids = array_keys($tmp); $ids = array_keys($tmp);
return implode(', ', $tmp); return implode(', ', $tmp);
} }
public static function formatSkillBreakpoints(array $bp, bool $html = false) : string
{
$tmp = Lang::game('difficulty').Lang::main('colon');
for ($i = 0; $i < 4; $i++)
if (!empty($bp[$i]))
$tmp .= $html ? '<span class="r'.($i + 1).'">'.$bp[$i].'</span> ' : '[color=r'.($i + 1).']'.$bp[$i].'[/color] ';
return trim($tmp);
}
public static function nf($number, $decimals = 0, $no1k = false)
{
// [decimal, thousand]
$seps = array(
LOCALE_EN => [',', '.'],
LOCALE_FR => [' ', ','],
LOCALE_DE => ['.', ','],
LOCALE_CN => [',', '.'],
LOCALE_ES => ['.', ','],
LOCALE_RU => [' ', ',']
);
return number_format($number, $decimals, $seps[User::$localeId][1], $no1k ? '' : $seps[User::$localeId][0]);
}
public static function typeName(int $type) : string
{
return Util::ucFirst(self::game(Type::getFileString($type)));
}
private static function vspf($var, $args)
{
if (is_array($var))
{
foreach ($var as &$v)
$v == self::vspf($v, $args);
return $var;
}
if ($args)
$var = vsprintf($var, $args);
// line break
// |n
$var = str_replace('|n', '<br />', $var);
// color
// |c<aarrggbb><word>|r
$var = preg_replace('/\|cff([a-f0-9]{6})(.+?)\|r/i', '<span style="color: #$1;">$2</span>', $var);
// icon
// |T<imgPath>:0:0:0:-1|t - not used, skip if found
$var = preg_replace('/\|T[^\|]+\|t/', '', $var);
// hyperlink
// |H<hyperlinkStruct>|h<name>|h - not used, truncate structure if found
$var = preg_replace('/\|H[^\|]+\|h([^\|]+)\|h/', '$1', $var);
// french preposition : de
// |2 <word>
$var = preg_replace_callback('/\|2\s(\w)/i', function ($m) {
if (in_array(strtolower($m[1]), ['a', 'e', 'h', 'i', 'o', 'u']))
return "d'".$m[1];
else
return 'de '.$m[1];
}, $var);
// russian word cunjugation thingy
// |3-<number>(<word>)
$var = preg_replace_callback('/\|3-(\d)\(([^\)]+)\)/i', function ($m) {
switch ($m[0])
{
case 1: // seen cases
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
default: // passthrough .. unk case
return $m[1];
}
}, $var);
// numeric switch
// <number> |4<singular>:<plural>[:<plural2>];
$var = preg_replace_callback('/([\d\.\,]+)([^\d]*)\|4([^:]*):([^;]*);/i', function ($m) {
$plurals = explode(':', $m[4]);
$result = '';
if (count($plurals) == 2) // special case: ruRU
{
switch (substr($m[1], -1)) // check last digit of number
{
case 1:
// but not 11 (teen number)
if (!in_array($m[1], [11]))
{
$result = $m[3];
break;
}
case 2:
case 3:
case 4:
// but not 12, 13, 14 (teen number) [11 is passthrough]
if (!in_array($m[1], [11, 12, 13, 14]))
{
$result = $plurals[0];
break;
}
break;
default:
$result = $plurals[1];
}
}
else
$result = ($m[1] == 1 ? $m[3] : $plurals[0]);
return $m[1].$m[2].$result;
}, $var);
return $var;
}
} }
?> ?>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -8,8 +8,8 @@ if (!defined('AOWOW_REVISION'))
class AccountPage extends GenericPage class AccountPage extends GenericPage
{ {
protected $tpl = 'acc-dashboard'; protected $tpl = 'acc-dashboard';
protected $js = [[JS_FILE, 'user.js'], [JS_FILE, 'profile.js']]; protected $js = ['user.js', 'profile.js'];
protected $css = [[CSS_FILE, 'Profiler.css']]; protected $css = [['path' => 'Profiler.css']];
protected $mode = CACHE_TYPE_NONE; protected $mode = CACHE_TYPE_NONE;
protected $category = null; protected $category = null;
protected $validCats = array( protected $validCats = array(
@@ -27,18 +27,13 @@ class AccountPage extends GenericPage
protected $lvTabs = []; protected $lvTabs = [];
protected $banned = []; protected $banned = [];
protected $_get = array( private $_post = array(
'token' => ['filter' => FILTER_SANITIZE_SPECIAL_CHARS, 'flags' => FILTER_FLAG_STRIP_AOWOW], 'username' => [FILTER_SANITIZE_SPECIAL_CHARS, 0xC], // FILTER_FLAG_STRIP_LOW | *_HIGH
'next' => ['filter' => FILTER_SANITIZE_SPECIAL_CHARS, 'flags' => FILTER_FLAG_STRIP_AOWOW], 'password' => [FILTER_UNSAFE_RAW, null],
); 'c_password' => [FILTER_UNSAFE_RAW, null],
'token' => [FILTER_UNSAFE_RAW, null],
protected $_post = array( 'remember_me' => [FILTER_CALLBACK, ['options' => 'AccountPage::rememberCallback']],
'username' => ['filter' => FILTER_SANITIZE_SPECIAL_CHARS, 'flags' => FILTER_FLAG_STRIP_AOWOW], 'email' => [FILTER_SANITIZE_EMAIL, null]
'password' => ['filter' => FILTER_UNSAFE_RAW],
'c_password' => ['filter' => FILTER_UNSAFE_RAW],
'token' => ['filter' => FILTER_UNSAFE_RAW],
'remember_me' => ['filter' => FILTER_CALLBACK, 'options' => 'AccountPage::rememberCallback'],
'email' => ['filter' => FILTER_SANITIZE_EMAIL]
); );
public function __construct($pageCall, $pageParam) public function __construct($pageCall, $pageParam)
@@ -48,6 +43,9 @@ class AccountPage extends GenericPage
parent::__construct($pageCall, $pageParam); parent::__construct($pageCall, $pageParam);
foreach ($this->_post as $k => &$v)
$v = !empty($_POST[$k]) ? filter_input(INPUT_POST, $k, $v[0], $v[1]) : null;
if ($pageParam) if ($pageParam)
{ {
// requires auth && not authed // requires auth && not authed
@@ -59,7 +57,7 @@ class AccountPage extends GenericPage
} }
} }
protected static function rememberCallback($val) private function rememberCallback($val)
{ {
return $val == 'yes' ? $val : null; return $val == 'yes' ? $val : null;
} }
@@ -75,13 +73,8 @@ class AccountPage extends GenericPage
switch ($this->category[0]) switch ($this->category[0])
{ {
case 'forgotpassword': case 'forgotpassword':
if (CFG_ACC_AUTH_MODE != AUTH_MODE_SELF) if (CFG_AUTH_MODE != AUTH_MODE_SELF) // only recover own accounts
{ $this->error();
if (CFG_ACC_EXT_RECOVER_URL)
header('Location: '.CFG_ACC_EXT_RECOVER_URL, true, 302);
else
$this->error();
}
$this->tpl = 'acc-recover'; $this->tpl = 'acc-recover';
$this->resetPass = false; $this->resetPass = false;
@@ -92,13 +85,8 @@ class AccountPage extends GenericPage
$this->head = sprintf(Lang::account('recoverPass'), $nStep); $this->head = sprintf(Lang::account('recoverPass'), $nStep);
break; break;
case 'forgotusername': case 'forgotusername':
if (CFG_ACC_AUTH_MODE != AUTH_MODE_SELF) if (CFG_AUTH_MODE != AUTH_MODE_SELF) // only recover own accounts
{ $this->error();
if (CFG_ACC_EXT_RECOVER_URL)
header('Location: '.CFG_ACC_EXT_RECOVER_URL, true, 302);
else
$this->error();
}
$this->tpl = 'acc-recover'; $this->tpl = 'acc-recover';
$this->resetPass = false; $this->resetPass = false;
@@ -130,22 +118,14 @@ class AccountPage extends GenericPage
header('Location: '.$this->getNext(true), true, 302); header('Location: '.$this->getNext(true), true, 302);
} }
} }
else if ($this->_get['token'] && ($_ = DB::Aowow()->selectCell('SELECT user FROM ?_account WHERE status IN (?a) AND token = ? AND statusTimer > UNIX_TIMESTAMP()', [ACC_STATUS_RECOVER_USER, ACC_STATUS_OK], $this->_get['token']))) else if (!empty($_GET['token']) && ($_ = DB::Aowow()->selectCell('SELECT user FROM ?_account WHERE status IN (?a) AND token = ? AND statusTimer > UNIX_TIMESTAMP()', [ACC_STATUS_RECOVER_USER, ACC_STATUS_OK], $_GET['token'])))
$this->user = $_; $this->user = $_;
break; break;
case 'signup': case 'signup':
if (!CFG_ACC_ALLOW_REGISTER) if (!CFG_ALLOW_REGISTER || CFG_AUTH_MODE != AUTH_MODE_SELF)
$this->error(); $this->error();
if (CFG_ACC_AUTH_MODE != AUTH_MODE_SELF)
{
if (CFG_ACC_EXT_CREATE_URL)
header('Location: '.CFG_ACC_EXT_CREATE_URL, true, 302);
else
$this->error();
}
$this->tpl = 'acc-signUp'; $this->tpl = 'acc-signUp';
$nStep = 1; $nStep = 1;
if ($this->_post['username'] || $this->_post['password'] || $this->_post['c_password'] || $this->_post['email']) if ($this->_post['username'] || $this->_post['password'] || $this->_post['c_password'] || $this->_post['email'])
@@ -158,13 +138,15 @@ class AccountPage extends GenericPage
$this->text = sprintf(Lang::account('createAccSent'), $this->_post['email']); $this->text = sprintf(Lang::account('createAccSent'), $this->_post['email']);
} }
} }
else if ($this->_get['token'] && ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE status = ?d AND token = ?', ACC_STATUS_NEW, $this->_get['token']))) else if (!empty($_GET['token']) && ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE status = ?d AND token = ?', ACC_STATUS_NEW, $_GET['token'])))
{ {
$nStep = 2; $nStep = 2;
DB::Aowow()->query('UPDATE ?_account SET status = ?d, statusTimer = 0, token = 0, userGroups = ?d WHERE token = ?', ACC_STATUS_OK, U_GROUP_NONE, $this->_get['token']); DB::Aowow()->query('UPDATE ?_account SET status = ?d WHERE token = ?', ACC_STATUS_OK, $_GET['token']);
DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 1, ?d + 1, UNIX_TIMESTAMP() + ?d)', User::$ip, CFG_ACC_FAILED_AUTH_COUNT, CFG_ACC_FAILED_AUTH_BLOCK); DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 1, ?d + 1, UNIX_TIMESTAMP() + ?d)', User::$ip, CFG_FAILED_AUTH_COUNT, CFG_FAILED_AUTH_EXCLUSION);
$this->text = sprintf(Lang::account('accActivated'), $this->_get['token']); Util::gainSiteReputation($newId, SITEREP_ACTION_REGISTER);
$this->text = sprintf(Lang::account('accActivated'), $_GET['token']);
} }
else else
$this->next = $this->getNext(); $this->next = $this->getNext();
@@ -236,6 +218,7 @@ class AccountPage extends GenericPage
/* Listview */ /* Listview */
/************/ /************/
$this->lvTabs = [];
$this->forceTabs = true; $this->forceTabs = true;
// Reputation changelog (params only for comment-events) // Reputation changelog (params only for comment-events)
@@ -244,7 +227,11 @@ class AccountPage extends GenericPage
foreach ($repData as &$r) foreach ($repData as &$r)
$r['when'] = date(Util::$dateFormatInternal, $r['when']); $r['when'] = date(Util::$dateFormatInternal, $r['when']);
$this->lvTabs[] = ['reputationhistory', ['data' => $repData]]; $this->lvTabs[] = array(
'file' => 'reputationhistory',
'data' => $repData,
'params' => []
);
} }
// comments // comments
@@ -254,11 +241,14 @@ class AccountPage extends GenericPage
// _totalCount: 377, // _totalCount: 377,
// note: $WH.sprintf(LANG.lvnote_usercomments, 377), // note: $WH.sprintf(LANG.lvnote_usercomments, 377),
$this->lvTabs[] = ['commentpreview', array( $this->lvTabs[] = array(
'data' => $_, 'file' => 'commentpreview',
'hiddenCols' => ['author'], 'data' => $_,
'onBeforeCreate' => '$Listview.funcBox.beforeUserComments' 'params' => array(
)]; 'hiddenCols' => "$['author']",
'onBeforeCreate' => '$Listview.funcBox.beforeUserComments'
)
);
} }
// replies // replies
@@ -269,10 +259,13 @@ class AccountPage extends GenericPage
// _totalCount: 377, // _totalCount: 377,
// note: $WH.sprintf(LANG.lvnote_usercomments, 377), // note: $WH.sprintf(LANG.lvnote_usercomments, 377),
$this->lvTabs[] = ['replypreview', array( $this->lvTabs[] = array(
'data' => $_, 'file' => 'replypreview',
'hiddenCols' => ['author'] 'data' => $_,
)]; 'params' => array(
'hiddenCols' => "$['author']"
)
);
} }
/* /*
@@ -315,17 +308,17 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
$this->text = sprintf(Lang::account('recovPassSent'), $this->_post['email']); $this->text = sprintf(Lang::account('recovPassSent'), $this->_post['email']);
} }
} }
else if ($this->_get['token']) // step 2 else if (isset($_GET['token'])) // step 2
{ {
$step = 2; $step = 2;
$this->resetPass = true; $this->resetPass = true;
$this->token = $this->_get['token']; $this->token = $_GET['token'];
} }
else if ($this->_post['token'] && $this->_post['email'] && $this->_post['password'] && $this->_post['c_password']) else if ($this->_post['token'] && $this->_post['email'] && $this->_post['password'] && $this->_post['c_password'])
{ {
$step = 2; $step = 2;
$this->resetPass = true; $this->resetPass = true;
$this->token = $this->_post['token']; // insecure source .. that sucks; but whats the worst that could happen .. this account cannot be recovered for some minutes $this->token = $_GET['token']; // insecure source .. that sucks; but whats the worst that could happen .. this account cannot be recovered for some minutes
if ($err = $this->doResetPass()) if ($err = $this->doResetPass())
$this->error = $err; $this->error = $err;
@@ -353,10 +346,9 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
return Lang::main('intError'); return Lang::main('intError');
// reset account status, update expiration // reset account status, update expiration
DB::Aowow()->query('UPDATE ?_account SET prevIP = IF(curIp = ?, prevIP, curIP), curIP = IF(curIp = ?, curIP, ?), allowExpire = ?d, status = IF(status = ?d, status, 0), statusTimer = IF(status = ?d, statusTimer, 0), token = IF(status = ?d, token, "") WHERE user = ?', DB::Aowow()->query('UPDATE ?_account SET prevIP = IF(curIp = ?, prevIP, curIP), curIP = IF(curIp = ?, curIP, ?), allowExpire = ?d, status = 0, statusTimer = 0, token = "" WHERE user = ?',
User::$ip, User::$ip, User::$ip, User::$ip, User::$ip, User::$ip,
$this->_post['remember_me'] != 'yes', $this->_post['remember_me'] != 'yes',
ACC_STATUS_NEW, ACC_STATUS_NEW, ACC_STATUS_NEW,
$this->_post['username'] $this->_post['username']
); );
@@ -374,9 +366,12 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
case AUTH_WRONGPASS: case AUTH_WRONGPASS:
User::destroy(); User::destroy();
return Lang::account('wrongPass'); return Lang::account('wrongPass');
case AUTH_ACC_INACTIVE:
User::destroy();
return Lang::account('accInactive');
case AUTH_IPBANNED: case AUTH_IPBANNED:
User::destroy(); User::destroy();
return sprintf(Lang::account('loginExceeded'), Util::formatTime(CFG_ACC_FAILED_AUTH_BLOCK * 1000)); return sprintf(Lang::account('loginExceeded'), Util::formatTime(CFG_FAILED_AUTH_EXCLUSION * 1000));
case AUTH_INTERNAL_ERR: case AUTH_INTERNAL_ERR:
User::destroy(); User::destroy();
return Lang::main('intError'); return Lang::main('intError');
@@ -408,19 +403,19 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
// limit account creation // limit account creation
$ip = DB::Aowow()->selectRow('SELECT ip, count, unbanDate FROM ?_account_bannedips WHERE type = 1 AND ip = ?', User::$ip); $ip = DB::Aowow()->selectRow('SELECT ip, count, unbanDate FROM ?_account_bannedips WHERE type = 1 AND ip = ?', User::$ip);
if ($ip && $ip['count'] >= CFG_ACC_FAILED_AUTH_COUNT && $ip['unbanDate'] >= time()) if ($ip && $ip['count'] >= CFG_FAILED_AUTH_COUNT && $ip['unbanDate'] >= time())
{ {
DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ? AND type = 1', CFG_ACC_FAILED_AUTH_BLOCK, User::$ip); DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ? AND type = 1', CFG_FAILED_AUTH_EXCLUSION, User::$ip);
return sprintf(Lang::account('signupExceeded'), Util::formatTime(CFG_ACC_FAILED_AUTH_BLOCK * 1000)); return sprintf(Lang::account('signupExceeded'), Util::formatTime(CFG_FAILED_AUTH_EXCLUSION * 1000));
} }
// username taken // username taken
if ($_ = DB::Aowow()->SelectCell('SELECT user FROM ?_account WHERE (user = ? OR email = ?) AND (status <> ?d OR (status = ?d AND statusTimer > UNIX_TIMESTAMP()))', $this->_post['username'], $this->_post['email'], ACC_STATUS_NEW, ACC_STATUS_NEW)) if ($_ = DB::Aowow()->SelectCell('SELECT user FROM ?_account WHERE (user = ? OR email = ?) AND (status <> ?d OR (status = ?d AND statusTimer > UNIX_TIMESTAMP()))', $this->_post['username'], $email, ACC_STATUS_NEW, ACC_STATUS_NEW))
return $_ == $this->_post['username'] ? Lang::account('nameInUse') : Lang::account('mailInUse'); return $_ == $this->_post['username'] ? Lang::account('nameInUse') : Lang::account('mailInUse');
// create.. // create..
$token = Util::createHash(); $token = Util::createHash();
$ok = DB::Aowow()->query('REPLACE INTO ?_account (user, passHash, displayName, email, joindate, curIP, allowExpire, locale, userGroups, status, statusTimer, token) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(), ?, ?d, ?d, ?d, ?d, UNIX_TIMESTAMP() + ?d, ?)', $id = DB::Aowow()->query('REPLACE INTO ?_account (user, passHash, displayName, email, joindate, curIP, allowExpire, locale, status, statusTimer, token) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(), ?, ?d, ?d, ?d, UNIX_TIMESTAMP() + ?d, ?)',
$this->_post['username'], $this->_post['username'],
User::hashCrypt($this->_post['password']), User::hashCrypt($this->_post['password']),
Util::ucFirst($this->_post['username']), Util::ucFirst($this->_post['username']),
@@ -428,23 +423,19 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
User::$ip, User::$ip,
$this->_post['remember_me'] != 'yes', $this->_post['remember_me'] != 'yes',
User::$localeId, User::$localeId,
U_GROUP_PENDING,
ACC_STATUS_NEW, ACC_STATUS_NEW,
CFG_ACC_CREATE_SAVE_DECAY, CFG_ACCOUNT_CREATE_SAVE_DECAY,
$token $token
); );
if (!$ok) if (!$id) // something went wrong
return Lang::main('intError'); return Lang::main('intError');
else if ($_ = $this->sendMail(Lang::user('accConfirm', 0), sprintf(Lang::user('accConfirm', 1), $token), CFG_ACC_CREATE_SAVE_DECAY)) else if ($_ = $this->sendMail(Lang::mail('accConfirm', 0), sprintf(Lang::mail('accConfirm', 1), $token), CFG_ACCOUNT_CREATE_SAVE_DECAY))
{ {
if ($id = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE token = ?', $token))
Util::gainSiteReputation($id, SITEREP_ACTION_REGISTER);
// success:: update ip-bans // success:: update ip-bans
if (!$ip || $ip['unbanDate'] < time()) if (!$ip || $ip['unbanDate'] < time())
DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 1, 1, UNIX_TIMESTAMP() + ?d)', User::$ip, CFG_ACC_FAILED_AUTH_BLOCK); DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 1, 1, UNIX_TIMESTAMP() + ?d)', User::$ip, CFG_FAILED_AUTH_EXCLUSION);
else else
DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ? AND type = 1', CFG_ACC_FAILED_AUTH_BLOCK, User::$ip); DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ? AND type = 1', CFG_FAILED_AUTH_EXCLUSION, User::$ip);
return $_; return $_;
} }
@@ -452,11 +443,11 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
private function doRecoverPass() private function doRecoverPass()
{ {
if ($_ = $this->initRecovery(ACC_STATUS_RECOVER_PASS, CFG_ACC_RECOVERY_DECAY, $token)) if ($_ = $this->initRecovery(ACC_STATUS_RECOVER_PASS, CFG_ACCOUNT_RECOVERY_DECAY, $token))
return $_; return $_;
// send recovery mail // send recovery mail
return $this->sendMail(Lang::user('resetPass', 0), sprintf(Lang::user('resetPass', 1), $token), CFG_ACC_RECOVERY_DECAY); return $this->sendMail(Lang::mail('resetPass', 0), sprintf(Lang::mail('resetPass', 1), $token), CFG_ACCOUNT_RECOVERY_DECAY);
} }
private function doResetPass() private function doResetPass()
@@ -475,20 +466,20 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
if (!$uId) if (!$uId)
return Lang::account('emailNotFound'); // assume they didn't meddle with the token return Lang::account('emailNotFound'); // assume they didn't meddle with the token
if (!User::verifyCrypt($this->_post['c_password'])) if (!User::verifyCrypt($newPass))
return Lang::account('newPassDiff'); return Lang::account('newPassDiff');
if (!DB::Aowow()->query('UPDATE ?_account SET passHash = ?, status = ?d WHERE id = ?d', User::hashCrypt($this->_post['c_password']), ACC_STATUS_OK, $uId)) if (!DB::Aowow()->query('UPDATE ?_account SET passHash = ?, status = ?d WHERE id = ?d', User::hashcrypt($newPass), ACC_STATUS_OK, $uId))
return Lang::main('intError'); return Lang::main('intError');
} }
private function doRecoverUser() private function doRecoverUser()
{ {
if ($_ = $this->initRecovery(ACC_STATUS_RECOVER_USER, CFG_ACC_RECOVERY_DECAY, $token)) if ($_ = $this->initRecovery(ACC_STATUS_RECOVER_USER, CFG_ACCOUNT_RECOVERY_DECAY, $token))
return $_; return $_;
// send recovery mail // send recovery mail
return $this->sendMail(Lang::user('recoverUser', 0), sprintf(Lang::user('recoverUser', 1), $token), CFG_ACC_RECOVERY_DECAY); return $this->sendMail(Lang::mail('recoverUser', 0), sprintf(Lang::mail('recoverUser', 1), $token), CFG_ACCOUNT_RECOVERY_DECAY);
} }
private function initRecovery($type, $delay, &$token) private function initRecovery($type, $delay, &$token)
@@ -510,7 +501,7 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
{ {
// send recovery mail // send recovery mail
$subj = CFG_NAME_SHORT.Lang::main('colon') . $subj; $subj = CFG_NAME_SHORT.Lang::main('colon') . $subj;
$msg .= "\r\n\r\n".sprintf(Lang::user('tokenExpires'), Util::formatTime($delay * 1000))."\r\n"; $msg .= "\r\n\r\n".sprintf(Lang::mail('tokenExpires'), Util::formatTime($delay * 1000))."\r\n";
$header = 'From: '.CFG_CONTACT_EMAIL . "\r\n" . $header = 'From: '.CFG_CONTACT_EMAIL . "\r\n" .
'Reply-To: '.CFG_CONTACT_EMAIL . "\r\n" . 'Reply-To: '.CFG_CONTACT_EMAIL . "\r\n" .
'X-Mailer: PHP/' . phpversion(); 'X-Mailer: PHP/' . phpversion();
@@ -522,8 +513,8 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
private function getNext($forHeader = false) private function getNext($forHeader = false)
{ {
$next = $forHeader ? '.' : ''; $next = $forHeader ? '.' : '';
if ($this->_get['next']) if (isset($_GET['next']))
$next = $this->_get['next']; $next = $_GET['next'];
else if (isset($_SERVER['HTTP_REFERER']) && strstr($_SERVER['HTTP_REFERER'], '?')) else if (isset($_SERVER['HTTP_REFERER']) && strstr($_SERVER['HTTP_REFERER'], '?'))
$next = explode('?', $_SERVER['HTTP_REFERER'])[1]; $next = explode('?', $_SERVER['HTTP_REFERER'])[1];

View File

@@ -23,32 +23,28 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader() // tabId 0: Database g_initHeader()
class AchievementPage extends GenericPage class AchievementPage extends GenericPage
{ {
use TrDetailPage; use DetailPage;
protected $type = Type::ACHIEVEMENT; protected $type = TYPE_ACHIEVEMENT;
protected $typeId = 0; protected $typeId = 0;
protected $tpl = 'achievement'; protected $tpl = 'achievement';
protected $path = [0, 9]; protected $path = [0, 9];
protected $tabId = 0; protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE; protected $mode = CACHE_TYPE_PAGE;
protected $_get = ['domain' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkDomain']];
private $powerTpl = '$WowheadPower.registerAchievement(%d, %d, %s);';
public function __construct($pageCall, $id) public function __construct($pageCall, $id)
{ {
parent::__construct($pageCall, $id); parent::__construct($pageCall, $id);
// temp locale // temp locale
if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain']) if ($this->mode == CACHE_TYPE_TOOLTIP && isset($_GET['domain']))
Util::powerUseLocale($this->_get['domain']); Util::powerUseLocale($_GET['domain']);
$this->typeId = intVal($id); $this->typeId = intVal($id);
$this->subject = new AchievementList(array(['id', $this->typeId])); $this->subject = new AchievementList(array(['id', $this->typeId]));
if ($this->subject->error) if ($this->subject->error)
$this->notFound(Lang::game('achievement'), Lang::achievement('notFound')); $this->notFound();
$this->extendGlobalData($this->subject->getJSGlobals(GLOBALINFO_REWARDS)); $this->extendGlobalData($this->subject->getJSGlobals(GLOBALINFO_REWARDS));
@@ -62,7 +58,7 @@ class AchievementPage extends GenericPage
do do
{ {
array_unshift($this->path, $curCat); array_unshift($this->path, $curCat);
$curCat = DB::Aowow()->SelectCell('SELECT parentCat FROM ?_achievementcategory WHERE id = ?d', $curCat); $curCat = DB::Aowow()->SelectCell('SELECT parentCategory FROM ?_achievementcategory WHERE id = ?d', $curCat);
} }
while ($curCat > 0); while ($curCat > 0);
@@ -103,20 +99,18 @@ class AchievementPage extends GenericPage
$infobox[] = Lang::main('side').Lang::main('colon').Lang::game('si', SIDE_BOTH); $infobox[] = Lang::main('side').Lang::main('colon').Lang::game('si', SIDE_BOTH);
} }
// icon
if ($_ = $this->subject->getField('iconId'))
{
$infobox[] = Util::ucFirst(lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
$this->extendGlobalIds(Type::ICON, $_);
}
// realm first available? // realm first available?
if ($this->subject->getField('flags') & 0x100 && DB::isConnectable(DB_AUTH)) if ($this->subject->getField('flags') & 0x100 && DB::isConnectable(DB_AUTH))
{ {
$avlb = []; $avlb = [];
foreach (Profiler::getRealms() AS $rId => $rData) foreach (DB::Auth()->selectCol('SELECT id AS ARRAY_KEY, name FROM realmlist WHERE allowedSecurityLevel = 0 AND gamebuild = ?d', WOW_VERSION) AS $rId => $name)
{
if (!DB::isConnectable(DB_CHARACTERS . $rId))
continue;
if (!DB::Characters($rId)->selectCell('SELECT 1 FROM character_achievement WHERE achievement = ?d LIMIT 1', $this->typeId)) if (!DB::Characters($rId)->selectCell('SELECT 1 FROM character_achievement WHERE achievement = ?d LIMIT 1', $this->typeId))
$avlb[] = Util::ucWords($rData['name']); $avlb[] = $name;
}
if ($avlb) if ($avlb)
$infobox[] = Lang::achievement('rfAvailable').implode(', ', $avlb); $infobox[] = Lang::achievement('rfAvailable').implode(', ', $avlb);
@@ -140,7 +134,7 @@ class AchievementPage extends GenericPage
$series[$pos][] = array( $series[$pos][] = array(
'side' => $chainAcv->getField('faction'), 'side' => $chainAcv->getField('faction'),
'typeStr' => Type::getFileString(Type::ACHIEVEMENT), 'typeStr' => Util::$typeStrings[TYPE_ACHIEVEMENT],
'typeId' => $aId, 'typeId' => $aId,
'name' => $chainAcv->getField('name', true) 'name' => $chainAcv->getField('name', true)
); );
@@ -154,17 +148,11 @@ class AchievementPage extends GenericPage
$this->mail = $this->createMail($reqBook); $this->mail = $this->createMail($reqBook);
$this->headIcons = [$this->subject->getField('iconString')]; $this->headIcons = [$this->subject->getField('iconString')];
$this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null; $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
$this->series = $series ? [[array_values($series), null]] : null; $this->series = $series ? [[$series, null]] : null;
$this->description = $this->subject->getField('description', true); $this->description = $this->subject->getField('description', true);
$this->redButtons = array( $this->redButtons = array(
BUTTON_WOWHEAD => !($this->subject->getField('cuFlags') & CUSTOM_SERVERSIDE), BUTTON_LINKS => ['color' => 'ffffff00', 'linkId' => Util::$typeStrings[TYPE_ACHIEVEMENT].':'.$this->typeId.':&quot;..UnitGUID(&quot;player&quot;)..&quot;:0:0:0:0:0:0:0:0'],
BUTTON_LINKS => array( BUTTON_WOWHEAD => !($this->subject->getField('cuFlags') & CUSTOM_SERVERSIDE)
'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->name,
'type' => $this->type,
'typeId' => $this->typeId
)
); );
$this->criteria = array( $this->criteria = array(
'reqQty' => $this->subject->getField('reqCriteriaCount'), 'reqQty' => $this->subject->getField('reqCriteriaCount'),
@@ -173,13 +161,13 @@ class AchievementPage extends GenericPage
); );
if ($reqBook) if ($reqBook)
$this->addScript([CSS_FILE, 'Book.css']); $this->addCss(['path' => 'Book.css']);
// create rewards // create rewards
if ($foo = $this->subject->getField('rewards')) if ($foo = $this->subject->getField('rewards'))
{ {
array_walk($foo, function(&$item) { array_walk($foo, function(&$item) {
$item = $item[0] != Type::ITEM ? null : $item[1]; $item = $item[0] != TYPE_ITEM ? null : $item[1];
}); });
$bar = new ItemList(array(['i.id', $foo])); $bar = new ItemList(array(['i.id', $foo]));
@@ -188,9 +176,9 @@ class AchievementPage extends GenericPage
$this->rewards['item'][] = array( $this->rewards['item'][] = array(
'name' => $bar->getField('name', true), 'name' => $bar->getField('name', true),
'quality' => $bar->getField('quality'), 'quality' => $bar->getField('quality'),
'typeStr' => Type::getFileString(Type::ITEM), 'typeStr' => Util::$typeStrings[TYPE_ITEM],
'id' => $id, 'id' => $id,
'globalStr' => Type::getJSGlobalString(Type::ITEM) 'globalStr' => 'g_items'
); );
} }
} }
@@ -198,7 +186,7 @@ class AchievementPage extends GenericPage
if ($foo = $this->subject->getField('rewards')) if ($foo = $this->subject->getField('rewards'))
{ {
array_walk($foo, function(&$item) { array_walk($foo, function(&$item) {
$item = $item[0] != Type::TITLE ? null : $item[1]; $item = $item[0] != TYPE_TITLE ? null : $item[1];
}); });
$bar = new TitleList(array(['id', $foo])); $bar = new TitleList(array(['id', $foo]));
@@ -236,12 +224,15 @@ class AchievementPage extends GenericPage
['id', $this->typeId, '!'] ['id', $this->typeId, '!']
); );
$saList = new AchievementList($conditions); $saList = new AchievementList($conditions);
$this->lvTabs[] = ['achievement', array( $this->lvTabs[] = array(
'data' => array_values($saList->getListviewData()), 'file' => 'achievement',
'id' => 'see-also', 'data' => $saList->getListviewData(),
'name' => '$LANG.tab_seealso', 'params' => array(
'visibleCols' => ['category'] 'id' => 'see-also',
)]; 'name' => '$LANG.tab_seealso',
'visibleCols' => "$['category']"
)
);
$this->extendGlobalData($saList->getJSGlobals()); $this->extendGlobalData($saList->getJSGlobals());
// tab: criteria of // tab: criteria of
@@ -252,12 +243,15 @@ class AchievementPage extends GenericPage
if (!empty($refs)) if (!empty($refs))
{ {
$coList = new AchievementList(array(['id', $refs])); $coList = new AchievementList(array(['id', $refs]));
$this->lvTabs[] = ['achievement', array( $this->lvTabs[] = array(
'data' => array_values($coList->getListviewData()), 'file' => 'achievement',
'id' => 'criteria-of', 'data' => $coList->getListviewData(),
'name' => '$LANG.tab_criteriaof', 'params' => array(
'visibleCols' => ['category'] 'id' => 'criteria-of',
)]; 'name' => '$LANG.tab_criteriaof',
'visibleCols' => "$['category']"
)
);
$this->extendGlobalData($coList->getJSGlobals()); $this->extendGlobalData($coList->getJSGlobals());
} }
@@ -267,16 +261,6 @@ class AchievementPage extends GenericPage
$iconId = 1; $iconId = 1;
$rightCol = []; $rightCol = [];
$scripts = [];
// serverside extra-Data
if ($crtIds = array_column($this->subject->getCriteria(), 'id'))
{
Util::checkNumeric($crtIds);
$crtExtraData = DB::World()->select('SELECT criteria_id AS ARRAY_KEY, type AS ARRAY_KEY2, value1, value2, ScriptName FROM achievement_criteria_data WHERE criteria_id IN (?a)', $crtIds);
}
else
$crtExtraData = [];
foreach ($this->subject->getCriteria() as $i => $crt) foreach ($this->subject->getCriteria() as $i => $crt)
{ {
@@ -366,10 +350,10 @@ class AchievementPage extends GenericPage
$tmp['icon'] = $iconId; $tmp['icon'] = $iconId;
$this->criteria['icons'][] = array( $this->criteria['icons'][] = array(
'itr' => $iconId++, 'itr' => $iconId++,
'type' => Type::getJSGlobalString(Type::ACHIEVEMENT), 'type' => 'g_achievements',
'id' => $obj, 'id' => $obj,
); );
$this->extendGlobalIds(Type::ACHIEVEMENT, $obj); $this->extendGlobalIds(TYPE_ACHIEVEMENT, $obj);
break; break;
// link to quest // link to quest
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
@@ -389,11 +373,11 @@ class AchievementPage extends GenericPage
'href' => '?spell='.$obj, 'href' => '?spell='.$obj,
'text' => ($crtName ?: SpellList::getName($obj)) 'text' => ($crtName ?: SpellList::getName($obj))
); );
$this->extendGlobalIds(Type::SPELL, $obj); $this->extendGlobalIds(TYPE_SPELL, $obj);
$tmp['icon'] = $iconId; $tmp['icon'] = $iconId;
$this->criteria['icons'][] = array( $this->criteria['icons'][] = array(
'itr' => $iconId++, 'itr' => $iconId++,
'type' => Type::getJSGlobalString(Type::SPELL), 'type' => 'g_spells',
'id' => $obj, 'id' => $obj,
); );
break; break;
@@ -413,7 +397,7 @@ class AchievementPage extends GenericPage
$tmp['icon'] = $iconId; $tmp['icon'] = $iconId;
$this->criteria['icons'][] = array( $this->criteria['icons'][] = array(
'itr' => $iconId++, 'itr' => $iconId++,
'type' => Type::getJSGlobalString(Type::ITEM), 'type' => 'g_items',
'id' => $obj, 'id' => $obj,
'count' => $qty, 'count' => $qty,
); );
@@ -434,84 +418,11 @@ class AchievementPage extends GenericPage
'text' => $crtName, 'text' => $crtName,
); );
break; break;
// link to emote
case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
$tmp['link'] = array(
'href' => '?emote='.$obj,
'text' => $crtName,
);
break;
default: default:
// Add a gold coin icon if required // Add a gold coin icon if required
$tmp['extraText'] = $displayMoney ? Util::formatMoney($qty) : $crtName; $tmp['extraText'] = $displayMoney ? Util::formatMoney($qty) : $crtName;
break; break;
} }
if (!empty($crtExtraData[$crt['id']]))
{
$tmp['extraData'] = [];
foreach ($crtExtraData[$crt['id']] as $xType => $xData)
{
// just pick stuff, that can actually be linked
switch ($xType)
{
case 1: // TYPE_T_CREATURE
$tmp['extraData'][] = ['?npc='.$xData['value1'], CreatureList::getName($xData['value1'])];
break;
case 2: // TYPE_T_PLAYER_CLASS_RACE
case 21: // TYPE_S_PLAYER_CLASS_RACE
if ($xData['value1'])
$tmp['extraData'][] = ['?class='.$xData['value1'], (new CharClassList(array(['id', $xData['value1']])))->getField('name', true)];
if ($xData['value2'])
$tmp['extraData'][] = ['?race='.$xData['value2'], (new CharRaceList(array(['id', $xData['value2']])))->getField('name', true)];
break;
// case 3: // TYPE_T_PLAYER_LESS_HEALTH
// case 4: // TYPE_T_PLAYER_DEAD
case 5: // TYPE_S_AURA
case 7: // TYPE_T_AURA
$tmp['extraData'][] = ['?spell='.$xData['value1'], SpellList::getName($xData['value1'])];
break;
case 6: // TYPE_S_AREA
$tmp['extraData'][] = ['?zone='.$xData['value1'], ZoneList::getName($xData['value1'])];
break;
// case 8: // TYPE_VALUE
// case 9: // TYPE_T_LEVEL
// case 10: // TYPE_T_GENDER
case 11: // TYPE_SCRIPT
if ($xData['ScriptName'])
$scripts[] = $xData['ScriptName'];
break;
// case 12: // TYPE_MAP_DIFFICULTY
// case 13: // TYPE_MAP_PLAYER_COUNT
// case 14: // TYPE_T_TEAM
// case 15: // TYPE_S_DRUNK
case 16: // TYPE_HOLIDAY
if ($we = new WorldEventList(array(['holidayId', $xData['value1']])))
$tmp['extraData'][] = ['?event='.$we->id, $we->getField('name', true)];
break;
// case 17: // TYPE_BG_LOSS_TEAM_SCORE
// case 18: // TYPE_INSTANCE_SCRIPT
// case 19: // TYPE_S_EQUIPED_ITEM
case 20: // TYPE_MAP_ID
if ($z = new ZoneList(array(['mapIdBak', $xData['value1']])))
$tmp['extraData'][] = ['?zone='.$z->id, $z->getField('name', true)];
break;
// case 22: // TYPE_NTH_BIRTHDAY
case 23: // TYPE_S_KNOWN_TITLE
$tmp['extraData'][] = ['?title='.$xData['value1'], trim(str_replace('%s', '', (new TitleList(array(['id', $xData['value1']])))->getField('male', true)))];
break;
}
// moar stuffz
}
}
// If the right column // If the right column
if ($i % 2) if ($i % 2)
$this->criteria['data'][] = $tmp; $this->criteria['data'][] = $tmp;
@@ -522,26 +433,45 @@ class AchievementPage extends GenericPage
// If you found the second column - merge data from it to the end of the main body // If you found the second column - merge data from it to the end of the main body
if ($rightCol) if ($rightCol)
$this->criteria['data'] = array_merge($this->criteria['data'], $rightCol); $this->criteria['data'] = array_merge($this->criteria['data'], $rightCol);
// criteria have scripts
if (User::isInGroup(U_GROUP_EMPLOYEE) && $scripts)
{
$s = '[li]Script'.Lang::main('colon').'[ul][li]'.implode('[/li][li]', array_unique($scripts)).'[/li][/ul][/li]';
$this->infobox = substr_replace($this->infobox, $s, -5, 0);
}
} }
protected function generateTooltip() protected function generateTooltip($asError = false)
{ {
$power = new StdClass(); if ($asError)
if (!$this->subject->error) return '$WowheadPower.registerAchievement('.$this->typeId.', '.User::$localeId.', {});';
$x = '$WowheadPower.registerAchievement('.$this->typeId.', '.User::$localeId.",{\n";
$x .= "\tname_".User::$localeString.": '".Util::jsEscape($this->subject->getField('name', true))."',\n";
$x .= "\ticon: '".urlencode($this->subject->getField('iconString'))."',\n";
$x .= "\ttooltip_".User::$localeString.": '".$this->subject->renderTooltip()."'\n";
$x .= "});";
return $x;
}
public function display($override = '')
{
if ($this->mode != CACHE_TYPE_TOOLTIP)
return parent::display($override);
if (!$this->loadCache($tt))
{ {
$power->{'name_'.User::$localeString} = $this->subject->getField('name', true); $tt = $this->generateTooltip();
$power->icon = rawurlencode($this->subject->getField('iconString', true, true)); $this->saveCache($tt);
$power->{'tooltip_'.User::$localeString} = $this->subject->renderTooltip();
} }
return sprintf($this->powerTpl, $this->typeId, User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER)); header('Content-type: application/x-javascript; charset=utf-8');
die($tt);
}
public function notFound()
{
if ($this->mode != CACHE_TYPE_TOOLTIP)
return parent::notFound(Lang::game('achievement'), Lang::achievement('notFound'));
header('Content-type: application/x-javascript; charset=utf-8');
echo $this->generateTooltip(true);
exit();
} }
private function createMail(&$reqCss = false) private function createMail(&$reqCss = false)
@@ -550,35 +480,31 @@ class AchievementPage extends GenericPage
if ($_ = $this->subject->getField('mailTemplate')) if ($_ = $this->subject->getField('mailTemplate'))
{ {
$letter = DB::Aowow()->selectRow('SELECT * FROM ?_mails WHERE id = ?d', $_); $letter = DB::Aowow()->selectRow('SELECT * FROM ?_mailtemplate WHERE id = ?d', $_);
if (!$letter) if (!$letter)
return []; return [];
$reqCss = true; $reqCss = true;
$mail = array( $mail = array(
'id' => $_, 'delay' => null,
'delay' => null, 'sender' => null,
'sender' => null, 'subject' => Util::parseHtmlText(Util::localizedString($letter, 'subject', true)),
'attachments' => [], 'text' => Util::parseHtmlText(Util::localizedString($letter, 'text', true))
'subject' => Util::parseHtmlText(Util::localizedString($letter, 'subject', true)),
'text' => Util::parseHtmlText(Util::localizedString($letter, 'text', true))
); );
} }
else if ($_ = Util::parseHtmlText($this->subject->getField('text', true, true))) else if ($_ = Util::parseHtmlText($this->subject->getField('text', true, true)))
{ {
$reqCss = true; $reqCss = true;
$mail = array( $mail = array(
'id' => -$this->typeId, 'delay' => null,
'delay' => null, 'sender' => null,
'sender' => null, 'subject' => Util::parseHtmlText($this->subject->getField('subject', true, true)),
'attachments' => [], 'text' => $_
'subject' => Util::parseHtmlText($this->subject->getField('subject', true, true)),
'text' => $_
); );
} }
if ($_ = CreatureList::getName($this->subject->getField('sender'))) if ($_ = CreatureList::getName($this->subject->getField('sender')))
$mail['sender'] = sprintf(Lang::mail('mailBy'), $this->subject->getField('sender'), $_); $mail['sender'] = sprintf(Lang::quest('mailBy'), $this->subject->getField('sender'), $_);
return $mail; return $mail;
} }

View File

@@ -8,17 +8,14 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader() // tabId 0: Database g_initHeader()
class AchievementsPage extends GenericPage class AchievementsPage extends GenericPage
{ {
use TrListPage; use ListPage;
protected $type = Type::ACHIEVEMENT; protected $type = TYPE_ACHIEVEMENT;
protected $tpl = 'achievements'; protected $tpl = 'achievements';
protected $path = [0, 9]; protected $path = [0, 9];
protected $tabId = 0; protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE; protected $mode = CACHE_TYPE_PAGE;
protected $js = [[JS_FILE, 'filters.js']]; protected $js = ['filters.js'];
protected $_get = ['filter' => ['filter' => FILTER_UNSAFE_RAW]];
protected $validCats = array( protected $validCats = array(
92 => true, 92 => true,
96 => [14861, 14862, 14863], 96 => [14861, 14862, 14863],
@@ -45,8 +42,8 @@ class AchievementsPage extends GenericPage
public function __construct($pageCall, $pageParam) public function __construct($pageCall, $pageParam)
{ {
$this->filterObj = new AchievementListFilter();
$this->getCategoryFromUrl($pageParam); $this->getCategoryFromUrl($pageParam);
$this->filterObj = new AchievementListFilter(false, $this->category);
parent::__construct($pageCall, $pageParam); parent::__construct($pageCall, $pageParam);
@@ -66,12 +63,9 @@ class AchievementsPage extends GenericPage
$conditions[] = ['category', (int)end($this->category)]; $conditions[] = ['category', (int)end($this->category)];
// recreate form selection // recreate form selection
$this->filter = $this->filterObj->getForm(); $this->filter = $this->filterObj->getForm('form');
$this->filter['query'] = $this->_get['filter']; $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null;
$this->filter['initData'] = ['init' => 'achievements']; $this->filter['fi'] = $this->filterObj->getForm();
if ($x = $this->filterObj->getSetCriteria())
$this->filter['initData']['sc'] = $x;
if ($fiCnd = $this->filterObj->getConditions()) if ($fiCnd = $this->filterObj->getConditions())
$conditions[] = $fiCnd; $conditions[] = $fiCnd;
@@ -79,43 +73,52 @@ class AchievementsPage extends GenericPage
$acvList = new AchievementList($conditions); $acvList = new AchievementList($conditions);
if (!$acvList->getMatches()) if (!$acvList->getMatches())
{ {
$category = [!empty($this->category) ? (int)end($this->category) : 0]; $curCats = $catList = [!empty($this->category) ? (int)end($this->category) : 0];
while ($curCats)
{
$curCats = DB::Aowow()->SelectCol('SELECT Id FROM ?_achievementcategory WHERE parentCategory IN (?a)', $curCats);
$catList = array_merge($catList, $curCats);
}
$conditions = []; $conditions = [];
if ($fiCnd) if ($fiCnd)
$conditions[] = $fiCnd; $conditions[] = $fiCnd;
if ($catList = DB::Aowow()->SelectCol('SELECT Id FROM ?_achievementcategory WHERE parentCat IN (?a) OR parentCat2 IN (?a) ', $category, $category)) if ($catList)
$conditions[] = ['category', $catList]; $conditions[] = ['category', $catList];
$acvList = new AchievementList($conditions); $acvList = new AchievementList($conditions);
} }
$tabData = []; $params = $data = [];
if (!$acvList->error) if (!$acvList->error)
{ {
$tabData['data'] = array_values($acvList->getListviewData()); $data = $acvList->getListviewData();
// fill g_items, g_titles, g_achievements // fill g_items, g_titles, g_achievements
$this->extendGlobalData($acvList->getJSGlobals()); $this->extendGlobalData($acvList->getJSGlobals());
// if we are have different cats display field // if we are have different cats display field
if ($acvList->hasDiffFields(['category'])) if ($acvList->hasDiffFields(['category']))
$tabData['visibleCols'] = ['category']; $params['visibleCols'] = "$['category']";
if (!empty($this->filter['fi']['extraCols'])) if (!empty($this->filter['fi']['extraCols']))
$tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; $params['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
// create note if search limit was exceeded // create note if search limit was exceeded
if ($acvList->getMatches() > CFG_SQL_LIMIT_DEFAULT) if ($acvList->getMatches() > CFG_SQL_LIMIT_DEFAULT)
{ {
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_achievementsfound', $acvList->getMatches(), CFG_SQL_LIMIT_DEFAULT); $params['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_achievementsfound', $acvList->getMatches(), CFG_SQL_LIMIT_DEFAULT);
$tabData['_truncated'] = 1; $params['_truncated'] = 1;
} }
if ($this->filterObj->error) if ($this->filterObj->error)
$tabData['_errors'] = 1; $params['_errors'] = '$1';
} }
$this->lvTabs[] = ['achievement', $tabData]; $this->lvTabs[] = array(
'file' => 'achievement',
'data' => $data,
'params' => $params
);
// sort for dropdown-menus in filter // sort for dropdown-menus in filter
Lang::sort('game', 'si'); Lang::sort('game', 'si');
@@ -125,14 +128,20 @@ class AchievementsPage extends GenericPage
{ {
array_unshift($this->title, Util::ucFirst(Lang::game('achievements'))); array_unshift($this->title, Util::ucFirst(Lang::game('achievements')));
if ($this->category) if ($this->category)
array_unshift($this->title, Lang::achievement('cat', end($this->category))); {
$catrow = DB::Aowow()->SelectRow('SELECT * FROM ?_achievementcategory WHERE id = ?d', end($this->category));
array_unshift($this->title, Util::localizedString($catrow, 'name'));
}
} }
protected function generatePath() protected function generatePath()
{ {
if ($this->category) if ($this->category)
foreach ($this->category as $cat) {
$catrows = DB::Aowow()->SelectCol('SELECT id FROM ?_achievementcategory WHERE id IN (?a)', $this->category);
foreach ($catrows as $cat)
$this->path[] = $cat; $this->path[] = $cat;
}
} }
} }

View File

@@ -6,19 +6,13 @@ if (!defined('AOWOW_REVISION'))
class AdminPage extends GenericPage class AdminPage extends GenericPage
{ {
protected $tpl = null; // depends on the subject protected $tpl = null; // depends on the subject
protected $reqUGroup = U_GROUP_NONE; // actual group dependant on the subPage protected $reqUGroup = U_GROUP_NONE; // actual group dependant on the subPage
protected $reqAuth = true; protected $reqAuth = true;
protected $path = [4]; protected $path = [4];
protected $tabId = 4; protected $tabId = 4;
protected $_get = array(
'all' => ['filter' => FILTER_UNSAFE_RAW],
'type' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkInt'],
'typeid' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkInt'],
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'urldecode'],
);
private $generator = ''; private $generator = '';
public function __construct($pageCall, $pageParam) public function __construct($pageCall, $pageParam)
@@ -26,7 +20,7 @@ class AdminPage extends GenericPage
switch ($pageParam) switch ($pageParam)
{ {
case 'screenshots': case 'screenshots':
$this->reqUGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT; $this->reqUGroup = U_GROUP_STAFF | U_GROUP_SCREENSHOT;
$this->generator = 'handleScreenshots'; $this->generator = 'handleScreenshots';
$this->tpl = 'admin/screenshots'; $this->tpl = 'admin/screenshots';
@@ -44,34 +38,18 @@ class AdminPage extends GenericPage
case 'siteconfig': case 'siteconfig':
$this->reqUGroup = U_GROUP_ADMIN | U_GROUP_DEV; $this->reqUGroup = U_GROUP_ADMIN | U_GROUP_DEV;
$this->generator = 'handleConfig'; $this->generator = 'handleConfig';
$this->tpl = 'admin/siteconfig'; $this->tpl = 'list-page-generic';
array_push($this->path, 2, 18); array_push($this->path, 2, 18);
$this->name = 'Site Configuration'; $this->name = 'Site Configuration';
break; break;
case 'weight-presets':
$this->reqUGroup = U_GROUP_ADMIN | U_GROUP_DEV | U_GROUP_BUREAU;
$this->generator = 'handleWeightPresets';
$this->tpl = 'admin/weight-presets';
array_push($this->path, 2, 16);
$this->name = 'Weight Presets';
break;
case 'guides':
$this->reqUGroup = U_GROUP_STAFF;
$this->generator = 'handleGuideApprove';
$this->tpl = 'list-page-generic';
array_push($this->path, 1, 25);
$this->name = 'Pending Guides';
break;
default: // error out through unset template default: // error out through unset template
} }
parent::__construct($pageCall, $pageParam); parent::__construct($pageCall, $pageParam);
} }
protected function generateContent() : void protected function generateContent()
{ {
if (!$this->generator || function_exists($this->generator)) if (!$this->generator || function_exists($this->generator))
return; return;
@@ -79,60 +57,330 @@ class AdminPage extends GenericPage
$this->{$this->generator}(); $this->{$this->generator}();
} }
private function handleConfig() : void private function handleConfig()
{ {
$this->addScript( $this->addCSS(array(
[CSS_STRING, '.grid input[type=\'text\'], .grid input[type=\'number\'] { width:250px; text-align:left; }'], ['string' => '.grid input[type=\'text\'] { width:250px; }'],
[CSS_STRING, '.grid input[type=\'button\'] { width:65px; padding:2px; }'], ['string' => '.grid input[type=\'button\'] { width:65px; padding:2px; }'],
[CSS_STRING, '.grid a.tip { margin:0px 5px; opacity:0.8; }'], ['string' => '.disabled { opacity:0.4 !important; }'],
[CSS_STRING, '.grid a.tip:hover { opacity:1; }'], ['string' => '.grid a.tip { margin:0px 5px; opacity:0.8; }'],
[CSS_STRING, '.grid tr { height:30px; }'], ['string' => '.grid a.tip:hover { opacity:1; }'],
[CSS_STRING, '.grid .disabled { opacity:0.4 !important; }'], ['string' => '.status { position:absolute; right:5px; }'],
[CSS_STRING, '.grid .status { position:absolute; right:5px; }'], ));
);
$head = '<table class="grid"><tr><th><b>Key</b></th><th><b>Value</b></th><th style="width:150px;"><b>Options</b></th></tr>'; // well .. fuck!
$mainTab = []; ob_start();
$miscTab = []; ?>
foreach (Util::$configCats as $idx => $catName) <script type="text/javascript">
function createStatusIcon(errTxt)
{
function fadeout()
{ {
if ($rows = DB::Aowow()->select('SELECT * FROM ?_config WHERE cat = ?d ORDER BY `flags` DESC, `key` ASC', $idx)) $(this).animate({ opacity: '0.0' }, 250, null, function() {
{ $WH.de(this);
$buff = $head; $WH.Tooltip.hide()
foreach ($rows as $r) });
$buff .= $this->configAddRow($r);
if (!$idx) //cat: misc
$buff .= '<tr><td colspan="3"><a class="icon-add" onclick="cfg_add(this)">new configuration</a></td></tr>';
$buff .= '</table>';
if ($idx)
$mainTab[$catName] = $buff;
else
$miscTab[$catName] = $buff;
}
} }
foreach ($mainTab as $n => $t) var a = $WH.ce('a');
$this->lvTabs[] = [null, array( a.style.opacity = 0;
'data' => $t, a.className = errTxt ? 'icon-report' : 'icon-tick';
'name' => $n, g_addTooltip(a, errTxt || 'success', 'q');
'id' => Profiler::urlize($n) a.onclick = fadeout.bind(a);
)]; setTimeout(function () { $(a).animate({ opacity: '1.0' }, 250); }, 50);
setTimeout(fadeout.bind(a), 10000);
foreach ($miscTab as $n => $t) return a;
$this->lvTabs[] = [null, array(
'data' => $t,
'name' => $n,
'id' => Profiler::urlize($n)
)];
} }
private function handlePhpInfo() : void function cfg_add(el)
{ {
$this->addScript([ _self = el.parentNode.parentNode;
CSS_STRING, "\npre {margin: 0px; font-family: monospace;}\n" .
var tr = $WH.ce('tr');
tr.style.position = 'relative';
var td = $WH.ce('td'),
key = $WH.ce('input');
key.type = 'text';
key.name = 'key';
$WH.ae(td, key);
$WH.ae(tr, td);
var td = $WH.ce('td'),
val = $WH.ce('input');
val.type = 'text';
val.name = 'value';
$WH.ae(td, val);
$WH.ae(tr, td);
var td = $WH.ce('td'),
aCancel = $WH.ce('a'),
aSubmit = $WH.ce('a'),
status = $WH.ce('span');
aSubmit.className = 'icon-save tip';
g_addTooltip(aSubmit, 'Submit Setting', 'q');
aCancel.className = 'icon-delete tip';
g_addTooltip(aCancel, 'Cancel', 'q');
aSubmit.onclick = cfg_new.bind(aSubmit, key, val);
aCancel.onclick = function () {
$WH.Tooltip.hide();
$WH.de(this.parentNode.parentNode);
};
status.className = 'status';
$WH.ae(td, aSubmit);
$WH.ae(td, $WH.ct('|'));
$WH.ae(td, aCancel);
$WH.ae(td, status);
$WH.ae(tr, td);
_self.parentNode.insertBefore(tr, _self);
key.focus();
}
function cfg_new(elKey, elVal)
{
var
_td = this.parentNode,
_row = this.parentNode.parentNode,
_status = $(_td).find('.status')[0];
// already performing action
if (_status.lastChild && _status.lastChild.tagName == 'IMG')
return;
else if (_status.lastChild && _status.lastChild.tagName == 'A')
$WH.ee(_status);
if (!elKey.value || !elVal.value)
{
$WH.ae(_status, createStatusIcon('key or value are empty'));
return;
}
var
key = elKey.value.toLowerCase().trim(),
value = elVal.value.trim();
$(_status).append(CreateAjaxLoader());
new Ajax('?admin=siteconfig&action=add&id=' + key + '&val=' + value, {
method: 'get',
onSuccess: function(xhr) {
$WH.ee(_status);
if (!xhr.responseText) {
$WH.ee(_row);
$(_row).append($('<td>' + key + '</td>')).append($('<td><input id="' + key + '" type="text" name="' + key + '" value="' + value + '" /></td>'));
var
td = $WH.ce('td'),
a = $WH.ce('a'),
sp = $WH.ce('span');
g_addTooltip(a, 'Save Changes', 'q');
a.onclick = cfg_submit.bind(a, key);
a.className = 'icon-save tip';
$WH.ae(td, a);
a = $WH.ce('a');
a.className = 'icon-refresh tip disabled';
$WH.ae(td, $WH.ct('|'));
$WH.ae(td, a);
a = $WH.ce('a');
g_addTooltip(a, 'Remove Setting', 'q');
a.onclick = cfg_remove.bind(a, key);
a.className = 'icon-delete tip';
$WH.ae(td, $WH.ct('|'));
$WH.ae(td, a);
sp.className = 'status';
$WH.ae(sp, createStatusIcon());
$WH.ae(td, sp);
$WH.ae(_row, td);
}
else {
$WH.ae(_status, createStatusIcon(xhr.responseText));
}
}
});
}
function cfg_submit(id)
{
var
node = $WH.ge(id),
_td = this.parentNode,
_status = $(_td).find('.status')[0];
if (!node)
return;
var value = 0;
// already performing action
if (_status.lastChild && _status.lastChild.tagName == 'IMG')
return;
else if (_status.lastChild && _status.lastChild.tagName == 'A')
$WH.ee(_status);
if (node.tagName == 'DIV')
{
// bitmask
$(node).find('input[type="checkbox"]').each(function(idx, opt) {
if (opt.checked)
value |= (1 << opt.value);
});
// boolean
$(node).find('input[type="radio"]').each(function(idx, opt) {
if (opt.checked)
value = opt.value;
});
}
else if (node.tagName == 'SELECT') // opt-list
{
$(node).find('option').each(function(idx, opt) {
if (opt.selected)
value = opt.value;
});
}
else if (node.tagName == 'INPUT') // string or numeric
{
if (node.value.search(/[^\d\s\/\*\-\+\.]/i) == -1)
node.value = eval(node.value);
value = node.value;
}
value = value.toString().trim();
if (!value.length)
{
$WH.ae(_status, createStatusIcon('value is empty'));
return;
}
$(_status).append(CreateAjaxLoader());
new Ajax('?admin=siteconfig&action=update&id=' + id + '&val=' + value, {
method: 'get',
onSuccess: function(xhr) {
$WH.ee(_status);
$WH.ae(_status, createStatusIcon(xhr.responseText));
}
});
}
function cfg_default(id, val)
{
var node = $WH.ge(id);
if (!node)
return;
if (node.tagName == 'DIV')
{
// bitmask
$(node).find('input[type="checkbox"]').each(function(idx, opt) { opt.checked = !!(val & (1 << opt.value)); });
// boolean
$(node).find('input[type="radio"]').each(function(idx, opt) { opt.checked = !!opt.value == !!val; });
}
else if (node.tagName == 'SELECT') // opt-list
$(node).find('option').each(function(idx, opt) { opt.selected = opt.value == val; });
else if (node.tagName == 'INPUT') // string or numeric
node.value = val;
}
function cfg_remove(id)
{
var
_td = this.parentNode,
_status = $(_td).find('.status')[0];
// already performing action
if (_status.lastChild && _status.lastChild.tagName == 'IMG')
return;
else if (_status.lastChild && _status.lastChild.tagName == 'A')
$WH.ee(_status);
if (!confirm('Confirm remove'))
return;
$(_status).append(CreateAjaxLoader());
new Ajax('?admin=siteconfig&action=remove&id=' + id, {
method: 'get',
onSuccess: function(xhr) {
if (!xhr.responseText)
$WH.de(_td.parentNode);
else {
$WH.ee(_status);
$WH.ae(_status, createStatusIcon(xhr.responseText));
}
}
});
}
</script>
<?php
$this->extraHTML = ob_get_contents();
ob_end_clean();
// eof (end of fuckup)
$head = '<table class="grid"><tr><th><b>Key</b></th><th><b>Value</b></th><th style="width:150px;"><b>Options</b></th></tr>';
// for aowow
if ($rows = DB::Aowow()->select('SELECT * FROM ?_config WHERE (flags & ?d) = 0 ORDER BY `key` ASC', CON_FLAG_PHP))
{
$buff = $head;
foreach ($rows as $r)
$buff .= $this->configAddRow($r);
$buff .= '</table>';
$this->lvTabs[] = array(
'file' => null,
'data' => $buff,
'params' => array(
'name' => 'Aowow',
'id' => 'aowow'
)
);
}
// for php
$rows = DB::Aowow()->select('SELECT * FROM ?_config WHERE flags & ?d ORDER BY `key` ASC', CON_FLAG_PHP);
$buff = $head;
foreach ($rows as $r)
$buff .= $this->configAddRow($r);
$buff .= '<tr><td colspan="3"><a class="icon-add" onclick="cfg_add(this)">new configuration</a></td></tr>';
$buff .= '</table>';
$this->lvTabs[] = array(
'file' => null,
'data' => $buff,
'params' => array(
'name' => 'PHP',
'id' => 'php'
)
);
}
private function handlePhpInfo()
{
$this->addCSS([
'string' => "\npre {margin: 0px; font-family: monospace;}\n" .
"td, th { border: 1px solid #000000; vertical-align: baseline;}\n" . "td, th { border: 1px solid #000000; vertical-align: baseline;}\n" .
".p {text-align: left;}\n" . ".p {text-align: left;}\n" .
".e {background-color: #ccccff; font-weight: bold; color: #000000;}\n" . ".e {background-color: #ccccff; font-weight: bold; color: #000000;}\n" .
@@ -175,47 +423,54 @@ class AdminPage extends GenericPage
else else
$name .= $p[0]; $name .= $p[0];
$this->lvTabs[] = [null, array( $this->lvTabs[] = array(
'data' => $body, 'file' => null,
'id' => strtolower(strtr($name, [' ' => ''])), 'data' => $body,
'name' => $name 'params' => array(
)]; 'id' => strtolower(strtr($name, [' ' => ''])),
'name' => $name
)
);
} }
} }
else else
{ {
$this->lvTabs[] = [null, array( $this->lvTabs[] = array(
'data' => $buff, 'file' => null,
'id' => strtolower($names[$i]), 'data' => $buff,
'name' => $names[$i] 'params' => array(
)]; 'id' => strtolower($names[$i]),
'name' => $names[$i]
)
);
} }
} }
} }
private function handleScreenshots() : void private function handleScreenshots()
{ {
$this->addScript( $this->addJS('screenshot.js');
[JS_FILE, 'screenshot.js'], $this->addCSS(array(
[CSS_STRING, '.layout {margin: 0px 25px; max-width: inherit; min-width: 1200px; }'], ['string' => '.layout {margin: 0px 25px; max-width: inherit; min-width: 1200px; }'],
[CSS_STRING, '#highlightedRow { background-color: #322C1C; }'], ['string' => '#highlightedRow { background-color: #322C1C; }']
); ));
$ssGetAll = $this->_get['all']; $ssGetAll = isset($_GET['all']) && empty($_GET['all']);
$ssPages = []; $ssPages = [];
$ssData = []; $ssData = [];
$nMatches = 0; $nMatches = 0;
if ($this->_get['type'] && $this->_get['typeId']) if (!empty($_GET['type']) && !empty($_GET['typeid']))
{ {
$ssData = CommunityContent::getScreenshotsForManager($this->_get['type'], $this->_get['typeid']); $ssData = CommunityContent::getScreenshotsForManager(intVal($_GET['type']), intVal($_GET['typeid']));
$nMatches = count($ssData); $nMatches = count($ssData);
} }
else if ($this->_get['user']) else if (!empty($_GET['user']))
{ {
if (mb_strlen($this->_get['user']) >= 3) $name = urldecode($_GET['user']);
if (strlen($name) > 3)
{ {
if ($uId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE displayName = ?', ucFirst($this->_get['user']))) if ($uId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE displayName = ?', ucFirst($name)))
{ {
$ssData = CommunityContent::getScreenshotsForManager(0, 0, $uId); $ssData = CommunityContent::getScreenshotsForManager(0, 0, $uId);
$nMatches = count($ssData); $nMatches = count($ssData);
@@ -231,60 +486,11 @@ class AdminPage extends GenericPage
$this->ssNFound = $nMatches; // ssm_numPagesFound $this->ssNFound = $nMatches; // ssm_numPagesFound
} }
private function handleWeightPresets() : void
{
$this->addScript(
[JS_FILE, 'filters.js'],
[CSS_STRING, '.wt-edit {display:inline-block; vertical-align:top; width:350px;}'],
);
$head = $body = '';
$scales = DB::Aowow()->select('SELECT class AS ARRAY_KEY, id AS ARRAY_KEY2, name, icon FROM ?_account_weightscales WHERE userId = 0');
$weights = DB::Aowow()->selectCol('SELECT awd.id AS ARRAY_KEY, awd.field AS ARRAY_KEY2, awd.val FROM ?_account_weightscale_data awd JOIN ?_account_weightscales ad ON awd.id = ad.id WHERE ad.userId = 0');
foreach ($scales as $cl => $data)
{
$ul = '';
foreach ($data as $id => $s)
{
$weights[$id]['__icon'] = $s['icon'];
$ul .= '[url=# onclick="loadScale.bind(this, '.$id.')();"]'.$s['name'].'[/url][br]';
}
$head .= '[td=header]'.Lang::game('cl', $cl).'[/td]';
$body .= '[td valign=top]'.$ul.'[/td]';
}
$this->extraText = '[table class=grid][tr]'.$head.'[/tr][tr]'.$body.'[/tr][/table]';
$this->extraHTML = '<script type="text/javascript">var wt_presets = '.Util::toJSON($weights).";</script>\n\n";
}
private function handleGuideApprove() : void
{
$pending = new GuideList([['status', GUIDE_STATUS_REVIEW]]);
if ($pending->error)
$data = [];
else
{
$data = $pending->getListviewData();
$latest = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, MAX(`rev`) FROM ?_articles WHERE `type` = ?d AND `typeId` IN (?a) GROUP BY `rev`', Type::GUIDE, $pending->getFoundIDs());
foreach ($latest as $id => $rev)
$data[$id]['rev'] = $rev;
}
$this->lvTabs[] = ['guide', array(
'data' => array_values($data),
'hiddenCols' => ['patch', 'comments', 'views', 'rating'],
'extraCols' => '$_'
), 'guideAdminCol'];
}
private function configAddRow($r) private function configAddRow($r)
{ {
$buff = '<tr>'; $buff = '<tr>';
$info = explode(' - ', $r['comment']); $info = explode(' - ', $r['comment']);
$key = $r['flags'] & CON_FLAG_PHP ? strtolower($r['key']) : strtoupper($r['key']); $key = $r['flags'] & CON_FLAG_PHP ? strtolower($r['key']) : 'CFG_'.strtoupper($r['key']);
// name // name
if (!empty($info[1])) if (!empty($info[1]))
@@ -316,7 +522,7 @@ class AdminPage extends GenericPage
$buff .= '</div></td>'; $buff .= '</div></td>';
} }
else else
$buff .= '<td><input id="'.$key.'" type="'.($r['flags'] & CON_FLAG_TYPE_STRING ? 'text" placeholder="<empty>' : 'number'.($r['flags'] & CON_FLAG_TYPE_FLOAT ? '" step="any' : '')).'" name="'.$key.'" value="'.$r['value'].'" /></td>'; $buff .= '<td><input id="'.$key.'" type="text" name="'.$key.'" value="'.$r['value'].'" /></td>';
// actions // actions
$buff .= '<td style="position:relative;">'; $buff .= '<td style="position:relative;">';

View File

@@ -1,146 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 102: Areatrigger g_initPath()
// tabid 0: Database g_initHeader()
class AreaTriggerPage extends GenericPage
{
use TrDetailPage;
protected $type = Type::AREATRIGGER;
protected $typeId = 0;
protected $tpl = 'detail-page-generic';
protected $path = [0, 102];
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
protected $reqUGroup = U_GROUP_STAFF;
public function __construct($pageCall, $id)
{
$this->contribute = CONTRIBUTE_NONE;
parent::__construct($pageCall, $id);
$this->typeId = intVal($id);
$this->subject = new AreaTriggerList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->notFound(Lang::game('areatrigger'), Lang::areatrigger('notFound'));
$this->name = $this->subject->getField('name') ?: 'AT #'.$this->typeId;
}
protected function generatePath()
{
$this->path[] = $this->subject->getField('type');
}
protected function generateTitle()
{
array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('areatrigger')));
}
protected function generateContent()
{
$this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
$_type = $this->subject->getField('type');
/****************/
/* Main Content */
/****************/
// get spawns
$map = null;
if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL))
{
$ta = $this->subject->getField('teleportA');
$tf = $this->subject->getField('teleportF');
$tx = $this->subject->getField('teleportX');
$ty = $this->subject->getField('teleportY');
$to = $this->subject->getField('teleportO');
// add teleport target
if ($ta && $tx && $ty)
{
$o = Util::O2Deg($to);
$endPoint = array($tx, $ty, array(
'type' => 4,
'tooltip' => array(
'Teleport Destination' => array(
'info' => ['Orientation'.Lang::main('colon').$o[0].'° ('.$o[1].')']
)
)
));
if (isset($spawns[$ta][$tf]))
$spawns[$ta][$tf]['coords'][] = $endPoint;
else
$spawns[$ta][$tf]['coords'] = [$endPoint];
}
$map = array(
'data' => ['parent' => 'mapper-generic'],
'mapperData' => &$spawns
);
foreach ($spawns as $areaId => &$areaData)
$map['extra'][$areaId] = ZoneList::getName($areaId);
}
// smart AI
$sai = null;
if ($_type == AT_TYPE_SMART)
{
$sai = new SmartAI(SAI_SRC_TYPE_AREATRIGGER, $this->typeId, ['name' => $this->name, 'teleportA' => $this->subject->getField('teleportA')]);
if ($sai->prepare())
$this->extendGlobalData($sai->getJSGlobals());
}
$this->map = $map;
$this->infobox = false;
$this->smartAI = $sai ? $sai->getMarkdown() : null;
$this->redButtons = array(
BUTTON_LINKS => false,
BUTTON_WOWHEAD => false
);
/**************/
/* Extra Tabs */
/**************/
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[] = ['quest', ['data' => array_values($relQuest->getListviewData())]];
}
}
else if ($_type == AT_TYPE_TELEPORT)
{
$relZone = new ZoneList(array(['id', $this->subject->getField('teleportA')]));
if (!$relZone->error)
{
$this->lvTabs[] = ['zone', ['data' => array_values($relZone->getListviewData())]];
}
}
else if ($_type == AT_TYPE_SCRIPT)
{
$relTrigger = new AreaTriggerList(array(['id', $this->typeId, '!'], ['name', $this->subject->getField('name')]));
if (!$relTrigger->error)
{
$this->lvTabs[] = ['areatrigger', ['data' => array_values($relTrigger->getListviewData()), 'name' => Util::ucFirst(Lang::game('areatrigger'))], 'areatrigger'];
}
}
}
}
?>

View File

@@ -1,89 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 102: Areatrigger g_initPath()
// tabid 0: Database g_initHeader()
class AreaTriggersPage extends GenericPage
{
use TrListPage;
protected $type = Type::AREATRIGGER;
protected $tpl = 'areatriggers';
protected $path = [0, 102];
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
protected $validCats = [0, 1, 2, 3, 4, 5];
protected $js = [[JS_FILE, 'filters.js']];
protected $reqUGroup = U_GROUP_STAFF;
protected $_get = ['filter' => ['filter' => FILTER_UNSAFE_RAW]];
public function __construct($pageCall, $pageParam)
{
$this->getCategoryFromUrl($pageParam);;
if (isset($this->category[0]))
header('Location: ?areatriggers&filter=ty='.$this->category[0], true, 302);
$this->filterObj = new AreaTriggerListFilter();
parent::__construct($pageCall, $pageParam);
$this->name = Util::ucFirst(Lang::game('areatriggers'));
}
protected function generateContent()
{
// recreate form selection
$this->filter = $this->filterObj->getForm();
$this->filter['query'] = $this->_get['filter'];
$this->filter['initData'] = ['init' => 'areatrigger'];
if ($x = $this->filterObj->getSetCriteria())
$this->filter['initData']['sc'] = $x;
$conditions = [];
if ($_ = $this->filterObj->getConditions())
$conditions[] = $_;
$tabData = [];
$trigger = new AreaTriggerList($conditions);
if (!$trigger->error)
{
$tabData['data'] = array_values($trigger->getListviewData());
// create note if search limit was exceeded; overwriting 'note' is intentional
if ($trigger->getMatches() > CFG_SQL_LIMIT_DEFAULT)
{
$tabData['note'] = sprintf(Util::$tryFilteringEntityString, $trigger->getMatches(), '"'.Lang::game('areatriggers').'"', CFG_SQL_LIMIT_DEFAULT);
$tabData['_truncated'] = 1;
}
if ($this->filterObj->error)
$tabData['_errors'] = 1;
}
$this->lvTabs[] = ['areatrigger', $tabData, 'areatrigger'];
}
protected function generateTitle()
{
array_unshift($this->title, $this->name);
$form = $this->filterObj->getForm();
if (isset($form['ty']) && count($form['ty']) == 1)
array_unshift($this->title, Lang::areatrigger('types', $form['ty'][0]));
}
protected function generatePath()
{
$form = $this->filterObj->getForm();
if (isset($form['ty']) && count($form['ty']) == 1)
$this->path[] = $form['ty'];
}
}
?>

View File

@@ -1,144 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 5: Profiler g_initPath()
// tabId 1: Tools g_initHeader()
class ArenaTeamPage extends GenericPage
{
use TrProfiler;
protected $lvTabs = [];
protected $type = Type::ARENA_TEAM;
protected $tabId = 1;
protected $path = [1, 5, 3];
protected $tpl = 'roster';
protected $js = [[JS_FILE, 'profile_all.js'], [JS_FILE, 'profile.js']];
protected $css = [[CSS_FILE, 'Profiler.css']];
public function __construct($pageCall, $pageParam)
{
if (!CFG_PROFILER_ENABLE)
$this->error();
$params = array_map('urldecode', explode('.', $pageParam));
if ($params[0])
$params[0] = Profiler::urlize($params[0]);
if (isset($params[1]))
$params[1] = Profiler::urlize($params[1]);
parent::__construct($pageCall, $pageParam);
if (count($params) == 1 && intval($params[0]))
{
$this->subject = new LocalArenaTeamList(array(['at.id', intval($params[0])]));
if ($this->subject->error)
$this->notFound();
header('Location: '.$this->subject->getProfileUrl(), true, 302);
}
else if (count($params) == 3)
{
$this->getSubjectFromUrl($pageParam);
if (!$this->subjectName)
$this->notFound();
// 3 possibilities
// 1) already synced to aowow
if ($subject = DB::Aowow()->selectRow('SELECT id, realmGUID, cuFlags FROM ?_profiler_arena_team WHERE realm = ?d AND nameUrl = ?', $this->realmId, Profiler::urlize($this->subjectName)))
{
if ($subject['cuFlags'] & PROFILER_CU_NEEDS_RESYNC)
{
$this->handleIncompleteData($subject['realmGUID']);
return;
}
$this->subjectGUID = $subject['id'];
$this->subject = new LocalArenaTeamList(array(['id', $subject['id']]));
if ($this->subject->error)
$this->notFound();
$this->profile = $params;
$this->name = sprintf(Lang::profiler('arenaRoster'), $this->subject->getField('name'));
}
// 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)
else if ($team = DB::Characters($this->realmId)->selectRow('SELECT at.arenaTeamId AS realmGUID, at.name, at.type FROM arena_team at WHERE at.name = ?', Util::ucFirst($this->subjectName)))
{
$team['realm'] = $this->realmId;
$team['cuFlags'] = PROFILER_CU_NEEDS_RESYNC;
// create entry from realm with basic info
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team (?#) VALUES (?a)', array_keys($team), array_values($team));
$this->handleIncompleteData($team['realmGUID']);
}
// 3) does not exist at all
else
$this->notFound();
}
else
$this->notFound();
}
protected function generateTitle()
{
$team = !empty($this->subject) ? $this->subject->getField('name') : $this->subjectName;
$team .= ' ('.$this->realm.' - '.Lang::profiler('regions', $this->region).')';
array_unshift($this->title, $team, Util::ucFirst(Lang::profiler('profiler')));
}
protected function generateContent()
{
if ($this->doResync)
return;
$this->addScript([JS_FILE, '?data=realms.weight-presets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
$this->redButtons[BUTTON_RESYNC] = [$this->subjectGUID, 'arena-team'];
/****************/
/* Main Content */
/****************/
// statistic calculations here
/**************/
/* Extra Tabs */
/**************/
// tab: members
$member = new LocalProfileList(array(['atm.arenaTeamId', $this->subjectGUID]));
if (!$member->error)
{
$this->lvTabs[] = ['profile', array(
'data' => array_values($member->getListviewData(PROFILEINFO_CHARACTER | PROFILEINFO_ARENA)),
'sort' => [-15],
'visibleCols' => ['race', 'classs', 'level', 'talents', 'gearscore', 'rating', 'wins', 'losses'],
'hiddenCols' => ['guild', 'location']
)];
}
}
public function notFound(string $title = '', string $msg = '') : void
{
parent::notFound($title ?: Util::ucFirst(Lang::profiler('profiler')), $msg ?: Lang::profiler('notFound', 'arenateam'));
}
private function handleIncompleteData($teamGuid)
{
//display empty page and queue status
$newId = Profiler::scheduleResync(Type::ARENA_TEAM, $this->realmId, $teamGuid);
$this->doResync = ['arena-team', $newId];
$this->initialSync();
}
}
?>

View File

@@ -1,123 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 5: Profiler g_initPath()
// tabId 1: Tools g_initHeader()
class ArenaTeamsPage extends GenericPage
{
use TrProfiler;
protected $type = Type::ARENA_TEAM;
protected $tabId = 1;
protected $path = [1, 5, 3];
protected $tpl = 'arena-teams';
protected $js = [[JS_FILE, 'filters.js'], [JS_FILE, 'profile_all.js'], [JS_FILE, 'profile.js']];
protected $_get = ['filter' => ['filter' => FILTER_UNSAFE_RAW]];
public function __construct($pageCall, $pageParam)
{
if (!CFG_PROFILER_ENABLE)
$this->error();
$this->getSubjectFromUrl($pageParam);
$this->filterObj = new ArenaTeamListFilter();
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');
}
parent::__construct($pageCall, $pageParam);
$this->name = Lang::profiler('arenaTeams');
$this->subCat = $pageParam ? '='.$pageParam : '';
}
protected function generateTitle()
{
if ($this->realm)
array_unshift($this->title, $this->realm,/* CFG_BATTLEGROUP,*/ Lang::profiler('regions', $this->region), Lang::profiler('arenaTeams'));
else if ($this->region)
array_unshift($this->title, Lang::profiler('regions', $this->region), Lang::profiler('arenaTeams'));
else
array_unshift($this->title, Lang::profiler('arenaTeams'));
}
protected function generateContent()
{
$this->addScript([JS_FILE, '?data=realms&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
$conditions = [];
if (!User::isInGroup(U_GROUP_EMPLOYEE))
$conditions[] = ['at.rating', 1000, '>'];
if ($_ = $this->filterObj->getConditions())
$conditions[] = $_;
// recreate form selection
$this->filter = $this->filterObj->getForm();
$this->filter['query'] = $this->_get['filter'];
$this->filter['initData'] = ['type' => 'arenateams'];
$tabData = array(
'id' => 'arena-teams',
'hideCount' => 1,
'sort' => [-16],
'extraCols' => ['$Listview.extraCols.members'],
'visibleCols' => ['rank', 'wins', 'losses', 'rating'],
'hiddenCols' => ['arenateam', 'guild'],
);
if (empty($this->filter['sz']))
$tabData['visibleCols'][] = 'size';
$miscParams = [];
if ($this->realm)
$miscParams['sv'] = $this->realm;
if ($this->region)
$miscParams['rg'] = $this->region;
$teams = new RemoteArenaTeamList($conditions, $miscParams);
if (!$teams->error)
{
$teams->initializeLocalEntries();
$dFields = $teams->hasDiffFields(['faction', 'type']);
if (!($dFields & 0x1))
$tabData['hiddenCols'][] = 'faction';
$tabData['data'] = array_values($teams->getListviewData());
// create note if search limit was exceeded
if ($this->filter['query'] && $teams->getMatches() > CFG_SQL_LIMIT_DEFAULT)
{
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound2', $this->sumSubjects, $teams->getMatches());
$tabData['_truncated'] = 1;
}
else if ($teams->getMatches() > CFG_SQL_LIMIT_DEFAULT)
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound', $this->sumSubjects, 0);
if ($this->filterObj->error)
$tabData['_errors'] = 1;
}
$this->lvTabs[] = ['profile', $tabData, 'membersCol'];
Lang::sort('game', 'cl');
Lang::sort('game', 'ra');
}
}
?>

View File

@@ -8,15 +8,15 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader() // tabId 0: Database g_initHeader()
class ClassPage extends GenericPage class ClassPage extends GenericPage
{ {
use TrDetailPage; use DetailPage;
protected $type = Type::CHR_CLASS; protected $type = TYPE_CLASS;
protected $typeId = 0; protected $typeId = 0;
protected $tpl = 'detail-page-generic'; protected $tpl = 'detail-page-generic';
protected $path = [0, 12]; protected $path = [0, 12];
protected $tabId = 0; protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE; protected $mode = CACHE_TYPE_PAGE;
protected $js = [[JS_FILE, 'swfobject.js']]; protected $js = ['swfobject.js'];
public function __construct($pageCall, $id) public function __construct($pageCall, $id)
{ {
@@ -43,7 +43,7 @@ class ClassPage extends GenericPage
protected function generateContent() protected function generateContent()
{ {
$this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]); $this->addJS('?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']);
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags')); $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
$_mask = 1 << ($this->typeId - 1); $_mask = 1 << ($this->typeId - 1);
@@ -96,7 +96,7 @@ class ClassPage extends GenericPage
$this->expansion = Util::$expansionString[$this->subject->getField('expansion')]; $this->expansion = Util::$expansionString[$this->subject->getField('expansion')];
$this->headIcons = ['class_'.strtolower($this->subject->getField('fileString'))]; $this->headIcons = ['class_'.strtolower($this->subject->getField('fileString'))];
$this->redButtons = array( $this->redButtons = array(
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId], BUTTON_LINKS => ['color' => '', 'linkId' => ''],
BUTTON_WOWHEAD => true, BUTTON_WOWHEAD => true,
BUTTON_TALENT => ['href' => '?talent#'.Util::$tcEncoding[$tcClassId[$this->typeId] * 3], 'pet' => false], BUTTON_TALENT => ['href' => '?talent#'.Util::$tcEncoding[$tcClassId[$this->typeId] * 3], 'pet' => false],
BUTTON_FORUM => false // todo (low): CFG_BOARD_URL + X BUTTON_FORUM => false // todo (low): CFG_BOARD_URL + X
@@ -126,25 +126,27 @@ class ClassPage extends GenericPage
'OR', 'OR',
['s.cuFlags', SPELL_CU_LAST_RANK, '&'], ['s.cuFlags', SPELL_CU_LAST_RANK, '&'],
['s.rankNo', 0] ['s.rankNo', 0]
], ]
CFG_SQL_LIMIT_NONE
); );
$genSpells = new SpellList($conditions); $genSpells = new SpellList($conditions);
if (!$genSpells->error) if (!$genSpells->error)
{ {
$this->extendGlobalData($genSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); $this->extendGlobalData($genSpells->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs[] = ['spell', array( $this->lvTabs[] = array(
'data' => array_values($genSpells->getListviewData()), 'file' => 'spell',
'id' => 'spells', 'data' => $genSpells->getListviewData(),
'name' => '$LANG.tab_spells', 'params' => array(
'visibleCols' => ['level', 'schools', 'type', 'classes'], 'id' => 'spells',
'hiddenCols' => ['reagents', 'skill'], 'name' => '$LANG.tab_spells',
'sort' => ['-level', 'type', 'name'], 'visibleCols' => "$['level', 'schools', 'type', 'classes']",
'computeDataFunc' => '$Listview.funcBox.initSpellFilter', 'hiddenCols' => "$['reagents', 'skill']",
'onAfterCreate' => '$Listview.funcBox.addSpellIndicator' 'sort' => "$['-level', 'type', 'name']",
)]; 'computeDataFunc' => '$Listview.funcBox.initSpellFilter',
'onAfterCreate' => '$Listview.funcBox.addSpellIndicator'
)
);
} }
// Tab: Items (grouped) // Tab: Items (grouped)
@@ -153,7 +155,7 @@ class ClassPage extends GenericPage
['requiredClass', $_mask, '&'], ['requiredClass', $_mask, '&'],
[['requiredClass', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!'], [['requiredClass', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!'],
['itemset', 0], // hmm, do or dont..? ['itemset', 0], // hmm, do or dont..?
CFG_SQL_LIMIT_NONE 0
); );
$items = new ItemList($conditions); $items = new ItemList($conditions);
@@ -161,21 +163,23 @@ class ClassPage extends GenericPage
{ {
$this->extendGlobalData($items->getJSGlobals()); $this->extendGlobalData($items->getJSGlobals());
$hiddenCols = null; if (!$items->hasDiffFields(['requiredRace']))
if ($items->hasDiffFields(['requiredRace'])) $hidden = "$['side']";
$hiddenCols = ['side'];
$this->lvTabs[] = ['item', array( $this->lvTabs[] = array(
'data' => array_values($items->getListviewData()), 'file' => 'item',
'id' => 'items', 'data' => $items->getListviewData(),
'name' => '$LANG.tab_items', 'params' => array(
'visibleCols' => ['dps', 'armor', 'slot'], 'id' => 'items',
'hiddenCols' => $hiddenCols, 'name' => '$LANG.tab_items',
'computeDataFunc' => '$Listview.funcBox.initSubclassFilter', 'visibleCols' => "$['dps', 'armor', 'slot']",
'onAfterCreate' => '$Listview.funcBox.addSubclassIndicator', 'hiddenCols' => isset($hidden) ? $hidden : null,
'note' => sprintf(Util::$filterResultString, '?items&filter=cr=152;crs='.$this->typeId.';crv=0'), 'computeDataFunc' => '$Listview.funcBox.initSubclassFilter',
'_truncated' => 1 'onAfterCreate' => '$Listview.funcBox.addSubclassIndicator',
)]; 'note' => sprintf(Util::$filterResultString, '?items&filter=cr=152;crs='.$this->typeId.';crv=0'),
'_truncated' => 1
)
);
} }
// Tab: Quests // Tab: Quests
@@ -189,10 +193,11 @@ class ClassPage extends GenericPage
{ {
$this->extendGlobalData($quests->getJSGlobals()); $this->extendGlobalData($quests->getJSGlobals());
$this->lvTabs[] = ['quest', array( $this->lvTabs[] = array(
'data' => array_values($quests->getListviewData()), 'file' => 'quest',
'sort' => ['reqlevel', 'name'] 'data' => $quests->getListviewData(),
)]; 'params' => ['sort' => "$['reqlevel', 'name']"]
);
} }
// Tab: Itemsets // Tab: Itemsets
@@ -201,35 +206,47 @@ class ClassPage extends GenericPage
{ {
$this->extendGlobalData($sets->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($sets->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs[] = ['itemset', array( $this->lvTabs[] = array(
'data' => array_values($sets->getListviewData()), 'file' => 'itemset',
'note' => sprintf(Util::$filterResultString, '?itemsets&filter=cl='.$this->typeId), 'data' => $sets->getListviewData(),
'hiddenCols' => ['classes'], 'params' => array(
'sort' => ['-level', 'name'] 'note' => sprintf(Util::$filterResultString, '?itemsets&filter=cl='.$this->typeId),
)]; 'hiddenCols' => "$['classes']",
'sort' => "$['-level', 'name']"
)
);
} }
// Tab: Trainer // Tab: Trainer
$conditions = array( $conditions = array(
['npcflag', 0x30, '&'], // is trainer ['npcflag', 0x30, '&'], // is trainer
['trainerType', 0], // trains class spells ['trainerType', 0], // trains class spells
['trainerRequirement', $this->typeId] ['trainerClass', $this->typeId]
); );
$trainer = new CreatureList($conditions); $trainer = new CreatureList($conditions);
if (!$trainer->error) if (!$trainer->error)
{ {
$this->lvTabs[] = ['creature', array( $this->lvTabs[] = array(
'data' => array_values($trainer->getListviewData()), 'file' => 'creature',
'id' => 'trainers', 'data' => $trainer->getListviewData(),
'name' => '$LANG.tab_trainers' 'params' => array(
)]; 'id' => 'trainers',
'name' => '$LANG.tab_trainers'
)
);
} }
// Tab: Races // Tab: Races
$races = new CharRaceList(array(['classMask', $_mask, '&'])); $races = new CharRaceList(array(['classMask', $_mask, '&']));
if (!$races->error) if (!$races->error)
$this->lvTabs[] = ['race', ['data' => array_values($races->getListviewData())]]; {
$this->lvTabs[] = array(
'file' => 'race',
'data' => $races->getListviewData(),
'params' => []
);
}
} }
} }

View File

@@ -8,9 +8,9 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader() // tabId 0: Database g_initHeader()
class ClassesPage extends GenericPage class ClassesPage extends GenericPage
{ {
use TrListPage; use ListPage;
protected $type = Type::CHR_CLASS; protected $type = TYPE_CLASS;
protected $tpl = 'list-page-generic'; protected $tpl = 'list-page-generic';
protected $path = [0, 12]; protected $path = [0, 12];
protected $tabId = 0; protected $tabId = 0;
@@ -27,7 +27,13 @@ class ClassesPage extends GenericPage
{ {
$classes = new CharClassList(); $classes = new CharClassList();
if (!$classes->error) if (!$classes->error)
$this->lvTabs[] = ['class', ['data' => array_values($classes->getListviewData())]]; {
$this->lvTabs[] = array(
'file' => 'class',
'data' => $classes->getListviewData(),
'params' => []
);
}
} }
protected function generateTitle() protected function generateTitle()

View File

@@ -1,7 +1,7 @@
<?php <?php
if (!defined('AOWOW_REVISION')) if (!defined('AOWOW_REVISION'))
die('illegal access'); die('invalid access');
// tabId 1: Tools g_initHeader() // tabId 1: Tools g_initHeader()
@@ -12,31 +12,28 @@ class ComparePage extends GenericPage
protected $path = [1, 3]; protected $path = [1, 3];
protected $mode = CACHE_TYPE_NONE; protected $mode = CACHE_TYPE_NONE;
protected $js = array( protected $js = array(
[JS_FILE, 'profile.js'], 'profile.js',
[JS_FILE, 'Draggable.js'], 'Draggable.js',
[JS_FILE, 'filters.js'], 'filters.js',
[JS_FILE, 'Summary.js'], 'Summary.js',
[JS_FILE, 'swfobject.js'], 'swfobject.js',
); );
protected $css = [[CSS_FILE, 'Summary.css']]; protected $css = [['path' => 'Summary.css']];
protected $summary = []; protected $summary = [];
protected $cmpItems = []; protected $cmpItems = [];
protected $_get = ['compare' => ['filter' => FILTER_CALLBACK, 'options' => 'ComparePage::checkCompareString']];
protected $_cookie = ['compare_groups' => ['filter' => FILTER_CALLBACK, 'options' => 'ComparePage::checkCompareString']];
private $compareString = ''; private $compareString = '';
public function __construct($pageCall, $__) public function __construct($pageCall, $__)
{ {
parent::__construct($pageCall, $__); parent::__construct($pageCall, $__);
// prefer GET over COOKIE // prefer $_GET over $_COOKIE
if ($this->_get['compare']) if (!empty($_GET['compare']))
$this->compareString = $this->_get['compare']; $this->compareString = $_GET['compare'];
else if ($this->_cookie['compare_groups']) else if (!empty($_COOKIE['compare_groups']))
$this->compareString = $this->_cookie['compare_groups']; $this->compareString = urldecode($_COOKIE['compare_groups']);
$this->name = Lang::main('compareTool'); $this->name = Lang::main('compareTool');
} }
@@ -44,13 +41,7 @@ class ComparePage extends GenericPage
protected function generateContent() protected function generateContent()
{ {
// add conditional js // add conditional js
$this->addScript([JS_FILE, '?data=weight-presets.gems.enchants.itemsets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]); $this->addJS('?data=weight-presets.gems.enchants.itemsets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']);
$this->summary = array(
'template' => 'compare',
'id' => 'compare',
'parent' => 'compare-generic'
);
if (!$this->compareString) if (!$this->compareString)
return; return;
@@ -59,20 +50,21 @@ class ComparePage extends GenericPage
$items = $outSet = []; $items = $outSet = [];
foreach ($sets as $set) foreach ($sets as $set)
{ {
$itemString = explode(':', $set); $itemSting = explode(':', $set);
$outString = []; $outString = [];
foreach ($itemString as $is) foreach ($itemSting as $substring)
{ {
$params = array_pad(explode('.', $is), 7, 0); $params = explode('.', $substring);
$items[] = (int)$params[0]; $items[] = (int)$params[0];
while (sizeof($params) < 7)
$params[] = 0;
$outString[] = $params; $outString[] = $params;
} }
$outSet[] = $outString; $outSet[] = $outString;
} }
$this->summary = $outSet;
$this->summary['groups'] = $outSet;
$iList = new ItemList(array(['i.id', $items])); $iList = new ItemList(array(['i.id', $items]));
$data = $iList->getListviewData(ITEMINFO_SUBITEMS | ITEMINFO_JSON); $data = $iList->getListviewData(ITEMINFO_SUBITEMS | ITEMINFO_JSON);
@@ -82,15 +74,12 @@ class ComparePage extends GenericPage
if (empty($data[$itemId])) if (empty($data[$itemId]))
continue; continue;
if (!empty($data[$itemId]['subitems'])) $this->cmpItems[] = [
foreach ($data[$itemId]['subitems'] as &$si) $itemId,
$si['enchantment'] = implode(', ', $si['enchantment']); $iList->getField('name', true),
$iList->getField('quality'),
$this->cmpItems[$itemId] = [ $iList->getField('iconString'),
'name_'.User::$localeString => $iList->getField('name', true), $data[$itemId]
'quality' => $iList->getField('quality'),
'icon' => $iList->getField('iconString'),
'jsonequip' => $data[$itemId]
]; ];
} }
} }
@@ -101,15 +90,6 @@ class ComparePage extends GenericPage
} }
protected function generatePath() {} protected function generatePath() {}
protected static function checkCompareString(string $val) : string
{
$val = urldecode($val);
if (preg_match('/[^\d\.:;]/', $val))
return '';
return $val;
}
} }
?> ?>

View File

@@ -8,9 +8,9 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader() // tabId 0: Database g_initHeader()
class CurrenciesPage extends GenericPage class CurrenciesPage extends GenericPage
{ {
use TrListPage; use ListPage;
protected $type = Type::CURRENCY; protected $type = TYPE_CURRENCY;
protected $tpl = 'list-page-generic'; protected $tpl = 'list-page-generic';
protected $path = [0, 15]; protected $path = [0, 15];
protected $tabId = 0; protected $tabId = 0;
@@ -37,7 +37,11 @@ class CurrenciesPage extends GenericPage
$conditions[] = ['category', (int)$this->category[0]]; $conditions[] = ['category', (int)$this->category[0]];
$money = new CurrencyList($conditions); $money = new CurrencyList($conditions);
$this->lvTabs[] = ['currency', ['data' => array_values($money->getListviewData())]]; $this->lvTabs[] = array(
'file' => 'currency',
'data' => $money->getListviewData(),
'params' => []
);
} }
protected function generateTitle() protected function generateTitle()

View File

@@ -8,27 +8,19 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader() // tabId 0: Database g_initHeader()
class CurrencyPage extends GenericPage class CurrencyPage extends GenericPage
{ {
use TrDetailPage; use DetailPage;
protected $type = Type::CURRENCY; protected $type = TYPE_CURRENCY;
protected $typeId = 0; protected $typeId = 0;
protected $tpl = 'detail-page-generic'; protected $tpl = 'detail-page-generic';
protected $path = [0, 15]; protected $path = [0, 15];
protected $tabId = 0; protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE; protected $mode = CACHE_TYPE_PAGE;
protected $_get = ['domain' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkDomain']];
private $powerTpl = '$WowheadPower.registerCurrency(%d, %d, %s);';
public function __construct($pageCall, $id) public function __construct($pageCall, $id)
{ {
parent::__construct($pageCall, $id); parent::__construct($pageCall, $id);
// temp locale
if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain'])
Util::powerUseLocale($this->_get['domain']);
$this->typeId = intVal($id); $this->typeId = intVal($id);
$this->subject = new CurrencyList(array(['id', $this->typeId])); $this->subject = new CurrencyList(array(['id', $this->typeId]));
@@ -50,7 +42,7 @@ class CurrencyPage extends GenericPage
protected function generateContent() protected function generateContent()
{ {
$this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]); $this->addJS('?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']);
$_itemId = $this->subject->getField('itemId'); $_itemId = $this->subject->getField('itemId');
@@ -58,18 +50,12 @@ class CurrencyPage extends GenericPage
/* Infobox */ /* Infobox */
/**********/ /**********/
$infobox = Lang::getInfoBoxForFlags(intval($this->subject->getField('cuFlags'))); $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
// cap if ($this->typeId == 103) // Arena Points
if ($_ = $this->subject->getField('cap')) $infobox[] = Lang::currency('cap').Lang::main('colon').'10\'000';
$infobox[] = Lang::currency('cap').Lang::main('colon').Lang::nf($_); else if ($this->typeId == 104) // Honor
$infobox[] = Lang::currency('cap').Lang::main('colon').'75\'000';
// icon
if ($_ = $this->subject->getField('iconId'))
{
$infobox[] = Util::ucFirst(lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
$this->extendGlobalIds(Type::ICON, $_);
}
/****************/ /****************/
/* Main Content */ /* Main Content */
@@ -83,9 +69,6 @@ class CurrencyPage extends GenericPage
BUTTON_LINKS => true BUTTON_LINKS => true
); );
if ($_ = $this->subject->getField('description', true))
$this->extraText = $_;
/**************/ /**************/
/* Extra Tabs */ /* Extra Tabs */
/**************/ /**************/
@@ -99,8 +82,20 @@ class CurrencyPage extends GenericPage
{ {
$this->extendGlobalData($lootTabs->jsGlobals); $this->extendGlobalData($lootTabs->jsGlobals);
foreach ($lootTabs->iterate() as [$file, $tabData]) foreach ($lootTabs->iterate() as $tab)
$this->lvTabs[] = [$file, $tabData]; {
$this->lvTabs[] = array(
'file' => $tab[0],
'data' => $tab[1],
'params' => [
'name' => $tab[2],
'id' => $tab[3],
'extraCols' => $tab[4] ? '$['.implode(', ', array_unique($tab[4])).']' : null,
'hiddenCols' => $tab[5] ? '$['.implode(', ', array_unique($tab[5])).']' : null,
'visibleCols' => $tab[6] ? '$'. Util::toJSON( array_unique($tab[6])) : null
]
);
}
} }
// tab: sold by // tab: sold by
@@ -114,15 +109,14 @@ class CurrencyPage extends GenericPage
if (!$soldBy->error) if (!$soldBy->error)
{ {
$sbData = $soldBy->getListviewData(); $sbData = $soldBy->getListviewData();
$extraCols = ['$Listview.extraCols.stock', "\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost']; $extraCols = ['Listview.extraCols.stock', "Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", 'Listview.extraCols.cost'];
$holidays = []; $holidays = [];
foreach ($sbData as $k => &$row) foreach ($sbData as $k => &$row)
{ {
$items = []; $items = [];
$tokens = []; $tokens = [];
// note: can only display one entry per row, so only use first entry of each vendor foreach ($vendors[$k] as $id => $qty)
foreach ($vendors[$k][0] as $id => $qty)
{ {
if (is_string($id)) if (is_string($id))
continue; continue;
@@ -133,16 +127,16 @@ class CurrencyPage extends GenericPage
$items[] = [-$id, $qty]; $items[] = [-$id, $qty];
} }
if ($vendors[$k][0]['event']) if ($vendors[$k]['event'])
{ {
if (count($extraCols) == 3) // not already pushed if (count($extraCols) == 3) // not already pushed
$extraCols[] = '$Listview.extraCols.condition'; $extraCols[] = 'Listview.extraCols.condition';
$this->extendGlobalIds(Type::WORLDEVENT, $vendors[$k][0]['event']); $this->extendGlobalIds(TYPE_WORLDEVENT, $vendors[$k]['event']);
$row['condition'][0][$this->typeId][] = [[CND_ACTIVE_EVENT, $vendors[$k][0]['event']]]; $row['condition'][0][$this->typeId][] = [[CND_ACTIVE_EVENT, $vendors[$k]['event']]];
} }
$row['stock'] = $vendors[$k][0]['stock']; $row['stock'] = $vendors[$k]['stock'];
$row['stack'] = $itemObj->getField('buyCount'); $row['stack'] = $itemObj->getField('buyCount');
$row['cost'] = array( $row['cost'] = array(
$itemObj->getField('buyPrice'), $itemObj->getField('buyPrice'),
@@ -151,13 +145,16 @@ class CurrencyPage extends GenericPage
); );
} }
$this->lvTabs[] = ['creature', array( $this->lvTabs[] = array(
'data' => array_values($sbData), 'file' => 'creature',
'name' => '$LANG.tab_soldby', 'data' => $sbData,
'id' => 'sold-by-npc', 'params' => [
'extraCols' => $extraCols, 'name' => '$LANG.tab_soldby',
'hiddenCols' => ['level', 'type'] 'id' => 'sold-by-npc',
)]; 'extraCols' => '$['.implode(', ', $extraCols).']',
'hiddenCols' => "$['level', 'type']"
]
);
} }
} }
} }
@@ -170,16 +167,18 @@ class CurrencyPage extends GenericPage
{ {
$this->extendGlobalData($createdBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); $this->extendGlobalData($createdBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
$tabData = array(
'data' => array_values($createdBy->getListviewData()),
'name' => '$LANG.tab_createdby',
'id' => 'created-by',
);
if ($createdBy->hasSetFields(['reagent1'])) if ($createdBy->hasSetFields(['reagent1']))
$tabData['visibleCols'] = ['reagents']; $visCols = ['reagents'];
$this->lvTabs[] = ['spell', $tabData]; $this->lvTabs[] = array(
'file' => 'spell',
'data' => $createdBy->getListviewData(),
'params' => [
'name' => '$LANG.tab_createdby',
'id' => 'created-by',
'visibleCols' => isset($visCols) ? '$'.Util::toJSON($visCols) : null
]
);
} }
} }
@@ -207,35 +206,24 @@ class CurrencyPage extends GenericPage
$boughtBy = new ItemList(array(['id', $boughtBy])); $boughtBy = new ItemList(array(['id', $boughtBy]));
if (!$boughtBy->error) if (!$boughtBy->error)
{ {
$tabData = array( if ($boughtBy->getMatches() <= CFG_SQL_LIMIT_DEFAULT)
'data' => array_values($boughtBy->getListviewData(ITEMINFO_VENDOR, [Type::CURRENCY => $this->typeId])), $n = null;
'name' => '$LANG.tab_currencyfor',
'id' => 'currency-for', $this->lvTabs[] = array(
'extraCols' => ["\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost'], 'file' => 'item',
'data' => $boughtBy->getListviewData(ITEMINFO_VENDOR, [TYPE_CURRENCY => $this->typeId]),
'params' => [
'name' => '$LANG.tab_currencyfor',
'id' => 'currency-for',
'extraCols' => "$[Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')]",
'note' => $n ? sprintf(Util::$filterResultString, $n) : null
]
); );
if ($boughtBy->getMatches() > CFG_SQL_LIMIT_DEFAULT)
$tabData['note'] = sprintf(Util::$filterResultString, $n);
$this->lvTabs[] = ['item', $tabData];
$this->extendGlobalData($boughtBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); $this->extendGlobalData($boughtBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
} }
} }
} }
protected function generateTooltip()
{
$power = new StdClass();
if (!$this->subject->error)
{
$power->{'name_'.User::$localeString} = $this->subject->getField('name', true);
$power->icon = rawurlencode($this->subject->getField('iconString', true, true));
$power->{'tooltip_'.User::$localeString} = $this->subject->renderTooltip();
}
return sprintf($this->powerTpl, $this->typeId, User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER));
}
} }
?> ?>

View File

@@ -1,128 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 100: Emotes g_initPath()
// tabid 0: Database g_initHeader()
class EmotePage extends GenericPage
{
use TrDetailPage;
protected $type = Type::EMOTE;
protected $typeId = 0;
protected $tpl = 'detail-page-generic';
protected $path = [0, 100];
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
public function __construct($pageCall, $id)
{
parent::__construct($pageCall, $id);
$this->typeId = intVal($id);
$this->subject = new EmoteList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->notFound(Lang::game('emote'), Lang::emote('notFound'));
$this->name = Util::ucFirst($this->subject->getField('cmd'));
}
protected function generatePath() { }
protected function generateTitle()
{
array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('emote')));
}
protected function generateContent()
{
/***********/
/* Infobox */
/***********/
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
// has Animation
if ($this->subject->getField('isAnimated'))
$infobox[] = Lang::emote('isAnimated');
/****************/
/* Main Content */
/****************/
$text = '';
if ($aliasses = DB::Aowow()->selectCol('SELECT command FROM ?_emotes_aliasses WHERE id = ?d AND locales & ?d', $this->typeId, 1 << User::$localeId))
{
$text .= '[h3]'.Lang::emote('aliases').'[/h3][ul]';
foreach ($aliasses as $a)
$text .= '[li]/'.$a.'[/li]';
$text .= '[/ul][br][br]';
}
$texts = [];
if ($_ = $this->subject->getField('self', true))
$texts[Lang::emote('self')] = $_;
if ($_ = $this->subject->getField('target', true))
$texts[Lang::emote('target')] = $_;
if ($_ = $this->subject->getField('noTarget', true))
$texts[Lang::emote('noTarget')] = $_;
if (!$texts)
$text .= '[div][i class=q0]'.Lang::emote('noText').'[/i][/div]';
else
foreach ($texts as $h => $t)
$text .= '[pad][b]'.$h.'[/b][ul][li][span class=s4]'.preg_replace('/%\d?\$?s/', '<'.Util::ucFirst(Lang::main('name')).'>', $t).'[/span][/li][/ul]';
$this->extraText = $text;
$this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
$this->redButtons = array(
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
BUTTON_WOWHEAD => false
);
/**************/
/* Extra Tabs */
/**************/
// tab: achievement
$condition = array(
['ac.type', ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE],
['ac.value1', $this->typeId],
);
$acv = new AchievementList($condition);
$this->lvTabs[] = ['achievement', ['data' => array_values($acv->getListviewData())]];
$this->extendGlobalData($acv->getJsGlobals());
// tab: sound
if ($em = DB::Aowow()->select('SELECT soundId AS ARRAY_KEY, BIT_OR(1 << (raceId - 1)) AS raceMask, BIT_OR(1 << (gender - 1)) AS gender FROM aowow_emotes_sounds WHERE emoteId = ?d GROUP BY soundId', $this->typeId))
{
$sounds = new SoundList(array(['id', array_keys($em)]));
if (!$sounds->error)
{
$this->extendGlobalData($sounds->getJSGlobals(GLOBALINFO_SELF));
$data = $sounds->getListviewData();
foreach($data as $id => &$d)
{
$d['races'] = $em[$id]['raceMask'];
$d['gender'] = $em[$id]['gender'];
}
$this->lvTabs[] = ['sound', array(
'data' => array_values($data),
// gender races
'extraCols' => ['$Listview.templates.title.columns[1]', '$Listview.templates.classs.columns[1]']
)];
}
}
}
}
?>

View File

@@ -1,44 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 100: Emotes g_initPath()
// tabid 0: Database g_initHeader()
class EmotesPage extends GenericPage
{
use TrListPage;
protected $type = Type::EMOTE;
protected $tpl = 'list-page-generic';
protected $path = [0, 100];
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
public function __construct($pageCall, $pageParam)
{
parent::__construct($pageCall, $pageParam);
$this->name = Util::ucFirst(Lang::game('emotes'));
}
protected function generateContent()
{
$tabData = array(
'data' => array_values((new EmoteList())->getListviewData()),
'name' => Util::ucFirst(Lang::game('emotes'))
);
$this->lvTabs[] = ['emote', $tabData, 'emote'];
}
protected function generateTitle()
{
array_unshift($this->title, $this->name);
}
protected function generatePath() { }
}
?>

View File

@@ -1,324 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 101: Enchantment g_initPath()
// tabId 0: Database g_initHeader()
class EnchantmentPage extends GenericPage
{
use TrDetailPage;
protected $type = Type::ENCHANTMENT;
protected $typeId = 0;
protected $tpl = 'enchantment';
protected $path = [0, 101];
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
public function __construct($pageCall, $id)
{
parent::__construct($pageCall, $id);
$this->typeId = intVal($id);
$this->subject = new EnchantmentList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->notFound(Lang::game('enchantment'), Lang::enchantment('notFound'));
$this->extendGlobalData($this->subject->getJSGlobals());
$this->name = Util::ucFirst($this->subject->getField('name', true));
}
private function getDistinctType()
{
$type = 0;
for ($i = 1; $i < 4; $i++)
{
if ($_ = $this->subject->getField('type'.$i))
{
if ($type) // already set
return 0;
else
$type = $_;
}
}
return $type;
}
protected function generateContent()
{
/***********/
/* Infobox */
/***********/
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
// reqLevel
if ($_ = $this->subject->getField('requiredLevel'))
$infobox[] = sprintf(Lang::game('reqLevel'), $_);
// reqskill
if ($_ = $this->subject->getField('skillLine'))
{
$this->extendGlobalIds(Type::SKILL, $_);
$foo = sprintf(Lang::game('requires'), '&nbsp;[skill='.$_.']');
if ($_ = $this->subject->getField('skillLevel'))
$foo .= ' ('.$_.')';
$infobox[] = $foo;
}
/****************/
/* Main Content */
/****************/
$this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
$this->redButtons = array(
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
BUTTON_WOWHEAD => false
);
$this->effects = [];
// 3 effects
for ($i = 1; $i < 4; $i++)
{
$_ty = $this->subject->getField('type'.$i);
$_qty = $this->subject->getField('amount'.$i);
$_obj = $this->subject->getField('object'.$i);
switch ($_ty)
{
case 1:
case 3:
case 7:
$sArr = $this->subject->getField('spells')[$i];
$spl = $this->subject->getRelSpell($sArr[0]);
$this->effects[$i]['name'] = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'Type: '.$_ty, Lang::item('trigger', $sArr[1])) : Lang::item('trigger', $sArr[1]);
$this->effects[$i]['proc'] = $sArr[3];
$this->effects[$i]['value'] = $_qty ?: null;
$this->effects[$i]['icon'] = array(
'name' => !$spl ? Util::ucFirst(Lang::game('spell')).' #'.$sArr[0] : Util::localizedString($spl, 'name'),
'id' => $sArr[0],
'count' => $sArr[2]
);
break;
case 5:
if ($_obj < 2) // [mana, health] are on [0, 1] respectively and are expected on [1, 2] ..
$_obj++; // 0 is weaponDmg .. ehh .. i messed up somewhere
$this->effects[$i]['tip'] = [$_obj, Game::$itemMods[$_obj]];
// DO NOT BREAK!
case 2:
case 6:
case 8:
case 4:
$this->effects[$i]['name'] = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'Type: '.$_ty, Lang::enchantment('types', $_ty)) : Lang::enchantment('types', $_ty);
$this->effects[$i]['value'] = $_qty;
if ($_ty == 4)
$this->effects[$i]['name'] .= Lang::main('colon').'('.(User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'Object: '.$_obj, Lang::getMagicSchools(1 << $_obj)) : Lang::getMagicSchools(1 << $_obj)).')';
}
}
// activation conditions
if ($_ = $this->subject->getField('conditionId'))
{
$x = '';
if ($gemCnd = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantmentcondition WHERE id = ?d', $_))
{
for ($i = 1; $i < 6; $i++)
{
if (!$gemCnd['color'.$i])
continue;
$fiColors = function ($idx)
{
$foo = '';
switch ($idx)
{
case 2: $foo = '0:3:5'; break; // red
case 3: $foo = '2:4:5'; break; // yellow
case 4: $foo = '1:3:4'; break; // blue
}
return $foo;
};
$bLink = $gemCnd['color'.$i] ? '<a class="tip" href="?items=3&filter=ty='.$fiColors($gemCnd['color'.$i]).'">'.Lang::item('gemColors', $gemCnd['color'.$i] - 1).'</a>' : '';
$cLink = $gemCnd['cmpColor'.$i] ? '<a class="tip" href="?items=3&filter=ty='.$fiColors($gemCnd['cmpColor'.$i]).'">'.Lang::item('gemColors', $gemCnd['cmpColor'.$i] - 1).'</a>' : '';
switch ($gemCnd['comparator'.$i])
{
case 2: // requires less <color> than (<value> || <comparecolor>) gems
case 5: // requires at least <color> than (<value> || <comparecolor>) gems
$sp = (int)$gemCnd['value'.$i] > 1;
$x .= '<span class="q0">'.Lang::achievement('reqNumCrt').' '.Lang::item('gemConditions', $gemCnd['comparator'.$i], [$gemCnd['value'.$i], $bLink]).'</span><br />';
break;
case 3: // requires more <color> than (<value> || <comparecolor>) gems
$link = '<a href="?items=3&filter=ty='.$fiColors($gemCnd['cmpColor'.$i]).'">'.Lang::item('gemColors', $gemCnd['cmpColor'.$i] - 1).'</a>';
$x .= '<span class="q0">'.Lang::achievement('reqNumCrt').' '.Lang::item('gemConditions', $gemCnd['comparator'.$i], [$bLink, $cLink]).'</span><br />';
break;
}
}
}
$this->activateCondition = $x;
}
/**************/
/* Extra Tabs */
/**************/
// used by gem
$gemList = new ItemList(array(['gemEnchantmentId', $this->typeId]));
if (!$gemList->error)
{
$this->lvTabs[] = ['item', array(
'data' => array_values($gemList->getListviewData()),
'name' => '$LANG.tab_usedby + \' \' + LANG.gems',
'id' => 'used-by-gem',
)];
$this->extendGlobalData($gemList->getJsGlobals());
}
// used by socket bonus
$socketsList = new ItemList(array(['socketBonus', $this->typeId]));
if (!$socketsList->error)
{
$this->lvTabs[] = ['item', array(
'data' => array_values($socketsList->getListviewData()),
'name' => '$LANG.tab_socketbonus',
'id' => 'used-by-socketbonus',
)];
$this->extendGlobalData($socketsList->getJsGlobals());
}
// used by spell
// used by useItem
$cnd = array(
'OR',
['AND', ['effect1Id', [53, 54, 156, 92]], ['effect1MiscValue', $this->typeId]],
['AND', ['effect2Id', [53, 54, 156, 92]], ['effect2MiscValue', $this->typeId]],
['AND', ['effect3Id', [53, 54, 156, 92]], ['effect3MiscValue', $this->typeId]],
);
$spellList = new SpellList($cnd);
if (!$spellList->error)
{
$spellData = $spellList->getListviewData();
$this->extendGlobalData($spellList->getJsGlobals());
$spellIds = $spellList->getFoundIDs();
$conditions = array(
'OR', // [use, useUndelayed]
['AND', ['spellTrigger1', [0, 5]], ['spellId1', $spellIds]],
['AND', ['spellTrigger2', [0, 5]], ['spellId2', $spellIds]],
['AND', ['spellTrigger3', [0, 5]], ['spellId3', $spellIds]],
['AND', ['spellTrigger4', [0, 5]], ['spellId4', $spellIds]],
['AND', ['spellTrigger5', [0, 5]], ['spellId5', $spellIds]]
);
$ubItems = new ItemList($conditions);
if (!$ubItems->error)
{
$this->lvTabs[] = ['item', array(
'data' => array_values($ubItems->getListviewData()),
'name' => '$LANG.tab_usedby + \' \' + LANG.types[3][0]',
'id' => 'used-by-item',
)];
$this->extendGlobalData($ubItems->getJSGlobals(GLOBALINFO_SELF));
}
// remove found spells if they are used by an item
if (!$ubItems->error)
{
foreach ($spellList->iterate() as $sId => $__)
{
// if Perm. Enchantment has a createItem its a Scroll of Enchantment (display both)
for ($i = 1; $i < 4; $i++)
if ($spellList->getField('effect'.$i.'Id') == 53 && $spellList->getField('effect'.$i.'CreateItemId'))
continue 2;
foreach ($ubItems->iterate() as $__)
{
for ($i = 1; $i < 6; $i++)
{
if ($ubItems->getField('spellId'.$i) == $sId)
{
unset($spellData[$sId]);
break 2;
}
}
}
}
}
$this->lvTabs[] = ['spell', array(
'data' => array_values($spellData),
'name' => '$LANG.tab_usedby + \' \' + LANG.types[6][0]',
'id' => 'used-by-spell',
)];
}
// used by randomAttrItem
$ire = DB::Aowow()->select(
'SELECT *, ABS(id) AS ARRAY_KEY FROM ?_itemrandomenchant WHERE enchantId1 = ?d OR enchantId2 = ?d OR enchantId3 = ?d OR enchantId4 = ?d OR enchantId5 = ?d',
$this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId
);
if ($ire)
{
if ($iet = DB::World()->select('SELECT entry AS ARRAY_KEY, ench, chance FROM item_enchantment_template WHERE ench IN (?a)', array_keys($ire)))
{
$randIds = []; // transform back to signed format
foreach ($iet as $tplId => $data)
$randIds[$ire[$data['ench']]['id'] > 0 ? $tplId : -$tplId] = $ire[$data['ench']]['id'];
$randItems = new ItemList(array(CFG_SQL_LIMIT_NONE, ['randomEnchant', array_keys($randIds)]));
if (!$randItems->error)
{
$data = $randItems->getListviewData();
foreach ($randItems->iterate() as $iId => $__)
{
$re = $randItems->getField('randomEnchant');
$data[$iId]['percent'] = $iet[abs($re)]['chance'];
$data[$iId]['count'] = 1; // expected by js or the pct-col becomes unsortable
$data[$iId]['rel'] = 'rand='.$ire[$iet[abs($re)]['ench']]['id'];
$data[$iId]['name'] .= ' '.Util::localizedString($ire[$iet[abs($re)]['ench']], 'name');
}
$this->lvTabs[] = ['item', array(
'data' => array_values($data),
'id' => 'used-by-rand',
'name' => '$LANG.tab_usedby + \' \' + \''.Lang::item('_rndEnchants').'\'',
'extraCols' => ['$Listview.extraCols.percent']
)];
$this->extendGlobalData($randItems->getJSGlobals(GLOBALINFO_SELF));
}
}
}
}
protected function generateTitle()
{
array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('enchantment')));
}
protected function generatePath()
{
if ($_ = $this->getDistinctType())
$this->path[] = $_;
}
}
?>

View File

@@ -1,110 +0,0 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 101: Enchantment g_initPath()
// tabId 0: Database g_initHeader()
class EnchantmentsPage extends GenericPage
{
use TrListPage;
protected $type = Type::ENCHANTMENT;
protected $tpl = 'enchantments';
protected $path = [0, 101];
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
protected $js = [[JS_FILE, 'filters.js']];
protected $_get = ['filter' => ['filter' => FILTER_UNSAFE_RAW]];
public function __construct($pageCall, $pageParam)
{
$this->getCategoryFromUrl($pageParam);;
$this->filterObj = new EnchantmentListFilter(false, ['parentCats' => $this->category]);
parent::__construct($pageCall, $pageParam);
$this->name = Util::ucFirst(Lang::game('enchantments'));
$this->subCat = $pageParam !== '' ? '='.$pageParam : '';
}
protected function generateContent()
{
$tabData = array(
'data' => [],
'name' => Util::ucFirst(Lang::game('enchantments'))
);
$conditions = [];
if (!User::isInGroup(U_GROUP_EMPLOYEE))
$conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
if ($_ = $this->filterObj->getConditions())
$conditions[] = $_;
$ench = new EnchantmentList($conditions);
$tabData['data'] = array_values($ench->getListviewData());
$this->extendGlobalData($ench->getJSGlobals());
// recreate form selection
$this->filter = $this->filterObj->getForm();
$this->filter['query'] = $this->_get['filter'];
$this->filter['initData'] = ['init' => 'enchantments'];
if ($x = $this->filterObj->getSetCriteria())
$this->filter['initData']['sc'] = $x;
$xCols = $this->filterObj->getExtraCols();
foreach (Util::$itemFilter as $fiId => $str)
if (array_column($tabData['data'], $str))
$xCols[] = $fiId;
if (array_column($tabData['data'], 'dmg'))
$xCols[] = 34;
if ($xCols)
$this->filter['initData']['ec'] = array_values(array_unique($xCols));
if ($xCols)
$tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
if ($ench->getMatches() > CFG_SQL_LIMIT_DEFAULT)
{
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_enchantmentsfound', $ench->getMatches(), CFG_SQL_LIMIT_DEFAULT);
$tabData['_truncated'] = 1;
}
if (array_filter(array_column($tabData['data'], 'spells')))
$tabData['visibleCols'] = ['trigger'];
if (!$ench->hasSetFields(['skillLine']))
$tabData['hiddenCols'] = ['skill'];
if ($this->filterObj->error)
$tabData['_errors'] = '$1';
$this->lvTabs[] = ['enchantment', $tabData, 'enchantment'];
}
protected function generateTitle()
{
$form = $this->filterObj->getForm('form');
if (!empty($form['ty']) && intVal($form['ty']) && $form['ty'] > 0 && $form['ty'] < 9)
array_unshift($this->title, Lang::enchantment('types', $form['ty']));
array_unshift($this->title, $this->name);
}
protected function generatePath()
{
$form = $this->filterObj->getForm('form');
if (isset($form['ty']) && count($form['ty']) == 1)
$this->path[] = $form['ty'][0];
}
}
?>

View File

@@ -4,22 +4,19 @@ if (!defined('AOWOW_REVISION'))
die('illegal access'); die('illegal access');
// menuId 11: Worldevent g_initPath() // menuId 11: Object g_initPath()
// tabId 0: Database g_initHeader() // tabId 0: Database g_initHeader()
class EventPage extends GenericPage class EventPage extends GenericPage
{ {
use TrDetailPage; use DetailPage;
protected $type = Type::WORLDEVENT; protected $type = TYPE_WORLDEVENT;
protected $typeId = 0; protected $typeId = 0;
protected $tpl = 'detail-page-generic'; protected $tpl = 'detail-page-generic';
protected $path = [0, 11]; protected $path = [0, 11];
protected $tabId = 0; protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE; protected $mode = CACHE_TYPE_PAGE;
protected $_get = ['domain' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkDomain']];
private $powerTpl = '$WowheadPower.registerHoliday(%d, %d, %s);';
private $hId = 0; private $hId = 0;
private $eId = 0; private $eId = 0;
@@ -27,25 +24,22 @@ class EventPage extends GenericPage
{ {
parent::__construct($pageCall, $id); parent::__construct($pageCall, $id);
// temp locale
if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain'])
Util::powerUseLocale($this->_get['domain']);
$this->typeId = intVal($id); $this->typeId = intVal($id);
$this->subject = new WorldEventList(array(['id', $this->typeId])); $conditions = $this->typeId < 0 ? [['id', -$this->typeId]] : [['holidayId', $this->typeId]];
$this->subject = new WorldEventList($conditions);
if ($this->subject->error) if ($this->subject->error)
$this->notFound(Lang::game('event'), Lang::event('notFound')); $this->notFound(Lang::game('event'), Lang::event('notFound'));
$this->hId = $this->subject->getField('holidayId'); $this->hId = $this->subject->getField('holidayId');
$this->eId = $this->typeId; $this->eId = $this->subject->getField('eventBak');
$this->name = $this->subject->getField('name', true);
$this->dates = array( // redirect if associated with a holiday
'firstDate' => $this->subject->getField('startTime'), if ($this->hId && $this->typeId != $this->hId)
'lastDate' => $this->subject->getField('endTime'), header('Location: '.HOST_URL.'?event='.$this->hId, true, 302);
'length' => $this->subject->getField('length'),
'rec' => $this->subject->getField('occurence') $this->name = $this->subject->getField('name', true);
);
} }
protected function generatePath() protected function generatePath()
@@ -67,7 +61,7 @@ class EventPage extends GenericPage
protected function generateContent() protected function generateContent()
{ {
$this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]); $this->addJS('?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']);
/***********/ /***********/
/* Infobox */ /* Infobox */
@@ -78,7 +72,7 @@ class EventPage extends GenericPage
// boss // boss
if ($_ = $this->subject->getField('bossCreature')) if ($_ = $this->subject->getField('bossCreature'))
{ {
$this->extendGlobalIds(Type::NPC, $_); $this->extendGlobalIds(TYPE_NPC, $_);
$this->infobox[] = Lang::npc('rank', 3).Lang::main('colon').'[npc='.$_.']'; $this->infobox[] = Lang::npc('rank', 3).Lang::main('colon').'[npc='.$_.']';
} }
@@ -90,14 +84,16 @@ class EventPage extends GenericPage
/* Main Content */ /* Main Content */
/****************/ /****************/
// no entry in ?_articles? use default HolidayDescription
if ($this->hId && empty($this->article))
$this->article = ['text' => Util::jsEscape($this->subject->getField('description', true)), 'params' => []];
$this->headIcons = [$this->subject->getField('iconString')]; $this->headIcons = [$this->subject->getField('iconString')];
$this->redButtons = array( $this->redButtons = array(
BUTTON_WOWHEAD => $this->hId > 0, BUTTON_WOWHEAD => $this->typeId > 0,
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId] BUTTON_LINKS => true
);
$this->dates = array(
'firstDate' => $this->subject->getField('startTime'),
'lastDate' => $this->subject->getField('endTime'),
'length' => $this->subject->getField('length'),
'rec' => $this->subject->getField('occurence')
); );
/**************/ /**************/
@@ -116,12 +112,11 @@ class EventPage extends GenericPage
foreach ($data as &$d) foreach ($data as &$d)
$d['method'] = $npcIds[$d['id']]; $d['method'] = $npcIds[$d['id']];
$tabData = ['data' => array_values($data)]; $this->lvTabs[] = array(
'file' => CreatureList::$brickFile,
if ($hasFilter) 'data' => $data,
$tabData['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=38;crs='.$this->hId.';crv=0'); 'params' => ['note' => $hasFilter ? sprintf(Util::$filterResultString, '?npcs&filter=cr=38;crs='.$this->hId.';crv=0') : null]
);
$this->lvTabs[] = ['creature', $tabData];
} }
} }
@@ -135,12 +130,11 @@ class EventPage extends GenericPage
foreach ($data as &$d) foreach ($data as &$d)
$d['method'] = $objectIds[$d['id']]; $d['method'] = $objectIds[$d['id']];
$tabData = ['data' => array_values($data)]; $this->lvTabs[] = array(
'file' => GameObjectList::$brickFile,
if ($hasFilter) 'data' => $data,
$tabData['note'] = sprintf(Util::$filterResultString, '?objects&filter=cr=16;crs='.$this->hId.';crv=0'); 'params' => ['note' => $hasFilter ? sprintf(Util::$filterResultString, '?objects&filter=cr=16;crs='.$this->hId.';crv=0') : null]
);
$this->lvTabs[] = ['object', $tabData];
} }
} }
@@ -153,15 +147,14 @@ class EventPage extends GenericPage
{ {
$this->extendGlobalData($acvs->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); $this->extendGlobalData($acvs->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
$tabData = array( $this->lvTabs[] = array(
'data' => array_values($acvs->getListviewData()), 'file' => AchievementList::$brickFile,
'visibleCols' => ['category'] 'data' => $acvs->getListviewData(),
'params' => array(
'note' => $hasFilter ? sprintf(Util::$filterResultString, '?achievements&filter=cr=11;crs='.$this->hId.';crv=0') : null,
'visibleCols' => "$['category']"
)
); );
if ($hasFilter)
$tabData['note'] = sprintf(Util::$filterResultString, '?achievements&filter=cr=11;crs='.$this->hId.';crv=0');
$this->lvTabs[] = ['achievement', $tabData];
} }
} }
@@ -170,27 +163,26 @@ class EventPage extends GenericPage
{ {
$itemCnd = array( $itemCnd = array(
'OR', 'OR',
['eventId', $this->eId], // direct requirement on item ['holidayId', $this->hId], // direct requirement on item
); );
// tab: quests (by table, go & creature) // tab: quests (by table, go & creature)
$quests = new QuestList(array(['eventId', $this->eId])); $quests = new QuestList(array(['holidayId', $this->hId]));
if (!$quests->error) if (!$quests->error)
{ {
$this->extendGlobalData($quests->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS)); $this->extendGlobalData($quests->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
$tabData = ['data'=> array_values($quests->getListviewData())]; $this->lvTabs[] = array(
'file' => QuestList::$brickFile,
if ($hasFilter) 'data' => $quests->getListviewData(),
$tabData['note'] = sprintf(Util::$filterResultString, '?quests&filter=cr=33;crs='.$this->hId.';crv=0'); 'params' => ['note' => $hasFilter ? sprintf(Util::$filterResultString, '?quests&filter=cr=33;crs='.$this->hId.';crv=0') : null]
);
$this->lvTabs[] = ['quest', $tabData];
$questItems = []; $questItems = [];
foreach (array_column($quests->rewards, Type::ITEM) as $arr) foreach (array_column($quests->rewards, TYPE_ITEM) as $arr)
$questItems = array_merge($questItems, $arr); $questItems = array_merge($questItems, $arr);
foreach (array_column($quests->requires, Type::ITEM) as $arr) foreach (array_column($quests->requires, TYPE_ITEM) as $arr)
$questItems = array_merge($questItems, $arr); $questItems = array_merge($questItems, $arr);
if ($questItems) if ($questItems)
@@ -216,24 +208,23 @@ class EventPage extends GenericPage
{ {
$this->extendGlobalData($eventItems->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($eventItems->getJSGlobals(GLOBALINFO_SELF));
$tabData = ['data'=> array_values($eventItems->getListviewData())]; $this->lvTabs[] = array(
'file' => ItemList::$brickFile,
if ($hasFilter) 'data' => $eventItems->getListviewData(),
$tabData['note'] = sprintf(Util::$filterResultString, '?items&filter=cr=160;crs='.$this->hId.';crv=0'); 'params' => ['note' => $hasFilter ? sprintf(Util::$filterResultString, '?items&filter=cr=160;crs='.$this->hId.';crv=0') : null]
);
$this->lvTabs[] = ['item', $tabData];
} }
} }
// tab: see also (event conditions) // tab: see also (event conditions)
if ($rel = DB::World()->selectCol('SELECT IF(eventEntry = prerequisite_event, NULL, IF(eventEntry = ?d, prerequisite_event, -eventEntry)) FROM game_event_prerequisite WHERE prerequisite_event = ?d OR eventEntry = ?d', $this->eId, $this->eId, $this->eId)) if ($rel = DB::World()->selectCol('SELECT IF(eventEntry = prerequisite_event, NULL, IF(eventEntry = ?d, -prerequisite_event, eventEntry)) FROM game_event_prerequisite WHERE prerequisite_event = ?d OR eventEntry = ?d', $this->eId, $this->eId, $this->eId))
{ {
$list = []; $list = [];
array_walk($rel, function($v, $k) use (&$list) { array_walk($rel, function($v, $k) use (&$list) {
if ($v > 0) if ($v > 0)
$list[] = $v; $list[] = $v;
else if ($v === null) else if ($v === null)
trigger_error('game_event_prerequisite: this event has itself as prerequisite', E_USER_WARNING); Util::addNote(U_GROUP_EMPLOYEE, 'game_event_prerequisite: this event has itself as prerequisite');
}); });
if ($list) if ($list)
@@ -242,107 +233,81 @@ class EventPage extends GenericPage
$this->extendGlobalData($relEvents->getJSGlobals()); $this->extendGlobalData($relEvents->getJSGlobals());
$relData = $relEvents->getListviewData(); $relData = $relEvents->getListviewData();
foreach ($relEvents->getFoundIDs() as $id) foreach ($relEvents->getFoundIDs() as $id)
$relData[$id]['condition'][0][$this->typeId][] = [[-CND_ACTIVE_EVENT, $this->eId]]; $relData[$id]['condition'][0][$this->typeId][] = [[-CND_ACTIVE_EVENT, -$this->eId]];
$this->extendGlobalData($this->subject->getJSGlobals()); $this->extendGlobalData($this->subject->getJSGlobals());
foreach ($rel as $r) foreach ($rel as $r)
{ {
if ($r <= 0) if ($r >= 0)
continue; continue;
$this->extendGlobalIds(Type::WORLDEVENT, $r); $this->extendGlobalIds(TYPE_WORLDEVENT, $r);
$d = $this->subject->getListviewData(); $d = $this->subject->getListviewData();
$d[$this->eId]['condition'][0][$this->typeId][] = [[-CND_ACTIVE_EVENT, $r]]; $d[-$this->eId]['condition'][0][$this->typeId][] = [[-CND_ACTIVE_EVENT, $r]];
$relData = array_merge($relData, $d); $relData = array_merge($relData, $d);
} }
$this->lvTabs[] = ['event', array( $this->lvTabs[] = array(
'data' => array_values($relData), 'file' => WorldEventList::$brickFile,
'id' => 'see-also', 'data' => $relData,
'name' => '$LANG.tab_seealso', 'params' => array(
'hiddenCols' => ['date'], 'id' => 'see-also',
'extraCols' => ['$Listview.extraCols.condition'] 'name' => '$LANG.tab_seealso',
)]; 'hiddenCols' => "$['date']",
'extraCols' => '$[Listview.extraCols.condition]'
)
);
} }
} }
} }
protected function generateTooltip() : string
{
$power = new StdClass();
if (!$this->subject->error)
{
$power->{'name_'.User::$localeString} = $this->subject->getField('name', true);
if ($this->subject->getField('iconString') != 'trade_engineering')
$power->icon = rawurlencode($this->subject->getField('iconString', true, true));
$power->{'tooltip_'.User::$localeString} = $this->subject->renderTooltip();
}
return sprintf($this->powerTpl, $this->typeId, User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER));
}
protected function postCache() protected function postCache()
{ {
/********************/
/* finalize infobox */
/********************/
// update dates to now() // update dates to now()
$updated = WorldEventList::updateDates($this->dates); $updated = WorldEventList::updateDates($this->dates);
if ($this->mode == CACHE_TYPE_TOOLTIP) // start
if ($updated['start'])
array_push($this->infobox, Lang::event('start').Lang::main('colon').date(Lang::main('dateFmtLong'), $updated['start']));
// end
if ($updated['end'])
array_push($this->infobox, Lang::event('end').Lang::main('colon').date(Lang::main('dateFmtLong'), $updated['end']));
// occurence
if ($updated['rec'] > 0)
array_push($this->infobox, Lang::event('interval').Lang::main('colon').Util::formatTime($updated['rec'] * 1000));
// in progress
if ($updated['start'] < time() && $updated['end'] > time())
array_push($this->infobox, '[span class=q2]'.Lang::event('inProgress').'[/span]');
$this->infobox = '[ul][li]'.implode('[/li][li]', $this->infobox).'[/li][/ul]';
/***************************/
/* finalize related events */
/***************************/
foreach ($this->lvTabs as &$view)
{ {
return array( if ($view['file'] != WorldEventList::$brickFile)
date(Lang::main('dateFmtLong'), $updated['start']), continue;
date(Lang::main('dateFmtLong'), $updated['end'])
);
}
else
{
if ($this->hId)
Util::$wowheadLink = 'http://'.Util::$subDomains[User::$localeId].'.wowhead.com/event='.$this->hId;
/********************/ foreach ($view['data'] as &$data)
/* finalize infobox */
/********************/
// start
if ($updated['start'])
array_push($this->infobox, Lang::event('start').Lang::main('colon').date(Lang::main('dateFmtLong'), $updated['start']));
// end
if ($updated['end'])
array_push($this->infobox, Lang::event('end').Lang::main('colon').date(Lang::main('dateFmtLong'), $updated['end']));
// occurence
if ($updated['rec'] > 0)
array_push($this->infobox, Lang::event('interval').Lang::main('colon').Util::formatTime($updated['rec'] * 1000));
// in progress
if ($updated['start'] < time() && $updated['end'] > time())
array_push($this->infobox, '[span class=q2]'.Lang::event('inProgress').'[/span]');
$this->infobox = '[ul][li]'.implode('[/li][li]', $this->infobox).'[/li][/ul]';
/***************************/
/* finalize related events */
/***************************/
foreach ($this->lvTabs as &$view)
{ {
if ($view[0] != WorldEventList::$brickFile) $updated = WorldEventList::updateDates($data['_date']);
continue; unset($data['_date']);
$data['startDate'] = $updated['start'] ? date(Util::$dateFormatInternal, $updated['start']) : false;
foreach ($view[1]['data'] as &$data) $data['endDate'] = $updated['end'] ? date(Util::$dateFormatInternal, $updated['end']) : false;
{ $data['rec'] = $updated['rec'];
$updated = WorldEventList::updateDates($data['_date']);
unset($data['_date']);
$data['startDate'] = $updated['start'] ? date(Util::$dateFormatInternal, $updated['start']) : false;
$data['endDate'] = $updated['end'] ? date(Util::$dateFormatInternal, $updated['end']) : false;
$data['rec'] = $updated['rec'];
}
} }
} }
} }
} }

View File

@@ -8,9 +8,9 @@ if (!defined('AOWOW_REVISION'))
// tabId 0: Database g_initHeader() // tabId 0: Database g_initHeader()
class EventsPage extends GenericPage class EventsPage extends GenericPage
{ {
use TrListPage; use ListPage;
protected $type = Type::WORLDEVENT; protected $type = TYPE_WORLDEVENT;
protected $tpl = 'list-page-generic'; protected $tpl = 'list-page-generic';
protected $path = [0, 11]; protected $path = [0, 11];
protected $tabId = 0; protected $tabId = 0;
@@ -52,16 +52,19 @@ class EventsPage extends GenericPage
if ($d = $events->getField('requires')) if ($d = $events->getField('requires'))
$this->deps[$events->id] = $d; $this->deps[$events->id] = $d;
$data = array_values($events->getListviewData()); $this->lvTabs[] = array(
'file' => 'event',
'data' => $events->getListviewData(),
'params' => []
);
$this->lvTabs[] = ['event', ['data' => $data]]; if ($_ = array_filter($events->getListviewData(), function($x) {return $x['id'] > 0;}))
if ($_ = array_values(array_filter($data, function($x) {return $x['category'] > 0;})))
{ {
$this->lvTabs[] = ['calendar', array( $this->lvTabs[] = array(
'data' => $_, 'file' => 'calendar',
'hideCount' => 1 'data' => $_,
)]; 'params' => ['hideCount' => 1]
);
} }
} }
@@ -83,7 +86,7 @@ class EventsPage extends GenericPage
// recalculate dates with now() // recalculate dates with now()
foreach ($this->lvTabs as &$views) foreach ($this->lvTabs as &$views)
{ {
foreach ($views[1]['data'] as &$data) foreach ($views['data'] as &$data)
{ {
// is a followUp-event // is a followUp-event
if (!empty($this->deps[$data['id']])) if (!empty($this->deps[$data['id']]))

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