Compare commits

...

260 commits
v2.0 ... master

Author SHA1 Message Date
1c88254a06 style: drop redundant semicolons after function declarations (filegen templates + static js) 2026-06-10 06:13:19 +02:00
Sarjuuk
1491db51e3 Template/Cleanup
* \n to PHP_EOL
 * add phpdoc for autocomplete
 * try to unify indentations
 * stop mixing quotation marks if possible
2026-04-11 20:48:18 +02:00
Sarjuuk
9fc6f7896e Profiler/Fixup
* fix achievementpoint total being the sum of both factions
2026-04-09 20:17:51 +02:00
Sarjuuk
725686d1cd Setup/Help
* be more clear that connecting to auth and characters db is conditional
 * updated some lost references to renamed databases and config options
2026-04-08 15:47:17 +02:00
emv33
20f38e1c2b
Readme/Fixup (#503)
* fix link to badge images in README.md
2026-04-03 01:13:02 +02:00
Sarjuuk
8ded8e8e97 Filter/Fixup
* sanitize input before trying to construct filter and redirect
2026-03-30 18:17:45 +02:00
Sarjuuk
681d29e5f3 Search/Indexing
* add reversed words and update token processing to allow
   fulltext search to match end of word (e.g. searching 'wind -storm'
   will now find 'whisperwind' or 'windrunner' and ignore 'stormwind')
 * fix search token duplication for spells
2026-03-28 21:13:59 +01:00
Sarjuuk
764ea1c7fa Setup/Spells
* manually set several trade skills and related items as unavailable
 * don't use unavailable trade skills to resolve reagent dependencies
   on Spell Detail Page
 * closes #499
2026-03-27 00:13:00 +01:00
Sarjuuk
7906b6c942 Localization/Fixup
* localize "unnamed" text for gameobjects and areatrigger
 * fix empty links in Quest Detail Page for quests with
   external completion event but empty AreaDescription
 * closes #501
2026-03-26 15:22:11 +01:00
Sarjuuk
38dc0e834e Search/Fixup
* fix creature indices only including creatures with name AND subname
 * closes #500
2026-03-25 20:56:01 +01:00
Sarjuuk
c93417301a Quests/Fixup
* fix showing "Gains:" header on detail page if there is nothing
   to be gained
2026-03-22 16:34:42 +01:00
Sarjuuk
edfe800348 Sitemap/Fixup
* fix broken link to icons
 * fixes #498
2026-03-16 20:34:14 +01:00
Sarjuuk
7c9131eca8 Misc/Fixup
* fix formated money having trailing whitespaces if copper amount was 0
 * also cleanup
2026-03-12 21:50:38 +01:00
Sarjuuk
0d645334cb Sitemap
* generate and link basic sitemap
2026-03-05 12:31:07 +01:00
Sarjuuk
5a230daad6 Search/Indexing
* move fulltext indizes for tables /w ~10k+ rows to separate tables
   > sounds have ~12k but names are effectively incompatible with FTI
 * normalize searchable strings to catch edgecases
   > preparse spell descriptions + buffs
   > move effect text cols of items to new table
 * also fix extended search of creatures, spells & quests
2026-03-03 21:02:33 +01:00
Sarjuuk
2161a7b846 Search/Fixup
* dashes also split words into multiple tokens, so they are now
   stripped from search entirely (see: 'weather-beaten')
 * only search for the full search input if the tokenizer didn't yield
   any fulltext tokens. This should fix search performance after 00f048d3ae
 * fixes #497
2026-02-27 12:02:38 +01:00
Sarjuuk
297d678270 Cookies/Fixup
* fix storing Item Summary state
 * amends e421bdba79
2026-02-26 19:16:40 +01:00
Sarjuuk
254f3f7f1a Filter/Fixup
* fix criterium related world event:none excluding unspawned entities
2026-02-26 16:26:03 +01:00
Sarjuuk
c85675e181 Setup/DBCReader
* port extended client file handling from other branch
 * class DBC -> DBCReader now initializes a DBCFile
   which itself is a BinaryFile
 * update DBCReader to use the new DB wrappers multi-insert feature
2026-02-26 16:26:03 +01:00
Sarjuuk
69df50619a DB/Dependency
* remove unmaintained DbSimple
 * add package db/dibi as substitute db abstraction
2026-02-26 16:26:02 +01:00
Sarjuuk
8a404b32aa ProfilerExclusions/Fixup
* fix including all exclude groups
2026-02-26 11:41:15 +01:00
Sarjuuk
1556a5e4c9 ItemFilter/Fixup
* fix including jewelcrafter only gems toggle in weights
2026-02-26 10:38:05 +01:00
Sarjuuk
15f20a63f7 Quests/Fixup
* fix 'Side' displayed in infobox on detail page
 * don't display 'Races' in infobox if they encompass an entire faction
2026-02-24 17:53:56 +01:00
Sarjuuk
750e176c33 Stats/Fixup
* fix melee attack power from spells counting towards generic attack power
 * combine melee and ranged attack power from the same spell into
   generic attack power, if amounts are equal
2026-02-24 14:14:09 +01:00
Sarjuuk
f7a1ae241f Quest/Fixup
* fix unused quests being shown in quest series on detail page
2026-02-14 22:49:01 +01:00
Sarjuuk
6051ef5a4a Misc/Cleanup
* update some unit flags / bytes
2026-02-14 22:36:23 +01:00
Sarjuuk
f6d7ccbb69 Quest/Filter
* update quest flags to current knowledge base
 * implemented filter criterium for pvp-enabling quests
2026-02-13 00:39:28 +01:00
Sarjuuk
68ca4973b1 Items/Filter
* implement 'effect text' filter
 * slight cleanup in itemset setup to match changes for effect text
2026-02-12 21:08:56 +01:00
Sarjuuk
84d2e30940 Filters/Fixup
* implement NPC 'has location' criterium
 * fix NPC 'uses model' criterium using string comparison on int values
2026-02-12 14:47:24 +01:00
Sarjuuk
2216b664fa DBTypes/Filters
* filters should test string input during initialization, so errors
   can be passed on to the PageTemplate.
 * have strings be compared for identity by default and (NOT) LIKE as
   optional operator
 * note: if a string criterium gets processed via callback function
   it will not get prechecked
2026-02-12 14:47:24 +01:00
Sarjuuk
9d187e8d4c StatWeights/Fixup
* move weightable stats from Util to Stat
 * align what stats can be saved in weightscales to match javascript
2026-02-12 00:28:27 +01:00
Sarjuuk
00f048d3ae Search/Fixup
* readd regular indizes for name cols. There are cases where entities
   are named in a way that does not work with FT indizes.
   ex. "XT:9" is two tokens "XT", "9" which are too short to be indexed.
 * additionally to FT search also exact match col to search string.
2026-02-12 00:27:49 +01:00
Sarjuuk
e421bdba79 PageTemplate/Cookies
* set path and domain on consent cookie
 * if page uses https, tag cookies as secure
 * use samesite=lax so cookies get sent if user visits via external link
2026-02-09 09:14:07 +01:00
Sarjuuk
511e1a78e6 PageTemplate/Styles
* set styles for audio controls so they visually fit on chrome
2026-02-05 21:08:16 +01:00
Sarjuuk
3510c8211c Spells/Scaling
* implement tooltip scaling calculation for spells with attribute
   SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION
 * I don't know why i loaded item scales as spell scales a decade ago.
   Lets hope removing that didn't break stuff.
2026-02-05 21:08:16 +01:00
Sarjuuk
55f599bbb8 Game/WorldPosition
* rework map pos -> zone pos calculation. It was kinda stupid to run a
   query for each individual spawn point. So cache all wma entries per
   map and do the calculation in php.

 * sqlgen 'spawns' is now about twice as fast
2026-02-05 08:19:35 +01:00
Sarjuuk
5258290332 Profiler/Cleanup
* cleanup in sync / queue
2026-02-02 19:21:21 +01:00
Sarjuuk
a29c88fe78 Profiler/Fixup
* fix Profile tooltips not loading if the triggering url wasn't entirely in lowercase
2026-02-02 19:21:08 +01:00
Sarjuuk
83ec057dea PageTemplate/Fixup
* fix not setting consentFooter for all cases and occasionally importing its value from cache
2026-02-01 17:30:10 +01:00
Sarjuuk
1292fa90bd Profiler/Fixup
* handle special sourcemore dungeondifficulty 99 in Profiler summaries
   (unclear if i'm doing it wrong or if sm.dd:99 is newer than the Profiler)
2026-02-01 17:30:10 +01:00
Sarjuuk
a21feab939 Profiler/Fixup
* catch failing queries for characters, arena teams and guilds on realm
2026-01-30 20:09:33 +01:00
Sarjuuk
dfabc9464d Profiler/Fixup
* fix tooltips for profiles pending rename
2026-01-30 17:33:01 +01:00
Sarjuuk
4f4f68bd66 Profiler/Fixup
* don't increment index for renamed chars again when a known to rename chars stub is synced
2026-01-30 17:33:01 +01:00
Sarjuuk
09f14ee4e1 Setup/Fixup
* column keys names like to be unquoted
 * amends cadb4aa0d3
2026-01-30 00:41:26 +01:00
Sarjuuk
7e4e52de7b Setup/Fixup
* remove extraneous , from query added in cadb4aa0d3
2026-01-29 23:43:59 +01:00
Sarjuuk
62d898173a Account/Fixup
* fix referencing nonexistent error message if password validation fails
 * fixes #488
2026-01-29 22:34:02 +01:00
Sarjuuk
021cdf6228 Spells/Glyphs
* link back from applied spell to applying glyph spell via new tab: glyphs
 * closes #489
2026-01-29 22:11:19 +01:00
Sarjuuk
08318d286c Objects/SpellFocus
* link from spellfocus in objects and spell infobox to objects filter if able
 * closes #491
2026-01-29 22:11:19 +01:00
Sarjuuk
26292d17c5 Objects/Fixup
* don't show loot charges in infobox if restock cd is set but min/max charges are not
 * fixes #493
2026-01-29 22:11:19 +01:00
Sarjuuk
b25a70458b Help/Fixup
* manually set articleUrl so help pages have content again
2026-01-29 22:11:18 +01:00
Sarjuuk
c58b073a73 Quests/Fixup
* fix inconsistency in linking to categoy: uncategorized (-2)
 * fixes #485
2026-01-28 19:52:13 +01:00
Sarjuuk
cadb4aa0d3 Icons/Fixup
* reference original icon names/paths during setup
 * fixes #487
 * amends 1e1ce29438
2026-01-28 19:52:12 +01:00
Sarjuuk
08ae564a48 PageTemplate/Fixup
* escape input username on user page
2026-01-27 14:32:05 +01:00
Sarjuuk
0378a84373 Routing/Fixup
* always route requests where page equals page parameter
   (i.e. /?spell=spell; /?user=user) to BaseResponse
 * fixes #486
2026-01-27 14:32:05 +01:00
Sarjuuk
e675a8f953 PageTemplate/Fixup
* Don't try to be smart with the helper methods as they are used to escape user input.
2026-01-26 20:04:49 +01:00
Sarjuuk
c6d92031c5 Quests/Fixup
* fix converting min/max reputation requirements to TC condition
   on detail page (it still lacks the exact amount)
 * also Game::getReputationLevelForPoints was off by 1
2026-01-25 19:26:47 +01:00
Sarjuuk
5f7247b292 Quest/Misc
* move honor/arena point reward to gains on detail page
   so it no longer looks like an item
2026-01-23 23:20:24 +01:00
Sarjuuk
98763c3060 Setup/Fixup
* fix calculation of honor reward
2026-01-23 18:43:29 +01:00
Sarjuuk
4c598972cc Search/Misc
* why loop when regex does trick
 * further unify variable naming and layout
2026-01-19 20:15:11 +01:00
Sarjuuk
09fe715bc1 Spells/Fixup
* add forgotten filter criterium 43 - usableinbgs
 * fixes #484
2026-01-19 19:47:07 +01:00
Sarjuuk
15338dc503 PageTemplate/Fixup
* only need to ask for consent if analytics is enabled
 * fixes #483
2026-01-19 19:47:07 +01:00
Sarjuuk
53374972ef Search/Fixup
* strip leading/trailing dashes from search tokens.
   Casues FT syntax errors when combined with our own modifiers
2026-01-19 19:31:35 +01:00
Sarjuuk
0ea5477d6e Items/Fixup
* fix spell struct to calculate stats from when viewing item XML
2026-01-19 19:31:35 +01:00
Sarjuuk
2e8abf6dff PageText/Misc
* $t is probably the players HK rank. Fill with name for lowest rank.
2026-01-18 14:56:26 +01:00
Sarjuuk
6df9145446 Search/Fixup
* do not use stopwords in fulltext search
   apparentyl there is a bug in innodb where including stopwords in a search causes the lookup to fail entirely.
   e.g. innodb_ft_enable_stopword must be disabled when the index is edited (rows are added/removed)
 * don't create a MATCH AGAINST search from empty search strings after sanitization
 * drop fulltext indizes for locale zhCN
   logographic languages need special treatment, which handling may differ by db provider
 * use LIKE search by default for locale zhCN. Added config option to use fulltext if supported by db.
2026-01-17 23:07:44 +01:00
Sarjuuk
7616ec25fc DB/Search
* add more indizes to large tables for cols used in lookups
 * drop multi-column indizes on spell as they are not utilized by mysql
 * add and use fulltext indizes for names of items, spells, quests, creatures & objects
   could add more, but is it really necessary?
 * limitations
   - still need a solution for race/class/spellFamily masks cols that are used as such
   - fulltext indizes in boolean mode cant partial match the end of a word.
     reverse name cols and search and match back to front like that..? blows up db size even more though
     (+trike* : "stormstrike" => +ekirt* : "ekirtsmrots")
2026-01-16 16:56:24 +01:00
Sarjuuk
a89eef5736 Setup/Fixup
* fix required class/race masks on items and quests
   during import instead of on demand
2026-01-16 08:27:27 +01:00
Sarjuuk
2ef3f575c3 PageTemplate/Misc
* fetch user vars on __wakeup / __construct so the queries get picked up by the DB profiler
 * unset temporary vars before serialization
 * reduce redundancy
2026-01-15 22:43:20 +01:00
Sarjuuk
59506dda11 DB/Profiler
* add indizes to searchable cols to hopefully speed up page loads
2026-01-14 17:10:03 +01:00
Sarjuuk
b4f40b4264 Filter/Fixup
* only criteria should be affected by the match any/none selector
 * multi-selects are never null and shouldn't be tested as such.
2026-01-13 22:19:48 +01:00
Sarjuuk
a5129b46b2 Template/Fixup
* remove excess colons from profiler related filters
2026-01-13 21:00:55 +01:00
Sarjuuk
a465de1653 Spells/Effects
* link to map with start/end position for SPELL_EFFECT_SEND_TAXI
2026-01-13 20:33:28 +01:00
Sarjuuk
03fb4045dc JS/Date
* add localeaware Date.getLocaleDay method and reorder week days array
 * calendars should now have a region approriate first day of the week
2026-01-11 19:38:57 +01:00
Sarjuuk
ce020204cb Events/Fixup
* fix events occupying an additional day in calendar view
   if starting and ending at 0:00
2026-01-11 19:38:57 +01:00
Sarjuuk
40717a8057 Site/Layout
* increase site layout further to make the Icons grid fit entirely
 * amends b3e215cc40
2026-01-09 17:32:28 +01:00
Sarjuuk
face95c1dd PageTemplate/Consent
* fix showing consent overlay when cookie is already set
2026-01-09 17:25:20 +01:00
Sarjuuk
a17d4a5528 Quests/Cleanup
* render quest objectives on detail page by var type instead of magic number
2026-01-07 23:07:37 +01:00
Sarjuuk
793dc4dfdd Dependencies/Composer
* use composer to manage future dependencies
2026-01-07 20:53:02 +01:00
Sarjuuk
d686f87467 User/Fixup
* fix my-guides query on user page
2026-01-07 20:42:22 +01:00
Sarjuuk
a6438454d6 Misc/Cleanup
* remove namespace includes sneakily auto placed by vscode
2026-01-07 19:42:17 +01:00
Sarjuuk
e96fafaf8b Quests/Fixup
* show related quests on detail page for events without associated holiday
 * closes #480
2026-01-03 18:07:24 +01:00
Sarjuuk
6077c39055 Account/Avatar
* minor cleanup
 * make use of array_find_key polyfill
2026-01-02 17:28:02 +01:00
Sarjuuk
8417b6854b PageTemplate/Fixup
* fix broken external links from detail pages
 * amends 715c1534eb
 * closes #477
2026-01-02 17:05:58 +01:00
Sarjuuk
a4629a4f6f Events/Fixup
* fix related items tab showing all the items when none should be shown
 * amends c44bf4f575
 * closes #478
2026-01-02 16:35:46 +01:00
Sarjuuk
de911d0db6 Profiler/Tooltips
* add some spacing between spell school and value
2026-01-01 15:42:34 +01:00
Sarjuuk
e88bbb774f Misc/Fixup
* yearly year increment
2026-01-01 01:28:38 +01:00
Sarjuuk
715c1534eb Core/Endpoints
* don't use raw input to recreate subcategories for filter urls and
   external links
 * if a page does not expect categories it will now error out if it is
   called with parameters
 * fixed infite redirect loop that could occur if the pageName was an
   invalid string
 * added lost filter string for external page call to NPCs Page
2026-01-01 01:26:35 +01:00
Sarjuuk
23c5c71f4a Misc/Fixup
* fixed crash when bit blob contains trailing whitespace
2025-12-30 20:39:22 +01:00
Sarjuuk
6c1b4e4e1a SQL/Fixup
* update zoneOrSort > questSortId in custom data table
 * amends eec21c2763
2025-12-30 20:36:06 +01:00
Sarjuuk
eec21c2763 Quests/Fixup
* rename columns of quests table to avoid name collisions
   and to match the dbc they are referencing
 * fixes #463
2025-12-30 19:16:10 +01:00
Sarjuuk
c44bf4f575 Config/Misc
* hardcode sql limits within their respective components instead of
   having them configurable
 * creating a new DBTypeList defaults it to being unlimited
   * SQL_LIMIT_NONE (0) > removed as it only existed for consistency
   * SQL_LIMIT_DEFAULT (300) > frontend / Listview::DEFAULT_SIZE
   * SQL_LIMIT_SEARCH (500) > Search::DEFAULT_MAX_RESULTS
   * SQL_LIMIT_QUICKSEARCH (10) > Search::SUGGESTIONS_MAX_RESULTS
2025-12-30 03:20:09 +01:00
Sarjuuk
b9d888ab3a Compat/TDB
* update conditions with changes from TC
   > 4fd3669f1b
2025-12-29 20:04:19 +01:00
Sarjuuk
2b3b9de8bc Entity/AI
* store and display StringIds
 * unify storage of AI/Scripts for NPCs and Objects
 * store and display StringIds and AI/Scripts from individual spawns in
   mapper tooltip and infobox
2025-12-29 20:04:19 +01:00
Sarjuuk
9db943e8f4 Compat/TDB
* drop references to script_waypoint and waypoints tables, removed in
   > 91dcae540e
   > f3b691dcb0
 * db requirement bump
2025-12-28 20:18:20 +01:00
Sarjuuk
289d5062bd NPC/Objective
* must test for KillCredit as there are cases where reqNpcOrGo is 0,
   but its required count is > 0
 * amends d34765eed5
 * fixes #475
2025-12-28 20:18:20 +01:00
Sarjuuk
4e848e29a7 Quests/Fixup
* fix missing objective quantity if required entity is unknwon
 * don't display go/npc objective if go/npc id is 0
2025-12-28 20:18:20 +01:00
Sarjuuk
69b8fdcc27 Misc/Cleanup
* could have sworn there were more blobs to be indexed..
2025-12-28 20:18:19 +01:00
Sarjuuk
9bbe95d5e8 Search/Fixup
* partially revert eb70065e0f as the
   regex without the /u flag destroys valid umlauts like 'ß'
 * convert strings from utf8 to utf8 instead to achieve the same effect
2025-12-26 17:22:29 +01:00
Sarjuuk
8f9acedb8f Items/Enchantments
* cleanup list generation for random enchantments
 * note that rand enchants should be grouped purely by stat type, which requires
   more work than it is currently worth
2025-12-23 21:31:05 +01:00
Sarjuuk
d34765eed5 NPC/Objective
* also show quests in objective-of tab the npc has a KillCredit entry for.

 * closes #470
2025-12-21 20:42:46 +01:00
Sarjuuk
e93774f854 SmartAI/Misc
* format times as float for more precision
 * display 0 values for numeric ranges
2025-12-21 20:42:45 +01:00
Sarjuuk
e60bed061d PageTemplate/Cache
* allow flagging scripts as non-caching and purge them before serialization
 * fixes consent scripts being passed on to other users
2025-12-18 21:45:28 +01:00
Sarjuuk
b591311803 Lang/Fixup
* fix crash when race mask was resolved to faction
 * fix id buffer bleeding from resolving class mask into
   resolving race mask and vice versa

 * fixes #472
2025-12-18 00:57:07 +01:00
Sarjuuk
2a8db972a1 Misc/Cleanup
* drop unused and obsolete runtime measurement
2025-12-18 00:57:07 +01:00
Sarjuuk
5bfc0da3f8 Mapper/SpawnPosFix
* automated refreshing the page after moving a spawn point
2025-12-15 16:43:06 +01:00
emv33
af67e84cd8
Server/Config (#468)
* wrap php_value directives in IfModule blocks to avoid a 500 error if you are not using mod_php
2025-12-15 14:46:45 +01:00
Sarjuuk
fe9fdb455c Achivements/Criteria
* link back to achievement from achievement_criteria_data
 * closes #466

 * also don't try to resolve continental maps to zoneIds
2025-12-15 14:42:48 +01:00
Sarjuuk
ec1b1d3da0 Infobox/Fixup
* remove excess escapes after Frontend\Markup implementation
 * fixes #467
2025-12-15 14:42:48 +01:00
Sarjuuk
a9a30d3106 Spells/Fixup
* don't display a power cost of Happiness (4)
   (looks like bogus dbc data)
2025-12-14 21:52:12 +01:00
Sarjuuk
8b35421e53 Spells/Fixup
* proc cooldown is expected in ms, not us
 * fixes #465
2025-12-14 21:21:54 +01:00
Sarjuuk
983eb69759 SmartAI/Fixup
* drop unicode colons from text definitions as they were displayed in plaintext
   (can't use real colons inside conditionals)
2025-12-14 18:41:43 +01:00
Sarjuuk
6f6adc127e SmartAI/Fixup
* fix AI for npc texts that use target placeholders
2025-12-14 18:12:52 +01:00
Sarjuuk
d22d062cce Setup/DB
* fix setup if db is defined but inaccessible
2025-12-13 19:52:41 +01:00
Sarjuuk
d3a293047e Setup/Fixup
* restore db self test broken by greedy grep in 14c159c164
 * update file reference in error mesasge
2025-12-13 16:15:59 +01:00
Sarjuuk
59c3f688d1 DB/Fixup
* add lost column from 1e1ce29438 to aowow_icons
2025-12-13 16:15:59 +01:00
Sarjuuk
92f949b3c6 Analytics/Tracking
* drag the tracking object a decade into the future
2025-12-13 16:15:59 +01:00
Sarjuuk
a90d1d242a Misc/Fixup
* the first GET param, the one the page view is derived from, should not be an array
2025-12-12 16:05:19 +01:00
Sarjuuk
eb70065e0f Search/Fixup
* improve handling of invalid unicode sequences in urls (%xx).
   Page no longer breaks entirely, just misses the search term as the faulty string gets silently dropped.
 * don't perform searches if you don't have valid terms to search for
2025-12-12 16:05:19 +01:00
Sarjuuk
f5d987a864 SpellDetailPage/Fixup
* fix multiple stack rules listviews sharing the same id
2025-12-08 16:37:10 +01:00
Sarjuuk
53182aedea Listviews/RefLoot
* tag ref loot rows as 'commonloot' to keep them out of the 'noteworthy loot' subtab
 * don't create icon links for ref loot rows
2025-12-07 15:12:46 +01:00
Sarjuuk
b85ee4ff23 Source/SpellLoot
* get additional items from spell_loot_template to source: Crafted
 * get loot from spells with effect 59 (Fast Loot) to source: Drop (obtained by item looting)
 * display spell_loot on ItemDetailPage if item has onUse spell
2025-12-05 21:32:26 +01:00
Sarjuuk
1e1ce29438 Icons
* fix names (still use original string for title cards, just not internals and files)
 * link manually created icons (holidays, classes, races) to icon db
 * use newly linked icons instead of hardcoded values for classes/races
 * implement used-by-classes filter on icons listing
 * fix internal name of race 5 (undead) which fucked up icons
 * version bump to invalidate cache
2025-12-04 15:14:32 +01:00
Sarjuuk
d6d589caba Items/Filter
* if 'sold by vendor' filter is selected, show vendor cost column
2025-12-02 23:09:36 +01:00
Sarjuuk
ca563f081e NPCs/Resistances
* evalue and display school immune mask
2025-12-02 18:49:01 +01:00
Sarjuuk
957f3bcf20 Template/JSGlobals
* fixed merging js globals from dynamic content into cached js globals
 * e.g. commenting with item markup on an item page will again create proper item links
2025-12-02 14:48:27 +01:00
Sarjuuk
ffffc16d63 Filter/Dropdowns
* group options for: currency, factions, itemcurrency & zones
 * update faction setup so faction "The Frostorn" are available and it does not rely on factiontemplate.dbc
 * fix broken purchasablewith* localization in locale DE
2025-11-30 18:05:38 +01:00
Sarjuuk
82376f9ead Loot/Fixup
* fix sending empty pctstack property to listview
2025-11-30 17:14:32 +01:00
Sarjuuk
f955c67026 Setup/Stats
* fix stat calculation for food by including triggered spells
2025-11-30 17:14:32 +01:00
Sarjuuk
2305d8bf13 Zone/Map
* group entities on map by name again so different types of the same herb/vein
   don't show up as separate groups
 * except mailboxes
 * partially reverts ea25776225
2025-11-23 20:55:05 +01:00
Sarjuuk
adc0e16064 Itemsets/Boni
* maybe fix unreproducable error where itemset bonus description was null
 * also slight cleanup in itemset bonus code
2025-11-23 00:35:14 +01:00
Sarjuuk
53d0813c80 Setup/Fixup
* fix fetching creature spawns from world
2025-11-23 00:35:14 +01:00
Sarjuuk
73d6be273e Page/Selector
* don't just cast the page call to lowercase but redirect to the fixed case if necessary
2025-11-22 19:04:51 +01:00
Sarjuuk
10ef33f709 Spells/SpellClick
* evaluate npc_spellclick_data and display on Spell and NPC Detail Pages
 * also categorize these spells as NPC-spells
 * closes #438
2025-11-21 22:47:43 +01:00
Sarjuuk
ae1b6c59b1 Quests/Requisites
* fixed case where an exclusiveGroup of 0 wasn't considered
 * closes #456
2025-11-21 21:09:09 +01:00
Sarjuuk
b764200c2a SmartAI/Conditions
* embed Conditions into SmartAI table so we can evaluate CONDITION_SOURCE_TYPE_SMART_EVENT (22)
 * make SmartAI table display flexible
2025-11-20 23:50:23 +01:00
Sarjuuk
c0454917ac Localization/Typo
* fix acc creation prompt for locale ES
2025-11-20 23:50:23 +01:00
Sarjuuk
b3e215cc40 Site/layout
* increase max width of layout from 1340px to 1640px
2025-11-19 20:30:57 +01:00
Sarjuuk
d4694cd2db User/Reputation
* fix storing negative reputation values
2025-11-19 20:30:57 +01:00
Sarjuuk
a5051c9bf5 Loot/Modes
* work against more correctly assigning instance mode to entities and loot
    - added manually collected data for difficulty versions of gameobjects, just boss chests for now.
      update setup/source to default object source to base difficulty version if able
    - update spelldifficulty table to contain the (likely) mapmode it will be used in
  * refactored class loot
    - implement loot mode indicators on listview for creature and gameobject loot
    - show 'drops' listview tab on instance zone page
    - fixes against tribute chest systems (toc / ulduar)
    - fix icc gunship battle chest ownership
2025-11-19 20:22:33 +01:00
Sarjuuk
be3701df91 Params/Fixup
* FILTER_SANITIZE_URL is absurdly strict and will not tolerate umlauts or spaces
   replaced with printable chars regex
2025-11-19 17:33:16 +01:00
Sarjuuk
9b905883df Listview/Conditions
* make column width flex
2025-11-19 16:59:57 +01:00
Sarjuuk
9db3e766da QuestDetailPage/ShowOnMap
* increase strictness for sources of required items shown on mapper from 1% to 5%
2025-11-19 16:59:57 +01:00
Sarjuuk
7f29c1d4b7 CurrencyDetailPage/Tabs
* add currency column to display gains
2025-11-19 16:59:57 +01:00
Sarjuuk
57665aaa9e ItemDetailPage/Misc
* fix "vendor in" mapper
 * add "fished in" mapper
 * move 'see-also' and 'same-model-as' tabs to the back of the tabs list
2025-11-19 16:59:57 +01:00
Sarjuuk
4cb544182d Setup/Spawns
* evaluate waypoint paths linked via creature_template_addon
2025-11-15 22:17:09 +01:00
Sarjuuk
a2b87da285 Localization/CN
* fix excess whitespaces betweeen number and unit
2025-11-15 22:17:03 +01:00
Sarjuuk
03bab92cb8 DateTime/Fixup
* fix displaying expected '0 seconds' instead of 'n/a' or '1 ms'
2025-11-15 22:16:58 +01:00
Sarjuuk
103287f91b Setup/Pets
* move custom data from pet script to db
2025-11-15 22:16:53 +01:00
Sarjuuk
82f36fd342 Setup/Source
* generally flag items of quality artifact as unavailable
 * 04f3aa7a82 caused some items transformed by spell to be 'available'
2025-11-15 22:16:48 +01:00
Sarjuuk
f5654ae21f DateTime
* recreate date functions from javascript in new class DateTime
 * move date and time functions from Util to new class
 * fixes various cooldown messages for account recovery
2025-11-14 19:16:12 +01:00
Sarjuuk
1fe3690244 Cache/Fixup
* fix cache collision on list pages caused by improper encoding of category
 * fix cache key not encoding category values of int: 0
 * version bump to flush caches
2025-11-13 21:29:35 +01:00
Sarjuuk
45417122c2 UserPage/Fixup
* only show related heading if we have tabs to display
2025-11-11 20:35:42 +01:00
Sarjuuk
7cf5dded98 Signatures
* add non-functional endpoint stubs with info
2025-11-11 20:17:39 +01:00
Sarjuuk
31f51276b2 Profiler/Fixup
* fix exception when manually querying for unsynced guild/arena-team
   that shares it's name with another guild/team (e.g. ìíîi is the same to SQL)
   and the target guild/team not being the first result
2025-11-10 20:31:09 +01:00
Sarjuuk
643c3c2a83 Comments/Goto
* fix comment links in reputation history on user page
2025-11-10 18:45:39 +01:00
Sarjuuk
a135dfce90 Profiler/Completions
* add keyed col `exalted` to reputation completions table to speed up lookups
2025-11-09 19:05:33 +01:00
Sarjuuk
0d42d2a2c4 UserPage/Optimization
* split up fetching of custom profiles and characters to make use of existing keys
2025-11-09 16:22:26 +01:00
Sarjuuk
fa89a5ad1e User/Fixup
* fix fetching user characters, borked in 474b5b5aec062b61e8d707c91739b50ad77e81ef
 * take #2
2025-11-09 16:22:00 +01:00
Sarjuuk
6eb5a67add Profiler/Optimization
* move searchable flags to their own db cols to speed up lookups
 * don't cast profile name to LOWER in SQL when displaying tooltips.
2025-11-09 16:20:52 +01:00
Sarjuuk
8a169eb400 Setup/Fixup
* catch error if url from self test is unreachable
2025-11-09 16:16:28 +01:00
Sarjuuk
cf4e8a527c User/Fixup
* fix fetching user characters, borked in 474b5b5aec062b61e8d707c91739b50ad77e81ef
2025-11-08 18:07:26 +01:00
Sarjuuk
48564ab8b5 Misc/Fixup
* fix building num ranges, added in 8212811970
2025-11-07 21:19:43 +01:00
Sarjuuk
edc297f97a NPCs/Fixup
* fix parent npcs name for locales CN and ES
2025-11-07 20:47:39 +01:00
Sarjuuk
5d02a20719 SQL/Misc
* add keys to spells table to speed up related spells queries
2025-11-07 20:34:49 +01:00
Sarjuuk
f44de66de7 User/Profiles
* speed up load of user profiles
2025-11-07 20:34:47 +01:00
Sarjuuk
16c5b73cd3 User/Misc
* don't run querys if not strictly required (e.g. query for chars from ajax context)
 * prepare user globals and favorites so errors can be handled and don't destroy the template
   this also allows for profiling of the affected queries
 * add keys to items table to speed up querying for recipes in general and user completions in particular
2025-11-05 15:39:28 +01:00
Sarjuuk
9020e36db6 SmartAI/Misc
* make errors more verbose if SAI tries to set unexpected flags
 * do not escape strings. By now thats handled by Frontend/Markup
2025-11-04 19:48:26 +01:00
Sarjuuk
597898450d WorldEvent/Misc
* fix excess colons in tooltip
 * fix advancing date window while event is still active
2025-11-04 19:48:19 +01:00
Sarjuuk
6a94888686 Filter/Fixup
* try to prune deselected criteria/weight selectors from filter input
2025-11-04 00:04:24 +01:00
Sarjuuk
e3d6f7b3a7 Profiler/Completions
* show completion info for claimed characters in infobox on
      appropriate db pages
2025-11-03 20:50:54 +01:00
Sarjuuk
37380ff515 Frontend/InfoboxMarkup
* you can now pass attributes to the [li] element
2025-11-03 18:47:06 +01:00
Sarjuuk
8212811970 Misc/Cleanup
* create function for num range .. creation
2025-11-01 21:01:03 +01:00
Sarjuuk
1e9e406ff0 TextResponse/Fixup
* make class non-abstract so we can generate a 403/404 message on base
2025-10-31 16:44:37 +01:00
Sarjuuk
3984bd0ae2 Spells/Misc
* limit chance/ppm precision on spell procs chances
 * do not apply a spells EffectXBonusMultiplier for physical spells
2025-10-29 15:51:23 +01:00
Sarjuuk
441ad38543 Filter/Locales
* fix embedding and triggering fi_toggle on filtrable listviews for locale zhCN
2025-10-28 19:58:47 +01:00
Sarjuuk
88cc76feae Localization/Filters
* add missing spell effects & aura names
 * don't return null for unused effects/auras (triggers an error)
2025-10-28 19:58:44 +01:00
Sarjuuk
96c777191d Spells/Scaling
* move scaling data to the appropriate spell effects (like WH)
2025-10-27 21:19:40 +01:00
Sarjuuk
40b2830cad Spells/Scaling
* hopefully fix a lot of nonsensical spell scaling infos
 * note: an aweful lot of physical spells are hardcoded or have spell scripts and won't display any info
2025-10-27 19:44:00 +01:00
Sarjuuk
9741774683 Spells/Reagents
* fix reagents listing and spell tooltips for nonexistent reagents
2025-10-27 17:02:30 +01:00
Sarjuuk
e8bc37f82f Filter/NPC
* fix react filter
 * remove excess colons
2025-10-27 01:47:27 +01:00
Sarjuuk
7cbe1f6007 Reports/Fixup
* also include source url when checking target context
 * cleanup source url to be usable as key
2025-10-26 19:19:50 +01:00
Sarjuuk
3a25c2390f Listviews/AddIns
* AddIns must be output directly before the listview it is used by
2025-10-26 17:32:18 +01:00
Sarjuuk
cf2ace805b Spawns/Fixup
* fix maps for single-floor dungeons borked in 33cd290dc3
2025-10-24 18:29:41 +02:00
Sarjuuk
2f8e035783 Search/Fixup
* fix redirecting to result on exact hit
2025-10-24 18:28:25 +02:00
Sarjuuk
1a55b30766 UserPage/Fixup
* move description inside text container
 * add missing 'related' heading
2025-10-23 20:49:58 +02:00
Sarjuuk
6d7f9c0f00 Quest/Fixup
* don't try to create an objective for empty SourceItem
 * fixed size of source spell icon
2025-10-23 16:26:59 +02:00
Sarjuuk
862b3dff73 IconElement/Fixup
* do not change type of num / qty params ('+1' is not numeric)
2025-10-23 00:48:04 +02:00
Sarjuuk
b1f22f7e68 Spells/ExtraLoot
* display extra loot from skill_extra_item_template like perfect gems
 * for specializations, display affected spells in Bonus Loot tab
 * cleanup subject->id to typeId
 * closes #286
2025-10-23 00:10:52 +02:00
Sarjuuk
1d922c1147 Locks
* implemented display of LOCK_TYPE_SPELL (3 cases)
 * show "unlocks" tab on spell detail page
 * closes #288
2025-10-22 22:18:27 +02:00
Sarjuuk
f9ace6a671 Spell/Sources
* always display quest source from RewardSpellCast
 * fix inherited quest sources via learn spells
 * closes #353
2025-10-22 18:57:33 +02:00
Sarjuuk
6ea1457c4f Guides/Fixup
* make preview area of class: text so styles are applied as expected
 * fix evaluation of Markup h2/h3 attribute toc=false, so headings are excluded from TOC as expected
2025-10-22 18:06:38 +02:00
Sarjuuk
f522c960d9 Modelviewer/Fixup
* fix buttons of lightbox
2025-10-22 17:08:36 +02:00
Sarjuuk
1365cdb261 Spell/Effects
* show spells affected by SPELL_AURA_IGNORE_COMBAT_RESULT
2025-10-22 01:57:58 +02:00
Sarjuuk
9b591e7a3a Items/Tooltips
* fix itemId to scientific notation conversion, when itemId was joined
   by an enchantmentId (1234e56 => 1.234e53), breaking tooltip display.
2025-10-21 23:35:42 +02:00
Sarjuuk
6da71afc68 Filter/Fixup
* allow unicode chars when checking GET param
2025-10-21 20:53:27 +02:00
Sarjuuk
033a9181ae Profiler/Localization
* TCs guild and arena_team tables as encoded as utf8mb4_general_ci,
   which is not accent-aware. So we have to get all results and filter
   for the correct one in php.
 * fixes an issue where direcly accessing a guild/arena-team whith a name
   simiar to an already known guild/team would lookup the wrong subject
   on the server and then fail to create a local stub with already existing key.
   (Shâdów vs Shadow)
2025-10-21 17:57:00 +02:00
Sarjuuk
1dcc9363da Profiler/Filter
* fix filter params being propagated between different profiler types
   (e.g. arena team size filter being appended to guilds menu)
2025-10-21 15:35:28 +02:00
Sarjuuk
51b6e29316 Profiler/Queue
* send ready status for characters/guilds/arena-teams whose resync
   cooldown hasn't expired yet
2025-10-20 20:38:15 +02:00
Sarjuuk
14c159c164 Setup/Factions
* fix switched base rep field indizes, causing Profiler to miscalculate
   character standing
 * replace hardcoded sql table prefixes
2025-10-20 19:23:52 +02:00
Sarjuuk
33cd290dc3 Setup/Spawns
* fix coords for cases with coords in both WorldMapArea.dbc and
   DungeonMap.dbc without using WorldMapArea.dbc as base floor
2025-10-20 19:23:02 +02:00
Sarjuuk
2e029f3d96 Profiler/Talents
* fix building talent string for hunter pets.
   the alternate spells (e.g. Dash & Swoop) must both be included
 * align talent order in build scripts talenticons, talentcalc with
   Profiler talent string builder
 * fix Shamans gaining 5% Parry by talent Spirit Weapons
 * cleanup
2025-10-20 16:25:24 +02:00
Sarjuuk
6a32c770cd Profiler/Pets
* catch error case where a player owns a pet that is no longer tameable/has no pet family
2025-10-19 22:46:08 +02:00
Sarjuuk
4d421d2bbb Filter/Errors
* handle stat weights quirk, analogous to the criteria quirk
2025-10-18 16:46:25 +02:00
Sarjuuk
176cf137fb Filter/Fixup
* criteria parameters can be placeholder/null
2025-10-17 14:33:52 +02:00
Sarjuuk
830edb8265 PageTemplate/Fixup
* use get_object_vars() instead of property_exists() to test if we can
   load a variable from provided context. The former only returns
   accessible vars while the latter returns true for all properties.
2025-10-16 02:14:47 +02:00
Sarjuuk
7d8b524478 Listview/Cost
* do not append 0 achievementpoints to cost builder. It will be displayed
2025-10-15 21:53:00 +02:00
Sarjuuk
95918c0410 SoundDetailPage/Fixup
* fix exception when assigning WorldState conditions to the zones tab
2025-10-15 01:06:57 +02:00
Sarjuuk
a275955ee3 Admin/Config
* handle Ajax errors
2025-10-15 00:26:05 +02:00
Sarjuuk
37beaa2db5 Filter/Errors
* move checks to __construct so they can be run on $_POST data
   and don't create malformed filter urls
 * if we received malformed $_GET params, build new params and reload
 * do not store error state in cache
 * cleanup
2025-10-15 00:05:55 +02:00
Sarjuuk
c0097f3987 Mapper/Objectives
* fix display of item objectives by making LocString JsonSerializable
2025-10-14 16:17:59 +02:00
Sarjuuk
92c58cc5d1 Localization/UIEscapes
* fixed expanding |2 placeholder in general and when the referenced word itself was a placeholder ($N => <nom>)
 * fixed expanding |3 placeholder for caseIds > 9
2025-10-13 20:37:39 +02:00
Sarjuuk
04f3aa7a82 Setup/Source
* respect disabled Quests and Spells when flagging Items as unavailable
 * reuse data from loot_link to set difficuly bits and zoneId
   for loot container GOs
2025-10-12 22:32:52 +02:00
Sarjuuk
65d490a8ae Enchantments/Stats
* entirely forgo ?_item_stats table when calculating enchantment stats
2025-10-12 22:24:09 +02:00
Sarjuuk
816eacaf73 Summary/Fixup
* allow signed integers (random enchantments) in summary definition
2025-10-12 22:24:03 +02:00
Sarjuuk
034eca1f58 Items/RandEnchants
* fix amount calculation for scaling enchantments
 * cache RandomPropPoints lookups
2025-10-12 22:22:56 +02:00
Sarjuuk
a33abb84fe Setup/Account
* don't overwrite existing account in case of email conflict
2025-10-12 17:48:06 +02:00
Sarjuuk
fb7b22db36 Account/Passwords
* use buildin php functions to handle passwords
 * increase cost of BCRYPT
 * make use of the SensitiveParameter attribute
2025-10-12 17:48:06 +02:00
Sarjuuk
dd838fa994 Misc/Doc
* add several ItemMods unusd by client but still found in item_template as comment
2025-10-12 00:57:09 +02:00
Sarjuuk
d32074fdcd ZoneDetailPage/Tabs
* only offer 'filter result' prompt on tabs for zones that are filtrable
2025-10-12 00:15:59 +02:00
Sarjuuk
494061de82 Cache/Fixup
* correct miscData offset for tooltips introduced in 3edac3c77a
 * fix generating cache key for item upgrade searches
2025-10-12 00:15:52 +02:00
Sarjuuk
9b0aa5c885 Achievements/Fixup
* fix fetching achievements from child catgs if selected catg is empty
2025-10-12 00:15:28 +02:00
Sarjuuk
40e98081c9 LatestComments/RSS
* fix url format for replies
2025-10-12 00:15:15 +02:00
Sarjuuk
77f2a0c21d Profiler/Resync
* fix logging ids on resync failure
2025-10-10 22:33:31 +02:00
Sarjuuk
465e019eaa FactionDetailPage/Tabs
* only offer 'filter result' prompt on tabs for factions that are filtrable
2025-10-10 22:33:25 +02:00
Sarjuuk
63053757c9 Guides/Fixup
* fix showing wrong guide version to staff
 * fix sticky icon offset
2025-10-10 20:53:56 +02:00
Sarjuuk
a96f6c4cdf PageTemplate/Fixup
* really fix merging jsGlobals from comments/etc. into existing PageTemplate
2025-10-10 20:49:56 +02:00
Sarjuuk
b832fc172c Items/Gearscore
* fix warning in GS calculation
2025-10-10 20:49:43 +02:00
Sarjuuk
196f60f176 Filter/Zones
* add missing Ruby Sanctum to zones dropdown
 * sort zones alphabetically
2025-10-10 20:49:32 +02:00
Sarjuuk
204d4b8ae2 Profiler/Sync
* fix SQL FK error when creating guild or arenateam stub.
 * Urlized name field is non-optional
2025-10-10 20:49:14 +02:00
Sarjuuk
5d2fd00358 Profiler/Save
* fixed inventory definitions not allowing for negative ids (random enchantments)
 * added handling invalid inventory definitions
2025-10-10 20:49:07 +02:00
Sarjuuk
1dcdf9623b Endpoints/User
* do not display user page for internal system user
2025-10-08 20:02:48 +02:00
Sarjuuk
215ad39cc6 NPC/Reputation
* try to fix reputation spillover
 * fix extra colon on reputation gains
2025-10-08 19:48:36 +02:00
Sarjuuk
a9ed897ea6 Filter/Fixup
* fix evaluating imbalanced criteria
2025-10-08 19:48:29 +02:00
Sarjuuk
3edac3c77a Endpoints/Cache
* fix cache id collision when category == dbTypeId for a given dbType
 * increment version number to invalidate existing caches
 * maps endpoint doesn't need caching. It is entirely static content.
2025-10-08 19:48:21 +02:00
Sarjuuk
95ee9d2c25 NPCs/Fixup
* fix exception when displaying NPC with elemental resistances in base
   version but not difficulty modes
2025-10-07 15:51:07 +02:00
Sarjuuk
d79742d599 Profiler/Fixup
* dont use unsynced profile stubs in attained % calculation
2025-10-06 23:23:40 +02:00
Sarjuuk
e300086cc8 IconElement/Fixup
* a DOMElements text value must be escaped manually
   (e.g. Foror &amp; Tigule)
2025-10-06 23:23:30 +02:00
Sarjuuk
a7e9ac2cf2 Misc/Fixup
* HTTP_USER_AGENT is not guaranteed to be set
2025-10-06 17:16:41 +02:00
Sarjuuk
05f5b0ed34 Response/Params
* so we can't directly use BackedEnum::tryFrom as validator, because if
   the Enum is of <int> and the string is not what php considers numeric,
   we get a straight TypeError Exception instead of null for failing the tryFrom.
2025-10-06 17:06:49 +02:00
Sarjuuk
e37620c01b Search/Fixup
* fix pruning empty tokens from search
2025-10-06 17:06:43 +02:00
Sarjuuk
c40bd3851b Profiler/Fixup
* fix scoring perm enchantments
2025-10-06 17:06:34 +02:00
Sarjuuk
452615a92d Filters/Misc
* be a bit more lenient on level inputs
 * fix displaying array of requirements on error
2025-10-06 17:06:25 +02:00
Sarjuuk
704894c1e3 Spells/Fixup
* skillLines can be empty for unused glyphs etc.
2025-10-06 17:06:14 +02:00
Sarjuuk
045c16c241 PageTemplate/Profiler
* don't skip running parent::generate for incomplete profiles
2025-10-06 15:00:47 +02:00
Sarjuuk
7b429811a9 Defines/Races
* add unplayable races to ChrRace enum so RaceDetailPage can display them.
 * don't show empty icons for unplayable races
2025-10-05 20:20:40 +02:00
Sarjuuk
aa7c0186fc Profiler/Cleanup
* gracefully handle DB errors when fetching realms instead of crashing
2025-10-05 20:19:48 +02:00
Sarjuuk
7b752143a0 Creature/Quotes
* don't add superfluous creature name placeholder to emotes
2025-10-05 18:55:39 +02:00
Sarjuuk
baf4ba5b98 Codestyle/Cleanup 2025-10-05 00:28:27 +02:00
Sarjuuk
eb95b03e31 SmartAI/Update
* implement events added in 3bb4f56773
2025-10-04 20:02:34 +02:00
Sarjuuk
4fe35d9e3c PageTemplate/Fixup
* fix merging jsGlobals from comments/etc. into existing PageTemplate
2025-10-04 17:13:45 +02:00
Sarjuuk
5355989015 Logging/Misc
* don't log passwords to DB (and neither check_passwords)
2025-10-04 17:13:34 +02:00
Sarjuuk
9fc84cdf9e Comments/Fixup
* fix false error when voting on relies
2025-10-04 15:39:44 +02:00
Sarjuuk
a6108be400 Spells/Parsing
* bandaid fix parsing deeply nested formulas in non-interactive mode
   (should rethink how/when formulas get flagged as un-evalable)
2025-10-04 01:10:38 +02:00
Sarjuuk
ff690770b5 Misc/Fixup
* type error when declaring listview
2025-10-04 00:15:38 +02:00
Sarjuuk
6263ccd92a SkillDetailPage/Tabs
* add tab for spells modifying skill value
2025-10-03 18:32:27 +02:00
Sarjuuk
60eb816002 Quickfacts
* where applicable:
   - show typeId
   - show english name if currently localized
   - show percentage attained by synced profiles
 * fixed some typos
2025-10-03 17:49:50 +02:00
Sarjuuk
bc112b2b16 Template/Fixup
* fix directly adding dataloader to PageTemplate
2025-10-03 17:49:49 +02:00
Sarjuuk
6d86f880f4 Analytics/Cookies
* don't ask users to consent on GA tracking if we don't use GA tracking
2025-10-02 19:53:53 +02:00
Sarjuuk
bd1f139c2e CLI/Misc
* handle error case where setup is run automated and receives no input on STDIN
2025-10-02 16:16:49 +02:00
Yrito
36aa33ac26 Setup/CLI
* Allow starting setup at specific step
2025-10-02 14:35:13 +02:00
559 changed files with 16274 additions and 11365 deletions

17
.gitignore vendored
View file

@ -1,10 +1,5 @@
# Git
*.orig
# cache # cache
/cache/template/* /cache/*
/cache/alphaMaps/*
/cache/setup/*
# extract from MPQ # extract from MPQ
/setup/mpqdata/* /setup/mpqdata/*
@ -17,10 +12,8 @@
/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/*
/datasets/* /datasets/*
!/datasets/zones
# /datasets/item-scaling
# extracted sounds # extracted sounds
/static/wowsounds/* /static/wowsounds/*
@ -30,7 +23,7 @@
/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_[end|start]
/static/images/wow/hunterpettalents/* /static/images/wow/hunterpettalents/*
/static/images/wow/Interface/* /static/images/wow/Interface/*
/static/images/wow/loadingscreens/* /static/images/wow/loadingscreens/*
@ -50,3 +43,7 @@
/static/uploads/signatures/* /static/uploads/signatures/*
/static/uploads/temp/* /static/uploads/temp/*
/static/uploads/guide/images/* /static/uploads/guide/images/*
# composer
/includes/libs/*
composer.phar

View file

@ -26,8 +26,10 @@ AddDefaultCharset utf8
</IfModule> </IfModule>
# UHD screenshots can get pretty large (cannot be set in config) # UHD screenshots can get pretty large (cannot be set in config)
<IfModule mod_php.c>
php_value upload_max_filesize 20M php_value upload_max_filesize 20M
php_value post_max_size 25M php_value post_max_size 25M
</IfModule>
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

View file

@ -2,7 +2,7 @@
## Build Status ## Build Status
![fuck it ship it](http://forthebadge.com/images/badges/fuck-it-ship-it.svg) ![fuck it ship it](https://forthebadge.com/badges/fuck-it-ship-it.svg)
## Introduction ## Introduction
@ -27,7 +27,8 @@ Also, this project is not meant to be used for commercial purposes of any kind!
+ [Internationalization](https://www.php.net/manual/en/book.intl.php) + [Internationalization](https://www.php.net/manual/en/book.intl.php)
+ [GNU Multiple Precision](https://www.php.net/manual/en/book.gmp.php) (When using TrinityCore as auth source) + [GNU Multiple Precision](https://www.php.net/manual/en/book.gmp.php) (When using TrinityCore as auth source)
+ MySQL ≥ 5.7.0 OR MariaDB ≥ 10.6.4 OR similar + MySQL ≥ 5.7.0 OR MariaDB ≥ 10.6.4 OR similar
+ [TDB 335.21101](https://github.com/TrinityCore/TrinityCore/releases/tag/TDB335.21101) (no other other providers are supported at this time) + [Composer](https://getcomposer.org/download/)
+ [TDB 335.25101](https://github.com/TrinityCore/TrinityCore/releases/tag/TDB335.25101) including updates up to [TrinityCore/TrinityCore@f3b691d](https://github.com/TrinityCore/TrinityCore/commit/f3b691dcb085014ec3f0e2c60ab94fc9c00e8aa8) (no other other providers are supported at this time)
+ WIN: php.exe needs to be added to the `PATH` system variable, if it isn't already. + 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 + 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)) + [MPQExtractor](https://github.com/Sarjuuk/MPQExtractor) / [FFmpeg](https://ffmpeg.org/download.html) / (optional: [BLPConverter](https://github.com/Sarjuuk/BLPConverter))
@ -38,7 +39,7 @@ audio processing may require [lame](https://sourceforge.net/projects/lame/files/
#### Highly Recommended #### Highly Recommended
+ setting the following configuration values on your TrinityCore server will greatly increase the accuracy of spawn points + setting the following configuration values on your TrinityCore server (and running it once) will greatly increase the accuracy of spawn points
> Calculate.Creature.Zone.Area.Data = 1 > Calculate.Creature.Zone.Area.Data = 1
> Calculate.Gameobject.Zone.Area.Data = 1 > Calculate.Gameobject.Zone.Area.Data = 1
@ -50,8 +51,10 @@ audio processing may require [lame](https://sourceforge.net/projects/lame/files/
`git clone git@github.com:Sarjuuk/MPQExtractor.git MPQExtractor` `git clone git@github.com:Sarjuuk/MPQExtractor.git MPQExtractor`
#### 2. Prepare the database #### 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. Ensure that the account you are going to use has **full** access on the database AoWoW is going to occupy and ideally only **read** access on the world and optionally auth and characters databases you are going to reference.
Import files 01 - 03 from `setup/sql/` in order into the AoWoW database `mysql -p {your-db-here} < setup/sql/01-db_structure.sql`, etc. Import files 01 - 03 from `setup/sql/` in order into the AoWoW database `mysql --default-character-set=utf8 -p {your-db-here} < setup/sql/01-db_structure.sql`, etc.
**Optional**: If you are using MySQL ≥ 8.4.0 and want to support fulltext search for locale zhCN, additionally import `setup/sql/04-db_optional_mysql_only.sql`. Enables this in settings after AoWoW has been set up.
#### 3. Server created files #### 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 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
@ -82,17 +85,17 @@ Extract the following directories from the client archives into `setup/mpqdata/`
> \<localeCode>/Interface/FlavorImages > \<localeCode>/Interface/FlavorImages
> \<localeCode>/Interface/Calendar/Holidays/ > \<localeCode>/Interface/Calendar/Holidays/
> \<localeCode>/Sound/ > \<localeCode>/Sound/
.. optionally (not used in AoWoW):
> \<localeCode>/Interface/Glues/Loadingscreens/
> \<localeCode>/Interface/Glues/Credits/
#### 5. Reencode the audio files #### 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`. 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 WIN](https://gist.github.com/Sarjuuk/d77b203f7b71d191509afddabad5fc9f)
* [example for \*nix](https://gist.github.com/Sarjuuk/1f05ef2affe49a7e7ca0fad7b01c081d) * [example for \*nix](https://gist.github.com/Sarjuuk/1f05ef2affe49a7e7ca0fad7b01c081d)
#### 6. Run the initial setup from the CLI #### 6. Install dependencies with composer
`php composer.phar install --no-dev` on a project level composer install, or
`composer install --no-dev` on a system level composer install
#### 7. Run the initial setup from the CLI
`php aowow --setup`. `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. 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. When you've created your admin account you are done.
@ -101,10 +104,10 @@ When you've created your admin account you are done.
## Troubleshooting ## Troubleshooting
Q: The Page appears white, without any styles. Q: The Page appears white, without any styles.
A: The static content is not being displayed. You are either using SSL and AoWoW is unable to detect it or STATIC_HOST is not defined properly. Either way this can be fixed via config `php aowow --siteconfig` A: The static content is not being displayed. You are either using SSL and AoWoW is unable to detect it or STATIC_HOST is not defined properly. Either way this can be fixed via config `php aowow --configure`
Q: Fatal error: Can't inherit abstract function \<functionName> (previously declared abstract in \<className>) in \<path> 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 conflict with each other. (Zend OPcache, XCache, ..) Disable all but one. A: You are using multiple cache optimization modules for php that are in conflict with each other. (Zend OPcache, XCache, ..) Disable all but one.
Q: Some generated images appear distorted or have alpha-channel issues. 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. A: Image compression is beyond my understanding, so i am unable to fix these issues within the blpReader.
@ -118,7 +121,7 @@ Q: I'm getting random javascript errors!
A: Some server configurations or external services (like Cloudflare) come with modules, that automatically minify js and css files. Sometimes they break in the process. Disable the module in this case. A: Some server configurations or external services (like Cloudflare) come with modules, that automatically 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? 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 against the characters database and have to use the incomplete/outdated cached profiles of AoWoW. A: Whenever you try to view a new character, AoWoW needs to fetch it first. Since the data is structured for the needs of TrinityCore and not for easy viewing, AoWoW needs to save and restructure it locally. To this end, every char request is placed in a queue. While the queue is not empty, a single instance of `prQueue` is run in the background as not to overwhelm the characters database with requests. This also means complex search queries can't be run against 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! 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. A: That's a web server configuration issue. If you are using Apache you may need to [enable the use of .htaccess](http://httpd.apache.org/docs/2.4/de/mod/core.html#allowoverride). Other servers require individual configuration.
@ -138,4 +141,4 @@ A: A search is only conducted against the currently used locale. You may have on
Said website with the red smiling rocket, for providing this beautiful website! Said website with the red smiling rocket, for providing this beautiful website!
Please do not regard this project as blatant rip-off, rather as "We do really liked your presentation, but since time and content progresses, you are sadly no longer supplying the data we need". Please do not regard this project as blatant rip-off, rather as "We do really liked your presentation, but since time and content progresses, you are sadly no longer supplying the data we need".
![uses badges](http://forthebadge.com/images/badges/uses-badges.svg) ![uses badges](https://forthebadge.com/badges/uses-badges.svg)

3
aowow
View file

@ -9,6 +9,9 @@ if (PHP_SAPI === 'cli' && getcwd().DIRECTORY_SEPARATOR.'aowow' != __FILE__)
require_once 'includes/kernel.php'; require_once 'includes/kernel.php';
require_once 'includes/setup/cli.class.php'; require_once 'includes/setup/cli.class.php';
require_once 'includes/setup/timer.class.php'; require_once 'includes/setup/timer.class.php';
require_once 'includes/setup/datatypes/primitives.php';
require_once 'includes/setup/files/binaryfile.class.php';
require_once 'includes/setup/files/dbcfile.class.php';
require_once 'setup/setup.php'; require_once 'setup/setup.php';

30
composer.json Normal file
View file

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

454
composer.lock generated Normal file
View file

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

View file

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

View file

@ -13,11 +13,11 @@ class AboutusBaseResponse extends TemplateResponse
protected ?int $activeTab = parent::TAB_MORE; protected ?int $activeTab = parent::TAB_MORE;
protected array $breadcrumb = [2, 0]; protected array $breadcrumb = [2, 0];
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
parent::__construct($pageParam); parent::__construct($rawParam);
if ($pageParam) if ($rawParam)
$this->generateError(); $this->generateError();
} }

View file

@ -42,19 +42,19 @@ class AccountBaseResponse extends TemplateResponse
public ?array $bans; public ?array $bans;
public function __construct($pageParam) public function __construct($rawParam)
{ {
if (!User::isLoggedIn()) if (!User::isLoggedIn())
$this->forwardToSignIn('account'); $this->forwardToSignIn('account');
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
{ {
array_unshift($this->title, Lang::account('settings')); array_unshift($this->title, Lang::account('settings'));
$user = DB::Aowow()->selectRow('SELECT `debug`, `email`, `description`, `avatar`, `wowicon`, `renameCooldown` FROM ?_account WHERE `id` = ?d', User::$id); $user = DB::Aowow()->selectRow('SELECT `debug`, `email`, `description`, `avatar`, `wowicon`, `renameCooldown` FROM ::account WHERE `id` = %i', User::$id);
Lang::sort('game', 'ra'); Lang::sort('game', 'ra');
@ -65,11 +65,11 @@ class AccountBaseResponse extends TemplateResponse
/* Ban Popup */ /* Ban Popup */
/*************/ /*************/
$b = DB::Aowow()->select( $b = DB::Aowow()->selectAssoc(
'SELECT ab.`end` AS "0", ab.`reason` AS "1", a.`username` AS "2" 'SELECT ab.`end` AS "0", ab.`reason` AS "1", a.`username` AS "2"
FROM ?_account_banned ab FROM ::account_banned ab
LEFT JOIN ?_account a ON a.`id` = ab.`staffId` LEFT JOIN ::account a ON a.`id` = ab.`staffId`
WHERE ab.`userId` = ?d AND ab.`typeMask` & ?d AND (ab.`end` = 0 OR ab.`end` > UNIX_TIMESTAMP())', WHERE ab.`userId` = %i AND ab.`typeMask` & %i AND (ab.`end` = 0 OR ab.`end` > UNIX_TIMESTAMP())',
User::$id, ACC_BAN_TEMP | ACC_BAN_PERM User::$id, ACC_BAN_TEMP | ACC_BAN_PERM
); );
@ -99,7 +99,7 @@ class AccountBaseResponse extends TemplateResponse
/* GENERAL */ /* GENERAL */
// Modelviewer // Modelviewer
if ($_ = DB::Aowow()->selectCell('SELECT `data` FROM ?_account_cookies WHERE `name` = ? AND `userId` = ?d', 'default_3dmodel', User::$id)) if ($_ = DB::Aowow()->selectCell('SELECT `data` FROM ::account_cookies WHERE `name` = %s AND `userId` = %i', 'default_3dmodel', User::$id))
[$this->modelrace, $this->modelgender] = explode(',', $_); [$this->modelrace, $this->modelgender] = explode(',', $_);
// Lists // Lists
@ -112,10 +112,10 @@ class AccountBaseResponse extends TemplateResponse
// Username // Username
$this->curName = User::$username; $this->curName = User::$username;
$this->renameCD = Util::formatTime(Cfg::get('ACC_RENAME_DECAY') * 1000); $this->renameCD = DateTime::formatTimeElapsedFloat(Cfg::get('ACC_RENAME_DECAY') * 1000);
if ($user['renameCooldown'] > time()) if ($user['renameCooldown'] > time())
{ {
$locCode = implode('_', str_split(Lang::getLocale()->json(), 2)); // ._. $locCode = substr_replace(Lang::getLocale()->json(), '_', 2, 0); // ._.
$this->activeCD = (new \IntlDateFormatter($locCode, pattern: Lang::main('dateFmtIntl')))->format($user['renameCooldown']); $this->activeCD = (new \IntlDateFormatter($locCode, pattern: Lang::main('dateFmtIntl')))->format($user['renameCooldown']);
} }
@ -131,24 +131,6 @@ class AccountBaseResponse extends TemplateResponse
$this->wowicon = $user['wowicon']; $this->wowicon = $user['wowicon'];
$this->avMode = $user['avatar']; $this->avMode = $user['avatar'];
// status [reviewing, ok, rejected]? (only 2: rejected processed in js)
if (User::isPremium() && ($cuAvatars = DB::Aowow()->select('SELECT `id`, `name`, `current`, `size`, `status`, `when` FROM ?_account_avatars WHERE `userId` = ?d', User::$id)))
{
array_walk($cuAvatars, function (&$x) {
$x['when'] *= 1000; // uploaded timestamp expected as msec for some reason
$x['caption'] = $x['name']; // only used for getVisibleText, duplicates name?
$x['type'] = 1; // always 1 ?, Dialog-popup doesn't work without it
});
foreach ($cuAvatars as $a)
if ($a['status'] != AvatarMgr::STATUS_REJECTED)
$this->customicons[$a['id']] = $a['name'];
// TODO - replace with array_find in PHP 8.4
if ($x = array_filter($cuAvatars, fn($x) => $x['current'] > 0 ))
$this->customicon = array_pop($x)['id'];
}
/* PREMIUM */ /* PREMIUM */
$this->premium = User::isPremium(); $this->premium = User::isPremium();
@ -156,8 +138,23 @@ class AccountBaseResponse extends TemplateResponse
if (!$this->premium) if (!$this->premium)
return; return;
// required by js to calc reputation border color in user selection
$this->reputation = User::getReputation(); $this->reputation = User::getReputation();
// status [reviewing, ok, rejected]? (only 2: rejected processed in js)
// * 'when': uploaded timestamp expected as msec for some reason
// * 'caption': only used for getVisibleText, duplicates name?
// * 'type': always 1 ?, Dialog-popup doesn't work without it
if ($cuAvatars = DB::Aowow()->selectAssoc('SELECT `id` AS ARRAY_KEY, `id`, `name`, `name` AS "caption", `current`, `size`, `status`, `when` * 1000 AS "when", 1 AS "type" FROM ::account_avatars WHERE `userId` = %i', User::$id))
{
foreach ($cuAvatars as $id => $a)
if ($a['status'] != AvatarMgr::STATUS_REJECTED)
$this->customicons[$id] = $a['name'];
if ($id = array_find_key($cuAvatars, fn($x) => $x['current'] > 0 ))
$this->customicon = $id;
}
// Avatar Manager // Avatar Manager
$this->avatarManager = new Listview([ $this->avatarManager = new Listview([
'template' => 'avatar', 'template' => 'avatar',

View file

@ -52,13 +52,13 @@ class AccountActivateResponse extends TemplateResponse
if (!$this->assertGET('key')) if (!$this->assertGET('key'))
return Lang::main('intError'); return Lang::main('intError');
if (DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE `status` IN (?a) AND `token` = ?', [ACC_STATUS_NONE, ACC_STATUS_NEW], $this->_get['key'])) if (DB::Aowow()->selectCell('SELECT `id` FROM ::account WHERE `status` IN %in AND `token` = %s', [ACC_STATUS_NONE, ACC_STATUS_NEW], $this->_get['key']))
{ {
// don't remove the token yet. It's needed on signin page. // don't remove the token yet. It's needed on signin page.
DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = 0, `userGroups` = ?d WHERE `token` = ?', ACC_STATUS_NONE, U_GROUP_NONE, $this->_get['key']); DB::Aowow()->qry('UPDATE ::account SET `status` = %i, `statusTimer` = 0, `userGroups` = %i WHERE `token` = %s', ACC_STATUS_NONE, U_GROUP_NONE, $this->_get['key']);
// fully apply block for further registration attempts from this ip // fully apply block for further registration attempts from this ip
DB::Aowow()->query('REPLACE INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, ?d + 1, UNIX_TIMESTAMP() + ?d)', DB::Aowow()->qry('REPLACE INTO ::account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (%s, %i, %i + 1, UNIX_TIMESTAMP() + %i)',
User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK')); User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
$this->success = true; $this->success = true;

View file

@ -35,12 +35,12 @@ class AccountConfirmdeleteResponse extends TemplateResponse
private bool $success = false; private bool $success = false;
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF) if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError(); $this->generateError();
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
@ -54,14 +54,14 @@ class AccountConfirmdeleteResponse extends TemplateResponse
$msg = Lang::account('inputbox', 'error', 'purgeTokenUsed'); $msg = Lang::account('inputbox', 'error', 'purgeTokenUsed');
// display default confirm template // display default confirm template
if ($this->assertGET('key') && DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP() AND `token` = ?', ACC_STATUS_PURGING, $this->_get['key'])) if ($this->assertGET('key') && DB::Aowow()->selectCell('SELECT 1 FROM ::account WHERE `status` = %i AND `statusTimer` > UNIX_TIMESTAMP() AND `token` = %s', ACC_STATUS_PURGING, $this->_get['key']))
{ {
$this->key = $this->_get['key']; $this->key = $this->_get['key'];
return; return;
} }
// perform action and display status // perform action and display status
if ($this->assertPOST('key') && ($userId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP() AND `token` = ?', ACC_STATUS_PURGING, $this->_post['key']))) if ($this->assertPOST('key') && ($userId = DB::Aowow()->selectCell('SELECT `id` FROM ::account WHERE `status` = %i AND `statusTimer` > UNIX_TIMESTAMP() AND `token` = %s', ACC_STATUS_PURGING, $this->_post['key'])))
{ {
if ($this->_post['cancel']) if ($this->_post['cancel'])
$msg = $this->cancel($userId); $msg = $this->cancel($userId);
@ -79,7 +79,7 @@ class AccountConfirmdeleteResponse extends TemplateResponse
private function cancel(int $userId) : string private function cancel(int $userId) : string
{ {
if (DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = 0, `token` = "" WHERE `id` = ?d', ACC_STATUS_NONE, $userId)) if (DB::Aowow()->qry('UPDATE ::account SET `status` = %i, `statusTimer` = 0, `token` = "" WHERE `id` = %i', ACC_STATUS_NONE, $userId))
{ {
$this->success = true; $this->success = true;
return Lang::account('inputbox', 'message', 'deleteCancel'); return Lang::account('inputbox', 'message', 'deleteCancel');
@ -91,32 +91,32 @@ class AccountConfirmdeleteResponse extends TemplateResponse
private function purge(int $userId) : string private function purge(int $userId) : string
{ {
// empty all user settings and cookies // empty all user settings and cookies
DB::Aowow()->query('DELETE FROM ?_account_cookies WHERE `userId` = ?d', $userId); DB::Aowow()->qry('DELETE FROM ::account_cookies WHERE `userId` = %i', $userId);
DB::Aowow()->query('DELETE FROM ?_account_avatars WHERE `userId` = ?d', $userId); DB::Aowow()->qry('DELETE FROM ::account_avatars WHERE `userId` = %i', $userId);
DB::Aowow()->query('DELETE FROM ?_account_excludes WHERE `userId` = ?d', $userId); DB::Aowow()->qry('DELETE FROM ::account_excludes WHERE `userId` = %i', $userId);
DB::Aowow()->query('DELETE FROM ?_account_favorites WHERE `userId` = ?d', $userId); DB::Aowow()->qry('DELETE FROM ::account_favorites WHERE `userId` = %i', $userId);
DB::Aowow()->query('DELETE FROM ?_account_reputation WHERE `userId` = ?d', $userId); DB::Aowow()->qry('DELETE FROM ::account_reputation WHERE `userId` = %i', $userId);
DB::Aowow()->query('DELETE FROM ?_account_weightscales WHERE `userId` = ?d', $userId); // cascades to aowow_account_weightscale_data DB::Aowow()->qry('DELETE FROM ::account_weightscales WHERE `userId` = %i', $userId); // cascades to aowow_account_weightscale_data
// delete profiles, unlink chars // delete profiles, unlink chars
DB::Aowow()->query('DELETE pp FROM ?_profiler_profiles pp JOIN ?_account_profiles ap ON ap.`profileId` = pp.`id` WHERE ap.`accountId` = ?d', $userId); DB::Aowow()->qry('DELETE pp FROM ::profiler_profiles pp JOIN ::account_profiles ap ON ap.`profileId` = pp.`id` WHERE ap.`accountId` = %i', $userId);
// DB::Aowow()->query('DELETE FROM ?_account_profiles WHERE `accountId` = ?d', $userId); // already deleted via FK? // DB::Aowow()->qry('DELETE FROM ::account_profiles WHERE `accountId` = %i', $userId); // already deleted via FK?
// delete all sessions and bans // delete all sessions and bans
DB::Aowow()->query('DELETE FROM ?_account_banned WHERE `userId` = ?d', $userId); DB::Aowow()->qry('DELETE FROM ::account_banned WHERE `userId` = %i', $userId);
DB::Aowow()->query('DELETE FROM ?_account_sessions WHERE `userId` = ?d', $userId); DB::Aowow()->qry('DELETE FROM ::account_sessions WHERE `userId` = %i', $userId);
// delete forum posts (msg: This post was from a user who has deleted their account. (no translations at src); comments/replies are unaffected) // delete forum posts (msg: This post was from a user who has deleted their account. (no translations at src); comments/replies are unaffected)
// ... // ...
// replace username with userId and empty fields // replace username with userId and empty fields
DB::Aowow()->query( DB::Aowow()->qry(
'UPDATE ?_account SET 'UPDATE ::account SET
`login` = "", `passHash` = "", `username` = `id`, `email` = NULL, `userGroups` = 0, `userPerms` = 0, `login` = "", `passHash` = "", `username` = `id`, `email` = NULL, `userGroups` = 0, `userPerms` = 0,
`curIp` = "", `prevIp` = "", `curLogin` = 0, `prevLogin` = 0, `curIp` = "", `prevIp` = "", `curLogin` = 0, `prevLogin` = 0,
`locale` = 0, `debug` = 0, `avatar` = 0, `wowicon` = "", `title` = "", `description` = "", `excludeGroups` = 0, `locale` = 0, `debug` = 0, `avatar` = 0, `wowicon` = "", `title` = "", `description` = "", `excludeGroups` = 0,
`status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "", `renameCooldown` = 0 `status` = %i, `statusTimer` = 0, `token` = "", `updateValue` = "", `renameCooldown` = 0
WHERE `id` = ?d', WHERE `id` = %i',
ACC_STATUS_DELETED, $userId ACC_STATUS_DELETED, $userId
); );

View file

@ -46,12 +46,12 @@ class AccountConfirmemailaddressResponse extends TemplateResponse
if (!$this->assertGET('key')) if (!$this->assertGET('key'))
return Lang::main('intError'); return Lang::main('intError');
$acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ?_account WHERE `token` = ?', $this->_get['key']); $acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ::account WHERE `token` = %s', $this->_get['key']);
if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_EMAIL || $acc['statusTimer'] < time()) if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_EMAIL || $acc['statusTimer'] < time())
return Lang::account('inputbox', 'error', 'mailTokenUsed'); return Lang::account('inputbox', 'error', 'mailTokenUsed');
// 0 changes == error // 0 changes == error
if (!DB::Aowow()->query('UPDATE ?_account SET `email` = `updateValue`, `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = ?', ACC_STATUS_NONE, $this->_get['key'])) if (!DB::Aowow()->qry('UPDATE ::account SET `email` = `updateValue`, `status` = %i, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = %s', ACC_STATUS_NONE, $this->_get['key']))
return Lang::main('intError'); return Lang::main('intError');
$this->success = true; $this->success = true;

View file

@ -44,12 +44,12 @@ class AccountConfirmpasswordResponse extends TemplateResponse
if (!$this->assertGET('key')) if (!$this->assertGET('key'))
return Lang::main('intError'); return Lang::main('intError');
$acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ?_account WHERE `token` = ?', $this->_get['key']); $acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ::account WHERE `token` = %s', $this->_get['key']);
if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_PASS || $acc['statusTimer'] < time()) if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_PASS || $acc['statusTimer'] < time())
return Lang::account('inputbox', 'error', 'passTokenUsed'); return Lang::account('inputbox', 'error', 'passTokenUsed');
// 0 changes == error // 0 changes == error
if (!DB::Aowow()->query('UPDATE ?_account SET `passHash` = `updateValue`, `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = ?', ACC_STATUS_NONE, $this->_get['key'])) if (!DB::Aowow()->qry('UPDATE ::account SET `passHash` = `updateValue`, `status` = %i, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = %s', ACC_STATUS_NONE, $this->_get['key']))
return Lang::main('intError'); return Lang::main('intError');
$this->success = true; $this->success = true;

View file

@ -28,15 +28,15 @@ class AccountDeleteiconResponse extends TextResponse
return; return;
// non-int > error // non-int > error
$selected = DB::Aowow()->selectCell('SELECT `current` FROM ?_account_avatars WHERE `id` = ?d AND `userId` = ?d', $this->_post['id'], User::$id); $selected = DB::Aowow()->selectCell('SELECT `current` FROM ::account_avatars WHERE `id` = %i AND `userId` = %i', $this->_post['id'], User::$id);
if ($selected === null || $selected === false) if ($selected === null || $selected === false)
return; return;
DB::Aowow()->query('DELETE FROM ?_account_avatars WHERE `id` = ?d AND `userId` = ?d', $this->_post['id'], User::$id); DB::Aowow()->qry('DELETE FROM ::account_avatars WHERE `id` = %i AND `userId` = %i', $this->_post['id'], User::$id);
// if deleted avatar is also currently selected, unset // if deleted avatar is also currently selected, unset
if ($selected) if ($selected)
DB::Aowow()->query('UPDATE ?_account SET `avatar` = 0 WHERE `id` = ?d', User::$id); DB::Aowow()->qry('UPDATE ::account SET `avatar` = 0 WHERE `id` = %i', User::$id);
$path = sprintf('static/uploads/avatars/%d.jpg', $this->_post['id']); $path = sprintf('static/uploads/avatars/%d.jpg', $this->_post['id']);
if (!unlink($path)) if (!unlink($path))

View file

@ -28,12 +28,12 @@ class AccountDeleteResponse extends TemplateResponse
public string $deleteFormTarget = '?account=delete'; public string $deleteFormTarget = '?account=delete';
public ?array $inputbox = null; public ?array $inputbox = null;
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF) if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError(); $this->generateError();
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
@ -47,11 +47,11 @@ class AccountDeleteResponse extends TemplateResponse
if ($this->_post['proceed']) if ($this->_post['proceed'])
{ {
$error = false; $error = false;
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `status` NOT IN (?a) AND `statusTimer` > UNIX_TIMESTAMP() AND `id` = ?d', [ACC_STATUS_NEW, ACC_STATUS_NONE, ACC_STATUS_PURGING], User::$id)) if (!DB::Aowow()->selectCell('SELECT 1 FROM ::account WHERE `status` NOT IN %in AND `statusTimer` > UNIX_TIMESTAMP() AND `id` = %i', [ACC_STATUS_NEW, ACC_STATUS_NONE, ACC_STATUS_PURGING], User::$id))
{ {
$token = Util::createHash(40); $token = Util::createHash(40);
DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = UNIX_TIMESTAMP() + ?d, `token` = ? WHERE `id` = ?d', DB::Aowow()->qry('UPDATE ::account SET `status` = %i, `statusTimer` = UNIX_TIMESTAMP() + %i, `token` = %s WHERE `id` = %i',
ACC_STATUS_PURGING, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id); ACC_STATUS_PURGING, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id);
Util::sendMail(User::$email, 'delete-account', [$token, User::$email, User::$username]); Util::sendMail(User::$email, 'delete-account', [$token, User::$email, User::$username]);

View file

@ -34,7 +34,7 @@ class AccountExcludeResponse extends TextResponse
else if ($this->_post['reset'] == 1) // defaults to unavailable else if ($this->_post['reset'] == 1) // defaults to unavailable
$this->resetExcludes(); $this->resetExcludes();
else if ($this->_post['groups']) // exclude by group mask else if ($this->_post['groups'] !== null) // exclude by group mask
$this->updateGroups(); $this->updateGroups();
} }
@ -49,12 +49,17 @@ class AccountExcludeResponse extends TextResponse
// we don't get signaled whether an id should be added to or removed from either includes or excludes // 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 // so we throw everything into one table and toggle the mode if its already in here
$includes = DB::Aowow()->selectCol('SELECT `typeId` FROM ?_profiler_excludes WHERE `type` = ?d AND `typeId` IN (?a)', $this->_post['type'], $validIds); $includes = DB::Aowow()->selectCol('SELECT `typeId` FROM ::profiler_excludes WHERE `type` = %i AND `typeId` IN %in', $this->_post['type'], $validIds);
$insert = [];
foreach ($validIds as $typeId) foreach ($validIds as $typeId)
DB::Aowow()->query('INSERT INTO ?_account_excludes (`userId`, `type`, `typeId`, `mode`) VALUES (?a) ON DUPLICATE KEY UPDATE `mode` = (`mode` ^ 0x3)', {
[User::$id, $this->_post['type'], $typeId, in_array($typeId, $includes) ? 2 : 1] $insert['userId'][] = User::$id;
); $insert['type'][] = $this->_post['type'];
$insert['typeId'][] = $typeId;
$insert['mode'][] = in_array($typeId, $includes) ? Profiler::COMPLETION_INCLUDE : Profiler::COMPLETION_EXCLUDE;
};
DB::Aowow()->qry('INSERT INTO ::account_excludes %m ON DUPLICATE KEY UPDATE `mode` = (`mode` ^ 0x3)', $insert);
} }
else else
trigger_error('AccountExcludeResponse::excludeById - validation failed [type: '.$this->_post['type'].', typeId: '.implode(',', $this->_post['id']).']', E_USER_NOTICE); trigger_error('AccountExcludeResponse::excludeById - validation failed [type: '.$this->_post['type'].', typeId: '.implode(',', $this->_post['id']).']', E_USER_NOTICE);
@ -62,14 +67,14 @@ class AccountExcludeResponse extends TextResponse
private function resetExcludes() : void private function resetExcludes() : void
{ {
DB::Aowow()->query('DELETE FROM ?_account_excludes WHERE `userId` = ?d', User::$id); DB::Aowow()->qry('DELETE FROM ::account_excludes WHERE `userId` = %i', User::$id);
DB::Aowow()->query('UPDATE ?_account SET `excludeGroups` = ?d WHERE `id` = ?d', PR_EXCLUDE_GROUP_UNAVAILABLE, User::$id); DB::Aowow()->qry('UPDATE ::account SET `excludeGroups` = %i WHERE `id` = %i', PR_EXCLUDE_GROUP_UNAVAILABLE, User::$id);
} }
private function updateGroups() : void private function updateGroups() : void
{ {
if ($this->assertPOST('groups')) // clamp to real groups if ($this->assertPOST('groups')) // clamp to real groups
DB::Aowow()->query('UPDATE ?_account SET `excludeGroups` = ?d WHERE `id` = ?d', $this->_post['groups'] & PR_EXCLUDE_GROUP_ANY, User::$id); DB::Aowow()->qry('UPDATE ::account SET `excludeGroups` = %i WHERE `id` = %i', $this->_post['groups'] & PR_EXCLUDE_GROUP_ANY, User::$id);
} }
} }

View file

@ -37,13 +37,13 @@ class AccountFavoritesResponse extends TextResponse
private function removeFavorite() : void private function removeFavorite() : void
{ {
if ($this->assertPOST('id', 'remove')) if ($this->assertPOST('id', 'remove'))
DB::Aowow()->query('DELETE FROM ?_account_favorites WHERE `userId` = ?d AND `type` = ?d AND `typeId` = ?d', User::$id, $this->_post['remove'], $this->_post['id']); DB::Aowow()->qry('DELETE FROM ::account_favorites WHERE `userId` = %i AND `type` = %i AND `typeId` = %i', User::$id, $this->_post['remove'], $this->_post['id']);
} }
private function addFavorite() : void private function addFavorite() : void
{ {
if ($this->assertPOST('id', 'add') && Type::validateIds($this->_post['add'], $this->_post['id'])) if ($this->assertPOST('id', 'add') && Type::validateIds($this->_post['add'], $this->_post['id']))
DB::Aowow()->query('INSERT INTO ?_account_favorites (`userId`, `type`, `typeId`) VALUES (?d, ?d, ?d)', User::$id, $this->_post['add'], $this->_post['id']); DB::Aowow()->qry('INSERT INTO ::account_favorites (`userId`, `type`, `typeId`) VALUES (%i, %i, %i)', User::$id, $this->_post['add'], $this->_post['id']);
else else
trigger_error('AccountFavoritesResponse::addFavorite() - failed to add [userId: '.User::$id.', type: '.$this->_post['add'].', typeId: '.$this->_post['id'], E_USER_NOTICE); trigger_error('AccountFavoritesResponse::addFavorite() - failed to add [userId: '.User::$id.', type: '.$this->_post['add'].', typeId: '.$this->_post['id'], E_USER_NOTICE);
} }

View file

@ -29,7 +29,7 @@ class AccountforgotpasswordResponse extends TemplateResponse
private bool $success = false; private bool $success = false;
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
// don't redirect logged in users // don't redirect logged in users
// you can be forgetful AND logged in // you can be forgetful AND logged in
@ -40,7 +40,7 @@ class AccountforgotpasswordResponse extends TemplateResponse
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF) if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError(); $this->generateError();
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
@ -72,14 +72,14 @@ class AccountforgotpasswordResponse extends TemplateResponse
if (!$this->_post['email']) if (!$this->_post['email'])
return Lang::account('emailInvalid'); return Lang::account('emailInvalid');
$timeout = DB::Aowow()->selectCell('SELECT `unbanDate` FROM ?_account_bannedips WHERE `ip` = ? AND `type` = ?d AND `count` > ?d AND `unbanDate` > UNIX_TIMESTAMP()', User::$ip, IP_BAN_TYPE_PASSWORD_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT')); $timeout = DB::Aowow()->selectCell('SELECT `unbanDate` FROM ::account_bannedips WHERE `ip` = %s AND `type` = %i AND `count` > %i AND `unbanDate` > UNIX_TIMESTAMP()', User::$ip, IP_BAN_TYPE_PASSWORD_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT'));
// on cooldown pretend we dont know the email address // on cooldown pretend we dont know the email address
if ($timeout && $timeout > time()) if ($timeout && $timeout > time())
return Cfg::get('DEBUG') ? 'resend on cooldown: '.Util::formatTimeDiff($timeout).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound'); return Cfg::get('DEBUG') ? 'resend on cooldown: '.DateTime::formatTimeElapsed($timeout * 1000).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound');
// pretend recovery started // pretend recovery started
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `email` = ?', $this->_post['email'])) if (!DB::Aowow()->selectCell('SELECT 1 FROM ::account WHERE `email` = %s', $this->_post['email']))
{ {
// do not confirm or deny existence of email // do not confirm or deny existence of email
$this->success = !Cfg::get('DEBUG'); $this->success = !Cfg::get('DEBUG');
@ -90,7 +90,7 @@ class AccountforgotpasswordResponse extends TemplateResponse
if ($err = $this->startRecovery(ACC_STATUS_RECOVER_PASS, 'reset-password', $this->_post['email'])) if ($err = $this->startRecovery(ACC_STATUS_RECOVER_PASS, 'reset-password', $this->_post['email']))
return $err; return $err;
DB::Aowow()->query('INSERT INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, ?d, UNIX_TIMESTAMP() + ?d) ON DUPLICATE KEY UPDATE `count` = `count` + ?d, `unbanDate` = UNIX_TIMESTAMP() + ?d', DB::Aowow()->qry('INSERT INTO ::account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (%s, %i, %i, UNIX_TIMESTAMP() + %i) ON DUPLICATE KEY UPDATE `count` = `count` + %i, `unbanDate` = UNIX_TIMESTAMP() + %i',
User::$ip, IP_BAN_TYPE_PASSWORD_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT') + 1, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK')); User::$ip, IP_BAN_TYPE_PASSWORD_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT') + 1, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
$this->success = true; $this->success = true;

View file

@ -28,7 +28,7 @@ class AccountforgotusernameResponse extends TemplateResponse
private bool $success = false; private bool $success = false;
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
// if the user is looged in goto account dashboard // if the user is looged in goto account dashboard
if (User::isLoggedIn()) if (User::isLoggedIn())
@ -40,7 +40,7 @@ class AccountforgotusernameResponse extends TemplateResponse
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF) if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError(); $this->generateError();
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
@ -71,14 +71,14 @@ class AccountforgotusernameResponse extends TemplateResponse
if (!$this->_post['email']) if (!$this->_post['email'])
return Lang::account('emailInvalid'); return Lang::account('emailInvalid');
$timeout = DB::Aowow()->selectCell('SELECT `unbanDate` FROM ?_account_bannedips WHERE `ip` = ? AND `type` = ?d AND `count` > ?d AND `unbanDate` > UNIX_TIMESTAMP()', User::$ip, IP_BAN_TYPE_USERNAME_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT')); $timeout = DB::Aowow()->selectCell('SELECT `unbanDate` FROM ::account_bannedips WHERE `ip` = %s AND `type` = %i AND `count` > %i AND `unbanDate` > UNIX_TIMESTAMP()', User::$ip, IP_BAN_TYPE_USERNAME_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT'));
// on cooldown pretend we dont know the email address // on cooldown pretend we dont know the email address
if ($timeout && $timeout > time()) if ($timeout && $timeout > time())
return Cfg::get('DEBUG') ? 'resend on cooldown: '.Util::formatTimeDiff($timeout).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound'); return Cfg::get('DEBUG') ? 'resend on cooldown: '.DateTime::formatTimeElapsed($timeout * 1000).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound');
// pretend recovery started // pretend recovery started
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `email` = ?', $this->_post['email'])) if (!DB::Aowow()->selectCell('SELECT 1 FROM ::account WHERE `email` = %s', $this->_post['email']))
{ {
// do not confirm or deny existence of email // do not confirm or deny existence of email
$this->success = !Cfg::get('DEBUG'); $this->success = !Cfg::get('DEBUG');
@ -89,7 +89,7 @@ class AccountforgotusernameResponse extends TemplateResponse
if ($err = $this->startRecovery(ACC_STATUS_RECOVER_USER, 'recover-user', $this->_post['email'])) if ($err = $this->startRecovery(ACC_STATUS_RECOVER_USER, 'recover-user', $this->_post['email']))
return $err; return $err;
DB::Aowow()->query('INSERT INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, ?d, UNIX_TIMESTAMP() + ?d) ON DUPLICATE KEY UPDATE `count` = `count` + ?d, `unbanDate` = UNIX_TIMESTAMP() + ?d', DB::Aowow()->qry('INSERT INTO ::account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (%s, %i, %i, UNIX_TIMESTAMP() + %i) ON DUPLICATE KEY UPDATE `count` = `count` + %i, `unbanDate` = UNIX_TIMESTAMP() + %i',
User::$ip, IP_BAN_TYPE_USERNAME_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT') + 1, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK')); User::$ip, IP_BAN_TYPE_USERNAME_RECOVERY, Cfg::get('ACC_FAILED_AUTH_COUNT') + 1, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
$this->success = true; $this->success = true;

View file

@ -48,7 +48,7 @@ class AccountForumavatarResponse extends TextResponse
private function unset() : string private function unset() : string
{ {
$x = DB::Aowow()->query('UPDATE ?_account SET `avatar` = 0 WHERE `id` = ?d', User::$id); $x = DB::Aowow()->qry('UPDATE ::account SET `avatar` = 0 WHERE `id` = %i', User::$id);
if ($x === null || $x === false) if ($x === null || $x === false)
return Lang::main('genericError'); return Lang::main('genericError');
@ -64,17 +64,17 @@ class AccountForumavatarResponse extends TextResponse
$icon = strtolower(trim($this->_post['wowicon'])); $icon = strtolower(trim($this->_post['wowicon']));
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_icons WHERE `name` = ?', $icon)) if (!DB::Aowow()->selectCell('SELECT 1 FROM ::icons WHERE `name` = %s', $icon))
return Lang::account('updateMessage', 'avNotFound'); return Lang::account('updateMessage', 'avNotFound');
$x = DB::Aowow()->query('UPDATE ?_account SET `avatar` = 1, `wowicon` = ? WHERE `id` = ?d', strtolower($icon), User::$id); $x = DB::Aowow()->qry('UPDATE ::account SET `avatar` = 1, `wowicon` = %s WHERE `id` = %i', $icon, User::$id);
if ($x === null || $x === false) if (is_null($x))
return Lang::main('genericError'); return Lang::main('genericError');
$this->success = true; $this->success = true;
$msg = Lang::account('updateMessage', $x === 0 ? 'avNoChange' : 'avSuccess'); $msg = Lang::account('updateMessage', $x === 0 ? 'avNoChange' : 'avSuccess');
if (($qty = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_account WHERE `wowicon` = ?', $icon)) > 1) if (($qty = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ::account WHERE `wowicon` = %s', $icon)) > 1)
$msg .= ' '.Lang::account('updateMessage', 'avNthUser', [$qty]); $msg .= ' '.Lang::account('updateMessage', 'avNthUser', [$qty]);
else else
$msg .= ' '.Lang::account('updateMessage', 'av1stUser'); $msg .= ' '.Lang::account('updateMessage', 'av1stUser');
@ -92,11 +92,11 @@ class AccountForumavatarResponse extends TextResponse
$customIcon = $this->_post['customicon'] ?? $this->_get['customicon']; $customIcon = $this->_post['customicon'] ?? $this->_get['customicon'];
$x = DB::Aowow()->query('UPDATE ?_account_avatars SET `current` = IF(`id` = ?d, 1, 0) WHERE `userId` = ?d AND `status` <> ?d', $customIcon, User::$id, AvatarMgr::STATUS_REJECTED); $x = DB::Aowow()->qry('UPDATE ::account_avatars SET `current` = IF(`id` = %i, 1, 0) WHERE `userId` = %i AND `status` <> %i', $customIcon, User::$id, AvatarMgr::STATUS_REJECTED);
if (!is_int($x)) if (!is_int($x))
return Lang::main('genericError'); return Lang::main('genericError');
if (!is_int(DB::Aowow()->query('UPDATE ?_account SET `avatar` = 2 WHERE `id` = ?d', User::$id))) if (!is_int(DB::Aowow()->qry('UPDATE ::account SET `avatar` = 2 WHERE `id` = %i', User::$id)))
return Lang::main('intError'); return Lang::main('intError');
$this->success = true; $this->success = true;

View file

@ -28,8 +28,8 @@ class AccountPremiumborderResponse extends TextResponse
if (!$this->assertPOST('avatarborder')) if (!$this->assertPOST('avatarborder'))
return; return;
$x = DB::Aowow()->query('UPDATE ?_account SET `avatarborder` = ?d WHERE `id` = ?d', $this->_post['avatarborder'], User::$id); $x = DB::Aowow()->qry('UPDATE ::account SET `avatarborder` = %i WHERE `id` = %i', $this->_post['avatarborder'], User::$id);
if (!is_int($x)) if (is_null($x))
$_SESSION['msg'] = ['premiumborder', false, Lang::main('genericError')]; $_SESSION['msg'] = ['premiumborder', false, Lang::main('genericError')];
else if (!$x) else if (!$x)
$_SESSION['msg'] = ['premiumborder', true, Lang::account('updateMessage', 'avNoChange')]; $_SESSION['msg'] = ['premiumborder', true, Lang::account('updateMessage', 'avNoChange')];

View file

@ -29,7 +29,7 @@ class AccountRenameiconResponse extends TextResponse
return; return;
// regexp same as in account.js // regexp same as in account.js
DB::Aowow()->query('UPDATE ?_account_avatars SET `name` = ? WHERE `id` = ?d AND `userId` = ?d', trim($this->_post['name']), $this->_post['id'], User::$id); DB::Aowow()->qry('UPDATE ::account_avatars SET `name` = %s WHERE `id` = %i AND `userId` = %i', trim($this->_post['name']), $this->_post['id'], User::$id);
} }
} }

View file

@ -20,12 +20,12 @@ class AccountResendsubmitResponse extends TemplateResponse
'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW] 'email' => ['filter' => FILTER_VALIDATE_EMAIL, 'flags' => FILTER_FLAG_STRIP_AOWOW]
); );
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
if (!Cfg::get('ACC_ALLOW_REGISTER') || Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF) if (!Cfg::get('ACC_ALLOW_REGISTER') || Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError(); $this->generateError();
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void

View file

@ -22,7 +22,7 @@ class AccountResendResponse extends TemplateResponse
private bool $success = false; private bool $success = false;
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
if (Cfg::get('ACC_EXT_RECOVER_URL')) if (Cfg::get('ACC_EXT_RECOVER_URL'))
$this->forward(Cfg::get('ACC_EXT_RECOVER_URL')); $this->forward(Cfg::get('ACC_EXT_RECOVER_URL'));
@ -30,7 +30,7 @@ class AccountResendResponse extends TemplateResponse
if (!Cfg::get('ACC_ALLOW_REGISTER') || Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF) if (!Cfg::get('ACC_ALLOW_REGISTER') || Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
$this->generateError(); $this->generateError();
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
@ -69,19 +69,19 @@ class AccountResendResponse extends TemplateResponse
if (!$this->_post['email']) if (!$this->_post['email'])
return Lang::account('emailInvalid'); return Lang::account('emailInvalid');
$timeout = DB::Aowow()->selectCell('SELECT `unbanDate` FROM ?_account_bannedips WHERE `ip` = ? AND `type` = ?d AND `count` > ?d AND `unbanDate` > UNIX_TIMESTAMP()', User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_COUNT')); $timeout = DB::Aowow()->selectCell('SELECT `unbanDate` FROM ::account_bannedips WHERE `ip` = %s AND `type` = %i AND `count` > %i AND `unbanDate` > UNIX_TIMESTAMP()', User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_COUNT'));
// on cooldown pretend we dont know the email address // on cooldown pretend we dont know the email address
if ($timeout && $timeout > time()) if ($timeout && $timeout > time())
return Cfg::get('DEBUG') ? 'resend on cooldown: '.Util::formatTimeDiff($timeout).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound'); return Cfg::get('DEBUG') ? 'resend on cooldown: '.DateTime::formatTimeElapsed($timeout * 1000).' remaining' : Lang::account('inputbox', 'error', 'emailNotFound');
// check email and account status // check email and account status
if ($token = DB::Aowow()->selectCell('SELECT `token` FROM ?_account WHERE `email` = ? AND `status` = ?d', $this->_post['email'], ACC_STATUS_NEW)) if ($token = DB::Aowow()->selectCell('SELECT `token` FROM ::account WHERE `email` = %s AND `status` = %i', $this->_post['email'], ACC_STATUS_NEW))
{ {
if (!Util::sendMail($this->_post['email'], 'activate-account', [$token])) if (!Util::sendMail($this->_post['email'], 'activate-account', [$token]))
return Lang::main('intError'); return Lang::main('intError');
DB::Aowow()->query('INSERT INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, ?d, UNIX_TIMESTAMP() + ?d) ON DUPLICATE KEY UPDATE `count` = `count` + ?d, `unbanDate` = UNIX_TIMESTAMP() + ?d', DB::Aowow()->qry('INSERT INTO ::account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (%s, %i, %i, UNIX_TIMESTAMP() + %i) ON DUPLICATE KEY UPDATE `count` = `count` + %i, `unbanDate` = UNIX_TIMESTAMP() + %i',
User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_COUNT') + 1, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK')); User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_COUNT') + 1, Cfg::get('ACC_FAILED_AUTH_COUNT'), Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
$this->success = true; $this->success = true;

View file

@ -25,7 +25,7 @@ class AccountresetpasswordResponse extends TemplateResponse
protected array $expectedGET = array( protected array $expectedGET = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']], 'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']],
'next' => ['filter' => FILTER_SANITIZE_URL, 'flags' => FILTER_FLAG_STRIP_AOWOW ] 'next' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/' ]]
); );
protected array $expectedPOST = array( protected array $expectedPOST = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']], 'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']],
@ -59,7 +59,7 @@ class AccountresetpasswordResponse extends TemplateResponse
$errMsg = ''; $errMsg = '';
if (!$this->assertGET('key') && !$this->assertPOST('key')) if (!$this->assertGET('key') && !$this->assertPOST('key'))
$errMsg = Lang::account('inputbox', 'error', 'passTokenLost'); $errMsg = Lang::account('inputbox', 'error', 'passTokenLost');
else if ($this->_get['key'] && !DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `token` = ? AND `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP()', $this->_get['key'], ACC_STATUS_RECOVER_PASS)) else if ($this->_get['key'] && !DB::Aowow()->selectCell('SELECT 1 FROM ::account WHERE `token` = %s AND `status` = %i AND `statusTimer` > UNIX_TIMESTAMP()', $this->_get['key'], ACC_STATUS_RECOVER_PASS))
$errMsg = Lang::account('inputbox', 'error', 'passTokenUsed'); $errMsg = Lang::account('inputbox', 'error', 'passTokenUsed');
if ($errMsg) if ($errMsg)
@ -99,7 +99,7 @@ class AccountresetpasswordResponse extends TemplateResponse
if ($this->_post['password'] != $this->_post['c_password']) if ($this->_post['password'] != $this->_post['c_password'])
return Lang::account('passCheckFail'); return Lang::account('passCheckFail');
$userData = DB::Aowow()->selectRow('SELECT `id`, `passHash` FROM ?_account WHERE `token` = ? AND `email` = ? AND `status` = ?d AND `statusTimer` > UNIX_TIMESTAMP()', $userData = DB::Aowow()->selectRow('SELECT `id`, `passHash` FROM ::account WHERE `token` = %s AND `email` = %s AND `status` = %i AND `statusTimer` > UNIX_TIMESTAMP()',
$this->_post['key'], $this->_post['key'],
$this->_post['email'], $this->_post['email'],
ACC_STATUS_RECOVER_PASS ACC_STATUS_RECOVER_PASS
@ -110,7 +110,7 @@ class AccountresetpasswordResponse extends TemplateResponse
if (!User::verifyCrypt($this->_post['c_password'], $userData['passHash'])) if (!User::verifyCrypt($this->_post['c_password'], $userData['passHash']))
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_NONE, $userData['id'])) if (!DB::Aowow()->qry('UPDATE ::account SET `passHash` = %s, `status` = %i WHERE `id` = %i', User::hashCrypt($this->_post['c_password']), ACC_STATUS_NONE, $userData['id']))
return Lang::main('intError'); return Lang::main('intError');
$this->success = true; $this->success = true;

View file

@ -46,12 +46,12 @@ class AccountRevertemailaddressResponse extends TemplateResponse
if (!$this->assertGET('key')) if (!$this->assertGET('key'))
return Lang::main('intError'); return Lang::main('intError');
$acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ?_account WHERE `token` = ?', $this->_get['key']); $acc = DB::Aowow()->selectRow('SELECT `updateValue`, `status`, `statusTimer` FROM ::account WHERE `token` = %s', $this->_get['key']);
if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_EMAIL || $acc['statusTimer'] < time()) if (!$acc || $acc['status'] != ACC_STATUS_CHANGE_EMAIL || $acc['statusTimer'] < time())
return Lang::account('inputbox', 'error', 'mailTokenUsed'); return Lang::account('inputbox', 'error', 'mailTokenUsed');
// 0 changes == error // 0 changes == error
if (!DB::Aowow()->query('UPDATE ?_account SET `status` = ?d, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = ?', ACC_STATUS_NONE, $this->_get['key'])) if (!DB::Aowow()->qry('UPDATE ::account SET `status` = %i, `statusTimer` = 0, `token` = "", `updateValue` = "" WHERE `token` = %s', ACC_STATUS_NONE, $this->_get['key']))
return Lang::main('intError'); return Lang::main('intError');
$this->success = true; $this->success = true;

View file

@ -28,7 +28,7 @@ class AccountSigninResponse extends TemplateResponse
); );
protected array $expectedGET = array( protected array $expectedGET = array(
'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']], 'key' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[a-zA-Z0-9]{40}$/']],
'next' => ['filter' => FILTER_SANITIZE_URL, 'flags' => FILTER_FLAG_STRIP_AOWOW ] 'next' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/'] ]
); );
private bool $success = false; private bool $success = false;
@ -53,7 +53,7 @@ class AccountSigninResponse extends TemplateResponse
// coming from user recovery or creation, prefill username // coming from user recovery or creation, prefill username
if ($this->_get['key']) if ($this->_get['key'])
{ {
if ($userData = DB::Aowow()->selectRow('SELECT a.`login` AS "0", IF(s.`expires`, 0, 1) AS "1" FROM ?_account a LEFT JOIN ?_account_sessions s ON a.`id` = s.`userId` AND a.`token` = s.`sessionId` WHERE a.`status` IN (?a) AND a.`token` = ?', if ($userData = DB::Aowow()->selectRow('SELECT a.`login` AS "0", IF(s.`expires`, 0, 1) AS "1" FROM ::account a LEFT JOIN ::account_sessions s ON a.`id` = s.`userId` AND a.`token` = s.`sessionId` WHERE a.`status` IN %in AND a.`token` = %s',
[ACC_STATUS_RECOVER_USER, ACC_STATUS_NONE], $this->_get['key'])) [ACC_STATUS_RECOVER_USER, ACC_STATUS_NONE], $this->_get['key']))
[$username, $rememberMe] = $userData; [$username, $rememberMe] = $userData;
} }
@ -99,7 +99,7 @@ class AccountSigninResponse extends TemplateResponse
// AUTH_BANNED => Lang::account('accBanned'); // ToDo: should this return an error? the actual account functionality should be blocked elsewhere // AUTH_BANNED => Lang::account('accBanned'); // ToDo: should this return an error? the actual account functionality should be blocked elsewhere
AUTH_WRONGUSER => Lang::account('userNotFound'), AUTH_WRONGUSER => Lang::account('userNotFound'),
AUTH_WRONGPASS => Lang::account('wrongPass'), AUTH_WRONGPASS => Lang::account('wrongPass'),
AUTH_IPBANNED => Lang::account('inputbox', 'error', 'loginExceeded', [Util::formatTime(Cfg::get('ACC_FAILED_AUTH_BLOCK') * 1000)]), AUTH_IPBANNED => Lang::account('inputbox', 'error', 'loginExceeded', [DateTime::formatTimeElapsedFloat(Cfg::get('ACC_FAILED_AUTH_BLOCK') * 1000)]),
AUTH_INTERNAL_ERR => Lang::main('intError'), AUTH_INTERNAL_ERR => Lang::main('intError'),
default => Lang::main('intError') default => Lang::main('intError')
}; };
@ -116,7 +116,7 @@ class AccountSigninResponse extends TemplateResponse
} }
// reset account status, update expiration // reset account status, update expiration
$ok = DB::Aowow()->query('UPDATE ?_account SET `prevIP` = IF(`curIp` = ?, `prevIP`, `curIP`), `curIP` = IF(`curIp` = ?, `curIP`, ?), `status` = IF(`status` = ?d, `status`, 0), `statusTimer` = IF(`status` = ?d, `statusTimer`, 0), `token` = IF(`status` = ?d, `token`, "") WHERE `id` = ?d', $ok = DB::Aowow()->qry('UPDATE ::account SET `prevIP` = IF(`curIp` = %s, `prevIP`, `curIP`), `curIP` = IF(`curIp` = %s, `curIP`, %s), `status` = IF(`status` = %i, `status`, 0), `statusTimer` = IF(`status` = %i, `statusTimer`, 0), `token` = IF(`status` = %i, `token`, "") WHERE `id` = %i',
User::$ip, User::$ip, User::$ip, User::$ip, User::$ip, User::$ip,
ACC_STATUS_NEW, ACC_STATUS_NEW, ACC_STATUS_NEW, ACC_STATUS_NEW, ACC_STATUS_NEW, ACC_STATUS_NEW,
User::$id // available after successful User:authenticate User::$id // available after successful User:authenticate
@ -130,12 +130,12 @@ class AccountSigninResponse extends TemplateResponse
// DELETE temp session // DELETE temp session
if ($this->_get['key']) if ($this->_get['key'])
DB::Aowow()->query('DELETE FROM ?_account_sessions WHERE `sessionId` = ?', $this->_get['key']); DB::Aowow()->qry('DELETE FROM ::account_sessions WHERE `sessionId` = %s', $this->_get['key']);
session_regenerate_id(true); // user status changed => regenerate id session_regenerate_id(true); // user status changed => regenerate id
// create new session entry // create new session entry
DB::Aowow()->query('INSERT INTO ?_account_sessions (`userId`, `sessionId`, `created`, `expires`, `touched`, `deviceInfo`, `ip`, `status`) VALUES (?d, ?, ?d, ?d, ?d, ?, ?, ?d)', DB::Aowow()->qry('INSERT INTO ::account_sessions (`userId`, `sessionId`, `created`, `expires`, `touched`, `deviceInfo`, `ip`, `status`) VALUES (%i, %s, %i, %i, %i, %s, %s, %i)',
User::$id, session_id(), time(), $this->_post['remember_me'] ? 0 : time() + Cfg::get('SESSION_TIMEOUT_DELAY'), time(), User::$agent, User::$ip, SESSION_ACTIVE); User::$id, session_id(), time(), $this->_post['remember_me'] ? 0 : time() + Cfg::get('SESSION_TIMEOUT_DELAY'), time(), User::$agent, User::$ip, SESSION_ACTIVE);
if (User::init()) // reinitialize the user if (User::init()) // reinitialize the user

View file

@ -11,25 +11,25 @@ class AccountSignoutResponse extends TextResponse
use TrGetNext; use TrGetNext;
protected array $expectedGET = array( protected array $expectedGET = array(
'next' => ['filter' => FILTER_SANITIZE_URL, 'flags' => FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH], 'next' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']],
'global' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet'] ] 'global' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet'] ]
); );
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
// if the user not is logged in goto login page // if the user not is logged in goto login page
if (!User::isLoggedIn()) if (!User::isLoggedIn())
$this->forwardToSignIn(); $this->forwardToSignIn();
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
{ {
if ($this->_get['global']) if ($this->_get['global'])
DB::Aowow()->query('UPDATE ?_account_sessions SET `touched` = ?d, `status` = ?d WHERE `userId` = ?d', time(), SESSION_FORCED_LOGOUT, User::$id); DB::Aowow()->qry('UPDATE ::account_sessions SET `touched` = %i, `status` = %i WHERE `userId` = %i', time(), SESSION_FORCED_LOGOUT, User::$id);
else else
DB::Aowow()->query('UPDATE ?_account_sessions SET `touched` = ?d, `status` = ?d WHERE `sessionId` = ?', time(), SESSION_LOGOUT, session_id()); DB::Aowow()->qry('UPDATE ::account_sessions SET `touched` = %i, `status` = %i WHERE `sessionId` = %s', time(), SESSION_LOGOUT, session_id());
User::destroy(); User::destroy();

View file

@ -26,7 +26,7 @@ class AccountSignupResponse extends TemplateResponse
); );
protected array $expectedGET = array( protected array $expectedGET = array(
'next' => ['filter' => FILTER_SANITIZE_URL, 'flags' => FILTER_FLAG_STRIP_AOWOW] 'next' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']]
); );
private bool $success = false; private bool $success = false;
@ -99,7 +99,7 @@ class AccountSignupResponse extends TemplateResponse
// check password // check password
if (!Util::validatePassword($this->_post['password'], $e)) if (!Util::validatePassword($this->_post['password'], $e))
return Lang::account($e == 1 ? 'errPassLength' : 'errPassChars'); return $e == 1 ? Lang::account('errPassLength') : Lang::main('intError');
if ($this->_post['password'] !== $this->_post['c_password']) if ($this->_post['password'] !== $this->_post['c_password'])
return Lang::account('passMismatch'); return Lang::account('passMismatch');
@ -109,24 +109,24 @@ class AccountSignupResponse extends TemplateResponse
return Lang::main('intError'); return Lang::main('intError');
// limit account creation // limit account creation
if (DB::Aowow()->selectRow('SELECT 1 FROM ?_account_bannedips WHERE `type` = ?d AND `ip` = ? AND `count` >= ?d AND `unbanDate` >= UNIX_TIMESTAMP()', IP_BAN_TYPE_REGISTRATION_ATTEMPT, User::$ip, Cfg::get('ACC_FAILED_AUTH_COUNT'))) if (DB::Aowow()->selectRow('SELECT 1 FROM ::account_bannedips WHERE `type` = %i AND `ip` = %s AND `count` >= %i AND `unbanDate` >= UNIX_TIMESTAMP()', IP_BAN_TYPE_REGISTRATION_ATTEMPT, User::$ip, Cfg::get('ACC_FAILED_AUTH_COUNT')))
{ {
DB::Aowow()->query('UPDATE ?_account_bannedips SET `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + ?d WHERE `ip` = ? AND `type` = ?d', Cfg::get('ACC_FAILED_AUTH_BLOCK'), User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT); DB::Aowow()->qry('UPDATE ::account_bannedips SET `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + %i WHERE `ip` = %s AND `type` = %i', Cfg::get('ACC_FAILED_AUTH_BLOCK'), User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT);
return Lang::account('inputbox', 'error', 'signupExceeded', [Util::formatTime(Cfg::get('ACC_FAILED_AUTH_BLOCK') * 1000)]); return Lang::account('inputbox', 'error', 'signupExceeded', [DateTime::formatTimeElapsedFloat(Cfg::get('ACC_FAILED_AUTH_BLOCK') * 1000)]);
} }
// username / email taken // username / email taken
if ($inUseData = DB::Aowow()->SelectRow('SELECT `id`, `username`, `status` = ?d AND `statusTimer` < UNIX_TIMESTAMP() AS "expired" FROM ?_account WHERE (LOWER(`username`) = LOWER(?) OR LOWER(`email`) = LOWER(?))', ACC_STATUS_NEW, $this->_post['username'], $this->_post['email'])) if ($inUseData = DB::Aowow()->SelectRow('SELECT `id`, `username`, `status` = %i AND `statusTimer` < UNIX_TIMESTAMP() AS "expired" FROM ::account WHERE (LOWER(`username`) = LOWER(%s) OR LOWER(`email`) = LOWER(%s))', ACC_STATUS_NEW, $this->_post['username'], $this->_post['email']))
{ {
if ($inUseData['expired']) if ($inUseData['expired'])
DB::Aowow()->query('DELETE FROM ?_account WHERE `id` = ?d', $inUseData['id']); DB::Aowow()->qry('DELETE FROM ::account WHERE `id` = %i', $inUseData['id']);
else else
return Util::lower($inUseData['username']) == Util::lower($this->_post['username']) ? Lang::account('nameInUse') : Lang::account('mailInUse'); return Util::lower($inUseData['username']) == Util::lower($this->_post['username']) ? Lang::account('nameInUse') : Lang::account('mailInUse');
} }
// create.. // create..
$token = Util::createHash(); $token = Util::createHash();
$userId = DB::Aowow()->query('INSERT INTO ?_account (`login`, `passHash`, `username`, `email`, `joindate`, `curIP`, `locale`, `userGroups`, `status`, `statusTimer`, `token`) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(), ?, ?d, ?d, ?d, UNIX_TIMESTAMP() + ?d, ?)', $userId = DB::Aowow()->qry('INSERT INTO ::account (`login`, `passHash`, `username`, `email`, `joindate`, `curIP`, `locale`, `userGroups`, `status`, `statusTimer`, `token`) VALUES (%s, %s, %s, %s, UNIX_TIMESTAMP(), %s, %i, %i, %i, UNIX_TIMESTAMP() + %i, %s)',
$this->_post['username'], $this->_post['username'],
User::hashCrypt($this->_post['password']), User::hashCrypt($this->_post['password']),
$this->_post['username'], $this->_post['username'],
@ -143,14 +143,14 @@ class AccountSignupResponse extends TemplateResponse
return Lang::main('intError'); return Lang::main('intError');
// create session tied to the token to store remember_me status // create session tied to the token to store remember_me status
DB::Aowow()->query('INSERT INTO ?_account_sessions (`userId`, `sessionId`, `created`, `expires`, `touched`, `deviceInfo`, `ip`, `status`) VALUES (?d, ?, ?d, ?d, ?d, ?, ?, ?d)', DB::Aowow()->qry('INSERT INTO ::account_sessions (`userId`, `sessionId`, `created`, `expires`, `touched`, `deviceInfo`, `ip`, `status`) VALUES (%i, %s, %i, %i, %i, %s, %s, %i)',
$userId, $token, time(), $this->_post['remember_me'] ? 0 : time() + Cfg::get('SESSION_TIMEOUT_DELAY'), time(), User::$agent, User::$ip, SESSION_ACTIVE); $userId, $token, time(), $this->_post['remember_me'] ? 0 : time() + Cfg::get('SESSION_TIMEOUT_DELAY'), time(), User::$agent, User::$ip, SESSION_ACTIVE);
if (!Util::sendMail($this->_post['email'], 'activate-account', [$token], Cfg::get('ACC_CREATE_SAVE_DECAY'))) if (!Util::sendMail($this->_post['email'], 'activate-account', [$token], Cfg::get('ACC_CREATE_SAVE_DECAY')))
return Lang::main('intError2', ['send mail']); return Lang::main('intError2', ['send mail']);
// success: update ip-bans // success: update ip-bans
DB::Aowow()->query('INSERT INTO ?_account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (?, ?d, 1, UNIX_TIMESTAMP() + ?d) ON DUPLICATE KEY UPDATE `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + ?d', DB::Aowow()->qry('INSERT INTO ::account_bannedips (`ip`, `type`, `count`, `unbanDate`) VALUES (%s, %i, 1, UNIX_TIMESTAMP() + %i) ON DUPLICATE KEY UPDATE `count` = `count` + 1, `unbanDate` = UNIX_TIMESTAMP() + %i',
User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK')); User::$ip, IP_BAN_TYPE_REGISTRATION_ATTEMPT, Cfg::get('ACC_FAILED_AUTH_BLOCK'), Cfg::get('ACC_FAILED_AUTH_BLOCK'));
Util::gainSiteReputation($userId, SITEREP_ACTION_REGISTER); Util::gainSiteReputation($userId, SITEREP_ACTION_REGISTER);

View file

@ -37,7 +37,7 @@ class AccountUpdatecommunitysettingsResponse extends TextResponse
return Lang::main('genericError'); return Lang::main('genericError');
// description - 0 modified rows is still success // description - 0 modified rows is still success
if (!is_int(DB::Aowow()->query('UPDATE ?_account SET `description` = ? WHERE `id` = ?d', $this->_post['desc'], User::$id))) if (!is_int(DB::Aowow()->qry('UPDATE ::account SET `description` = %s WHERE `id` = %i', $this->_post['desc'], User::$id)))
return Lang::main('genericError'); return Lang::main('genericError');
$this->success = true; $this->success = true;

View file

@ -22,12 +22,12 @@ class AccountUpdateemailResponse extends TextResponse
private bool $success = false; private bool $success = false;
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF) if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
(new TemplateResponse())->generateError(); (new TemplateResponse())->generateError();
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
@ -48,21 +48,21 @@ class AccountUpdateemailResponse extends TextResponse
if (!$this->_post['newemail']) if (!$this->_post['newemail'])
return Lang::account('emailInvalid'); return Lang::account('emailInvalid');
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE `email` = ? AND `id` <> ?d', $this->_post['newemail'], User::$id)) if (DB::Aowow()->selectCell('SELECT 1 FROM ::account WHERE `email` = %s AND `id` <> %i', $this->_post['newemail'], User::$id))
return Lang::account('mailInUse'); return Lang::account('mailInUse');
$status = DB::Aowow()->selectCell('SELECT `status` FROM ?_account WHERE `statusTimer` > UNIX_TIMESTAMP() AND `id` = ?d', User::$id); $status = DB::Aowow()->selectCell('SELECT `status` FROM ::account WHERE `statusTimer` > UNIX_TIMESTAMP() AND `id` = %i', User::$id);
if ($status != ACC_STATUS_NONE && $status != ACC_STATUS_CHANGE_EMAIL) if ($status != ACC_STATUS_NONE && $status != ACC_STATUS_CHANGE_EMAIL)
return Lang::account('isRecovering', [Util::formatTime(Cfg::get('ACC_RECOVERY_DECAY') * 1000)]); return Lang::account('inputbox', 'error', 'isRecovering', [DateTime::formatTimeElapsedFloat(Cfg::get('ACC_RECOVERY_DECAY') * 1000)]);
$oldEmail = DB::Aowow()->selectCell('SELECT `email` FROM ?_account WHERE `id` = ?d', User::$id); $oldEmail = DB::Aowow()->selectCell('SELECT `email` FROM ::account WHERE `id` = %i', User::$id);
if ($this->_post['newemail'] == $oldEmail) if ($this->_post['newemail'] == $oldEmail)
return Lang::account('newMailDiff'); return Lang::account('newMailDiff');
$token = Util::createHash(); $token = Util::createHash();
// store new mail in updateValue field, exchange when confirmation mail gets confirmed // store new mail in updateValue field, exchange when confirmation mail gets confirmed
if (!DB::Aowow()->query('UPDATE ?_account SET `updateValue` = ?, `status` = ?d, `statusTimer` = UNIX_TIMESTAMP() + ?d, `token` = ? WHERE `id` = ?d', if (!DB::Aowow()->qry('UPDATE ::account SET `updateValue` = %s, `status` = %i, `statusTimer` = UNIX_TIMESTAMP() + %i, `token` = %s WHERE `id` = %i',
$this->_post['newemail'], ACC_STATUS_CHANGE_EMAIL, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id)) $this->_post['newemail'], ACC_STATUS_CHANGE_EMAIL, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id))
return Lang::main('intError'); return Lang::main('intError');

View file

@ -41,15 +41,15 @@ class AccountUpdategeneralsettingsResponse extends TextResponse
if ($this->_post['modelrace'] && !ChrRace::tryFrom($this->_post['modelrace'])) if ($this->_post['modelrace'] && !ChrRace::tryFrom($this->_post['modelrace']))
return Lang::main('genericError'); return Lang::main('genericError');
// js handles this as cookie, so saved as cookie; Q - also save in ?_account table? // js handles this as cookie, so saved as cookie; Q - also save in ::account table?
if (!DB::Aowow()->query('REPLACE INTO ?_account_cookies (`userId`, `name`, `data`) VALUES (?d, ?, ?)', User::$id, 'default_3dmodel', $this->_post['modelrace']. ',' . $this->_post['modelgender'])) if (!DB::Aowow()->qry('REPLACE INTO ::account_cookies (`userId`, `name`, `data`) VALUES (%i, %s, %s)', User::$id, 'default_3dmodel', $this->_post['modelrace']. ',' . $this->_post['modelgender']))
return Lang::main('genericError'); return Lang::main('genericError');
if (!setcookie('default_3dmodel', $this->_post['modelrace']. ',' . $this->_post['modelgender'], 0, '/')) if (!setcookie('default_3dmodel', $this->_post['modelrace']. ',' . $this->_post['modelgender'], 0, '/'))
return Lang::main('intError'); return Lang::main('intError');
// int > number of edited rows > no changes is still success // int > number of edited rows > no changes is still success
if (!is_int(DB::Aowow()->query('UPDATE ?_account SET `debug` = ?d WHERE `id` = ?d', $this->_post['idsInLists'] ? 1 : 0, User::$id))) if (!is_int(DB::Aowow()->qry('UPDATE ::account SET `debug` = %i WHERE `id` = %i', $this->_post['idsInLists'] ? 1 : 0, User::$id)))
return Lang::main('intError'); return Lang::main('intError');
$this->success = true; $this->success = true;

View file

@ -25,12 +25,12 @@ class AccountUpdatepasswordResponse extends TextResponse
private bool $success = false; private bool $success = false;
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF) if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
(new TemplateResponse())->generateError(); (new TemplateResponse())->generateError();
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
@ -48,14 +48,14 @@ class AccountUpdatepasswordResponse extends TextResponse
return Lang::main('intError'); return Lang::main('intError');
if (!Util::validatePassword($this->_post['newPassword'], $e)) if (!Util::validatePassword($this->_post['newPassword'], $e))
return Lang::account($e == 1 ? 'errPassLength' : 'errPassChars'); return $e == 1 ? Lang::account('errPassLength') : Lang::main('intError');
if ($this->_post['newPassword'] !== $this->_post['confirmPassword']) if ($this->_post['newPassword'] !== $this->_post['confirmPassword'])
return Lang::account('passMismatch'); return Lang::account('passMismatch');
$userData = DB::Aowow()->selectRow('SELECT `status`, `passHash`, `statusTimer` FROM ?_account WHERE `id` = ?d', User::$id); $userData = DB::Aowow()->selectRow('SELECT `status`, `passHash`, `statusTimer` FROM ::account WHERE `id` = %i', User::$id);
if ($userData['status'] != ACC_STATUS_NONE && $userData['status'] != ACC_STATUS_CHANGE_PASS && $userData['statusTimer'] > time()) if ($userData['status'] != ACC_STATUS_NONE && $userData['status'] != ACC_STATUS_CHANGE_PASS && $userData['statusTimer'] > time())
return Lang::account('isRecovering', [Util::formatTime(Cfg::get('ACC_RECOVERY_DECAY') * 1000)]); return Lang::account('inputbox', 'error', 'isRecovering', [DateTime::formatTimeElapsedFloat(Cfg::get('ACC_RECOVERY_DECAY') * 1000)]);
if (!User::verifyCrypt($this->_post['currentPassword'], $userData['passHash'])) if (!User::verifyCrypt($this->_post['currentPassword'], $userData['passHash']))
return Lang::account('wrongPass'); return Lang::account('wrongPass');
@ -66,17 +66,17 @@ class AccountUpdatepasswordResponse extends TextResponse
$token = Util::createHash(); $token = Util::createHash();
// store new hash in updateValue field, exchange when confirmation mail gets confirmed // store new hash in updateValue field, exchange when confirmation mail gets confirmed
if (!DB::Aowow()->query('UPDATE ?_account SET `updateValue` = ?, `status` = ?d, `statusTimer` = UNIX_TIMESTAMP() + ?d, `token` = ? WHERE `id` = ?d', if (!DB::Aowow()->qry('UPDATE ::account SET `updateValue` = %s, `status` = %i, `statusTimer` = UNIX_TIMESTAMP() + %i, `token` = %s WHERE `id` = %i',
User::hashCrypt($this->_post['newPassword']), ACC_STATUS_CHANGE_PASS, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id)) User::hashCrypt($this->_post['newPassword']), ACC_STATUS_CHANGE_PASS, Cfg::get('ACC_RECOVERY_DECAY'), $token, User::$id))
return Lang::main('intError'); return Lang::main('intError');
$email = DB::Aowow()->selectCell('SELECT `email` FROM ?_account WHERE `id` = ?d', User::$id); $email = DB::Aowow()->selectCell('SELECT `email` FROM ::account WHERE `id` = %i', User::$id);
if (!Util::sendMail($email, 'update-password', [$token, $email], Cfg::get('ACC_RECOVERY_DECAY'))) if (!Util::sendMail($email, 'update-password', [$token, $email], Cfg::get('ACC_RECOVERY_DECAY')))
return Lang::main('intError2', ['send mail']); return Lang::main('intError2', ['send mail']);
// logout all other active sessions // logout all other active sessions
if ($this->_post['globalLogout']) if ($this->_post['globalLogout'])
DB::Aowow()->query('UPDATE ?_account_sessions SET `status` = ?d, `touched` = ?d WHERE `userId` = ?d AND `sessionId` <> ? AND `status` = ?d', SESSION_FORCED_LOGOUT, time(), User::$id, session_id(), SESSION_ACTIVE); DB::Aowow()->qry('UPDATE ::account_sessions SET `status` = %i, `touched` = %i WHERE `userId` = %i AND `sessionId` <> ? AND `status` = %i', SESSION_FORCED_LOGOUT, time(), User::$id, session_id(), SESSION_ACTIVE);
$this->success = true; $this->success = true;
return Lang::account('updateMessage', 'personal', [User::$email]); return Lang::account('updateMessage', 'personal', [User::$email]);

View file

@ -22,12 +22,12 @@ class AccountUpdateusernameResponse extends TextResponse
private bool $success = false; private bool $success = false;
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF) if (Cfg::get('ACC_AUTH_MODE') != AUTH_MODE_SELF)
(new TemplateResponse())->generateError(); (new TemplateResponse())->generateError();
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
@ -44,14 +44,14 @@ class AccountUpdateusernameResponse extends TextResponse
if (!$this->assertPOST('newUsername')) if (!$this->assertPOST('newUsername'))
return Lang::main('intError'); return Lang::main('intError');
if (DB::Aowow()->selectCell('SELECT `renameCooldown` FROM ?_account WHERE `id` = ?d', User::$id) > time()) if (DB::Aowow()->selectCell('SELECT `renameCooldown` FROM ::account WHERE `id` = %i', User::$id) > time())
return Lang::main('intError'); // should have grabbed the error response.. return Lang::main('intError'); // should have grabbed the error response..
// yes, including your current name. you don't want to change into your current name, right? // yes, including your current name. you don't want to change into your current name, right?
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_post['newUsername'])) if (DB::Aowow()->selectCell('SELECT 1 FROM ::account WHERE LOWER(`username`) = LOWER(%s)', $this->_post['newUsername']))
return Lang::account('nameInUse'); return Lang::account('nameInUse');
DB::Aowow()->query('UPDATE ?_account SET `username` = ?, `renameCooldown` = ?d WHERE `id` = ?d', $this->_post['newUsername'], time() + Cfg::get('acc_rename_decay'), User::$id); DB::Aowow()->qry('UPDATE ::account SET `username` = %s, `renameCooldown` = %i WHERE `id` = %i', $this->_post['newUsername'], time() + Cfg::get('acc_rename_decay'), User::$id);
$this->success = true; $this->success = true;
return Lang::account('updateMessage', 'username', [User::$username, $this->_post['newUsername']]); return Lang::account('updateMessage', 'username', [User::$username, $this->_post['newUsername']]);

View file

@ -46,11 +46,11 @@ class AccountWeightscalesResponse extends TextResponse
if (!$this->assertPOST('name', 'scale')) if (!$this->assertPOST('name', 'scale'))
return; return;
$nScales = DB::Aowow()->selectCell('SELECT COUNT(`id`) FROM ?_account_weightscales WHERE `userId` = ?d', User::$id); $nScales = DB::Aowow()->selectCell('SELECT COUNT(`id`) FROM ::account_weightscales WHERE `userId` = %i', User::$id);
if ($nScales >= self::MAX_SCALES) if ($nScales >= self::MAX_SCALES)
return; return;
if ($id = DB::Aowow()->query('INSERT INTO ?_account_weightscales (`userId`, `name`) VALUES (?d, ?)', User::$id, $this->_post['name'])) if ($id = DB::Aowow()->qry('INSERT INTO ::account_weightscales (`userId`, `name`) VALUES (%i, %s)', User::$id, $this->_post['name']))
if ($this->storeScaleData($id)) if ($this->storeScaleData($id))
$this->result = $id; $this->result = $id;
} }
@ -61,13 +61,13 @@ class AccountWeightscalesResponse extends TextResponse
return; return;
// not in DB or not owned by user // not in DB or not owned by user
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account_weightscales WHERE `userId` = ?d AND `id` = ?d', User::$id, $this->_post['id'])) if (!DB::Aowow()->selectCell('SELECT 1 FROM ::account_weightscales WHERE `userId` = %i AND `id` = %i', User::$id, $this->_post['id']))
{ {
trigger_error('AccountWeightscalesResponse::updateWeights - scale #'.$this->_post['id'].' not in db or not owned by user #'.User::$id, E_USER_ERROR); trigger_error('AccountWeightscalesResponse::updateWeights - scale #'.$this->_post['id'].' not in db or not owned by user #'.User::$id, E_USER_ERROR);
return; return;
} }
DB::Aowow()->query('UPDATE ?_account_weightscales SET `name` = ? WHERE `id` = ?d', $this->_post['name'], $this->_post['id']); DB::Aowow()->qry('UPDATE ::account_weightscales SET `name` = %s WHERE `id` = %i', $this->_post['name'], $this->_post['id']);
$this->storeScaleData($this->_post['id']); $this->storeScaleData($this->_post['id']);
// return edited id on success // return edited id on success
@ -77,20 +77,24 @@ class AccountWeightscalesResponse extends TextResponse
private function deleteWeights() : void private function deleteWeights() : void
{ {
if ($this->assertPOST('id')) if ($this->assertPOST('id'))
DB::Aowow()->query('DELETE FROM ?_account_weightscales WHERE `id` = ?d AND `userId` = ?d', $this->_post['id'], User::$id); DB::Aowow()->qry('DELETE FROM ::account_weightscales WHERE `id` = %i AND `userId` = %i', $this->_post['id'], User::$id);
$this->result = ''; $this->result = '';
} }
private function storeScaleData(int $scaleId) : bool private function storeScaleData(int $scaleId) : bool
{ {
if (!is_int(DB::Aowow()->query('DELETE FROM ?_account_weightscale_data WHERE `id` = ?d', $scaleId))) if (!is_int(DB::Aowow()->qry('DELETE FROM ::account_weightscale_data WHERE `id` = %i', $scaleId)))
return false; return false;
foreach ($this->_post['scale'] as [$k, $v]) // $x['val'] is known to be a positive int due to regex check
if (in_array($k, Util::$weightScales)) // $v is known to be a positive int due to regex check $scaleData = array_filter($this->_post['scale'], fn($x) => Stat::getWeightJson($x['field']) && $x['val'] > 0);
if (!is_int(DB::Aowow()->query('INSERT INTO ?_account_weightscale_data VALUES (?d, ?, ?d)', $scaleId, $k, $v)))
return false; array_walk($scaleData, fn(&$x) => $x['id'] = $scaleId);
foreach ($scaleData as $sd)
if (is_null(DB::Aowow()->qry('INSERT INTO ::account_weightscale_data %v', $sd)))
return false;
return true; return true;
} }
@ -103,7 +107,7 @@ class AccountWeightscalesResponse extends TextResponse
protected static function checkScale(string $val) : array protected static function checkScale(string $val) : array
{ {
if (preg_match('/^((\w+:\d+)(,\w+:\d+)*)$/', $val)) if (preg_match('/^((\w+:\d+)(,\w+:\d+)*)$/', $val))
return array_map(fn($x) => explode(':', $x), explode(',', $val)); return array_map(fn($x) => array_combine(['field', 'val'], explode(':', $x)), explode(',', $val));
return []; return [];
} }

View file

@ -21,7 +21,7 @@ class AchievementBaseResponse extends TemplateResponse implements ICache
{ {
use TrDetailPage, TrCache; use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'achievement'; protected string $template = 'achievement';
protected string $pageName = 'achievement'; protected string $pageName = 'achievement';
@ -73,7 +73,7 @@ class AchievementBaseResponse extends TemplateResponse implements ICache
while ($curCat > 0) while ($curCat > 0)
{ {
$catPath[] = $curCat; $catPath[] = $curCat;
$curCat = DB::Aowow()->SelectCell('SELECT `parentCat` FROM ?_achievementcategory WHERE `id` = ?d', $curCat); $curCat = DB::Aowow()->SelectCell('SELECT `parentCat` FROM ::achievementcategory WHERE `id` = %i', $curCat);
} }
$this->breadcrumb = array_merge($this->breadcrumb, array_reverse($catPath)); $this->breadcrumb = array_merge($this->breadcrumb, array_reverse($catPath));
@ -107,15 +107,32 @@ class AchievementBaseResponse extends TemplateResponse implements ICache
default => Lang::game('si', SIDE_BOTH) // 0, 3 default => Lang::game('si', SIDE_BOTH) // 0, 3
}; };
// id
$infobox[] = Lang::achievement('id') . $this->typeId;
// icon // icon
if ($_ = $this->subject->getField('iconId')) if ($_ = $this->subject->getField('iconId'))
{ {
$infobox[] = Util::ucFirst(lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]'; $infobox[] = Util::ucFirst(Lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
$this->extendGlobalIds(Type::ICON, $_); $this->extendGlobalIds(Type::ICON, $_);
} }
// profiler relateed (note that this is part of the cache. I don't think this is important enough to calc for every view)
if (Cfg::get('PROFILER_ENABLE') && !($this->subject->getField('flags') & ACHIEVEMENT_FLAG_COUNTER))
{
$x = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ::profiler_completion_achievements WHERE `achievementId` = %i', $this->typeId);
$y = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ::profiler_profiles WHERE `custom` = 0 AND `stub` = 0');
$infobox[] = Lang::profiler('attainedBy', [round(($x ?: 0) * 100 / ($y ?: 1))]);
// completion row added by InfoboxMarkup
}
// original name
if (Lang::getLocale() != Locale::EN)
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
if ($infobox) if ($infobox)
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0', !($this->subject->getField('flags') & ACHIEVEMENT_FLAG_COUNTER));
/**********/ /**********/
@ -190,7 +207,7 @@ class AchievementBaseResponse extends TemplateResponse implements ICache
$this->rewards = [$rewItems, $rewTitles, $text]; $this->rewards = [$rewItems, $rewTitles, $text];
// factionchange-equivalent // factionchange-equivalent
if ($pendant = DB::World()->selectCell('SELECT IF(`horde_id` = ?d, `alliance_id`, -`horde_id`) FROM player_factionchange_achievement WHERE `alliance_id` = ?d OR `horde_id` = ?d', $this->typeId, $this->typeId, $this->typeId)) if ($pendant = DB::World()->selectCell('SELECT IF(`horde_id` = %i, `alliance_id`, -`horde_id`) FROM player_factionchange_achievement WHERE `alliance_id` = %i OR `horde_id` = %i', $this->typeId, $this->typeId, $this->typeId))
{ {
$altAcv = new AchievementList(array(['id', abs($pendant)])); $altAcv = new AchievementList(array(['id', abs($pendant)]));
if (!$altAcv->error) if (!$altAcv->error)
@ -213,7 +230,7 @@ class AchievementBaseResponse extends TemplateResponse implements ICache
// serverside extra-Data (not sure why ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE is set, let a lone a couple hundred times) // serverside extra-Data (not sure why ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE is set, let a lone a couple hundred times)
if ($crtIds = array_column($this->subject->getCriteria(), 'id')) if ($crtIds = array_column($this->subject->getCriteria(), 'id'))
$crtExtraData = DB::World()->select('SELECT `criteria_id` AS ARRAY_KEY, `type` AS ARRAY_KEY2, `value1`, `value2`, `ScriptName` FROM achievement_criteria_data WHERE `type` <> ?d AND `criteria_id` IN (?a)', ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE, $crtIds); $crtExtraData = DB::World()->selectAssoc('SELECT `criteria_id` AS ARRAY_KEY, `type` AS ARRAY_KEY2, `value1`, `value2`, `ScriptName` FROM achievement_criteria_data WHERE `type` <> %i AND `criteria_id` IN %in', ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE, $crtIds);
else else
$crtExtraData = []; $crtExtraData = [];
@ -244,7 +261,7 @@ class AchievementBaseResponse extends TemplateResponse implements ICache
case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA: case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA:
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
$zoneId = DB::Aowow()->selectCell('SELECT `id` FROM ?_zones WHERE `mapId` = ?', $obj); $zoneId = DB::Aowow()->selectCell('SELECT `id` FROM ::zones WHERE `mapId` = %s', $obj);
$crtIcon = new IconElement(Type::ZONE, $zoneId ?: 0, $crtName ?: ZoneList::getName($zoneId), size: IconElement::SIZE_SMALL, element: 'iconlist-icon'); $crtIcon = new IconElement(Type::ZONE, $zoneId ?: 0, $crtName ?: ZoneList::getName($zoneId), size: IconElement::SIZE_SMALL, element: 'iconlist-icon');
break; break;
// link to area // link to area
@ -353,8 +370,17 @@ class AchievementBaseResponse extends TemplateResponse implements ICache
$extraData[] = '<a href="?event='.$we->id.'">'.$we->getField('name', true).'</a>'; $extraData[] = '<a href="?event='.$we->id.'">'.$we->getField('name', true).'</a>';
break; break;
case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_ID: case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_ID:
if ($z = new ZoneList(array(['mapIdBak', $xData['value1']]))) $extraData[] = match((int)$xData['value1'])
$extraData[] = '<a href="?zone='.$z->id.'">'.$z->getField('name', true).'</a>'; {
0 => Lang::maps('EasternKingdoms'),
1 => Lang::maps('Kalimdor'),
530 => Lang::maps('Outland'),
571 => Lang::maps('Northrend'),
default => (function(int $mapId) {
$z = new ZoneList(array(['mapId', $mapId]));
return '<a href="?zone='.$z->id.'">'.$z->getField('name', true).'</a>';
})($xData['value1'])
};
break; break;
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE: case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE:
$extraData[] = TitleList::makeLink($xData['value1']); $extraData[] = TitleList::makeLink($xData['value1']);
@ -397,7 +423,7 @@ class AchievementBaseResponse extends TemplateResponse implements ICache
} }
// tab: criteria of // tab: criteria of
$refs = DB::Aowow()->SelectCol('SELECT `refAchievementId` FROM ?_achievementcriteria WHERE `type` = ?d AND `value1` = ?d', $refs = DB::Aowow()->SelectCol('SELECT `refAchievementId` FROM ::achievementcriteria WHERE `type` = %i AND `value1` = %i',
ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT, ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT,
$this->typeId $this->typeId
); );
@ -437,7 +463,7 @@ class AchievementBaseResponse extends TemplateResponse implements ICache
{ {
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 ::mails WHERE `id` = %i', $_);
if (!$letter) if (!$letter)
return false; return false;
@ -476,7 +502,7 @@ class AchievementBaseResponse extends TemplateResponse implements ICache
$avlb = []; $avlb = [];
foreach (Profiler::getRealms() AS $rId => $rData) foreach (Profiler::getRealms() AS $rId => $rData)
if (!DB::Characters($rId)->selectCell('SELECT 1 FROM character_achievement WHERE `achievement` = ?d', $pt->typeId)) if (!DB::Characters($rId)->selectCell('SELECT 1 FROM character_achievement WHERE `achievement` = %i', $pt->typeId))
$avlb[] = Util::ucWords($rData['name']); $avlb[] = Util::ucWords($rData['name']);
if (!$avlb) if (!$avlb)

View file

@ -11,7 +11,7 @@ class AchievementsBaseResponse extends TemplateResponse implements ICache
use TrListPage, TrCache; use TrListPage, TrCache;
protected int $type = Type::ACHIEVEMENT; protected int $type = Type::ACHIEVEMENT;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected string $template = 'achievements'; protected string $template = 'achievements';
protected string $pageName = 'achievements'; protected string $pageName = 'achievements';
@ -46,14 +46,22 @@ class AchievementsBaseResponse extends TemplateResponse implements ICache
) )
); );
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
$this->getCategoryFromUrl($pageParam); $this->getCategoryFromUrl($rawParam);
parent::__construct($pageParam); parent::__construct($rawParam);
if ($this->category)
$this->subCat = '='.implode('.', $this->category);
$this->subCat = $pageParam !== '' ? '='.$pageParam : '';
$this->filter = new AchievementListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); $this->filter = new AchievementListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]);
if ($this->filter->shouldReload)
{
$_SESSION['error']['fi'] = $this->filter::class;
$get = $this->filter->buildGETParam();
$this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : ''));
}
$this->filterError = $this->filter->error; $this->filterError = $this->filter->error;
} }
@ -61,7 +69,7 @@ class AchievementsBaseResponse extends TemplateResponse implements ICache
{ {
$this->h1 = Util::ucFirst(Lang::game('achievements')); $this->h1 = Util::ucFirst(Lang::game('achievements'));
$conditions = []; $conditions = [Listview::DEFAULT_SIZE];
if (!User::isInGroup(U_GROUP_EMPLOYEE)) if (!User::isInGroup(U_GROUP_EMPLOYEE))
$conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
@ -69,13 +77,9 @@ class AchievementsBaseResponse extends TemplateResponse implements ICache
if ($this->category) if ($this->category)
$conditions[] = ['category', end($this->category)]; $conditions[] = ['category', end($this->category)];
$this->filter->evalCriteria();
if ($fiCnd = $this->filter->getConditions()) if ($fiCnd = $this->filter->getConditions())
$conditions[] = $fiCnd; $conditions[] = $fiCnd;
$this->filterError = $this->filter->error; // maybe the evalX() caused something
/*************/ /*************/
/* Menu Path */ /* Menu Path */
@ -116,10 +120,10 @@ class AchievementsBaseResponse extends TemplateResponse implements ICache
if (!$acvList->getMatches() && $this->category) if (!$acvList->getMatches() && $this->category)
{ {
// ToDo - we also branch into here if the filter prohibits results. That should be skipped. // ToDo - we also branch into here if the filter prohibits results. That should be skipped.
$conditions = []; $conditions = [Listview::DEFAULT_SIZE];
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) ', end($this->category), end($this->category))) if ($catList = DB::Aowow()->SelectCol('SELECT `id` FROM ::achievementcategory WHERE `parentCat` IN %in OR `parentCat2` IN %in ', $this->category, $this->category))
$conditions[] = ['category', $catList]; $conditions[] = ['category', $catList];
$acvList = new AchievementList($conditions, ['calcTotal' => true]); $acvList = new AchievementList($conditions, ['calcTotal' => true]);
@ -141,9 +145,9 @@ class AchievementsBaseResponse extends TemplateResponse implements ICache
$tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; $tabData['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::get('SQL_LIMIT_DEFAULT')) if ($acvList->getMatches() > Listview::DEFAULT_SIZE)
{ {
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_achievementsfound', $acvList->getMatches(), Cfg::get('SQL_LIMIT_DEFAULT')); $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_achievementsfound', $acvList->getMatches(), Listview::DEFAULT_SIZE);
$tabData['_truncated'] = 1; $tabData['_truncated'] = 1;
} }
} }

View file

@ -44,13 +44,13 @@ class AdminAnnouncementsResponse extends TemplateResponse
return; return;
} }
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_announcements WHERE `id` = ?d', $this->_get['id'])) if (!DB::Aowow()->selectCell('SELECT 1 FROM ::announcements WHERE `id` = %i', $this->_get['id']))
{ {
trigger_error('AdminAnnouncementsResponse::updateStatus - announcement does not exist'); trigger_error('AdminAnnouncementsResponse::updateStatus - announcement does not exist');
return; return;
} }
DB::Aowow()->query('UPDATE ?_announcements SET `status` = ?d WHERE `id` = ?d', $this->_get['status'], $this->_get['id']); DB::Aowow()->qry('UPDATE ::announcements SET `status` = %i WHERE `id` = %i', $this->_get['status'], $this->_get['id']);
} }
private function displayEditor() : void private function displayEditor() : void

View file

@ -33,13 +33,13 @@ class AdminCommentResponse extends TextResponse
$ok = false; $ok = false;
if ($this->_post['status']) // outdated, mark as deleted and clear other flags (sticky + outdated) if ($this->_post['status']) // outdated, mark as deleted and clear other flags (sticky + outdated)
{ {
if ($ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = ?d, `deleteUserId` = ?d, `deleteDate` = ?d WHERE `id` = ?d', CC_FLAG_DELETED, User::$id, time(), $this->_post['id'])) if ($ok = DB::Aowow()->qry('UPDATE ::comments SET `flags` = %i, `deleteUserId` = %i, `deleteDate` = %i WHERE `id` = %i', CC_FLAG_DELETED, User::$id, time(), $this->_post['id']))
if ($rep = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id'])) if ($rep = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id']))
$rep->close(Report::STATUS_CLOSED_SOLVED); $rep->close(Report::STATUS_CLOSED_SOLVED);
} }
else // up to date else // up to date
{ {
if ($ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id'])) if ($ok = DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` & ~%i WHERE `id` = %i', CC_FLAG_OUTDATED, $this->_post['id']))
if ($rep = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id'])) if ($rep = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id']))
$rep->close(Report::STATUS_CLOSED_WONTFIX); $rep->close(Report::STATUS_CLOSED_WONTFIX);
} }

View file

@ -31,7 +31,7 @@ class AdminGuideResponse extends TextResponse
return; return;
} }
$guide = DB::Aowow()->selectRow('SELECT `userId`, `status` FROM ?_guides WHERE `id` = ?d', $this->_post['id']); $guide = DB::Aowow()->selectRow('SELECT `userId`, `status` FROM ::guides WHERE `id` = %i', $this->_post['id']);
if (!$guide) if (!$guide)
{ {
trigger_error('AdminGuideResponse - guide #'.$this->_post['id'].' not found', E_USER_ERROR); trigger_error('AdminGuideResponse - guide #'.$this->_post['id'].' not found', E_USER_ERROR);
@ -63,16 +63,16 @@ class AdminGuideResponse extends TextResponse
private function update(int $id, int $status, ?string $msg = null) : bool private function update(int $id, int $status, ?string $msg = null) : bool
{ {
if ($status == GuideMgr::STATUS_APPROVED) // set display rev to latest if ($status == GuideMgr::STATUS_APPROVED) // set display rev to latest
$ok = DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d, `rev` = (SELECT `rev` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d ORDER BY `rev` DESC LIMIT 1), `approveUserId` = ?d, `approveDate` = ?d WHERE `id` = ?d', $status, Type::GUIDE, $id, User::$id, time(), $id); $ok = DB::Aowow()->qry('UPDATE ::guides SET `status` = %i, `rev` = (SELECT `rev` FROM ::articles WHERE `type` = %i AND `typeId` = %i ORDER BY `rev` DESC LIMIT 1), `approveUserId` = %i, `approveDate` = %i WHERE `id` = %i', $status, Type::GUIDE, $id, User::$id, time(), $id);
else else
$ok = DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d WHERE `id` = ?d', $status, $id); $ok = DB::Aowow()->qry('UPDATE ::guides SET `status` = %i WHERE `id` = %i', $status, $id);
if (!$ok) if (!$ok)
return false; return false;
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `status`) VALUES (?d, ?d, ?d, ?d)', $id, time(), User::$id, $status); DB::Aowow()->qry('INSERT INTO ::guides_changelog (`id`, `date`, `userId`, `status`) VALUES (%i, %i, %i, %i)', $id, time(), User::$id, $status);
if ($msg) if ($msg)
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `msg`) VALUES (?d, ?d, ?d, ?)', $id, time(), User::$id, $msg); DB::Aowow()->qry('INSERT INTO ::guides_changelog (`id`, `date`, `userId`, `msg`) VALUES (%i, %i, %i, %s)', $id, time(), User::$id, $msg);
return true; return true;
} }

View file

@ -30,7 +30,7 @@ class AdminGuidesResponse extends TemplateResponse
else else
{ {
$data = $pending->getListviewData(); $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()); $latest = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, MAX(`rev`) FROM ::articles WHERE `type` = %i AND `typeId` IN %in GROUP BY `rev`', Type::GUIDE, $pending->getFoundIDs());
foreach ($latest as $id => $rev) foreach ($latest as $id => $rev)
$data[$id]['rev'] = $rev; $data[$id]['rev'] = $rev;
} }

View file

@ -52,7 +52,7 @@ class AdminScreenshotsResponse extends TemplateResponse
else if ($this->_get['user']) else if ($this->_get['user'])
{ {
if (mb_strlen($this->_get['user']) >= 3) if (mb_strlen($this->_get['user']) >= 3)
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user'])) if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ::account WHERE LOWER(`username`) = LOWER(%s)', $this->_get['user']))
$ssData = ScreenshotMgr::getScreenshots(userId: $uId, nFound: $nMatches); $ssData = ScreenshotMgr::getScreenshots(userId: $uId, nFound: $nMatches);
} }
else else

View file

@ -2,8 +2,6 @@
namespace Aowow; namespace Aowow;
use GdImage;
if (!defined('AOWOW_REVISION')) if (!defined('AOWOW_REVISION'))
die('illegal access'); die('illegal access');
@ -27,7 +25,7 @@ class AdminScreenshotsActionApproveResponse extends TextResponse
ScreenshotMgr::init(); ScreenshotMgr::init();
// create resized and thumb version of screenshot // create resized and thumb version of screenshot
$ssEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId` FROM ?_screenshots WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_APPROVED, $this->_get['id']); $ssEntries = DB::Aowow()->selectAssoc('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId` FROM ::screenshots WHERE (`status` & %i) = 0 AND `id` IN %in', CC_FLAG_APPROVED, $this->_get['id']);
foreach ($ssEntries as $id => $ssData) foreach ($ssEntries as $id => $ssData)
{ {
if (!ScreenshotMgr::loadFile(ScreenshotMgr::PATH_PENDING, $id)) if (!ScreenshotMgr::loadFile(ScreenshotMgr::PATH_PENDING, $id))
@ -44,14 +42,14 @@ class AdminScreenshotsActionApproveResponse extends TextResponse
continue; continue;
// set as approved in DB // set as approved in DB
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id); DB::Aowow()->qry('UPDATE ::screenshots SET `status` = %i, `userIdApprove` = %i WHERE `id` = %i', CC_FLAG_APPROVED, User::$id, $id);
// gain siterep // gain siterep
Util::gainSiteReputation($ssData['userIdOwner'], SITEREP_ACTION_SUBMIT_SCREENSHOT, ['id' => $id, 'what' => 1, 'date' => $ssData['date']]); Util::gainSiteReputation($ssData['userIdOwner'], SITEREP_ACTION_SUBMIT_SCREENSHOT, ['id' => $id, 'what' => 1, 'date' => $ssData['date']]);
// flag DB entry as having screenshots // flag DB entry as having screenshots
if ($tbl = Type::getClassAttrib($ssData['type'], 'dataTable')) if ($tbl = Type::getClassAttrib($ssData['type'], 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_SCREENSHOT, $ssData['typeId']); DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $tbl, CUSTOM_HAS_SCREENSHOT, $ssData['typeId']);
unset($ssEntries[$id]); unset($ssEntries[$id]);
} }

View file

@ -26,9 +26,9 @@ class AdminScreenshotsActionDeleteResponse extends TextResponse
foreach ($this->_get['id'] as $id) foreach ($this->_get['id'] as $id)
{ {
// irrevocably purge files already flagged as deleted (should only exist as pending) // irrevocably purge files already flagged as deleted (should only exist as pending)
if (User::isInGroup(U_GROUP_ADMIN) && DB::Aowow()->selectCell('SELECT 1 FROM ?_screenshots WHERE `status` & ?d AND `id` = ?d', CC_FLAG_DELETED, $id)) if (User::isInGroup(U_GROUP_ADMIN) && DB::Aowow()->selectCell('SELECT 1 FROM ::screenshots WHERE `status` & %i AND `id` = %i', CC_FLAG_DELETED, $id))
{ {
DB::Aowow()->query('DELETE FROM ?_screenshots WHERE `id` = ?d', $id); DB::Aowow()->qry('DELETE FROM ::screenshots WHERE `id` = %i', $id);
if (file_exists(sprintf(ScreenshotMgr::PATH_PENDING, $id))) if (file_exists(sprintf(ScreenshotMgr::PATH_PENDING, $id)))
unlink(sprintf(ScreenshotMgr::PATH_PENDING, $id)); unlink(sprintf(ScreenshotMgr::PATH_PENDING, $id));
@ -47,16 +47,16 @@ class AdminScreenshotsActionDeleteResponse extends TextResponse
} }
// flag as deleted if not aready // 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']); $oldEntries = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, GROUP_CONCAT(`typeId`) FROM ::screenshots WHERE `id` IN %in GROUP BY `type`', $this->_get['id']);
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = ?d, `userIdDelete` = ?d WHERE `id` IN (?a)', CC_FLAG_DELETED, User::$id, $this->_get['id']); DB::Aowow()->qry('UPDATE ::screenshots SET `status` = %i, `userIdDelete` = %i WHERE `id` IN %in', CC_FLAG_DELETED, User::$id, $this->_get['id']);
// deflag db entry as having screenshots // deflag db entry as having screenshots
foreach ($oldEntries as $type => $typeIds) foreach ($oldEntries as $type => $typeIds)
{ {
$typeIds = explode(',', $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); $toUnflag = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, IF(BIT_OR(`status`) & %i, 1, 0) AS "hasMore" FROM ::screenshots WHERE `type` = %i AND `typeId` IN %in GROUP BY `typeId` HAVING `hasMore` = 0', CC_FLAG_APPROVED, $type, $typeIds);
if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable'))) if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable')))
DB::Aowow()->query('UPDATE ?# SET cuFlags = cuFlags & ~?d WHERE id IN (?a)', $tbl, CUSTOM_HAS_SCREENSHOT, array_keys($toUnflag)); DB::Aowow()->qry('UPDATE %n SET cuFlags = cuFlags & ~%i WHERE id IN %in', $tbl, CUSTOM_HAS_SCREENSHOT, array_keys($toUnflag));
} }
} }
} }

View file

@ -24,7 +24,7 @@ class AdminScreenshotsActionEditaltResponse extends TextResponse
if (!$this->assertGET('id')) if (!$this->assertGET('id'))
return; return;
DB::Aowow()->query('UPDATE ?_screenshots SET `caption` = ? WHERE `id` = ?d', DB::Aowow()->qry('UPDATE ::screenshots SET `caption` = %s WHERE `id` = %i',
$this->handleCaption($this->_post['alt']), $this->handleCaption($this->_post['alt']),
$this->_get['id'] $this->_get['id']
); );

View file

@ -23,7 +23,7 @@ class AdminScreenshotsActionManageResponse extends TextResponse
if ($this->_get['type'] && $this->_get['typeid']) if ($this->_get['type'] && $this->_get['typeid'])
$res = ScreenshotMgr::getScreenshots($this->_get['type'], $this->_get['typeid']); $res = ScreenshotMgr::getScreenshots($this->_get['type'], $this->_get['typeid']);
else if ($this->_get['user']) else if ($this->_get['user'])
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user'])) if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ::account WHERE LOWER(`username`) = LOWER(%s)', $this->_get['user']))
$res = ScreenshotMgr::getScreenshots(userId: $uId); $res = ScreenshotMgr::getScreenshots(userId: $uId);
$this->result = 'ssm_screenshotData = '.Util::toJSON($res); $this->result = 'ssm_screenshotData = '.Util::toJSON($res);

View file

@ -24,7 +24,7 @@ class AdminScreenshotsActionRelocateResponse extends TextResponse
return; return;
} }
[$type, $oldTypeId] = array_values(DB::Aowow()->selectRow('SELECT `type`, `typeId` FROM ?_screenshots WHERE `id` = ?d', $this->_get['id'])); [$type, $oldTypeId] = array_values(DB::Aowow()->selectRow('SELECT `type`, `typeId` FROM ::screenshots WHERE `id` = %i', $this->_get['id']));
$typeId = $this->_get['typeid']; $typeId = $this->_get['typeid'];
if (Type::validateIds($type, $typeId)) if (Type::validateIds($type, $typeId))
@ -32,15 +32,15 @@ class AdminScreenshotsActionRelocateResponse extends TextResponse
$tbl = Type::getClassAttrib($type, 'dataTable'); $tbl = Type::getClassAttrib($type, 'dataTable');
// move screenshot // move screenshot
DB::Aowow()->query('UPDATE ?_screenshots SET `typeId` = ?d WHERE `id` = ?d', $typeId, $this->_get['id']); DB::Aowow()->qry('UPDATE ::screenshots SET `typeId` = %i WHERE `id` = %i', $typeId, $this->_get['id']);
// flag target as having screenshot // flag target as having screenshot
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_SCREENSHOT, $typeId); DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $tbl, CUSTOM_HAS_SCREENSHOT, $typeId);
// deflag source for having had screenshots (maybe) // 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); $ssInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~`status`) & %i, 1, 0) AS "hasMore" FROM ::screenshots WHERE `status`& %i AND `type` = %i AND `typeId` = %i', CC_FLAG_DELETED, CC_FLAG_APPROVED, $type, $oldTypeId);
if ($ssInfo || !$ssInfo['hasMore']) if ($ssInfo || !$ssInfo['hasMore'])
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` & ~?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_SCREENSHOT, $oldTypeId); DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` & ~%i WHERE `id` = %i', $tbl, CUSTOM_HAS_SCREENSHOT, $oldTypeId);
} }
else else
trigger_error('AdminScreenshotsActionRelocateResponse - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR); trigger_error('AdminScreenshotsActionRelocateResponse - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR);

View file

@ -25,7 +25,7 @@ class AdminScreenshotsActionStickyResponse extends TextResponse
// 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 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 // 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 // so, handle it one by one and the last one affecting one particular type/typId-key gets the cake
$ssEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId`, `status` FROM ?_screenshots WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_DELETED, $this->_get['id']); $ssEntries = DB::Aowow()->selectAssoc('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId`, `status` FROM ::screenshots WHERE (`status` & %i) = 0 AND `id` IN %in', CC_FLAG_DELETED, $this->_get['id']);
foreach ($ssEntries as $id => $ssData) foreach ($ssEntries as $id => $ssData)
{ {
// approve yet unapproved screenshots // approve yet unapproved screenshots
@ -47,21 +47,21 @@ class AdminScreenshotsActionStickyResponse extends TextResponse
continue; continue;
// set as approved in DB // set as approved in DB
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id); DB::Aowow()->qry('UPDATE ::screenshots SET `status` = %i, `userIdApprove` = %i WHERE `id` = %i', CC_FLAG_APPROVED, User::$id, $id);
// gain siterep // gain siterep
Util::gainSiteReputation($ssData['userIdOwner'], SITEREP_ACTION_SUBMIT_SCREENSHOT, ['id' => $id, 'what' => 1, 'date' => $ssData['date']]); Util::gainSiteReputation($ssData['userIdOwner'], SITEREP_ACTION_SUBMIT_SCREENSHOT, ['id' => $id, 'what' => 1, 'date' => $ssData['date']]);
// flag DB entry as having screenshots // flag DB entry as having screenshots
if ($tbl = Type::getClassAttrib($ssData['type'], 'dataTable')) if ($tbl = Type::getClassAttrib($ssData['type'], 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_SCREENSHOT, $ssData['typeId']); DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $tbl, CUSTOM_HAS_SCREENSHOT, $ssData['typeId']);
} }
// reset all others // 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); DB::Aowow()->qry('UPDATE ::screenshots a, ::screenshots b SET a.`status` = a.`status` & ~%i WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND a.`id` <> b.`id` AND b.`id` = %i', CC_FLAG_STICKY, $id);
// toggle sticky status // 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); DB::Aowow()->qry('UPDATE ::screenshots SET `status` = IF(`status` & %i, `status` & ~%i, `status` | %i) WHERE `id` = %i AND `status` & %i', CC_FLAG_STICKY, CC_FLAG_STICKY, CC_FLAG_STICKY, $id, CC_FLAG_APPROVED);
unset($ssEntries[$id]); unset($ssEntries[$id]);
} }

View file

@ -2,8 +2,6 @@
namespace Aowow; namespace Aowow;
use Error;
if (!defined('AOWOW_REVISION')) if (!defined('AOWOW_REVISION'))
die('illegal access'); die('illegal access');
@ -47,7 +45,7 @@ class AdminSpawnoverrideResponse extends TextResponse
return; return;
} }
DB::Aowow()->query('REPLACE INTO ?_spawns_override (`type`, `typeGuid`, `areaId`, `floor`, `revision`) VALUES (?d, ?d, ?d, ?d, ?d)', $type, $guid, $area, $floor, AOWOW_REVISION); DB::Aowow()->qry('REPLACE INTO ::spawns_override (`type`, `typeGuid`, `areaId`, `floor`, `revision`) VALUES (%i, %i, %i, %i, %i)', $type, $guid, $area, $floor, AOWOW_REVISION);
$wPos = WorldPosition::getForGUID($type, $guid); $wPos = WorldPosition::getForGUID($type, $guid);
if (!$wPos) if (!$wPos)
@ -74,39 +72,30 @@ class AdminSpawnoverrideResponse extends TextResponse
// if creature try for waypoints // if creature try for waypoints
if ($type == Type::NPC) if ($type == Type::NPC)
{ {
$jobs = array( if ($swp = DB::World()->selectAssoc('SELECT -w.`id` AS "entry", w.`point` AS "pointId", w.`position_x` AS "posX", w.`position_y` AS "posY" FROM creature_addon ca JOIN waypoint_data w ON w.`id` = ca.`path_id` WHERE ca.`guid` = %i AND ca.`path_id` <> 0', $guid))
'SELECT -w.`id` AS "entry", w.`point` AS "pointId", w.`position_x` AS "posX", w.`position_y` AS "posY" FROM creature_addon ca JOIN waypoint_data w ON w.`id` = ca.`path_id` WHERE ca.`guid` = ?d AND ca.`path_id` <> 0',
'SELECT `entry`, `pointId`, `location_x` AS "posX", `location_y` AS "posY" FROM `script_waypoint` WHERE `entry` = ?d',
'SELECT `entry`, `pointId`, `position_x` AS "posX", `position_y` AS "posY" FROM `waypoints` WHERE `entry` = ?d'
);
foreach ($jobs as $idx => $job)
{ {
if ($swp = DB::World()->select($job, $idx ? $wPos[$guid]['id'] : $guid)) foreach ($swp as $w)
{ {
foreach ($swp as $w) if ($point = WorldPosition::toZonePos($wPos[$guid]['mapId'], $w['posX'], $w['posY'], $area, $floor))
{ {
if ($point = WorldPosition::toZonePos($wPos[$guid]['mapId'], $w['posX'], $w['posY'], $area, $floor)) $p = array(
{ 'posX' => $point[0]['posX'],
$p = array( 'posY' => $point[0]['posY'],
'posX' => $point[0]['posX'], 'areaId' => $point[0]['areaId'],
'posY' => $point[0]['posY'], 'floor' => $point[0]['floor']
'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']); DB::Aowow()->qry('UPDATE ::creature_waypoints SET %a WHERE `creatureOrPath` = %i AND `point` = %i', $p, $w['entry'], $w['pointId']);
}
} }
} }
} }
// also move linked vehicle accessories (on the very same position) // 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 $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)); s1.`areaId` = s2.`areaId` AND s1.`floor` = s2.`floor` AND s2.`guid` < 0 WHERE s1.`guid` = %i', $guid));
} }
if (DB::Aowow()->query('UPDATE ?_spawns SET ?a WHERE `type` = ?d AND `guid` IN (?a)', $newPos, $type, $updGUIDs)) if (DB::Aowow()->qry('UPDATE ::spawns SET %a WHERE `type` = %i AND `guid` IN %in', $newPos, $type, $updGUIDs))
$this->result = self::ERR_NONE; $this->result = self::ERR_NONE;
else else
$this->result = self::ERR_WRITE_DB; $this->result = self::ERR_WRITE_DB;

View file

@ -52,7 +52,7 @@ class AdminVideosResponse extends TemplateResponse
else if ($this->_get['user']) else if ($this->_get['user'])
{ {
if (mb_strlen($this->_get['user']) >= 3) if (mb_strlen($this->_get['user']) >= 3)
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user'])) if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ::account WHERE LOWER(`username`) = LOWER(%s)', $this->_get['user']))
$viData = VideoMgr::getVideos(userId: $uId, nFound: $nMatches); $viData = VideoMgr::getVideos(userId: $uId, nFound: $nMatches);
} }
else else

View file

@ -2,8 +2,6 @@
namespace Aowow; namespace Aowow;
use GdImage;
if (!defined('AOWOW_REVISION')) if (!defined('AOWOW_REVISION'))
die('illegal access'); die('illegal access');
@ -24,18 +22,18 @@ class AdminVideosActionApproveResponse extends TextResponse
return; return;
} }
$viEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId` FROM ?_videos WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_APPROVED, $this->_get['id']); $viEntries = DB::Aowow()->selectAssoc('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId` FROM ::videos WHERE (`status` & %i) = 0 AND `id` IN %in', CC_FLAG_APPROVED, $this->_get['id']);
foreach ($viEntries as $id => $viData) foreach ($viEntries as $id => $viData)
{ {
// set as approved in DB // set as approved in DB
DB::Aowow()->query('UPDATE ?_videos SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id); DB::Aowow()->qry('UPDATE ::videos SET `status` = %i, `userIdApprove` = %i WHERE `id` = %i', CC_FLAG_APPROVED, User::$id, $id);
// gain siterep // gain siterep
Util::gainSiteReputation($viData['userIdOwner'], SITEREP_ACTION_SUGGEST_VIDEO, ['id' => $id, 'what' => 1, 'date' => $viData['date']]); Util::gainSiteReputation($viData['userIdOwner'], SITEREP_ACTION_SUGGEST_VIDEO, ['id' => $id, 'what' => 1, 'date' => $viData['date']]);
// flag DB entry as having videos // flag DB entry as having videos
if ($tbl = Type::getClassAttrib($viData['type'], 'dataTable')) if ($tbl = Type::getClassAttrib($viData['type'], 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $viData['typeId']); DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $tbl, CUSTOM_HAS_VIDEO, $viData['typeId']);
unset($viEntries[$id]); unset($viEntries[$id]);
} }

View file

@ -25,19 +25,19 @@ class AdminVideosActionDeleteResponse extends TextResponse
// irrevocably purge files already flagged as deleted (should only exist as pending) // irrevocably purge files already flagged as deleted (should only exist as pending)
if (User::isInGroup(U_GROUP_ADMIN)) if (User::isInGroup(U_GROUP_ADMIN))
DB::Aowow()->selectCell('SELECT 1 FROM ?_videos WHERE `status` & ?d AND `id` IN (?a)', CC_FLAG_DELETED, $this->_get['id']); DB::Aowow()->selectCell('SELECT 1 FROM ::videos WHERE `status` & %i AND `id` IN %in', CC_FLAG_DELETED, $this->_get['id']);
// flag as deleted if not aready // flag as deleted if not aready
$oldEntries = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, GROUP_CONCAT(`typeId`) FROM ?_videos WHERE `id` IN (?a) GROUP BY `type`', $this->_get['id']); $oldEntries = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, GROUP_CONCAT(`typeId`) FROM ::videos WHERE `id` IN %in GROUP BY `type`', $this->_get['id']);
DB::Aowow()->query('UPDATE ?_videos SET `status` = ?d, `userIdDelete` = ?d WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_DELETED, User::$id, CC_FLAG_DELETED, $this->_get['id']); DB::Aowow()->qry('UPDATE ::videos SET `status` = %i, `userIdDelete` = %i WHERE (`status` & %i) = 0 AND `id` IN %in', CC_FLAG_DELETED, User::$id, CC_FLAG_DELETED, $this->_get['id']);
// deflag db entry as having videos // deflag db entry as having videos
foreach ($oldEntries as $type => $typeIds) foreach ($oldEntries as $type => $typeIds)
{ {
$typeIds = explode(',', $typeIds); $typeIds = explode(',', $typeIds);
$toUnflag = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, IF(BIT_OR(`status`) & ?d, 1, 0) AS "hasMore" FROM ?_videos WHERE `type` = ?d AND `typeId` IN (?a) GROUP BY `typeId` HAVING `hasMore` = 0', CC_FLAG_APPROVED, $type, $typeIds); $toUnflag = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, IF(BIT_OR(`status`) & %i, 1, 0) AS "hasMore" FROM ::videos WHERE `type` = %i AND `typeId` IN %in GROUP BY `typeId` HAVING `hasMore` = 0', CC_FLAG_APPROVED, $type, $typeIds);
if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable'))) if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable')))
DB::Aowow()->query('UPDATE ?# SET cuFlags = cuFlags & ~?d WHERE id IN (?a)', $tbl, CUSTOM_HAS_VIDEO, array_keys($toUnflag)); DB::Aowow()->qry('UPDATE %n SET cuFlags = cuFlags & ~%i WHERE id IN %in', $tbl, CUSTOM_HAS_VIDEO, array_keys($toUnflag));
} }
} }
} }

View file

@ -26,6 +26,6 @@ class AdminVideosActionEdittitleResponse extends TextResponse
$caption = $this->handleCaption($this->_post['title']); $caption = $this->handleCaption($this->_post['title']);
DB::Aowow()->query('UPDATE ?_videos SET `caption` = ? WHERE `id` = ?d', $caption, $this->_get['id'][0]); DB::Aowow()->qry('UPDATE ::videos SET `caption` = %s WHERE `id` = %i', $caption, $this->_get['id'][0]);
} }
} }

View file

@ -23,7 +23,7 @@ class AdminVideosActionManageResponse extends TextResponse
if ($this->_get['type'] && $this->_get['typeid']) if ($this->_get['type'] && $this->_get['typeid'])
$res = VideoMgr::getVideos($this->_get['type'], $this->_get['typeid']); $res = VideoMgr::getVideos($this->_get['type'], $this->_get['typeid']);
else if ($this->_get['user']) else if ($this->_get['user'])
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user'])) if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ::account WHERE LOWER(`username`) = LOWER(%s)', $this->_get['user']))
$res = VideoMgr::getVideos(userId: $uId); $res = VideoMgr::getVideos(userId: $uId);
$this->result = 'vim_videoData = '.Util::toJSON($res); $this->result = 'vim_videoData = '.Util::toJSON($res);

View file

@ -25,7 +25,7 @@ class AdminVideosActionOrderResponse extends TextResponse
$id = $this->_get['id'][0]; $id = $this->_get['id'][0];
$videos = DB::Aowow()->selectCol('SELECT a.`id` AS ARRAY_KEY, a.`pos` FROM ?_videos a, ?_videos b WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND (a.`status` & ?d) = 0 AND b.`id` = ?d ORDER BY a.`pos` ASC', CC_FLAG_DELETED, $id); $videos = DB::Aowow()->selectCol('SELECT a.`id` AS ARRAY_KEY, a.`pos` FROM ::videos a, ::videos b WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND (a.`status` & %i) = 0 AND b.`id` = %i ORDER BY a.`pos` ASC', CC_FLAG_DELETED, $id);
if (!$videos || count($videos) == 1) if (!$videos || count($videos) == 1)
{ {
trigger_error('AdminVideosActionOrderResponse - not enough videos to sort', E_USER_WARNING); trigger_error('AdminVideosActionOrderResponse - not enough videos to sort', E_USER_WARNING);
@ -52,6 +52,6 @@ class AdminVideosActionOrderResponse extends TextResponse
$videos[$id] += $dir; $videos[$id] += $dir;
foreach ($videos as $id => $pos) foreach ($videos as $id => $pos)
DB::Aowow()->query('UPDATE ?_videos SET `pos` = ?d WHERE `id` = ?d', $pos, $id); DB::Aowow()->qry('UPDATE ::videos SET `pos` = %i WHERE `id` = %i', $pos, $id);
} }
} }

View file

@ -25,7 +25,7 @@ class AdminVideosActionRelocateResponse extends TextResponse
} }
$id = $this->_get['id'][0]; $id = $this->_get['id'][0];
[$type, $oldTypeId] = array_values(DB::Aowow()->selectRow('SELECT `type`, `typeId` FROM ?_videos WHERE `id` = ?d', $id)); [$type, $oldTypeId] = array_values(DB::Aowow()->selectRow('SELECT `type`, `typeId` FROM ::videos WHERE `id` = %i', $id));
$typeId = $this->_get['typeid']; $typeId = $this->_get['typeid'];
if (Type::validateIds($type, $typeId)) if (Type::validateIds($type, $typeId))
@ -33,15 +33,15 @@ class AdminVideosActionRelocateResponse extends TextResponse
$tbl = Type::getClassAttrib($type, 'dataTable'); $tbl = Type::getClassAttrib($type, 'dataTable');
// move video // move video
DB::Aowow()->query('UPDATE ?_videos SET `typeId` = ?d WHERE `id` = ?d', $typeId, $id); DB::Aowow()->qry('UPDATE ::videos SET `typeId` = %i WHERE `id` = %i', $typeId, $id);
// flag target as having video // flag target as having video
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $typeId); DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $tbl, CUSTOM_HAS_VIDEO, $typeId);
// deflag source for having had videos (maybe) // deflag source for having had videos (maybe)
$viInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~`status`) & ?d, 1, 0) AS "hasMore" FROM ?_videos WHERE `status`& ?d AND `type` = ?d AND `typeId` = ?d', CC_FLAG_DELETED, CC_FLAG_APPROVED, $type, $oldTypeId); $viInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~`status`) & %i, 1, 0) AS "hasMore" FROM ::videos WHERE `status`& %i AND `type` = %i AND `typeId` = %i', CC_FLAG_DELETED, CC_FLAG_APPROVED, $type, $oldTypeId);
if ($viInfo || !$viInfo['hasMore']) if ($viInfo || !$viInfo['hasMore'])
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` & ~?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $oldTypeId); DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` & ~%i WHERE `id` = %i', $tbl, CUSTOM_HAS_VIDEO, $oldTypeId);
} }
else else
trigger_error('AdminVideosActionRelocateResponse - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR); trigger_error('AdminVideosActionRelocateResponse - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR);

View file

@ -24,28 +24,28 @@ class AdminVideosActionStickyResponse extends TextResponse
// this one is a bit strange: as far as i've seen, the only thing a 'sticky' video does is show up in the infobox // this one is a bit strange: as far as i've seen, the only thing a 'sticky' video does is show up in the infobox
// this also means, that only one video per page should be sticky // this also means, that only one video per page should be sticky
// so, handle it one by one and the last one affecting one particular type/typId-key gets the cake // so, handle it one by one and the last one affecting one particular type/typId-key gets the cake
$viEntries = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId`, `status` FROM ?_videos WHERE (`status` & ?d) = 0 AND `id` IN (?a)', CC_FLAG_DELETED, $this->_get['id']); $viEntries = DB::Aowow()->selectAssoc('SELECT `id` AS ARRAY_KEY, `userIdOwner`, `date`, `type`, `typeId`, `status` FROM ::videos WHERE (`status` & %i) = 0 AND `id` IN %in', CC_FLAG_DELETED, $this->_get['id']);
foreach ($viEntries as $id => $viData) foreach ($viEntries as $id => $viData)
{ {
// approve yet unapproved videos // approve yet unapproved videos
if (!($viData['status'] & CC_FLAG_APPROVED)) if (!($viData['status'] & CC_FLAG_APPROVED))
{ {
// set as approved in DB // set as approved in DB
DB::Aowow()->query('UPDATE ?_videos SET `status` = ?d, `userIdApprove` = ?d WHERE `id` = ?d', CC_FLAG_APPROVED, User::$id, $id); DB::Aowow()->qry('UPDATE ::videos SET `status` = %i, `userIdApprove` = %i WHERE `id` = %i', CC_FLAG_APPROVED, User::$id, $id);
// gain siterep // gain siterep
Util::gainSiteReputation($viData['userIdOwner'], SITEREP_ACTION_SUGGEST_VIDEO, ['id' => $id, 'what' => 1, 'date' => $viData['date']]); Util::gainSiteReputation($viData['userIdOwner'], SITEREP_ACTION_SUGGEST_VIDEO, ['id' => $id, 'what' => 1, 'date' => $viData['date']]);
// flag DB entry as having videos // flag DB entry as having videos
if ($tbl = Type::getClassAttrib($viData['type'], 'dataTable')) if ($tbl = Type::getClassAttrib($viData['type'], 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_VIDEO, $viData['typeId']); DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $tbl, CUSTOM_HAS_VIDEO, $viData['typeId']);
} }
// reset all others // reset all others
DB::Aowow()->query('UPDATE ?_videos a, ?_videos b SET a.`status` = a.`status` & ~?d WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND a.`id` <> b.`id` AND b.`id` = ?d', CC_FLAG_STICKY, $id); DB::Aowow()->qry('UPDATE ::videos a, ::videos b SET a.`status` = a.`status` & ~%i WHERE a.`type` = b.`type` AND a.`typeId` = b.`typeId` AND a.`id` <> b.`id` AND b.`id` = %i', CC_FLAG_STICKY, $id);
// toggle sticky status // toggle sticky status
DB::Aowow()->query('UPDATE ?_videos SET `status` = IF(`status` & ?d, `status` & ~?d, `status` | ?d) WHERE `id` = ?d AND `status` & ?d', CC_FLAG_STICKY, CC_FLAG_STICKY, CC_FLAG_STICKY, $id, CC_FLAG_APPROVED); DB::Aowow()->qry('UPDATE ::videos SET `status` = IF(`status` & %i, `status` & ~%i, `status` | %i) WHERE `id` = %i AND `status` & %i', CC_FLAG_STICKY, CC_FLAG_STICKY, CC_FLAG_STICKY, $id, CC_FLAG_APPROVED);
unset($viEntries[$id]); unset($viEntries[$id]);
} }

View file

@ -27,8 +27,8 @@ class AdminWeightpresetsResponse extends TemplateResponse
$head = $body = ''; $head = $body = '';
$scales = DB::Aowow()->select('SELECT `class` AS ARRAY_KEY, `id` AS ARRAY_KEY2, `name`, `icon` FROM ?_account_weightscales WHERE `userId` = 0'); $scales = DB::Aowow()->selectAssoc('SELECT `class` AS ARRAY_KEY, `id` AS ARRAY_KEY2, `name`, `icon` FROM ::account_weightscales WHERE `userId` = 0');
$weights = DB::Aowow()->selectCol('SELECT awd.`id` AS ARRAY_KEY, awd.`field` AS ARRAY_KEY2, awd.`val` FROM ?_account_weightscale_data awd JOIN ?_account_weightscales ad ON awd.`id` = ad.`id` WHERE ad.`userId` = 0'); $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) foreach ($scales as $cl => $data)
{ {
$ul = ''; $ul = '';

View file

@ -30,17 +30,17 @@ class AdminWeightpresetsActionSaveResponse extends TextResponse
} }
// save to db // save to db
DB::Aowow()->query('DELETE FROM ?_account_weightscale_data WHERE `id` = ?d', $this->_post['id']); DB::Aowow()->qry('DELETE FROM ::account_weightscale_data WHERE `id` = %i', $this->_post['id']);
DB::Aowow()->query('UPDATE ?_account_weightscales SET `icon`= ? WHERE `id` = ?d', $this->_post['__icon'], $this->_post['id']); DB::Aowow()->qry('UPDATE ::account_weightscales SET `icon`= %s WHERE `id` = %i', $this->_post['__icon'], $this->_post['id']);
foreach (explode(',', $this->_post['scale']) as $s) foreach (explode(',', $this->_post['scale']) as $s)
{ {
[$k, $v] = explode(':', $s); [$k, $v] = explode(':', $s);
if (!in_array($k, Util::$weightScales) || $v < 1) if (!Stat::getWeightJson($k) || $v < 1)
continue; continue;
if (DB::Aowow()->query('INSERT INTO ?_account_weightscale_data VALUES (?d, ?, ?d)', $this->_post['id'], $k, $v) === null) if (DB::Aowow()->qry('INSERT INTO ::account_weightscale_data VALUES (%i, %s, %i)', $this->_post['id'], $k, $v) === null)
{ {
trigger_error('AdminWeightpresetsActionSaveResponse - failed to write to database', E_USER_ERROR); trigger_error('AdminWeightpresetsActionSaveResponse - failed to write to database', E_USER_ERROR);
$this->result = self::ERR_WRITE_DB; $this->result = self::ERR_WRITE_DB;

View file

@ -10,7 +10,7 @@ class AreatriggerBaseResponse extends TemplateResponse implements ICache
{ {
use TrDetailPage, TrCache; use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected int $requiredUserGroup = U_GROUP_STAFF; protected int $requiredUserGroup = U_GROUP_STAFF;
protected string $template = 'detail-page-generic'; protected string $template = 'detail-page-generic';
@ -105,7 +105,7 @@ class AreatriggerBaseResponse extends TemplateResponse implements ICache
// tab: conditions // tab: conditions
$cnd = new Conditions(); $cnd = new Conditions();
$cnd->getBySourceEntry($this->typeId, Conditions::SRC_AREATRIGGER_CLIENT)->prepare(); $cnd->getBySource(Conditions::SRC_AREATRIGGER_CLIENT, entry: $this->typeId)->prepare();
if ($tab = $cnd->toListviewTab()) if ($tab = $cnd->toListviewTab())
{ {
$this->extendGlobalData($cnd->getJsGlobals()); $this->extendGlobalData($cnd->getJsGlobals());

View file

@ -11,7 +11,7 @@ class AreatriggersBaseResponse extends TemplateResponse implements ICache
use TrListPage, TrCache; use TrListPage, TrCache;
protected int $type = Type::AREATRIGGER; protected int $type = Type::AREATRIGGER;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected int $requiredUserGroup = U_GROUP_STAFF; protected int $requiredUserGroup = U_GROUP_STAFF;
protected string $template = 'areatriggers'; protected string $template = 'areatriggers';
@ -23,16 +23,22 @@ class AreatriggersBaseResponse extends TemplateResponse implements ICache
protected array $expectedGET = ['filter' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Filter::PATTERN_PARAM]]]; protected array $expectedGET = ['filter' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Filter::PATTERN_PARAM]]];
protected array $validCats = [0, 1, 2, 3, 4, 5]; protected array $validCats = [0, 1, 2, 3, 4, 5];
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
$this->getCategoryFromUrl($pageParam); $this->getCategoryFromUrl($rawParam);
if (isset($this->category[0])) if (isset($this->category[0]))
$this->forward('?areatriggers&filter=ty='.$this->category[0]); $this->forward('?areatriggers&filter=ty='.$this->category[0]);
parent::__construct($pageParam); parent::__construct($rawParam);
$this->filter = new AreaTriggerListFilter($this->_get['filter'] ?? ''); $this->filter = new AreaTriggerListFilter($this->_get['filter'] ?? '');
if ($this->filter->shouldReload)
{
$_SESSION['error']['fi'] = $this->filter::class;
$get = $this->filter->buildGETParam();
$this->forward('?' . $this->pageName . ($get ? '&filter=' . $get : ''));
}
$this->filterError = $this->filter->error; $this->filterError = $this->filter->error;
} }
@ -40,8 +46,6 @@ class AreatriggersBaseResponse extends TemplateResponse implements ICache
{ {
$this->h1 = Util::ucFirst(Lang::game('areatriggers')); $this->h1 = Util::ucFirst(Lang::game('areatriggers'));
$this->filter->evalCriteria();
$fiForm = $this->filter->values; $fiForm = $this->filter->values;
@ -69,12 +73,10 @@ class AreatriggersBaseResponse extends TemplateResponse implements ICache
$this->redButtons[BUTTON_WOWHEAD] = false; $this->redButtons[BUTTON_WOWHEAD] = false;
$conditions = []; $conditions = [Listview::DEFAULT_SIZE];
if ($_ = $this->filter->getConditions()) if ($_ = $this->filter->getConditions())
$conditions[] = $_; $conditions[] = $_;
$this->filterError = $this->filter->error; // maybe the evalX() caused something
$tabData = []; $tabData = [];
$trigger = new AreaTriggerList($conditions, ['calcTotal' => true]); $trigger = new AreaTriggerList($conditions, ['calcTotal' => true]);
if (!$trigger->error) if (!$trigger->error)
@ -82,9 +84,9 @@ class AreatriggersBaseResponse extends TemplateResponse implements ICache
$tabData['data'] = $trigger->getListviewData(); $tabData['data'] = $trigger->getListviewData();
// create note if search limit was exceeded; overwriting 'note' is intentional // create note if search limit was exceeded; overwriting 'note' is intentional
if ($trigger->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) if ($trigger->getMatches() > Listview::DEFAULT_SIZE)
{ {
$tabData['note'] = sprintf(Util::$tryFilteringEntityString, $trigger->getMatches(), '"'.Lang::game('areatriggers').'"', Cfg::get('SQL_LIMIT_DEFAULT')); $tabData['note'] = sprintf(Util::$tryFilteringEntityString, $trigger->getMatches(), '"'.Lang::game('areatriggers').'"', Listview::DEFAULT_SIZE);
$tabData['_truncated'] = 1; $tabData['_truncated'] = 1;
} }
} }

View file

@ -46,24 +46,26 @@ class ArenateamBaseResponse extends TemplateResponse
// 3 possibilities // 3 possibilities
// 1) already synced to aowow // 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 = DB::Aowow()->selectRow('SELECT `id`, `realmGUID`, `stub` FROM ::profiler_arena_team WHERE `realm` = %i AND `nameUrl` = %s', $this->realmId, Profiler::urlize($this->subjectName)))
{ {
$this->typeId = $subject['id']; $this->typeId = $subject['id'];
if ($subject['cuFlags'] & PROFILER_CU_NEEDS_RESYNC) if ($subject['stub'])
$this->handleIncompleteData(Type::ARENA_TEAM, $subject['realmGUID']); $this->handleIncompleteData(Type::ARENA_TEAM, $subject['realmGUID']);
return; return;
} }
// 2) not yet synced but exists on realm (wont work if we get passed an urlized name, but there is nothing we can do about it) // 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 ($subject = 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))) $subjects = DB::Characters($this->realmId)->selectAssoc('SELECT at.`arenaTeamId` AS "realmGUID", at.`name`, at.`type` FROM arena_team at WHERE at.`name` = %s', $this->subjectName);
if ($subject = array_find($subjects ?: [], fn($x) => Util::lower($x['name']) === Util::lower($this->subjectName)))
{ {
$subject['realm'] = $this->realmId; $subject['realm'] = $this->realmId;
$subject['cuFlags'] = PROFILER_CU_NEEDS_RESYNC; $subject['stub'] = 1;
$subject['nameUrl'] = Profiler::urlize($subject['name']);
// create entry from realm with basic info // create entry from realm with basic info
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team (?#) VALUES (?a)', array_keys($subject), array_values($subject)); DB::Aowow()->qry('INSERT IGNORE INTO ::profiler_arena_team %v', $subject);
$this->handleIncompleteData(Type::ARENA_TEAM, $subject['realmGUID']); $this->handleIncompleteData(Type::ARENA_TEAM, $subject['realmGUID']);
return; return;

View file

@ -13,9 +13,9 @@ class ArenaTeamResyncResponse extends TextResponse
'profile' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']] 'profile' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet']]
); );
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
parent::__construct($pageParam); parent::__construct($rawParam);
if (!Cfg::get('PROFILER_ENABLE')) if (!Cfg::get('PROFILER_ENABLE'))
$this->generate404(); $this->generate404();
@ -32,12 +32,12 @@ class ArenaTeamResyncResponse extends TextResponse
if (!$this->assertGET('id')) if (!$this->assertGET('id'))
return; return;
if ($teams = DB::Aowow()->select('SELECT `realm`, `realmGUID` FROM ?_profiler_arena_team WHERE `id` IN (?a)', $this->_get['id'])) if ($teams = DB::Aowow()->selectAssoc('SELECT `realm`, `realmGUID` FROM ::profiler_arena_team WHERE `id` IN %in', $this->_get['id']))
foreach ($teams as $t) foreach ($teams as $t)
Profiler::scheduleResync(Type::ARENA_TEAM, $t['realm'], $t['realmGUID']); Profiler::scheduleResync(Type::ARENA_TEAM, $t['realm'], $t['realmGUID']);
if ($this->_get['profile']) 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'])) if ($chars = DB::Aowow()->selectAssoc('SELECT `realm`, `realmGUID` FROM ::profiler_profiles p JOIN ::profiler_arena_team_member atm ON atm.`profileId` = p.`id` WHERE atm.`arenaTeamId` IN %in', $this->_get['id']))
foreach ($chars as $c) foreach ($chars as $c)
Profiler::scheduleResync(Type::PROFILE, $c['realm'], $c['realmGUID']); Profiler::scheduleResync(Type::PROFILE, $c['realm'], $c['realmGUID']);

View file

@ -12,9 +12,9 @@ class ArenaTeamStatusResponse extends TextResponse
'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdList']] 'id' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIdList']]
); );
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
parent::__construct($pageParam); parent::__construct($rawParam);
if (!Cfg::get('PROFILER_ENABLE')) if (!Cfg::get('PROFILER_ENABLE'))
$this->generate404(); $this->generate404();

View file

@ -29,14 +29,14 @@ class ArenateamsBaseResponse extends TemplateResponse implements IProfilerList
private int $sumSubjects = 0; private int $sumSubjects = 0;
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
if (!Cfg::get('PROFILER_ENABLE')) if (!Cfg::get('PROFILER_ENABLE'))
$this->generateError(); $this->generateError();
$this->getSubjectFromUrl($pageParam); $this->getSubjectFromUrl($rawParam);
parent::__construct($pageParam); parent::__construct($rawParam);
$realms = []; $realms = [];
foreach (Profiler::getRealms() as $idx => $r) foreach (Profiler::getRealms() as $idx => $r)
@ -51,8 +51,16 @@ class ArenateamsBaseResponse extends TemplateResponse implements IProfilerList
$realms[] = $idx; $realms[] = $idx;
} }
$this->subCat = $pageParam !== '' ? '='.$pageParam : ''; if ($this->category)
$this->subCat = '='.implode('.', $this->category);
$this->filter = new ArenaTeamListFilter($this->_get['filter'] ?? '', ['realms' => $realms]); $this->filter = new ArenaTeamListFilter($this->_get['filter'] ?? '', ['realms' => $realms]);
if ($this->filter->shouldReload)
{
$_SESSION['error']['fi'] = $this->filter::class;
$get = $this->filter->buildGETParam();
$this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : ''));
}
$this->filterError = $this->filter->error; $this->filterError = $this->filter->error;
} }
@ -84,7 +92,7 @@ class ArenateamsBaseResponse extends TemplateResponse implements IProfilerList
/* Main Content */ /* Main Content */
/****************/ /****************/
$conditions = []; $conditions = [Listview::DEFAULT_SIZE];
if (!User::isInGroup(U_GROUP_EMPLOYEE)) if (!User::isInGroup(U_GROUP_EMPLOYEE))
$conditions[] = ['at.seasonGames', 0, '>']; $conditions[] = ['at.seasonGames', 0, '>'];
@ -123,12 +131,12 @@ class ArenateamsBaseResponse extends TemplateResponse implements IProfilerList
$tabData['data'] = $teams->getListviewData(); $tabData['data'] = $teams->getListviewData();
// create note if search limit was exceeded // create note if search limit was exceeded
if ($this->filter->query && $teams->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) if ($this->filter->query && $teams->getMatches() > Listview::DEFAULT_SIZE)
{ {
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound2', $this->sumSubjects, $teams->getMatches()); $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound2', $this->sumSubjects, $teams->getMatches());
$tabData['_truncated'] = 1; $tabData['_truncated'] = 1;
} }
else if ($teams->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) else if ($teams->getMatches() > Listview::DEFAULT_SIZE)
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound', $this->sumSubjects, 0); $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound', $this->sumSubjects, 0);
} }

View file

@ -12,7 +12,7 @@ class ClassBaseResponse extends TemplateResponse implements ICache
private const TC_CLASS_IDS = [null, 8, 3, 1, 5, 4, 9, 6, 2, 7, null, 0]; // see TalentCalc.js private const TC_CLASS_IDS = [null, 8, 3, 1, 5, 4, 9, 6, 2, 7, null, 0]; // see TalentCalc.js
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'detail-page-generic'; protected string $template = 'detail-page-generic';
protected string $pageName = 'class'; protected string $pageName = 'class';
@ -103,6 +103,20 @@ class ClassBaseResponse extends TemplateResponse implements ICache
if ($specList) if ($specList)
$infobox[] = Lang::game('specs').'[ul][li]'.implode('[/li][li]', $specList).'[/li][/ul]'; $infobox[] = Lang::game('specs').'[ul][li]'.implode('[/li][li]', $specList).'[/li][/ul]';
// id
$infobox[] = Lang::chrClass('id') . $this->typeId;
// icon
if ($_ = $this->subject->getField('iconId'))
{
$infobox[] = Util::ucFirst(Lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
$this->extendGlobalIds(Type::ICON, $_);
}
// original name
if (Lang::getLocale() != Locale::EN)
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
if ($infobox) if ($infobox)
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
@ -112,7 +126,6 @@ class ClassBaseResponse extends TemplateResponse implements ICache
/****************/ /****************/
$this->expansion = Util::$expansionString[$this->subject->getField('expansion')]; $this->expansion = Util::$expansionString[$this->subject->getField('expansion')];
$this->headIcons = ['class_'.$cl->json()];
$this->redButtons = array( $this->redButtons = array(
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId], BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
BUTTON_WOWHEAD => true, BUTTON_WOWHEAD => true,
@ -120,6 +133,9 @@ class ClassBaseResponse extends TemplateResponse implements ICache
BUTTON_FORUM => false // todo (low): Cfg::get('BOARD_URL') + X BUTTON_FORUM => false // todo (low): Cfg::get('BOARD_URL') + X
); );
if ($_ = $this->subject->getField('iconString'))
$this->headIcons[] = $_;
/**************/ /**************/
/* Extra Tabs */ /* Extra Tabs */
@ -127,7 +143,7 @@ class ClassBaseResponse extends TemplateResponse implements ICache
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true); $this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
// Tab: Spells (grouped) // tab: spells (grouped)
// '$LANG.tab_armorproficiencies', // '$LANG.tab_armorproficiencies',
// '$LANG.tab_weaponskills', // '$LANG.tab_weaponskills',
// '$LANG.tab_glyphs', // '$LANG.tab_glyphs',
@ -137,19 +153,18 @@ class ClassBaseResponse extends TemplateResponse implements ICache
['s.typeCat', [-13, -11, -2, 7]], ['s.typeCat', [-13, -11, -2, 7]],
[['s.cuFlags', (SPELL_CU_TRIGGERED | CUSTOM_EXCLUDE_FOR_LISTVIEW), '&'], 0], [['s.cuFlags', (SPELL_CU_TRIGGERED | CUSTOM_EXCLUDE_FOR_LISTVIEW), '&'], 0],
[ [
'OR', DB::OR,
// Glyphs, Proficiencies // Glyphs, Proficiencies
['s.reqClassMask', $cl->toMask(), '&'], ['s.reqClassMask', $cl->toMask(), '&'],
// Abilities / Talents // Abilities / Talents
['s.skillLine1', $this->subject->getField('skills')], ['s.skillLine1', $this->subject->getField('skills')],
['AND', ['s.skillLine1', 0, '>'], ['s.skillLine2OrMask', $this->subject->getField('skills')]] [DB::AND, ['s.skillLine1', 0, '>'], ['s.skillLine2OrMask', $this->subject->getField('skills')]]
], ],
[ // last rank or unranked [ // last rank or unranked
'OR', DB::OR,
['s.cuFlags', SPELL_CU_LAST_RANK, '&'], ['s.cuFlags', SPELL_CU_LAST_RANK, '&'],
['s.rankNo', 0] ['s.rankNo', 0]
], ]
Cfg::get('SQL_LIMIT_NONE')
); );
$genSpells = new SpellList($conditions); $genSpells = new SpellList($conditions);
@ -169,13 +184,10 @@ class ClassBaseResponse extends TemplateResponse implements ICache
), SpellList::$brickFile)); ), SpellList::$brickFile));
} }
// Tab: Items (grouped) // tab: items (grouped)
$conditions = array( $conditions = array(
['requiredClass', 0, '>'],
['requiredClass', $cl->toMask(), '&'], ['requiredClass', $cl->toMask(), '&'],
[['requiredClass', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL, '!'], ['itemset', 0]
['itemset', 0],
Cfg::get('SQL_LIMIT_NONE')
); );
$items = new ItemList($conditions); $items = new ItemList($conditions);
@ -200,7 +212,7 @@ class ClassBaseResponse extends TemplateResponse implements ICache
), ItemList::$brickFile)); ), ItemList::$brickFile));
} }
// Tab: Quests // tab: quests
$conditions = array( $conditions = array(
['reqClassMask', $cl->toMask(), '&'], ['reqClassMask', $cl->toMask(), '&'],
[['reqClassMask', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL, '!'] [['reqClassMask', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL, '!']
@ -217,7 +229,7 @@ class ClassBaseResponse extends TemplateResponse implements ICache
), QuestList::$brickFile)); ), QuestList::$brickFile));
} }
// Tab: Itemsets // tab: itemsets
$sets = new ItemsetList(array(['classMask', $cl->toMask(), '&'])); $sets = new ItemsetList(array(['classMask', $cl->toMask(), '&']));
if (!$sets->error) if (!$sets->error)
{ {
@ -231,7 +243,7 @@ class ClassBaseResponse extends TemplateResponse implements ICache
), ItemsetList::$brickFile)); ), ItemsetList::$brickFile));
} }
// Tab: Trainer // tab: trainers
$conditions = array( $conditions = array(
['npcflag', NPC_FLAG_TRAINER | NPC_FLAG_CLASS_TRAINER, '&'], ['npcflag', NPC_FLAG_TRAINER | NPC_FLAG_CLASS_TRAINER, '&'],
['trainerType', 0], // trains class spells ['trainerType', 0], // trains class spells
@ -249,11 +261,33 @@ class ClassBaseResponse extends TemplateResponse implements ICache
), CreatureList::$brickFile)); ), CreatureList::$brickFile));
} }
// Tab: Races // tab: races
$races = new CharRaceList(array(['classMask', $cl->toMask(), '&'])); $races = new CharRaceList(array(['classMask', $cl->toMask(), '&']));
if (!$races->error) if (!$races->error)
$this->lvTabs->addListviewTab(new Listview(['data' => $races->getListviewData()], CharRaceList::$brickFile)); $this->lvTabs->addListviewTab(new Listview(['data' => $races->getListviewData()], CharRaceList::$brickFile));
// tab: criteria-of
$conditions = array(
DB::AND,
['ac.type', ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS],
['ac.value1', $this->typeId]
);
if ($extraCrt = DB::World()->selectCol('SELECT `criteria_id` FROM achievement_criteria_data WHERE `type` IN %in AND `value1` = %i', [ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE, ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE], $this->typeId))
$conditions = [DB::OR, $conditions, ['ac.id', $extraCrt]];
$crtOf = new AchievementList($conditions);
if (!$crtOf->error)
{
$this->extendGlobalData($crtOf->getJSGlobals());
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $crtOf->getListviewData(),
'name' => '$LANG.tab_criteriaof',
'id' => 'criteria-of'
), AchievementList::$brickFile));
}
// tab: condition-for // tab: condition-for
$cnd = new Conditions(); $cnd = new Conditions();
$cnd->getByCondition(Type::CHR_CLASS, $this->typeId)->prepare(); $cnd->getByCondition(Type::CHR_CLASS, $this->typeId)->prepare();

View file

@ -11,18 +11,18 @@ class ClassesBaseResponse extends TemplateResponse implements ICache
use TrListPage, TrCache; use TrListPage, TrCache;
protected int $type = Type::CHR_CLASS; protected int $type = Type::CHR_CLASS;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected string $template = 'list-page-generic'; protected string $template = 'list-page-generic';
protected string $pageName = 'classes'; protected string $pageName = 'classes';
protected ?int $activeTab = parent::TAB_DATABASE; protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 12]; protected array $breadcrumb = [0, 12];
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
$this->getCategoryFromUrl($pageParam); $this->getCategoryFromUrl($rawParam);
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void

View file

@ -29,7 +29,7 @@ class CommentAddreplyResponse extends TextResponse
if (!User::canReply()) if (!User::canReply())
$this->generate404(Lang::main('cannotComment')); $this->generate404(Lang::main('cannotComment'));
if (!$this->_post['commentId'] || !DB::Aowow()->selectCell('SELECT 1 FROM ?_comments WHERE `id` = ?d', $this->_post['commentId'])) if (!$this->_post['commentId'] || !DB::Aowow()->selectCell('SELECT 1 FROM ::comments WHERE `id` = %i', $this->_post['commentId']))
{ {
trigger_error('CommentAddreplyResponse - parent comment #'.$this->_post['commentId'].' does not exist', E_USER_ERROR); trigger_error('CommentAddreplyResponse - parent comment #'.$this->_post['commentId'].' does not exist', E_USER_ERROR);
$this->generate404(Lang::main('intError')); $this->generate404(Lang::main('intError'));
@ -38,7 +38,7 @@ class CommentAddreplyResponse extends TextResponse
if (mb_strlen($this->_post['body']) < CommunityContent::REPLY_LENGTH_MIN || mb_strlen($this->_post['body']) > CommunityContent::REPLY_LENGTH_MAX) if (mb_strlen($this->_post['body']) < CommunityContent::REPLY_LENGTH_MIN || mb_strlen($this->_post['body']) > CommunityContent::REPLY_LENGTH_MAX)
$this->generate404(Lang::main('textLength', [mb_strlen($this->_post['body']), CommunityContent::REPLY_LENGTH_MIN, CommunityContent::REPLY_LENGTH_MAX])); $this->generate404(Lang::main('textLength', [mb_strlen($this->_post['body']), CommunityContent::REPLY_LENGTH_MIN, CommunityContent::REPLY_LENGTH_MAX]));
if (!DB::Aowow()->query('INSERT INTO ?_comments (`userId`, `roles`, `body`, `date`, `replyTo`) VALUES (?d, ?d, ?, UNIX_TIMESTAMP(), ?d)', User::$id, User::$groups, $this->_post['body'], $this->_post['commentId'])) if (!DB::Aowow()->qry('INSERT INTO ::comments (`userId`, `roles`, `body`, `date`, `replyTo`) VALUES (%i, %i, %s, UNIX_TIMESTAMP(), %i)', User::$id, User::$groups, $this->_post['body'], $this->_post['commentId']))
{ {
trigger_error('CommentAddreplyResponse - write to db failed', E_USER_ERROR); trigger_error('CommentAddreplyResponse - write to db failed', E_USER_ERROR);
$this->generate404(Lang::main('intError')); $this->generate404(Lang::main('intError'));

View file

@ -30,7 +30,7 @@ class CommentAddResponse extends TextResponse
// we now have a valid return target // we now have a valid return target
$idOrUrl = $this->_get['typeid']; $idOrUrl = $this->_get['typeid'];
if ($this->_get['type'] == Type::GUIDE) if ($this->_get['type'] == Type::GUIDE)
if ($_ = DB::Aowow()->selectCell('SELECT `url` FROM ?_guides WHERE `id` = ?d', $this->_get['typeid'])) if ($_ = DB::Aowow()->selectCell('SELECT `url` FROM ::guides WHERE `id` = %i', $this->_get['typeid']))
$idOrUrl = $_; $idOrUrl = $_;
$this->redirectTo = '?'.Type::getFileString($this->_get['type']).'='.$idOrUrl.'#comments'; $this->redirectTo = '?'.Type::getFileString($this->_get['type']).'='.$idOrUrl.'#comments';
@ -57,16 +57,16 @@ class CommentAddResponse extends TextResponse
return; return;
} }
if ($postId = DB::Aowow()->query('INSERT INTO ?_comments (`type`, `typeId`, `userId`, `roles`, `body`, `date`) VALUES (?d, ?d, ?d, ?d, ?, UNIX_TIMESTAMP())', $this->_get['type'], $this->_get['typeid'], User::$id, User::$groups, $this->_post['commentbody'])) if ($postId = DB::Aowow()->qry('INSERT INTO ::comments (`type`, `typeId`, `userId`, `roles`, `body`, `date`) VALUES (%i, %i, %i, %i, %s, UNIX_TIMESTAMP())', $this->_get['type'], $this->_get['typeid'], User::$id, User::$groups, $this->_post['commentbody']))
{ {
Util::gainSiteReputation(User::$id, SITEREP_ACTION_COMMENT, ['id' => $postId]); Util::gainSiteReputation(User::$id, SITEREP_ACTION_COMMENT, ['id' => $postId]);
// every comment starts with a rating of +1 and i guess the simplest thing to do is create a db-entry with the system as owner // every comment starts with a rating of +1 and i guess the simplest thing to do is create a db-entry with the system as owner
DB::Aowow()->query('INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, 0, 1)', RATING_COMMENT, $postId); DB::Aowow()->qry('INSERT INTO ::user_ratings (`type`, `entry`, `userId`, `value`) VALUES (%i, %i, 0, 1)', RATING_COMMENT, $postId);
// flag target with hasComment // flag target with hasComment
if ($tbl = Type::getClassAttrib($this->_get['type'], 'dataTable')) if ($tbl = Type::getClassAttrib($this->_get['type'], 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $this->_get['typeid']); DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $tbl, CUSTOM_HAS_COMMENT, $this->_get['typeid']);
return; return;
} }

View file

@ -23,16 +23,13 @@ class CommentDeletereplyResponse extends TextResponse
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : ''); $this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
} }
// flag as deleted (unset sticky (can a reply even be sticky?) $where = [['`id` = %i', $this->_post['id']]];
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d | ?d, `deleteUserId` = ?d, `deleteDate` = UNIX_TIMESTAMP() WHERE `id` = ?d { AND `userId` = ?d }', if (!User::isInGroup(U_GROUP_MODERATOR))
CC_FLAG_STICKY, CC_FLAG_DELETED, $where[] = ['`userId` = %i', User::$id];
User::$id,
$this->_post['id'],
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id
);
if ($ok) // flag as deleted
DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d', RATING_COMMENT, $this->_post['id']); if (DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` | %i, `deleteUserId` = %i, `deleteDate` = UNIX_TIMESTAMP() WHERE %and', CC_FLAG_DELETED, User::$id, $where))
DB::Aowow()->qry('DELETE FROM ::user_ratings WHERE `type` = %i AND `entry` = %i', RATING_COMMENT, $this->_post['id']);
else else
{ {
trigger_error('CommentDeletereplyResponse - deleting reply #'.$this->_post['id'].' by user #'.User::$id.' from db failed', E_USER_ERROR); trigger_error('CommentDeletereplyResponse - deleting reply #'.$this->_post['id'].' by user #'.User::$id.' from db failed', E_USER_ERROR);

View file

@ -24,24 +24,21 @@ class CommentDeleteResponse extends TextResponse
} }
// in theory, there is a username passed alongside if executed from userpage... lets just use the current user (see user.js) // in theory, there is a username passed alongside if executed from userpage... lets just use the current user (see user.js)
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d, `deleteUserId` = ?d, `deleteDate` = UNIX_TIMESTAMP() WHERE `id` IN (?a) { AND `userId` = ?d }', $where = [['`id` IN %in', $this->_post['id']]];
CC_FLAG_DELETED, if (!User::isInGroup(U_GROUP_MODERATOR))
User::$id, $where[] = ['`userId` = %i', User::$id];
$this->_post['id'],
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id
);
// unflag subject: hasComment // flag as deleted; unflag subject: hasComment
if ($ok) if (DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` | %i, `deleteUserId` = %i, `deleteDate` = UNIX_TIMESTAMP() WHERE %and', CC_FLAG_DELETED, User::$id, $where))
{ {
$coInfo = DB::Aowow()->select( $coInfo = DB::Aowow()->selectAssoc(
'SELECT IF(BIT_OR(~b.`flags`) & ?d, 1, 0) AS "0", b.`type` AS "1", b.`typeId` AS "2" FROM ?_comments a JOIN ?_comments b ON a.`type` = b.`type` AND a.`typeId` = b.`typeId` WHERE a.`id` IN (?a) GROUP BY b.`type`, b.`typeId`', 'SELECT IF(BIT_OR(~b.`flags`) & %i, 1, 0) AS "0", b.`type` AS "1", b.`typeId` AS "2" FROM ::comments a JOIN ::comments b ON a.`type` = b.`type` AND a.`typeId` = b.`typeId` WHERE a.`id` IN %in GROUP BY b.`type`, b.`typeId`',
CC_FLAG_DELETED, $this->_post['id'] CC_FLAG_DELETED, $this->_post['id']
); );
foreach ($coInfo as [$hasMore, $type, $typeId]) foreach ($coInfo as [$hasMore, $type, $typeId])
if (!$hasMore && ($tbl = Type::getClassAttrib($type, 'dataTable'))) if (!$hasMore && ($tbl = Type::getClassAttrib($type, 'dataTable')))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` & ~?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $typeId); DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` & ~%i WHERE `id` = %i', $tbl, CUSTOM_HAS_COMMENT, $typeId);
return; return;
} }

View file

@ -23,7 +23,7 @@ class CommentDetachreplyResponse extends TextResponse
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : ''); $this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
} }
DB::Aowow()->query('UPDATE ?_comments c1, ?_comments c2 SET c1.`replyTo` = 0, c1.`type` = c2.`type`, c1.`typeId` = c2.`typeId` WHERE c1.`replyTo` = c2.`id` AND c1.`id` = ?d', $this->_post['id']); DB::Aowow()->qry('UPDATE ::comments c1, ::comments c2 SET c1.`replyTo` = 0, c1.`type` = c2.`type`, c1.`typeId` = c2.`typeId` WHERE c1.`replyTo` = c2.`id` AND c1.`id` = %i', $this->_post['id']);
} }
} }

View file

@ -26,7 +26,7 @@ class CommentDownvotereplyResponse extends TextResponse
if (!User::canDownvote()) if (!User::canDownvote())
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'cannot downvote' : ''); $this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'cannot downvote' : '');
$comment = DB::Aowow()->selectRow('SELECT `userId`, IF(`flags` & ?d, 1, 0) AS "deleted" FROM ?_comments WHERE `id` = ?d', CC_FLAG_DELETED, $this->_post['id']); $comment = DB::Aowow()->selectRow('SELECT `userId`, IF(`flags` & %i, 1, 0) AS "deleted" FROM ::comments WHERE `id` = %i', CC_FLAG_DELETED, $this->_post['id']);
if (!$comment) if (!$comment)
{ {
trigger_error('CommentDownvotereplyResponse - comment #'.$this->_post['id'].' not found in db', E_USER_ERROR); trigger_error('CommentDownvotereplyResponse - comment #'.$this->_post['id'].' not found in db', E_USER_ERROR);
@ -39,15 +39,9 @@ class CommentDownvotereplyResponse extends TextResponse
if ($comment['deleted']) if ($comment['deleted'])
$this->generate404('LANG.votedeleted_tip'); $this->generate404('LANG.votedeleted_tip');
$ok = DB::Aowow()->query( if (is_null(DB::Aowow()->qry('INSERT INTO ::user_ratings (`type`, `entry`, `userId`, `value`) VALUES (%i, %i, %i, %i)',
'INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)', RATING_COMMENT, $this->_post['id'], User::$id, User::canSupervote() ? -2 : -1
RATING_COMMENT, )))
$this->_post['id'],
User::$id,
User::canSupervote() ? -2 : -1
);
if (!$ok)
{ {
trigger_error('CommentDownvotereplyResponse - write to db failed', E_USER_ERROR); trigger_error('CommentDownvotereplyResponse - write to db failed', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'write to db failed' : ''); $this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'write to db failed' : '');

View file

@ -26,7 +26,7 @@ class CommentEditreplyResponse extends TextResponse
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : ''); $this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
} }
$ownerId = DB::Aowow()->selectCell('SELECT `userId` FROM ?_comments WHERE `id` = ?d AND `replyTo` = ?d', $this->_post['replyId'], $this->_post['commentId']); $ownerId = DB::Aowow()->selectCell('SELECT `userId` FROM ::comments WHERE `id` = %i AND `replyTo` = %i', $this->_post['replyId'], $this->_post['commentId']);
if (!User::canReply() || (User::$id != $ownerId && !User::isInGroup(U_GROUP_MODERATOR))) if (!User::canReply() || (User::$id != $ownerId && !User::isInGroup(U_GROUP_MODERATOR)))
$this->generate404(Lang::main('cannotComment')); $this->generate404(Lang::main('cannotComment'));
@ -48,8 +48,11 @@ class CommentEditreplyResponse extends TextResponse
if (User::$id == $ownerId) if (User::$id == $ownerId)
$update['roles'] = User::$groups; $update['roles'] = User::$groups;
if (!DB::Aowow()->query('UPDATE ?_comments SET `editCount` = `editCount` + 1, ?a WHERE `id` = ?d AND `replyTo` = ?d { AND `userId` = ?d }', $where = [['`id` = %i', $this->_post['replyId']], ['`replyTo` = %i', $this->_post['commentId']]];
$update, $this->_post['replyId'], $this->_post['commentId'], User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id)) if (!User::isInGroup(U_GROUP_MODERATOR))
$where[] = ['`userId` = %i', User::$id];
if (!DB::Aowow()->qry('UPDATE ::comments SET `editCount` = `editCount` + 1, %a WHERE %and', $update, $where))
{ {
trigger_error('CommentEditreplyResponse - write to db failed', E_USER_ERROR); trigger_error('CommentEditreplyResponse - write to db failed', E_USER_ERROR);
$this->generate404(Lang::main('intError')); $this->generate404(Lang::main('intError'));

View file

@ -26,7 +26,7 @@ class CommentEditResponse extends TextResponse
return; return;
} }
$ownerId = DB::Aowow()->selectCell('SELECT `userId` FROM ?_comments WHERE `id` = ?d', $this->_get['id']); $ownerId = DB::Aowow()->selectCell('SELECT `userId` FROM ::comments WHERE `id` = %i', $this->_get['id']);
if (!User::canComment() || (User::$id != $ownerId && !User::isInGroup(U_GROUP_MODERATOR))) if (!User::canComment() || (User::$id != $ownerId && !User::isInGroup(U_GROUP_MODERATOR)))
{ {
@ -56,7 +56,7 @@ class CommentEditResponse extends TextResponse
$update['responseRoles'] = User::$groups; $update['responseRoles'] = User::$groups;
} }
DB::Aowow()->query('UPDATE ?_comments SET `editCount` = `editCount` + 1, ?a WHERE `id` = ?d', $update, $this->_get['id']); DB::Aowow()->qry('UPDATE ::comments SET `editCount` = `editCount` + 1, %a WHERE `id` = %i', $update, $this->_get['id']);
} }
} }

View file

@ -23,7 +23,7 @@ class CommentFlagreplyResponse extends TextResponse
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : ''); $this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'request malformed' : '');
} }
$replyOwner = DB::Aowow()->selectCell('SELECT `userId` FROM ?_commments WHERE `id` = ?d', $this->_post['id']); $replyOwner = DB::Aowow()->selectCell('SELECT `userId` FROM ::commments WHERE `id` = %i', $this->_post['id']);
if (!$replyOwner) if (!$replyOwner)
{ {
trigger_error('CommentFlagreplyResponse - reply not found', E_USER_ERROR); trigger_error('CommentFlagreplyResponse - reply not found', E_USER_ERROR);
@ -38,7 +38,7 @@ class CommentFlagreplyResponse extends TextResponse
if (!$report->create('Report Reply Button Click')) if (!$report->create('Report Reply Button Click'))
$this->generate404('LANG.ct_resp_error'.$report->getError()); $this->generate404('LANG.ct_resp_error'.$report->getError());
else if (count($report->getSimilar()) >= CommunityContent::REPORT_THRESHOLD_AUTO_DELETE) else if (count($report->getSimilar()) >= CommunityContent::REPORT_THRESHOLD_AUTO_DELETE)
DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d WHERE `id` = ?d', CC_FLAG_DELETED, $this->_post['id']); DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` | %i WHERE `id` = %i', CC_FLAG_DELETED, $this->_post['id']);
} }
} }

View file

@ -30,9 +30,9 @@ class CommentOutofdateResponse extends TextResponse
if (User::isInGroup(U_GROUP_MODERATOR)) // directly mark as outdated if (User::isInGroup(U_GROUP_MODERATOR)) // directly mark as outdated
{ {
if (!$this->_post['remove']) if (!$this->_post['remove'])
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id']); $ok = DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` | %i WHERE `id` = %i', CC_FLAG_OUTDATED, $this->_post['id']);
else else
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id']); $ok = DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` & ~%i WHERE `id` = %i', CC_FLAG_OUTDATED, $this->_post['id']);
} }
else // try to report as outdated else // try to report as outdated
{ {
@ -41,7 +41,7 @@ class CommentOutofdateResponse extends TextResponse
$this->result = Lang::main('intError'); $this->result = Lang::main('intError');
if (count($report->getSimilar()) >= CommunityContent::REPORT_THRESHOLD_AUTO_OUT_OF_DATE) if (count($report->getSimilar()) >= CommunityContent::REPORT_THRESHOLD_AUTO_OUT_OF_DATE)
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id']); $ok = DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` | %i WHERE `id` = %i', CC_FLAG_OUTDATED, $this->_post['id']);
} }
if (!$ok) if (!$ok)

View file

@ -21,7 +21,7 @@ class CommentRatingResponse extends TextResponse
return; return;
} }
if ($votes = DB::Aowow()->selectRow('SELECT 1 AS "success", SUM(IF(`value` > 0, `value`, 0)) AS "up", SUM(IF(`value` < 0, -`value`, 0)) AS "down" FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND `userId` <> 0 GROUP BY `entry`', RATING_COMMENT, $this->_get['id'])) if ($votes = DB::Aowow()->selectRow('SELECT 1 AS "success", SUM(IF(`value` > 0, `value`, 0)) AS "up", SUM(IF(`value` < 0, -`value`, 0)) AS "down" FROM ::user_ratings WHERE `type` = %i AND `entry` = %i AND `userId` <> 0 GROUP BY `entry`', RATING_COMMENT, $this->_get['id']))
$this->result = Util::toJSON($votes); $this->result = Util::toJSON($votes);
else else
$this->result = Util::toJSON(['success' => 1, 'up' => 0, 'down' => 0]); $this->result = Util::toJSON(['success' => 1, 'up' => 0, 'down' => 0]);

View file

@ -25,9 +25,9 @@ class CommentStickyResponse extends TextResponse
} }
if ($this->_post['sticky']) if ($this->_post['sticky'])
DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d WHERE `id` = ?d', CC_FLAG_STICKY, $this->_post['id']); DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` | %i WHERE `id` = %i', CC_FLAG_STICKY, $this->_post['id']);
else else
DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` = ?d', CC_FLAG_STICKY, $this->_post['id']); DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` & ~%i WHERE `id` = %i', CC_FLAG_STICKY, $this->_post['id']);
} }
} }

View file

@ -24,19 +24,20 @@ class CommentUndeleteResponse extends TextResponse
} }
// in theory, there is a username passed alongside if executed from userpage... lets just use the current user (see user.js) // in theory, there is a username passed alongside if executed from userpage... lets just use the current user (see user.js)
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` IN (?a) { AND `userId` = `deleteUserId` AND `deleteUserId` = ?d }', $where = [['`id` IN %in', $this->_post['id']]];
CC_FLAG_DELETED, if (!User::isInGroup(U_GROUP_MODERATOR))
$this->_post['id'], {
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id $where[] = ['`deleteUserId` = `userId'];
); $where[] = ['`deleteUserId` = %i', User::$id];
}
// unflag subject: hasComment // unflag subject: hasComment
if ($ok) if (DB::Aowow()->qry('UPDATE ::comments SET `flags` = `flags` & ~%i WHERE %and', CC_FLAG_DELETED, $where))
{ {
$coInfo = DB::Aowow()->select('SELECT `type` AS "0", `typeId` AS "1" FROM ?_comments WHERE `id` IN (?a) GROUP BY `type`, `typeId`', $this->_post['id']); $coInfo = DB::Aowow()->selectAssoc('SELECT `type` AS "0", `typeId` AS "1" FROM ::comments WHERE `id` IN %in GROUP BY `type`, `typeId`', $this->_post['id']);
foreach ($coInfo as [$type, $typeId]) foreach ($coInfo as [$type, $typeId])
if ($tbl = Type::getClassAttrib($type, 'dataTable')) if ($tbl = Type::getClassAttrib($type, 'dataTable'))
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $typeId); DB::Aowow()->qry('UPDATE %n SET `cuFlags` = `cuFlags` | %i WHERE `id` = %i', $tbl, CUSTOM_HAS_COMMENT, $typeId);
return; return;
} }

View file

@ -26,7 +26,7 @@ class CommentUpvotereplyResponse extends TextResponse
if (!User::canUpvote()) if (!User::canUpvote())
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'cannot upvote' : ''); $this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'cannot upvote' : '');
$comment = DB::Aowow()->selectRow('SELECT `userId`, IF(`flags` & ?d, 1, 0) AS "deleted" FROM ?_comments WHERE `id` = ?d', CC_FLAG_DELETED, $this->_post['id']); $comment = DB::Aowow()->selectRow('SELECT `userId`, IF(`flags` & %i, 1, 0) AS "deleted" FROM ::comments WHERE `id` = %i', CC_FLAG_DELETED, $this->_post['id']);
if (!$comment) if (!$comment)
{ {
trigger_error('CommentUpvotereplyResponse - comment #'.$this->_post['id'].' not found in db', E_USER_ERROR); trigger_error('CommentUpvotereplyResponse - comment #'.$this->_post['id'].' not found in db', E_USER_ERROR);
@ -39,15 +39,9 @@ class CommentUpvotereplyResponse extends TextResponse
if ($comment['deleted']) if ($comment['deleted'])
$this->generate404('LANG.votedeleted_tip'); $this->generate404('LANG.votedeleted_tip');
$ok = DB::Aowow()->query( if (is_null(DB::Aowow()->qry('INSERT INTO ::user_ratings (`type`, `entry`, `userId`, `value`) VALUES (%i, %i, %i, %i)',
'INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)', RATING_COMMENT, $this->_post['id'], User::$id, User::canSupervote() ? 2 : 1
RATING_COMMENT, )))
$this->_post['id'],
User::$id,
User::canSupervote() ? 2 : 1
);
if (!$ok)
{ {
trigger_error('CommentUpvotereplyResponse - write to db failed', E_USER_ERROR); trigger_error('CommentUpvotereplyResponse - write to db failed', E_USER_ERROR);
$this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'write to db failed' : ''); $this->generate404(User::isInGroup(U_GROUP_STAFF) ? 'write to db failed' : '');

View file

@ -32,7 +32,7 @@ class CommentVoteResponse extends TextResponse
} }
$target = DB::Aowow()->selectRow( $target = DB::Aowow()->selectRow(
'SELECT c.`userId` AS "owner", ur.`value`, IF(c.`flags` & ?d, 1, 0) AS "deleted" FROM ?_comments c LEFT JOIN ?_user_ratings ur ON ur.`type` = ?d AND ur.`entry` = c.id AND ur.`userId` = ?d WHERE c.id = ?d', 'SELECT c.`userId` AS "owner", ur.`value`, IF(c.`flags` & %i, 1, 0) AS "deleted" FROM ::comments c LEFT JOIN ::user_ratings ur ON ur.`type` = %i AND ur.`entry` = c.id AND ur.`userId` = %i WHERE c.id = %i',
CC_FLAG_DELETED, RATING_COMMENT, User::$id, $this->_get['id'] CC_FLAG_DELETED, RATING_COMMENT, User::$id, $this->_get['id']
); );
if (!$target) if (!$target)
@ -62,9 +62,9 @@ class CommentVoteResponse extends TextResponse
$ok = false; $ok = false;
// old and new have same sign; undo vote (user may have gained/lost access to superVote in the meantime) // 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)) 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); $ok = DB::Aowow()->qry('DELETE FROM ::user_ratings WHERE `type` = %i AND `entry` = %i AND `userId` = %i', RATING_COMMENT, $this->_get['id'], User::$id);
else // replace, because we may be overwriting an old, opposing vote else // replace, because we may be overwriting an old, opposing vote
if ($ok = DB::Aowow()->query('REPLACE INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)', RATING_COMMENT, $this->_get['id'], User::$id, $val)) if ($ok = DB::Aowow()->qry('REPLACE INTO ::user_ratings (`type`, `entry`, `userId`, `value`) VALUES (%i, %i, %i, %i)', RATING_COMMENT, $this->_get['id'], User::$id, $val))
User::decrementDailyVotes(); // do not refund retracted votes! User::decrementDailyVotes(); // do not refund retracted votes!
if ($ok) if ($ok)

View file

@ -34,9 +34,9 @@ class CompareBaseResponse extends TemplateResponse
private string $compareString = ''; private string $compareString = '';
public function __construct($pageParam) public function __construct($rawParam)
{ {
parent::__construct($pageParam); parent::__construct($rawParam);
// prefer GET over COOKIE // prefer GET over COOKIE
if ($this->_get['compare']) if ($this->_get['compare'])
@ -106,7 +106,7 @@ class CompareBaseResponse extends TemplateResponse
protected static function checkCompareString(string $val) : string protected static function checkCompareString(string $val) : string
{ {
$val = urldecode($val); $val = urldecode($val);
if (preg_match('/[^\d\.:;]/', $val)) if (preg_match('/[^-?\d\.:;]/', $val))
return ''; return '';
return $val; return $val;

View file

@ -9,15 +9,15 @@ if (!defined('AOWOW_REVISION'))
class ContactusBaseResponse extends TextResponse class ContactusBaseResponse extends TextResponse
{ {
protected array $expectedPOST = array( protected array $expectedPOST = array(
'mode' => ['filter' => FILTER_VALIDATE_INT ], 'mode' => ['filter' => FILTER_VALIDATE_INT ],
'reason' => ['filter' => FILTER_VALIDATE_INT ], 'reason' => ['filter' => FILTER_VALIDATE_INT ],
'ua' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']], 'ua' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'appname' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine']], 'appname' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'page' => ['filter' => FILTER_SANITIZE_URL ], 'page' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']],
'desc' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob']], 'desc' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ],
'id' => ['filter' => FILTER_VALIDATE_INT ], 'id' => ['filter' => FILTER_VALIDATE_INT ],
'relatedurl' => ['filter' => FILTER_SANITIZE_URL ], 'relatedurl' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']],
'email' => ['filter' => FILTER_SANITIZE_EMAIL ] 'email' => ['filter' => FILTER_SANITIZE_EMAIL ]
); );
/* responses /* responses

View file

@ -32,7 +32,7 @@ class CookieBaseResponse extends TextResponse
{ {
if (!$this->param && $this->_get['purge']) if (!$this->param && $this->_get['purge'])
{ {
if (User::$id && DB::Aowow()->query('UPDATE ?_account_cookies SET `data` = "purged" WHERE `userId` = ?d AND `name` LIKE "announcement-%"', User::$id) !== null) if (User::$id && DB::Aowow()->qry('UPDATE ::account_cookies SET `data` = "purged" WHERE `userId` = %i AND `name` LIKE "announcement-%"', User::$id) !== null)
$this->result = 0; $this->result = 0;
return; return;
@ -44,7 +44,7 @@ class CookieBaseResponse extends TextResponse
return; return;
} }
if (DB::Aowow()->query('REPLACE INTO ?_account_cookies VALUES (?d, ?, ?)', User::$id, $this->param, $this->_get[$this->param])) if (DB::Aowow()->qry('REPLACE INTO ::account_cookies VALUES (%i, %s, %s)', User::$id, $this->param, $this->_get[$this->param]))
$this->result = 0; $this->result = 0;
else else
trigger_error('CookieBaseResponse - write to db failed', E_USER_ERROR); trigger_error('CookieBaseResponse - write to db failed', E_USER_ERROR);

View file

@ -11,7 +11,7 @@ class CurrenciesBaseResponse extends TemplateResponse implements ICache
use TrListPage, TrCache; use TrListPage, TrCache;
protected int $type = Type::CURRENCY; protected int $type = Type::CURRENCY;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected string $template = 'list-page-generic'; protected string $template = 'list-page-generic';
protected string $pageName = 'currencies'; protected string $pageName = 'currencies';
@ -20,11 +20,11 @@ class CurrenciesBaseResponse extends TemplateResponse implements ICache
protected array $validCats = [1, 2, 3, 22]; protected array $validCats = [1, 2, 3, 22];
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
$this->getCategoryFromUrl($pageParam); $this->getCategoryFromUrl($rawParam);
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void

View file

@ -10,7 +10,7 @@ class CurrencyBaseResponse extends TemplateResponse implements ICache
{ {
use TrDetailPage, TrCache; use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'detail-page-generic'; protected string $template = 'detail-page-generic';
protected string $pageName = 'currency'; protected string $pageName = 'currency';
@ -72,13 +72,20 @@ class CurrencyBaseResponse extends TemplateResponse implements ICache
if ($_ = $this->subject->getField('cap')) if ($_ = $this->subject->getField('cap'))
$infobox[] = Lang::currency('cap').Lang::nf($_); $infobox[] = Lang::currency('cap').Lang::nf($_);
// id
$infobox[] = Lang::currency('id') . $this->typeId;
// icon // icon
if ($_ = $this->subject->getField('iconId')) if ($_ = $this->subject->getField('iconId'))
{ {
$infobox[] = Util::ucFirst(lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]'; $infobox[] = Util::ucFirst(Lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
$this->extendGlobalIds(Type::ICON, $_); $this->extendGlobalIds(Type::ICON, $_);
} }
// original name
if (Lang::getLocale() != Locale::EN)
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
if ($infobox) if ($infobox)
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
@ -110,9 +117,9 @@ class CurrencyBaseResponse extends TemplateResponse implements ICache
if ($this->typeId != CURRENCY_HONOR_POINTS && $this->typeId != CURRENCY_ARENA_POINTS) if ($this->typeId != CURRENCY_HONOR_POINTS && $this->typeId != CURRENCY_ARENA_POINTS)
{ {
// tabs: this currency is contained in.. // tabs: this currency is contained in..
$lootTabs = new Loot(); $lootTabs = new LootByItem($_relItemId);
if ($lootTabs->getByItem($_relItemId)) if ($lootTabs->getByItem())
{ {
$this->extendGlobalData($lootTabs->jsGlobals); $this->extendGlobalData($lootTabs->jsGlobals);
@ -121,6 +128,15 @@ class CurrencyBaseResponse extends TemplateResponse implements ICache
if ($template == 'npc' || $template == 'object') if ($template == 'npc' || $template == 'object')
$this->addDataLoader('zones'); $this->addDataLoader('zones');
if ($template != 'quest')
{
foreach ($tabData['data'] as &$row)
if (!empty($row['stack']))
$row['currency'] = [[$this->typeId, $row['stack'][0]]];
$tabData['extraCols'][] = '$Listview.extraCols.currency';
}
$this->lvTabs->addListviewTab(new Listview($tabData, $template)); $this->lvTabs->addListviewTab(new Listview($tabData, $template));
} }
} }
@ -182,10 +198,10 @@ class CurrencyBaseResponse extends TemplateResponse implements ICache
} }
} }
// tab: created by (spell) [for items its handled in Loot::getByContainer()] // tab: created by (spell) [for items its handled in LootByItem]
if ($this->typeId == CURRENCY_HONOR_POINTS) if ($this->typeId == CURRENCY_HONOR_POINTS)
{ {
$createdBy = new SpellList(array(['effect1Id', SPELL_EFFECT_ADD_HONOR], ['effect2Id', SPELL_EFFECT_ADD_HONOR], ['effect3Id', SPELL_EFFECT_ADD_HONOR], 'OR')); $createdBy = new SpellList(array(['effect1Id', SPELL_EFFECT_ADD_HONOR], ['effect2Id', SPELL_EFFECT_ADD_HONOR], ['effect3Id', SPELL_EFFECT_ADD_HONOR], DB::OR));
if (!$createdBy->error) if (!$createdBy->error)
{ {
$this->extendGlobalData($createdBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); $this->extendGlobalData($createdBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
@ -221,8 +237,8 @@ class CurrencyBaseResponse extends TemplateResponse implements ICache
if (!$n && !is_null(ItemListFilter::getCriteriaIndex(158, $_relItemId))) if (!$n && !is_null(ItemListFilter::getCriteriaIndex(158, $_relItemId)))
$n = '?items&filter=cr=158;crs='.$_relItemId.';crv=0'; $n = '?items&filter=cr=158;crs='.$_relItemId.';crv=0';
$xCosts = DB::Aowow()->selectCol('SELECT `id` FROM ?_itemextendedcost WHERE '.$w); $xCosts = DB::Aowow()->selectCol('SELECT `id` FROM ::itemextendedcost WHERE '.$w);
$boughtBy = $xCosts ? DB::World()->selectCol('SELECT `item` FROM npc_vendor WHERE `extendedCost` IN (?a) UNION SELECT `item` FROM game_event_npc_vendor WHERE `extendedCost` IN (?a)', $xCosts, $xCosts) : []; $boughtBy = $xCosts ? DB::World()->selectCol('SELECT `item` FROM npc_vendor WHERE `extendedCost` IN %in UNION SELECT `item` FROM game_event_npc_vendor WHERE `extendedCost` IN %in', $xCosts, $xCosts) : [];
if ($boughtBy) if ($boughtBy)
{ {
$boughtBy = new ItemList(array(['id', $boughtBy])); $boughtBy = new ItemList(array(['id', $boughtBy]));

View file

@ -9,7 +9,7 @@ if (!defined('AOWOW_REVISION'))
class DataBaseResponse extends TextResponse class DataBaseResponse extends TextResponse
{ {
protected array $expectedGET = array( protected array $expectedGET = array(
'locale' => ['filter' => FILTER_CALLBACK, 'options' => [Locale::class, 'tryFrom'] ], 'locale' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkLocale' ]],
't' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine' ]], 't' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine' ]],
'catg' => ['filter' => FILTER_VALIDATE_INT ], 'catg' => ['filter' => FILTER_VALIDATE_INT ],
'skill' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkSkill' ]], 'skill' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkSkill' ]],
@ -17,9 +17,9 @@ class DataBaseResponse extends TextResponse
'callback' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkCallback' ]] 'callback' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkCallback' ]]
); );
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
parent::__construct($pageParam); parent::__construct($rawParam);
if ($this->_get['locale']?->validate()) if ($this->_get['locale']?->validate())
Lang::load($this->_get['locale']); Lang::load($this->_get['locale']);
@ -31,7 +31,7 @@ class DataBaseResponse extends TextResponse
foreach ($this->params as $set) foreach ($this->params as $set)
{ {
// requires valid token to hinder automated access // requires valid token to hinder automated access
if ($set != 'item-scaling' && (!$this->_get['t'] || empty($_SESSION['dataKey']) || $this->_get['t'] != $_SESSION['dataKey'])) if ($set != 'item-scaling' && $set != 'spell-scaling' && (!$this->_get['t'] || empty($_SESSION['dataKey']) || $this->_get['t'] != $_SESSION['dataKey']))
{ {
trigger_error('DataBaseResponse::generate - session data key empty or expired', E_USER_ERROR); trigger_error('DataBaseResponse::generate - session data key empty or expired', E_USER_ERROR);
continue; continue;
@ -54,6 +54,7 @@ class DataBaseResponse extends TextResponse
'quick-excludes', 'quick-excludes',
'weight-presets', 'weight-presets',
'item-scaling', 'item-scaling',
'spell-scaling',
'realms', 'realms',
'statistics' => $this->loadAgnosticFile($set), 'statistics' => $this->loadAgnosticFile($set),
// localized // localized

View file

@ -10,7 +10,7 @@ class EmoteBaseResponse extends TemplateResponse implements ICache
{ {
use TrDetailPage, TrCache; use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'detail-page-generic'; protected string $template = 'detail-page-generic';
protected string $pageName = 'emote'; protected string $pageName = 'emote';
@ -94,6 +94,9 @@ class EmoteBaseResponse extends TemplateResponse implements ICache
} }
} }
// id
$infobox[] = Lang::emote('id') . $this->typeId;
if ($infobox) if ($infobox)
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
@ -106,7 +109,7 @@ class EmoteBaseResponse extends TemplateResponse implements ICache
if ($this->subject->getField('cuFlags') & EMOTE_CU_MISSING_CMD) if ($this->subject->getField('cuFlags') & EMOTE_CU_MISSING_CMD)
$text .= Lang::emote('noCommand').'[br][br]'; $text .= Lang::emote('noCommand').'[br][br]';
else if ($aliasses = DB::Aowow()->selectCol('SELECT `command` FROM ?_emotes_aliasses WHERE `id` = ?d AND `locales` & ?d', $this->typeId, 1 << Lang::getLocale()->value)) else if ($aliasses = DB::Aowow()->selectCol('SELECT `command` FROM ::emotes_aliasses WHERE `id` = %i AND `locales` & %i', $this->typeId, 1 << Lang::getLocale()->value))
{ {
$text .= '[h3]'.Lang::emote('aliases').'[/h3][ul]'; $text .= '[h3]'.Lang::emote('aliases').'[/h3][ul]';
foreach ($aliasses as $a) foreach ($aliasses as $a)
@ -181,13 +184,12 @@ class EmoteBaseResponse extends TemplateResponse implements ICache
} }
// tab: sound // tab: sound
$ems = DB::Aowow()->select( $ems = DB::Aowow()->selectAssoc(
'SELECT `soundId` AS ARRAY_KEY, BIT_OR(1 << (`raceId` - 1)) AS "raceMask", BIT_OR(1 << (`gender` - 1)) AS "gender" 'SELECT `soundId` AS ARRAY_KEY, BIT_OR(1 << (`raceId` - 1)) AS "raceMask", BIT_OR(1 << (`gender` - 1)) AS "gender"
FROM ?_emotes_sounds FROM ::emotes_sounds
WHERE `emoteId` = ?d { OR -`emoteId` = ?d } WHERE %if', $this->typeId < 0, '-`emoteId` = %i OR', $this->subject->getField('parentEmote'), '%end `emoteId` = %i
GROUP BY `soundId`', GROUP BY `soundId`',
$this->typeId, $this->typeId,
$this->typeId < 0 ? $this->subject->getField('parentEmote') : DBSIMPLE_SKIP
); );
if ($ems) if ($ems)

View file

@ -10,7 +10,7 @@ class EmotesBaseResponse extends TemplateResponse implements ICache
{ {
use TrListPage, TrCache; use TrListPage, TrCache;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected int $type = Type::EMOTE; protected int $type = Type::EMOTE;
protected string $template = 'list-page-generic'; protected string $template = 'list-page-generic';
@ -18,11 +18,11 @@ class EmotesBaseResponse extends TemplateResponse implements ICache
protected ?int $activeTab = parent::TAB_DATABASE; protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 100]; protected array $breadcrumb = [0, 100];
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
$this->getCategoryFromUrl($pageParam); $this->getCategoryFromUrl($rawParam);
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
@ -41,7 +41,7 @@ class EmotesBaseResponse extends TemplateResponse implements ICache
/* Main Content */ /* Main Content */
/****************/ /****************/
$cnd = [Cfg::get('SQL_LIMIT_NONE')]; $cnd = []; // don't limit, for we have no filter or category
if (!User::isInGroup(U_GROUP_STAFF)) if (!User::isInGroup(U_GROUP_STAFF))
$cnd[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; $cnd[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];

View file

@ -10,7 +10,7 @@ class EnchantmentBaseResponse extends TemplateResponse implements ICache
{ {
use TrDetailPage, TrCache; use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'enchantment'; protected string $template = 'enchantment';
protected string $pageName = 'enchantment'; protected string $pageName = 'enchantment';
@ -85,6 +85,13 @@ class EnchantmentBaseResponse extends TemplateResponse implements ICache
$infobox[] = $foo; $infobox[] = $foo;
} }
// id
$infobox[] = Lang::enchantment('id') . $this->typeId;
// original name
if (Lang::getLocale() != Locale::EN)
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
if ($infobox) if ($infobox)
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
@ -187,10 +194,10 @@ class EnchantmentBaseResponse extends TemplateResponse implements ICache
// used by spell // used by spell
// used by useItem // used by useItem
$cnd = array( $cnd = array(
'OR', DB::OR,
['AND', ['effect1Id', SpellList::EFFECTS_ENCHANTMENT], ['effect1MiscValue', $this->typeId]], [DB::AND, ['effect1Id', SpellList::EFFECTS_ENCHANTMENT], ['effect1MiscValue', $this->typeId]],
['AND', ['effect2Id', SpellList::EFFECTS_ENCHANTMENT], ['effect2MiscValue', $this->typeId]], [DB::AND, ['effect2Id', SpellList::EFFECTS_ENCHANTMENT], ['effect2MiscValue', $this->typeId]],
['AND', ['effect3Id', SpellList::EFFECTS_ENCHANTMENT], ['effect3MiscValue', $this->typeId]], [DB::AND, ['effect3Id', SpellList::EFFECTS_ENCHANTMENT], ['effect3MiscValue', $this->typeId]],
); );
$spellList = new SpellList($cnd); $spellList = new SpellList($cnd);
if (!$spellList->error) if (!$spellList->error)
@ -200,12 +207,12 @@ class EnchantmentBaseResponse extends TemplateResponse implements ICache
$spellIds = $spellList->getFoundIDs(); $spellIds = $spellList->getFoundIDs();
$conditions = array( $conditions = array(
'OR', DB::OR,
['AND', ['spellTrigger1', [SPELL_TRIGGER_USE, SPELL_TRIGGER_USE_NODELAY]], ['spellId1', $spellIds]], [DB::AND, ['spellTrigger1', [SPELL_TRIGGER_USE, SPELL_TRIGGER_USE_NODELAY]], ['spellId1', $spellIds]],
['AND', ['spellTrigger2', [SPELL_TRIGGER_USE, SPELL_TRIGGER_USE_NODELAY]], ['spellId2', $spellIds]], [DB::AND, ['spellTrigger2', [SPELL_TRIGGER_USE, SPELL_TRIGGER_USE_NODELAY]], ['spellId2', $spellIds]],
['AND', ['spellTrigger3', [SPELL_TRIGGER_USE, SPELL_TRIGGER_USE_NODELAY]], ['spellId3', $spellIds]], [DB::AND, ['spellTrigger3', [SPELL_TRIGGER_USE, SPELL_TRIGGER_USE_NODELAY]], ['spellId3', $spellIds]],
['AND', ['spellTrigger4', [SPELL_TRIGGER_USE, SPELL_TRIGGER_USE_NODELAY]], ['spellId4', $spellIds]], [DB::AND, ['spellTrigger4', [SPELL_TRIGGER_USE, SPELL_TRIGGER_USE_NODELAY]], ['spellId4', $spellIds]],
['AND', ['spellTrigger5', [SPELL_TRIGGER_USE, SPELL_TRIGGER_USE_NODELAY]], ['spellId5', $spellIds]] [DB::AND, ['spellTrigger5', [SPELL_TRIGGER_USE, SPELL_TRIGGER_USE_NODELAY]], ['spellId5', $spellIds]]
); );
$ubItems = new ItemList($conditions); $ubItems = new ItemList($conditions);
@ -252,19 +259,19 @@ class EnchantmentBaseResponse extends TemplateResponse implements ICache
} }
// used by randomAttrItem // used by randomAttrItem
$ire = DB::Aowow()->select( $ire = DB::Aowow()->selectAssoc(
'SELECT *, ABS(`id`) AS ARRAY_KEY FROM ?_itemrandomenchant WHERE `enchantId1` = ?d OR `enchantId2` = ?d OR `enchantId3` = ?d OR `enchantId4` = ?d OR `enchantId5` = ?d', 'SELECT *, ABS(`id`) AS ARRAY_KEY FROM ::itemrandomenchant WHERE `enchantId1` = %i OR `enchantId2` = %i OR `enchantId3` = %i OR `enchantId4` = %i OR `enchantId5` = %i',
$this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId
); );
if ($ire) 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))) if ($iet = DB::World()->selectAssoc('SELECT `entry` AS ARRAY_KEY, `ench`, `chance` FROM item_enchantment_template WHERE `ench` IN %in', array_keys($ire)))
{ {
$randIds = []; // transform back to signed format $randIds = []; // transform back to signed format
foreach ($iet as $tplId => $data) foreach ($iet as $tplId => $data)
$randIds[$ire[$data['ench']]['id'] > 0 ? $tplId : -$tplId] = $ire[$data['ench']]['id']; $randIds[$ire[$data['ench']]['id'] > 0 ? $tplId : -$tplId] = $ire[$data['ench']]['id'];
$randItems = new ItemList(array(Cfg::get('SQL_LIMIT_NONE'), ['randomEnchant', array_keys($randIds)])); $randItems = new ItemList(array(['randomEnchant', array_keys($randIds)]));
if (!$randItems->error) if (!$randItems->error)
{ {
$data = $randItems->getListviewData(); $data = $randItems->getListviewData();

View file

@ -11,7 +11,7 @@ class EnchantmentsBaseResponse extends TemplateResponse implements ICache
use TrListPage, TrCache; use TrListPage, TrCache;
protected int $type = Type::ENCHANTMENT; protected int $type = Type::ENCHANTMENT;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected string $template = 'enchantments'; protected string $template = 'enchantments';
protected string $pageName = 'enchantments'; protected string $pageName = 'enchantments';
@ -24,17 +24,25 @@ class EnchantmentsBaseResponse extends TemplateResponse implements ICache
); );
protected array $validCats = [1, 2, 3, 4, 5, 6, 7, 8]; protected array $validCats = [1, 2, 3, 4, 5, 6, 7, 8];
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
$this->getCategoryFromUrl($pageParam); $this->getCategoryFromUrl($rawParam);
parent::__construct($pageParam); parent::__construct($rawParam);
if ($this->category) if ($this->category)
$this->forward('?enchantments&filter=ty='.$this->category[0]); $this->forward('?enchantments&filter=ty='.$this->category[0]);
$this->subCat = $pageParam !== '' ? '='.$pageParam : ''; if ($this->category)
$this->subCat = '='.implode('.', $this->category);
$this->filter = new EnchantmentListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); $this->filter = new EnchantmentListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]);
if ($this->filter->shouldReload)
{
$_SESSION['error']['fi'] = $this->filter::class;
$get = $this->filter->buildGETParam();
$this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : ''));
}
$this->filterError = $this->filter->error; $this->filterError = $this->filter->error;
} }
@ -42,18 +50,13 @@ class EnchantmentsBaseResponse extends TemplateResponse implements ICache
{ {
$this->h1 = Util::ucFirst(Lang::game('enchantments')); $this->h1 = Util::ucFirst(Lang::game('enchantments'));
$this->filter->evalCriteria(); $conditions = [Listview::DEFAULT_SIZE];
$conditions = [];
if (!User::isInGroup(U_GROUP_EMPLOYEE)) if (!User::isInGroup(U_GROUP_EMPLOYEE))
$conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
if ($_ = $this->filter->getConditions()) if ($_ = $this->filter->getConditions())
$conditions[] = $_; $conditions[] = $_;
$this->filterError = $this->filter->error; // maybe the evalX() caused something
/**************/ /**************/
/* Page Title */ /* Page Title */
@ -111,9 +114,9 @@ class EnchantmentsBaseResponse extends TemplateResponse implements ICache
if (!$ench->hasSetFields('skillLine')) if (!$ench->hasSetFields('skillLine'))
$tabData['hiddenCols'] = ['skill']; $tabData['hiddenCols'] = ['skill'];
if ($ench->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) if ($ench->getMatches() > Listview::DEFAULT_SIZE)
{ {
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_enchantmentsfound', $ench->getMatches(), Cfg::get('SQL_LIMIT_DEFAULT')); $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_enchantmentsfound', $ench->getMatches(), Listview::DEFAULT_SIZE);
$tabData['_truncated'] = 1; $tabData['_truncated'] = 1;
} }

View file

@ -10,7 +10,7 @@ class EventBaseResponse extends TemplateResponse implements ICache
{ {
use TrDetailPage, TrCache; use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'detail-page-generic'; protected string $template = 'detail-page-generic';
protected string $pageName = 'event'; protected string $pageName = 'event';
@ -88,9 +88,23 @@ class EventBaseResponse extends TemplateResponse implements ICache
$infobox[] = Lang::npc('rank', 3).Lang::main('colon').'[npc='.$_.']'; $infobox[] = Lang::npc('rank', 3).Lang::main('colon').'[npc='.$_.']';
} }
// display internal id to staff // id
if (User::isInGroup(U_GROUP_STAFF)) $infobox[] = Lang::event('id') . $this->typeId;
$infobox[] = 'Event-Id'.Lang::main('colon').$this->typeId;
// display holiday id to staff
if ($_holidayId && User::isInGroup(U_GROUP_STAFF))
$infobox[] = 'Holiday ID'.Lang::main('colon').$_holidayId;
// icon
if ($_ = $this->subject->getField('iconId'))
{
$infobox[] = Util::ucFirst(Lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
$this->extendGlobalIds(Type::ICON, $_);
}
// original name
if (Lang::getLocale() != Locale::EN)
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
if ($infobox) if ($infobox)
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
@ -100,7 +114,7 @@ class EventBaseResponse extends TemplateResponse implements ICache
/* Main Content */ /* Main Content */
/****************/ /****************/
// no entry in ?_articles? use default HolidayDescription // no entry in ::articles? use default HolidayDescription
if ($_holidayId && empty($this->article)) if ($_holidayId && empty($this->article))
$this->article = new Markup($this->subject->getField('description', true), ['dbpage' => true]); $this->article = new Markup($this->subject->getField('description', true), ['dbpage' => true]);
@ -123,7 +137,7 @@ class EventBaseResponse extends TemplateResponse implements ICache
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true); $this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
// tab: npcs // tab: npcs
if ($npcIds = DB::World()->selectCol('SELECT `id` AS ARRAY_KEY, IF(ec.`eventEntry` > 0, 1, 0) AS "added" FROM creature c, game_event_creature ec WHERE ec.`guid` = c.`guid` AND ABS(ec.`eventEntry`) = ?d', $this->typeId)) if ($npcIds = DB::World()->selectCol('SELECT `id` AS ARRAY_KEY, IF(ec.`eventEntry` > 0, 1, 0) AS "added" FROM creature c, game_event_creature ec WHERE ec.`guid` = c.`guid` AND ABS(ec.`eventEntry`) = %i', $this->typeId))
{ {
$creatures = new CreatureList(array(['id', array_keys($npcIds)])); $creatures = new CreatureList(array(['id', array_keys($npcIds)]));
if (!$creatures->error) if (!$creatures->error)
@ -143,7 +157,7 @@ class EventBaseResponse extends TemplateResponse implements ICache
} }
// tab: objects // tab: objects
if ($objectIds = DB::World()->selectCol('SELECT `id` AS ARRAY_KEY, IF(eg.`eventEntry` > 0, 1, 0) AS "added" FROM gameobject g, game_event_gameobject eg WHERE eg.`guid` = g.`guid` AND ABS(eg.`eventEntry`) = ?d', $this->typeId)) if ($objectIds = DB::World()->selectCol('SELECT `id` AS ARRAY_KEY, IF(eg.`eventEntry` > 0, 1, 0) AS "added" FROM gameobject g, game_event_gameobject eg WHERE eg.`guid` = g.`guid` AND ABS(eg.`eventEntry`) = %i', $this->typeId))
{ {
$objects = new GameObjectList(array(['id', array_keys($objectIds)])); $objects = new GameObjectList(array(['id', array_keys($objectIds)]));
if (!$objects->error) if (!$objects->error)
@ -163,6 +177,7 @@ class EventBaseResponse extends TemplateResponse implements ICache
} }
// tab: achievements // tab: achievements
$exclAcvs = [];
if ($_ = $this->subject->getField('achievementCatOrId')) if ($_ = $this->subject->getField('achievementCatOrId'))
{ {
$condition = $_ > 0 ? [['category', $_]] : [['id', -$_]]; $condition = $_ > 0 ? [['category', $_]] : [['id', -$_]];
@ -176,6 +191,9 @@ class EventBaseResponse extends TemplateResponse implements ICache
'visibleCols' => ['category'] 'visibleCols' => ['category']
); );
// don't reuse for criteria-of tab
$exclAcvs = array_keys($tabData['data']);
if ($_holidayId && AchievementListFilter::getCriteriaIndex(11, $_holidayId)) if ($_holidayId && AchievementListFilter::getCriteriaIndex(11, $_holidayId))
$tabData['note'] = sprintf(Util::$filterResultString, '?achievements&filter=cr=11;crs='.$_holidayId.';crv=0'); $tabData['note'] = sprintf(Util::$filterResultString, '?achievements&filter=cr=11;crs='.$_holidayId.';crv=0');
@ -186,37 +204,54 @@ class EventBaseResponse extends TemplateResponse implements ICache
$itemCnd = []; $itemCnd = [];
if ($_holidayId) if ($_holidayId)
{ {
$itemCnd = array( // tab: criteria-of
'OR', if ($extraCrt = DB::World()->selectCol('SELECT `criteria_id` FROM achievement_criteria_data WHERE `type` = %i AND `value1` = %i', ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY, $_holidayId))
['eventId', $this->typeId], // direct requirement on item
);
// tab: quests (by table, go & creature)
$quests = new QuestList(array(['eventId', $this->typeId]));
if (!$quests->error)
{ {
$this->extendGlobalData($quests->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS)); $condition = array(['ac.id', $extraCrt]);
if ($exclAcvs)
$condition[] = ['a.id', $exclAcvs, '!'];
$tabData = ['data'=> $quests->getListviewData()]; $crtOf = new AchievementList($condition);
if (!$crtOf->error)
{
$this->extendGlobalData($crtOf->getJSGlobals());
if (QuestListFilter::getCriteriaIndex(33, $_holidayId)) $this->lvTabs->addListviewTab(new Listview(array(
$tabData['note'] = sprintf(Util::$filterResultString, '?quests&filter=cr=33;crs='.$_holidayId.';crv=0'); 'data' => $crtOf->getListviewData(),
'name' => '$LANG.tab_criteriaof',
$this->lvTabs->addListviewTab(new Listview($tabData, QuestList::$brickFile)); 'id' => 'criteria-of'
), AchievementList::$brickFile));
$questItems = []; }
foreach (array_column($quests->rewards, Type::ITEM) as $arr)
$questItems = array_merge($questItems, array_keys($arr));
foreach (array_column($quests->choices, Type::ITEM) as $arr)
$questItems = array_merge($questItems, array_keys($arr));
foreach (array_column($quests->requires, Type::ITEM) as $arr)
$questItems = array_merge($questItems, $arr);
if ($questItems)
$itemCnd[] = ['id', $questItems];
} }
$itemCnd[] = ['eventId', $this->typeId]; // direct requirement on item
}
// tab: quests (by table, go & creature)
$quests = new QuestList(array(['eventId', $this->typeId]));
if (!$quests->error)
{
$this->extendGlobalData($quests->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
$tabData = ['data'=> $quests->getListviewData()];
if (QuestListFilter::getCriteriaIndex(33, $_holidayId))
$tabData['note'] = sprintf(Util::$filterResultString, '?quests&filter=cr=33;crs='.$_holidayId.';crv=0');
$this->lvTabs->addListviewTab(new Listview($tabData, QuestList::$brickFile));
$questItems = [];
foreach (array_column($quests->rewards, Type::ITEM) as $arr)
$questItems = array_merge($questItems, array_keys($arr));
foreach (array_column($quests->choices, Type::ITEM) as $arr)
$questItems = array_merge($questItems, array_keys($arr));
foreach (array_column($quests->requires, Type::ITEM) as $arr)
$questItems = array_merge($questItems, $arr);
if ($questItems)
$itemCnd[] = ['id', $questItems];
} }
// items from creature // items from creature
@ -225,9 +260,9 @@ class EventBaseResponse extends TemplateResponse implements ICache
// vendor // vendor
$cIds = $creatures->getFoundIDs(); $cIds = $creatures->getFoundIDs();
if ($sells = DB::World()->selectCol( if ($sells = DB::World()->selectCol(
'SELECT `item` FROM npc_vendor nv WHERE `entry` IN (?a) UNION 'SELECT `item` FROM npc_vendor nv WHERE `entry` IN %in UNION
SELECT nv1.`item` FROM npc_vendor nv1 JOIN npc_vendor nv2 ON -nv1.`entry` = nv2.`item` WHERE nv2.`entry` IN (?a) UNION SELECT nv1.`item` FROM npc_vendor nv1 JOIN npc_vendor nv2 ON -nv1.`entry` = nv2.`item` WHERE nv2.`entry` IN %in UNION
SELECT `item` FROM game_event_npc_vendor genv JOIN creature c ON genv.`guid` = c.`guid` WHERE c.`id` IN (?a)', SELECT `item` FROM game_event_npc_vendor genv JOIN creature c ON genv.`guid` = c.`guid` WHERE c.`id` IN %in',
$cIds, $cIds, $cIds $cIds, $cIds, $cIds
)) ))
$itemCnd[] = ['id', $sells]; $itemCnd[] = ['id', $sells];
@ -237,6 +272,7 @@ class EventBaseResponse extends TemplateResponse implements ICache
// not checking for loot ... cant distinguish between eventLoot and fillerCrapLoot // not checking for loot ... cant distinguish between eventLoot and fillerCrapLoot
if ($itemCnd) if ($itemCnd)
{ {
array_unshift($itemCnd, DB::OR);
$eventItems = new ItemList($itemCnd); $eventItems = new ItemList($itemCnd);
if (!$eventItems->error) if (!$eventItems->error)
{ {
@ -252,7 +288,7 @@ class EventBaseResponse extends TemplateResponse implements ICache
} }
// 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->typeId, $this->typeId, $this->typeId)) if ($rel = DB::World()->selectCol('SELECT IF(`eventEntry` = `prerequisite_event`, NULL, IF(`eventEntry` = %i, `prerequisite_event`, -`eventEntry`)) FROM game_event_prerequisite WHERE `prerequisite_event` = %i OR `eventEntry` = %i', $this->typeId, $this->typeId, $this->typeId))
{ {
if (array_filter($rel, fn($x) => $x === null)) if (array_filter($rel, fn($x) => $x === null))
trigger_error('game_event_prerequisite: this event has itself as prerequisite', E_USER_WARNING); trigger_error('game_event_prerequisite: this event has itself as prerequisite', E_USER_WARNING);
@ -320,7 +356,7 @@ class EventBaseResponse extends TemplateResponse implements ICache
// interval // interval
if ($rec > 0) if ($rec > 0)
$infobox[] = Lang::event('interval').Util::formatTime($rec * 1000); $infobox[] = Lang::event('interval').DateTime::formatTimeElapsed($rec * 1000);
// in progress // in progress
if ($start < time() && $end > time()) if ($start < time() && $end > time())

View file

@ -11,7 +11,7 @@ class EventsBaseResponse extends TemplateResponse implements ICache
use TrListPage, TrCache; use TrListPage, TrCache;
protected int $type = Type::WORLDEVENT; protected int $type = Type::WORLDEVENT;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected string $template = 'list-page-generic'; protected string $template = 'list-page-generic';
protected string $pageName = 'events'; protected string $pageName = 'events';
@ -20,11 +20,11 @@ class EventsBaseResponse extends TemplateResponse implements ICache
protected array $validCats = [0, 1, 2, 3]; protected array $validCats = [0, 1, 2, 3];
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
$this->getCategoryFromUrl($pageParam); $this->getCategoryFromUrl($rawParam);
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
@ -55,7 +55,7 @@ class EventsBaseResponse extends TemplateResponse implements ICache
$this->redButtons[BUTTON_WOWHEAD] = true; $this->redButtons[BUTTON_WOWHEAD] = true;
$condition = []; $condition = [Listview::DEFAULT_SIZE];
if (!User::isInGroup(U_GROUP_EMPLOYEE)) if (!User::isInGroup(U_GROUP_EMPLOYEE))
$condition[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; $condition[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];

View file

@ -10,7 +10,7 @@ class FactionBaseResponse extends TemplateResponse implements ICache
{ {
use TrDetailPage, TrCache; use TrDetailPage, TrCache;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'detail-page-generic'; protected string $template = 'detail-page-generic';
protected string $pageName = 'faction'; protected string $pageName = 'faction';
@ -96,8 +96,25 @@ class FactionBaseResponse extends TemplateResponse implements ICache
if ($_ = $this->subject->getField('side')) if ($_ = $this->subject->getField('side'))
$infobox[] = Lang::main('side').'[span class=icon-'.($_ == SIDE_ALLIANCE ? 'alliance' : 'horde').']'.Lang::game('si', $_).'[/span]'; $infobox[] = Lang::main('side').'[span class=icon-'.($_ == SIDE_ALLIANCE ? 'alliance' : 'horde').']'.Lang::game('si', $_).'[/span]';
if ($infobox) // id
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); $infobox[] = Lang::faction('id') . $this->typeId;
// profiler relateed (note that this is part of the cache. I don't think this is important enough to calc for every view)
if (Cfg::get('PROFILER_ENABLE') && !($this->subject->getField('cuFlags') & CUSTOM_EXCLUDE_FOR_LISTVIEW))
{
$x = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ::profiler_completion_reputation WHERE `exalted` = 1 AND `factionId` = %i', $this->typeId);
$y = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ::profiler_profiles WHERE `custom` = 0 AND `stub` = 0');
$infobox[] = Lang::profiler('attainedBy', [round(($x ?: 0) * 100 / ($y ?: 1))]);
// completion row added by InfoboxMarkup
}
// original name
if (Lang::getLocale() != Locale::EN)
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
if ($infobox) // unsure if this should be tracked (needs data dump in User::getCompletion())
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0', 0);
/****************/ /****************/
@ -115,7 +132,7 @@ class FactionBaseResponse extends TemplateResponse implements ICache
CONCAT_WS(" ", faction1, faction2, faction3, faction4) AS faction, CONCAT_WS(" ", faction1, faction2, faction3, faction4) AS faction,
CONCAT_WS(" ", rate_1, rate_2, rate_3, rate_4) AS rate, CONCAT_WS(" ", rate_1, rate_2, rate_3, rate_4) AS rate,
CONCAT_WS(" ", rank_1, rank_2, rank_3, rank_4) AS rank CONCAT_WS(" ", rank_1, rank_2, rank_3, rank_4) AS rank
FROM reputation_spillover_template WHERE faction = ?d', $this->typeId); FROM reputation_spillover_template WHERE faction = %i', $this->typeId);
*/ */
@ -125,7 +142,7 @@ class FactionBaseResponse extends TemplateResponse implements ICache
); );
if ($p = $this->subject->getField('parentFactionId')) // linked via parent if ($p = $this->subject->getField('parentFactionId')) // linked via parent
$conditions[] = ['OR', ['id', $p], ['parentFactionId', $p]]; $conditions[] = [DB::OR, ['id', $p], ['parentFactionId', $p]];
else // self as parent else // self as parent
$conditions[] = ['parentFactionId', $this->typeId]; $conditions[] = ['parentFactionId', $this->typeId];
@ -145,7 +162,7 @@ class FactionBaseResponse extends TemplateResponse implements ICache
); );
// reward rates (ultimately this should be calculated into each reward display) // reward rates (ultimately this should be calculated into each reward display)
if ($rates = DB::World()->selectRow('SELECT `quest_rate`, `quest_daily_rate`, `quest_weekly_rate`, `quest_monthly_rate`, `quest_repeatable_rate`, `creature_rate`, `spell_rate` FROM reputation_reward_rate WHERE `faction` = ?d', $this->typeId)) if ($rates = DB::World()->selectRow('SELECT `quest_rate`, `quest_daily_rate`, `quest_weekly_rate`, `quest_monthly_rate`, `quest_repeatable_rate`, `creature_rate`, `spell_rate` FROM reputation_reward_rate WHERE `faction` = %i', $this->typeId))
{ {
$buff = ''; $buff = '';
foreach ($rates as $k => $v) foreach ($rates as $k => $v)
@ -174,7 +191,7 @@ class FactionBaseResponse extends TemplateResponse implements ICache
} }
// factionchange-equivalent // factionchange-equivalent
if ($pendant = DB::World()->selectCell('SELECT IF(`horde_id` = ?d, `alliance_id`, -`horde_id`) FROM player_factionchange_reputations WHERE `alliance_id` = ?d OR `horde_id` = ?d', $this->typeId, $this->typeId, $this->typeId)) if ($pendant = DB::World()->selectCell('SELECT IF(`horde_id` = %i, `alliance_id`, -`horde_id`) FROM player_factionchange_reputations WHERE `alliance_id` = %i OR `horde_id` = %i', $this->typeId, $this->typeId, $this->typeId))
{ {
$altFac = new FactionList(array(['id', abs($pendant)])); $altFac = new FactionList(array(['id', abs($pendant)]));
if (!$altFac->error) if (!$altFac->error)
@ -196,7 +213,7 @@ class FactionBaseResponse extends TemplateResponse implements ICache
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true); $this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
// tab: items // tab: items
$items = new ItemList(array(['requiredFaction', $this->typeId]), ['calcTotal' => true]); $items = new ItemList(array(Listview::DEFAULT_SIZE, ['requiredFaction', $this->typeId]), ['calcTotal' => true]);
if (!$items->error) if (!$items->error)
{ {
$this->extendGlobalData($items->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($items->getJSGlobals(GLOBALINFO_SELF));
@ -207,8 +224,9 @@ class FactionBaseResponse extends TemplateResponse implements ICache
'sort' => ['standing', 'name'] 'sort' => ['standing', 'name']
); );
if ($items->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) if ($items->getMatches() > Listview::DEFAULT_SIZE)
$tabData['note'] = sprintf(Util::$filterResultString, '?items&filter=cr=17;crs='.$this->typeId.';crv=0'); if (!is_null(ItemListFilter::getCriteriaIndex(17, $this->typeId)))
$tabData['note'] = sprintf(Util::$filterResultString, '?items&filter=cr=17;crs='.$this->typeId.';crv=0');
$this->lvTabs->addListviewTab(new Listview($tabData, ItemList::$brickFile, 'itemStandingCol')); $this->lvTabs->addListviewTab(new Listview($tabData, ItemList::$brickFile, 'itemStandingCol'));
} }
@ -219,16 +237,16 @@ class FactionBaseResponse extends TemplateResponse implements ICache
{ {
// inherit siblings/children from $spillover // inherit siblings/children from $spillover
$cRep = DB::World()->selectCol('SELECT DISTINCT `creature_id` AS ARRAY_KEY, `qty` FROM ( $cRep = DB::World()->selectCol('SELECT DISTINCT `creature_id` AS ARRAY_KEY, `qty` FROM (
SELECT `creature_id`, `RewOnKillRepValue1` as "qty" FROM creature_onkill_reputation WHERE `RewOnKillRepValue1` > 0 AND (`RewOnKillRepFaction1` = ?d { OR (`RewOnKillRepFaction1` IN (?a) AND `IsTeamAward1` <> 0) } ) UNION SELECT `creature_id`, `RewOnKillRepValue1` as "qty" FROM creature_onkill_reputation WHERE `RewOnKillRepValue1` > 0 AND (`RewOnKillRepFaction1` = %i OR (`RewOnKillRepFaction1` IN %in AND `IsTeamAward1` <> 0) ) UNION
SELECT `creature_id`, `RewOnKillRepValue2` as "qty" FROM creature_onkill_reputation WHERE `RewOnKillRepValue2` > 0 AND (`RewOnKillRepFaction2` = ?d { OR (`RewOnKillRepFaction2` IN (?a) AND `IsTeamAward2` <> 0) } ) SELECT `creature_id`, `RewOnKillRepValue2` as "qty" FROM creature_onkill_reputation WHERE `RewOnKillRepValue2` > 0 AND (`RewOnKillRepFaction2` = %i OR (`RewOnKillRepFaction2` IN %in AND `IsTeamAward2` <> 0) )
) x', ) x',
$this->typeId, $spillover->getFoundIDs() ?: DBSIMPLE_SKIP, $this->typeId, $spillover->getFoundIDs() ?: [0],
$this->typeId, $spillover->getFoundIDs() ?: DBSIMPLE_SKIP $this->typeId, $spillover->getFoundIDs() ?: [0]
); );
if ($cRep) if ($cRep)
{ {
$killCreatures = new CreatureList(array(['id', array_keys($cRep)]), ['calcTotal' => true]); $killCreatures = new CreatureList(array(Listview::DEFAULT_SIZE, ['id', array_keys($cRep)]), ['calcTotal' => true]);
if (!$killCreatures->error) if (!$killCreatures->error)
{ {
$data = $killCreatures->getListviewData(); $data = $killCreatures->getListviewData();
@ -241,8 +259,9 @@ class FactionBaseResponse extends TemplateResponse implements ICache
'sort' => ['-reputation', 'name'] 'sort' => ['-reputation', 'name']
); );
if ($killCreatures->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) if ($killCreatures->getMatches() > Listview::DEFAULT_SIZE)
$tabData['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=42;crs='.$this->typeId.';crv=0'); if (!is_null(CreatureListFilter::getCriteriaIndex(42, $this->typeId)))
$tabData['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=42;crs='.$this->typeId.';crv=0');
$this->addDataLoader('zones'); $this->addDataLoader('zones');
$this->lvTabs->addListviewTab(new Listview($tabData, CreatureList::$brickFile, 'npcRepCol')); $this->lvTabs->addListviewTab(new Listview($tabData, CreatureList::$brickFile, 'npcRepCol'));
@ -253,7 +272,7 @@ class FactionBaseResponse extends TemplateResponse implements ICache
// tab: members // tab: members
if ($_ = $this->subject->getField('templateIds')) if ($_ = $this->subject->getField('templateIds'))
{ {
$members = new CreatureList(array(['faction', $_]), ['calcTotal' => true]); $members = new CreatureList(array(Listview::DEFAULT_SIZE, ['faction', $_]), ['calcTotal' => true]);
if (!$members->error) if (!$members->error)
{ {
$tabData = array( $tabData = array(
@ -262,8 +281,9 @@ class FactionBaseResponse extends TemplateResponse implements ICache
'name' => '$LANG.tab_members' 'name' => '$LANG.tab_members'
); );
if ($members->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) if ($members->getMatches() > Listview::DEFAULT_SIZE)
$tabData['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=3;crs='.$this->typeId.';crv=0'); if (!is_null(CreatureListFilter::getCriteriaIndex(3, $this->typeId)))
$tabData['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=3;crs='.$this->typeId.';crv=0');
$this->addDataLoader('zones'); $this->addDataLoader('zones');
$this->lvTabs->addListviewTab(new Listview($tabData, CreatureList::$brickFile)); $this->lvTabs->addListviewTab(new Listview($tabData, CreatureList::$brickFile));
@ -283,12 +303,13 @@ class FactionBaseResponse extends TemplateResponse implements ICache
// tab: quests // tab: quests
$conditions = array( $conditions = array(
['AND', ['rewardFactionId1', $this->typeId], ['rewardFactionValue1', 0, '>']], DB::OR,
['AND', ['rewardFactionId2', $this->typeId], ['rewardFactionValue2', 0, '>']], Listview::DEFAULT_SIZE,
['AND', ['rewardFactionId3', $this->typeId], ['rewardFactionValue3', 0, '>']], [DB::AND, ['rewardFactionId1', $this->typeId], ['rewardFactionValue1', 0, '>']],
['AND', ['rewardFactionId4', $this->typeId], ['rewardFactionValue4', 0, '>']], [DB::AND, ['rewardFactionId2', $this->typeId], ['rewardFactionValue2', 0, '>']],
['AND', ['rewardFactionId5', $this->typeId], ['rewardFactionValue5', 0, '>']], [DB::AND, ['rewardFactionId3', $this->typeId], ['rewardFactionValue3', 0, '>']],
'OR' [DB::AND, ['rewardFactionId4', $this->typeId], ['rewardFactionValue4', 0, '>']],
[DB::AND, ['rewardFactionId5', $this->typeId], ['rewardFactionValue5', 0, '>']]
); );
$quests = new QuestList($conditions, ['calcTotal' => true]); $quests = new QuestList($conditions, ['calcTotal' => true]);
if (!$quests->error) if (!$quests->error)
@ -300,8 +321,9 @@ class FactionBaseResponse extends TemplateResponse implements ICache
'extraCols' => '$_' 'extraCols' => '$_'
); );
if ($quests->getMatches() > Cfg::get('SQL_LIMIT_DEFAULT')) if ($quests->getMatches() > Listview::DEFAULT_SIZE)
$tabData['note'] = sprintf(Util::$filterResultString, '?quests&filter=cr=1;crs='.$this->typeId.';crv=0'); if (!is_null(QuestListFilter::getCriteriaIndex(1, $this->typeId)))
$tabData['note'] = sprintf(Util::$filterResultString, '?quests&filter=cr=1;crs='.$this->typeId.';crv=0');
$this->lvTabs->addListviewTab(new Listview($tabData, QuestList::$brickFile, 'questRepCol')); $this->lvTabs->addListviewTab(new Listview($tabData, QuestList::$brickFile, 'questRepCol'));
} }

View file

@ -11,7 +11,7 @@ class FactionsBaseResponse extends TemplateResponse implements ICache
use TrListPage, TrCache; use TrListPage, TrCache;
protected int $type = Type::FACTION; protected int $type = Type::FACTION;
protected int $cacheType = CACHE_TYPE_PAGE; protected int $cacheType = CACHE_TYPE_LIST_PAGE;
protected string $template = 'list-page-generic'; protected string $template = 'list-page-generic';
protected string $pageName = 'factions'; protected string $pageName = 'factions';
@ -25,11 +25,11 @@ class FactionsBaseResponse extends TemplateResponse implements ICache
0 => true 0 => true
); );
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
$this->getCategoryFromUrl($pageParam); $this->getCategoryFromUrl($rawParam);
parent::__construct($pageParam); parent::__construct($rawParam);
} }
protected function generate() : void protected function generate() : void
@ -71,7 +71,7 @@ class FactionsBaseResponse extends TemplateResponse implements ICache
$this->redButtons[BUTTON_WOWHEAD] = true; $this->redButtons[BUTTON_WOWHEAD] = true;
$conditions = []; $conditions = [Listview::DEFAULT_SIZE];
if (!User::isInGroup(U_GROUP_EMPLOYEE)) // unlisted factions if (!User::isInGroup(U_GROUP_EMPLOYEE)) // unlisted factions
$conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
@ -81,11 +81,11 @@ class FactionsBaseResponse extends TemplateResponse implements ICache
else if (isset($this->category[0])) else if (isset($this->category[0]))
{ {
if ($this->category[0]) if ($this->category[0])
$subs = DB::Aowow()->selectCol('SELECT `id` FROM ?_factions WHERE `parentFactionId` = ?d', $this->category[0]); $subs = DB::Aowow()->selectCol('SELECT `id` FROM ::factions WHERE `parentFactionId` = %i', $this->category[0]);
else else
$subs = [0]; $subs = [0];
$conditions[] = ['OR', ['parentFactionId', $subs], ['id', $subs]]; $conditions[] = [DB::OR, ['parentFactionId', $subs], ['id', $subs]];
} }
$data = []; $data = [];

View file

@ -13,11 +13,11 @@ class FaqBaseResponse extends TemplateResponse
protected ?int $activeTab = parent::TAB_MORE; protected ?int $activeTab = parent::TAB_MORE;
protected array $breadcrumb = [2, 3]; protected array $breadcrumb = [2, 3];
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
parent::__construct($pageParam); parent::__construct($rawParam);
if ($pageParam) if ($rawParam)
$this->generateError(); $this->generateError();
} }

View file

@ -12,21 +12,35 @@ class FilterBaseResponse extends TextResponse
private string $page = ''; private string $page = '';
private ?Filter $filter = null; private ?Filter $filter = null;
public function __construct(string $pageParam) public function __construct(string $rawParam)
{ {
if (!$pageParam) if (!$rawParam)
return; return;
parent::__construct($pageParam); parent::__construct($rawParam);
$catg = null; $catg = $page = null;
if (strstr($pageParam, '=')) if (strstr($rawParam, '='))
[$this->page, $catg] = explode('=', $pageParam); [$page, $catg] = explode('=', $rawParam);
else else
$this->page = $pageParam; $page = $rawParam;
if (!$page || preg_match('/[^a-z\-]/i', $page))
return;
$this->page = strtolower($page);
if ($catg !== null) if ($catg !== null)
$this->catg = explode('.', $catg); {
// category is a string for profiler (region.realm) but not passed through here
foreach (explode('.', $catg) as $c)
{
if (preg_match('/\D/', $c))
break;
$this->catg[] = intval($c);
}
}
$opts = ['parentCats' => $this->catg]; $opts = ['parentCats' => $this->catg];
@ -38,14 +52,22 @@ class FilterBaseResponse extends TextResponse
}; };
// yes, the whole _POST! .. should the input fields be exposed and static so they can be evaluated via BaseResponse::initRequestData() ? // yes, the whole _POST! .. should the input fields be exposed and static so they can be evaluated via BaseResponse::initRequestData() ?
$this->filter = Type::newFilter($fileStr, $_POST, $opts); if (!$this->filter = Type::newFilter($fileStr, $_POST, $opts))
trigger_error('Filter::__construct - tried to init filter from bogus GET data', E_USER_WARNING);
} }
protected function generate() : void protected function generate() : void
{ {
// could not build filter from $this->page > go to front page
if (!$this->filter)
{
$this->redirectTo = '.';
return;
}
$url = '?'.$this->page; $url = '?'.$this->page;
$this->filter?->mergeCat($this->catg); $this->filter->mergeCat($this->catg);
if ($this->catg) if ($this->catg)
$url .= '='.implode('.', $this->catg); $url .= '='.implode('.', $this->catg);
@ -53,7 +75,7 @@ class FilterBaseResponse extends TextResponse
if ($x = $this->filter?->buildGETParam()) if ($x = $this->filter?->buildGETParam())
$url .= '&filter='.$x; $url .= '&filter='.$x;
if ($this->filter?->error) if ($this->filter->error)
$_SESSION['error']['fi'] = $this->filter::class; $_SESSION['error']['fi'] = $this->filter::class;
// do get request // do get request

View file

@ -21,8 +21,9 @@ class GotocommentBaseResponse extends TextResponse
return; return;
} }
// type <> 0 AND typeId <> 0 AND replyTo = 0 for comments // the reputation-history listview only creates go-to-comment links. So either upvoting replies does not grant reputation, or.... bug.?
$comment = DB::Aowow()->selectRow('SELECT `id`, `type`, `typeId` FROM ?_comments WHERE `replyTo` = 0 AND `id` = ?d', $this->_get['id']);
$comment = 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` = %i', $this->_get['id']);
if (!$comment) if (!$comment)
{ {
trigger_error('GotocommentBaseResponse - comment #'.$this->_get['id'].' not found', E_USER_ERROR); trigger_error('GotocommentBaseResponse - comment #'.$this->_get['id'].' not found', E_USER_ERROR);
@ -36,6 +37,8 @@ class GotocommentBaseResponse extends TextResponse
} }
$this->redirectTo = sprintf('?%s=%d#comments:id=%d', Type::getFileString($comment['type']), $comment['typeId'], $comment['id']); $this->redirectTo = sprintf('?%s=%d#comments:id=%d', Type::getFileString($comment['type']), $comment['typeId'], $comment['id']);
if ($comment['id'] != $this->_get['id']) // i am reply
$this->redirectTo .= ':reply='.$this->_get['id'];
} }
} }

View file

@ -22,7 +22,7 @@ class GotoreplyBaseResponse extends TextResponse
} }
// type = typeId = 0 AND replyTo <> 0 for replies // type = typeId = 0 AND replyTo <> 0 for replies
$reply = DB::Aowow()->selectRow('SELECT c.`id`, r.`id` AS "reply", c.`type`, c.`typeId` FROM ?_comments r JOIN ?_comments c ON r.`replyTo` = c.`id` WHERE r.`id` = ?d', $this->_get['id']); $reply = DB::Aowow()->selectRow('SELECT c.`id`, r.`id` AS "reply", c.`type`, c.`typeId` FROM ::comments r JOIN ::comments c ON r.`replyTo` = c.`id` WHERE r.`id` = %i', $this->_get['id']);
if (!$reply) if (!$reply)
{ {
trigger_error('GotoreplyBaseResponse - reply #'.$this->_get['id'].' not found', E_USER_ERROR); trigger_error('GotoreplyBaseResponse - reply #'.$this->_get['id'].' not found', E_USER_ERROR);

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