1134 Commits

Author SHA1 Message Date
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
Sarjuuk
647be4a946 Setup/SQL
* move sql files into its own folder.
 * move outdated updates out of the update folder, sorted by github tags
 * split up the db dump so my editor doesn't try to hang itself if i dare to touch that file
2025-09-25 18:44:48 +02:00
Sarjuuk
92c1c59d3a Template/Update (Cleanup)
* smush leftover changes into a commit
 * create fresh db dump, without dbc placeholders
 * version bump

 ... all done
2025-09-25 16:01:16 +02:00
Sarjuuk
6557e70d5c Template/Update (Part 47)
* split global.js into its components, so it can be reasonably processed by setup
 * make reputation requirements configurable
 * move Markup and Locale back into global.js (removed associated build scripts)
 * extend Icon to display iconId in lightbox popup
2025-09-25 16:01:14 +02:00
Sarjuuk
a48e94cd8b Template/Update (Part 46 - VI)
* account management rework: Delete account
2025-09-25 16:01:06 +02:00
Sarjuuk
1d5539b362 Template/Update (Part 46 - V)
* account management rework: Avatar functionality
 * show avatar at comments (beckported, because no forums)
2025-09-25 16:01:04 +02:00
Sarjuuk
258ac19f0a Template/Update (Part 46 - IV)
* account management rework: Personal Settings functionality
 * email, password, username update
 * email updates now also mails the old address for confirmation
2025-09-25 16:00:47 +02:00
Sarjuuk
8fadce88ad Template/Update (Part 46 - III)
* account management rework: Recovery Options
2025-09-25 16:00:36 +02:00
Sarjuuk
f16479b50c Template/Update (Part 46 - II)
* account management rework: Signup functionality
2025-09-25 16:00:36 +02:00
Sarjuuk
155bf1e4a3 Template/Update (Part 46 - I)
* account management rework: Base
 * create proper account settings page
   - modelviewer preferences
   - show ids in lists
   - announcement purge
   - public description
  * fix broken FKs between aowow_user_ratings and aowow_account
2025-09-25 16:00:30 +02:00
Sarjuuk
ab27976132 Template/Update (Part 45)
* convert misc admin endpoints
2025-09-25 15:59:11 +02:00
Sarjuuk
3f8a1838c0 Template/Update (Part 44)
* convert admin - config
 * test for presence of Memcached on enable
 * allow self-signed certs on domain self test
2025-09-25 15:59:09 +02:00
Sarjuuk
f1b613cfa0 Template/Update (Part 43)
* split 'arena-teams' into separate endpoints
2025-09-25 15:58:56 +02:00
Sarjuuk
398ff16b65 Template/Update (Part 42)
* split 'guilds' into separate endpoints
2025-09-25 15:58:56 +02:00
Sarjuuk
69df20af63 Template/Update (Part 41)
* split 'profiler' into separate endpoints
 * implement profile=avatar endpoint (though it doesn't do a whole lot and isn't referenced (see comments))
2025-09-25 15:58:56 +02:00
Sarjuuk
fef27c58e6 Template/Update (Part 40)
* convert 'guides' (listing, viewing, writing & management)
 * don't allow comments on WIP guides
2025-09-25 15:58:56 +02:00
Sarjuuk
cb523353fd Template/Update (Part 39)
* implement video suggestion & management
2025-09-25 15:58:52 +02:00
Sarjuuk
a369244908 Template/Update (Part 38)
* split Screenshot upload & management into separate endpoints
 * move shared functions to manager classes
 * cleanup javascript
 * move test for config screenshot min size to cfg class
2025-09-25 15:58:17 +02:00
Sarjuuk
3d3e2211e5 Template/Update (Part 37)
* convert dbtype 'sound'
2025-09-25 15:56:51 +02:00
Sarjuuk
3f8d5d90e1 Template/Update (Part 36)
* convert dbtype 'mail'
2025-09-25 15:56:51 +02:00
Sarjuuk
0cf9069eb1 Template/Update (Part 35)
* convert dbtype 'icon'
 * improve on IconlistFilter
2025-09-25 15:56:50 +02:00
Sarjuuk
1672883186 Template/Update (Part 34)
* convert dbtype 'areatrigger'
2025-09-25 15:56:50 +02:00
Sarjuuk
3f7f522d50 Template/Update (Part 33)
* convert dbtype 'zone'
2025-09-25 15:56:50 +02:00
Sarjuuk
d66a863f55 Template/Update (Part 32)
* convert dbtype 'event'
2025-09-25 15:56:50 +02:00
Sarjuuk
e876463f3b Template/Update (Part 31)
* convert dbtype 'quest'
 * make use of separate GlobalStrings for spell rewards
2025-09-25 15:56:50 +02:00
Sarjuuk
253cbcb4d9 Template/Update (Part 30)
* convert dbtype 'object'
2025-09-25 15:56:50 +02:00
Sarjuuk
f17b4f58bf Template/Update (Part 29)
* convert dbtype 'npc'
2025-09-25 15:56:50 +02:00
Sarjuuk
a824bb106c Template/Update (Part 28)
* convert dbtype 'faction'
2025-09-25 15:56:50 +02:00
Sarjuuk
79c937e0a3 Template/Update (Part 27)
* convert dbtype 'currency'
2025-09-25 15:56:49 +02:00
Sarjuuk
f76869ecbe Template/Update (Part 26)
* convert dbtype 'title'
2025-09-25 15:56:49 +02:00
Sarjuuk
e6980ce220 Template/Update (Part 25)
* convert dbtype 'spell'
 * point spell effects on detail page to spells filter
2025-09-25 15:56:49 +02:00
Sarjuuk
3ba0cc4ade Template/Update (Part 24)
* convert dbtype 'race'
2025-09-25 15:56:48 +02:00
Sarjuuk
64ef350e0d Template/Update (Part 23)
* convert dbtype 'skill'
2025-09-25 15:56:48 +02:00
Sarjuuk
cdb7e1e7ec Template/Update (Part 22)
* convert dbtype 'pet'
2025-09-25 15:56:48 +02:00
Sarjuuk
2dd9265700 Template/Update (Part 21)
* convert dbtype 'emotes'
 * in setup use voicemacros as additional aliasses
 * also fix emote text descriptor
2025-09-25 15:56:47 +02:00
Sarjuuk
e33bc9117c Template/Update (Part 20)
* convert dbtype 'class'
2025-09-25 15:56:47 +02:00
Sarjuuk
98a54cd871 Template/Update (Part 19)
* convert dbtype 'achievement'
2025-09-25 15:56:47 +02:00
Sarjuuk
26226e2bad Template/Update (Part 18)
* convert dbtype 'item'
 * StatsContainer::toJson - exclude empty values in listviews + xml
2025-09-25 15:56:47 +02:00
Sarjuuk
11bb5a521b Template/Update (Part 17)
* convert dbtype 'itemset'
2025-09-25 15:56:47 +02:00
Sarjuuk
70e4bca10f Template/Update (Part 16)
* convert amalgamation utility.php into separate endpoints
2025-09-25 15:56:47 +02:00
Sarjuuk
503b9458e0 Template/Update (Part 15)
* convert comment/reply ajax (add, edit, delete, vote, report and management)
   and redirects (comment/reply > db-page)
 * update roles when updating own comment/reply
2025-09-25 15:56:47 +02:00
Sarjuuk
12ef04c634 Template/Update (Part 14)
* convert ajax for site features
   (Profiler exclusions, favorites, custom weights, settings-cookie, contact)
2025-09-25 15:56:47 +02:00
Sarjuuk
d71ab58855 Template/Update (Part 13)
* convert dbtype 'enchantment'
2025-09-25 15:56:47 +02:00
Sarjuuk
2899cc881b Template/Update (Part 12)
* convert user page
 * update db to handle custom avatars
2025-09-25 15:56:42 +02:00
Sarjuuk
b3b790d424 Template/Update (Part 11)
* convert signin/signout functionality
 * implement 'log out all devices' option
2025-09-25 15:55:38 +02:00
Sarjuuk
b3ea80c6cc Template/Update (Part 10)
* convert language switcher
2025-09-25 15:55:38 +02:00
Sarjuuk
d03f482864 Template/Update (Part 9)
* convert filter handler
2025-09-25 15:55:38 +02:00
Sarjuuk
e17cbfe51f Template/Update (Part 8)
* convert maps tool
2025-09-25 15:55:38 +02:00
Sarjuuk
2c1b1196a7 Template/Update (Part 7)
* convert item comparison tool
2025-09-25 15:55:38 +02:00
Sarjuuk
d5275b1bf8 Template/Update (Part 6)
* convert talent calculators
2025-09-25 15:55:38 +02:00
Sarjuuk
24cb218060 Template/Update (Part 5)
* convert data loader
2025-09-25 15:55:37 +02:00
Sarjuuk
1f5152c871 Template/Update (Part 4)
* convert search into separate endpoints
 * move shared functionalty to components
 * NOTE: acceptance of opensearch has waned over the last decade and
         the script should be updated
2025-09-25 15:55:37 +02:00
Sarjuuk
81d9248541 Template/Update (Part 3)
* convert amalgamation more.php into separate endpoints
 * fix url of help articles
2025-09-25 15:55:31 +02:00
Sarjuuk
5713834f90 Template/Update (Part 2)
* convert landing (home) page
2025-09-25 15:32:59 +02:00
Sarjuuk
e943e27b5b Template/Update (Part 1)
* update TrinityCore components to return new Frontend objects
   - SmartAI => Markup
   - Conditions => Data Listview
  * update template files to accept the new Frontend objects
2025-09-25 15:32:59 +02:00
Sarjuuk
226f521439 Template/Endpoints (Base)
* redo page render following the logic of:
      Response ─┬─> TextResponse ─> TextResponseImpl
                └─> TemplateResponse ─> TemplateResponseImpl
    * split up giant files, one per response path
    * caching becomes a trait, implemented where necessary
        * TextResponses (Ajax) can now be cached
    * make use of previously defined php classes for js objects
        * Tabs, Listview, Tooltip, Announcement, Markup, Book, ...
    * \Aowow\Template\PageTemplate is the new class to be cached
    * do not discard error messages generated after vars have been sent to template
      and store in session for display at a later time
    * implement tracking consent management
    * move logic out of template into their respective endpoints
2025-09-25 15:32:18 +02:00
Sarjuuk
aeb84327d6 Template/Endpoints (Prep)
* modernize DB-Types
   - long term: should be split in class that describes the DB-Type and container class that handles multiples
 * make unchanging filter props static, allow lookup of criteria indizes through filter
 * move username/mail/password checks to util and make them usable as input filter
2025-09-25 15:32:16 +02:00
Sarjuuk
8cf0b6243d [Live] Markup/Fixup
* fix replacing tags with names
2025-09-25 15:32:14 +02:00
Sarjuuk
8243be8d8e Spells/Tooltips
* allow spells to scale up to its maxLevel instead of max player level
   (e.g. spell 42891)
2025-09-18 20:20:25 +02:00
Sarjuuk
a4a3876cdc Items/Fixup
* casting the icon string to int may be considered suboptimal
 * fixes eb3b4ca5ec
2025-08-11 23:57:15 +02:00
Sarjuuk
4c89c9061e Filter/Fixup
* lost changes to spells from 16eabb90b6
 * fix criteria access for icons
2025-08-11 19:49:48 +02:00
Sarjuuk
7d8ffdd7da ItemFilter/Fixup
* fix slot panel after 16eabb90b6
2025-08-08 22:22:38 +02:00
Sarjuuk
eb3b4ca5ec Core/Optimization
* avoid using expensive numeric cast for anything but user inputs
   especially within nested loops
 * CharStats aggregation should be about x5 faster
2025-08-08 22:10:55 +02:00
Sarjuuk
0753bfbcf6 ItemFilter/Fixup 16eabb90b6
* fix lost item grouping
 * fix type selector in filter form
 * fix item upgrade search
2025-08-06 14:19:17 +02:00
Sarjuuk
8d7c95378c Core/Cleanup
* set type declarations in DB Wrapper
2025-08-06 01:10:24 +02:00
Sarjuuk
16eabb90b6 Core/Cleanup
* move DBType Filter base to its own file under components
 * modernize class and its children
2025-08-05 21:12:23 +02:00
Sarjuuk
08f0ae711e Misc/Fixup
* fix fetching areatriggers from DB and calculating found matches
 * fix Lang concatenating an array of strings with len < 2
 * don't show debug-id col in picker windows (Summary/Profiler) and sort them by score if able
2025-08-05 21:11:02 +02:00
Sarjuuk
569c9efca4 Setup/Source
* fix copy/paste error breaking item - pvp source query
2025-07-31 18:02:32 +02:00
Sarjuuk
6d3b3e1fcb Misc/Cleanup
* modernize extAuth template and add more help text
 * type declarations + cleanup in kernel
 * respect max col size when logging errors
2025-07-28 19:47:31 +02:00
Sarjuuk
112acb2216 Zones/QuickFacts
* unlink quick facts from articles and store per-row
 * new system allows generic and manual QuickFacts to coexist
 * fill new table with data for zones
 * if someone used static quickFacts .. uh .. good luck?
2025-07-28 00:30:04 +02:00
Sarjuuk
ceec228718 Util/Mails
* make sendMail a member of Util
 * move mail templates from strings to template files
 * enabled debug output to page
2025-07-27 18:37:40 +02:00
Sarjuuk
40b5c992e2 Misc/Fixup
* make time formatting js compatible
 * add auto-discovery for rss feeds
 * fix typos
 * define more magic numbers
2025-07-27 16:42:15 +02:00
Sarjuuk
b35ab67360 Items/Stats
* fixed stats for random enchanted items with scaling enchantments
 * don't send unused 'chance' to Summary tool
2025-07-27 16:42:14 +02:00
Sarjuuk
a99fff46aa User/Sessions
* implement tracking
    * FUTURE: log out all devices for user
    * generally store less info in _SESSION
2025-07-27 16:42:13 +02:00
Sarjuuk
086760b9b1 User/Cleanup
* the great unfuckening of user and displayName
    * `login` is purely used as login with AUTH_MODE_SELF
    * `email` may now also be used to log in (if the system knows it)
    * `username` is purely used for display around the site, and lookups from web context
    * both must exist because of external logins
        a) that may be not unique
        b) you may not want to share with the rest of the world
    * todo: implement rename ( because of b) )
2025-07-27 16:42:13 +02:00
Sarjuuk
bffdb9672e Future/Frontend
* create php classes, each mirroring a js object
 * each frontend class implements __toString and json_serialize and as such can be directly used by the template
 * also allows for sane object creation before js screams in agony

 * usage TBD
2025-07-27 16:42:12 +02:00
Sarjuuk
58412e0491 Titles/Name
* partially revert 398b93e9a7
 * generic "name" is required by CommunityContent system
2025-07-27 16:42:12 +02:00
Sarjuuk
5de9759b90 DBType
* extend functions
   * FilterFactory
   * test ::hasIcon()
   * test ::isRandomSearchable()
2025-07-27 16:42:12 +02:00
Sarjuuk
967841fcb9 Spell/DetailPage
* finally set GCDCat var
 * (a smooth decade later and it turns out it was StartRecoveryCategory all along)
2025-07-27 16:42:12 +02:00
Sarjuuk
3f0d6c2de6 JS
* add clickToCopy functionality
2025-07-26 23:27:11 +02:00
Sarjuuk
0928b1b430 User
* slightly modernize static class
2025-07-26 23:18:59 +02:00
Sarjuuk
0562989196 Fix LocString serialization
.. just implement __serialize, d'uh!
2025-07-26 23:16:07 +02:00
Sarjuuk
06bd7aa665 More/Searchbox
* modernize article
 * (clicktocopy pending cherry-pick)
2025-07-26 23:04:23 +02:00
Sarjuuk
a7cf96307c JS/CSS
* remove vendor-specific styles and replace with generics where necessary
 * remove browser hacks as far as possible (Presto/Trident)
 * remove FontAwesome reference
 * minor cleanup
2025-07-26 20:36:01 +02:00
Sarjuuk
8403f9ffd9 Comments/Replies
* fix load on demand for more than 5 replies per comment
2025-07-26 20:19:21 +02:00
Sarjuuk
fbfb81cd25 Profiler/Talents
* use icon from g_file_specs instead of weightscale data
 * fix detecting tank subspec for class 6
2025-07-24 19:43:09 +02:00
Sarjuuk
7a803a8783 Profiler/Talents
* fix grabbing unused gyph items for spec (like 44432)
2025-07-24 19:43:08 +02:00
Sarjuuk
dfefa914af Misc/Fixup
* remove vintage copy-paste error (that probably didn't do anything) in SmartAI
 * fix well-riped copy-paste error (that definitely did something) in Conditions
2025-07-18 02:29:20 +02:00
Sarjuuk
707dc32495 Util/Sanity
* break closing html tags contained in js
2025-07-07 17:55:11 +02:00
smansesdottk
e7baa27e27 Misc/Typos
* fix typos in README.md
 * cherry-picked from smansesdottk/www-aowow
2025-07-05 20:53:15 +02:00
Sarjuuk
f826e4d68a NPCs/Vendors
* fix npc_vendor self-referencing itself
 * game_event_npc_vendor seems to not allow for self references
2025-07-04 22:29:15 +02:00
Sarjuuk
e173de9a97 Quests/Series
* apply cuFlags to quest series
 * implement firstquestseries, lastquestseries, partseries filters using said flags
2025-06-22 19:31:22 +02:00
Sarjuuk
7c527e6144 Quest/Series
* fix series steps not displaying quests running in parallel for this step
2025-06-20 19:04:22 +02:00
Sarjuuk
74c0727bdb Profiler/ArenaTeams
* filter unused teams explicitly by played game count instead of implicitly by rating
2025-06-17 18:02:56 +02:00
Sarjuuk
cd94a2fa4e Profiler/Misc
* fix realm selection in profiler filters
 * minor cleanup of prQueue
2025-06-17 17:45:49 +02:00
Sarjuuk
91bb53aa1d Profiler/Realms
* skip out of List construction if realms are empty or do not match preselection
 * also prefilter Guilds and Prolfiles Lists for server or region (unless custom profiles)
 * discard ProfileList entries for inaccessible realms
2025-06-13 21:04:32 +02:00
Sarjuuk
069ca27b35 Localization/esES
* having ChatGPT translate missing strings
    * cherry picked examples seem fine, even those with control sequences
2025-06-03 20:50:13 +02:00
Sarjuuk
c3048fe1f8 Misc/Fixup
* test arrays first before use, not the other way round
 * do not try to init local arena team entries if there are no entries in list.
 * fix equally distributing chars/guilds/arenateams across realms for unlimited (0) lists
 * fix double declaration of realms in ArenateamList
2025-06-03 17:13:44 +02:00
Sarjuuk
ee02e70571 Profiler/Sync
* only truncate arena team members for OTHER teams of the given size
2025-06-03 17:02:24 +02:00
Sarjuuk
af69aa8e94 Profiler/ArenaTeams
* fixed LocalArenaTeamList being incompatible with it's inherited getListviewData()
 * also apply region and server limiters in LocalArenaTeamList
2025-06-03 17:02:23 +02:00
Sarjuuk
1de0535629 Mapper/Fixup
* no longer need to provide empty mapperData object on ZoneListPage
2025-05-23 19:59:51 +02:00
Sarjuuk
9b6ed672c4 Misc/Fixup
* FCK BOM
 * ItemsetDetailPage is visible again
2025-05-23 19:04:49 +02:00
Sarjuuk
9d704c5ff9 ItemDetailPage/Vendors
* show mapper for vendor locations
2025-05-20 20:07:32 +02:00
Sarjuuk
6475a9d181 SpellDetailPage/SpellGroups
* get a spells first rank from TC table if able
 * fix display of spell group stack rules tab
2025-05-17 22:01:21 +02:00
Sarjuuk
a8e1e3cf19 Misc/Fixup
* fixed typo when trying to include file for CharStats handling
2025-05-14 21:53:19 +02:00
Sarjuuk
44e0b6c62d Spells/Racial Skills
* fix manually set race mask for expansion races (and one class mask)
 * show applicable classes in race detail page listing
2025-05-13 20:11:20 +02:00
Sarjuuk
b837e55edc Misc/Fixups
* copy/paste errors in MiscValue handling for SpellEffect: skinning
2025-04-23 01:28:53 +02:00
Sarjuuk
d75aa56b38 Spells/Attributes
* update spell attribute descriptions primary from TC, secondary from Wowdev Wiki (not guaranteed to be correct for 335)
 * show all attributes on SpellDetailPage
 * update links from attributes to SpellFilter
   * unsure: should the attribute filters work purely on attributes or also consider other factors?
 * implement some of the client side modifiers on the spell tooltips and buffs
2025-04-22 21:38:58 +02:00
Sarjuuk
9c73f3cf95 Style/Fixup
* vertical align tinyicons in SpellMod array on SpellDetailPage
2025-04-13 00:37:36 +02:00
Sarjuuk
9a893e03d7 Misc/External Links
* try to prompt the brwoser to not send a referrer header when leaving the site.
2025-04-11 04:52:22 +02:00
Sarjuuk
43970189a6 Misc/Fixup
* happy three quarters of a new year .. or something
2025-04-02 23:34:36 +02:00
Sarjuuk
682b315e17 User/Misc
* floating changes
 * codify user checks into functions
2025-04-02 23:26:01 +02:00
Sarjuuk
3078763ec3 Misc/Fixup
* floating changes
 * condense several switch() into match() constructs
 * fix ucFirst not making the rest of the word lowercase
2025-04-02 22:22:57 +02:00
Sarjuuk
44ff43c113 Misc/Fixup
* floating changes against account ajax
 * make validation of DBtype/typeIds a method of Type
2025-04-02 22:14:45 +02:00
Sarjuuk
790264ba08 Misc/Fixup
* floating changes against profiler
 * no functional changes
2025-04-02 21:49:25 +02:00
Sarjuuk
e29d1e69fe Template/Fixup
* those js snippets don't really care what namespace they are included in
 * .. why are they even php files...?
2025-04-01 23:18:24 +02:00
Sarjuuk
2689fba992 SpellDetailPage/Source
* do not acces sources directly
 * fixes source display in infobox
2025-04-01 23:10:24 +02:00
Sarjuuk
db1d3ccace Core/Cleanup
* try to give included files a logical structure
 * move objects from Util and Game to their own files
 * make non-essential files auto-loaded
2025-04-01 22:33:36 +02:00
Sarjuuk
3a6c86092b Core/Compat
* create namespace Aowow to avoid naming conflicts
 * inclues/libs/ is outside of the Aowow namespace
2025-04-01 22:32:37 +02:00
Sarjuuk
4ccf917707 Listview/ReplyPreview
* fix links to parent comment
 * implement go-to-reply redirect
2025-03-29 14:15:59 +01:00
Sarjuuk
b347794ce5 Guides/Editor
* fix language selector
 * fix changelog
2025-03-29 13:52:36 +01:00
Sarjuuk
ed25f1f5f5 Misc/Fixup
* add type guide (300) to js so comments can be linked to guides
2025-03-26 21:22:37 +01:00
Sarjuuk
390801f53c NpcDetailPage/Skinning Skill
* fix displayed skill requirement for non-standard skinning (engineering, herbalism)
2025-03-25 17:30:33 +01:00
Sarjuuk
73f4a69a41 Setup/CustomData
* fix entry for item 33147
2025-03-22 19:24:01 +01:00
Sarjuuk
197b097ee5 Filters/StaffFlags
* fixed flag filters visibility for spells
 * fixed implementation of staff flag filters. Now they acutually do something.
 * fixed localization
2025-03-20 21:30:01 +01:00
Sarjuuk
f2bbb87eef SpellDetailPage/Effects
* readd lost markup used by EFFECT_PLAY_SOUND, EFFECT_PLAY_MUSIC and AURA_OVERRIDE_SPELLS, AURA_SCREEN_EFFECT
2025-03-20 19:07:21 +01:00
Sarjuuk
676c8617a5 Misc/Fixup
* fixed an exception for using an enum as string when generating log message.
2025-03-19 13:50:28 +01:00
Sarjuuk
6f8b72c980 Spells/Tooltips
* use cooldown strings from future clients to avoid scientific notation for long cooldowns hidden by the 335 client
2025-03-16 01:11:32 +01:00
Sarjuuk
b2d3bc1076 Types/Filters
* followup on 748a78c3c7
 * fix return type of criteria filter callbacks (mostly in case of faulty input)
 * spread the criteria var into criteria filter callbacks
 * some magic numbers to constants and type declarations for params/return types
2025-03-12 00:29:23 +01:00
Sarjuuk
9345309c07 ItemDetailPage/Tabs
* add filter result prompt to "disenchanted from" tab
2025-03-11 22:16:43 +01:00
Sarjuuk
50a5ccbaf6 Items/Summary
* Fixed itemset bonus calculation for sets with interchangeable items.
 * Fixed multiple items for the same slot activating set boni
2025-03-07 16:29:14 +01:00
Sarjuuk
460f4857ad Items/Filters
* add 'fished' as valid item source
 * item source 'none' now properly selects items without source, not items without source from this list
 * fixed edgecases where filter callback might have no return value
2025-03-06 18:51:51 +01:00
Sarjuuk
3b0d288418 Profiler/Fixup
* profiler expects teamId, not sideId
   (yes i know they are basically the same thing offset by 1!)
2025-03-06 02:00:28 +01:00
Sarjuuk
c712d1234b WorldEvents/Query
* try to fix generated aggregate query by not selecting multiple cols as `id`
2025-03-04 23:49:32 +01:00
Sarjuuk
31ad2e4944 Misc/Locale
* coerce malformed locale values from GET to int instead of failing outright
2025-03-04 23:49:27 +01:00
Sarjuuk
6249f2957e User/Fixup
* user linked characters where also uninitialized
2025-03-04 23:49:15 +01:00
Sarjuuk
efc8b51c8f Enchantment/Spells
*  initialize SpellList as null so it can be referenced
2025-03-04 00:29:08 +01:00
Sarjuuk
44bd9f521b Misc/Fixup
* fixed return type declaration to match inherited class
2025-03-04 00:22:13 +01:00
Sarjuuk
8b59551905 Locale/Search
* remove obsolete and broken logographic check.
 * this fixes searches with "too short" search tokens
2025-03-03 19:42:00 +01:00
Sarjuuk
4fe930cdca Quest/Condition
* fix quest display for reputation restricted quests
 * also check sources from externaly added conditions
2025-03-01 22:00:12 +01:00
Sarjuuk
337eddcc0b Fixup/Spells
* fixed variable reuse
2025-03-01 21:17:34 +01:00
Sarjuuk
013f1845b5 Fixup/Class/Race
* matching bitmasks on IDs may be considered counterproductive
2025-02-28 19:17:17 +01:00
Sarjuuk
4c6d93881c Spells/Fixup
* format effect cooldown as time
2025-02-28 14:36:49 +01:00
Sarjuuk
fa33467610 Misc/Cleanup
* more trait properties to const as they were always intended to be
2025-02-28 14:05:04 +01:00
Sarjuuk
1f59e6fe2d Util/Enums
* create helper enum classes ChrClass & ChrRace
 * replace various iterators and checks with said enums
2025-02-28 14:04:19 +01:00
Sarjuuk
a5bd6ddc8a Misc/Fixup
* fix external links by escaping quest name as javascript
2025-02-27 16:25:25 +01:00
Sarjuuk
6660125154 Items/Tooltips
* fix scaling string in item tooltips for scaling & non-interactive tooltips
2025-02-27 16:25:25 +01:00
Sarjuuk
9abe9b0d56 Loot/Conditions
* do not link questitems for multiple quests with 'and'
2025-02-27 16:25:25 +01:00
Sarjuuk
83b99c47d2 Auth/Fixup
* provide empty values for non-default db fields when initializing external accounts
 * removed browser check for username maxlength as it depends on auth method
2025-02-27 16:25:24 +01:00
Sarjuuk
cd4c023c61 SpellDetailPage/Fixup
* fix extraCols from spell_loot bleeding into other tabs
 * fix linking to effect spell from glyph_properties
2025-02-27 16:25:24 +01:00
Sarjuuk
870cbea2ca SmartAI/Update
* update events and actions to match TrinityCore again
   * removed events and actions have been kept but marked as deprecated
 * general rewording and use of UIES for better readability
 * move constants to respective classes
 * reevaluate usage of UNIT_FIELD_BYTES1 content
2025-02-27 16:25:24 +01:00
Sarjuuk
748a78c3c7 Filters/Cleanup
* move filter constants to class Filter
 * use less magic numbers
 * add type declarations
2025-02-27 16:25:23 +01:00
Sarjuuk
398b93e9a7 Locale
* detatch from User and Util and move to its own enum class
 * added definitions for all locales the 12340 client could in theory have
 * this is incompatble with the Intl extension
 * version bump and php requirement bump
2025-02-27 16:25:23 +01:00
Sarjuuk
40c2c63d1b NPC/MapModes
* try to improve map mode detection for NPCs again...
2025-01-27 23:09:45 +01:00
Sarjuuk
9da1e1575f Locale/Typo
* fixed title categories for locale 8
 * .. after a decade
2025-01-27 10:31:23 +01:00
Sarjuuk
88c066a8f5 ErrorHandling/Fixup
* do not write to STDERR from web context
2025-01-27 10:31:23 +01:00
Sarjuuk
88da3588e5 Compat/SQL
* make ON DUPLICATE KEY UPDATE queries compatible with both MySQL8 and MariaDB by providing update values from php
2025-01-26 13:53:26 +01:00
Sarjuuk
5309843d77 Misc/Robots
* add sensible robots.txt expected by configuration self test
2025-01-20 07:09:48 +01:00
Sarjuuk
452e056499 Misc/Date Format
* use 12h format when using AM/PM suffix
2025-01-20 06:41:32 +01:00
Sarjuuk
d86936f6f5 Setup/ItemStats
* fixed array structure when converting equal amounts of SPELL_DAMAGE and SPELL_HEALING to SPELL_POWER
2025-01-20 06:34:38 +01:00
Sarjuuk
c1eecb4c22 ItemStats/Filters
* do not use NULL on item stats as it prevents searching for an amount of 0 (except for stats that certain items just cant have)
 * fix stats from spells granting spell power and spell healing separately
 * define and use some item subclasses
2024-09-10 16:54:49 +02:00
Sarjuuk
a62f24b97c Locales/Domain
* entirely switch over to write 'en' for locale 0 and accept both 'www' and 'en' for locale 0 when receiving
2024-09-10 14:42:27 +02:00
Sarjuuk
e3fc4ebd62 Updates/Fixup
* DROP COLUMN IF EXIST is MariaDB specific and should not be used.
2024-09-08 21:46:59 +02:00
Sarjuuk
79aa8fda7e Profiler/Fixup
* fix index error in ArenaTeam member update query
2024-08-27 22:56:33 +02:00
Sarjuuk
fdf8d783b1 AjaxHandler/Fixup
* fix invalid return type in handleWeightscales() for action: delete
2024-08-27 22:34:52 +02:00
Sarjuuk
48ce7267e7 ItemFilter/Fixup
* fix invalid return type in createConditionsForWeights()
2024-08-27 22:34:52 +02:00
Sarjuuk
5270e70cd1 ListviewTabs/Fixup
* filter empty tabs before trying to display, case in point
 * do not always assign conditions tab if empty
2024-08-27 22:34:52 +02:00
Sarjuuk
50f5af07f3 SpellEffects/Icons
* fix icon formater: num/qty can be strings
2024-08-27 22:34:52 +02:00
Sarjuuk
64fb86f3a9 Reports/Fixup
* reports can have 'null' subject (general bug reports, general feedback)
2024-08-27 22:34:52 +02:00
Sarjuuk
778c21e817 BaseType/Fixup
* fix totals query. Rather treat original query as subquery than trying to modify it
 * fixes queries utilizing HAVING and GROUP BY
2024-08-27 20:46:06 +02:00
Sarjuuk
af303f447a CommunityContent/Fixup
* fix totals query params after 481a3dc63f
2024-08-27 20:44:43 +02:00
Sarjuuk
03cf3a5918 UtilityPage/Random
* fix invalid type error
 * simplyfy query building (there are no longer tables with an index named `entry`)
2024-08-27 20:42:06 +02:00
Sarjuuk
3aede18926 SpellDetailPage/Fixup
* fix empty scaling bar
 * fix setting max level for scaling tooltips
 * fix displayed periodic power gain value for runic power and rage
 * attempt to fix spell coefficient calculation (dear god...)
 * append % sign to value for two more auras
 * display value per combo point
2024-08-24 20:02:51 +02:00
Sarjuuk
075e15ba0c Setup/Fixup
* even more forgotten renames in help text
 * see 07e001ee9c
2024-08-24 15:11:31 +02:00
Sarjuuk
7d2e306f0c Game/Spawns
* fix spawn point calculation on instance maps without dungeonmap entry
   (can't wait to see what breaks elsewhere)
2024-08-24 14:49:22 +02:00
Sarjuuk
b11c1125f6 Filters/Wildcards
* fix usage of generic search wildcards: ? *
 * only transform search form fields expected to be text
 * some cleanup (magic numbers to define, fn return types, nullsafe assignemnts)
2024-08-22 16:15:07 +02:00
Sarjuuk
57ad861da7 Items/Arena
* changed logic to not display required arena rating in tooltip or infobox if there are sources that don't require it.
 * also, should there be a difference in sources, display lowest required rating, not not highest.
2024-08-22 01:40:03 +02:00
Sarjuuk
d2e109d818 SpellDetailPage
* attempted cleanup in effect generation and template
 * display summon properties
 * display unit of effect value
 * fix spell effect layout, typos, missing tools, ...
2024-08-19 21:35:36 +02:00
Sarjuuk
dab110475c Items/Tooltips
* do not display crafted item tooltip inside recipe tooltip if the craft spell doesn't create an item.
   (notably: enchantments were displaying enchantment scrolls)
 * always display reagent cost if set
2024-07-31 22:02:15 +02:00
Sarjuuk
b330f88699 Itemstats/Ammunition
* don't use 'delay' to calc added dps from ammunition
2024-07-31 02:38:25 +02:00
Sarjuuk
481a3dc63f MySQL/Compat
* fixed several deprecation notices and warnings from MySQL8, most notably:
   - SQL_CALC_FOUND_ROWS: stopped using DBSimple::selectPage and query 'SELECT COUNT(*) ...' separately where needed
   - ON DUPLICATE KEY UPDATE ... VALUES(): use row alias for new values instead of VALUES function
   - boolean shorthands to long form (&& -> AND, etc)
2024-07-31 02:38:19 +02:00
Sarjuuk
1b2b773663 PHP/Compat
* avoid using "echo" to write to CLI as php mistakes it for sent headers (see php-src #12303)
 * as we are using fwrite now, errors are written to STDERR instead of STDOUT
 * fixes an issue where reloading the config would cause "ini_set(): Session ini settings cannot be changed after headers have already been sent" to be spammed
2024-07-31 00:51:10 +02:00
Sarjuuk
b5c2f7a296 Git/Misc
* force text files eol to \n
2024-07-31 00:50:46 +02:00
Sarjuuk
07e001ee9c Setup/Fixup
* fixed help text referencing renamed parameter
2024-07-31 00:50:46 +02:00
Sarjuuk
c30e68d86f Enchantments/ProcSpells
* fixed resolving procSpells with a triggerSpell of 0
 * spells are now always displayed in listview
2024-07-14 17:48:30 +02:00
Sarjuuk
7d545167df Errors/Log
* also log POST data
2024-07-14 17:18:22 +02:00
Sarjuuk
31928d56a7 Localization/Fixup
* kill an antique typo causing an error whenever a tooltip for a hearthstoneesque effect is displayed for Locale FR an RU
2024-07-14 16:23:43 +02:00
Sarjuuk
99a95f3995 Profiler/Completions
* removed old workaround for js date object creation
2024-07-13 19:22:25 +02:00
Sarjuuk
a9f1832b6d GearScore/Enchants
* reapply lost /4 modifier for enchantment scores
2024-07-11 01:48:20 +02:00
Sarjuuk
b0a51f4746 Spells/SpellClassMask
* fix confusion of SpellClassMask fields
 * no functional change
2024-07-10 18:04:35 +02:00
Sarjuuk
f55945780b Setup/Fixup
* fix syntax of db_structure.sql
 * not to self: SHOW CREATE TABLE doesn't return a terminated statement
 * fixes #431
2024-07-09 21:10:17 +02:00
Sarjuuk
81078bcf3d Comments/Date
* add missing elapsed value to comment and reply rows to make them sortable
2024-07-09 14:41:05 +02:00
Sarjuuk
c14d53067b Comments/Replies
* add default values to comments table so replying doesn't cause errors
2024-07-09 13:30:51 +02:00
Sarjuuk
460615c112 Profiler/Completion
* split completion table into it's subcomponents
 * this should save some disk space as some keys and null fields have been optimized out and col sizes have been reduced
 * sort ICC raid bosses first to last
2024-07-08 23:56:43 +02:00
Sarjuuk
98b1771850 Config/Locales
* flag locales as 'required'
 * allow bitmasks to be empty unless required
 * skip empty check in javascript
2024-07-08 17:16:10 +02:00
Sarjuuk
37def70f6a Setup/Realms
* force use of Locale EN instead of last used locale (usually RU)
2024-07-08 16:51:44 +02:00
Sarjuuk
f4364099c6 DB/CustomData
* alter value column from varchar to text to prevent truncated data
2024-07-08 16:51:44 +02:00
Sarjuuk
92a6e0122f Misc/Fixup
* Profiler: handle items with invalid enchantments
 * Profiler: convert (last?) forgotten INSERT IGNORE to ON DUPLICATE KEY UPDATE id=id
 * Areatrigger: fix null related tabs
 * LatestComments: fix lv template for replies
 * Conditions: fixed tab names for locale DE
2024-07-08 16:51:08 +02:00
Sarjuuk
828fa40d4b Loot/LootLink
* link Ahune and Majordomo Executus to their chests
2024-07-07 23:02:44 +02:00
Sarjuuk
815f13e530 Pages/TopUsers
* hide 'reports' column as reputation for reports is NYI
2024-07-07 20:55:00 +02:00
Sarjuuk
9435c0fc2e EventDetailPage/RelItems
* fixed getting related items from QuestList
2024-07-07 20:50:22 +02:00
Sarjuuk
b8898797ed SoundDetailPage/Conditions
* fix error assigning a worldstate conditional from child zone to parent zone
2024-07-07 14:18:08 +02:00
Sarjuuk
be7a84a651 Core/Conditions
* minor rework
 * fixed columns of tab item loot
 * fixed lookup of classes/races (as they are the only type used as bitmask)
 * implemented reverse lookups everywhere (arguably class, race and skill are too spammy)
 * reverse lookups no longer contain redundant data
 * changed how the groupKey is set, so there are no more cases that can't be looked up
 * fixes #273

 * title: added tab 'criteria-of'
2024-07-06 04:03:56 +02:00
Sarjuuk
0f6b8015a1 Misc/Fixup
* Loc EN: removed unnessecary 'to translate' backets
 * npc: add missing index to loot tabs to kill a warning
2024-07-05 22:10:46 +02:00
Sarjuuk
bd5200de85 Mapper/Quests
* sort zones by number of pins, most to least
 * always preselect zone with most pins
2024-07-05 22:10:36 +02:00
Sarjuuk
f21e8045b2 Misc/Fixup
* spell:    resolve MiscValue for SPELL_AURA_MOD_INVISIBILITY
 * search:   do not double escape page title
 * profiler: fixed typo, causing shaman spirit weapons to be applied _everywhere_
2024-07-05 18:15:05 +02:00
Sarjuuk
8d885a5a67 Setup/WIN
* fix paths for WIN ... and try to not use slashes in the future...
 * fixes #430
2024-07-05 18:15:05 +02:00
Sarjuuk
a4bcb33ba4 NPCs/DetailPage
* added item to infobox when npc is a spirit (only visible when dead)
2024-07-05 18:01:49 +02:00
Sarjuuk
8b46607c29 Setup/Spawns
* handle erronous zone data from TDB creature/gameobject tables
2024-07-04 23:22:29 +02:00
Sarjuuk
c3bae7fe5e Localization/zhCN
* add lost string lookups
2024-07-04 21:31:52 +02:00
Sarjuuk
2e9b503c59 Loot/LinkedLoot
* rework npc <-> chest loot linking
 * difficulty is now directly stored
 * should fix multiple issues where loot tabs had wrong difficulty or null title
2024-07-04 21:31:48 +02:00
Sarjuuk
02e33b4038 Profiler/Tooltips
* do not transform page parameters to lowercase. The tooltip Javascript expects the server response in the same case it got sent.
 * transform to lowercase just for lookups
 * fixes #394
2024-07-04 16:27:00 +02:00
Sarjuuk
c3347b8e9c CLI/WIN
* read input as a whole line under WIN instead as stream_get_contents always blocks
 * fixes #429
2024-07-04 15:20:07 +02:00
Sarjuuk
cd4e049680 DB/Engine
* drop usage of MyISAM and switch to InnoDB.
2024-07-03 18:45:52 +02:00
Sarjuuk
2bd588045a PHP/Compat
* remove sources of deprecation warnings. Mostly dynamic creation of object properties.
 * some string function no longer accept null as string
2024-07-03 18:38:28 +02:00
Sarjuuk
05c036bd9f Misc/Fixup
* fix Conditions derived from spell_area
 * Type Statistic should not be random searchable and excluded from "missing screenshots" UtilityPage
 * do not cut off querys in log
2024-07-03 18:33:25 +02:00
Sarjuuk
d93b5df5bc Misc/Fixup
* added datetime increment to dbversion table, lost from 69de457108
2024-07-02 16:12:13 +02:00
Sarjuuk
f12d16ea5b Localiziation/Fixup
* fixed cast time strings for locale enUS
2024-07-01 22:25:06 +02:00
Sarjuuk
d2277d1034 Setup/Icons
* do not consider the setup step failed if icons are missing. There are a couple void references in dbc
2024-07-01 19:44:31 +02:00
Sarjuuk
4d306e64fb CLI/Config
* fixed skipping the value in oneline mode being interpreted as empty string
 * fixed entering infinite loop in oneline mode, when passed value was invalid
 * fixed displaying config hint, when passed value was valid
2024-07-01 19:05:33 +02:00
Sarjuuk
8016802ec6 CLISetup/Fixup
* also convert Cfg test function, forgotten in 2386e35207
2024-06-30 16:55:22 +02:00
Sarjuuk
2386e35207 CLISetup/locales
* removed redundant declaration of locales to process
2024-06-29 13:03:18 +02:00
Sarjuuk
5d4051928a Setup/Fixup
* spell:  do not create a temporary copy of the spell.dbc, but merge serverside spells directly into aowow_spell.
           This sidesteps a mysql issue where a temp table can't be read multiple times.
 * spawns: fixed lost var rename in log output
 * items:  use UNSIGNED instead of INT when typecasting in query so mysql can also understand it
2024-06-29 12:18:13 +02:00
Sarjuuk
33d2192431 JS/Mapper
* generally allow zone links on a map on the zone detail page
 * manually disable it for the main map
2024-06-28 01:04:48 +02:00
Sarjuuk
ae54e5e213 Setup/Spawns
* restore info on manually moved spawn points
 * don't use worldmaparea zone dimensions for maps that don't use them
2024-06-28 01:04:42 +02:00
Sarjuuk
cdf06deb90 Misc/Fixup
* restore display of creature targets on quest detail page
 * fix ShowOnMap utility for quest displays
2024-06-28 01:04:36 +02:00
Sarjuuk
69de457108 Map/Spawns
* move areatrigger teleport endpoints and instance entrance points to spawns. This makes them editable like spawn points.
 * since instance entrances aren't shown on maps they are moved through the admin menu on the instances page.
 * rewrote spawns SetupScript. Individual groups can now be recalculated separately.
2024-06-28 01:04:29 +02:00
Sarjuuk
040cac41a1 Setup/Help
* created a generic help display for SetupScripts with sub commands
2024-06-28 01:04:24 +02:00
Sarjuuk
abbedf9ae4 Misc/README
* extracting Glues/Credits/ is optional. They are not used by aowow itself.
2024-06-27 14:51:16 +02:00
Sarjuuk
22b0f8c1c1 Setup/Update
* setup step names are allowed to have dashes and underscores
2024-06-26 16:52:45 +02:00
Sarjuuk
a4c734435e Map/ShowOnMap
* fix pins being combined across multiple floors
2024-06-26 16:52:45 +02:00
Sarjuuk
88c5127ab5 Map/Spawns
* fix spawns for multifloor dungeonmaps that use their worldmaparea entry for coordinates
 * fix mapper feature: move visible spawn point
 * resolve confused X/Y coordinate remapping from a time before i knew world coordinated are rotated by 90°
 * PS: Ulduar floor indizes were funky. No idea why.
 * restore old define order for g_zone_areas
2024-06-26 16:52:00 +02:00
Sarjuuk
a08a713dcc Misc/Fixup
* fixed typo in locale deDE
 * fixed markup not processing emotes with negative id
2024-06-26 16:42:04 +02:00
Sarjuuk
79764ced60 Core/ErrorHandler
* do not handle errors outside of the registered handlers
 * always handle all errors otherwise they get stored for error_get_last
 * always print errors to CLI
 * shutdown function handler should not be picky about what errors it gets to report
 * removed some mostly unused error strings
2024-06-24 18:10:44 +02:00
Sarjuuk
e614f415a9 Util/FileHandler
* try to create directory if file is to be written into nonexistent directory
2024-06-24 17:13:50 +02:00
Sarjuuk
10de320616 CLI/readline
* added checks if STDIN is available and open before accessing it.
2024-06-23 15:54:58 +02:00
Sarjuuk
9bb5afd9a7 Setup/CustomData
* removed unused option to have custom data defined in code
 * table `aowow_setup_custom_data` should be used instead
 * this kills a "creation of dynamic property" deprecation notice
2024-06-23 14:42:16 +02:00
Sarjuuk
615c203c7a Setup/img-maps
* script cleanup
 * fixed subzone generation and made color less garish
 * fixed alphamap generation and alphamapcheck not pointing to the same path
 * fixed padding UtilityScript args with unexpected var types
2024-06-21 16:57:33 +02:00
Sarjuuk
931a90f5c8 Misc/Fixup
* revision bump
 * minor log format fix
2024-06-20 20:49:11 +02:00
Sarjuuk
bf184e7555 Core/Setup
* rewritten to be able to dynamicly load it's components
   - CLISetup -> checks for UtilityScripts (config, setup, dbc reader, etc.) -> checks for SetupScripts (individual sql/file generators)
   - each step may now have a help prompt attached. If none are provided, the containing script may provide it's help.
   - all Scripts are self contained modules. No more editing of 3+ files if some component is added/removed
 * removed intermediaries FileGen & SqlGen
 * functional changes
   - allow providing CLI arguments to siteconfig and account UtilityScript and skip the interactive prompts
   - set slot for consumable enchantment items so they are filtrable
   - zones dataset is now localized and generated from GlobalStrings.lua and DungeonMap.dbc. Related data dumps removed.
   - 'aowow' and 'prQueue' executables now have shebangs

    WARNING - command line options have been renamed!
2024-06-20 18:10:12 +02:00
Sarjuuk
ab4cf67e80 Misc/Fixups
* Util::writeTable .. border length, toned down colors
 * Cfg::get .. empty strings and 0 can be valid, return null on failure
 * Game::getWorldPosForGUID .. fixed referencing soundemitters by soundId instead if index
2024-06-20 16:28:29 +02:00
Sarjuuk
7412a518a5 Setup/Classes
* fix skill aggregate
 * ya cant sum strings, ya dummy
2024-06-19 02:52:43 +02:00
Sarjuuk
3d84870db8 DBC/Fixup
* fix broken locale string extraction after e164023b8a
2024-06-19 01:34:35 +02:00
Sarjuuk
b5b62a5a62 Localization/GlobalStrings
* apply more strings from GLobalStrings.lua to localization
 * mostly ITEM_MOD_* and PVP_RANK_*
 * some fixes in the process
2024-06-18 22:59:20 +02:00
Sarjuuk
7e0be11323 Stats/CombatRatings
* define magic numbers from combat ratings
 * move forgotten rating base values from Util to Stats
 * fix localized rating strings for item and spell tooltips (expected float, got string)
 * some cleanup in item/spell types to make it more clear when an internal stat is being handled, not an itemMod or combatRating
2024-06-18 22:34:50 +02:00
Sarjuuk
e164023b8a Setup/DBC
* move dbc structures to separate files and allow loading a specific build
 * handle localized single string fields
 * add cli option for output table name
 * add cli option for wowbuild
2024-06-17 19:59:51 +02:00
Sarjuuk
12ddc6fe82 Misc/Fixup
* prevent direct access to some internal files where applicable and reword error for others
 * if CFG_DEBUG is set also enable debug in client javascript
 * non-breaking formatting changes
 * define regions and missing locales for later use
2024-06-17 19:59:51 +02:00
Sarjuuk
06ffba0239 CLI/readline
* do not reuse prompt variable for use input
2024-06-17 19:59:51 +02:00
Sarjuuk
d03448b053 CLI/FileAccess
* fix deep file/dir creation
 * also edited file permissions again and left a note so it doen't happen again .. maybe
2024-06-17 18:13:03 +02:00
Sarjuuk
0117916da9 CLI
* make tables more legible
2024-06-17 18:13:03 +02:00
Sarjuuk
c2bbfe17a6 Setup/DataStores
* do not rely on temporary converted dbc tables during runtime
 * create permanent tables instead
2024-06-17 18:12:59 +02:00
Sarjuuk
7b924a197e Mapper/Spawns
* do not try to place spawns assigned by instanced map <=> zoneId association. They have (0,0) as coordinates
2024-06-17 13:36:45 +02:00
Sarjuuk
8fe18ed41c Articles/Classes
* add class articles from 8e5bdebea0 db_structure.sql
2024-06-16 22:16:38 +02:00
Sarjuuk
d16b08bb29 Core/CharStats
* unify stat handling. If there are discrepancies left at least they are now centralized.
   - health should no longer pretend to be mana
   - stats are no longer capped to 32.7k points as items can have multiple of the same stat and handily exceed smallints
 * weight scale fixes:
   - "repair cost" is no longer a weight scale option. Why was it one in the first place? Also it wasn't even accessible before. (that was a bug)
   - "bonus armor" is now searchable and only applied to armor pieces
 * removed unused parsed stats from itemsets
2024-06-16 19:28:26 +02:00
Sarjuuk
cb3c7d4ef0 Misc/Util
* relax numeric type requirements when working with filters
 * restore smart type casting functionality of Util::checkNumeric when used with NUM_ANY
 * enable Util::arraySumByKey to work recursively
 * fix source display in listview
2024-06-16 17:22:08 +02:00
AthenaSui
a4d05dc036 Localization/zhCN
* cherry-picked translation from 820c88d412
2024-06-14 02:01:54 +02:00
Sarjuuk
ce0e57e390 Setup/Fixup
* run TDB checks agains word db as they are supposed to.
 * fixes #423
2024-06-10 21:07:57 +02:00
Sarjuuk
33ee358e0c Setup/DBconfig
* test for known world db formats and throw appropriate errors
 * test for imported aowow db and throw error if missing
 * make DB connection reloadable
2024-06-09 15:02:31 +02:00
Sarjuuk
82c04c9ed5 CLI/readline
* there are still terminals out there sending \r as line terminator...
2024-06-07 17:15:01 +02:00
Sarjuuk
fc86825b15 Pages/Item
* fixed fractional buy price for items sold in stacks in infobox (fixes #362)
 * fixed filter criteria enums being generally invalid
2024-06-07 16:40:26 +02:00
Sarjuuk
e873d8cbd0 CLI/readline
* ignore \e sequences except the single \e chars read to leave the current loop
2024-06-07 15:32:35 +02:00
Sarjuuk
69fe0b5c7d Cfg/Fixup
* also trigger first time load as soon as db is set up
2024-06-04 23:15:18 +02:00
Sarjuuk
e734b41632 Cfg/Fixup
* only throw errors if there is actually a config to work with. (like, not when you are just setting up for the first time)
 * do not use trigger_error() in CLI mode as it can cause a lockup as the error_handler tries to throw more errors.
 * assign lost validator fn flag to settings
 * also use validator onReset
2024-06-04 22:15:52 +02:00
Sarjuuk
5c1e9747c6 Misc/Cleanup
* remove some derelict code
2024-06-03 15:58:41 +02:00
Sarjuuk
f861886fdf Misc/Fixup
* Conditions: loot rows initially have no 'id'
 * fixed building talent string for hunter pets (different talents can occupy the same row/col spot)
 * added keys loot cols on creature table
 * fixed trying to show itemset type for itemsets without type
 * fixed waypoint calculation when moving entity between floors
2024-06-02 21:15:29 +02:00
Sarjuuk
efab0bad32 Misc/Fixup
* missed const from f77d676a19
 * missing start of string anchor in Util::checkNumeric
2024-06-02 02:45:29 +02:00
Sarjuuk
bc7d561da2 Core/Conditions
* rewritten and moved to its own class, should be easier to expand in the future
 * add missing sources and types from TrinityCore
 * implement conditions on Areatrigger and Loot containers
 * implement reverse lookups (e.g. a spell is a conditional for something else)
 * general beautification pass .. should be more legible in general

NOTE:
 * texts have been changed, so the existing translation for esES ist gone
 * selecting and describing condition targets is still wonky
2024-06-01 02:47:58 +02:00
Sarjuuk
84555afae3 Page/Notes
* also color code notes according to message severity
2024-06-01 02:47:58 +02:00
Sarjuuk
f77d676a19 Core/Config
* convert configuration from list of constants to object
 * fixes config changes not applying on cli whithout closing and reopening again
 * config variables are no longer embedded in localization text
2024-05-30 20:50:44 +02:00
Sarjuuk
454e09cc78 DB/Errors
* don't need to handle sql warnings in batch when the error handler doesn't use exit() any longer
 * display warnings as [WARN] and use consts instead of magic numbers
2024-05-28 22:40:46 +02:00
Sarjuuk
2d5caba814 DB/Structure
* some more corrections to field types
   - quests: rewardArenaPoints - unsigned -> signed
   - events: unify event id - tinyint -> smallint
   - objects: unify quest id - smallint -> mediumint
   - item_stats: stat cols - smallint -> mediumint (Tester Ring has 64k HP+MP)
2024-05-28 22:37:59 +02:00
Sarjuuk
f422c4ecfb Setup/Spawns
* implement SpawnedByDefault from TC
 * maybe fix a longstanding issue where multiple parent areas per instanced map lead to wrong spawn points
    map  should be                  can also be
    36   The Deadmines              The Great Sea, Unused Ironcladcove
    109  The Temple of Atal'Hakkar  Sunken Temple
    540  The Shattered Halls        Hellfire Citadel
    560  Old Hillsbrad Foothills    Hyjal Past
    631  Icecrown Citadel           The Frost Queen's Lair, Putricide's Laboratory of Alchemical Horrors and Fun, The Crimson Hall, The Frozen Throne, The Sanctum of Blood
2024-05-28 21:58:44 +02:00
Sarjuuk
a87b869896 Setup/BLP
* fix using replacement patch files as images
2024-05-28 20:17:16 +02:00
Sarjuuk
2c451b8deb Locales/Search
* do not apply minimum string length limiter to logographic languages
2024-05-27 17:54:58 +02:00
Sarjuuk
f6565ea924 DB/Structure
* fix data types and data length and add default values where necessary
 * data should no longer get truncated
 * misc fixes
2024-05-22 20:35:02 +02:00
Sarjuuk
7d5930865c DB/Setup
* fix erronous foreign key constraint revealed by MySQL 8.4
2024-05-21 17:46:23 +02:00
Sarjuuk
90b04865f5 Setup/DBC
* use utf8mb4 for dbc_* tables
2024-05-17 23:52:47 +02:00
Sarjuuk
a03d44ae67 ZoneDetailPage/Fishing
* if subzone has no entry in skill_fishing_base_level, try to look up parent entry
2024-05-17 23:27:03 +02:00
Sarjuuk
a97cede1e0 Profiler/Realms
* generally allow all realm types
 * filter visibility/access by userGroup
2024-05-16 22:33:01 +02:00
Sarjuuk
b2690aea08 Setup/Update
* underscores are valid chars for script names
2024-05-15 00:08:45 +02:00
Sarjuuk
c87178c791 PHP/Compat
* spreading a nested array prevents it from being passed as reference
 * this worked previously under php v7.2
2024-05-11 00:32:01 +02:00
Sarjuuk
b5829cc954 Misc/Fixup
* fix reference error from 9a1cb5f2f9
2024-05-10 17:37:13 +02:00
Sarjuuk
9a1cb5f2f9 Setup/ImageGen
* consider preconverted png images when initially checking resources
 * fixed minor bugs
2024-05-09 16:05:23 +02:00
Sarjuuk
d7fa4a900e Admin/Screenshots
* whelp, didn't see the onKeydown handler for the input field
 * fix the fix in bf06c418d4
 * allow "ArrowUp", "ArrowDown" and "-" as inputs
 * also fix search by pressing <Return>
2024-05-07 19:26:06 +02:00
Sarjuuk
bf06c418d4 Misc/Fixup
* do not urlencode mysqli uris  ..  can't wait for this to cause issues again.
 * input[type=number] does not allow inputing dashes to signify negative numbers. Use input[type=text] instead and cast to int afterwards.
2024-05-07 16:43:35 +02:00
Sarjuuk
937bec3d84 Spells/Tooltips
* enable level scaling for spells with RealPointsPerLevel
 * note: While BasePoint vars ($m, $M) can scale, they are often involved in formulas that would have to be recalculated in javascript. This is currently impossible. So this var is skipped for now.
2024-05-06 01:06:31 +02:00
Sarjuuk
99eca2661f Items/Tooltips
* do not display charges if they can't be used
2024-05-05 19:45:27 +02:00
Sarjuuk
6b25288e2b Spells/Reagents
* always check all reagent fields, they are not set first to last
2024-05-05 14:31:15 +02:00
Sarjuuk
92c51237c6 Misc/Fixup
* lost changes from 5f4c62644d
2024-05-04 18:52:56 +02:00
Sarjuuk
5bb277bf2f Tooltips/CombatRatings
* modenize interactive rating string
2024-05-03 21:01:24 +02:00
Sarjuuk
5f4c62644d DB/Errors
* improve db error handling
 * web view should always result in an user friendly error if the db connection is missing or erronous
 * cli use should never fatal if the db connection is erronous. How are you going to fix it, d'uh.
 * if some CLISetup script requires a db connection check it individually
2024-05-03 20:58:30 +02:00
Sarjuuk
41c0af16b3 Misc/Fixup
* drop build steps possibly scheduled to sql update after de2fa377 (they can never be executed)
2024-04-21 22:27:15 +02:00
Sarjuuk
a4c15653d8 DB/SqlModes (#406)
* drop more modes that depend on previously dropped STRICT_TRANS_TABLES
2024-04-21 15:10:35 +02:00
Sarjuuk
42d284dce0 CLI/CR
* always skip \r inputs.
* May fix weird issues when accessing a *nix container from WIN and MacOS hasn't been a thing for decades...
2024-04-20 23:39:29 +02:00
Sarjuuk
d084e6072b DB/SqlModes
* only update sql_mode if strictly necessary
* keep other modes set in my.cfg / my.ini
2024-04-18 19:14:47 +02:00
Sarjuuk
67d4f12cfe Loot/Errors
* gracefully handle loot referencing nonexistent items
2024-04-18 14:12:20 +02:00
Sarjuuk
97d59dbb98 Tooltips/Icons
* properly format icons in tooltips after b125e55a4a
2024-04-03 21:53:52 +02:00
Sarjuuk
f35adfeb3a Misc/Fixups
* fix rogue letter in zhCN localization
 * remove unused redirects from powered tooltips, ocasionally breaking locale detection (preserved for posterity)
2024-04-03 19:31:11 +02:00
Sarjuuk
e5e4446366 Items/Icon
* set a default icon for items so they don't break listviews
2024-04-03 17:49:54 +02:00
Sarjuuk
e01c3ac205 Misc/Fixups
* fix line terminators in update file
2024-04-02 02:03:39 +02:00
Sarjuuk
e09e3a7260 Spells/Effects
* also list affected spells directly in the spell effect
 * allow more spell auras to display affected spells
2024-04-02 02:00:22 +02:00
Sarjuuk
7b43739dbc Localization/NumberFormat
* use narrow non-breaking space (&#8239) to separate thousands blocks for locales frFR and ruRU
2024-03-30 00:37:22 +01:00
SrzmGit
8e5bdebea0 Articles/Class (#397)
* Added german version of the class articles
2024-03-29 20:19:56 +01:00
Sarjuuk
555a6211bd Misc/Fixup
* no idea
2024-03-26 13:33:33 +01:00
Sarjuuk
6249ac6b2a Misc/Fixup
* STDOUT is only defined if php is in run in cli mode
2024-03-26 12:07:45 +01:00
Sarjuuk
494328cf53 DetailPage/Articles
* initialize articles null as originally expected
 * properly sort found articles or the wrong locale may get selected
2024-03-25 14:52:46 +01:00
Sarjuuk
e2e0a0295b ItemDetailPage/Infobox
* properly line break arena requirements after 8bf7b3ee06
 * fix ancient typo in unused code
2024-03-24 22:11:54 +01:00
Sarjuuk
676a7ef24e Misc/Formating
* removed excess ;
2024-03-16 16:38:02 +01:00
Sarjuuk
c01c9ce901 Misc/Fixups
* use built in function to determine if CLI can use escape codes
 * define _post, _get and _cookie in all cases
 * do not apply 8 regexes to a string that doesn't even contain a UI Sequence
2024-03-16 00:17:19 +01:00
Sarjuuk
d37eb9a59b Maps/Repsawn (#172)
* use more verbose time formater for repsawn time
2024-03-16 00:09:56 +01:00
Sarjuuk
d4a0abf704 Types/Objects
* make traps their own category
 * move "trap for" from infobox to listview tabs as there can be a couple hundred GOs pointing to the same trap.
2024-03-15 23:49:58 +01:00
Sarjuuk
ec1a2afc5f PHP/Compat
* fixed misc issues Intellisense was nice enough to highlight.
 * mostly deprecated usage of uninitialized parameters
 * class GenericPage still needs a long, hard look and a refactor
2024-03-11 23:20:17 +01:00
Sarjuuk
3e6e43fd68 MySQL/Compat
* fix col name / function name conflict
2024-03-11 23:20:06 +01:00
Sarjuuk
c37d2fd594 CLI/WIN
* support ansi escape codes for win 10 and up
 * also kill a warning
2024-03-11 23:16:32 +01:00
Sarjuuk
25b5928a22 ItemFilter/TabLinks
* always display FilterResult prompt on currency-for tabs
 * fixed display of FilterResult prompt on item currency-for tab
2024-03-09 22:44:55 +01:00
Sarjuuk
88b62730e1 SmartAI/Misc
* fix footer for ACTION_FOLLOW
 * use \u003A for : so it doesn't get evaluated as a switch
2024-03-06 21:26:39 +01:00
Sarjuuk
f00ccedbd3 Localization/Invisibility
* invisibility type General is on index 0 not index 1.
2024-03-06 21:25:44 +01:00
Sarjuuk
de2fa3770b JS/Tooltips
* try to get current domain from Locale when used on the site itself and no other params are given
2024-02-29 18:12:46 +01:00
Sarjuuk
be06b1e0cf Items/ExtraLoot
* corrected calculation after writing a simulator and some consultation
 * sad thing is, this will not even be visible after rounding for display
2024-02-29 17:33:55 +01:00
Sarjuuk
29f80f9a76 Items/ExtraLoot
* break down chances for loot from skill_extra_item_template
 * rename tab to make it clear that its content is a bonus
2024-02-29 01:25:34 +01:00
Sarjuuk
e85a9e9d6a Setup/Zones
* instances with with a entrance touple of (0, 0, 0) will no longer be displayed somewhere in Alterac (at the map 0 origin point)
2024-02-28 22:17:07 +01:00
Sarjuuk
54b224d929 Achievements/Rewards
* fix createing bogus ItemLists or TitleLists when displaying achievement rewards
 * don't display empty related-achievements tab on Emotes DetailPage
2024-02-28 21:52:34 +01:00
Sarjuuk
d0e5bec845 Setup/FileGen (#390)
* directories should probably have write permissions
 * add forgotten directory for guide image storage
2024-02-28 21:18:05 +01:00
Sarjuuk
b125e55a4a Custom/UiEscapes
* so, apparently the client allows ui escape sequences in the weirdest of places. Guess i have to follow suit.
 * also, the sequences can be multiline
 * also also, the file extension for icons is optional
2024-02-28 21:04:18 +01:00
Sarjuuk
611d2c40bd Profiler/Access
* load css/js even when profiler is disabled
 * do not display page speciffic announcements for overridden displays (e.g. Profiler help on maintenance, error, etc. display)
2024-02-28 19:05:21 +01:00
Sarjuuk
8b1fd3ac79 Setup/Source
* setting an unsigned column -1 is pretty pointless, isn't it?
 * using a recently truncated, not yet repopulated table to determine if items can be obtained is pretty pointless, RIGHT!?
   (items are not longer blanket tagged as unobtainable)
2024-02-28 19:05:17 +01:00
Sarjuuk
9831038729 Misc/Fixups
* remove w from umask for generated files
 * fixed warning in setup dbconfig
 * added lost Markup.js generation to initial setup
2024-02-28 19:05:17 +01:00
Sarjuuk
8bbffae837 Setup/DB
* fixed syntax error in initial db setup
 * corrected class role custom data
2024-02-28 16:28:06 +01:00
Amandil
b2ca072120 Misc/Fixups (#391)
* lost spell effect/aura declaration changes from 0e0116b27
     * case-sensitivity conflict (ID => id) caused by 77f81c1bd
2024-02-28 15:57:55 +01:00
Sarjuuk
9aeb2177cf Pages/SpellDetailPage
* fixed "modefied-by" and "modifies" tab having different results
 * evalueate some more spell effects
 * replace magic numbers with defined strings
 * get model info from some more spell auras
2024-02-26 01:23:35 +01:00
Sarjuuk
3dfdc300c5 DetailPage/Links (#388)
* add missing trailing slash to url in links list
2024-02-25 22:41:16 +01:00
Sarjuuk
4e65f0a955 Merge remote-tracking branch 'github/master' into ghMaster 2024-02-25 22:31:33 +01:00
Sarjuuk
dd9eaf49ff Page/Listview
* refer to Listview template by default name if able
2024-02-25 21:34:03 +01:00
Sarjuuk
6b0f617d1b Page/Screenshots
* handle more error cases more gracefully when uploading screenshots
2024-02-25 21:28:36 +01:00
Sarjuuk
ba53a5c760 Page/WH
* handle backlink in Page instead of Util
 * update event/holiday link
2024-02-25 21:26:30 +01:00
Sarjuuk
6958efcd0f Items/Tooltip
* do not show cooldown for passive onEquip spells
2024-02-25 21:24:36 +01:00
Sarjuuk
85e8175338 Spells/Misc
* try to handle bogus data from creature_template_addon.auras
2024-02-25 21:23:33 +01:00
Sarjuuk
a14b5e2be1 Libs/DBSimple
* also collect sql warnings for error handling
 * urlescape user/password on the db connection url (mysqli://)
2024-02-25 21:23:09 +01:00
Sarjuuk
d8a6f67688 Misc/Timer
* calling reset() on the Timer no longer breaks the interval
2024-02-25 21:19:10 +01:00
Sarjuuk
979a21afae Localization/Caching
* store source lists as object so different locales can be fetched
 * store parsed spell texts locale dependent so the locale isn't fixed
2024-02-25 21:19:04 +01:00
Sarjuuk
8269d4946f Misc/Home
* update currentyear
2024-02-25 21:18:52 +01:00
Sarjuuk
a39881e73f Misc/Fixup
* fixed borked Lang::load in setup after d0d2451ff51157728e622142c3be7ae0ba7dcebe
2024-02-25 21:17:33 +01:00
Sarjuuk
cfa5030f85 Profiler/ArenaTeams
* remove profiles from existing teams of the same type they are going to be added to.
 * should prevent characters being stuck in old teams.
2024-02-25 21:15:33 +01:00
Sarjuuk
c84d1181bb Localization
* add translation for dungeon floors from GlobalStrings.lua
 * fix SpellMod mistranslation
2024-01-08 00:12:30 +01:00
Sarjuuk
d92b17a386 Loot/Difficulty
* fixed encoding Dungeon Difficulty in source
 * resolve difficulty dummy loot sources to base creature if able
 * added listview note for difficulty source on item detail page
 * misc: fixed parsing color UI escape sequence
2024-01-08 00:06:30 +01:00
Sarjuuk
9e857035bd Creatures/Filter
* implement filter #34 (modelId)
 * thats the actual modelId .. not displayId
2024-01-08 00:05:32 +01:00
Sarjuuk
79383fc83a Search/Opensearch
* return results with correct mime type.
2024-01-08 00:05:32 +01:00
Sarjuuk
0e0116b274 Misc/Defines
* declare and use Spell Effects and Spell Auras
2024-01-08 00:05:32 +01:00
Sarjuuk
deba5325a7 Spells/DetailPage
* display proc info for dummy auras
2024-01-08 00:05:32 +01:00
Sarjuuk
a73d71b966 Profiler/Pets
* fix talent distribution being converted to int
* store pets tamed in heroic dungeons/raids with its base entry
2024-01-08 00:05:32 +01:00
Sarjuuk
06ecfd93d5 Setup/DBC
* add Item.dbc (maybe use later for creature eqipment display)
2024-01-08 00:05:32 +01:00
Sarjuuk
ac34b47c26 Spells/Sounds
* get sounds from ScreenEffect.dbc and link to type Spell and type Sound
 * resolve ScreenEffect name in SpellDetailPage
Fixup
 * fix warning in UI escape sequence parsing
2024-01-08 00:05:32 +01:00
Sarjuuk
9b16f2d84a Localization/WoW strings
* generalize WoW UI escape sequence handling
 * implement use of declinated words from dbc for locale 8 (ruRU)
2024-01-08 00:05:24 +01:00
Sarjuuk
cc594e3415 Skills/Requirements
* fix requirements  display for low level skinning
 * display fishing skill requirements on zone detail page in fishing tab
2024-01-07 23:04:03 +01:00
Sarjuuk
4d6fb4975e Emotes
* have creature emotes in the same system (on a negeative index) as SAI links were uselessly pointing to player emotes before
 * player emotes are now gendered
 * display two more cases of who points at whom when using a player emote
 * use and link event sound from emote
 * display more misc info
2024-01-07 23:03:21 +01:00
Sarjuuk
0f186576d3 Filter/Fixups
* fixed wrong indizes for gem color picker in locale 3 (deDE)
 * fixed interpretation of filter enums. Some are providing their value directly, others have an intermediate key.
 * createSQLForCriterium is no longer abstract. (was implemented in exactly the same way everywhere)
 * erronous criteria are now logged and discarded entirely)
2024-01-07 22:53:38 +01:00
Sarjuuk
837fdf78a0 Zone/DetailPage
* fix quests tab not displaying quests from zone
 * only include special quests (QuestSortID set) and quests directly related to zone instead of any quest in tab
 * adjust quest rewards tab accordingly
 * fix breadcrumbs for dungeon quests with sub category
2023-06-08 21:09:48 +02:00
Sarjuuk
6382302a30 Defines/SpellTrigger
* convert magic numbers to define and use as spell trigger types
2023-06-08 15:35:51 +02:00
Sarjuuk
da8943095b Localization/Typo
* introduced about 10 years ago *nostagia*
2023-06-08 15:35:42 +02:00
Sarjuuk
26da03f029 Libs/jQuery
* upgrade from v1.12.4 to v3.7.0
2023-06-08 15:35:31 +02:00
Sarjuuk
73dd745fe8 Profiler/Achievementpoints
* remove redundant total calculation as it is already stored with the profile
 * do not include statistic in sum calculation. (ffs why do they even have points assigned)
2023-06-08 15:35:19 +02:00
Sarjuuk
eca3e09482 Types/Filter
* do not split strings at \s if match:exact is set.
2023-06-08 15:35:07 +02:00
Sarjuuk
d8d2676596 Profiler/Completion
* move spells w/o source to excludeGroup unavailable
   shouldn't hide. They may have been available in the past.
2023-06-08 15:34:59 +02:00
Sarjuuk
a6f6e0b05d Itemset/Listpage
* implement filter: available to players
2023-06-08 15:30:44 +02:00
Sarjuuk
e9622e6991 Filter/Cleanup
* move shared criteria enums to parent
 * define shared regex patterns filter
 * set missing enum checks
 * fixed some filters
2023-06-08 15:28:57 +02:00
Sarjuuk
fcf24b3a45 Misc
* define and use some more magic numbers
 * move id-based custom data from spell SetupScript to aowow_setup_custom_data table
 * hide on unused glyph while at it
2023-06-08 15:27:49 +02:00
Sarjuuk
77f81c1bde Setup/Sources
* rewrote SetupSrcipt
 * implemented 'zone' parameter
 * implemented 'bossdrop' parameter
 * implemented 'dungeondifficulty' parameter
 * implemented item filter relying on zone information (dropsInX)
 * fixed world random drops showing a single loot source
 * extended Source column of spells to the same functionality as items

ToDo:
 * apply new 'commondrop' parameter on loot listviews
 * gather difficuly versions of gameobjects and apply the same logic as for creatures
 * implement fake spawns so npcs can get linked to a zone
2023-06-08 15:07:13 +02:00
Sarjuuk
fc7a526a67 Items/ListPage
* fixed displaying icon for currencies in vendor related columns
2023-06-08 14:16:29 +02:00
Sarjuuk
e71da620c6 Comments/Replies
* also show replies on latest-comments utility page
 * sort replies by score DESC
2023-06-08 14:16:03 +02:00
Sarjuuk
0d6a6e163c Profiler/Statistics
* use spells for skill modifying racials instead of hardcoded values
 * get baseline statistics from player_levelstats instead of hardcoded values
2023-06-08 14:01:36 +02:00
Sarjuuk
1c5e43d378 Profiler/Completion
* apply factions from player_factionchange_spells to all spells
 * filter companion pets without suitable item source
2023-06-08 14:00:32 +02:00
Sarjuuk
d16d685b70 Guides/Markup
* promote user guides to blogger level so the markup promised by the editor is actually appied.
2023-06-08 13:58:53 +02:00
Sarjuuk
856a98d875 Profiler/Characters
* fixed position of pending rename note
2023-06-08 13:55:35 +02:00
Sarjuuk
bc3ba23162 Guides/Fixups
* fix urls in user menu
 * strip anchors from tooltip title
 * prevent line breaks in description
 * make only in english info popup modular
2023-06-08 13:53:48 +02:00
Sarjuuk
0e9ca3ff90 Quests/Filter
* filter "repeatable" now also considers specialFlags
2023-06-08 13:48:56 +02:00
Sarjuuk
24d683332d Profiler/Completion
* fix partial loading of quests in profiler
 * provide inital category totals on a per class & race basis
2023-06-08 13:45:55 +02:00
Sarjuuk
ebc20be508 Spells/Localization
* use more GlobalStrings in spell context (as far as possible)
 * fixed rune cost order
 * fixes agains locale based time formater
2023-06-08 13:03:05 +02:00
Sarjuuk
8bf7b3ee06 Lang/cleanup
* fixed break and trim functions not handling text shorter than break length
 * add option to output raw text besides html, markup format
 * decalare return types and parameter types
 * cleanup
2023-06-08 12:56:36 +02:00
Sarjuuk
78f7f6b9cf Page/Profile
* fixed hang on profile load without base data
2023-06-08 12:55:06 +02:00
Sarjuuk
bfb7abb843 Profiler/Progression
* reorient icon texture so instance icons no longer appear multiple times on long progress bars
2023-06-08 12:52:54 +02:00
Sarjuuk
70a93d9905 Misc/Lang
* remove some more square brackets denoting missing translations
2023-06-07 19:46:52 +02:00
Stefano Borzì
153d489400 Lang/zhCN (#384)
* cherry pick missing translation from https://github.com/azerothcore/aowow/pull/36
---------

Co-authored-by: yuanf225 <43561197+yuanf225@users.noreply.github.com>
2023-06-06 21:49:23 +02:00
Sarjuuk
40014755e2 Setup/Log
* allow overwriting generic/fine log output
 * fix some errors
 * can't catch notices generated by mysqli_connect
 * removed some unnessecary ORDER from querys
2023-04-25 23:26:25 +02:00
Sarjuuk
acb9c60c9a Profiler/QuickInfo
* remove padding from profiles icon list
2023-04-25 23:23:49 +02:00
Sarjuuk
934066fed8 Tooltips/Style
* also fix width for secondary tooltips
2023-04-25 23:23:41 +02:00
Sarjuuk
e916deaafc Setup/Fixups
* sanity check slot / invtype realtion on setup (and hide offenders)
 * hide internal/unused items by name part
 * always truncate table to get rid of old data
 * fixing one->many relation revealed by replacing REPLACE with INSERT in creature setup
2023-04-25 23:23:19 +02:00
Sarjuuk
1130581152 Items/Stats
* resolve more col name conflicts .. should probably rename the cols in the table itself...
2023-04-20 22:04:25 +02:00
Sarjuuk
5c227c01e4 Currency/PvP
* convert and assign correct icons for honor points and arena points from /Interface/PvPFrame
2023-04-20 19:53:22 +02:00
Sarjuuk
a6897ad2f8 Misc/Fixups
* fixed more error pages without styles/scripts
 * fixed sourcemore for items if more then 300 items where requested at once
 * fixed item name localization in rare cases
2023-04-20 19:51:46 +02:00
Sarjuuk
5be5c2b59e Guides/Listview
* display class/spec in category if applicable
 * make class/spec searchable
 * unify class/spec display with tooltips
2023-04-18 17:20:40 +02:00
Sarjuuk
9c8656f4b5 Admin/Guides
* log approver / time as intended
2023-04-18 15:45:01 +02:00
Sarjuuk
14658a5016 Pages/Scripts
* do not skip generic page constructor ... ever
2023-04-15 11:46:21 +02:00
wodim
5f708470fc Localization/Typo (#354)
* small typo
2023-04-13 22:04:24 +02:00
Sarjuuk
f2a0e75bb1 Scripts/Fixup
*  fix tooltip localization after 9f1cbc0233
2023-04-13 19:26:23 +02:00
Sarjuuk
4f13c492f3 Pages/Icon (#360)
* fixed missing icons in used-by-spell tab
2023-04-13 17:48:10 +02:00
Sarjuuk
6123b6bafc JS/jQuery
* workaround jQuery.htmlPrefilter vulnerability
2023-04-13 17:34:52 +02:00
Sarjuuk
2c142c506c Search/Forms (#383)
* do not prefil search form with unchecked user input
 * thx @Endalaust
2023-04-13 17:34:39 +02:00
Sarjuuk
9f1cbc0233 HTML/Scripts
* append a filemtime timestamp to js/css files to work around browser caching after update
 * shuffle script handling around a bit
 * also user pages cant have community content
 * also fix breadcrumbs on items page
2023-04-13 17:30:23 +02:00
Sarjuuk
b06d1a5c2c Pages/Caching (#380)
* move localized option sorting to postCache() to prevent real order display in cached versions
2023-04-13 17:26:27 +02:00
Sarjuuk
beb32da3b4 Itemset/Summary (#351)
* disable compare button for empty item sets
 * do not display summary for empty item sets
2023-04-13 17:25:42 +02:00
Sarjuuk
2e82bf84d2 Pages/Spell (#378)
* fixed effect index offset on DetailPage
2023-04-13 17:25:31 +02:00
Sarjuuk
25ddb0ca99 Items/FAP
* show Feral Attack Power on weapons a druid can actually use (incuding 1H-weapons, excluding swords & axes (usually))
2023-04-13 17:25:17 +02:00
Sarjuuk
02239b4f74 Misc/Fixups
* fix text vars in tooltips for missing entities
 * fix notice in weapon GS calculation
 * reduce tooltip max width slightly to prevent overlap with secondary tooltip
2023-04-13 17:21:55 +02:00
Sarjuuk
e0a3c44776 Setup/Spells (#374)
* fixed rarity color for spells that create items
2023-04-13 17:20:50 +02:00
Sarjuuk
e513e01b29 Setup/TalentIcons (#382)
* fix icon order for pet talents
2023-04-13 17:16:49 +02:00
Sarjuuk
138dbbc8a5 Game/Text
* try to catch more html-like constructs in text blobs and escape them
 * ...<hic>!
2023-04-13 17:16:01 +02:00
Sarjuuk
6f87870e09 Item/Tooltips (#368)
* fixed broken link to required event
2023-04-13 17:15:53 +02:00
Sarjuuk
2210c0c4c5 Spells/Auras (#379)
* get auras from creature_template_addon for tabs
   - npc: abilities
   - spell: used by npc
2023-04-13 17:15:28 +02:00
Sarjuuk
8ab8eee1f4 Tooltips
* revert e092a69175 as it causes the tooltip to grow when moving the mouse.
 * use CSS to solve
2023-04-13 17:13:38 +02:00
Sarjuuk
d77e459da3 Tooltips/TimeFMT
* use strings from globalstrings.lua to format time in item and spell tooltips
 * fixed item filter ItemCooldown
 * fixed timeAgo format
 * move item duration to correct position in tooltip
2023-04-13 17:12:58 +02:00
Sarjuuk
6ee0d63766 Update external links to point to wotlk speciffic database 2023-04-13 17:12:02 +02:00
Sarjuuk
0c47f262ea Userdata
* replace input filter FILTER_UNSAFE_RAW (+ STRIP flags) with regex checks to preserve \n and utf8 chars
2023-04-13 17:07:59 +02:00
Sarjuuk
ffa4cf5b29 Misc/Fixes
* resolve more spellEffects/Auras
 * fixed item filter with multiple upgrade items
 * localized unknown spellAura/Effects text
 * remove unreferenced error code from image setup
2023-04-13 17:05:30 +02:00
Sarjuuk
ca26955ac2 Setup/SQL
* change sql batching to account for non continuous indizes in world tables
2023-04-13 16:48:52 +02:00
Sarjuuk
f05fe60030 Spells/Tooltips
* fix level slider for spells with buffs but without tooltips
2023-04-13 16:31:09 +02:00
Sarjuuk
2b15c13e88 User/Guides
* fixed copy/paste fail
2023-04-13 16:16:58 +02:00
Sarjuuk
df1ba841c5 Item/Tooltip
* display hidden/cosmetic spells for staff
2022-06-10 16:58:18 +02:00
Sarjuuk
47da18b717 Profiler/Caching
* redirect to search for subjects without cache, instead of hanging on an empty profile
 * todo: fix properly
2022-06-10 16:41:03 +02:00
Sarjuuk
117ab617b6 SAI/Teleport (#346)
* fixed resolving teleport target for NPCs
2022-06-10 16:00:33 +02:00
Sarjuuk
7e5659f49f NPC/Waypoints
* improve display for NPCs traversing zones and/or floors (e.g.: npc 844)
2022-06-10 15:15:28 +02:00
Sarjuuk
e493acca0d Item/XML
* fix output for currency, quest and key subclasses
2022-06-10 14:40:30 +02:00
Sarjuuk
6de6853cfe Core/PHP
* bump version requirements to v8.0
2022-06-10 14:00:35 +02:00
Sarjuuk
8ec6cc548d Merge remote-tracking branch 'origin/master' 2022-06-10 13:58:55 +02:00
Sarjuuk
c0e9159c1e Misc/Fixups
* colAddins should be null if not in use
 * fix index warnings in search + profiler
 * cast url param 'locale' to int for all uses
 * fix breadcrumbs for sounds page
 * fix determining actionOwner for SmartAI
2022-06-10 13:58:13 +02:00
Dima
1bd752a60f Typo/Spells (#343)
* fixed legacy typo in spell power calculation
2022-05-30 16:27:56 +02:00
Sarjuuk
df3694b539 Setup/Mails (#340)
* fix borked setup after b3e8f5e50f
 * thx @jackpoz for the research
2022-05-08 00:22:10 +02:00
Sarjuuk
6594d6fa42 Spells/Tooltips
* htmlify \n in spell tooltips & buffs
 * allow recursion of Util::parseHtmlText()
2022-04-05 18:32:41 +02:00
Sarjuuk
8425eeb685 Reports
* move related functions to separate class
 * implement out of date comment handling shorthand
 * implement admin interface to work with reports
   - listing based on user group
   - assign to self / unassign from self
   - close with reason
   - comment functionality
   - reward reputation to creator based on resolution
2022-04-05 15:45:56 +02:00
HelloKitty
2daa724720 README/Typo (#337)
Fix typo in the Special Thanks section.
2022-03-30 13:18:16 +02:00
Sarjuuk
6db77ed4f2 CommunityContent/Listing
* generally use indexed/non-asssociative arrays when returning content
2022-03-28 16:25:09 +02:00
Sarjuuk
b08d30d043 Screenshots/Crop
* fixed coordinate filter broken after a8edf6c912
2022-03-28 00:58:43 +02:00
Sarjuuk
b3e8f5e50f DB/Mails
* add field cuFlags to Type:Mail storage (RandomPage search no longer randomly fails, when randomly selecting mails)
2022-03-25 16:41:58 +01:00
Sarjuuk
1f5e2645f0 Misc/Fixups
* forgotten rename fails
2022-03-25 16:41:58 +01:00
Sarjuuk
04e55b5498 NPCs/Vendors
* show restock time if available
2022-03-25 16:41:49 +01:00
Sarjuuk
32b4c451e4 Misc/Fixup
* remove unnecessary tab components from ?unrated-comments page
 * fix urls generated by guides listview
2022-03-24 10:38:45 +01:00
Sarjuuk
05f6d68070 Utility
* implemented page: unrated-comment
2022-03-23 19:28:40 +01:00
Sarjuuk
e572967c08 Misc/Fixup
* lost changes to timestamps from a8edf6c912
* dates on comment, screenshot, video previews are now formatted correctly
2022-03-23 11:19:00 +01:00
Sarjuuk
1dc8d50289 Misc/Fixup
* move guide localization, so Lang::typeName can pick it up
 * rename forgotten TYPE_* definitions
2022-03-22 22:27:19 +01:00
Sarjuuk
7caabc0fa8 Misc/Fixup
* obligatory after-the-fact fixups
2022-03-22 19:07:47 +01:00
Sarjuuk
d819bf60f5 Merge remote-tracking branch 'origin/master' 2022-03-22 15:57:18 +01:00
Sarjuuk
b890d6504e Guides: initial implementation
* a guide is a wrapper around an article providing management tools.
 * administration is limited to the review process. Needs to be expanded.
 * articles on DB pages are seperate. Editor will be added in the future.
2022-03-22 15:43:39 +01:00
Sarjuuk
33a870ef78 Structure/Types
* move distributed constant values to object
 * move reused iterators and checks to object
2022-03-22 15:05:50 +01:00
Sarjuuk
e109a6deed Siteconfig/Email
* make hardcoded email configurable
2022-03-22 14:59:45 +01:00
Sarjuuk
feb6ee25dd Misc/Fixups
* lost changes
2022-03-22 14:59:13 +01:00
Sarjuuk
a8edf6c912 POST/GET
* unify accessing &_GET and &_POST data
 * properly calc and display diffTime
2022-03-22 14:55:43 +01:00
Sarjuuk
3cb02f2204 JS/CSS
* unify handling
2022-03-22 14:45:55 +01:00
Sarjuuk
4b32811424 Tooltips/Errors
* do not send 404 header for xml or tooltips
2022-03-18 17:28:39 +01:00
Sarjuuk
4972cc0faf POST/GET
* unify accessing &_GET and &_POST data
2022-03-17 13:47:48 +01:00
Sarjuuk
65bfd93761 Profiler
* truncate local profile if sync with game server fails (entry deleted)
 * do not use chars/guilds/arena teams with empty names
2022-03-13 15:03:11 +01:00
Sarjuuk
e092a69175 JS/Misc
* grid tables no longer clear floating elements (have an eye out if this broke something)
 * add 1px to inner tooltips so it doesn't unexpectedly line break
2022-03-11 15:13:29 +01:00
Sarjuuk
1c7acf6a08 CommunityContent/Misc
* granularly define inclusion of comments, screenshots or videos
 * i hope everyone is on a 2y old php version by now
2022-03-10 14:09:38 +01:00
Sarjuuk
3a98201837 Zones/Locations
* fixed Quick Info defaulting expansion maps to have [0, 0, 0] as parent
 * corrected looking for custom excluded flag when collecting instances for area
2022-03-08 18:52:01 +01:00
Sarjuuk
012ebe578b Items/Filter
* apply stats and name suffix to result set for filter #124 - random enchantment
 * fix filter #125 - required arena team rating
 * fix filter #85 - objective of quest
2022-03-06 09:52:19 +01:00
Sarjuuk
e125cab690 Search/Spells
* do not search for npc abilities under misc. spells tab
2022-03-04 10:27:19 +01:00
Sarjuuk
a1e7bfaa02 Quest/Detailpage
* fixed breadcrumb trail for quests categorized to subzones
 * display auto accept status in infobox
2022-03-04 10:01:30 +01:00
Sarjuuk
73c1118601 Setup/Update
* reapply flags for community content when rebuilding DB
   fixes associated filters and meta page 'missing screenshots'
2022-03-03 19:07:10 +01:00
Sarjuuk
33ec14f232 Merge remote-tracking branch 'github/master' into ghMaster 2022-02-28 21:03:36 +01:00
Sarjuuk
366f68f24f Setup/Misc
* squash some more php warnings during setup
 * automaticly sync if required by updates
2022-02-28 21:01:07 +01:00
Sarjuuk
91f8fe8925 Setup/Mails
* add lost world db dependencies to script
2022-02-28 12:52:09 +01:00
Sarjuuk
8431329645 SmartAI/Spells
* fixed fetching casters for spellId (as seen on 'used by' tabs)
2022-02-28 09:11:22 +01:00
Sarjuuk
19584feab0 Setup/Itemset
* fixed missing items in set
 * fixed php warnings during setup
 * fixed triggering scripts when changing config values
2022-02-27 23:53:36 +01:00
Sarjuuk
bc71ae762c Setup/Data
* move custom data from setup scripts to DB
 * move strings from DB to locale files
 * use common function to calculate coordinates of dungeon entrances instead of inline sql
 * fixed manual data zones and itemset
 * fixed typos across the board
2022-02-21 23:31:14 +01:00
Sarjuuk
10805a1f70 Profiler/Regions
* add Asia regions
 * fixed some typos
2022-02-20 22:47:54 +01:00
Sarjuuk
a24e8594fb Profiler/Localization
* support non-latin realm names for guilds and arena teams also
2022-02-16 04:17:40 +01:00
Sarjuuk
01a9744ba7 Locks
* reworked how an dwhen locks on GameObjects and Items are displayed
 * added structure for LockType.dbc
2022-02-14 23:17:51 +01:00
Sarjuuk
53e2af2116 Profiler/Localization
* support non-latin realm names
 * actually set characters as updated after resync was skipped due to character not having logged in since last resync
2022-02-14 16:04:57 +01:00
Metalaka
4255933328 Creature/Loot
* display skill condition on drop (#325)
2022-02-13 23:44:36 +01:00
Sarjuuk
1856f0ef8f README/QA
* added another repeat gotcha to the Q+A section
2022-02-13 23:43:12 +01:00
Sarjuuk
731f648cd2 Setup/Misc
* wrap db test in try/catch block
 * escape more mysql8 keywords
2022-02-12 15:22:52 +01:00
Sarjuuk
6a2511866e Conditions/Localization
* fixed index for descriptions
 thx @Faq for noticing
2022-02-12 14:37:32 +01:00
Sarjuuk
b777262670 Merge branch 'master' of github.com:Sarjuuk/aowow 2022-02-12 14:27:10 +01:00
Sarjuuk
c950cdc757 Setup/Misc
* fixed variable name: setup parameter --locales should work again.
 * escaped GROUPS as keyword in mysql8
2022-02-12 14:21:07 +01:00
Sarjuuk
541224f87b Markup/Misc
* fixed ajax fulltext sanitizer eating \s instead of \n after 57864d2544
 * happy new year
2022-01-06 14:21:51 +01:00
Sarjuuk
dcb8995b1a Markup
* allow 'auto' as width for tables
 * db tag 'money' may include currency AND items
2022-01-05 23:32:06 +01:00
Sarjuuk
2ec4809c7f Spells/Reagents
* try to fix wrong reagent amount in cases where one reagent requires multiples of itself and is triggered by an item
2022-01-05 23:27:44 +01:00
Sarjuuk
061449686d Revert "[WIP] Conditions"
This reverts commit a1f0f3d30d.
2021-11-21 09:50:59 +01:00
Sarjuuk
f3f8dacef9 Profiler/Talents (#315)
* fix usage of active talent spec
2021-11-19 20:30:16 +01:00
Sarjuuk
a1f0f3d30d [WIP] Conditions
* add missing conditionsTypes

todo:
 process params in Util::getServerConditions()
 add locale strings to js
 extend verbose display in global.js ConditionList
2021-11-04 22:49:12 +01:00
Sarjuuk
e4b8a8e908 Zone/Mapper
* show fishing nodes on map
2021-11-04 22:40:07 +01:00
Sarjuuk
41f415095a JS/Tooltips
* define tooltip_noequipcooldown
2021-11-04 22:39:58 +01:00
Sarjuuk
b824bc54c2 ItemList/ExtendedCost
* restore internal list pointer after using iterator get extended costs
2021-11-04 22:39:07 +01:00
Sarjuuk
35aca18346 JS/Markup
* enable "event" and "currency" codes for regular users
2021-11-04 22:38:55 +01:00
Sarjuuk
4b37a4fd70 CreatureText/Formating
* removed excess escape on talker name
2021-11-04 22:38:43 +01:00
Sarjuuk
c665062faa Staff/vehicles
* fixed error when trying to lookup accessory creatures in world db with aowow guids
2021-11-04 22:38:30 +01:00
Sarjuuk
fca627c408 * fixed bitshift by negative number issue when evaluating spell MiscValues
* defined upper limit for localeIds and also discard negative localeIds
* fixed eval error when parsing spell descriptions. Calculated numbers sadly can't be localized.
2021-11-04 22:37:15 +01:00
Sarjuuk
ea25776225 Zone/Map
* display mailboxes on zone map
2021-11-04 22:36:26 +01:00
Giacomo Pozzoni
5781dfb69e Fix syntax error (#312)
Fix wrong bracket order revealed by PHP 8
2021-10-27 19:52:15 +02:00
Sarjuuk
60e3f62504 Update README.md
Added two more 'gotchas' to the README.md
2021-10-26 18:53:53 +02:00
Sarjuuk
9cda84ef88 added but report template 2021-10-26 10:57:57 +02:00
Sarjuuk
3495a7a40d Achievememnt/Series
* keep series zero indexed like quests
2021-10-24 21:29:33 +02:00
Sarjuuk
8acc93c325 Search/Tabs
* switch between single and multi-class display as necessary
2021-10-24 21:20:46 +02:00
Sarjuuk
4755058fd0 Merge branch 'master' of https://github.com/Sarjuuk/aowow 2021-10-24 20:59:47 +02:00
Sarjuuk
306f8eb9b6 Locale/enUS
* addition to 5fe9c23be2
2021-10-24 20:59:25 +02:00
wodim
5fe9c23be2 Fix category name in enUS (#239) 2021-10-24 20:58:38 +02:00
Sarjuuk
68e262d96c Locale/zhCN
* analogue to 8727d4f66f
2021-10-24 20:55:33 +02:00
Dima
8727d4f66f Update locale_ruru.js (#291)
missing space
2021-10-24 20:55:02 +02:00
Sarjuuk
07b9ed4275 Setup/CLI
* also fix empty --build param building all files
 * fixed copy/paste typo
2021-10-24 20:41:44 +02:00
Giacomo Pozzoni
cb2274bbab Fix php aowow --sql not doing anything (#307) 2021-10-24 20:40:50 +02:00
Stefano Borzì
7c293c405f remove unused static method getName() in SoundList, close #293 (#294) 2021-10-24 20:30:14 +02:00
Sarjuuk
6819c1b1f2 Misc
* fixed misc php8 errors that went unnoticed as php7 warnings before
 * added lost areatrigger generator to Setup script
 * updated readme with working TCDB info and php 8.0 support
2021-10-24 19:47:45 +02:00
Sarjuuk
57864d2544 PHP8/AjaxHandler
* filter callback function must be defined static
 * fixed some typos
 * fixed commentbody filter callback eating \n chars
2021-10-24 19:23:53 +02:00
Sarjuuk
f9ed75d5af SmartAI/Spells
* fixed fetching spells from SmartAI for Abilities tab

Co-authored-by: jackpoz <giacomopoz@gmail.com>
2021-10-24 18:12:53 +02:00
jackpoz
453690c575 Fix error on windows when no parameters are passed in query string 2021-10-24 17:35:24 +02:00
Sarjuuk
557638b3fe PHP8/Misc
* reduce redundancy after merge
2021-10-24 17:33:47 +02:00
jackpoz
f9584f09b5 Handle is_callable returning false with instance methods on static types since PHP 8
See https://php.watch/versions/8.0/non-static-static-call-fatal-error
2021-10-24 17:15:26 +02:00
Sarjuuk
6b49aa6069 Setup/Misc
* fix some outdated array indizes during setup
 * fix indexing when connecting to DB for first time
 * add initial loading of config strings during setup
2021-10-24 17:12:05 +02:00
Sarjuuk
56e70e22bb Item/DetailPage
* fixed infobox cost for items with buycount > 1
2021-03-14 16:31:35 +01:00
Sarjuuk
6f69144498 Merge remote-tracking branch 'github/master' into ghMaster 2021-03-14 16:13:39 +01:00
Sarjuuk
aa82655845 SmarAI/Lookups
* unify lookups in functional groups
 * move code to class SmartAI
2021-03-14 02:04:56 +01:00
Sarjuuk
c456ade870 Skills/Breakpoints
* display skill learning breakpoints for gathering skills
 * fixed mixed up mining-skinning and savanging-skinning filters for NPCs
 * defined some magic skill numbers
2021-03-14 02:04:43 +01:00
Dima
aa70ea0323 Localization (#284)
use multibyte function to manipulate localized text.
2021-03-02 20:24:11 +01:00
Sarjuuk
fbc5d43aab Setup/Maintenance
* toggle maintenance mode in a more reasonable manner (i.e. not when displaying help test)
 * move connectivity tests to class DB
 * restore generate everything functionality when running --sql and --build with an empty parameter set
2021-02-20 20:35:53 +01:00
Sarjuuk
43778b01e7 Setup/DB
* connect to db after successful setup so siteconfig doesn't fail
2021-02-18 22:31:30 +01:00
Sarjuuk
f5701e7979 Misc/Config
* add config values lost from dd63a6a2ab
 * update TCDB compatibility info
 * update current year
2021-02-18 19:00:55 +01:00
Dima
33c3132346 Localization/ruRU (#281)
* add reputation strings
2021-02-18 13:36:29 +01:00
Sarjuuk
d6ca3d70cf Listview/Percent
* use same logic for display and sorting
2021-02-15 20:07:49 +01:00
Giacomo Pozzoni
392b5bbdda CLI/Typos (#272)
* fixed typos in log
2021-02-15 19:48:35 +01:00
Sarjuuk
e810554695 Mapper/Instances
* do not display unmapped instances (3.3.5 from data) by default
2021-02-15 19:30:48 +01:00
Sarjuuk
8313bb4194 Icons/DetailPage
* don't cast name to ucFirst
2021-02-15 19:16:10 +01:00
Sarjuuk
0c1b73d6ac Icons/Names
* icon names can contain spaces. So indiscriminate trimming should be discouraged.
 * ...
 * unrelated stuff: please don't break...
2021-02-15 19:08:56 +01:00
Sarjuuk
7a74c36448 Setup/Profiler exclusions
* set time limit to generation of profiler exclusions file
 * remove some unused logging
2021-02-15 18:20:01 +01:00
Sarjuuk
23abce304c Readme/Setup
* use more intuitive setup command. "firstrun" is still available but usage is discouraged.
2021-02-15 17:59:36 +01:00
Sarjuuk
e5e62e2936 Merge remote-tracking branch 'github/master' into ghMaster 2021-02-15 17:55:24 +01:00
Dima
c0a1e393b0 Localization/ruRU (#278)
* fixed typo in user contribution template
2021-02-15 17:32:46 +01:00
Dima
898a4fd8a4 Localization/Typo (#279)
* fixed icons path in ruRU and frFR menus
2021-02-15 17:13:10 +01:00
Dima
3978169bf6 Localization/ruRU (#280)
* added missing zone and gameobject strings
2021-02-15 17:10:22 +01:00
Dima
01e89db491 Setup/Help (#276)
* fixed log command instructions
2021-02-15 16:50:25 +01:00
Sarjuuk
59f58f8506 Setup
* automatically set and unset maintenance mode when data is being edited (either sql or files)
 * added more verbose help prompts. They should now always appear when -h is used.
 * added --setup as more intuitive command for --firstrun. --firstrun is now considered deprecated and will be removed in due time.
 * unfuck order of indizes in --siteconfig
 * fixed some typos
 * convert echo use to CLI::write
 * move scattered command line argument checks to CLISetup
2020-12-30 00:29:30 +01:00
Sarjuuk
c65bd88867 Setup/RandPropPoints
* refer to correct dbc file to get item level scaling for random enchantments
2020-12-29 13:20:42 +01:00
Sarjuuk
2eaed47ba4 Items/Misc
* fixed queries erronously using ucFirst column names
 * fixed visible html in infobox
 * added forgotten string for locale 3
2020-12-28 23:27:49 +01:00
Sarjuuk
888ff28121 Item/DetailPage
* alwaya display item level of gems regardless if cut or not
 * fixed similar item search result for items without class restrictions
2020-12-27 20:45:44 +01:00
Sarjuuk
baf47433dd Enchantment/DetailPage
* removed tooltip-highlight when there is no tooltip to display
Spells/Tooltips
 * fixed ancient copy-paste error and 'greater than or equal' conditions are now always evaluated as such
2020-12-27 20:07:57 +01:00
Sarjuuk
89275a0e71 Readme
* remove reference to expired branch
2020-12-19 01:06:30 +01:00
Sarjuuk
15fb7b8711 NPCs/DetailPage
* display difficulty versions of the creature in the infobox
2020-12-19 00:18:46 +01:00
Sarjuuk
467a31fa3b Template/Escaped Strings
* escape creature subnames in DetailPage
 * escape creature names & subnames in Tooltips
 * js escape inherited filter froms
2020-12-19 00:16:09 +01:00
Sarjuuk
5b414500a7 Creature/DetailPage
* display school resistance under stats in infobox
2020-12-19 00:00:49 +01:00
Sarjuuk
cac57f5cd8 Spell/DetailPage
* fixed recursing SmartAI ActionLists for used-by relations
2020-12-19 00:00:49 +01:00
Sarjuuk
3188a7b55f Template/generic detail page
* switch position of article and extra text section
 * pray the layout doesn't break somewhere unexpected
2020-12-19 00:00:49 +01:00
Sarjuuk
4ed2d0836f Localization/Invisibility
* set placeholders for unknown but used invisibilityTypes to suppress errors on access
2020-12-17 22:25:25 +01:00
Sarjuuk
be77474637 Misc/Optimizations
* general minor cleanups
 * do not sync characters that haven't logged in since last resync.
2020-12-17 22:24:42 +01:00
Sarjuuk
82d3a8508d Localization/Placeholders
* apply changes from d070b303b4 to placeholder texts
2020-10-25 17:56:12 +01:00
wodim
d070b303b4 Localization (#237)
* removed excess hyphens
2020-10-25 17:49:26 +01:00
wodim
7062ff74a4 Localization/Typo (#226)
* Fix typo
2020-10-25 17:41:53 +01:00
Sarjuuk
df1f29c986 LinkedSpells/Localization
* Smoothed out word choice. Should be more clear now.
2020-10-25 17:27:24 +01:00
Sarjuuk
178c13ec72 SmartAI/SpellCasts
* fixed getting spell casts per NPC
 * fixed spell usage for NPCs when used by Timed Actionlists
 * added 'used by (smart) areatrigger' to SpellDetailPage
 * fixed forgotten rename from SmartAI overhaul
2020-10-25 17:09:05 +01:00
Sarjuuk
e2fe765980 SAI/Compat
* add changes from TC of the last three years
* reduce redundancy when handling creature text
* reserve more space in target, event, action parameters. Move own parameters further back to avoid confict with future param expansion by TC
2020-10-25 15:47:23 +01:00
Sarjuuk
003ac1c931 Listviews/Quests
* fixed displaying as many quest requirement columns as items requiring quests
2020-10-10 11:08:04 +02:00
Sarjuuk
0430d75989 Tooltips/Cleanup
* removed double escaped json
 * fixed itemset tooltips with classes
 * set cookies to samesite to avoid being rejected by the browser
2020-10-10 11:00:59 +02:00
Sarjuuk
59fb6c1bc9 DetailPage/GameObject
* display related faction in infobox
2020-10-10 10:14:13 +02:00
Sarjuuk
12c5f6f468 Setup/Maps
* fixed small map height (small maps used in tooltips)
 * map files need to be regenerated manually

thx @wodim
2020-09-16 21:29:04 +02:00
Sarjuuk
c92ab37de1 Misc/Typo
* fixed typo in SAI localization

Co-authored-by: wodim <neikokz@gmail.com>
2020-09-16 20:08:13 +02:00
Sarjuuk
88417cd5a6 Setup/Spawns
* added required areatable.dbc to spawns setup script, forgotten in 62acd541b2
2020-09-11 23:08:03 +02:00
Sarjuuk
583f8658d7 Pages/Caching
* use native functions of ReflectionProperty instead of magic numbers. GetModifiers() may return different values then expected/documented
2020-09-02 21:17:05 +02:00
Sarjuuk
04a14393e5 DB/MySQL
* fixed some more use cases of 'rank'
2020-09-02 21:12:00 +02:00
Giacomo Pozzoni
f34b4827ee DB/MySQL (#219)
* fixed usage of field name 'rank' which became a keyword in MySQL 8
2020-09-02 20:58:38 +02:00
Sarjuuk
aac9aab3c9 Spells/DetailPage
* diplay filtrable spell attributes in overview table
 * replace some magic numbers with constants
2020-08-31 18:21:53 +02:00
Sarjuuk
349c32d0a6 SAI/Targets
* add forgotten change from 243429bf68 to target tooltips
2020-08-30 18:28:28 +02:00
Sarjuuk
5179e6e09b SAI/Typo
* fixed typo in SmartAI event handler 76 bricking related object/npc pages
2020-08-30 18:04:57 +02:00
Sarjuuk
366a0f2040 Game/worldPosToZonePos
* catch DB errors resulting in wrong return type
2020-08-30 17:33:27 +02:00
Sarjuuk
93948a2ec5 Spells/DetailPage
* resolve spell effect #86 with info from 203573db83
2020-08-06 16:24:35 +02:00
Sarjuuk
3e24814518 README/Conf
* check for presence of lib GMP when trying to enable TC auth
 * also added missing requirement to README (thx @jackpoz for reminding me)
2020-08-03 22:11:05 +02:00
Sarjuuk
726fe90bb0 README
* hear ye hear ye! the regulations have changed!
 * while the master branch will now use SRP6 for TrinityCores auth, the branch sha1-auth will be available for about three months to ease migration.
 * until Nov. 2020 updates will be applied to both branches
2020-08-03 15:36:49 +02:00
Treeston
fb2ab8f613 SRP6 for user auth (#38)
* SRP6 for user auth, implementation of 3164b58c7d
2020-08-03 15:20:34 +02:00
Sarjuuk
72e950713a Achievement/Criteria
* use reference achievement for criteria if set
2020-07-25 21:53:23 +02:00
Sarjuuk
ce90229a8b Quest/Rewards
* implement changes to max level xp conversion from 670085d8c0
2020-07-25 21:04:36 +02:00
Sarjuuk
4bd32207db Setup/Updates
* skip unexpected characters in update fields of aowow_dbversion
2020-07-20 21:14:06 +02:00
Sarjuuk
844a4c0e52 Setup/Fixups
* added lost changes from
   321f28d35c and
   62acd541b2 to setup
2020-06-25 19:27:33 +02:00
Sarjuuk
d858c85465 Items/Tooltips
* display ppm in tooltip
2020-06-07 21:53:03 +02:00
Sarjuuk
82d3441b22 Spells/DetailPage
* expand used by - tabs to show spells from timed action lists and smart_script casts from gameobjects in general
2020-06-07 21:52:55 +02:00
Sarjuuk
c08773eb6f Screenshots/Misc
* no functional changes
2020-06-06 17:30:04 +02:00
Sarjuuk
240024fb10 Summary/Itemsets
* fixed set bonus calculation when using multiple sets in one summary

Localization
 * create more concise error messages when accessing nonexistant locale strings
2020-06-06 15:55:54 +02:00
Sarjuuk
321f28d35c Achievements/Category
* moved names from db to locale files
2020-06-04 00:56:59 +02:00
Amandil
8e14e4a9f3 fix signin url for locale_frfr (#208) 2020-06-03 20:32:19 +02:00
Sarjuuk
184e5e5c8d Setup/DB
* fixed input prompt broken after iputting aowow's info
2020-06-01 12:24:54 +02:00
Sarjuuk
7b9b542e65 Merge remote-tracking branch 'github/master' into ghMaster 2020-05-30 11:50:21 +02:00
Stefano Borzì
a3d35a7ad5 Update credits (#205)
* Update credits
2020-05-29 18:53:22 +02:00
Sarjuuk
366bdf54a6 User/Cookies
* additional fixes against announcements not staying closed
2020-05-28 19:37:54 +02:00
Sarjuuk
d22e90ca85 DB/Logs
* set up some query profiling
2020-05-28 18:43:24 +02:00
Sarjuuk
c290f845d6 Quest/DetailPage
* added tab and note with quests from the same pool if available

 * fixed announcements poping up after being dismissed previously
 * fixed double escaping of quest tooltips
2020-05-28 18:43:14 +02:00
Sarjuuk
8741c7479f Misc/Fixups
* vehicle accessories now get moved with their vehicle
 * try to account for multilevel zone offset nonsense
 * fixed some more typos and unaccounted null cases
2020-05-27 15:27:13 +02:00
Sarjuuk
62acd541b2 Maps/Spawns
* Entities (Objects, NPCs, ect) can now easily be assigned to a different map to be displayed on by clicking their pip on the map
 * Entities with already assigned area (by TrinityCore) that were unable to be matched onto the map are no longer discarded. They'll now show up in appropriate listviews.
 * Entities without already assigned area that are also unable to be matched onto the map now get an area assigned as long as the relationship areaId <=> mapId is unique (read instanced areas)
2020-05-26 21:11:15 +02:00
Sarjuuk
7db841b823 Misc/Typo
* fixed typo from 6cabfd3
2020-05-25 22:26:45 +02:00
Sarjuuk
6cabfd3864 Misc/Cleanup
* moving commonly used strings to defines
 * moving commonly reused/similar page generation functions to the parent
 * generally using consistent return types, more type hints and less strings
 * prevent browser context menu when right clicking on UI elements with their own context menus
 * fixed menu path for icons
2020-05-24 15:04:42 +02:00
Sarjuuk
b044488308 Profiler/Recipes
* _really_ fix learning starter recipes per skill attempted in 96bbe326a8
2020-05-18 20:23:23 +02:00
Sarjuuk
a1d3be86f7 Profiler/Talents
* fixed column reference changed in f8230a59a9
2020-05-09 16:57:45 +02:00
Sarjuuk
8378354f8b Creatures/Objects
* added some missing links between boss npcs and loot chests
 * use name of group encounters from dungeonencounter.dbc instead of single creature
Profler
 * remove link to blizzard arsenal
 * truncate long titles in raid progression tracker
2020-05-03 16:52:00 +02:00
Sarjuuk
08f8297629 Objects/SpellFocus
* added tab to DetailPage with spells requiring this object to be cast
2020-04-26 14:22:11 +02:00
Sarjuuk
9b4c0bb09e Zones/Misc
* hide some more unused zone
 * link zones Plaguelands: Scarlet Enclave and Onyxia's Lair to correct map
 * fixed wotlk raids all being heroic
 * link icecrown citadel sub maps to master and hide sub maps
2020-04-19 18:42:10 +02:00
Sarjuuk
92a1016a0f Spells/Misc
* display dbc definitions before articles on detail pages
 * fixed logic when displaying skill step numbers
 * made custom spell columns for linked spells and stack rules localizable
2020-04-19 16:51:34 +02:00
Sarjuuk
08c98bc8ee Search/Spells
* fixed some spells being skipped in search (hopefully)
2020-04-10 23:01:15 +02:00
Sarjuuk
b7bf58a664 Profiler/Excludes
* exclude some more unobtainable receipes
2020-04-10 12:35:53 +02:00
Sarjuuk
64ce4826c6 Profiler/Excludes
* tag some more receipes as unavailable
2020-04-05 15:14:49 +02:00
Sarjuuk
96bbe326a8 Profiler/Spells
* fixed applying auto-learned profession spells
2020-04-05 14:24:08 +02:00
Sarjuuk
0f9a3e8cb6 Profiler/Custom
* fixed deleted profiles counting towards the user cap of 10
2020-04-05 12:31:59 +02:00
Sarjuuk
9f8c643cf4 Filter/Criteria
fixed
 * Quest: class/race-specific
 * NPC: skinnable
2020-04-05 11:58:36 +02:00
Sarjuuk
c2a1556e8a Loot/Refloot
* fixed Reference Loot's drop chance counting towards the adaptive drop chance of loot within the same loot group.
2020-04-04 15:20:51 +02:00
Sarjuuk
15246ea964 Util/Time
* fixed time formatting for milliseconds
 * only process absolute time values

Co-authored-by: Helias <stefanoborzi32@gmail.com>
2020-04-01 22:29:36 +02:00
Sarjuuk
3b749025de Items/Itemsets
* fixed erronous linking to the baseline set for virtual sets
 * display items from the same set, for the same slot in the 'see also' tab
2020-03-28 15:56:24 +01:00
Sarjuuk
bf2e1bd612 Profiler/DB
* implement changes from https://github.com/TrinityCore/TrinityCore/commit/1925778e44a
 * thx @kep123 for pointing these out
2020-02-28 22:04:33 +01:00
Sarjuuk
301c29e944 Localization/Profiler
* added some more strings for loc 4 (zhCN)
 * thx @kep123 for providing those
2020-02-28 22:04:03 +01:00
Sarjuuk
54b20c3131 NPC/Spells
* always treat spells from creature_template_spell as controlled abilities
2020-02-26 17:29:33 +01:00
Sarjuuk
55d19589b8 Merge remote-tracking branch 'github/master' into ghMaster 2020-02-26 16:37:48 +01:00
Sarjuuk
59ef124a26 SmartAI
* fixed several ocurences of time strings being cast to int
 * always use short time format
 * also fixed that one questionmark typo that kicked this whole commit off
2020-02-26 16:33:31 +01:00
Sarjuuk
47458e3ec2 Spell/Effects
* also link back from creature to summon spell for spell effects 56 & 112
2020-02-24 23:33:01 +01:00
Giacomo Pozzoni
4d08fb974d Escape MySQL 8 keywords (#193) 2020-02-24 22:02:49 +01:00
Sarjuuk
3f1e44d3e2 Titles/DetailPage
* fix page titles for .. titles
 * escape htmlesque tags not in the template but individually as needed
2020-02-24 21:40:50 +01:00
Sarjuuk
aa66a7644b Areatrigger/Names
* fix unnamed areatrigger after fetching them from DB and not clientside
Mail/Names
 * fix unnamed mails after fetching them from DB and not clientside
2020-02-24 02:00:37 +01:00
Sarjuuk
3ff855afe8 Setup/Mails
* index can actually be negative
2020-02-24 01:36:42 +01:00
Sarjuuk
3c088cd4e8 Spell/Effects
* parse npcs for spell effect 56 and 112
2020-02-23 22:17:16 +01:00
Sarjuuk
0694367ee9 Spell/Effects
* parse triggered spell from effect 140 and 141
 * generally display non-zero effect values
2020-02-23 21:36:26 +01:00
Sarjuuk
4bda806b9b DB/Mails
* add lost column `attachment`
2020-02-23 20:32:40 +01:00
Sarjuuk
b37efc480a Item/Icons
* handle icons containing whitespaces
2020-02-23 19:46:08 +01:00
Sarjuuk
717e9c43be Objects/Names
* fix unnamed objects after fetching them from DB and not clientside
2020-02-23 19:13:43 +01:00
jackpoz
1f84cae1dd Sort zones in ?npc=... by spawn count 2020-02-23 19:01:06 +01:00
Sarjuuk
5bb15d47d7 Misc/Readme
* update required TDB-version (hint: its never 'latest')
 * fixed artifacts from setup conversion
 * version push
2020-02-23 18:45:44 +01:00
Sarjuuk
16bd59e0cc NPC/Trainer
* add support for https://github.com/TrinityCore/TrinityCore/commit/bf3ab6d9c43
 * fixed some bugs causing conditions to not be displayed
 * fixed some trainer not being displayed for most skills
2020-02-23 17:29:12 +01:00
Sarjuuk
d4c0c0535a Quest/Breadcrumbs
* implemented https://github.com/TrinityCore/TrinityCore/commit/5ed77113b63
 * fixed some engrish
 * fixed a typo in typecasting string searches
2020-02-23 14:14:34 +01:00
Sarjuuk
eaa982e5b3 Totems/Display
* implment https://github.com/TrinityCore/TrinityCore/commit/915f8a9d2c2
2020-02-23 12:33:09 +01:00
Sarjuuk
48552a97ab Creature/Spells
* update db structure for https://github.com/TrinityCore/TrinityCore/commit/d5fb0a30ec6
2020-02-14 17:35:44 +01:00
Sarjuuk
243429bf68 SmartAI
* update db structure for https://github.com/TrinityCore/TrinityCore/commit/7634a57f64a
2020-02-14 16:54:36 +01:00
Sarjuuk
bc18ca174c Spells
* update db structure for https://github.com/TrinityCore/TrinityCore/commit/8edea4a3c29
2020-02-14 16:39:06 +01:00
jackpoz
1c08e5d9cf PHP/v7.3
* fixed warnings when migrating to php v7.3
 * https://www.php.net/manual/en/migration73.incompatible.php#migration73.incompatible.core.continue-targeting-switch
2020-02-14 16:26:38 +01:00
Sarjuuk
d3c5011694 Achievements
* update db structure for https://github.com/TrinityCore/TrinityCore/commit/352c4a4c680
2020-02-14 16:22:24 +01:00
Sarjuuk
c77cd92609 * updated footer to currentYear™
* revision push
2020-02-14 16:16:40 +01:00
jackpoz
1ff81ab07c Add support to https://github.com/TrinityCore/TrinityCore/pull/24102 2020-02-14 16:15:34 +01:00
Sarjuuk
1c7660316d Items/Gems
* fixed indexing error when calculating item enchantments introduced in c7fe84b7e0
2020-02-14 16:15:34 +01:00
wodim
eeb39bb83f Better wording for NPC loot containers 2020-02-14 15:19:02 +01:00
wodim
f99ff8c4b8 Fix typo 2020-02-14 15:19:02 +01:00
Sarjuuk
cf129ca3ce Pages/Quest
* escape htmlesque tags from quest titles for Infobox
2020-02-14 09:25:13 +01:00
Sarjuuk
fd04e9f977 Implemented new type: mail
* display and link clientside mails to other types and events
 * fixed favorites menu for new types
 * fixed sorting column triggered spells in enchantment listview
 * some misc cleanups
2020-02-14 00:36:41 +01:00
Sarjuuk
ccef11323b Setup/Scripts
* restructure setup to allow for self contained setup steps to self register (just the sql for now)
 * should ease adding new scripts in future
2020-02-14 00:36:41 +01:00
Sarjuuk
163e3d82b0 Spells/DetailPage
* fixed rogue reference causing created items to display as a different item

 thx @Rushor for pointing in the right direction
2020-02-12 23:59:35 +01:00
Sarjuuk
fc0902d476 Profiler/CSS
* fixed some batch-replace fails
 * year++++
2019-07-30 23:17:56 +02:00
Sarjuuk
0fd2944d8b Spells/DK
* fixed setup flagging wrong spells as hidden
 * fixed menu structure for DK spells not requiring DK class
 * fixed type for rune cost in spell tooltip
2019-01-04 17:19:14 +01:00
Sarjuuk
7f36dc87cf Mails
* apply table quest_mail_sender to display correct mail source
 * fixed mails from mailtemplate.dbc for achievement rewards
2018-12-15 00:21:52 +01:00
Sarjuuk
34fe4c2654 Creatures/Rank
* set unused ranks to 0 during setup to fix display issues
2018-12-14 23:30:26 +01:00
Sarjuuk
392610b899 Icons
* add icon to infobox for db types with icon (*duh!*)
 * fixed a couple issues with markup icondb in the process
 * fixed wrong data display due to shared name fields on joined table
2018-12-14 23:09:32 +01:00
Sarjuuk
ead6e72668 Page/Currency
* display price column in "currency for" tab
2018-12-11 19:55:02 +01:00
Sarjuuk
db012cfa8c Page/Search
* dropped support of wildcard characters
 * search for literal usage of underscore
2018-12-07 22:09:52 +01:00
Sarjuuk
09176d1ae9 Profiler/Names
* handle duplicate names due to rename flags more gracefully
 * should fix infinite reload bug on newly renamed profies
2018-12-02 19:25:43 +01:00
Sarjuuk
972a7f241e Misc/Fixes
* cast GET-params to lower case
 * fixed typo in constant name
 * fixed scope issue when aggregating sql stats
2018-12-02 19:22:21 +01:00
Sarjuuk
da1946df0f Misc/Fixes
* resolve two additional spell effects (11, 44)
 * fixed overzealous find/replace in Loot::iterate()
 * fixed wrong placeholder for type 32 in Conditions System
2018-12-02 00:55:34 +01:00
Sarjuuk
cc5be5261c Setup/DBconfig
* remove nonfunctional placeholder file
 * its too confusing
2018-12-01 20:03:26 +01:00
Sarjuuk
af7b9f57b0 Page/Search
* always display tab to denote what type was found
2018-11-30 18:33:59 +01:00
Sarjuuk
d9cd24026c PHP/Core
* enforced v7.1 compliance, preparations for v7.2
 * removed deprecated usage of each()
 * prefer array deconstruction via [] instead of calling list()
 * try to catch failed session_start()
 * prefer ... - Token instead of calling func_get_args() func_num_args()
 * enforce return types in AjaxHandler
 * revision push
2018-11-29 00:45:19 +01:00
Sarjuuk
f8a34aa98e Account/Cookies
* fixed saving cookie-data to account
 * e.g. announcements shouldn't pop up again if you close them once
2018-11-27 00:01:06 +01:00
Sarjuuk
13db19c64f Spells/Models
* fixed assigning same display to multiple spells from the same SpellList
 * fixes missing mounts in Profiler
2018-11-26 23:44:46 +01:00
Sarjuuk
04209cfc6d PHP-Version
* return type declarations require php v7.0 or newer
 * updated README and and version check accordingly
2018-11-20 19:00:20 +01:00
Sarjuuk
470498f63c Page/Achievement
* catch fatal error for achievements without criteria
2018-11-18 16:17:11 +01:00
Sarjuuk
484944bfc0 SmartAI
* parse and verbosely display smartAI for creatures, gameobjects and areatrigger
2018-11-18 15:31:22 +01:00
Sarjuuk
8620cbcf20 Setup/Quests
* update structure for TrinityCore/TrinityCore@2f5403d4af (Thx @TheWinchesters for noticing)
 * updated README accordingly
2018-11-18 15:31:14 +01:00
Sarjuuk
0286cb20f1 Spell/TaxiPath
* resolve taxi path routes on spells
 * fixed display of taxi paths on maps
2018-10-07 12:55:38 +02:00
Sarjuuk
31ec17d279 Pages/Quest
* escape placeholder tags in quest name for display
2018-08-26 16:49:29 +02:00
Sarjuuk
6f578b31e0 Pages/Achivement
* resorted criteria to match ingame order
2018-08-26 16:41:14 +02:00
Sarjuuk
0fd0a66137 Lang/Races
Hey Lang, what races does this thing have assigned?
  Yes!

   .. this will no longer happen
2018-08-16 21:49:16 +02:00
Sarjuuk
ceedf3fbdd Search
* made racial traits searchable under abilities
2018-08-11 23:13:09 +02:00
Sarjuuk
ed614d3938 Objects/Filters
* added filter for common spell foci
2018-07-29 22:41:32 +02:00
Sarjuuk
d32f21f9ca Spells/DetailPage
* display spells affectged by category cooldowns

Items/DetailPage
 * display items affected by category cooldowns defined in spell template
2018-07-20 17:34:33 +02:00
Sarjuuk
e0150feda6 implemented type areatrigger
* staff only
2018-07-16 21:08:18 +02:00
Sarjuuk
56ccc592a6 Sounds/DetailPage
* fixed generating links to play via the WoW API
 * fixed usage of not yet available PlaySoundKitID() when selecting file from list
 * while internally it is pretty laissez-faire about cases in file paths, suddenly WoW DOES care, when it comes to its LUA API.
   so .. don't cast to lowercase when storing sounds in DB
2018-06-11 23:17:55 +02:00
Sarjuuk
04e183f5e3 Spells/Filters
* add various filters (mostly attribute flags)
 * resolve additional dbc-data on detail page
 * fixed DK rune indexing
2018-06-06 22:31:56 +02:00
Sarjuuk
adc1273b08 Ajax/Debug
* be a lot more verbose when errors are occuring (to staff anyway)
 * made hardcoded error messages for comments localizable
 * add error messages from posting comments to session to be displayed on next page update
2018-05-26 21:02:19 +02:00
Sarjuuk
93a72013b8 Reports
* added timestamp column to db table
 * consolidated write operation
 * fixed storing reports when get_browser() would return empty
2018-05-26 15:59:01 +02:00
Sarjuuk
c5d28a9bbc Profiler/Chars
* prefix characters flagged for rename to prevent collision with real char names (multiple chars flagged for rename will still collide, but what the hoo haa)
 * alter table index to prevent creation and subsequent conflict with duplicate entries of the same char (realm/guid pair)
2018-05-26 13:29:23 +02:00
Sarjuuk
7dc283f649 Misc/Fixups
* squash multiple notices from error log
 * fixed copy&paste error preventing view on custom profiles
 * fixed display of spell requirements if mount was required after changs to mount sorting
 * properly get/send error code in profile synchronisation
 * use clear:left after floating mail block in template
2018-05-26 12:18:02 +02:00
Sarjuuk
9645ad0877 Localization/deDE
* kitsch is inappropriate???
2018-05-13 14:41:23 +02:00
Sarjuuk
45f325a0fa Admin/WeightPresets
* removed redundant code
 * fixed empty scales not being written to dataset as expected
2018-05-13 12:41:54 +02:00
Sarjuuk
37652ce011 Profiler/Raids
* fixed displaying individual encounters in raid activity tracker
2018-05-13 12:24:00 +02:00
Sarjuuk
594b2269bd Profiler/Profiles
* fixed regular characters being hidden by default
2018-05-13 12:03:44 +02:00
Sarjuuk
55a554ca06 Spells/Tooltips
* remove level scaling from tooltips (for now)
2018-05-11 13:14:06 +02:00
Sarjuuk
a0198ae5f4 Spells/Mounts
* group mounts into ground, flying & misc.
 * display mount speed bonus in listview
2018-05-10 22:14:13 +02:00
Sarjuuk
3bbd7f97da Item/ExtendedCost
* display a "sells" entry for every combination of currencies the vendor accepts per item
 * display every cost combination in the quick infos on the item detail page
2018-05-10 20:04:24 +02:00
Sarjuuk
761da59ee9 Localization/Chinese
* added support for locale 4
* some strings are missing and will need to be translated at a later date
* thx @qyh214 for a lot of content

Co-authored-by: qyh214 <sandy0214qin@msn.com>
2018-05-08 20:36:52 +02:00
Sarjuuk
09d2013012 Misc/Fixups
* fixed typo inlocalization
 * have the profiler queue run at least one cycle so it can get registered as successfully started
 * a map popup will no longer scroll the page to bottom
 * replaced an input placeholder image with proper placeholder property
2018-05-07 22:51:18 +02:00
Sarjuuk
4f0d045ff4 Quests/Misc
* remove Uldaman from Eastern Kingdoms
 * add Crystalsong forest to Northrend
2018-05-05 17:42:24 +02:00
Sarjuuk
e2e23c430a Quests/Categories
* shuffled quests into a structure that seems sensible (menus should no longer point to dead pages or display 'undefined' links)
2018-05-01 19:09:38 +02:00
Sarjuuk
9609c93f8c Quest/Detail Page
* display mail attachments besides the actual mail instead of extra tab
2018-04-30 10:59:24 +02:00
Sarjuuk
35dc835c62 NPCs/Detail Page
* fixed vendors displaying the same sell price for every item
2018-04-28 13:32:16 +02:00
Sarjuuk
808bbcc2fd Profiler/Profiles
* do not display deleted custom profiles in their parent profiles quick info
2018-04-28 12:03:52 +02:00
Sarjuuk
c5e9762830 Profiler/Profiles
* allow strings with less then 3 chars in name search
 * store name as binary to allow accent-sensitive search
   (note: guilds and arenateams have the same issue but utf8mb4_bin can't be applied blindly as the search also becomes case-sensitive (wich is already handled for profiles))
2018-04-28 11:32:22 +02:00
Sarjuuk
b13f3af03e Spell/Detail Page
* display effective MiscValue for effect PowerBurn
2018-04-28 10:57:41 +02:00
Sarjuuk
000a3208c0 Items/Armor
* fixed handling of additional Armor
2018-04-28 10:55:25 +02:00
Sarjuuk
efd68bdb5f Profiler
* fixed possible bug where a custom profile would load instead of the original character it was created from
2018-04-22 12:02:53 +02:00
Sarjuuk
d0b7fa9ef5 Profiler
* reworked default exclusions (hid some things entirely, flagged others for appropriate cases)
 * char names consisting of two letters are now valid
 * fixed matching enchantments onto ranged weapons
2018-04-22 11:48:30 +02:00
Sarjuuk
9dcb0b3a15 Profiler/Mounts
* display class-specific mounts on their respective classes
2018-04-08 11:28:46 +02:00
Sarjuuk
2518e4730b BaseType
* revert change to get number total results
   this partially reverts bf42973c
2018-04-07 20:52:10 +02:00
Sarjuuk
74308da407 Setup/Spells
* hide a lot of internal spells from searches
 * apply a faction to mount listing, so the Profiler can hide mounts from the opposite faction
 * apply GS to profession enchantments
2018-04-07 20:34:01 +02:00
Sarjuuk
bb9e6a36dd pages/Search
* fix searching by stat weights (used in Profiler, Item Comparison)
2018-04-07 20:31:50 +02:00
Sarjuuk
c448207724 JS/Locales
* grep-fix multiple issues with replacement patterns and hrefs
2018-04-07 20:30:40 +02:00
Sarjuuk
81335ad6dc Profiler/Reputation
* add missing base values for each race/class
2018-04-02 15:18:28 +02:00
Sarjuuk
d46a78b3cb Misc/Typos
* fixed display of charges on GameObject Infobox
 * removed display of achievement points as cost from vendors
2018-04-01 12:12:43 +02:00
Sarjuuk
492b7cd3c4 Profiler/Tooltips
* fixed display of titles
 * supply iconstring in the tooltip data as this does not rely on variables within global.js
   fixes externally embeded profile tooltips
2018-03-31 23:59:27 +02:00
Sarjuuk
c19691033a Profiler/Raid-Tracker
* sort raids by difficulty to fix progress bars
 * rename raids to include raid sizes
2018-03-31 17:25:11 +02:00
Sarjuuk
164cdc234c Profiler
* fixed directly accessing existing, but not yet fetched, profile
2018-03-31 17:21:24 +02:00
Sarjuuk
394a2699d8 Profiler
* translate anouncements for loc 3
 * fix queue locking up on error
 * fix queue status display
 * fix flagging real characters as private
2018-03-31 17:21:24 +02:00
Sarjuuk
e50333a518 DB/Favorites
* implement favorites for DB entries
 * click on the star besides the name of the entry to add it to a new quick menu
2018-03-30 19:22:45 +02:00
Sarjuuk
d848d316fe Misc/Typo 2018-03-29 23:52:02 +02:00
Sarjuuk
fa46aefa27 Modelviewer
* removed quality options (java .. holy crap!)
 * added animations menu
2018-03-29 21:19:36 +02:00
Sarjuuk
0912248fd5 Misc/Typos 2018-03-29 18:59:09 +02:00
Sarjuuk
fab71f9325 Misc
* don't cache playlists
 * don't cache new custom profiles
 * forgot to sanitize and use param from js
2018-03-29 14:40:46 +02:00
Sarjuuk
51eda12099 Pages/Home
* implement random home titles
2018-03-29 13:16:23 +02:00
Sarjuuk
bf42973c00 Profiler
* further optimize search
   - use achievement 4496 as shortcut for everything based around total achievement points
   - get talent distribution separately
   - get total profiler-items found separately
   - opt to not sort found results
 * fixed Profiles with zero Achievement Points
 * added cache key genrator i forgot :<
 * fixed typos
2018-03-29 12:47:27 +02:00
Sarjuuk
431e984f03 DB/Structure
* update collate to use utf8mb4
 * fixed creature paths being capped at 255 waypoints
2018-03-28 11:59:13 +02:00
Sarjuuk
e973e5e33b Profiler/RaidTracker
* ALL the Raids .. track em
2018-03-28 11:55:06 +02:00
Sarjuuk
22d02378ef Config/Profiler
* update config to enable/disable profiler in general, instead of just the queue
 * regenerate affected files on config change
2018-03-27 12:41:32 +02:00
Sarjuuk
c17cf9c043 Profiler/Profile
* shorten raid activity string for locale deDE to prevent icons from showing up where they shouldn't (also needed for locale ruRU)
 * define offhand frill as unfit for enchantments
 * resort raid activity tracker and add placeholder for ulduar
2018-03-27 00:03:56 +02:00
Sarjuuk
72c1dacd3f Profiler/Guilds
* fixed forcing guildnames to string for js
 * made static string localized
2018-03-26 22:09:53 +02:00
Sarjuuk
bc834245d7 Setup/Sounds
* make log more clear, that every available locale is checked
2018-03-26 19:58:32 +02:00
Sarjuuk
160c15a5f2 Misc/Mergefix
* added clean db dump
 * cleanup fails from profiler merge
 * maybe fixed setup being unable to find images for processing
 * version push
2018-03-26 19:57:54 +02:00
Sarjuuk
0ff0e5b59e Profiler/Frontend
* add pages and templates to display arena teams, guilds and chars
2018-03-22 18:17:16 +01:00
Sarjuuk
3fd25ca889 Profiler/Backend
* added core functions nessecary for profiler
2018-03-22 17:36:28 +01:00
Sarjuuk
b0a5f477c1 Profiler/CLI
* move command line related functions to its own class
2018-03-22 16:32:29 +01:00
Sarjuuk
f25498d4d0 Profiler/Gearscore
* implement gearscore calculation

im sad and i hate myself
2018-03-22 15:24:36 +01:00
Sarjuuk
fbcf00e0fc Profiler/Filter
* implemented flag options when searching for strings
2018-03-22 14:45:26 +01:00
Sarjuuk
3303722bc7 Profiler/Prep:
* misc changes from profiler branch. No real changes on its own
2018-03-22 13:59:30 +01:00
Sarjuuk
5abb3f78cf Auth/Fixup
* fix external login after 49a10e22
2018-02-02 21:15:25 +01:00
Sarjuuk
457e5f1eb5 Items/Armor
* fixed calculation of extra armor
2018-02-02 19:26:29 +01:00
Sarjuuk
ada57d4384 Misc/Versions
* AOWOW_REVISION is no longer expected to be numeric
   this allows for user-defined versions (e.g.4.1c) when changes
   within css/js files need to be forced into the web browser or
   existing cache files need to be invalidated
2017-09-04 16:50:16 +02:00
Sarjuuk
f2fce0f602 Misc/Typos
* fixed typo/php-warning introduced in d83f370fb4
2017-09-04 16:41:00 +02:00
Magnifikator
55dfe0ccc9 Misc/Git
added *.orig files to .gitignore
2017-09-04 16:39:14 +02:00
Magnifikator
69abd8c434 Misc/Git
added extracted sounds and setup files to .gitignore
2017-09-03 17:31:28 +02:00
Magnifikator
011de295fd Page/Home
Fixes 2 html errors
2017-09-03 17:27:01 +02:00
Sarjuuk
dbb4ad0b2a Setup/Sounds
* change extension of the temporary audio files for easier use with the supplied converter scripts.
2017-09-03 17:19:21 +02:00
Sarjuuk
bd56a5dad9 Factions/Articles
* added french localization

thx @Endalaust
2017-09-03 16:32:52 +02:00
Sarjuuk
79d9225c02 Setup/Firstrun:
* try to resolve bad inputs for *_host and force_ssl, when they result in redirects (301, 302)
2017-09-03 15:50:54 +02:00
Sarjuuk
d83f370fb4 Page/Maintenance
* send appropriate status code: 503 (temporarily unavailable)

thx @Magnifikator for the heads up
2017-09-03 15:25:30 +02:00
Sarjuuk
889f14a64f User/Auth
* import user access from external auth source if available
2017-08-30 17:25:40 +02:00
Sarjuuk
858e5a0492 [PH] spell_group error suppressor 2017-05-18 00:56:42 +02:00
Sarjuuk
c61fb787ae Setup/Images
* fixed error, causing everything to be displayed as not dound if anything was missing
 * added readable output to source finding stage
 * should now be obvious if something is missing
2017-05-17 10:50:30 +02:00
Sarjuuk
57d4dc2459 Quests/DetailPage
* correctly interpret exclusiveGroup values (maybe.. (who knows..))
2017-05-17 01:25:17 +02:00
Sarjuuk
a17c45a698 README
* added another gotcha with auto-minifiers
2017-05-15 22:22:47 +02:00
Sarjuuk
94321ea178 Spell/ShapeshiftForms
* move all localized strings to class Lang
 * added missing negation in flag check, causing stances not to be displayed
2017-05-15 15:16:58 +02:00
Sarjuuk
bf6bf20ae4 Quests/DetailPage
* link to quest-ending spell if possible
2017-05-10 03:03:04 +02:00
Sarjuuk
7b7ec2d0aa Admin/siteconfig
* also rebuild js-locales when updating available locales
 * somewhat unhappy with the access to generated files. need to find a better solution.
2017-05-09 19:11:47 +02:00
Sarjuuk
1bb7fdf89a NPCs/DetailPage
* fixed extraCols for 'teaches' tab
2017-05-08 15:09:55 +02:00
Sarjuuk
05ce1f4242 README
* removed SoX for WIN in favor of FFmpeg
 * added example scripts for batch audio conversion.
 * added a curious error case to the Q/A-section
2017-05-07 23:09:40 +02:00
Sarjuuk
9c6a789d2b Admin/config
* added onUpdate handler to rebuild affected files
2017-05-07 18:40:45 +02:00
Sarjuuk
33a1a45a63 Misc/Notices
* fixed display of GemConditions on enchantment detail page
 * correctly initialize class and race masks for items
2017-05-07 01:19:43 +02:00
Sarjuuk
39e917927f Adomin/Config
* urlencode values or loose data on submit
2017-05-06 12:43:00 +02:00
Sarjuuk
79861569a2 Readme
* bump for new TDB dependancy
2017-05-04 21:50:11 +02:00
Sarjuuk
e762340953 DB/Structure
* implemented changes from d17a45ee7e (creature text localization)
2017-05-04 21:45:53 +02:00
Sarjuuk
4a8fe6de7d DB/Structure
* implemented changed from 309a7f7e2a (achievement reward mail localization)
2017-05-04 20:58:36 +02:00
Sarjuuk
7c456b970c Misc/Fixup
* added missing leading whitespace to update queries
2017-05-04 03:58:29 +02:00
Sarjuuk
dd63a6a2ab Accounts
* added config option for external recovery & creation
 * fixed typos in contribute brick for locale 2
2017-05-04 02:29:59 +02:00
Sarjuuk
00342b0b61 Readme
* updated TDB requirement
2017-05-02 22:41:12 +02:00
Sarjuuk
d34f7c304e DB/Structure
* implemented changes from 3739d8444b (quest localization)
2017-05-02 22:41:12 +02:00
Sarjuuk
6168937901 DB/Structure
* implemented changes from 460e2c5dc5 (item localization)
2017-05-02 22:41:12 +02:00
Sarjuuk
189ac56fc0 DB/Structure
* implemented changes from 9a3b28894d (broadcastText localization)
2017-05-02 22:41:12 +02:00
Sarjuuk
f7f5cafec2 Spells/Tooltips
* added base stats to be parsed (where skipped before)
 * dummy effects do not singal a healing spell
2017-04-30 13:33:36 +02:00
Sarjuuk
8248dad50a Misc/Errors
* fixes some notices from log
 * fixed error when handling cookies
2017-04-29 19:49:46 +02:00
Sarjuuk
12b9e3fa89 Tooltips
* place tooltip for daily quests
 * place tooltip for real time duration
2017-04-28 20:36:50 +02:00
Sarjuuk
269487f32c Misc/Fixup
* stray backet braking enchantments list page
2017-04-24 22:35:44 +02:00
Sarjuuk
d19a920c09 Filters/Items
* fixed forgotten rename, breaking page with certain tabs
 * fixed typo, breaking stat weights
2017-04-24 17:06:08 +02:00
Sarjuuk
29c8c3d0af Misc/Debug
* remove leftover output
2017-04-24 00:53:57 +02:00
Sarjuuk
a59fd45076 Pages/Spell
* display category cooldown in table
2017-04-23 22:55:40 +02:00
Sarjuuk
4316d3ad40 Zones/DetailPage
* directly display AudioControls for all ZoneMusic & Ambience
2017-04-23 20:20:44 +02:00
Sarjuuk
6377a9e659 Misc
* added option to 'verbose' concatenate a list of items to class Lang
 * added option to strip tags from a string to class Markdown
 * fixed several instances of wrong categories on pages
 * actually display world state conditions for zone music
 * some typos all over the place
2017-04-23 20:20:44 +02:00
Sarjuuk
959d0ace0b ListPages/Filter
* vastly improved input validation
 * content and type validation occurs when filter is created
 * contextual filters like itemTypes are now only applied in context
2017-04-23 20:20:34 +02:00
Sarjuuk
178a67e180 Zones/Misc
* fix allowed categories on ListPage
 * fix instances for zone 3523
2017-04-22 00:35:51 +02:00
Sarjuuk
a695a4188c Util/Game
* cleanup Util 2/2
 * move game related functions and variables to new class: Game
2017-04-19 01:47:03 +02:00
Sarjuuk
f503967c45 Util/Lang
* cleanup Util 1/2
 * move spell effect/aura strings to Lang
2017-04-19 01:47:03 +02:00
Sarjuuk
c1af1fd884 DB/PageText
* update structure for TrinityCore/TrinityCore@fbbe247114
2017-04-19 00:05:06 +02:00
Sarjuuk
e5013e79fd Fixup/Typo
* bracket order fail
2017-04-14 01:22:37 +02:00
Sarjuuk
6fddbe40a4 Lang/Items
* try to account for non-latin formating
 * use correct words provided by GlobalStrings.lua
2017-04-13 19:19:26 +02:00
Sarjuuk
5a45c6a5e6 Lang/Items
* apply templates from GlobalStrings.lua
 * fix some translation errors in the process .. also, if s.o. knows, what constructs like |3-1(%s) do to the passed string, please tell me.
2017-04-13 02:01:12 +02:00
Sarjuuk
b5c7faff65 Setup/DBC
* index column Id => id
 * why was this exception even there.
 * also DBCs copied directly into aowow_tables should work again
2017-04-11 15:32:37 +02:00
Sarjuuk
6df3a27279 Creatures
* flag creatures using creaturedisplayinfoextra.dbc as humanoid
2017-04-11 15:11:18 +02:00
Sarjuuk
dde09a8a02 WIN/Misc
* windows compatibility changes
   - prevent crash on very large querys by reducing query size
   - set memory limit to something less than 2GB. Old value caused memory_limit to be set to something miniscule. (with the changes to DBC extraction this should now be feasable)
   - be a tad more lenient with time when generating enchant-datasets
2017-04-10 22:06:44 +02:00
Sarjuuk
fa9d518980 Setup/DB
* table prefix for aowow is now fixed to 'aowow_'
 * no more input required
2017-04-09 22:04:40 +02:00
Sarjuuk
d63ac946f6 Setup/DBCs
* vastly improved memory usage when handling DBCs
 * DBCs are no longer read entirely into memory, instead appropriate file handles are used.
 * read data is written to DB in reasonable intervals instead of all at once
 * DBC-class can no longer read from DB, but that wasn't used anyway

CLISetup
 * can now interpret the HOME-shorthand (~) in paths
2017-04-09 21:20:31 +02:00
Sarjuuk
33b1e4a978 Misc/Errors
* Item/XML
    - fixed setting typeId when lookup was item name
    - fixed lookup by name with utf8-chars
 * Items
    - do not display page for invalid categories
 * NPC
    - fixed reputation display
 * Quest
    - broke infinite loop occuring when quest is in chain with itself
 * Filters
    - handle different sizes of cr, crs and crv passed
2017-04-08 03:10:37 +02:00
Sarjuuk
bb00355ca3 Currencies
* fixed sold-by tab on detail pag
 * fixed display if rewarded for quest
2017-04-07 18:56:55 +02:00
Sarjuuk
24e1c46a8c Merge remote-tracking branch 'github/master' into ghMaster 2017-04-06 23:54:26 +02:00
Sarjuuk
0360f6f600 Misc/Fixups
* fixed file generators involving ?_icons table
 * known spells onspell detail page should have a localized tooltip
 * fixed numer formating in spell tooltips for locales with a ',' as decimal
 * fixed php-tag in contribute template, causing parse error
2017-04-06 23:53:22 +02:00
Sarjuuk
b74b03151b Fixups
* fixed two typos
2017-04-02 16:05:32 +02:00
Sarjuuk
cc328baf19 Fixups
* fixed two typos
2017-04-02 16:04:04 +02:00
Sarjuuk
ec05120118 README
* updated audio generation as suggested in #93
* fixed indentation
2017-04-02 14:49:58 +02:00
Sarjuuk
acad94770b Item/Tooltips
* restore icon to tooltips after iconDB-changes tripped over a long forgotten change
2017-04-01 21:28:54 +02:00
Sarjuuk
92394e415a Spells/Effects
* only use MiscValue as spellid for SpellEffect:155
 * interpret and display SpellEffect 293
 * SpellEffects 131 & 132 can both play audio
2017-03-31 01:05:38 +02:00
Sarjuuk
f3d694ac79 README
fixed line breaks in Q/A-secion
2017-03-29 22:38:30 +02:00
Sarjuuk
cd9f90cea9 IconDB/Fixups (obligatory)
* table had wrong engine
 * two hunter pets have unique icons only referenced in creaturefamily.dbc
 * fixed links to rel-tabs
2017-03-29 22:16:08 +02:00
Sarjuuk
ee568da6ec IconDB
* initial implementation
 * this includes a complete reindexing of everything touching icons
 * this also means, no linking to red-rocket-site though, they index them differently
2017-03-29 21:48:14 +02:00
Sarjuuk
4651165e4d DB/SQL-Modes
* force disable special modes that will prevent our funny queries
2017-03-29 19:45:21 +02:00
Sarjuuk
e5de7a8a45 PHP/Compatibility
* should now work with php5 again
2017-03-29 18:51:02 +02:00
Sarjuuk
9ee7b44ddb Layout
* make main-contents slightly wider to prevent overlaps
 * make classes listview name column slightly wider to prevent overlap
2017-03-29 14:37:27 +02:00
Sarjuuk
e400155a64 Articles/Classes
* added localization for frFR (thx @Endalaust and his translators)
2017-03-29 14:11:55 +02:00
Sarjuuk
0fc006d2d4 Contribute
* added option to hide contribute tabs depending on subject (screenshots for sounds .. yeah, right)
2017-03-29 03:24:28 +02:00
Sarjuuk
5082a6155d Setup/Icons
* fixed offsets for class / race icons (should no longer capture icon border from tile)

not important, but you may want to run: aowow --build=simpleImg --icons -f
2017-03-28 23:22:29 +02:00
Sarjuuk
fd96f8f758 Errors / Logging
* try to log fatal errors if able
2017-03-28 18:00:08 +02:00
Sarjuuk
d91090e806 Admin/Screenshots
* do not limit screenshot sources

Search
 * display floored maps
2017-03-26 17:18:00 +02:00
Sarjuuk
1a04ee3354 Merge remote-tracking branch 'github/master' into ghMaster 2017-03-25 01:35:21 +01:00
Sarjuuk
f0e1e02f03 Admin/Screenshots
* do not display deleted screenshots on initial listing
2017-03-25 01:33:38 +01:00
Sarjuuk
8c56610418 Screenshots/filesize
* UHD screens are pretty large
 * also nginx is a thing and not affected by htaccess (just to keep in mind)
2017-03-25 01:26:22 +01:00
Sarjuuk
b6b734ff36 Fixups
* undefined variables in Markup.js
 * logic error preventing screenshot uploads
 * bitshift by negative value in spell
 * and i'm pretty sure i already added an exception handler at one point
2017-03-25 01:26:22 +01:00
Sarjuuk
a937fb7ac6 Setup/Sounds
* updated sql update for changed generator
Setup/Updates
 * do not throw an invalid scripts supplied - error if no actual scripts where suppied
   (in case of update it is skipped, in every other case it generates everything anyway)
2017-03-22 15:19:10 +01:00
Sarjuuk
efe7b468c9 Setup/Firstrun
* forgot to add sounds generator to firstrun script
2017-03-22 12:58:40 +01:00
Sarjuuk
f9416c549d Updates/SQL
* noticed having comments inside querys in these files breaks them
 * also there appear to be files with \r line endings left
2017-03-21 23:09:55 +01:00
Sarjuuk
9516affde5 topUsres
* implemented top users overview
 * w/o achievements, uploads, forum posts, yadda yadda
2017-03-21 16:22:17 +01:00
Sarjuuk
c85ba1a208 Setup/Sounds
* resolved duplicate naming of setup function
2017-03-21 11:20:00 +01:00
Sarjuuk
00de81c60d Errors
* try to die a bit more gracefull if errors occur
2017-03-21 10:43:31 +01:00
Sarjuuk
62a95effb8 Privilege
* add forgotten template file
2017-03-21 02:36:15 +01:00
Sarjuuk
85e1accebc Privileges
* add individual pages and corresponding articles
 * yes the sql-update was forgotten
2017-03-20 23:44:41 +01:00
Sarjuuk
90a31ac99f Config/Sanity
* Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn
2017-03-20 23:38:00 +01:00
Sarjuuk
8b9b653028 Disables
* display db-entries as disabled or unavailable if appropriate
 * affects now achievements and quests in addition to spells
 * flag items without source as unavailable
 * flag quests without starter als unavailable

Privileges
 * i guess i forgott to add the template...
 ...among other things
2017-03-20 18:00:12 +01:00
Sarjuuk
4f1854b138 Misc
* Github link to home-footer
 * formating errors in README.md

 * oh, i hate this markdown syntax sooo much :x
2017-03-20 13:16:41 +01:00
Sarjuuk
33554e8942 User/Contribs
* added reputation threshold for replys to config
 * check canComment and canReply separately
 * disable contrib-tabs if the user is banned
2017-03-20 01:12:36 +01:00
Sarjuuk
f0231c8d9f SiteConfig
* always place newly added php-configurations in the misc-category
2017-03-19 23:54:54 +01:00
Sarjuuk
5360b09d0f Articles
* distinguish between articles for dbpage and other articles
 * can be served by url instead of arbitrary picked type/typeId-pair
 * save access to article in db (not used yet)
 * serve correct g_pageInfo if on article-page

SiteReputation/Privileges
 * implemented info pages
2017-03-19 23:41:49 +01:00
Sarjuuk
939a3f1e31 Markup
* add some more options to tags (mostly directly setting class and style)
2017-03-19 22:57:52 +01:00
Sarjuuk
bf0f218b28 Template
* merge text-page-generic into list-page-generic
2017-03-19 14:42:08 +01:00
Sarjuuk
9fabd2c728 Reputation
* apply reputation_reward_rate to rewards on Quest Detail Page
 * apply reputation_reward_rate to NPC Detail Page
2017-03-16 21:32:23 +01:00
Sarjuuk
a163279fab Spell/Detail Page
* parse effect 103 (Reputation)
 * calc actual value from custom_reward_rate
2017-03-16 19:12:43 +01:00
Sarjuuk
5598b80ef4 Quests/Map
* correctly apply styles to map selector
 * correcly prepare map selector string
2017-03-16 18:49:11 +01:00
Sarjuuk
d6b722a967 Articles/Races
* added french translation (thx @Endalaust and his translators)
2017-03-16 15:33:13 +01:00
Sarjuuk
2ec5fda3e7 Items/Menu
* fixed preselection of menu items on Items List Page
 * fixed display and preselection of 'Type' option list
2017-03-16 15:13:25 +01:00
Sarjuuk
13d0dfeb07 Quest/Map
* assign objective text to map pips if possible
2017-03-13 22:07:44 +01:00
Sarjuuk
ddce998cad Spells/Tooltips
* fixed parser for lastValue/gender-switch (previous pattern matched against that too)
 * also its a stupid idea to replace a var with itself
2017-03-13 16:58:26 +01:00
Sarjuuk
103251a537 Spell/Effects
* do not rely displayInfo for spell effects, that can occur multiple times per spell
Items/CurrencyFor
 * always send all currency-info to javascript (an item may require more then one to be bought)
2017-03-13 16:10:54 +01:00
Sarjuuk
e1fd2db64d Admin/Screenshots
* restrict ability to irrevocably delete files to U_GROUP_ADMIN
Pages/Quests
 * be a tad more lenient when filtering excessive sources
2017-03-13 15:37:43 +01:00
Sarjuuk
0c492531b8 Localization/frFR
* some touch-ups
 * thx @Endalaust
2017-03-13 15:37:31 +01:00
Sarjuuk
8b9854804b Pages/Quest
* added map to quest detail page
 * some minor issues rooted in weired loot templates remain
2017-03-12 05:12:53 +01:00
Sarjuuk
849872d181 Misc
* fixed note in quest tab on zone detail page
 * fixed a notice when creating item tooltips while passing an empty randomEnchantment value
 * fixed currencies having no icons on zone detail page
2017-03-09 12:48:53 +01:00
Sarjuuk
a48f30e8a4 Misc
* edited example in README.md to be more clear
 * fixed a typo in RedButton template.
 * fixed a typo in comment ajax. Can now use sticky again.
 * fixed a logic error in comment reply js. do not line break image sources.
2017-03-09 02:21:08 +01:00
Sarjuuk
fce8b01493 Setup/ItemEnchantment
* updated setup to for f609e7a08b
2017-03-08 14:00:36 +01:00
Sarjuuk
05d24dbde8 revision push 2017-03-06 18:26:51 +01:00
Sarjuuk
20a1829317 implementation of Sound DB
Client sounds are cross-referenced with as many other DB-Types as possible. Including, but not limited to:
 * Character VOs (Errors, Emotes)
 * Creature VOs (Boss Dialogue)
 * Zone Music and Ambience
 * Sounds triggerd by spells
 * Sounds from general item/spell usage, creature behavior

Restrictions:
 * only one locale is supported. Choose wisely!
2017-03-06 15:16:34 +01:00
Sarjuuk
8fcd8ea429 Spells/Tooltips
* fixed range display when friend- and foe-distances differ
 * fixed interpretation of $l-switch

Item/DetailPage
 * do note prepare unnessecary data for the currency-for tab (eases load)
2017-02-26 21:42:30 +01:00
Sarjuuk
d73557ee5e Setup/Creatures
* hide creatures for internal use from listviews
 * correct Nightelf Player faction
 * trying to enforce strict line endings
2017-02-25 23:55:31 +01:00
Sarjuuk
3b1bcfe1a5 Fixup
* added missing brackets from copypasta-disaster
2017-02-25 18:39:16 +01:00
Sarjuuk
da59440106 Fixup
* access class property in screenshot/comment-admin by function. Aowow is compatible with PHP 5.5 again
 * changed line-endings across the board from \r\n to \n
2017-02-24 18:45:36 +01:00
Sarjuuk
83cc16107e Whoops
* partially revert 99760d7c72
 * fixed another type on quests list page
 * added default cases for empty data to pets and races list pages
 * improved typelist iterator to be recursively callable
2017-02-23 19:46:15 +01:00
Sarjuuk
99760d7c72 Screenshots
* aproving a screenshot now flags the corresponding db entry as having a screenshot
 * deleting & moving screenshots works equivalent
 * instancing a new TypeClass without parameters no longer grabs everything from the corresponding tables ( pass a "[true]" to retain this behaviour)
 * retroactively flag db entries for having screenshots
 * fixed a typo
2017-02-23 16:30:58 +01:00
Sarjuuk
ab8d7caced Config/Locales
* if your current language becomes invalid select the first viable one instead of defaulting to LOCALE_EN
2017-02-23 00:47:25 +01:00
Sarjuuk
090410cbf8 Nav/RedButtons
* pass parameters as ready to use - objects
2017-02-20 02:52:07 +01:00
Sarjuuk
739e7bc81a Articles
* escape article content again
 * restore home logo on maintenance page
2017-02-18 20:59:25 +01:00
Sarjuuk
f1483f4595 Forms/Placeholder
* removed placeholder text images for input fields
 * readded text as placeholder attribute
2017-02-17 01:37:43 +01:00
Sarjuuk
62a4bcf2d2 Item/Shared CD
* items should not share a cooldown with themself
2017-02-16 19:50:25 +01:00
Sarjuuk
8de6296878 Spell/Trainer
* do not use trainer templates when getting trainer by spell
2017-02-16 19:45:13 +01:00
Sarjuuk
e5a5ad425a Spells/Description
* support parsing another form of formula
 * mod regen auras (mostly food) have an implicit periode of 5sec.
2017-02-16 19:40:12 +01:00
Sarjuuk
b3369d0b3f Items/Currency
* do not display unique-limited amount on item if currency token
 * display as cap on currency page instead
2017-02-13 02:22:22 +01:00
Sarjuuk
294ba109df Item/Tooltip
* fixed quote in faction link
2017-02-13 01:43:59 +01:00
Sarjuuk
3eeecf11cb Setup/Creature
* fixed sync for creature_template_locale
2017-02-12 22:54:53 +01:00
Sarjuuk
d00252ad72 Misc
* ignore one more generated file
 * also its 2017 already
2017-02-12 20:03:19 +01:00
Sarjuuk
dcff6f6525 Setup/Zones
* fixed flagging valid zones as hidden if no displayable map was present
2017-02-12 13:20:26 +01:00
Sarjuuk
ffddd3c6b8 User/Weightscales
* saving now always updates the name
 * name is now urldecoded
2017-02-12 03:56:38 +01:00
Sarjuuk
d0ec950dc1 Setup/Misc
* fixed complexImage always selecting images for LOCALE_EN if able
 * fixed serverside events being displayed in the calendar
 * set initial memory limit to 4GB
 * set initial db_version to current version (forgotten to do that for a long time :x )
 * updated README.md and .gitignore; revision incremented
2017-02-12 02:42:59 +01:00
Sarjuuk
7b9d7bec1b Setup/Titles
* fixed title sources, where one title has multiple sources
2017-02-12 02:37:23 +01:00
Sarjuuk
88e21d8597 Setup/Misc
* wrap LEFT JOINed fields in IFNULL() to prevent errors on setup
 * also adapt to changed TC table structure
 * add ORDER to prevent skipping entries, if not already ordered in source table
2017-02-12 02:37:23 +01:00
Sarjuuk
b81e1745f6 Spell/Procs
* updated to get data from new table
 * fixed proc data being displayed for unprocable effects
2017-02-12 02:37:23 +01:00
Sarjuuk
201f31b3ac Setuo/enchants
* reduced overhead by directly querying for spells instead of wrapping them in a SpellList (that functions i didn't neeed)
 * skip enchantments from items without source
2017-02-12 02:37:23 +01:00
Sarjuuk
a31e018959 Types/Items
* prevent a possible recursion occuring when preparing ItemSources
2017-02-11 19:54:32 +01:00
Sarjuuk
efa93e218a Setup/Items
* fixed an sql error by forgotten var
2017-02-11 16:19:05 +01:00
Sarjuuk
ca0e57b99c Libs/jQuery
* updated to 1.12.4 (plus compatibility changes in affected javascripts)
 * fixed double escapes on featuredbox text
 * fixed rogue whitespace
2017-02-11 15:38:55 +01:00
Sarjuuk
d0f64fe66c Home/Featuredbox
* allow defining a time span during which the box should be displayed
 * added fields optional fields to point to diffenret header and home logos for the duration
2017-02-11 15:38:16 +01:00
Sarjuuk
8fd52835a6 Page/Header
* send 404 if an incorrect page or typeId was requested
2017-01-08 23:00:11 +01:00
Sarjuuk
8a01124eed comments/formating
* added missing whitespace betweeen date and patch version
 * reordered dmy to fit selected locale.
2016-12-24 13:27:51 +01:00
Sarjuuk
a50ddb98dd Pages/Achievements
* display additional crosslinkable conditions from achievement_criteria_data
2016-11-22 13:48:39 +01:00
Sarjuuk
e896daa148 setup/quests
* fixed fatal typo (thx @Fra298)
2016-10-25 15:26:58 +02:00
Sarjuuk
1a46e58477 Items/XML
* added source and cooldown to xml
2016-10-16 16:12:06 +02:00
Sarjuuk
e2ab00b4ba Spells/PageTexts
* fixed numeric & gender switch for russian locale
 * treat $| like a $l-switch (only one case iirc)
 * remived output for unhandled text variables
 * fixed display of pageText for items
2016-10-04 21:55:56 +02:00
Sarjuuk
568677ff32 Locales/esES
* some more square bracket shuffling (should be fine now)
2016-10-04 19:52:09 +02:00
Sarjuuk
321c9ba7f4 Setup/Sqlgen
* fixed setup skipping custom data if it was >stepSize away from the last entry
2016-10-04 19:45:13 +02:00
Sarjuuk
674a2a7814 Markdown
* added [money ..] and [currency=] to tag parser
 * added missing styles to properly display [quote ..] and diffs
 * added missing js to display [n5 ..] and progress bars
2016-10-04 19:45:13 +02:00
Sarjuuk
2bc40e9421 Misc/Fixes: fixed erronous sql-update 2016-10-04 19:45:12 +02:00
Sarjuuk
770f11321f Screenshots/Upload
* use fileInfo to determine the filetype (getimagesize fails if the file is not an actual image)
2016-10-04 19:45:12 +02:00
Sarjuuk
bb87762467 Merge/Fixup:
restore array structure after merge
2016-10-04 19:10:22 +02:00
AFROM
317e12eddf Update spanish localization (#64)
Update spanish localization
2016-10-04 19:05:27 +02:00
AFROM
1cbe1aea76 Update spanish localization in static/js/locale_eses.js (#65)
Update spanish localization
2016-10-04 18:58:39 +02:00
Sarjuuk
8f77774569 Misc/Fixup
* really fix broken sql structure after c75777e5c3 (fix the fix!)
2016-03-30 00:26:06 +02:00
Sarjuuk
a521935934 User/Auth
* use char limit appropriate to current auth method, when checking usernames
2016-03-29 21:16:16 +02:00
Sarjuuk
b84854d4ba Misc/Fixes
* fixed db_structure.sql broken after
 * readded %-column to GO-Loot listviews
2016-03-29 20:51:21 +02:00
Sarjuuk
c75777e5c3 Misc/Fixups
* added forgotten mapper strings for esES, ruRU, frFR
 * readded default name for screenshots and videos tab
 * fixed referencing not set $id in opensearch (wich now displays icons once again)
 * fixed referencing a long removed variable in admin=screenshots, preventing delete operations
2016-03-23 22:14:15 +01:00
Sarjuuk
44c90b71fa Pages/Quest
* fixed display of spell rewards, if rewardSpellCast was negative
2016-03-20 18:43:59 +01:00
Sarjuuk
a8d686104e Fixup
override foreign key checks while applying update
2016-03-18 18:43:14 +01:00
Sarjuuk
e80015a7cd Weight Scales/Presets:
* moved wt_presets to DB
 * scales are now saved sensibly instead of just being dumped as is (also, tables have now constrains)
 * added admin=weight-presets to edit presets
 * added an internal user on id:0 who 'owns' the wt-presets and the +1 rates on new comments
 * consequently added constraints to comment-related tables

Misc:
 * Util::toJSON() priorizes its flags over CFG_DEBUG, wich fixes the modelviewer-redButton
 * moved terrible javascript-dump from admin-page to template
2016-03-12 14:05:39 +01:00
Sarjuuk
71e44e572a Misc/Misc
* fixed auth against TCs auth table
 * check for real paths when creating emote aliasses
 * races page no longer identifies as classes
 * fixed item comparison tool
 * fixed js error when trying to display spawn maps
2016-02-27 23:23:37 +01:00
Sarjuuk
741ebf38f9 Users/Markup
* implemented pending-status for newly created accounts
 * Markup should now properly recognize external links
 * Markup will remove links by pending accounts
2016-02-17 00:23:00 +01:00
Sarjuuk
08717c36d0 Templates/Listviews
* removed most templates by sensibly restructuring lvTab-data. Util::toJSON() does the rest.
 * taught Util::toJSON() to recognize values prefixed with $ as js-code
 * use Util::toJSON whenever possible
 * misc
   - mail attachments are displyed again
   - always show all currency rewards on Quest-LVs
   - suppress an error, when encountering unused ItemMods
2016-02-11 22:14:42 +01:00
Sarjuuk
fe7f05c89c Comments/rating
* do not include the +1 every comment starts with in the distribution and preview
2016-02-06 21:18:02 +01:00
Sarjuuk
447f7f02a7 Pages/Zone
* fixed displaying nonexisting multiples of spawns on the map
2016-02-06 14:17:42 +01:00
Sarjuuk
c1b92df9bf PHP7/compatibility
* changes to search.php somehow slipped through
2016-01-31 22:54:18 +01:00
Sarjuuk
7c1e61d843 Admin/Config
* fixed calling $this in static context (broke web-config)
2016-01-31 22:35:27 +01:00
Sarjuuk
63cd83c102 Setup/Update
* fixed errors introduced in c3fe4b0224
2016-01-31 21:57:46 +01:00
Sarjuuk
0220587637 Template/Home
* update footer to current year
 * display current revision
2016-01-31 19:05:12 +01:00
Sarjuuk
f076a30180 AoWoW is now compatible with php7
(php7 is recommended but not required)
2016-01-31 16:32:31 +01:00
Sarjuuk
5abdbe2080 Misc/Fixes
* fixed stats when comparing items with scaling random enchantments
 * fixed BOM on compare.php
 * fixed multiple typos and notices
2016-01-31 15:46:20 +01:00
Sarjuuk
2bc85dd109 Acc/Passwords
* do not enforce minimum password length for imported accounts
2016-01-26 23:12:41 +01:00
Sarjuuk
bd2a5acf21 Template/JS
* removed some readability formating, that caused an arbitrary text-node to appear in between DIVs, screwing with javascript
2016-01-23 21:17:50 +01:00
Sarjuuk
278176a48e Setup/DBCs
* directly extract integers as signed (may or may not help with a reported case of extracted nonsensical values)
2016-01-23 11:46:39 +01:00
Sarjuuk
eddb034a5c User/Passwords
* allow for passwords longer than 15 characters
2016-01-23 11:29:18 +01:00
Sarjuuk
c3fe4b0224 Setup/Update
* get files/tables, which need to be regenerated after! applying sql updates
 * small wording change, to make exiting dbConfig feel less like an error occured
2016-01-16 10:37:37 +01:00
Sarjuuk
432223264e Items/Block
* fixed formating of xml-export
 * fixed handling of block value (can have multiple sources per item (itemMods, spell, set individually) and is then displayed separately in tooltip, but summed for calculations)
2016-01-13 21:57:38 +01:00
Sarjuuk
99eb8f426b Misc/Fixes
* fixed typo preventing management of custom weight scales
 * added forgotten GET-Parameter to sanitization, preventing rating of comments
 * avoid hardcoding locales
 * do not show tooltip on BUTTON_WOWHEAD
2016-01-13 20:58:06 +01:00
Sarjuuk
ff3babacb7 Skills/Trainer
* fixed copy/paste-error asigning riding trainer-templates to first aid
2015-12-06 20:05:06 +01:00
Sarjuuk
4daf474ff4 Admin/AjaxHandler
* removed rogue \s, which prevented handling of id-lists
 * affected mass handling of screenshots and comments
2015-12-06 11:21:38 +01:00
Sarjuuk
f01c624f82 Comments
* do not double escape chat message body
2015-12-05 20:33:25 +01:00
Sarjuuk
5661f09454 Tools/Submodules
* removed submodules from project
 * both tools can now be found at https://github.com/Sarjuuk?tab=repositories
2015-12-05 00:46:01 +01:00
Sarjuuk
2f53abdb3e Setup/Spells:
* fixed some talents with erronous itemCreateIds to have an altIcon and quality color assigned
2015-11-28 17:23:06 +01:00
Sarjuuk
3eace0680d Setup/Fixups
- also change function call talents() to talentCalc() for firstrun script
 - filter waypoint_data query for creature_addon with path_id 0 (where did this get lost...)
2015-11-22 20:52:16 +01:00
Sarjuuk
3b32ce6b1f Conf
- moved misc categoty to index 0 as adding new categories became complicated.
 - display misc category last...
2015-11-22 16:25:25 +01:00
Sarjuuk
74f68c8a69 Setup/TalentCalc
* renamed file generator function to avoid naming conflict with sql generator function
2015-11-19 19:46:50 +01:00
Sarjuuk
e197072409 Locale/FR
* added some more translations

 thanks @joshwhedon
2015-11-18 23:49:21 +01:00
Sarjuuk
4c4cde1165 Items/GemPerfection
* use skill_perfect_item_template to display Gem Perfection procs
2015-11-18 23:47:53 +01:00
Sarjuuk
d55989510a Updated setup to TDB r60
* removed --resume command. Always offer to resume first time setup instead.
 * added skill_perfect_item_template to sources
2015-11-18 23:47:53 +01:00
Sarjuuk
99fdad29dd AjaxHandler
* separated into appropriate subclasses
* unified sanitizing of $_GET and $_POST data using build in filter_input()
* index now always tries to resolve page calls with ajaxHandler first and as a page last

minor bug-fixes to bugs that wre not reported yet, because they didn't occur yet
(e.g.: nobody tried to compose a comment with >7500 characters yet)
2015-11-08 23:14:17 +01:00
Sarjuuk
141bc10901 DB/Setup
* allow "build" and "sql" fields to be NULL in ?_dbversion
2015-11-07 18:35:49 +01:00
Sarjuuk
b5d73f0ac4 Setup/Readme
* added alternative versions to the mpqExtractor and blpConverter to readme
2015-11-01 11:30:30 +01:00
Sarjuuk
e69811c4de NPCs/Bosses
* link boss loot chests to boss
Home/Locales
 * properly set selected locale if featuredbox is not displayed

(also include fresh db dump)
2015-10-31 13:55:22 +01:00
Sarjuuk
b6e1bcaeac Setup/Tracking
* added tracking code for Google Analytics
   - enabling is optional
   - add account in config to enable tracking
 * removed usage tracking
   - the results were just seriously depressing..
2015-10-20 00:38:48 +02:00
Sarjuuk
7eda6dd778 Markup/Videos:
* updated to use current youtube-api
2015-10-16 16:41:15 +02:00
Sarjuuk
dabf307c13 Setup/firstrun
* also added check for port on this option
 * hide control chars from error input request for win
2015-10-06 22:04:12 +02:00
Sarjuuk
ef5d7cce3b Setup/DBConfig
* fixed parsing port on host ip (already worked, but setup threw an error)
2015-10-03 12:20:50 +02:00
Sarjuuk
d7c6092d5f Strings/Encoding
* replaced some more uses of string functions with multibyte equivalents where it might matter
   note: i didn't touch the spell-tooltip parser as it seems to work as intended regardless
2015-09-19 16:11:10 +02:00
Sarjuuk
6dc6124673 Merge pull request #37 from Toshik/patch-2
Quests/Encoding:
 * string manipulations should be multibyte
2015-09-19 15:32:53 +02:00
Sarjuuk
f33a15db4d Util/Encoding
* set multibyte-encoding globally as suggested by @Toshik
2015-09-19 15:27:20 +02:00
Toshik
6603cbf099 String cutting should support multibyte (UTF-8) 2015-09-18 13:12:25 +03:00
Sarjuuk
b1ef81c7e9 Merge pull request #36 from Toshik/patch-1
Setup/Quests
 - update to f7c7b92ef3
2015-09-12 22:26:55 +02:00
Sarjuuk
00bd33abfb CLISetup/OSCompatibility
- generally check for readline handler
 - only ignore \r for WIN
2015-09-12 22:15:12 +02:00
Toshik
973d2d90aa Quest DB structure fix
Fixes related to latest DB structure changes:
2015-09-10 11:51:15 +03:00
Sarjuuk
58babe24fe Util/Realms
* check for accessibility
 * use for achievements/realm firsts
2015-08-30 14:04:27 +02:00
Sarjuuk
645894fdbf Setup/Source:
* fixup
2015-08-29 13:48:21 +02:00
Sarjuuk
cb1916c484 Setup/Fixups
* some additions to latest pull requests
   - added missing dependancies
   - added quest_template changes to source.func.php
2015-08-19 17:29:21 +02:00
Sarjuuk
af7015097b Setup/Source
* get starter spells from playercreateinfo_skill instead of playercreateinfo_spell
   (changed in 2a3546ca36)

thanks @Toshik for pointing that out
2015-08-19 16:52:14 +02:00
Sarjuuk
c9aa0b3f6d Merge pull request #33 from Toshik/patch-6
Update game object DB structure to https://github.com/TrinityCore/TrinityCore/commit/9245bad
2015-08-19 13:44:50 +02:00
Toshik
ce915cbada Update game object DB structure
Corresponds to TC changeset TrinityCore/TrinityCore@9245bad
Closes #28
2015-08-19 13:08:09 +03:00
Sarjuuk
b0194a6e81 Merge pull request #30 from Toshik/patch-2
Update quests DB structure to  5178395a4f
2015-08-18 21:31:37 +02:00
Toshik
482a8dbd7d Update quests DB structure
According to 5178395a4f
2015-08-18 22:05:16 +03:00
Sarjuuk
59e254be54 Spawns
* fixed any spawns in Northrend refering to Dalarans dungeon maps and thus being inaccurate
2015-08-18 21:01:03 +02:00
Sarjuuk
17fdd13d3d fixed copy/paste error 2015-08-18 17:00:21 +02:00
Sarjuuk
e825ed84e8 Items
* fixed displaying zero value stats as socketbonus
2015-08-18 14:43:07 +02:00
Sarjuuk
a7eabb4b9d Itemsats
* fixed an translation issue on itemMod <=> statId causing wrong search results
 * fixed an sql-error breaking stat-based searches for items
2015-08-17 21:44:39 +02:00
Sarjuuk
960e48b7aa Localization
* replace null with "" for spelltriggers on items (otherwise valid values will throw an error)
2015-08-17 21:11:04 +02:00
Sarjuuk
8394a3863f Misc
* added column bitIdx to ?_titles
 * actually don't use COLLATE as it ignores indizes. Just search with lowerCase and properCase simultaneously
2015-08-15 00:09:48 +02:00
Sarjuuk
23260c2366 Tooltips
* fixed display for spells after e28c00ccda
   (if you are loading something, you DO need to register it afterwards)
2015-08-13 14:33:55 +02:00
Sarjuuk
a2d4b014fc updated submodule MPQExtractor as requested in #26 and #27
if you never inited the module you are fine. If you did, you have to
 * git submodule deinit -f includes/tools/MPQExtractor
 * rm -rf .git/modules/includes/tools/MPQExtractor
 * git submodule sync
 * git submodule update --init --recursive --remote
or something among those lines (good lord..)
2015-08-13 12:43:44 +02:00
Sarjuuk
11584a68d6 Profiler preparations
* Setup
   - accessing realm info by way of Util function
   - added table ?_talents
 * Lists
   - added support for querying multiple databases with the same structure at once (read: realms)
   - added support for exact string matches and forcing collate of fields to ci
 * JS
   - renamed occurences of 'subregion' to 'battlegroup' to be in line with other scripts (the usage is the same)
2015-08-11 15:03:42 +02:00
Sarjuuk
bd2e0ccada Emotes:
* display empty listview if no data
Setup
 * do not pass null instead of expected type array
 * added setup step for emotes to --firstrun *d'oh!*
 * fixed a wrong constant
2015-08-10 00:04:33 +02:00
Sarjuuk
3c69284e15 Misc
* fixed some recuring errors from db-log (mostly missing checks)
2015-08-08 00:47:52 +02:00
Sarjuuk
220fb81833 forgot to apply sql-update to dbversion-update 2015-08-07 20:01:36 +02:00
Sarjuuk
00f53fbb09 Spell/Tooltips
* added an exception for a bugged tooltip
 * better handle refs to unknown spells
2015-08-07 19:47:43 +02:00
Sarjuuk
63756b23d9 Spell/Tooltips
* only trim floats, when in topLevel while parsing tooltips
 * there is still an issue, where html-code gets returned from lower levels, screwing the eval()

should probably not bulk-handle all spellVars/formulas/variables on top level, but one after another
2015-08-06 21:13:11 +02:00
Sarjuuk
44e6e2ed38 Setup/Update
* using --update now reads scripts that need to be executed from ?_dbversion
 * if the script fails it will be kept in ?_dbversion until it was successfully run via --update
2015-08-06 20:48:28 +02:00
Sarjuuk
81e6098960 Home
* added variable titles and oneliners to frontpage
 * added cascading foreign keys to news_overlay -> news
 * renamed news -> featuredbox for consistency

Misc
 * aowow_dbversion is now part of the basic sql
 * also the db-dump is a fresh one and i'm shocked how often i forgot to apply updated there :<
 * from now on shorttags will be used for 'echo' in template (e.g. <?=$var; ?>)
2015-08-06 18:33:05 +02:00
Sarjuuk
38ac7b1dcb tabs2spaces in global.js 2015-08-06 12:49:38 +02:00
Sarjuuk
bfa1f261e0 Tooltips/Itemset
* handle enforced locale as espected
2015-08-04 23:23:37 +02:00
Sarjuuk
a0887f189f Faction
* fixed display of reputation columns on listviews on detail page
2015-08-04 21:25:36 +02:00
Sarjuuk
7ce72bf623 Articles
* convert all holidayIds to eventIds (by TC standard assignment)
2015-08-04 21:16:27 +02:00
Sarjuuk
320ad252d1 Tooltips/Items
* skipping an error, when handling heirloom-armor with scaled level on external tooltips
   the tooltip will now be displayed but without the armor-class changing at level 40
 * prevent assigning arbitrary randomEnchantments to items (e.g. Warglaive of Azzinoth of the Whale)
2015-08-04 19:09:37 +02:00
Sarjuuk
ef686c9c57 Achievements:
* display criterium-Ids for everyone on detail page
 * unified display with subitems on item detail page
2015-08-04 16:04:55 +02:00
Sarjuuk
e28c00ccda Misc:
* also load item-scaling data for spell-tooltips
 * fixed an event reference in the markup-guide (holidayIds became eventIds some time ago)
 * removed underline from anchor elements
 * also increment version
2015-08-04 14:58:22 +02:00
Sarjuuk
e2beb82f29 Items
* display randomEnchantment-Id as title on suffix-text on detail page
Compare
 * fixed stats display in subitem-picker
2015-08-04 14:02:51 +02:00
Sarjuuk
b1a6911ede Optimization
* minor improvements to the use of Util::checkNumeric
 * gargantuan improvements to ItemList handling random enchantments (sec -> ms)
2015-08-04 11:45:10 +02:00
Sarjuuk
f35be8fa36 Util/MostComments
* fixed wrong indexing on counting comments per page
2015-08-04 00:34:08 +02:00
Sarjuuk
1d5436bbd2 don't ask.. 2015-08-03 23:24:04 +02:00
Sarjuuk
a5f13325c0 Enchantments
* forgot notFound message
 * added Enchantments Tab to Spell Detail Page
2015-08-03 22:31:55 +02:00
Sarjuuk
7ac0601f88 Enchantments
* added tab: used by socketbonus to detail page
 * format ppm as float
2015-08-03 21:26:25 +02:00
Sarjuuk
7f08708555 added enchantments to DB
* added markdown [enchantment=123] and [emote=456]
 * updated item_stats to accomodate enchantments
 * updated tables for community content to handle emotes and enchantments

Spells:
 * getStatGain() now reads aura 123 as Spell Penetration
 * fixed error in tooltips when displaying effect ranges (e.g. 10 - 10 damage)
 * display the miscValue of aura 50, 102, 131 and 180 human readable

Emote:
 * no longer displays empty quickInfo on detail page
 * detail page no longer pretends to be a pet

Setup:
 * sync no longer regenerates all tables/files, when the supplied world-table affects nothing
 * fixed dependancy of ?_source from ?_achievment (damn plural-s)

Misc:
 * FireFox doesn't understand <node>.innerText, use <node>.textContent instead

please refresh the affected tables and files
   php aowow --sql=itemenchantment,item_stats
   php aowow --build=enchants,gems
2015-08-03 20:05:39 +02:00
Sarjuuk
f6e15c35fc Tooltips
* added word events to tooltips
  * added option to pass achieved criteria to achievement tooltips via rel-parameter
2015-07-25 00:14:59 +02:00
Sarjuuk
7b30c49785 Setup/Emotes
fixed field count after 8fa83eab64
2015-07-23 22:48:46 +02:00
Sarjuuk
d771103428 added forgotten file
should have been committed in 8fa83eab64
2015-07-23 19:57:11 +02:00
Sarjuuk
2afcceaefb Heirlooms
* fixed expected structure of ScalingStatValues (js-scaling heirlooms should be working again)
  * weapons are now properly assigned ranged- or melee-dps

  need to rerun: php aowow --build=itemScaling
2015-07-21 23:24:06 +02:00
Sarjuuk
3d08de51e1 Items/Heirlooms
* damage range of melee weapons is noe 20% and ranged weapons 30%
  * removed localized number format from item-tooltips. this broke damage recalculation
User/Weightscales
  * fixed managing weightscales by moving ! around
2015-07-21 21:16:36 +02:00
Sarjuuk
8fa83eab64 Misc/Fixups
* made emotes random-searchable
 * fixed error with pageTexts on Item Detail Page
2015-07-21 11:54:35 +02:00
Sarjuuk
4ab558858f Users
* do not numeric-check g_user in case of numeric user names (wich then break js string functions)
2015-07-21 01:07:57 +02:00
Sarjuuk
6f59afe8e6 DB/Emotes
* added emotes to DB .. why? just because!
  * also added to search
  * cross-linked achievements and emotes
  * data is generated via: php aowow --sql=emotes
  * setup requires GlobalStrings.lua (see README.md)
2015-07-21 01:07:21 +02:00
Sarjuuk
5239cbd293 NPCs / Quests
display reputattion change in list when using corresponding filters
2015-07-20 17:01:39 +02:00
Sarjuuk
7276fed9e7 Setup/Quests
* fixed wrong offset on questXP.dbc and factionReward.dbc
  * xp & reputation should be correct after running `aowow --sql=quests`
2015-07-20 15:36:29 +02:00
Sarjuuk
022ceba20d fixup to 998763b
so .. basicly everything was wrong with this one
2015-07-20 12:03:36 +02:00
Sarjuuk
5b8a862df9 DB/Structure (thx @Carbenium for the heads up & suggestions)
* adapted structure to TDB 335.59
  * dropped questItem[1-6] fields from ?_creature and ?_object (why were they even there)

 - removed a function-stub from Util (already forgot, what it was supposed to achieve)
2015-07-19 22:25:21 +02:00
Sarjuuk
11ab3e0f19 typo fix 2015-07-18 16:51:28 +02:00
Sarjuuk
998763be7b Errors/Logging
* log addNotes to DB as php user-errors
  * always skip the native php error handler
2015-07-18 16:48:00 +02:00
Sarjuuk
58a235e2e3 PageText
handle cases where pageTextId is given but pageText is not in DB
2015-07-18 15:16:35 +02:00
Sarjuuk
3e7a34a2ea Spells/Tooltips
* implemented 'know'-parameter (modifies tooltips based on known spells)
  * displayed on spell detail page
  * usale as urlParam (&know=<spellId>) or rel-attribute (rel="know=<spellId>)
2015-07-18 13:19:18 +02:00
Sarjuuk
111e53aaef Spells/Tooltips
* fixed text-parsing for spells with a combat rating in the tooltip, that is inside a formula (e.g. spell 24574)
  * fixed client tooltip updates when modifying level for affected tooltips
  * fixed spell-links on item tooltips with multiple combat ratings
2015-07-18 03:41:13 +02:00
Sarjuuk
c1f5d2ea9b Setup/Spawns:
* only priorize captials over their surrounding if there is no alphaMap present
  * Dalaran and Shattrath are capitals
  * truncate old data as it may not get overwitten
2015-07-18 01:52:08 +02:00
Sarjuuk
20732e38d8 DB/Structure
added proper keys to ?_comments
2015-07-14 21:51:20 +02:00
Sarjuuk
cea81d1810 fixup#2
* save sessions under absolute path
  * somehow forgot to add garbage collect - divisor to config
2015-07-13 00:29:27 +02:00
Sarjuuk
8b317d4b21 fixup
add forgtten categories to config values
2015-07-12 23:19:07 +02:00
Sarjuuk
dd0bb30481 Comments/Replies
* fixed reply editing
  * fixed displaying error messages
2015-07-12 01:40:09 +02:00
Sarjuuk
051334da22 Events
drop usage of holidayIds (as far as possible)
  the obvious change is, that all events are now refenreced by a positive eventId.
  (?event=375 will probably become ?event=5)
Comments
  fixed malformed db-table. It can now hold negative typeIds.
  applying this commit will drop any comments related to events without holiday
  added gain of SiteReputation for comment-replies

resyncing dependencies of 'game_event' is required
2015-07-12 00:06:21 +02:00
Sarjuuk
e9399c169a Config
move default_charset from .htaccess to DB
2015-07-11 19:48:52 +02:00
Sarjuuk
ce82a8f09f rather enforce interpretation as php for aowow/aowow than hiding it (wich broke filter forms) 2015-07-11 19:27:36 +02:00
Sarjuuk
54695a9490 Currency
* expanded tooltips
  * moved cap from hardcoded to DB
  * can now set description (manually)
Itemset
  * expanded tooltips
Lang
  * number formating is now locale-aware
2015-07-11 19:27:28 +02:00
Sarjuuk
08270ae09e Screenshots
* reworked upload process to be more user-friendly
  * handle bricks that consist mostly of localized text separately
  * added handle to display errors, when adding CommunityContent on the page they originated from
2015-07-11 14:19:59 +02:00
Sarjuuk
f6d80cf473 Spawns
* display phaseMask as hexadecimal
  * hide phasemasks, containing every single phase (0xFFFF)
2015-07-09 23:29:20 +02:00
Sarjuuk
0cb5d6b896 CLISetup
* added msg-level INFO
  * changed some WARN-level messages to INFO
Util
  * added function to handle directories aowow wants to write to / read from
SiteConfig
  * group options to be less cluttered
  * allow empty strings (numerical values must still at least be 0)
  * renamed account related config values to be make more sense
  * make cache path configurable
  * make session save path configurable - use this to avoid the garbage collect cron job on Debian or Ubuntu, that cleans sessions and only depends on your php.ini (NOTE: putting this inside a web-enabled directory is a risk!)
2015-07-09 21:04:00 +02:00
Sarjuuk
4a47900860 Utility/RSS
use SimpleXML to generate rss-feed
  send rss as utf-8
  fixed some misc errors when generating a feed
2015-07-06 01:07:12 +02:00
Sarjuuk
f2ec843f7e Lang
rename /gameObjects?/ => /objects?/ so it can be localized via typeString
2015-07-05 21:17:55 +02:00
Sarjuuk
ed67493bb8 Items/Filter
fixed error, when appending empty upg= to query
NPC/Text
  handled rare case of uppercase string-placeholders
2015-07-05 19:15:36 +02:00
Sarjuuk
09e0886cdd Utility
* implemented sub-page: latest-videos (can't be suggested yet, i know!)
  * also show subject on latest-screenshots
2015-07-05 00:37:54 +02:00
Sarjuuk
5ecb46633a UserPage
* display amount of pending screenshots / videos
  * corrected amount of comment ratings on display
2015-07-05 00:06:48 +02:00
Sarjuuk
5db946590e display user reputation in top-menu 2015-07-04 19:37:10 +02:00
Sarjuuk
444e372a66 Utility/Random
no longer tries to lookup a random profile
Utility/Comments
  implemented sub-page: most-comments
2015-07-04 18:42:24 +02:00
Sarjuuk
af39933cc8 Utility
implemented subpage: latest-screenshots
2015-07-04 16:40:48 +02:00
Sarjuuk
2619c88db4 Admins/Screenshots
* allow empty captions on edit
  * set date of reputation reward to time of upload not approve
2015-07-04 16:24:15 +02:00
Sarjuuk
b76fc3b2a7 added memory_limit to initial config 2015-07-01 21:54:03 +02:00
Sarjuuk
cc886b4db9 Quest Detail Page:
properly distinguish between provided, required and provided-required items
2015-07-01 21:13:22 +02:00
Sarjuuk
a3c9b52073 Screenshots:
fixed sql-error in manager
Comments:
  no longer appear as upvoted for anonymous user
  can be voted on, again
Class detail page:
  removed default limit on class ability query (300). now shows all spells
Filter/Items:
  added missing table-prefix; fixed search for iconString
Spells/Search:
  display triggered player abilities as misc. spells. (previously hidden)
Misc:
  removed some obscure piece of old config
2015-07-01 19:44:44 +02:00
Sarjuuk
7673e256c8 Setup: added some checks when useing WIN
lets just say, the CLI works somewhat differently..
2015-06-29 00:10:03 +02:00
Sarjuuk
2f767ba835 misc fixes 2015-06-28 19:23:26 +02:00
Sarjuuk
7ec07c0dca Merge remote-tracking branch 'github/master' into ghMaster 2015-06-28 17:17:41 +02:00
Sarjuuk
c779e9139a Setup: unpacked db_structure.sql
README: added required db-version
2015-06-28 17:16:53 +02:00
Sarjuuk
97b3e860a0 Merge pull request #23 from Carbenium/setup
Setup: Fix two issues which lead to an setup error
2015-06-28 17:04:09 +02:00
Sarjuuk
4a80934346 added premium-related placeholder images
(not for acutal use, just a layout reference)
2015-06-28 16:56:36 +02:00
Carbenium
9897a6cf23 Setup: folder 'static/download/searchplugins/' has to be created during setup 2015-06-28 15:30:15 +02:00
Carbenium
7c0a2192f7 Setup: testSelf should reference README.md instead of README, otherwise setup will fail 2015-06-28 15:28:11 +02:00
Sarjuuk
92b180ca05 Updated README with installation instructions
thats it folks .. lets call it v1.0
2015-06-27 21:52:20 +02:00
1064 changed files with 148341 additions and 72265 deletions

14
.gitattributes vendored Normal file
View File

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

22
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

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

23
.gitignore vendored
View File

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

6
.gitmodules vendored
View File

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

View File

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

BIN
README

Binary file not shown.

141
README.md Normal file
View File

@@ -0,0 +1,141 @@
![logo](static/images/logos/home.png)
## Build Status
![fuck it ship it](http://forthebadge.com/images/badges/fuck-it-ship-it.svg)
## Introduction
AoWoW is a Database tool for World of Warcraft v3.3.5 (build 12340)
It is based upon the other famous Database tool for WoW, featuring the red smiling rocket.
While the first releases can be found as early as 2008, today it is impossible to say who created this project.
This is a complete rewrite of the serverside php code and update to the clientside javascripts from 2008 to something 2013ish.
I myself take no credit for the clientside scripting, design and layout that these php-scripts cater to.
Also, this project is not meant to be used for commercial purposes of any kind!
## Requirements
+ Webserver running PHP ≥ 8.2 including extensions:
+ [SimpleXML](https://www.php.net/manual/en/book.simplexml.php)
+ [GD](https://www.php.net/manual/en/book.image)
+ [MySQL Improved](https://www.php.net/manual/en/book.mysqli.php)
+ [Multibyte String](https://www.php.net/manual/en/book.mbstring.php)
+ [File Information](https://www.php.net/manual/en/book.fileinfo.php)
+ [Internationalization](https://www.php.net/manual/en/book.intl.php)
+ [GNU Multiple Precision](https://www.php.net/manual/en/book.gmp.php) (When using TrinityCore as auth source)
+ MySQL ≥ 5.7.0 OR MariaDB ≥ 10.6.4 OR similar
+ [TDB 335.21101](https://github.com/TrinityCore/TrinityCore/releases/tag/TDB335.21101) (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.
+ Tools require cmake: Please refer to the individual repositories for detailed information
+ [MPQExtractor](https://github.com/Sarjuuk/MPQExtractor) / [FFmpeg](https://ffmpeg.org/download.html) / (optional: [BLPConverter](https://github.com/Sarjuuk/BLPConverter))
+ WIN users may find it easier to use these alternatives
+ [MPQEditor](http://www.zezula.net/en/mpq/download.html) / [FFmpeg](http://ffmpeg.zeranoe.com/builds/) / (optional: [BLPConverter](https://github.com/PatrickCyr/BLPConverter))
audio processing may require [lame](https://sourceforge.net/projects/lame/files/lame/3.99/) or [vorbis-tools](https://www.xiph.org/downloads/) (which may require libvorbis (which may require libogg))
#### Highly Recommended
+ setting the following configuration values on your TrinityCore server will greatly increase the accuracy of spawn points
> Calculate.Creature.Zone.Area.Data = 1
> Calculate.Gameobject.Zone.Area.Data = 1
## Install
#### 1. Acquire the required repositories
`git clone git@github.com:Sarjuuk/aowow.git aowow`
`git clone git@github.com:Sarjuuk/MPQExtractor.git MPQExtractor`
#### 2. Prepare the database
Ensure that the account you are going to use has **full** access on the database AoWoW is going to occupy and ideally only **read** access on the world database you are going to reference.
Import files 01 - 03 from `setup/sql/` in order into the AoWoW database `mysql -p {your-db-here} < setup/sql/01-db_structure.sql`, etc.
#### 3. Server created files
See to it, that the web server is able to write the following directories and their children. If they are missing, the setup will create them with appropriate permissions
* `cache/`
* `config/`
* `static/download/`
* `static/widgets/`
* `static/js/`
* `static/uploads/`
* `static/images/wow/`
* `datasets/`
#### 4. Extract the client archives (MPQs)
Extract the following directories from the client archives into `setup/mpqdata/`, while maintaining patch order (base mpq -> patch-mpq: 1 -> 9 -> A -> Z). The required paths are scattered across the archives. Overwrite older files if asked to.
.. for every locale you are going to use:
> \<localeCode>/DBFilesClient/
> \<localeCode>/Interface/WorldMap/
> \<localeCode>/Interface/FrameXML/GlobalStrings.lua
.. once is enough (still apply the localeCode though):
> \<localeCode>/Interface/TalentFrame/
> \<localeCode>/Interface/Icons/
> \<localeCode>/Interface/Spellbook/
> \<localeCode>/Interface/PaperDoll/
> \<localeCode>/Interface/Glues/CharacterCreate/
> \<localeCode>/Interface/Pictures
> \<localeCode>/Interface/PvPRankBadges
> \<localeCode>/Interface/FlavorImages
> \<localeCode>/Interface/Calendar/Holidays/
> \<localeCode>/Sound/
.. optionally (not used in AoWoW):
> \<localeCode>/Interface/Glues/Loadingscreens/
> \<localeCode>/Interface/Glues/Credits/
#### 5. Reencode the audio files
WAV-files need to be reencoded as `ogg/vorbis` and some MP3s may identify themselves as `application/octet-stream` instead of `audio/mpeg`.
* [example for WIN](https://gist.github.com/Sarjuuk/d77b203f7b71d191509afddabad5fc9f)
* [example for \*nix](https://gist.github.com/Sarjuuk/1f05ef2affe49a7e7ca0fad7b01c081d)
#### 6. Run the initial setup from the CLI
`php aowow --setup`.
This should guide you through with minimal input required from your end, but will take some time though, especially compiling the zone-images. Use it to familiarize yourself with the other functions this setup has. Yes, I'm dead serious: *Go read the code!* It will help you understand how to configure AoWoW and keep it in sync with your world database.
When you've created your admin account you are done.
## Troubleshooting
Q: The Page appears white, without any styles.
A: The static content is not being displayed. You are either using SSL and AoWoW is unable to detect it or STATIC_HOST is not defined properly. Either way this can be fixed via config `php aowow --siteconfig`
Q: Fatal error: Can't inherit abstract function \<functionName> (previously declared abstract in \<className>) in \<path>
A: You are using cache optimization modules for php, that are in conflict with each other. (Zend OPcache, XCache, ..) Disable all but one.
Q: Some generated images appear distorted or have alpha-channel issues.
A: Image compression is beyond my understanding, so i am unable to fix these issues within the blpReader.
BUT you can convert the affected blp file into a png file in the same directory, using the provided BLPConverter.
AoWoW will prioritize png files over blp files.
Q: How can i get the modelviewer to work?
A: You can't anymore. Wowhead switched from Flash to WebGL (as they should) and moved or deleted the old files in the process.
Q: I'm getting random javascript errors!
A: Some server configurations or external services (like Cloudflare) come with modules, that automatically minify js and css files. Sometimes they break in the process. Disable the module in this case.
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.
Q: Screenshot upload fails, because the file size is too large and/or the subdirectories are visible from the web!
A: That's a web server configuration issue. If you are using Apache you may need to [enable the use of .htaccess](http://httpd.apache.org/docs/2.4/de/mod/core.html#allowoverride). Other servers require individual configuration.
Q: An Item, Quest or NPC i added or edited can't be searched. Why?
A: A search is only conducted against the currently used locale. You may have only edited the name field in the base table instead of adding multiple strings into the appropriate \*_locale tables. In this case searches in a non-english locale are run against an empty name field.
## Thanks
@mix: for providing the php-script to parse .blp and .dbc into usable images and tables
@LordJZ: the wrapper-class for DBSimple; the basic idea for the user-class
@kliver: basic implementation of screenshot uploads
@Sarjuuk: maintainer of the project
## Special Thanks
Said website with the red smiling rocket, for providing this 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".
![uses badges](http://forthebadge.com/images/badges/uses-badges.svg)

27
aowow Normal file → Executable file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

81
endpoints/admin/guide.php Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

277
endpoints/class/class.php Normal file
View File

@@ -0,0 +1,277 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class ClassBaseResponse extends TemplateResponse implements ICache
{
use TrDetailPage, TrCache;
private const TC_CLASS_IDS = [null, 8, 3, 1, 5, 4, 9, 6, 2, 7, null, 0]; // see TalentCalc.js
protected int $cacheType = CACHE_TYPE_DETAIL_PAGE;
protected string $template = 'detail-page-generic';
protected string $pageName = 'class';
protected ?int $activeTab = parent::TAB_DATABASE;
protected array $breadcrumb = [0, 12];
public int $type = Type::CHR_CLASS;
public int $typeId = 0;
public ?string $expansion = null;
private CharClassList $subject;
public function __construct(string $id)
{
parent::__construct($id);
$this->typeId = intVal($id);
$this->contribute = Type::getClassAttrib($this->type, 'contribute') ?? CONTRIBUTE_NONE;
}
protected function generate() : void
{
$this->subject = new CharClassList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->generateNotFound(Lang::game('class'), Lang::chrClass('notFound'));
$this->h1 = $this->subject->getField('name', true);
$this->gPageInfo += array(
'type' => $this->type,
'typeId' => $this->typeId,
'name' => $this->h1
);
/*************/
/* Menu Path */
/*************/
$this->breadcrumb[] = $this->typeId;
/**************/
/* Page Title */
/**************/
array_unshift($this->title, $this->h1, Util::ucFirst(Lang::game('class')));
/***********/
/* Infobox */
/***********/
$cl = ChrClass::from($this->typeId);
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
// hero class
if ($this->subject->getField('flags') & 0x40)
$infobox[] = '[tooltip=tooltip_heroclass]'.Lang::game('heroClass').'[/tooltip]';
// resource
if ($cl == ChrClass::DRUID) // special Druid case
$infobox[] = Lang::game('resources').
'[tooltip name=powertype1]'.Lang::game('st', 0).', '.Lang::game('st', 31).', '.Lang::game('st', 2).'[/tooltip][span class=tip tooltip=powertype1]'.Util::ucFirst(Lang::spell('powerTypes', POWER_MANA)).'[/span], '.
'[tooltip name=powertype2]'.Lang::game('st', 5).', '.Lang::game('st', 8).'[/tooltip][span class=tip tooltip=powertype2]'.Util::ucFirst(Lang::spell('powerTypes', POWER_RAGE)).'[/span], '.
'[tooltip name=powertype8]'.Lang::game('st', 1).'[/tooltip][span class=tip tooltip=powertype8]'.Util::ucFirst(Lang::spell('powerTypes', POWER_ENERGY)).'[/span]';
else if ($cl == ChrClass::DEATHKNIGHT) // special DK case
$infobox[] = Lang::game('resources').'[span]'.Util::ucFirst(Lang::spell('powerTypes', POWER_RUNE)).', '.Util::ucFirst(Lang::spell('powerTypes', $this->subject->getField('powerType'))).'[/span]';
else // regular case
$infobox[] = Lang::game('resource').'[span]'.Util::ucFirst(Lang::spell('powerTypes', $this->subject->getField('powerType'))).'[/span]';
// roles
$roles = [];
for ($i = 0; $i < 4; $i++)
if ($this->subject->getField('roles') & (1 << $i))
$roles[] = (count($roles) == 2 ? "[br]" : '').Lang::game('_roles', $i);
if ($roles)
$infobox[] = (count($roles) > 1 ? Lang::game('roles') : Lang::game('role')).implode(', ', $roles);
// specs
$specList = [];
$skills = new SkillList(array(['id', $this->subject->getField('skills')]));
foreach ($skills->iterate() as $k => $__)
$specList[$k] = '[icon name='.$skills->getField('iconString').'][url=?spells=7.'.$this->typeId.'.'.$k.']'.$skills->getField('name', true).'[/url][/icon]';
if ($specList)
$infobox[] = Lang::game('specs').'[ul][li]'.implode('[/li][li]', $specList).'[/li][/ul]';
// id
$infobox[] = Lang::chrClass('id') . $this->typeId;
// original name
if (Lang::getLocale() != Locale::EN)
$infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]';
if ($infobox)
$this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0');
/****************/
/* Main Content */
/****************/
$this->expansion = Util::$expansionString[$this->subject->getField('expansion')];
$this->headIcons = ['class_'.$cl->json()];
$this->redButtons = array(
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
BUTTON_WOWHEAD => true,
BUTTON_TALENT => ['href' => '?talent#'.Util::$tcEncoding[self::TC_CLASS_IDS[$this->typeId] * 3], 'pet' => false],
BUTTON_FORUM => false // todo (low): Cfg::get('BOARD_URL') + X
);
/**************/
/* Extra Tabs */
/**************/
$this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true);
// Tab: Spells (grouped)
// '$LANG.tab_armorproficiencies',
// '$LANG.tab_weaponskills',
// '$LANG.tab_glyphs',
// '$LANG.tab_abilities',
// '$LANG.tab_talents',
$conditions = array(
['s.typeCat', [-13, -11, -2, 7]],
[['s.cuFlags', (SPELL_CU_TRIGGERED | CUSTOM_EXCLUDE_FOR_LISTVIEW), '&'], 0],
[
'OR',
// Glyphs, Proficiencies
['s.reqClassMask', $cl->toMask(), '&'],
// Abilities / Talents
['s.skillLine1', $this->subject->getField('skills')],
['AND', ['s.skillLine1', 0, '>'], ['s.skillLine2OrMask', $this->subject->getField('skills')]]
],
[ // last rank or unranked
'OR',
['s.cuFlags', SPELL_CU_LAST_RANK, '&'],
['s.rankNo', 0]
],
Cfg::get('SQL_LIMIT_NONE')
);
$genSpells = new SpellList($conditions);
if (!$genSpells->error)
{
$this->extendGlobalData($genSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $genSpells->getListviewData(),
'id' => 'spells',
'name' => '$LANG.tab_spells',
'visibleCols' => ['level', 'schools', 'type', 'classes'],
'hiddenCols' => ['reagents', 'skill'],
'sort' => ['-level', 'type', 'name'],
'computeDataFunc' => '$Listview.funcBox.initSpellFilter',
'onAfterCreate' => '$Listview.funcBox.addSpellIndicator'
), SpellList::$brickFile));
}
// Tab: Items (grouped)
$conditions = array(
['requiredClass', 0, '>'],
['requiredClass', $cl->toMask(), '&'],
[['requiredClass', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL, '!'],
['itemset', 0],
Cfg::get('SQL_LIMIT_NONE')
);
$items = new ItemList($conditions);
if (!$items->error)
{
$this->extendGlobalData($items->getJSGlobals());
$hiddenCols = null;
if ($items->hasDiffFields('requiredRace'))
$hiddenCols = ['side'];
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $items->getListviewData(),
'id' => 'items',
'name' => '$LANG.tab_items',
'visibleCols' => ['dps', 'armor', 'slot'],
'hiddenCols' => $hiddenCols,
'computeDataFunc' => '$Listview.funcBox.initSubclassFilter',
'onAfterCreate' => '$Listview.funcBox.addSubclassIndicator',
'note' => sprintf(Util::$filterResultString, '?items&filter=cr=152;crs='.$this->typeId.';crv=0'),
'_truncated' => 1
), ItemList::$brickFile));
}
// Tab: Quests
$conditions = array(
['reqClassMask', $cl->toMask(), '&'],
[['reqClassMask', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL, '!']
);
$quests = new QuestList($conditions);
if (!$quests->error)
{
$this->extendGlobalData($quests->getJSGlobals());
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $quests->getListviewData(),
'sort' => ['reqlevel', 'name']
), QuestList::$brickFile));
}
// Tab: Itemsets
$sets = new ItemsetList(array(['classMask', $cl->toMask(), '&']));
if (!$sets->error)
{
$this->extendGlobalData($sets->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $sets->getListviewData(),
'note' => sprintf(Util::$filterResultString, '?itemsets&filter=cl='.$this->typeId),
'hiddenCols' => ['classes'],
'sort' => ['-level', 'name']
), ItemsetList::$brickFile));
}
// Tab: Trainer
$conditions = array(
['npcflag', NPC_FLAG_TRAINER | NPC_FLAG_CLASS_TRAINER, '&'],
['trainerType', 0], // trains class spells
['trainerRequirement', $this->typeId]
);
$trainer = new CreatureList($conditions);
if (!$trainer->error)
{
$this->addDataLoader('zones');
$this->lvTabs->addListviewTab(new Listview(array(
'data' => $trainer->getListviewData(),
'id' => 'trainers',
'name' => '$LANG.tab_trainers'
), CreatureList::$brickFile));
}
// Tab: Races
$races = new CharRaceList(array(['classMask', $cl->toMask(), '&']));
if (!$races->error)
$this->lvTabs->addListviewTab(new Listview(['data' => $races->getListviewData()], CharRaceList::$brickFile));
// tab: condition-for
$cnd = new Conditions();
$cnd->getByCondition(Type::CHR_CLASS, $this->typeId)->prepare();
if ($tab = $cnd->toListviewTab('condition-for', '$LANG.tab_condition_for'))
{
$this->extendGlobalData($cnd->getJsGlobals());
$this->lvTabs->addDataTab(...$tab);
}
parent::generate();
}
}
?>

View File

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

View File

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

79
endpoints/comment/add.php Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,49 @@
<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class ContactusBaseResponse extends TextResponse
{
protected array $expectedPOST = array(
'mode' => ['filter' => FILTER_VALIDATE_INT ],
'reason' => ['filter' => FILTER_VALIDATE_INT ],
'ua' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'appname' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextLine'] ],
'page' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']],
'desc' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkTextBlob'] ],
'id' => ['filter' => FILTER_VALIDATE_INT ],
'relatedurl' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => '/^[[:print:]]+$/']],
'email' => ['filter' => FILTER_SANITIZE_EMAIL ]
);
/* responses
0: success
1: captcha invalid
2: description too long
3: reason missing
7: already reported
$: prints response
*/
protected function generate() : void
{
if (!$this->assertPOST('mode', 'reason'))
{
$this->result = 4;
return;
}
$report = new Report($this->_post['mode'], $this->_post['reason'], $this->_post['id']);
if ($report->create($this->_post['desc'], $this->_post['ua'], $this->_post['appname'], $this->_post['page'], $this->_post['relatedurl'], $this->_post['email']))
$this->result = 0;
else if (($e = $report->getError()) > 0)
$this->result = $e;
else
$this->result = Lang::main('intError');
}
}
?>

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