Compare commits

...

420 Commits

Author SHA1 Message Date
slawkens
02918684fd Merge branch '0.8' of https://github.com/slawkens/myaac into 0.8 2025-04-22 14:20:45 +02:00
slawkens
5bc70beae6 Release v.0.8.23 2025-04-22 14:20:34 +02:00
slawkens
6418b782a3 Update buttons.base.html.twig 2025-04-22 14:15:37 +02:00
slawkens
687ccc4559 README.md corrections
* Add links to docs & faq
* add info about 2.x dev version
* fix some grammar typos
2025-04-22 14:13:12 +02:00
slawkens
29a198bfce Display more info when error parsing config.lua value 2025-04-22 14:07:46 +02:00
slawkens
06a235237d Plugin name is required, version is optional 2025-04-22 14:05:10 +02:00
slawkens
9c15c2fa68 Add $db->hasTableAndColumns($table, $columns), credits to @opentibiabr Team 2025-04-22 14:03:25 +02:00
slawkens
1708a48186 Change logout button color 2025-04-22 13:46:28 +02:00
slawkens
68170e42cd Fix headline.php: change image format to .png cause of black background 2025-04-22 13:45:35 +02:00
slawkens
4b9a7eaf85 Add noSubmit option to buttons.base 2025-04-22 13:44:45 +02:00
slawkens
01dda11a2f Merge branch '0.8' of https://github.com/slawkens/myaac into 0.8 2025-03-30 07:16:23 +02:00
slawkens
1d33750e85 Update .gitignore 2025-03-30 07:16:15 +02:00
slawkens
89f537c8c2 getPremDays: returns -1 if freePremium 2025-03-15 23:00:01 +01:00
slawkens
727da8b0cc Fix long title on headline.php 2025-02-12 16:19:07 +01:00
slawkens
5b6f29741d Update README.md 2025-01-27 22:59:02 +01:00
slawkens
e13bd879db Fix branch 2025-01-27 22:04:54 +01:00
slawkens
f696f74a06 Release v0.8.22 2025-01-27 22:04:17 +01:00
slawkens
3b18e400c0 Update CHANGELOG.md 2025-01-27 22:03:55 +01:00
slawkens
d1c72b3240 Update exception.php 2025-01-27 21:45:59 +01:00
slawkens
7abc3c7833 Exception: better style - better showing of longer exceptions 2025-01-27 21:30:39 +01:00
slawkens
c0c4fe33e9 Secure template.php of direct access 2025-01-27 21:30:16 +01:00
slawkens
05827b7861 Typo 2025-01-27 21:29:57 +01:00
slawkens
c304a9ab43 Better exception handler, which clears the whole html output, so the message is better visible 2025-01-27 21:29:43 +01:00
slawkens
d390ea325e Fix exception $account->getName 2025-01-18 19:12:08 +01:00
slawkens
37ba9c7366 All $cache->set calls should have $ttl 2025-01-17 22:55:35 +01:00
slawkens
7c3ebf70fa Optimize $player->isOnline() function, thanks @gesior 2025-01-17 22:52:00 +01:00
slawkens
eda773cb55 Start v0.8.22-dev 2025-01-09 20:26:17 +01:00
slawkens
c5c266b023 Update CHANGELOG.md 2025-01-09 19:26:27 +01:00
slawkens
9483cfaad8 vendor should be accessible, cause of debugbar 2025-01-09 13:39:58 +01:00
slawkens
b4ed68dfd7 Prepare v0.8.21 release 2025-01-09 10:46:51 +01:00
slawkens
39b19ed4c8 Fix change sex price deducted 2025-01-09 09:42:09 +01:00
slawkens
580b888b1d Fix XSS in forum 2025-01-08 23:29:34 +01:00
slawkens
19d3e15c11 Fix move_thread by unauthorized user 2025-01-08 22:34:16 +01:00
slawkens
5a68d204bb Fix for TFS 1.4.2 where conditions is NULL 2024-12-29 15:38:02 +01:00
slawkens
e09fe51774 Set default_socket_timeout for ipinfo.io checkup 2024-12-26 08:53:40 +01:00
slawkens
b2c9eb4745 Support for button_color (red, green, blue) 2024-12-05 21:49:24 +01:00
slawkens
d96787ec21 Update to v0.8.21-defv 2024-11-26 19:00:20 +01:00
slawkens
5df5c64e4f Update to v0.8.20 2024-11-26 18:34:51 +01:00
slawkens
3c4b19743f Linux is case-sensitive, made a mistake here, sorry! 2024-11-26 18:25:01 +01:00
slawkens
0e25ce553c Set current version to 0.8.20-dev 2024-11-19 15:24:42 +01:00
slawkens
2e4bbeb7f7 Release v0.8.19 2024-11-19 14:10:25 +01:00
slawkens
cb6640343b Prepare 0.8.19 changelog 2024-11-19 14:09:49 +01:00
slawkens
4658d1cb29 More obvious name for parameter in -> installMenus 2024-11-19 14:03:49 +01:00
slawkens
b25feaadf6 Fix includes 2024-11-19 14:03:02 +01:00
slawkens
2693db5f6f images/news is in the main folder 2024-11-19 08:21:06 +01:00
slawkens
8195b44061 require src/plugins.php 2024-11-19 07:46:16 +01:00
slawkens
035d0c1012 Add forward-compatible MyAAC namespace 2024-11-19 07:15:26 +01:00
slawkens
5a953ce901 Do not clear menus by default 2024-11-19 07:05:40 +01:00
slawkens
01660bd2b4 Fix for console displaying REQUEST_URI 2024-11-18 23:47:31 +01:00
slawkens
5b858c521a target_blank in template menus links 2024-11-18 23:47:11 +01:00
slawkens
d1c5a189c3 Add Plugins::installMenus function 2024-11-18 23:46:37 +01:00
slawkens
de1bb37bcb getGuildNameById($id) + getGuildLogoById($id) 2024-11-18 23:46:04 +01:00
slawkens
e0036a3e32 Syntactic sugar for db structure changes 2024-11-08 14:38:00 +01:00
slawkens
7f4737631d Add none vocation to highscores 2024-10-10 15:33:25 +02:00
slawkens
317505bf19 Add missing Validator::characterName check 2024-09-12 09:40:36 +02:00
slawkens
55b8645d3f Set default encryption to sha1 2024-09-08 15:05:12 +02:00
slawkens
ffb8f0879b two hooks for compatibility 2024-09-08 15:03:45 +02:00
slawkens
79f5614dce Add more clients (13.22+) 2024-09-08 14:47:36 +02:00
slawkens
2c347d0eac Interesting update from opentibiabr (Uptime readable) 2024-09-08 14:47:21 +02:00
slawkens
d40178104b New hooks in account manage + create 2024-09-08 06:23:17 +02:00
slawkens
55543ee881 Fix title - should be account logs 2024-09-08 06:22:29 +02:00
slawkens
d39386cfab Fix bans page getPlayerNameByAccount + getPlayerNameById 2024-09-08 06:21:40 +02:00
slawkens
b5bbae62b0 Prefer get_browser_real_ip() over REMOTE_ADDR 2024-09-08 05:36:33 +02:00
slawkens
71ef30d35e Better tables.headline.html.twig (patched from 1.0) 2024-09-07 17:28:45 +02:00
anyeor
263c7bed07
fix: require login before create new thread (#261)
We have to login first to see new thread button.
2024-07-26 18:19:29 +02:00
slawkens
1458b7a412 Fix $db->update when there is null value 2024-07-24 17:30:33 +02:00
slawkens
3e00c52128 Optimize OTS_House::load function with appropriate FETCH_ASSOC 2024-07-24 17:30:03 +02:00
slawkens
d73aceb272 Better https detection (patched from develop) 2024-07-23 16:39:24 +02:00
slawkens
1c55d4a220 Set Admin Account verified by default 2024-07-12 22:19:36 +02:00
slawkens
1edf8833c8 Patching from develop
* Allow account_create_character_create even if account_mail_verify is activated
* Fixes to account verify - do not allow login without verified email (Thanks @anyeor)
2024-07-09 23:51:12 +02:00
slawkens
0ffc5f68b4 deny all is enough 2024-06-23 09:56:12 +02:00
slawkens
81b6652738 Fix if <flags> is not present in monster.xml 2024-06-05 21:58:44 +02:00
slawkens
e5b4d2c6b3 Fix warnings in basic.js 2024-05-30 09:49:06 +02:00
slawkens
da1830371f Fix blessings longer than 3 characters 2024-05-30 08:24:02 +02:00
slawkens
6ba04967ed Update to 0.8.19-dev 2024-05-29 21:55:15 +02:00
slawkens
f503e140f3 Release v0.8.18 2024-05-29 21:29:31 +02:00
slawkens
f875f3cd20 New hook for guild-wars-old (0.8) 2024-05-24 22:44:34 +02:00
slawkens
72632c7b45 Fix typo 2024-05-24 17:02:14 +02:00
slawkens
1f2e7bd72d Update to 0.8.18-dev 2024-05-23 23:45:01 +02:00
slawkens
016138ab55 Release v0.8.17 2024-05-18 21:39:50 +02:00
slawkens
77efb80a12 Update config.php 2024-05-15 22:20:31 +02:00
slawkens
02eea950e4 Fix XSS in creatures.php, thanks to @gesior
Closes #254
2024-05-15 22:15:36 +02:00
slawkens
2793c41655 Set default status_ip, most server are hosted locally anyway 2024-05-15 22:07:46 +02:00
slawkens
62d3c198d5 Fix change_info if account_country is disabled 2024-04-15 21:55:02 +02:00
slawkens
ef62b53cec Don't allow redirect to external website 2024-04-08 19:05:42 +02:00
slawkens
7181b988e9 Add TwigTypeCastingExtension
Useful for casting variables in Twig
2024-04-08 07:35:48 +02:00
slawkens
8b0b123f42 deny vendor, composer.json, changelog.md etc. in nginx config sample 2024-04-06 19:51:57 +02:00
slawkens
f98332c698 Update .gitignore 2024-02-18 12:01:22 +01:00
slawkens
b1660bf27a Update README.md
[skip ci]
2024-02-17 09:06:54 +01:00
slawkens
191ad25eb2 Use word-break: break-all in guilds description + character comment 2024-02-16 20:39:40 +01:00
slawkens
7469be6efb Fix date 2024-02-12 21:48:50 +01:00
slawkens
47a3bfd265 Release v0.8.16 2024-02-12 21:48:17 +01:00
slawkens
5ae0be2323 Revert "Fix installation"
This reverts commit 9c318f9012a4d1a484bd1b63e99240193cba4669.
2024-02-12 21:43:55 +01:00
slawkens
42154d55a0 Fix broken installation I introduced in 0.8.15 2024-02-12 21:39:04 +01:00
slawkens
9dcc08ee6e Seems that this is better solution to the #245 (output buffering)
This works for both, when output_buffering is enabled, and disabled
2024-01-30 19:20:18 +01:00
slawkens
ba537b42bb Remove 31.php migration -> was for develop branch 2024-01-30 18:23:20 +01:00
slawkens
9c318f9012 Fix installation 2024-01-28 11:11:51 +01:00
Danilo Pucci
a88103a956
- adding check before flush buffer (#245) 2024-01-01 23:31:26 +01:00
slawkens
e26e6f3a1c Silently ignore if the hook does not exist 2023-12-28 19:12:47 +01:00
Slawomir Boczek
08d67a07e0
Create SECURITY.md 2023-12-15 20:56:06 +01:00
slawkens
6e9a89cb2e Update common.php 2023-12-14 16:34:44 +01:00
slawkens
e3aa3d4031 Release v 0.8.15 2023-12-09 00:14:16 +01:00
slawkens
156a68f8bd Update phplint.yml 2023-12-09 00:11:01 +01:00
slawkens
6a28da5d33 Update phplint.yml 2023-12-09 00:04:20 +01:00
slawkens
ee32384dca Seems there was more XSS in bugtracker 2023-12-08 23:45:13 +01:00
slawkens
19afd73e8a This is better 2023-11-29 15:48:03 +01:00
slawkens
eead6a2975 Fix exception showing 2023-11-28 17:11:04 +01:00
slawkens
11b11dd3ee Release v0.8.14 2023-11-27 23:31:45 +01:00
slawkens
483155cf4c Prevent session fixation 2023-11-27 23:16:51 +01:00
slawkens
55dbade8d5 Fix XSS in forum 2023-11-27 22:58:24 +01:00
slawkens
d1bc63d07a Fix forum XSS 2023-11-27 22:58:00 +01:00
slawkens
83a91ec540 Fix XSS in bugtracker.php 2023-11-27 20:28:43 +01:00
slawkens
7b43c972dd Fix missing query_string in nginx sample config
Causes missing parameters in $_GET query
2023-11-25 16:34:57 +01:00
slawkens
3fdf1d3f44 require_once is better 2023-11-05 20:13:31 +01:00
slawkens
764db0c203 Fix display ban info on account page
https://otland.net/threads/myacc-bans-display-problem.286825/
2023-11-02 22:06:07 +01:00
slawkens
538076bc45 My fault 2023-09-26 22:00:45 +02:00
slawkens
4327b66f91 Clear some additional cache keys 2023-09-26 20:45:50 +02:00
slawkens
3f27724569 Update common.php 2023-09-16 10:46:17 +02:00
slawkens
9c0c2bbece Update CHANGELOG.md 2023-09-16 10:45:54 +02:00
slawkens
946144016b Release v0.8.13 2023-09-16 10:35:10 +02:00
slawkens
5c3b01aca4 Fix XSS vulnerability 2023-09-16 10:31:33 +02:00
slawkens
50983a2b85 Fix error log when coins column does not exist 2023-09-14 16:29:31 +02:00
slawkens
765886f0c7 Add latest clients versions 2023-08-31 14:20:49 +02:00
slawkens
8ea78a5852 thanks @elsongabriel, seems str_contains is not available in php 7 2023-08-25 20:45:45 +02:00
slawkens
063cbab93e Allow hooks to be prefixed with HOOK_ 2023-08-23 12:00:03 +02:00
slawkens
f1670f4012 Patching from develop - twig context for hooks 2023-08-21 12:25:53 +02:00
slawkens
6fcf0f7117 Ignore gallery 2023-08-21 12:21:24 +02:00
slawkens
7a07763625 Update README.md 2023-08-11 22:21:54 +02:00
slawkens
8d2172a649 Added JetBrains logo + notice, thanks for support! 2023-08-11 22:17:17 +02:00
slawkens
b8f65207b6 Add version support table + fix badges 2023-08-11 22:11:29 +02:00
slawkens
ea675afe86 Start 0.8.13-dev 2023-08-07 22:53:02 +02:00
slawkens
cc1cebf359 Update CHANGELOG & release v0.8.12 2023-08-07 22:14:47 +02:00
slawkens
1e874c7027 Fixed not working links from database, introduced in 0.8.10 2023-08-07 21:45:56 +02:00
slawkens
a338fd967c Removed deprecated functions: utf8_encode & decode 2023-08-05 19:58:52 +02:00
slawkens
8796ff7e72 Remove whitespaces 2023-08-05 19:58:20 +02:00
slawkens
a8172a518f Add some functions to compatibility layer of gesioraac 2023-08-05 19:58:04 +02:00
slawkens
559c2c7bd2 Add .htaccess to .gitignore 2023-08-05 11:57:15 +02:00
slawkens
7a546e5a41 There is no more info. That never worked. 2023-07-29 07:26:03 +02:00
slawkens
5f7a9154b7 Thanks @anyeor for previous fix 2023-07-11 11:17:18 +02:00
slawkens
0d52978d9f Fix: cannot create topic on this board (check wasn't working) 2023-07-11 11:15:58 +02:00
slawkens
df48363ea4 Shorten some forum code about length 2023-07-07 17:15:13 +02:00
slawkens
34725e0257 Forum: better error messages (Suggested by @anyeor) 2023-07-07 14:34:26 +02:00
slawkens
df321154f6 Fix guild description on guilds page 2023-07-02 13:47:32 +02:00
slawkens
f2a3ec1185 Fix guild description not shown 2023-06-30 19:53:16 +02:00
slawkens
ce4aed0f17 Add word-break on forum thread & reply
When someone inserts long word, is will break into multiple lines
2023-06-30 19:32:47 +02:00
slawkens
d0c82f6fb0 Start 0.8.12-dev 2023-06-30 19:13:38 +02:00
slawkens
89b76e721d Release 0.8.11 2023-06-30 17:12:38 +02:00
slawkens
6091290efe Update CHANGELOG.md 2023-06-30 17:12:02 +02:00
slawkens
e4c4990e7f Forum: Fix quote and edit post buttons not being shown 2023-06-30 15:46:25 +02:00
slawkens
4f1235bfe9 Fix twig exception thrown when player does not exist 2023-06-28 15:15:14 +02:00
slawkens
bf9d440a95 Fix BASE_DIR when accessing /tools 2023-06-27 18:17:37 +02:00
slawkens
59a149c253 Move <base href> above, so it works, thanks @Leesneaks 2023-06-27 18:15:54 +02:00
slawkens
563099f290 Revert "<base> is not working properly, use full URL instead"
This reverts commit fa015b8d39d926aa0bc3ea2f59577afa22174c51.
2023-06-27 17:44:19 +02:00
slawkens
3732bf988d More changes to deleted characters (Account, guilds)
Account: Cannot change name, comment, gender
+ Cannot be deleted if owns a guild
Guilds: Cannot create, cannot be invited, cannot accept invite, cannot be passed leadership to
2023-06-27 17:41:04 +02:00
slawkens
ab964fa1de Important fix: Not allow create char if limit is exceeded (by @anyeor )
Could have been used to spam database, now it doesn't ignore deleted characters

He is not my brother :P Just same last name
2023-06-27 15:02:28 +02:00
slawkens
b5c694224e code formatting 2023-06-27 14:50:44 +02:00
slawkens
23810345f6 small adjustments 2023-06-25 08:38:45 +02:00
slawkens
b574a29331 Better Gesior support 2023-06-22 22:15:18 +02:00
slawkens
6593e32d83 Change title to "Support in game" 2023-06-19 08:04:46 +02:00
slawkens
b09adc836d Nothing important, just some comments and small code style fixes 2023-06-19 08:03:47 +02:00
slawkens
dcf9a45974 Do not display warning if HTTP_ACCEPT_LANGUAGE is not set 2023-06-19 08:01:35 +02:00
slawkens
21258313ef New function Cache::remember($key, $ttl, $callback) 2023-06-19 08:01:11 +02:00
slawkens
f851fa3845 New characters page hooks
HOOK_CHARACTERS_BEFORE_SKILLS
HOOK_CHARACTERS_AFTER_SKILLS
HOOK_CHARACTERS_AFTER_QUESTS
HOOK_CHARACTERS_AFTER_EQUIPMENT
HOOK_CHARACTERS_BEFORE_DEATHS
2023-06-19 08:00:54 +02:00
slawkens
2fdd507902 Display warning if hook file does not exist 2023-06-19 08:00:11 +02:00
slawkens
b850e56ff1 Use $i for hooks, easier compare 2023-06-19 07:47:49 +02:00
SRNT-GG
8d10082179
WIP - Removing unneccessary closing tags to prevent potential issues. (#223)
* Part 1

Removing closing tags when no HTML or other output comes after the last PHP codeblock.

* Further removals

* nothing

---------

Co-authored-by: slawkens <slawkens@gmail.com>
2023-06-15 20:53:55 +02:00
SRNT-GG
996ae625c9
Update README.md (#224)
updated required php version
2023-06-11 18:41:47 +02:00
slawkens
467f7ef927 Rename to .htaccess.dist
Causes problems on default setup
2023-06-03 09:04:41 +02:00
slawkens
fa015b8d39 <base> is not working properly, use full URL instead 2023-06-02 15:26:09 +02:00
slawkens
4b4864561c Better news back button 2023-06-01 11:23:28 +02:00
slawkens
475cea8549 Change button style (characters - view)
was causing issues in other templates
2023-06-01 09:57:20 +02:00
slawkens
760214fdbd Init $account_logged, if no logged 2023-06-01 08:52:22 +02:00
slawkens
9c5dcd7b19 Add cypress/e2e/2-advanced-examples to .gitignore 2023-05-29 08:20:56 +02:00
slawkens
720e400f7c Add cypress.env.json to .gitignore 2023-05-29 08:20:33 +02:00
slawkens
c261c6ba48 Add line & file to exception handler 2023-05-29 08:19:58 +02:00
slawkens
933d4e1d6f Release 0.8.10 2023-05-18 19:29:53 +02:00
slawkens
1d08833726 Update CHANGELOG.md 2023-05-18 19:25:50 +02:00
slawkens
7cfca55e3c PHP 7.2.5 is now required, cause of Twig 2.x 2023-05-18 19:21:13 +02:00
slawkens
7e13b62b8f Fix Twig error on create account 2023-05-18 19:06:50 +02:00
slawkens
5ccfcd541e Allow pages to be placed in templates folder (second attempt) 2023-05-14 08:59:34 +02:00
slawkens
ba4d2a9c48 Dirty workaround for fb links 2023-05-14 08:38:20 +02:00
slawkens
7a61f613ec Revert "Workaround for links from fb, like ?fbclid=x"
This reverts commit 073d9da0bc13363222227b8c2c0d07c9210f50b3.
2023-05-14 08:15:44 +02:00
slawkens
073d9da0bc Workaround for links from fb, like ?fbclid=x
Now shows news page, instead of "not found".
For 0.9 there is better solution
2023-05-13 10:20:58 +02:00
slawkens
e081a67589 Print more info if character cannot be created 2023-04-12 12:52:05 +02:00
slawkens
37a27b8065 Add check for player_deaths columns 2023-04-01 15:09:10 +02:00
slawkens
d34f7eb2fc Exclude polyfill-mbstring/bootstrap80.php 2023-03-31 11:18:58 +02:00
slawkens
f6c080cb5c Add overtrue/phplint@7.4 2023-03-31 10:26:16 +02:00
slawkens
a983fd03b1 Revert "test github actions"
This reverts commit 5b651886a52ed9ebf3d1522b57e49e8a4332f6c3.
2023-03-31 09:34:21 +02:00
slawkens
5b651886a5 test github actions 2023-03-31 09:30:28 +02:00
slawkens
6484ab75d9 Do not allow to continue install when there is no server database imported 2023-03-26 00:17:55 +01:00
slawkens
becad18465 fix small bug on install - please fill all input 2023-03-26 00:02:25 +01:00
slawkens
ec7e5a8838 Fix when server uses another items serializer 2023-03-25 21:59:13 +01:00
slawkens
300c1b4ebc Fix cannot go forward when config.local.php cannot be saved 2023-03-19 14:46:57 +01:00
slawkens
4f0dd89eb9 Change from warning to error (config.local.php save error) 2023-03-19 14:46:15 +01:00
slawkens
79f7c3dbd4 nothing important 2023-03-19 14:34:29 +01:00
slawkens
f24fc75b12 Bump version to 0.8.10-dev 2023-03-16 10:41:57 +01:00
slawkens
4fcc71e127 Update CHANGELOG.md 2023-03-16 09:55:05 +01:00
slawkens
403b4aa89b Release v0.8.9 2023-03-16 09:44:26 +01:00
slawkens
613bcf379b Update CHANGELOG.md 2023-03-16 09:44:06 +01:00
slawkens
8f2cc2ca38 fix rel path 2023-03-16 09:03:45 +01:00
slawkens
cdae11226d add PLUGINS dir to twig paths 2023-03-15 18:05:24 +01:00
slawkens
79fd97ad78 plugins folder should be accessible from public 2023-03-15 18:03:24 +01:00
slawkens
b477d4c821 fix installer hang on 2023-03-07 09:28:52 +01:00
slawkens
289f82ad23 Update nginx-sample.conf 2023-03-06 08:27:26 +01:00
slawkens
92569b7965 patch some changes
add contributors
2023-03-01 10:36:38 +01:00
slawkens
c03b041f40 add .git to denied folders in nginx 2023-02-28 19:05:34 +01:00
slawkens
2ac8ed7411 more php 8.x compatibility 2023-02-18 21:23:21 +01:00
slawkens
3280b3b9df Update tables.headline.html.twig 2023-02-18 21:10:02 +01:00
slawkens
05c37b94bb Create account.back_button.html.twig 2023-02-18 21:09:56 +01:00
slawkens
a91e7226dc new buttons code for tibiacom template, can create button with any text 2023-02-18 21:09:51 +01:00
slawkens
a39600efe2 fix player save on tfs 1.5 with new ipv6 2023-02-18 20:57:55 +01:00
slawkens
4fd5922784 You can now disable status checking for testing purposes
Useful for local testing when there is no server running
2023-02-18 11:44:32 +01:00
slawkens
b3d1274ffe Release v0.8.8 2023-02-18 11:14:45 +01:00
slawkens
9de49b4b6a Update CHANGELOG.md 2023-02-18 11:13:56 +01:00
slawkens
e6a368c3ac Update CHANGELOG.md 2023-02-18 11:12:51 +01:00
slawkens
3dca1b519a 760 is correct permission 2023-02-16 10:16:07 +01:00
slawkens
ae8af396f4 fix #136 2023-02-16 08:56:08 +01:00
slawkens
38294420d5 patch from develop, IS_CLI fixes 2023-02-07 22:49:01 +01:00
slawkens
c0dee61add accounts.block has been removed 2023-02-07 22:46:47 +01:00
slawkens
a84c92e007 allow template pages to be placed in templates dir 2023-02-07 22:46:35 +01:00
slawkens
60a854e5fd new function> escapeHtml + fix css in admin menus 2023-02-06 17:39:23 +01:00
slawkens
fa9f7aab7c accounts.blocked is not used by AAC 2023-02-03 17:26:50 +01:00
slawkens
d697a556c2 Update online.php 2023-02-03 17:21:54 +01:00
slawkens
802fd831cb (probably) fix #204 2023-02-03 17:21:30 +01:00
slawkens
52ca8a844a Fix #178 2023-02-03 16:13:53 +01:00
slawkens
573fc819d3 fix db table detection failure 2023-02-03 16:05:21 +01:00
slawkens
ead9d79cb1 fix #185 2023-02-03 15:36:57 +01:00
slawkens
43c197316a feature: mail confirmed reward
Suggested by @EPuncker
2023-02-03 14:39:09 +01:00
slawkens
c318d3a9de Option to disable plugin adjusted 2023-02-03 14:09:39 +01:00
slawkens
80d3f5ffe8 Fix logout hook & add images/editor to .gitignore 2023-02-02 20:54:47 +01:00
slawkens
f9d85b10b7 Update .gitignore 2023-02-02 16:24:12 +01:00
slawkens
4028a58adc Update OTS_DB_PDOQuery_PHP71.php 2023-02-02 16:20:09 +01:00
slawkens
0a3a079b86 PHP 8.1 compatibility 2023-02-02 16:19:28 +01:00
slawkens
d691148c84 Revert "Fix compatibility with PHP 8.1"
This reverts commit 99338afacbb11fbe75449966ecb8f476ec27a89d.
2023-02-02 16:17:33 +01:00
slawkens
48f74b9c7a Update tinymce to v4.9.11 (latest release in 4.x series)
Taken from composer
2023-02-02 11:51:44 +01:00
slawkens
99338afacb Fix compatibility with PHP 8.1 2023-02-02 11:15:17 +01:00
slawkens
301c3b86e2 Add fill-mbstring, which is required by twig 2023-02-02 10:42:47 +01:00
slawkens
130f7ba405 Update Twig to v2.15.4 2023-02-02 10:37:45 +01:00
slawkens
e552bcfe82 Fix ipv6 introduced in latest TFS 2022-12-16 23:05:43 +01:00
the-overdriven
ad75499a91
Update admin.news.form.html.twig (#207)
rename Ticket to Ticker
2022-11-28 08:17:58 +01:00
slawkens
7ddcb441c8 nothing important..
some visual fixes
2022-11-04 09:28:51 +01:00
slawkens
99da8dbec1 Update account.change_mail.html.twig 2022-10-28 13:41:39 +02:00
slawkens
743d5164b3 Add more client versions 2022-10-28 13:41:35 +02:00
slawkens
1f7dfdca50 Add vocation into getTopPlayers 2022-10-28 13:41:23 +02:00
slawkens
2164d59331 Fix typo in br locale 2022-10-28 13:41:10 +02:00
slawkens
0d845b764b Add exception class
from develop
2022-10-28 13:40:16 +02:00
slawkens
0a2cd69a4b Add compat Gesior classes
To allow more custom pages be used with myaac
2022-09-12 14:16:36 +02:00
slawkens
ddb60fa1e0 Bump version to 0.8.8-dev 2022-09-12 11:13:21 +02:00
slawkens
b7e33c5e6d Fix config.account_premium_days for TFS 1.4+ 2022-09-10 21:37:42 +02:00
slawkens
095ff7963d Update CHANGELOG.md 2022-08-31 19:01:30 +02:00
slawkens
dfb8be07f0 Fix: get_version for release 2022-08-31 18:56:48 +02:00
slawkens
74b4d98bba Update to 0.8.7 2022-08-31 18:45:57 +02:00
slawkens
8a7e4f0132 Update CHANGELOG.md 2022-08-31 18:45:40 +02:00
slawkens
6ebdb0ba89 Update CHANGELOG.md 2022-08-31 18:45:13 +02:00
slawkens
33817e5ab1 Fix undefined notice
Ahh @gpedro ;)
2022-08-31 18:43:42 +02:00
slawkens
cd1b481de5 Delete VERSION 2022-08-16 17:38:50 +02:00
slawkens
ab99db62bd Update version 2022-08-15 20:14:24 +02:00
Gabriel Pedro
dd3d6b3f47
feat: custom words blocked (#190)
* Update config.php

* Update validator.php

* Update config.php
2022-07-30 22:53:19 +02:00
Gabriel Pedro
d99f507244
fix: query blob param escape (#200) 2022-07-30 22:47:44 +02:00
Gabriel Pedro
b6c8a0923f
feat: config use character sample skill (#201) 2022-07-27 10:12:30 +02:00
davi costa
0663b3bbf4
fix guild invite page (#196)
* fix guild invite

* removing var_dump

* sending error
2022-06-05 16:52:22 +02:00
slawkens
d683fce2b9 Fix #171 2022-06-04 21:43:37 +02:00
slawkens
3d56214c07 Fix #195 2022-06-04 20:45:12 +02:00
slawkens
e2575c3612 Don't count deleted players (patched from develop) 2022-05-31 11:54:56 +02:00
slawkens
084256ce01 Comment useless log line 2022-05-16 20:37:36 +02:00
slawkens
240be18367 Update login.php for latest TFS 1.x and otservbr
Works in both.
Thanks for Znote for rfc6238 lib.
2022-05-16 20:31:19 +02:00
slawkens
ac271839a6 Merge branch 'master' of https://github.com/otsoft/myaac 2022-05-16 14:33:53 +02:00
slawkens
734a63f6c3 Fix #191
Allow admin to create GM, God etc. names
2022-05-16 14:33:50 +02:00
thatmichaelguy
e73daedd42
Update change_rank.php (#194) 2022-04-26 21:17:40 +02:00
slawkens
802e6c228c login.php is now part of official repo
Big thanks to folks from OpenTibiaBR Team
Will be updated in next commits to support latest TFS too
2022-04-15 19:34:12 +02:00
slawkens
edf2004539 Fiz wrong path in .gitignore 2022-04-15 19:30:38 +02:00
slawkens
9e949eb32a Fix highscores page bug with high pages 2022-03-16 16:56:03 +01:00
slawkens
e255c35002 Add tables.headline
For future reference
2022-02-26 17:50:50 +01:00
slawkens
dfd3c2c4a5 <div> should not be inside of <table> element 2022-02-26 17:50:31 +01:00
slawkens
876543f064 Fix monsters reloading
Was wrong code applied from develop branch
2022-02-26 17:27:10 +01:00
slawkens
e10f82e0e9 Fix typo 2022-01-07 08:32:37 +01:00
slawkens
f496a48a4d
Add notice about branch for contributions 2022-01-07 08:28:33 +01:00
slawkens
1fbb7c373e Fixes (config.news_author, group_name|capitalize) 2022-01-02 07:31:57 +01:00
slawkens
d58d7f79e7 Save php sessions in myaac dir
Instead of default PHP location
This fixes problem with permissions
2021-12-28 07:28:16 +01:00
slawkens
0643c56bc5
move contributing to wiki 2021-12-27 10:03:10 +01:00
slawkens
c51acf9dbd Add browsehappy code 2021-12-22 07:03:05 +01:00
slawkens
2f2a326eac Revert "Update CHANGELOG.md"
This reverts commit 10dad0fb4e4770ae35178f8cd2749b5349b0bd73.
2021-12-16 20:17:44 +01:00
slawkens
10dad0fb4e Update CHANGELOG.md 2021-12-16 20:17:24 +01:00
slawkens
fe01070bd1
Update README.md 2021-12-07 19:44:55 +01:00
slawkens
b558109844
Update README.md 2021-12-07 19:41:11 +01:00
slawkens
ac37802b7a Typo. 2021-12-04 14:38:17 +01:00
slawkens
f9c8027c3f Fix undefined variable notice 2021-11-04 19:54:27 +01:00
Silic0n Alph4
28dd1969b3
Fix rules page formatting (#177)
The rules page uses a textarea to show lines break.
This commit replaces the textarea and uses the Twig
nl2br function to format the text for web browsers.

Fixes #176
2021-10-30 19:29:36 +02:00
anyeor
50270f6d6f
Update nginx-sample.conf (#175)
Now we prevent access to system directory and update php version.
2021-10-28 21:39:38 +02:00
slawkens
fad80307d8 Revert "Adjustments"
This reverts commit 323d1b0504c5ce9fb83e853b19c1aef700c081ac.
2021-10-23 12:15:58 +02:00
slawkens
323d1b0504 Adjustments 2021-10-23 12:00:52 +02:00
slawkens
d6c1232d2d Update .gitignore 2021-10-23 11:52:20 +02:00
silic0nalph4
678d719036
Fix: admin page changed feet to match body colour (#174)
When saving changes to a character, the admin page
overwrote their foot colour with the body colour.
This fix renders the correct variable into the page
so the foot colour is preserved.
2021-10-20 20:58:28 +02:00
slawkens
723e81e90e Fix: undefined variable notice on database_log enabled 2021-08-30 16:10:54 +02:00
slawkens
60d2cfea99 Fix #169 2021-08-11 22:47:59 +02:00
slawkens
84c39676ee Fix account character create if auto_login is enabled 2021-07-27 18:42:52 +02:00
slawkens
a11d038c1d Update to 0.8.6 2021-07-10 23:35:57 +02:00
slawkens
2f627bf4b0 VERSION needs eol=lf 2021-07-10 23:35:02 +02:00
slawkens
67c603ef94 Fix some unexpected behaviour in release.sh on "cd" command 2021-07-10 23:24:48 +02:00
slawkens
dec63f353f Update to 0.8.6-dev 2021-07-05 03:13:30 +02:00
slawkens
7ab6b026fb Move admin pages part 2 2021-07-05 03:11:42 +02:00
slawkens
a2a773d714 This is the actual security fix 2021-07-05 02:59:41 +02:00
slawkens
aa26a71949 Revert "Security fix"
This reverts commit ef2a4082980ef55f811803eff155c1d356465b26.
2021-07-05 02:51:45 +02:00
slawkens
e3c695175b Update admin files path 2021-07-04 07:10:46 +02:00
slawkens
ccdcdd01d8 Move admin files 2021-07-04 06:50:34 +02:00
slawkens
ef2a408298 Security fix
Don't allow slash in URL
2021-07-03 08:38:32 +02:00
slawkens
6a4dbcef62 Fix release.sh (some warning) 2021-06-08 23:18:24 +02:00
slawkens
c8a87a2a8a Update CHANGELOG.md 2021-06-08 22:26:54 +02:00
slawkens
d0bfe93d38 Update VERSION 2021-06-08 22:26:14 +02:00
slawkens
75df8c5a6a Update VERSION 2021-06-08 22:20:57 +02:00
slawkens
b55813e362 Update to v0.8.5 2021-06-08 22:19:52 +02:00
slawkens
575f0c62b4 Update CHANGELOG.md 2021-06-08 22:18:42 +02:00
slawkens
3e9544f1dc Fix forum boards white color style
So it works on all templates
2021-06-08 22:16:42 +02:00
slawkens
152e5ac70e Fix forum table style (boards & thread view) 2021-06-08 22:16:03 +02:00
slawkens
3544643a07 Fix guild back buttons (change logo & motd) 2021-06-08 22:15:47 +02:00
slawkens
f7ae76d10f Remove unneeded escape 2021-06-08 22:15:31 +02:00
slawkens
add9370696 Update CHANGELOG.md 2021-06-08 22:15:17 +02:00
anyeor
cadc17cc52
Update 404 response (#163)
Updating for new SPL standard.
2021-06-05 05:17:51 +02:00
slawkens
878dfc5a01 bcsub is not needed here
bcmath module is not required anymore
2021-05-01 01:21:22 +02:00
slawkens
2400f7c20a Fix #158 Thanks @Misztrz 2021-05-01 01:05:19 +02:00
czbadaro
9d7854dda6
Gratis premium account (#156)
* skip premdays and lastdays calculation when premdays = 65535 (gratis premium in TFS)

* TFS consider 65535 as gratis premium account and PHP_INT_MAX does not assume this value

* adds condition of premdays=65535 and standardize the label "gratis premium account" with tibia client

* adjust the label "days" when there is only one day of premium account

* adjusted premium account status

* Some small adjustment

* Sorry, typo.

Co-authored-by: slawkens <slawkens@gmail.com>
2021-04-22 22:49:42 +02:00
slawkens
7303aabc2b Some small fix regarding parsing creature name from request 2021-04-22 19:13:37 +02:00
slawkens
ab478f488a Fix some unexpected behaviour in characters.php
Just in case someone uses $storage variable somewhere in their code
Thanks TheEther
2021-04-14 02:20:26 +02:00
slawkens
c7a2b090d7 Fix guild list description new lines <br> being ignored
Thanks TheEther for reporting
2021-04-08 22:08:37 +02:00
slawkens
f2c3b6362d Fix travis 2021-02-23 23:13:22 +01:00
slawkens
c664be7b74 Update version to 0.8.5-dev 2021-02-23 15:10:44 +01:00
slawkens
aa17ddbf24 Fix compatibility with PHP 7.0 and lower 2021-02-23 15:10:03 +01:00
slawkens
62faacbed6 Update CHANGELOG.md 2021-02-18 18:41:58 +01:00
slawkens
d03d6e2ec1 Release of v0.8.4 2021-02-18 18:19:04 +01:00
slawkens
93a1760263 Patch "Delete char with house" from develop
Co-Authored-By: Lee <42119604+Leesneaks@users.noreply.github.com>
2021-02-16 02:18:13 +01:00
Lee
0de8894e4d #142 Guildnick fix
Fixes the Guildnick not showing in the guild pages.
2021-02-16 02:10:30 +01:00
slawkens
e95ea22dbd Revert "Fix phpmailer array style (PHP 8.0)"
This reverts commit 0ba886bc6bc898689e4c0630f724f7c620070981.
2021-02-16 01:56:21 +01:00
slawkens
5dbfde62a6 Update .travis.yml 2021-02-16 01:56:11 +01:00
slawkens
0ba886bc6b Fix phpmailer array style (PHP 8.0) 2021-02-16 01:42:42 +01:00
slawkens
2684205b5a More fixes for PHP 8.0 2021-02-16 01:39:58 +01:00
slawkens
856507fb66 Update .travis.yml 2021-02-16 01:33:53 +01:00
slawkens
d019fbc050 Attempt to fix travis build 2021-02-16 01:31:48 +01:00
slawkens
129d5653e6 Apply changes from develop branch 2021-02-16 01:18:01 +01:00
slawkens
9560ad0c20 Add missing migration from 0.9 2021-02-16 01:06:03 +01:00
slawkens
a4fa7567aa Increase size of myaac_visitors.page column to 2048
Thanks to OtLand user kaleuui (https://otland.net/threads/myaac-v0-8-3.268654/page-11#post-2643853)
2021-02-16 01:05:29 +01:00
slawkens
9ff032740c Minimum PHP 5.6 is now required 2021-02-16 00:30:56 +01:00
slawkens
dbc76abcdd Fix compatibility with PHP 8.0 (latest XAMPP)
Solution by doctrine developers
2021-02-16 00:29:09 +01:00
slawkens
746a5dc816 Fix setPremDays for latest TFS
Fixes editing account in admin panel
2021-02-15 21:05:19 +01:00
slawkens
194d110079 Fixed account getPremDays() function for latest TFS
This fixes account management + signature
2021-02-15 20:57:54 +01:00
slawkens
eed490507c Fix parsing empty strings in config.lua (with comments) 2021-02-13 22:56:53 +01:00
slawkens
2800ab1e88 Fix headling.php cannot find font 2021-02-13 22:35:57 +01:00
slawkens
faf40f8bed Fix typo 2021-02-13 22:08:38 +01:00
slawkens
3f12ee40ac Update .gitignore 2021-02-13 21:55:35 +01:00
slawkens
b4532bd473 Add ./login.php to .gitignore 2021-02-13 21:55:08 +01:00
slawkens
b389874a7e Ignore arrays in config.lua (fixes experienceStages loading)
In future we want to parse arrays too, this is just a temporary solution
Thread: https://otland.net/threads/myacc-problem.274795/
2021-02-13 21:54:12 +01:00
slawkens
ea2dc69f7c Add more clients to clients.conf.php 2021-01-18 01:49:08 +01:00
slawkens
b0593b0ae1 Fixed the check if vocations.xml were correctly loaded 2021-01-17 17:44:53 +01:00
slawkens
664348e475 Merge branch 'master' of https://github.com/slawkens/myaac 2021-01-17 17:16:02 +01:00
slawkens
e3e00f0109 Just typo.. 2021-01-17 17:15:54 +01:00
slawkens
d3850280f4
Add some badges to README.md 2021-01-17 10:18:43 +01:00
slawkens
d8b3b41358 Remove facebook.js, replace with direct live link
This fixes some console errors
2021-01-07 23:34:17 +01:00
slawkens
e7706cad74 Use local storage for saving menu items
Fixes a bug when visiting with browser: www.wykop.pl, and then navigating back to myaac (browser freeze)
2021-01-07 22:43:39 +01:00
slawkens
727d6788fe Password can now contain any characters
Also added limit of 29 characters (client limitation)
2020-12-30 00:28:42 +01:00
slawkens
e3ecf8ec96 Fix notice about premend 2020-12-30 00:28:05 +01:00
slawkens
1999b19a1c Add support for accounts.premium_ends_at (Latest tfs 1.x) 2020-12-30 00:11:46 +01:00
slawkens
c55e2910ac On prod it won't display any PHP errors
As suggested by PHP Manual
2020-12-29 22:11:58 +01:00
slawkens
ad3694ef96 Add SSL on external image requests of items and outfits
Co-Authored-By: Fernando Matos <fernando@pixele.com.br>
2020-12-26 23:45:36 +01:00
slawkens
7fd784b2f6 You cannot delete character more than twice (Thanks Okke) 2020-11-24 16:13:23 +01:00
slawkens
d8f0ac5880 Update .gitignore 2020-11-02 23:29:54 +01:00
slawkens
b4ee4de110 Bump version to 0.8.4-dev 2020-11-02 23:19:38 +01:00
slawkens
b9713fea76 Fix branch 2020-10-27 08:10:23 +01:00
slawkens
c6dd937922 Release 0.8.3 2020-10-27 07:53:11 +01:00
slawkens
81d4158c03 Update CHANGELOG.md 2020-10-27 07:51:30 +01:00
slawkens
bf0e6ff862 Add accept=".zip" to plugin upload file 2020-10-24 05:32:28 +02:00
slawkens
8518afe70d Fix two boxes being show on email_change_cancel 2020-10-12 22:30:04 +02:00
slawkens
091ab688e7 Fix when adding poll = template tibiacom broken
With Exception and red message
2020-10-12 21:59:47 +02:00
slawkens
2e5b066d88 Remove duplicated code 2020-10-09 20:07:56 +02:00
slawkens
cd3a15feab Add pdo_mysql as required extension
+ Some code refactoring
2020-10-09 20:07:20 +02:00
slawkens
836499a48c Fix some PhpStorm editor error message 2020-10-09 20:06:08 +02:00
slawkens
4983816ff6 Change wrong table header: Description -> Version 2020-10-09 20:03:39 +02:00
slawkens
0326657d60 Fix creating very uncommon (bugged) account names 2020-07-07 01:23:36 +02:00
slawkens
fcff820858 Fix #131 2020-07-07 00:53:56 +02:00
whiteblXK
dc536f0fc0
Added limit to search characters (#134)
* Update characters.php

* Update config.php

* Variable name change, better use LIMIT in query instead in loop

* Just to be sure. Security first :)

* use config function

Co-authored-by: slawkens <slawkens@gmail.com>
2020-07-07 00:31:50 +02:00
slawkens
f958b8dd4f Change hasTable -> hasColumn 2020-07-07 00:16:09 +02:00
slawkens
352d3b1bde
Merge pull request #133 from whiteblXK/patch-1
Fixed bug with showing hidden characters
2020-07-06 23:59:50 +02:00
whiteblXK
f3061a0e74
Fixed bug with showing hidden character 2020-07-06 23:07:18 +02:00
slawkens
d4222e98e6 Fix #132 2020-07-03 23:24:46 +02:00
slawkens
8dd07d4873 Fix account create when account_mail_verify is enabled 2020-07-03 22:44:21 +02:00
slawkens
5f891fb9d6 Add some notice about Email validation 2020-07-03 22:15:27 +02:00
slawkens
b3b6d0ff5d Fix for CloudFlare IP detection 2020-07-03 20:38:48 +02:00
slawkens
0ac01b3f0d Fix undefined constant 2020-06-26 23:57:22 +02:00
slawkens
c6e55edb09 Fix network_twitter link in tibiacom template 2020-06-20 08:50:11 +02:00
slawkens
dfc70c098f Fix XSS in character search 2020-06-06 18:32:22 +02:00
slawkens
c1d1e9596a Update CHANGELOG.md 2020-06-06 18:01:40 +02:00
slawkens
53078e046e Fix admin menu news editing warning when leaving page without touching the inputs 2020-06-06 17:57:23 +02:00
slawkens
2af968031c Update version to 0.8.3-dev 2020-06-06 09:10:37 +02:00
slawkens
bdd3c394a3 Move register DATABASE_VERSION into schema.sql
Caused migrations being fired when user manually imported database
2020-06-06 07:33:33 +02:00
slawkens
f719b3c112 Update CHANGELOG.md 2020-06-03 23:56:13 +02:00
slawkens
8e0001a635 Fix release branch 2020-06-03 23:47:46 +02:00
slawkens
5b3581b88e Update CHANGELOG.md 2020-06-03 21:39:47 +02:00
slawkens
ca1436ea3f Fix #123 Guild Invite not working on otservbr-global 2020-06-03 21:35:45 +02:00
slawkens
5cd6b79ee0 Revert some change I did
Causing "'" and "-" being accepted as first character in player name
2020-06-03 21:21:28 +02:00
slawkens
0ec5942ee4 Update CHANGELOG.md 2020-06-03 21:04:49 +02:00
slawkens
90af164a8a Release v0.8.2 2020-06-03 21:02:33 +02:00
slawkens
fd83ee37ae Update CHANGELOG.md for 0.8.2 release 2020-06-03 21:01:36 +02:00
slawkens
8e935e62be Avoid ERR_TOO_MANY_REDIRECTS on template change
(cherry picked from commit 523afccb51f5e19f0da301e7f475e799d27d5303)
2020-06-03 20:42:52 +02:00
slawkens
a0d38b1f36 Fix #128 (Remove MyISAM engine) from migration scripts
(cherry picked from commit 2c09b0ae8637572dcb4d84e8ed51929092561ee0)
2020-06-03 20:42:41 +02:00
slawkens
6b49ecc99a Fix message() function when executed in CLI
(cherry picked from commit 8de8ad13bf2f0851b958d8fad82ea29938e4e7b2)
2020-06-03 20:42:36 +02:00
slawkens
ae24a464dc Add new constant: IS_CLI
Also fixed some warnings when running in CLI mode

(cherry picked from commit 70bd442bb004f405f117f45787f4c5c357490835)
2020-06-03 20:42:29 +02:00
slawkens
f519784cae Fix #126 (Max count and chance not shown)
(cherry picked from commit 5250b3189b287048a710f78cc7f43174e30e8dee)
2020-06-03 20:42:10 +02:00
slawkens
601cbd5ab7 dummy me.. thanks @gerotib
(cherry picked from commit 2534651e20b50b808c6e043f3516ac4ec4c08585)
2020-06-03 20:42:05 +02:00
Lee
fd4a507645 Update version.php
- removed extra line that is added when using a newer version than official release.

(cherry picked from commit e2ab3013403075647118441d32000ed8e327caae)
2020-06-03 20:41:53 +02:00
slawkens
bf8d07226e Fix #125 (wrong mana of character samples)
Should be 90.

(cherry picked from commit 700f835243c368d8b9ce2654cc01921e59d16593)
2020-06-03 20:41:36 +02:00
slawkens
fcddfb6adf Remove duplicated code
(cherry picked from commit 9ce7162a045b8544977d78bf3268694861f79380)
2020-06-03 20:41:23 +02:00
slawkens
5fcd97129e Rewrite towns support for TFS 1.3
Won't show warning anymore

(cherry picked from commit cd58008a0f667e97122d6c0e3b164df7979ade3f)
2020-06-03 20:41:14 +02:00
slawkens
af3a1c2f55 Add error_reporting in admin panel
Same as in main page

(cherry picked from commit 1f6bd975d08c451ef626da354510ad6b078719b7)
2020-06-03 20:41:08 +02:00
slawkens
13584a4d96 Move migration into separate file + add into admin panel
This fixes some rare bugs when database is no up-to-date and someone enters admin panel

(cherry picked from commit dbe83f8a747a792add9045e645f9be78e7a13148)
2020-06-03 20:40:52 +02:00
slawkens
6de4953d50 Change input type of account_login to text
This fixes autofill by Chrome and other tools

(cherry picked from commit fb326d03545c6403da98da50b37a05e276e11ad0)
2020-06-03 20:40:21 +02:00
slawkens
b15c213890 Add executing missing migration on install
This fixes missing rules on clean install

(cherry picked from commit 8e04328482d1888256dd22362332078fb23f6a61)
2020-06-03 20:40:13 +02:00
slawkens
2f52e5d9f3 Fixes in create new character nick
+ fixed config.character_name_min/max_length being ignored in change_name.php

(cherry picked from commit d148b71f0f7559029ac2e6afee0af2bd9493dc8a)
2020-06-03 20:39:52 +02:00
Lee
1d6afea9c4 CreateChar Fix
-checks if name has double space on create character (#121)

(cherry picked from commit 4e68838172e8ddfaa20fe4592c66d992f353255f)
2020-06-03 20:35:13 +02:00
slawkens
8d79efd6ad Add system/data to .gitignore
(cherry picked from commit 1799ef42a7ec7d446b922c5a07697c367c60e80f)
2020-06-03 20:34:47 +02:00
slawkens
99bcd54afe Fix cancel change email request
Thanks to OtLand user anyeor

(cherry picked from commit df59b104db5924b26501090901c75096c3538f4b)
2020-06-03 20:34:36 +02:00
slawkens
6ce6eee529 Fix exception when characters.frags enabled on TFS 1.x
(cherry picked from commit ee6e68d0bf9417f3193bcfd4ea2920d8430806db)
2020-06-03 20:34:19 +02:00
slawkens
054b40e358 Add example quest
(cherry picked from commit 7c208b38ed9376044787502a8634c6f099451423)
2020-06-03 20:33:53 +02:00
648 changed files with 11193 additions and 6849 deletions

1
.gitattributes vendored
View File

@ -8,3 +8,4 @@ _config.yml export-ignore
release.sh export-ignore
*.sh text eol=lf
VERSION text eol=lf

16
.github/workflows/phplint.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: PHP Linting
on:
pull_request:
branches: [master]
push:
branches: [master]
jobs:
phplint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: overtrue/phplint@3.4.0
with:
path: .
options: --exclude="system/libs/polyfill-mbstring/bootstrap80.php"

42
.gitignore vendored
View File

@ -1,11 +1,29 @@
Thumbs.db
.DS_Store
.idea
#
/.htaccess
lua
# composer
composer.lock
vendor
# npm
node_modules
tools/ext
# cypress
cypress.env.json
cypress/e2e/2-advanced-examples
cypress/screenshots
# created by release.sh
releases
tmp
releases
config.local.php
PERSONAL_NOTES
# all custom templates
templates/*
@ -16,6 +34,16 @@ templates/*
images/guilds/*
!images/guilds/default.gif
# editor images
images/editor/*
!images/editor/index.html
# gallery images
images/gallery/*
!images/gallery/index.html
!images/gallery/demon.jpg
!images/gallery/demon_thumb.gif
# cache
system/cache/*
!system/cache/index.html
@ -23,16 +51,26 @@ system/cache/*
!system/cache/signatures/index.html
!system/cache/plugins/index.html
# php sessions
system/php_sessions/*
!system/php_sessions/index.html
# logs
system/logs/*
!system/logs/index.html
# data
system/data/*
!system/data/index.html
# plugins
plugins/*
!plugins/.htaccess
!plugins/example.json
!plugins/account-create-hint.json
!plugins/account-create-hint
!plugins/email-confirmed-reward.json
!plugins/email-confirmed-reward
landing
# others/rest

View File

@ -7,13 +7,14 @@ php:
- 7.2
- 7.3
- 7.4
- 8.0
cache:
directories:
- $HOME/.composer/cache
before_script:
- composer require jakub-onderka/php-parallel-lint --no-suggest --no-progress --no-interaction --no-ansi --quiet --optimize-autoloader
- composer require php-parallel-lint/php-parallel-lint --no-suggest --no-progress --no-interaction --no-ansi --quiet --optimize-autoloader
script:
- php vendor/bin/parallel-lint --no-progress --no-colors --exclude vendor .
- php vendor/bin/parallel-lint --no-progress --no-colors --exclude vendor --exclude "system/libs/pot/OTS_DB_PDOQuery_PHP71.php" .

View File

@ -1,19 +1,372 @@
# Changelog
## [0.8.2 - x.x.2020]
## [0.8.23 - 22.04.2025]
### Added
* Add db->hasTableAndColumns(table, columns), credits to @opentibiabr Team (https://github.com/slawkens/myaac/commit/9c15c2fa6848d966457972fce0d04347ecbd4f2c)
* Add noSubmit option to buttons.base (https://github.com/slawkens/myaac/commit/4b9a7eaf859f9d3dbc90deb03564fcddde2f90d3)
### Changed
* Display more info when error parsing config.lua value (https://github.com/slawkens/myaac/commit/29a198bfcedeb5d22e0d34c5c53098142acdf477)
* Change logout button color to red in tibiacom template (https://github.com/slawkens/myaac/commit/1708a48186294e3eea0a79344fdf56ba93327c6d)
### Fixed
* Fix headline.php: change image format to .png cause of black background (https://github.com/slawkens/myaac/commit/68170e42cd8fac43c55654ba595f09425618f26e)
* Fix long title on headline.php (https://github.com/slawkens/myaac/commit/727da8b0cc968d0cafc502dd9c8cebf17e46bf76)
* getPremDays: returns -1 if freePremium (https://github.com/slawkens/myaac/commit/89f537c8c2646c748a14ee46539a6103fda88ec1)
## [0.8.22 - 27.01.2025]
### Changed
* Better exception handler, which clears the whole html output, so the message is better visible + better style (https://github.com/slawkens/myaac/commit/c304a9ab43bf0bda41907db3e6f65293350640a2 + https://github.com/slawkens/myaac/commit/7abc3c78334d44fb64684b26d8a305d0fe676caa)
* Optimize $player->isOnline() function, thanks @gesior (https://github.com/slawkens/myaac/commit/7c3ebf70fa4751af986be7b46ee3530d4875271e)
* All $cache->set calls should have $ttl (https://github.com/slawkens/myaac/commit/37ba9c7366139778d09d1316d7cb49a255165778)
### Fixed
* exception in lostaccount.php - $account->getName() (https://github.com/slawkens/myaac/commit/d390ea325e2dbfd87a830cfe40991f58e07a87a1)
## [0.8.21 - 09.01.2025]
### Added
* support for button_color (red, green, blue) (https://github.com/slawkens/myaac/commit/b2c9eb474513650a014352d820602b8007eb3bf3)
### Changed
* Set default_socket_timeout for ipinfo.io checkup (https://github.com/slawkens/myaac/commit/e09fe517747e4f462c72395ede39759bf308d171)
### Fixed
* XSS in forum (https://github.com/slawkens/myaac/commit/580b888b1dd1317d7ccf5f888536159c3bfe4324)
* move_thread by unauthorized user (https://github.com/slawkens/myaac/commit/19d3e15c114de65ef6c379e4da66d32138a0e7c4)
* change sex wrong price deducted (https://github.com/slawkens/myaac/commit/39b19ed4c8724385ee80f7d02219e84f6b3f5d95)
* fix for TFS 1.4.2 exception on creating character, where conditions column is NULL (https://github.com/slawkens/myaac/commit/5a68d204bb24392d424efde4133b0a3222e788bb)
## [0.8.20 - 26.11.2024]
Small fix regarding the latest release and the linux system.
Download this one, instead of the 0.8.19 if you are using linux.
If you are using 0.8.19, make this update:
The fix is to make this change in the system/libs/hooks.php
Change
```
require_once LIBS . 'src/plugins.php';
```
Into:
```
require_once LIBS . 'src/Plugins.php';
```
Yeah, we just changed 'p' to 'P' - that's just case-sensitive nature of linux.
## [0.8.19 - 19.11.2024]
### Added
* syntactic sugar for db structure changes (https://github.com/slawkens/myaac/commit/e0036a3e32e8c37c28665dd7ae18ac9b8fc167d9)
* add "None" vocation to highscores (https://github.com/slawkens/myaac/commit/7f4737631dfcb6ec255c6d9301304d3bf222a033)
* new hooks in account manage + create (https://github.com/slawkens/myaac/commit/d40178104b0f411b9672102c49a4b87ac16e1779)
* new functions: getGuildNameById($id) + getGuildLogoById($id) + Plugins::installMenus($templateName, $menus, $clearOld = false) (https://github.com/slawkens/myaac/commit/de1bb37bcb6d111fbdf185ef9c2fec7e7f05053e + https://github.com/slawkens/myaac/commit/d1c5a189c3b182a36933ed507c6ae36b61fe1d45 + https://github.com/slawkens/myaac/commit/5a953ce901522d080aa16fcfcd268e9544bf6e1a)
### Changed
* set default encryption to sha1 (https://github.com/slawkens/myaac/commit/55b8645d3f38c47f4aafc1906625b676c429cdd5)
* prefer get_browser_real_ip() over REMOTE_ADDR (cause of Cloudflare and similar services) (https://github.com/slawkens/myaac/commit/b5bbae62b09db50a73bfa3e288245ea718005aa9)
* allow account_create_character_create even if account_mail_verify is activated (https://github.com/slawkens/myaac/commit/1edf8833c844b25372017e4affaf12aa02cdce7a)
* better https detection (patched from develop) (https://github.com/slawkens/myaac/commit/d73aceb272d0615244fcfd0998d75e6c6c15d3fe)
* require login before create new thread (#261, @anyeor)
* better tables.headline.html.twig (patched from 1.0) (https://github.com/slawkens/myaac/commit/71ef30d35ecb2f876e9b861f211f737302bf408e)
### Fixed
* bans page fixed functions getPlayerNameByAccount + getPlayerNameById (https://github.com/slawkens/myaac/commit/d39386cfabfa13e5c916ead69e2f8f90fdc47f4f)
* account verify - do not allow login without verified email (https://github.com/slawkens/myaac/commit/1edf8833c844b25372017e4affaf12aa02cdce7a, Thanks @anyeor)
* if <flags> is not present in monster.xml (https://github.com/slawkens/myaac/commit/81b6652738a7b04be3980cbf55443a6fbe437b34)
* $db->update when there is null value (https://github.com/slawkens/myaac/commit/1458b7a412ff6875cebba1b88d380f7f959ee6be)
* error on $db __destruct saving current script name in CLI (https://github.com/slawkens/myaac/commit/01660bd2b4967315c0e16d2f83c6c39f0b78683d)
## [0.8.18 - 29.05.2024]
### Added
* hook in guilds page to support guild wars (https://github.com/slawkens/myaac/commit/f875f3cd2059fac5c23a08ce73dd8621a66613e0)
## [0.8.17 - 18.05.2024]
### Added
* TwigTypeCastingExtension (https://github.com/slawkens/myaac/commit/7181b988e9518320d57486670ca4e2d3b2fe1cfa)
### Fixed
* fix XSS in creatures.php (https://github.com/slawkens/myaac/commit/02eea950e4fd756e8d5c32e56181986d51f5ac70, @gesior)
* don't allow redirect to external website (https://github.com/slawkens/myaac/commit/ef62b53cec5a479cc85aa15940ad9ebbcefde876)
* change_info if account_country is disabled (https://github.com/slawkens/myaac/commit/62d3c198d567541a90900fe2d7ede070e7b1ff68)
### Changed
* use word-break: break-all in guilds description + character comment (https://github.com/slawkens/myaac/commit/191ad25eb2d4c1cec6f6668da7a345fec0ad2a7f)
* set default status_ip to 127.0.0.1, most server are hosted locally anyway (https://github.com/slawkens/myaac/commit/2793c41655b47f7db295143a298ccda70f11462b)
## [0.8.16 - 12.02.2024]
### Fixed
* broken installation
* database and finish step warnings/errors (https://github.com/slawkens/myaac/pull/245, @danilopucci)
* silently ignore if the hook does not exist
## [0.8.15 - 09.12.2023]
More security fixes, especially in bugtracker.
## [0-8.14 - 27.11.2023]
Security fixes.
### Fixed
* XSS vulnerability in bugtracker (https://github.com/slawkens/myaac/commit/83a91ec540072d319dd338abff45f8d5ebf48190)
* XSS vulnerability in forum (https://github.com/slawkens/myaac/commit/d1bc63d07ad88a143358cacd2c417891eea74dcc + https://github.com/slawkens/myaac/commit/55dbade8d5280c5baed45e5f7ebc3613b8e9b9e8)
* Session Fixation (https://github.com/slawkens/myaac/commit/483155cf4c1e3068aaee0d44541dfa61f6223379)
* displaying ban info on account page (https://github.com/slawkens/myaac/commit/764db0c203d1826ffce3a5a78f83a97e56bd0685)
### Changed
* Clear some additional cache keys - like database cache (https://github.com/slawkens/myaac/commit/4327b66f915d06dce504211692173606b9ef3b4e)
## [0.8.13 - 16.09.2023]
### Added
* latest client versions to config (https://github.com/slawkens/myaac/commit/765886f0c782807400c429577cde5e45bd7c308f)
* patching from develop - twig context for hooks (https://github.com/slawkens/myaac/commit/f1670f4012cc7595433fe0b1937c1f9b15a60b07)
### Fixed
* fixed XSS vulnerability in some pages (https://github.com/slawkens/myaac/commit/5c3b01aca4f3cfe8abc86b8ce48194b2da87b808)
Nothing more or less!
## [0.8.12 - 07.08.2023]
I've moved the repository back to my personal account. (Just so you know!)
I will also try to add git commits pointed to each change, lets see if you like it or not - you can comment in discussion, that will be created just after releasing this version :)
### Added
* forum: better error messages (Suggested by @anyeor) (https://github.com/slawkens/myaac/commit/34725e0257684fe5fa43875cc3a8f587ba04642e)
* more support for GesiorAAC classes, so some of them will work with MyAAC (https://github.com/slawkens/myaac/commit/a8172a518ff8939c4402349b16c064fcaf855d31)
* word-break on forum thread & reply (Suggested by @anyeor) (https://github.com/slawkens/myaac/commit/ce4aed0f1719d2aadc749e5238e883e3c10e2686)
### Fixed
* not working pages/links from database, introduced in 0.8.10 (Thanks to OtLand user - https://otland.net/members/0lo.99657/ for report) (https://github.com/slawkens/myaac/commit/1e874c7027769bd09e772a1cdac75d7e37991256)
* it was possible to create topic in board that was closed, ommiting the error check (Thanks to @anyeor for report) (https://github.com/slawkens/myaac/commit/0d52978d9fb99869500d35e7676f454ca5eaba14)
* PHP 8.2 compatibility - removed deprecated functions utf8_encode & utf8_decode (https://github.com/slawkens/myaac/commit/a338fd967cdbcc89e86be4e6b66b2cad2ff23251)
* guild description not being correctly shown (Reported by @anyeor) (https://github.com/slawkens/myaac/commit/f2a3ec1185df64ad9084d4ff55790ae4a5b3e5fd, https://github.com/slawkens/myaac/commit/df321154f63d458a4bc7d83bac5e3447b67317a4)
### Removed
* Some old code for verifying messages length (Reported by @anyeor) (https://github.com/slawkens/myaac/commit/df48363ea4ced4350fd90ffddf57d464ba5afa8b)
* some info about config failed to load, was never working (https://github.com/slawkens/myaac/commit/7a546e5a41036b0e9e926d337c6f2e3c41c591d2)
## [0.8.11 - 30.06.2023]
### Added
* new function from 0.9 - Cache::remember($key, $ttl, $callback)
* new characters page hooks
* line number & file to exception handler, to easier localize exceptions
### Changed
* rename to .htaccess.dist, causes some problems on default setup
* removing unneccessary PHP closing tags to prevent potential issues (by @SRNT-GG)
* display warning if hook file does not exist
### Fixed
* important: Not allow create char if limit is exceeded (by @anyeor) could have been used to spam database
* deleted chars: cannot change comment, name, gender, cannot create guild, cannot be invited, cannot accept invite, cannot be passed leadership to
* forum: quote and edit post buttons not being shown
* twig exception thrown when player does not exist, on character change comment (thanks @anyeor)
* BASE_DIR when accessing /tools
* do not display warning if HTTP_ACCEPT_LANGUAGE is not set
## [0.8.10 - 18.05.2023]
### Changed
* PHP 7.2.5 is now required, cause of Twig 2.x
* allow pages to be placed in templates folder, under pages/ subfolder
### Fixed
* Twig error with global variable on create account
* links/redirects from facebook, etc. like ?fbclid=x
* do not allow to continue install when there is no server database imported
* cannot go forward when config.local.php cannot be saved
* when server uses another items serializer
* small bug on install - please fill all input
## [0.8.9 - 16.03.2023]
### Added
* You can now disable server status checking for testing purposes, useful for local testing when there is no server running
* with this, the page won't need 2 seconds to load
* set status_enabled to false in config.php
* new buttons code for tibiacom template, can create button with any text
* patched some small changes from develop branch
### Changed
* add .git to denied folders in nginx-sample.conf
* plugins folder is now accessible from outside
* add plugins folder to twig search paths
### Fixed
* player save on tfs 1.5 with new ipv6
* more php 8.x compatibility
* rel path for exception message, causing message to be not in red background
## [0.8.8 - 18.02.2023]
### Added
* mail confirmed reward
* support for latest group changes in TFS
* new function: escapeHtml
### Updated
* TinyMCE to v4.9.1 (latest release in 4.x series)
* Twig to v2.15.4
### Changed
* you can now place custom pages in your template directory under pages/ folder
* HOOK_LOGOUT parameters, now only account_id is passed
### Fixed
* ipv6 introduced in latest TFS
* config.account_premium_days for TFS 1.4+
* better compatibility with GesiorAAC
* PHP 8.1 compatibility
* myaac_ db table detection failure
* reload creatures error, when items cache has been cleared
### Removed
* accounts.blocked column, which is not used by AAC
## [0.8.7 - 31.08.2022]
### Added
* login.php for client 12.x is now part of official repo
* browsehappy code
* config use character sample skill (#201, @gpedro)
* custom words blocked (#190, @gpedro)
### Changed
* save php sessions in myaac dir
* don't count deleted players when creating new character
### Fixed
* patch vulnerability in change_rank.php (#194, @gesior, @thatmichaelguy)
* fix guild invite page (#196, @worthdavi)
* players not showing on highscores page (#195)
* highscores page bug with high pages
* $player->getStorage() does not work at all (#169, @gesior)
* copying sample character when it have items with quotes (#200, @gpedro)
* IPv6 issue when env is set to dev (#171)
* admin page changed feet to match body colour (#174, @silic0nalph4)
* exception being thrown when creating duplicated character name (#191)
* rules page formatting (#177, @silic0nalph4)
* account character create if auto_login is enabled
* undefined variable notice on database_log enabled
* removed VERSION file
## [0.8.6 - 10.07.2021]
This update contains very important security fix.
Please update your MyAAC instances to this version.
## [0.8.5 - 08.06.2021]
### Changed
* bcmath module is not required anymore
* Gratis premium account fixes (#156, by @czbadaro)
* Update 404 response (#163, by @anyeor)
### Fixed
* compatibility with PHP 7.0 and lower
* deleting ranks in guilds (#158, by @Misztrz)
* guild back buttons (change logo & motd)
* forum table style (boards & thread view)
* guild list description new lines `<br>` being ignored (Thanks @anyeor for reporting)
## [0.8.4 - 18.02.2021]
### Added
* support for accounts.premium_ends_at (Latest TFS 1.x)
* more clients to clients.conf.php
### Changed
* minimum PHP 5.6 is now required
* password can now contain any characters
* add SSL on external image requests of items and outfits (@fernandomatos)
* Use local storage for saving menu items (tibiacom template) - fixes bug with some websites like wykop.pl (browser freeze)
* increase size of myaac_visitors.page column to 2048 (Thanks to OtLand user kaleuui)
### Fixed
* compatibility with PHP 8.0 (latest XAMPP)
* displaying PHP errors on env = "prod"
* the Guildnick not showing in the guild pages (@leesneaks)
* you cannot delete character more than twice (Thanks Okke)
* ignore arrays in config.lua (fixes experienceStages loading)
* parsing empty strings in config.lua (with comments)
* headling.php cannot find font
## [0.8.3 - 27.10.2020]
### Added
* pdo_mysql as required extension
* some notice about Email validation in create account
### Changed
* Move register DATABASE_VERSION into schema.sql
* Caused migrations being fired when user manually imported database
### Fixed
* creating very uncommon (bugged) account names
* XSS in character search
* Admin menu news editing warning when leaving page without touching the inputs
* Guild Invite not working on otservbr-global
* two boxes being show on email_change_cancel
* when adding poll = template tibiacom broken
* houses: Unknown column 'guild' in 'where clause (https://github.com/slawkens/myaac/issues/131)
* account create when account_mail_verify is enabled
* CloudFlare IP detection
* network_twitter link in tibiacom template
## [0.8.2 - 03.06.2020]
### Added
* Log query time in database_log (can be used for benchmarking)
* new PHP constant: IS_CLI
* $_SERVER['REQUEST_URI'] to database.log
* outfit to highscores box in tibiacom template
* system/data to .gitignore
* error_reporting in admin panel (when in dev mode), so it shows php notices and warnings
* example quests in config.php
### Changed
* account_login input type from password to text
### Fixed
* Updating template menus on template change
* Guild Invite not working on otservbr-global (#123)
* news not updating after adding in admin panel
* wrong mana of character samples (#125)
* missing rules page on clean install
* double space character name creation (@Lee, #121)
* creatures page: Max count and chance not shown on hovered items
* exception being thrown when characters.frags enabled on TFS 1.x
* TFS 0.4 guilds creation (Where guilds.checkdata and motd doesn't have default value)
* ERR_TOO_MANY_REDIRECTS browser error on template change
* updating template menus on template change
* Account change info when config.account_country is disabled
* cancel change email request
* config.character_name_min/max_length being ignored in change_name.php
* some rare bugs when database is no up-to-date and someone enters admin panel
* extra line that is added when using a newer version than official release (@Lee)
* admin links in featured article
* some PHP Notice when HTTP_HOST is not set (Can happen on some old versions of HTTP protocol)
* Show character indicator in check_name.js
* Houses list View button
* Fix OTS_House houseid parameter
* Houses list View button was wrong (was from bootstrap)
* OTS_House __construct - not loading by houseid parameter
* message() function when executed in CLI
### Removed
* unused myaac_commands table from schema
* MyISAM engine from migration scripts (#128)
## [0.8.1 - 10.03.2020]

14
CONTRIBUTORS.txt Normal file
View File

@ -0,0 +1,14 @@
# automatically exported using this script:
# git log --all --format='%cN <%cE>' | sort -u > contributors
# in no particular order
# cleaned for readability
Evil Puncker <EPuncker@users.noreply.github.com>
Fernando Matos <fernando@pixele.com.br>
Lee <42119604+Leesneaks@users.noreply.github.com>
caio <caio.zucoli@gmail.com>
slawkens <slawkens@gmail.com>
tobi132 <52947952+tobi132@users.noreply.github.com>
vankk <nwtr.otland@hotmail.com>
whiteblXK <krzys16001@gmail.com>
xitobuh <jonas.hockert92@gmail.com>

View File

@ -1,2 +1,3 @@
* Gesior.pl (2007 - 2008)
* Slawkens (2009 - 2020)
* Slawkens (2009 - 2023)
* Contributors listed in CONTRIBUTORS.txt

View File

@ -1,18 +1,38 @@
# myaac
# [MyAAC](https://my-aac.org)
MyAAC is a free and open-source Automatic Account Creator (AAC) written in PHP. It is a fork of the [Gesior](https://github.com/gesior/Gesior2012) project. It supports only MySQL databases.
Official website: https://my-aac.org
### REQUIREMENTS
[![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/slawkens/myaac/cypress.yml)](https://github.com/slawkens/myaac/actions)
[![License: GPL-3.0](https://img.shields.io/github/license/slawkens/myaac)](https://opensource.org/licenses/gpl-license)
[![Downloads Count](https://img.shields.io/github/downloads/slawkens/myaac/total)](https://github.com/slawkens/myaac/releases)
[![OpenTibia Discord](https://img.shields.io/discord/288399552581468162)](https://discord.gg/2J39Wus)
[![Closed Issues](https://img.shields.io/github/issues-closed-raw/slawkens/myaac)](https://github.com/slawkens/myaac/issues?q=is%3Aissue+is%3Aclosed)
| Version | Status | Branch | Requirements |
|:--------|:-----------------------|:--------|:---------------|
| 2.x | Experimental features | develop | PHP >= 8.1 |
| **1.x** | **Active development** | main | **PHP >= 8.1** |
| 0.9.x | Not developed anymore | 0.9 | PHP >= 7.2.5 |
| 0.8.x | Active support | 0.8 | PHP >= 7.2.5 |
| 0.7.x | End Of Life | 0.7 | PHP >= 5.3.3 |
The recommended version to install is 1.x, which can be found at releases page - [https://github.com/slawkens/myaac/releases](https://github.com/slawkens/myaac/releases).
### Documentation
* [docs.my-aac.org](https://docs.my-aac.org)
* [my-aac.org - FAQ](https://my-aac.org/faqs/)
### Requirements
- PHP 5.5 or later
- MySQL database
- PDO PHP Extension
- XML PHP Extension
- ZIP PHP Extension
- (optional) mod_rewrite to use friendly_urls
- PHP Extensions: pdo, xml, json
- (optional) apache2 mod_rewrite (to use friendly_urls)
- (optional) zip PHP Extension (to install plugins)
- (optional) gd PHP Extension (for generating signature images)
### INSTALLATION AND CONFIGURATION
### Installation
Just decompress and untar the source (which you should have done by now,
if you're reading this), into your webserver's document root.
@ -28,19 +48,51 @@ Official website: https://my-aac.org
chmod 660 images/guilds
chmod 660 images/houses
chmod 660 images/gallery
chmod -R 770 system/cache
chmod -R 760 system/cache
Visit http://your_domain/install (http://localhost/install) and follow instructions in the browser.
### KNOWN PROBLEMS
### Configuration
- none -
Check *config.php* to get more information. (Notice: MyAAC 1.0+ doesn't use config.php anymore, it has been moved to Admin Panel - Settings page).
### OTHER NOTES
Use *config.local.php* for your local configuration changes.
If you have a great idea or want contribute to the project - visit our website at https://www.my-aac.org
### Branches
### LICENSING
This repository follows the Git Flow Workflow.
Cheatsheet: [Git-Flow-Cheatsheet](https://danielkummer.github.io/git-flow-cheatsheet)
This program and all associated files are released under the GNU Public
License, see LICENSE for details.
That means, we use:
* main branch, for current stable release
* develop branch, for development version (next release)
* feature branches, for features etc.
### Known Problems
- Some compatibility issues with some exotic distributions.
### Contributing
Contributions are more than welcome.
Pull requests should be made to the *develop* branch as that is the working branch, master is for release code.
Bug fixes to current release should be done to master branch.
Look: [Contributing](https://github.com/otsoft/myaac/wiki/Contributing) in our wiki.
### Other Notes
If you have a great idea or want to contribute to the project - visit our website at https://www.my-aac.org
## Project supported by JetBrains
Many thanks to Jetbrains for kindly providing a license for me to work on this and other open-source projects.
[![JetBrains](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/slawkens)
### License
This program and all associated files are released under the GNU Public License.
See [LICENSE](https://github.com/slawkens/myaac/blob/master/LICENSE) for details.

16
SECURITY.md Normal file
View File

@ -0,0 +1,16 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 1.x.y | :white_check_mark: |
| 0.9.x | :x: |
| 0.8.x | :white_check_mark: |
| < 0.7 | :x: |
## Reporting a Vulnerability
If you found a security vulnerability, please write an email to security@my-aac.org
All reports will be taken very seriously, and a fix will be posted as soon as possible.

View File

@ -1 +0,0 @@
0.8.2-dev

View File

@ -1 +1,2 @@
<?php // nothing yet here ?>
<?php
// nothing yet here

View File

@ -2,6 +2,9 @@
// few things we'll need
require '../common.php';
define('ADMIN_PANEL', true);
define('MYAAC_ADMIN', true);
if(file_exists(BASE . 'config.local.php')) {
require_once BASE . 'config.local.php';
}
@ -12,8 +15,6 @@ if(file_exists(BASE . 'install') && (!isset($config['installed']) || !$config['i
throw new RuntimeException('Setup detected that <b>install/</b> directory exists. Please visit <a href="' . BASE_URL . 'install">this</a> url to start MyAAC Installation.<br/>Delete <b>install/</b> directory if you already installed MyAAC.<br/>Remember to REFRESH this page when you\'re done!');
}
define('ADMIN_PANEL', true);
$content = '';
// validate page
@ -27,6 +28,12 @@ define('PAGE', $page);
require SYSTEM . 'functions.php';
require SYSTEM . 'init.php';
if(config('env') === 'dev') {
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
}
// event system
require_once SYSTEM . 'hooks.php';
$hooks = new Hooks();
@ -34,6 +41,7 @@ $hooks->load();
require SYSTEM . 'status.php';
require SYSTEM . 'login.php';
require SYSTEM . 'migrate.php';
require ADMIN . 'includes/functions.php';
$twig->addGlobal('config', $config);
@ -45,7 +53,7 @@ if(!$logged || !admin()) {
}
// include our page
$file = SYSTEM . 'pages/admin/' . $page . '.php';
$file = ADMIN . 'pages/' . $page . '.php';
if(!@file_exists($file)) {
$page = '404';
$file = SYSTEM . 'pages/404.php';
@ -60,4 +68,4 @@ ob_end_clean();
// template
$template_path = 'template/';
require ADMIN . $template_path . 'template.php';
?>

View File

@ -182,7 +182,7 @@ if ($id > 0) {
}
$lastDay = 0;
if($p_days != 0 && $p_days != PHP_INT_MAX ) {
if($p_days != 0 && $p_days != OTS_Account::GRATIS_PREMIUM_DAYS) {
$lastDay = time();
} else if ($lastDay != 0) {
$lastDay = 0;
@ -279,7 +279,13 @@ else if ($id > 0 && isset($account) && $account->isLoaded()) {
<?php
$acc_group = $account->getAccGroupId();
if ($hasTypeColumn) {
$acc_type = array("Normal", "Tutor", "Senior Tutor", "Gamemaster", "God"); ?>
$groups = new OTS_Groups_List();
$acc_type = array("Normal", "Tutor", "Senior Tutor", "Gamemaster", "God");
if ($groups->getHighestId() == 6) {
$acc_type = array("Normal", "Tutor", "Senior Tutor", "Gamemaster", "Community Manager", "God");
}
?>
<div class="col-xs-6">
<label for="group" class="control-label">Account Type:</label>
<select name="group" id="group" class="form-control">
@ -420,7 +426,7 @@ else if ($id > 0 && isset($account) && $account->isLoaded()) {
<div class="box-body">
<form action="<?php echo $base; ?>" method="post">
<div class="input-group input-group-sm">
<input type="text" class="form-control" name="search_name" value="<?php echo $search_account; ?>"
<input type="text" class="form-control" name="search_name" value="<?php echo escapeHtml($search_account); ?>"
maxlength="32" size="32">
<span class="input-group-btn">
<button type="submit" type="button" class="btn btn-info btn-flat">Search</button>

View File

@ -10,8 +10,8 @@
defined('MYAAC') or die('Direct access not allowed!');
$title = 'Load items.xml';
require LIBS . 'items.php';
require LIBS . 'weapons.php';
require_once LIBS . 'items.php';
require_once LIBS . 'weapons.php';
$twig->display('admin.items.html.twig');

View File

@ -89,7 +89,7 @@ if (isset($_REQUEST['template'])) {
if (isset($menus[$id])) {
$i = 0;
foreach ($menus[$id] as $menu) {
echo '<li class="ui-state-default" id="list-' . $id . '-' . $i . '"><label>Name:</label><input type="text" name="menu[' . $id . '][]" value="' . $menu['name'] . '"/>
echo '<li class="ui-state-default" id="list-' . $id . '-' . $i . '"><label>Name:</label><input type="text" name="menu[' . $id . '][]" value="' . escapeHtml($menu['name']) . '"/>
<label>Link:</label><input type="text" name="menu_link[' . $id . '][]" value="' . $menu['link'] . '"/>
<input type="hidden" name="menu_blank[' . $id . '][]" value="0" />
<label><input class="blank-checkbox" type="checkbox" ' . ($menu['blank'] == 1 ? 'checked' : '') . '/><span title="Open in New Window">Open in New Window</span></label>
@ -134,4 +134,4 @@ if (isset($_REQUEST['template'])) {
$twig->display('admin.menus.form.html.twig', array(
'templates' => $templates
));
}
}

View File

@ -117,7 +117,7 @@ if($action == 'edit' || $action == 'new') {
'news_link_form' => '?p=news&action=' . ($action == 'edit' ? 'edit' : 'add'),
'news_id' => isset($id) ? $id : null,
'title' => isset($p_title) ? $p_title : '',
'body' => isset($body) ? htmlentities($body, ENT_COMPAT, 'UTF-8') : '',
'body' => isset($body) ? escapeHtml($body) : '',
'type' => isset($type) ? $type : null,
'player' => isset($player) && $player->isLoaded() ? $player : null,
'player_id' => isset($player_id) ? $player_id : null,

View File

@ -105,7 +105,7 @@ $twig->display('admin.pages.form.html.twig', array(
'title' => $p_title,
'php' => $php,
'enable_tinymce' => $enable_tinymce,
'body' => isset($body) ? htmlentities($body, ENT_COMPAT, 'UTF-8') : '',
'body' => isset($body) ? escapeHtml($body) : '',
'groups' => $groups->getGroups(),
'access' => $access
));
@ -196,5 +196,3 @@ class Pages
return !count($errors);
}
}
?>

View File

@ -210,7 +210,7 @@ if ($id > 0) {
if ($hasBlessingsColumn) {
$blessings = $_POST['blessings'];
verify_number($blessings, 'Blessings', 2);
verify_number($blessings, 'Blessings', 3);
}
$balance = $_POST['balance'];
@ -650,7 +650,7 @@ else if ($id > 0 && isset($player) && $player->isLoaded())
<label for="look_feet" class="control-label">Feet: <span
id="look_feet_val"></span></label>
<input type="range" min="0" max="132"
value="<?php echo $player->getLookBody(); ?>"
value="<?php echo $player->getLookFeet(); ?>"
class="slider form-control" id="look_feet" name="look_feet">
</div>
</div>
@ -697,7 +697,14 @@ else if ($id > 0 && isset($player) && $player->isLoaded())
<label for="lastip" class="control-label">Last IP:</label>
<input type="text" class="form-control" id="lastip" name="lastip"
autocomplete="off"
maxlength="10" value="<?php echo longToIp($player->getLastIP()); ?>"
maxlength="10" value="<?php
if (strlen($player->getLastIP()) > 11) {
echo inet_ntop($player->getLastIP());
}
else {
echo longToIp($player->getLastIP());
}
?>"
readonly/>
</div>
</div>
@ -777,7 +784,7 @@ else if ($id > 0 && isset($player) && $player->isLoaded())
<div class="box-body">
<form action="<?php echo $base; ?>" method="post">
<div class="input-group input-group-sm">
<input type="text" class="form-control" name="search_name" value="<?php echo $search_name; ?>"
<input type="text" class="form-control" name="search_name" value="<?php echo escapeHtml($search_name); ?>"
maxlength="32" size="32">
<span class="input-group-btn">
<button type="submit" type="button" class="btn btn-info btn-flat">Search</button>
@ -885,15 +892,13 @@ else if ($id > 0 && isset($player) && $player->isLoaded())
var look_feet = $('#look_feet').val();
var look_type = $('#look_type').val();
var look_addons = '';
<?php if($hasLookAddons): ?>
var look_addons = '&addons=' + $('#look_addons').val();
<?php
else: ?>
var look_addons = '';
look_addons = '&addons=' + $('#look_addons').val();
<?php endif; ?>
new_outfit = '<?= $config['outfit_images_url']; ?>?id=' + look_type + look_addons + '&head=' + look_head + '&body=' + look_body + '&legs=' + look_legs + '&feet=' + look_feet;
$("#player_outfit").attr("src", new_outfit);
console.log(new_outfit);
}
</script>
</script>

View File

@ -10,8 +10,6 @@
defined('MYAAC') or die('Direct access not allowed!');
$title = 'Plugin manager';
require_once LIBS . 'plugins.php';
$twig->display('admin.plugins.form.html.twig');
if (isset($_REQUEST['uninstall'])) {

View File

@ -36,4 +36,3 @@ $twig->display('admin.statistics.html.twig', array(
'account_type' => (USE_ACCOUNT_NAME ? 'name' : 'number'),
'points' => $points
));
?>

View File

@ -26,7 +26,7 @@ if ($version_compare == 0) {
success('MyAAC latest version is ' . $myaac_version . '. You\'re using the latest version.
<br/>View CHANGELOG ' . generateLink(ADMIN_URL . '?p=changelog', 'here'));
} else if ($version_compare < 0) {
echo success('Woah, seems you\'re using newer version as latest released one! MyAAC latest released version is ' . $myaac_version . ', and you\'re using version ' . MYAAC_VERSION . '.
success('Woah, seems you\'re using newer version as latest released one! MyAAC latest released version is ' . $myaac_version . ', and you\'re using version ' . MYAAC_VERSION . '.
<br/>View CHANGELOG ' . generateLink(ADMIN_URL . '?p=changelog', 'here'));
} else {
warning('You\'re using outdated version.<br/>
@ -47,4 +47,3 @@ function version_revert($version)
$release = $version;
return $major . '.' . $minor . '.' . $release;
}*/
?>

View File

@ -1,4 +1,6 @@
<?php
define('MYAAC_ADMIN', true);
require '../../common.php';
require SYSTEM . 'functions.php';
require SYSTEM . 'init.php';
@ -11,4 +13,3 @@ if(!function_exists('phpinfo'))
die('phpinfo() disabled on this web server.');
phpinfo();
?>

View File

@ -1,4 +1,6 @@
<?php
define('MYAAC_ADMIN', true);
require '../../common.php';
require SYSTEM . 'init.php';
require SYSTEM . 'functions.php';

View File

@ -23,15 +23,15 @@
* @copyright 2019 MyAAC
* @link https://my-aac.org
*/
if (version_compare(phpversion(), '5.5', '<')) die('PHP version 5.5 or higher is required.');
session_start();
if (version_compare(phpversion(), '7.2.5', '<')) die('PHP version 7.2.5 or higher is required.');
define('MYAAC', true);
define('MYAAC_VERSION', '0.8.2-dev');
define('DATABASE_VERSION', 30);
define('MYAAC_VERSION', '0.8.23');
define('DATABASE_VERSION', 33);
define('TABLE_PREFIX', 'myaac_');
define('START_TIME', microtime(true));
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX'));
define('IS_CLI', in_array(php_sapi_name(), ['cli', 'phpdb']));
// account flags
define('FLAG_ADMIN', 1);
@ -85,6 +85,11 @@ define('TFS_03', 4);
define('TFS_FIRST', TFS_02);
define('TFS_LAST', TFS_03);
if (!IS_CLI) {
session_save_path(SYSTEM . 'php_sessions');
session_start();
}
// basedir
$basedir = '';
$tmp = explode('/', $_SERVER['SCRIPT_NAME']);
@ -92,26 +97,39 @@ $size = count($tmp) - 1;
for($i = 1; $i < $size; $i++)
$basedir .= '/' . $tmp[$i];
$basedir = str_replace(array('/admin', '/install'), '', $basedir);
$basedir = str_replace(array('/admin', '/install', '/tools'), '', $basedir);
define('BASE_DIR', $basedir);
if(isset($_SERVER['HTTP_HOST'][0])) {
$baseHost = $_SERVER['HTTP_HOST'];
}
else {
if(isset($_SERVER['SERVER_NAME'][0])) {
$baseHost = $_SERVER['SERVER_NAME'];
}
else {
$baseHost = $_SERVER['SERVER_ADDR'];
}
if (file_exists(BASE . 'config.local.php') && !defined('MYAAC_INSTALL')) {
require BASE . 'config.local.php';
}
define('SERVER_URL', 'http' . (isset($_SERVER['HTTPS'][0]) && strtolower($_SERVER['HTTPS']) === 'on' ? 's' : '') . '://' . $baseHost);
define('BASE_URL', SERVER_URL . BASE_DIR . '/');
define('ADMIN_URL', SERVER_URL . BASE_DIR . '/admin/');
if(!IS_CLI) {
if (isset($_SERVER['HTTP_HOST'][0])) {
$baseHost = $_SERVER['HTTP_HOST'];
} else {
if (isset($_SERVER['SERVER_NAME'][0])) {
$baseHost = $_SERVER['SERVER_NAME'];
} else {
$baseHost = $_SERVER['SERVER_ADDR'];
}
}
//define('CURRENT_URL', BASE_URL . $_SERVER['REQUEST_URI']);
define('SERVER_URL', 'http' . (isHttps() ? 's' : '') . '://' . $baseHost);
define('BASE_URL', SERVER_URL . BASE_DIR . '/');
define('ADMIN_URL', SERVER_URL . BASE_DIR . '/admin/');
require SYSTEM . 'exception.php';
//define('CURRENT_URL', BASE_URL . $_SERVER['REQUEST_URI']);
if(@$config['env'] === 'dev') {
require SYSTEM . 'exception.php';
}
}
require SYSTEM . 'autoload.php';
function isHttps(): bool
{
return
(!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https')
|| (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|| (isset($_SERVER['SERVER_PORT']) && (int) $_SERVER['SERVER_PORT'] === 443);
}

View File

@ -77,6 +77,7 @@ $config = array(
'database_log' => false, // should database queries be logged and and saved into system/logs/database.log?
'database_socket' => '', // set if you want to connect to database through socket (example: /var/run/mysqld/mysqld.sock)
'database_persistent' => false, // use database permanent connection (like server), may speed up your site
'database_encryption' => 'sha1',
// multiworld system (only TFS 0.3)
'multiworld' => false, // use multiworld system?
@ -86,14 +87,21 @@ $config = array(
),
// images
'outfit_images_url' => 'http://outfit-images.ots.me/outfit.php', // set to animoutfit.php for animated outfit
'item_images_url' => 'http://item-images.ots.me/1092/', // set to images/items if you host your own items in images folder
'outfit_images_url' => 'https://outfit-images.ots.me/outfit.php', // set to animoutfit.php for animated outfit
'item_images_url' => 'https://item-images.ots.me/1092/', // set to images/items if you host your own items in images folder
// account
'account_management' => true, // disable if you're using other method to manage users (fe. tfs account manager)
'account_create_auto_login' => false, // auto login after creating account?
'account_create_character_create' => true, // allow directly to create character on create account page?
'account_mail_verify' => false, // force users to confirm their email addresses when registering account
'account_mail_verify' => false, // force users to confirm their email addresses when registering
'account_mail_confirmed_reward' => [ // reward users for confirming their E-Mails
// account_mail_verify needs to be enabled too
'premium_days' => 0,
'premium_points' => 0,
'coins' => 0,
'message' => 'You received %d %s for confirming your E-Mail address.' // example: You received 20 premium points for confirming your E-Mail address.
],
'account_mail_unique' => true, // email addresses cannot be duplicated? (one account = one email)
'account_premium_days' => 0, // default premium days on new account
'account_premium_points' => 0, // default premium points on new account
@ -151,12 +159,17 @@ $config = array(
4 => 'Knight Sample'
),
'use_character_sample_skills' => false,
// it must show limited number of players after using search in character page
'characters_search_limit' => 15,
// town list used when creating character
// won't be displayed if there is only one item (rookgaard for example)
'character_towns' => array(1),
// characters lenght
// This is the minimum and the maximum length that a player can create a character. It is highly recommend the maximum lenght be 21.
// characters length
// This is the minimum and the maximum length that a player can create a character. It is highly recommend the maximum length to be 21.
'character_name_min_length' => 4,
'character_name_max_length' => 21,
@ -221,7 +234,10 @@ $config = array(
'frags' => false,
'deleted' => false, // should deleted characters from same account be still listed on the list of characters? When enabled it will show that character is "[DELETED]"
),
'quests' => array(), // quests list (displayed in character view), name => storage
'quests' => array(
//'Some Quest' => 123,
//'Some Quest Two' => 456,
), // quests list (displayed in character view), name => storage
'signature_enabled' => true,
'signature_type' => 'tibian', // signature engine to use: tibian, mango, gesior
'signature_cache_time' => 5, // how long to store cached file (in minutes), default 5 minutes
@ -252,9 +268,10 @@ $config = array(
'last_kills_limit' => 50, // max. number of deaths shown on the last kills page
// status, took automatically from config file if empty
'status_ip' => '',
'status_enabled' => true, // you can disable status checking by settings this to "false"
'status_ip' => '127.0.0.1',
'status_port' => '',
'status_timeout' => 2, // how long to wait for the initial response from the server (default: 2 seconds)
'status_timeout' => 1.0, // how long to wait for the initial response from the server (default: 1 second)
// how often to connect to server and update status (default: every minute)
// if your status timeout in config.lua is bigger, that it will be used instead
@ -273,5 +290,13 @@ $config = array(
'date_timezone' => 'Europe/Berlin', // more info at http://php.net/manual/en/timezones.php
'footer_show_load_time' => true, // display load time of the page in the footer
'npc' => array()
'npc' => array(),
// character name blocked
'character_name_blocked' => array(
'prefix' => array(),
'names' => array(),
'words' => array(),
),
);

View File

View File

@ -24,6 +24,7 @@
* @link https://my-aac.org
*/
ob_start();
require_once 'common.php';
require_once SYSTEM . 'functions.php';
@ -38,7 +39,7 @@ else
$uri = str_replace(array('index.php/', '?'), '', $uri);
define('URI', $uri);
if(preg_match("/^[A-Za-z0-9-_%\'+]+\.png$/i", $uri)) {
if(preg_match("/^[A-Za-z0-9-_%'+]+\.png$/i", $uri)) {
$tmp = explode('.', $uri);
$_REQUEST['name'] = urldecode($tmp[0]);
@ -48,7 +49,7 @@ if(preg_match("/^[A-Za-z0-9-_%\'+]+\.png$/i", $uri)) {
}
if(preg_match("/^(.*)\.(gif|jpg|png|jpeg|tiff|bmp|css|js|less|map|html|php|zip|rar|gz|ttf|woff|ico)$/i", $_SERVER['REQUEST_URI'])) {
header('HTTP/1.0 404 Not Found');
http_response_code(404);
exit;
}
@ -56,11 +57,17 @@ if(file_exists(BASE . 'config.local.php')) {
require_once BASE . 'config.local.php';
}
ini_set('log_errors', 1);
if(config('env') === 'dev') {
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
}
else {
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
}
if((!isset($config['installed']) || !$config['installed']) && file_exists(BASE . 'install'))
{
@ -68,6 +75,14 @@ if((!isset($config['installed']) || !$config['installed']) && file_exists(BASE .
throw new RuntimeException('Setup detected that <b>install/</b> directory exists. Please visit <a href="' . BASE_URL . 'install">this</a> url to start MyAAC Installation.<br/>Delete <b>install/</b> directory if you already installed MyAAC.<br/>Remember to REFRESH this page when you\'re done!');
}
require_once SYSTEM . 'init.php';
require_once SYSTEM . 'template.php';
// verify myaac tables exists in database
if(!$db->hasTable('myaac_account_actions')) {
throw new RuntimeException('Seems that the table <strong>myaac_account_actions</strong> of MyAAC doesn\'t exist in the database. This is a fatal error. You can try to reinstall MyAAC by visiting <a href="' . BASE_URL . 'install">this</a> url.');
}
$found = false;
if(empty($uri) || isset($_REQUEST['template'])) {
$_REQUEST['p'] = 'news';
@ -75,7 +90,11 @@ if(empty($uri) || isset($_REQUEST['template'])) {
}
else {
$tmp = strtolower($uri);
if(!preg_match('/[^A-z0-9_\-]/', $uri) && file_exists(SYSTEM . 'pages/' . $tmp . '.php')) {
if (!preg_match('/[^A-z0-9_\-]/', $uri) && file_exists(TEMPLATES . $template_name . '/pages/' . $tmp . '.php')) {
$_REQUEST['p'] = $uri;
$found = true;
}
else if (!preg_match('/[^A-z0-9_\-]/', $uri) && file_exists(SYSTEM . 'pages/' . $tmp . '.php')) {
$_REQUEST['p'] = $uri;
$found = true;
}
@ -127,13 +146,13 @@ else {
'/^houses\/view\/?$/' => array('subtopic' => 'houses', 'page' => 'view')
);
foreach($rules as $rule => $redirect) {
foreach ($rules as $rule => $redirect) {
if (preg_match($rule, $uri)) {
$tmp = explode('/', $uri);
/* @var $redirect array */
foreach($redirect as $key => $value) {
foreach ($redirect as $key => $value) {
if(strpos($value, '$') !== false) {
if (strpos($value, '$') !== false) {
$value = str_replace('$' . $value[1], $tmp[$value[1]], $value);
}
@ -148,6 +167,12 @@ else {
}
}
// handle ?fbclid=x, etc. (show news page)
if (!$found && count($_GET) > 0 && !isset($_REQUEST['subtopic']) && !isset($_REQUEST['p']) && !in_array($_SERVER['QUERY_STRING'], getDatabasePages())) {
$_REQUEST['p'] = $_REQUEST['subtopic'] = 'news';
$found = true;
}
// define page visited, so it can be used within events system
$page = isset($_REQUEST['subtopic']) ? $_REQUEST['subtopic'] : (isset($_REQUEST['p']) ? $_REQUEST['p'] : '');
if(empty($page) || !preg_match('/^[A-z0-9\_\-]+$/', $page)) {
@ -168,44 +193,17 @@ define('PAGE', $page);
$template_place_holders = array();
require_once SYSTEM . 'init.php';
// event system
require_once SYSTEM . 'hooks.php';
$hooks = new Hooks();
$hooks->load();
require_once SYSTEM . 'template.php';
require_once SYSTEM . 'login.php';
require_once SYSTEM . 'status.php';
$twig->addGlobal('config', $config);
$twig->addGlobal('status', $status);
// verify myaac tables exists in database
if(!$db->hasTable('myaac_account_actions')) {
throw new RuntimeException('Seems that the table <strong>myaac_account_actions</strong> of MyAAC doesn\'t exist in the database. This is a fatal error. You can try to reinstall MyAAC by visiting <a href="' . BASE_URL . 'install">this</a> url.');
}
// database migrations
$tmp = '';
if(fetchDatabaseConfig('database_version', $tmp)) { // we got version
$tmp = (int)$tmp;
if($tmp < DATABASE_VERSION) { // import if older
$db->revalidateCache();
for($i = $tmp + 1; $i <= DATABASE_VERSION; $i++) {
require SYSTEM . 'migrations/' . $i . '.php';
updateDatabaseConfig('database_version', $i);
}
}
}
else { // register first version
registerDatabaseConfig('database_version', 0);
$db->revalidateCache();
for($i = 1; $i <= DATABASE_VERSION; $i++) {
require SYSTEM . 'migrations/' . $i . '.php';
updateDatabaseConfig('database_version', $i);
}
}
require SYSTEM . 'migrate.php';
$hooks->trigger(HOOK_STARTUP);
@ -224,7 +222,7 @@ if(isset($config['anonymous_usage_statistics']) && $config['anonymous_usage_stat
if(fetchDatabaseConfig('last_usage_report', $value)) {
$should_report = time() > (int)$value + $report_time;
if($cache->enabled()) {
$cache->set('last_usage_report', $value);
$cache->set('last_usage_report', $value, 60 * 60);
}
}
else {
@ -239,7 +237,7 @@ if(isset($config['anonymous_usage_statistics']) && $config['anonymous_usage_stat
updateDatabaseConfig('last_usage_report', time());
if($cache->enabled()) {
$cache->set('last_usage_report', time());
$cache->set('last_usage_report', time(), 60 * 60);
}
}
}
@ -301,6 +299,7 @@ if($config['backward_support']) {
$config['site'] = &$config;
$config['server'] = &$config['lua'];
$config['site']['shop_system'] = $config['gifts_system'];
$config['site']['gallery_page'] = true;
if(!isset($config['vdarkborder']))
$config['vdarkborder'] = '#505050';
@ -325,8 +324,10 @@ if($load_it)
if(SITE_CLOSED && admin())
$content .= '<p class="note">Site is under maintenance (closed mode). Only privileged users can see it.</p>';
if($config['backward_support'])
require SYSTEM . 'compat_pages.php';
if($config['backward_support']) {
require SYSTEM . 'compat/pages.php';
require SYSTEM . 'compat/classes.php';
}
$ignore = false;
@ -346,11 +347,13 @@ if($load_it)
)) . $content;
}
} else {
$file = SYSTEM . 'pages/' . $page . '.php';
if(!@file_exists($file))
{
$page = '404';
$file = SYSTEM . 'pages/404.php';
$file = TEMPLATES . "$template_name/pages/$page.php";
if(!@file_exists($file) || preg_match('/[^A-z0-9_\-]/', $page)) {
$file = SYSTEM . "pages/$page.php";
if(!@file_exists($file) || preg_match('/[^A-z0-9_\-]/', $page)) {
$page = '404';
$file = SYSTEM . 'pages/404.php';
}
}
}

View File

@ -38,4 +38,3 @@ if(!isset($error) || !$error) {
$error = true;
}
}
?>

View File

@ -1,3 +1,5 @@
SET @myaac_database_version = 33;
CREATE TABLE `myaac_account_actions`
(
`account_id` INT(11) NOT NULL,
@ -57,6 +59,8 @@ CREATE TABLE `myaac_config`
UNIQUE (`name`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
INSERT INTO `myaac_config` (`name`, `value`) VALUES ('database_version', @myaac_database_version);
CREATE TABLE `myaac_faq`
(
`id` INT(11) NOT NULL AUTO_INCREMENT,
@ -320,9 +324,9 @@ CREATE TABLE `myaac_spells`
CREATE TABLE `myaac_visitors`
(
`ip` VARCHAR(16) NOT NULL,
`ip` VARCHAR(45) NOT NULL,
`lastvisit` INT(11) NOT NULL DEFAULT 0,
`page` VARCHAR(100) NOT NULL,
`page` VARCHAR(2048) NOT NULL,
UNIQUE (`ip`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;

View File

@ -1,11 +1,11 @@
We have detected that you don't have access to write to the system/cache directory. Under linux you can fix it by using this two command, where first one should be enough (for apache):<br/><br/><span class="console">chown -R www-data.www-data /var/www/*</span><br/><span class="console">chmod -R 660 system/cache</span>
We have detected that you don't have access to write to the system/cache directory. Under linux you can fix it by using this two command, where first one should be enough (for apache):<br/><br/><span class="console">chown -R www-data.www-data /var/www/*</span><br/><span class="console">chmod -R 760 system/cache</span>
<style type="text/css">
.console {
font-family:Courier;
font-family: Courier,serif;
color: #CCCCCC;
background: #000000;
border: 3px double #CCCCCC;
padding: 0px;
padding: 0;
}
</style>

View File

@ -70,7 +70,7 @@ if($step == 'database') {
$key = str_replace('var_', '', $key);
if(in_array($key, array('account', 'password', 'email', 'player_name'))) {
if(in_array($key, array('account', 'account_id', 'password', 'email', 'player_name'))) {
continue;
}
@ -114,14 +114,12 @@ if($step == 'database') {
}
}
else if($step == 'admin') {
$config_failed = true;
if(file_exists(BASE . 'config.local.php') && isset($config['installed']) && $config['installed'] && isset($_SESSION['saved'])) {
$config_failed = false;
}
if($config_failed) {
if(!file_exists(BASE . 'config.local.php') || !isset($config['installed']) || !$config['installed']) {
$step = 'database';
}
else {
$_SESSION['saved'] = true;
}
}
else if($step == 'finish') {
$email = $_SESSION['var_email'];
@ -191,14 +189,14 @@ clearstatcache();
if(is_writable(CACHE) && (MYAAC_OS != 'WINDOWS' || win_is_writable(CACHE))) {
if(!file_exists(BASE . 'install/ip.txt')) {
$content = warning('AAC installation is disabled. To enable it make file <b>ip.txt</b> in install/ directory and put there your IP.<br/>
Your IP is:<br /><b>' . $_SERVER['REMOTE_ADDR'] . '</b>', true);
Your IP is:<br /><b>' . get_browser_real_ip() . '</b>', true);
}
else {
$file_content = trim(file_get_contents(BASE . 'install/ip.txt'));
$allow = false;
$listIP = preg_split('/\s+/', $file_content);
foreach($listIP as $ip) {
if($_SERVER['REMOTE_ADDR'] == $ip) {
if(get_browser_real_ip() == $ip) {
$allow = true;
}
}

View File

@ -5,4 +5,3 @@ $twig->display('install.license.html.twig', array(
'license' => file_get_contents(BASE . 'LICENSE'),
'buttons' => next_buttons()
));
?>

View File

@ -1,6 +1,10 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
// configuration
$extensions_required = [
'pdo', 'pdo_mysql', 'xml', 'zip'
];
/*
*
* @param string $name
@ -35,9 +39,11 @@ version_check('register_long_arrays', !$ini_register_globals, $ini_register_glob
$ini_safe_mode = ini_get_bool('safe_mode');
version_check('safe_mode', !$ini_safe_mode, $ini_safe_mode ? $locale['on'] : $locale['off'], true);
version_check(str_replace('$EXTENSION$', 'PDO', $locale['step_requirements_extension']) , extension_loaded('pdo'), extension_loaded('pdo') ? $locale['loaded'] : $locale['not_loaded']);
version_check(str_replace('$EXTENSION$', 'XML', $locale['step_requirements_extension']), extension_loaded('xml'), extension_loaded('xml') ? $locale['loaded'] : $locale['not_loaded']);
version_check(str_replace('$EXTENSION$', 'ZIP', $locale['step_requirements_extension']), extension_loaded('zip'), extension_loaded('zip') ? $locale['loaded'] : $locale['not_loaded']);
foreach ($extensions_required as $ext) {
$loaded = extension_loaded($ext);
version_check(str_replace('$EXTENSION$', strtoupper($ext), $locale['step_requirements_extension']) , $loaded, $loaded ? $locale['loaded'] : $locale['not_loaded']);
}
if($failed)
{

View File

@ -18,4 +18,3 @@ $twig->display('install.config.html.twig', array(
'errors' => isset($errors) ? $errors : null,
'buttons' => next_buttons()
));
?>

View File

@ -57,16 +57,35 @@ if(!$error) {
error($database_error);
}
else {
$twig->display('install.installer.html.twig', array(
'url' => 'tools/5-database.php',
'message' => $locale['loading_spinner']
));
if(!$db->hasTable('accounts')) {
$tmp = str_replace('$TABLE$', 'accounts', $locale['step_database_error_table']);
error($tmp);
$error = true;
}
if(!$db->hasTable('players')) {
$tmp = str_replace('$TABLE$', 'players', $locale['step_database_error_table']);
error($tmp);
$error = true;
}
if(!$db->hasTable('guilds')) {
$tmp = str_replace('$TABLE$', 'guilds', $locale['step_database_error_table']);
error($tmp);
$error = true;
}
if(!$error) {
$twig->display('install.installer.html.twig', array(
'url' => 'tools/5-database.php',
'message' => $locale['loading_spinner']
));
if(!Validator::email($_SESSION['var_mail_admin'])) {
error($locale['step_config_mail_admin_error']);
$error = true;
}
if(!Validator::email($_SESSION['var_mail_address'])) {
error($locale['step_config_mail_address_error']);
$error = true;
@ -82,6 +101,7 @@ if(!$error) {
}
if($saved) {
success($locale['step_database_config_saved']);
if(!$error) {
$_SESSION['saved'] = true;
}
@ -91,7 +111,7 @@ if(!$error) {
unset($_SESSION['saved']);
$locale['step_database_error_file'] = str_replace('$FILE$', '<b>' . BASE . 'config.local.php</b>', $locale['step_database_error_file']);
warning($locale['step_database_error_file'] . '<br/>
error($locale['step_database_error_file'] . '<br/>
<textarea cols="70" rows="10">' . $content . '</textarea>');
}
}
@ -102,6 +122,6 @@ if(!$error) {
<form action="<?php echo BASE_URL; ?>install/" method="post">
<input type="hidden" name="step" id="step" value="admin" />
<?php echo next_buttons(true, $error ? false : true);
<?php echo next_buttons(true, !$error);
?>
</form>
</form>

View File

@ -66,7 +66,6 @@ else {
$new_account->setPassword(encrypt($password));
$new_account->setEMail($email);
$new_account->unblock();
$new_account->save();
$new_account->setCustomField('created', time());
@ -80,10 +79,12 @@ else {
$account_used->setCustomField('web_flags', FLAG_ADMIN + FLAG_SUPER_ADMIN);
$account_used->setCustomField('country', 'us');
$account_used->setCustomField('email_verified', 1);
if($db->hasColumn('accounts', 'group_id'))
$account_used->setCustomField('group_id', $groups->getHighestId());
if($db->hasColumn('accounts', 'type'))
$account_used->setCustomField('type', 5);
$account_used->setCustomField('type', 6);
if(!$player_db->isLoaded())
$player->setAccountId($account_used->getId());

View File

@ -1,3 +1,4 @@
<?php defined('MYAAC') or die('Direct access not allowed!'); ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="<?php echo $locale['direction']; ?>" lang="<?php echo $locale['lang']; ?>" xml:lang="<?php echo $locale['lang']; ?>">
<head>
@ -45,4 +46,4 @@
<p style="text-align: center;"><?php echo base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4='); ?></p>
</div>
</body>
</html>
</html>

View File

@ -1,4 +1,6 @@
<?php
define('MYAAC_INSTALL', true);
require_once '../../common.php';
require SYSTEM . 'functions.php';
@ -9,8 +11,10 @@ $error = false;
require BASE . 'install/includes/config.php';
ini_set('max_execution_time', 300);
@ob_end_flush();
ob_implicit_flush();
ob_end_flush();
header('X-Accel-Buffering: no');
if(!$error) {
@ -21,24 +25,6 @@ if(!$error) {
}
}
if(!$db->hasTable('accounts')) {
$locale['step_database_error_table'] = str_replace('$TABLE$', 'accounts', $locale['step_database_error_table']);
error($locale['step_database_error_table']);
return;
}
if(!$db->hasTable('players')) {
$locale['step_database_error_table'] = str_replace('$TABLE$', 'players', $locale['step_database_error_table']);
error($locale['step_database_error_table']);
return;
}
if(!$db->hasTable('guilds')) {
$locale['step_database_error_table'] = str_replace('$TABLE$', 'guilds', $locale['step_database_error_table']);
error($locale['step_database_error_table']);
return;
}
if($db->hasTable(TABLE_PREFIX . 'account_actions')) {
$locale['step_database_error_table_exist'] = str_replace('$TABLE$', TABLE_PREFIX . 'account_actions', $locale['step_database_error_table_exist']);
warning($locale['step_database_error_table_exist']);
@ -48,7 +34,6 @@ else {
try {
$db->query(file_get_contents(BASE . 'install/includes/schema.sql'));
registerDatabaseConfig('database_version', DATABASE_VERSION);
$locale['step_database_success_schema'] = str_replace('$PREFIX$', TABLE_PREFIX, $locale['step_database_success_schema']);
success($locale['step_database_success_schema']);
}
@ -72,13 +57,8 @@ else {
success($locale['step_database_adding_field'] . ' accounts.key...');
}
if(!$db->hasColumn('accounts', 'blocked')) {
if(query("ALTER TABLE `accounts` ADD `blocked` TINYINT(1) NOT NULL DEFAULT FALSE COMMENT 'internal usage' AFTER `key`;"))
success($locale['step_database_adding_field'] . ' accounts.blocked...');
}
if(!$db->hasColumn('accounts', 'created')) {
if(query("ALTER TABLE `accounts` ADD `created` INT(11) NOT NULL DEFAULT 0 AFTER `" . ($db->hasColumn('accounts', 'group_id') ? 'group_id' : 'blocked') . "`;"))
if(query("ALTER TABLE `accounts` ADD `created` INT(11) NOT NULL DEFAULT 0 AFTER `" . ($db->hasColumn('accounts', 'group_id') ? 'group_id' : 'email') . "`;"))
success($locale['step_database_adding_field'] . ' accounts.created...');
}
@ -247,4 +227,4 @@ if($db->hasTable('z_forum')) {
success($locale['step_database_adding_field'] . ' z_forum.closed...');
}
}
}
}

View File

@ -1,4 +1,6 @@
<?php
define('MYAAC_INSTALL', true);
require_once '../../common.php';
require SYSTEM . 'functions.php';
@ -6,8 +8,10 @@ require BASE . 'install/includes/functions.php';
require BASE . 'install/includes/locale.php';
ini_set('max_execution_time', 300);
@ob_end_flush();
ob_implicit_flush();
ob_end_flush();
header('X-Accel-Buffering: no');
if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['saved'])) {
@ -34,10 +38,10 @@ function insert_sample_if_not_exist($p) {
$success = true;
insert_sample_if_not_exist(array('name' => 'Rook Sample', 'level' => 1, 'vocation_id' => 0, 'health' => 150, 'healthmax' => 150, 'experience' => 0, 'looktype' => 130, 'mana' => 0, 'manamax' => 0, 'soul' => 100, 'cap' => 400));
insert_sample_if_not_exist(array('name' => 'Sorcerer Sample', 'level' => 8, 'vocation_id' => 1, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 35, 'manamax' => 35, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Druid Sample', 'level' => 8, 'vocation_id' => 2, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 35, 'manamax' => 35, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Paladin Sample', 'level' => 8, 'vocation_id' => 3, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 129, 'mana' => 35, 'manamax' => 35, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Knight Sample', 'level' => 8, 'vocation_id' => 4, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 131, 'mana' => 35, 'manamax' => 35, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Sorcerer Sample', 'level' => 8, 'vocation_id' => 1, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Druid Sample', 'level' => 8, 'vocation_id' => 2, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Paladin Sample', 'level' => 8, 'vocation_id' => 3, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 129, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Knight Sample', 'level' => 8, 'vocation_id' => 4, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 131, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
if($success) {
success($locale['step_database_imported_players']);
@ -91,6 +95,7 @@ require_once SYSTEM . 'migrations/22.php';
// add myaac_pages pages
require_once SYSTEM . 'migrations/27.php';
require_once SYSTEM . 'migrations/30.php';
$locale['step_finish_desc'] = str_replace('$ADMIN_PANEL$', generateLink(str_replace('tools/', '',ADMIN_URL), $locale['step_finish_admin_panel'], true), $locale['step_finish_desc']);
$locale['step_finish_desc'] = str_replace('$HOMEPAGE$', generateLink(str_replace('tools/', '', BASE_URL), $locale['step_finish_homepage'], true), $locale['step_finish_desc']);

285
login.php Normal file
View File

@ -0,0 +1,285 @@
<?php
require_once 'common.php';
require_once 'config.php';
require_once 'config.local.php';
require_once SYSTEM . 'functions.php';
require_once SYSTEM . 'init.php';
require_once SYSTEM . 'status.php';
# error function
function sendError($message, $code = 3){
$ret = [];
$ret['errorCode'] = $code;
$ret['errorMessage'] = $message;
die(json_encode($ret));
}
# event schedule function
function parseEvent($table1, $date, $table2)
{
if ($table1) {
if ($date) {
if ($table2) {
$date = $table1->getAttribute('startdate');
return date_create("{$date}")->format('U');
} else {
$date = $table1->getAttribute('enddate');
return date_create("{$date}")->format('U');
}
} else {
foreach($table1 as $attr) {
if ($attr) {
return $attr->getAttribute($table2);
}
}
}
}
return 'error';
}
$request = json_decode(file_get_contents('php://input'));
$action = $request->type ?? '';
/** @var OTS_Base_DB $db */
/** @var array $config */
switch ($action) {
case 'cacheinfo':
$playersonline = $db->query("select count(*) from `players_online`")->fetchAll();
die(json_encode([
'playersonline' => (intval($playersonline[0][0])),
'twitchstreams' => 0,
'twitchviewer' => 0,
'gamingyoutubestreams' => 0,
'gamingyoutubeviewer' => 0
]));
case 'eventschedule':
$eventlist = [];
$file_path = config('server_path') . 'data/XML/events.xml';
if (!file_exists($file_path)) {
die(json_encode([]));
}
$xml = new DOMDocument;
$xml->load($file_path);
$tmplist = [];
$tableevent = $xml->getElementsByTagName('event');
foreach ($tableevent as $event) {
if ($event) { $tmplist = [
'colorlight' => parseEvent($event->getElementsByTagName('colors'), false, 'colorlight'),
'colordark' => parseEvent($event->getElementsByTagName('colors'), false, 'colordark'),
'description' => parseEvent($event->getElementsByTagName('description'), false, 'description'),
'displaypriority' => intval(parseEvent($event->getElementsByTagName('details'), false, 'displaypriority')),
'enddate' => intval(parseEvent($event, true, false)),
'isseasonal' => getBoolean(intval(parseEvent($event->getElementsByTagName('details'), false, 'isseasonal'))),
'name' => $event->getAttribute('name'),
'startdate' => intval(parseEvent($event, true, true)),
'specialevent' => intval(parseEvent($event->getElementsByTagName('details'), false, 'specialevent'))
];
$eventlist[] = $tmplist; } }
die(json_encode(['eventlist' => $eventlist, 'lastupdatetimestamp' => time()]));
case 'boostedcreature':
$boostDB = $db->query("select * from " . $db->tableName('boosted_creature'))->fetchAll();
foreach ($boostDB as $Tableboost) {
die(json_encode([
'boostedcreature' => true,
'raceid' => intval($Tableboost['raceid'])
]));
}
break;
case 'login':
$port = $config['lua']['gameProtocolPort'];
// default world info
$world = [
'id' => 0,
'name' => $config['lua']['serverName'],
'externaladdress' => $config['lua']['ip'],
'externalport' => $port,
'externaladdressprotected' => $config['lua']['ip'],
'externalportprotected' => $port,
'externaladdressunprotected' => $config['lua']['ip'],
'externalportunprotected' => $port,
'previewstate' => 0,
'location' => 'BRA', // BRA, EUR, USA
'anticheatprotection' => false,
'pvptype' => array_search($config['lua']['worldType'], ['pvp', 'no-pvp', 'pvp-enforced']),
'istournamentworld' => false,
'restrictedstore' => false,
'currenttournamentphase' => 2
];
$characters = [];
$account = new OTS_Account();
$inputEmail = $request->email ?? false;
$inputAccountName = $request->accountname ?? false;
$inputToken = $request->token ?? false;
if ($inputEmail != false) { // login by email
$account->findByEmail($request->email);
}
else if($inputAccountName != false) { // login by account name
$account->find($inputAccountName);
}
$config_salt_enabled = fieldExist('salt', 'accounts');
$current_password = encrypt(($config_salt_enabled ? $account->getCustomField('salt') : '') . $request->password);
if (!$account->isLoaded() || $account->getPassword() != $current_password) {
sendError(($inputEmail != false ? 'Email' : 'Account name') . ' or password is not correct.');
}
//log_append('test.log', var_export($account->getCustomField('secret'), true));
$accountHasSecret = false;
if (fieldExist('secret', 'accounts')) {
$accountSecret = $account->getCustomField('secret');
if ($accountSecret != null && $accountSecret != '') {
$accountHasSecret = true;
if ($inputToken === false) {
sendError('Submit a valid two-factor authentication token.', 6);
} else {
require_once LIBS . 'rfc6238.php';
if (TokenAuth6238::verify($accountSecret, $inputToken) !== true) {
sendError('Two-factor authentication failed, token is wrong.', 6);
}
}
}
}
// common columns
$columns = 'id, name, level, sex, vocation, looktype, lookhead, lookbody, looklegs, lookfeet, lookaddons';
if (fieldExist('isreward', 'accounts')) {
$columns .= ', isreward';
}
if (fieldExist('istutorial', 'accounts')) {
$columns .= ', istutorial';
}
$players = $db->query("select {$columns} from players where account_id = " . $account->getId() . " AND deletion = 0");
if($players && $players->rowCount() > 0) {
$players = $players->fetchAll();
$highestLevelId = 0;
$highestLevel = 0;
foreach ($players as $player) {
if ($player['level'] >= $highestLevel) {
$highestLevel = $player['level'];
$highestLevelId = $player['id'];
}
}
foreach ($players as $player) {
$characters[] = create_char($player, $highestLevelId);
}
}
if (fieldExist('premdays', 'accounts') && fieldExist('lastday', 'accounts')) {
$save = false;
$timeNow = time();
$query = $db->query("select `premdays`, `lastday` from `accounts` where `id` = " . $account->getId());
if ($query->rowCount() > 0) {
$query = $query->fetch();
$premDays = (int)$query['premdays'];
$lastDay = (int)$query['lastday'];
$lastLogin = $lastDay;
} else {
sendError("Error while fetching your account data. Please contact admin.");
}
if ($premDays != 0 && $premDays != PHP_INT_MAX) {
if ($lastDay == 0) {
$lastDay = $timeNow;
$save = true;
} else {
$days = (int)(($timeNow - $lastDay) / 86400);
if ($days > 0) {
if ($days >= $premDays) {
$premDays = 0;
$lastDay = 0;
} else {
$premDays -= $days;
$reminder = ($timeNow - $lastDay) % 86400;
$lastDay = $timeNow - $reminder;
}
$save = true;
}
}
} else if ($lastDay != 0) {
$lastDay = 0;
$save = true;
}
if ($save) {
$db->query("update `accounts` set `premdays` = " . $premDays . ", `lastday` = " . $lastDay . " where `id` = " . $account->getId());
}
}
$worlds = [$world];
$playdata = compact('worlds', 'characters');
$sessionKey = ($inputEmail !== false) ? $inputEmail : $inputAccountName; // email or account name
$sessionKey .= "\n" . $request->password; // password
if (!fieldExist('istutorial', 'players')) {
$sessionKey .= "\n";
}
$sessionKey .= ($accountHasSecret && strlen($accountSecret) > 5) ? $inputToken : '';
// this is workaround to distinguish between TFS 1.x and otservbr
// TFS 1.x requires the number in session key
// otservbr requires just login and password
// so we check for istutorial field which is present in otservbr, and not in TFS
if (!fieldExist('istutorial', 'players')) {
$sessionKey .= "\n".floor(time() / 30);
}
//log_append('slaw.log', $sessionKey);
$session = [
'sessionkey' => $sessionKey,
'lastlogintime' => 0,
'ispremium' => $config['lua']['freePremium'] || $account->isPremium(),
'premiumuntil' => ($account->getPremDays()) > 0 ? (time() + ($account->getPremDays() * 86400)) : 0,
'status' => 'active', // active, frozen or suspended
'returnernotification' => false,
'showrewardnews' => true,
'isreturner' => true,
'fpstracking' => false,
'optiontracking' => false,
'tournamentticketpurchasestate' => 0,
'emailcoderequest' => false
];
die(json_encode(compact('session', 'playdata')));
default:
sendError("Unrecognized event {$action}.");
break;
}
function create_char($player, $highestLevelId) {
global $config;
return [
'worldid' => 0,
'name' => $player['name'],
'ismale' => intval($player['sex']) === 1,
'tutorial' => isset($player['istutorial']) && $player['istutorial'],
'level' => intval($player['level']),
'vocation' => $config['vocations'][$player['vocation']],
'outfitid' => intval($player['looktype']),
'headcolor' => intval($player['lookhead']),
'torsocolor' => intval($player['lookbody']),
'legscolor' => intval($player['looklegs']),
'detailcolor' => intval($player['lookfeet']),
'addonsflags' => intval($player['lookaddons']),
'ishidden' => isset($player['deletion']) && (int)$player['deletion'] === 1,
'istournamentparticipant' => false,
'ismaincharacter' => $highestLevelId == $player['id'],
'dailyrewardstate' => isset($player['isreward']) ? intval($player['isreward']) : 0,
'remainingdailytournamentplaytime' => 0
];
}

View File

@ -1,25 +1,36 @@
server {
listen 80;
root /home/otserv/www/public;
index index.php;
server_name your-domain.com;
listen 80;
root /home/otserv/www/public;
index index.php;
server_name your-domain.com;
location / {
try_files $uri $uri/ /index.php;
}
# increase max file upload
client_max_body_size 10M;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_read_timeout 240;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
}
# this is very important, be sure its in your nginx conf - it prevents access to logs etc.
location ~ /system {
deny all;
}
location ~ /\.ht {
deny all;
}
# block .htaccess, CHANGELOG.md, composer.json etc.
# this is to prevent finding software versions
location ~\.(ht|md|json|dist)$ {
deny all;
}
location /system {
deny all;
return 404;
}
# block git files and folders
location ~ /\.git {
deny all;
}
location / {
try_files $uri $uri/ /index.php?$query_string;;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_read_timeout 240;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
# for ubuntu 22.04+ it will be php8.1-fpm.sock
}
}

View File

@ -1,11 +1,3 @@
<IfModule mod_autoindex.c>
Options -Indexes
</IfModule>
<IfVersion < 2.4>
order allow,deny
deny from all
</IfVersion>
<IfVersion >= 2.4>
Require all denied
</IfVersion>

View File

@ -0,0 +1,17 @@
{
"name": "EMail Confirmed Reward",
"description": "Reward users for confirming their E-Mail.",
"version": "1.0",
"author": "MyAAC Authors",
"contact": "www.my-aac.org",
"hooks": {
"mail-confirmed-reward": {
"type": "EMAIL_CONFIRMED",
"file": "plugins/email-confirmed-reward/reward.php"
}
},
"uninstall": [
"plugins/email-confirmed-reward.json",
"plugins/email-confirmed-reward"
]
}

View File

@ -0,0 +1,33 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
$reward = config('account_mail_confirmed_reward');
$hasCoinsColumn = $db->hasColumn('accounts', 'coins');
if ($reward['coins'] > 0 && !$hasCoinsColumn) {
log_append('email_confirm_error.log', 'accounts.coins column does not exist.');
}
if (!isset($account) || !$account->isLoaded()) {
//log_append('email_confirm_error.log', 'Account not loaded.');
return;
}
if ($reward['premium_points'] > 0) {
$account->setCustomField('premium_points', (int)$account->getCustomField('premium_points') + $reward['premium_points']);
success(sprintf($reward['message'], $reward['premium_points'], 'premium points'));
}
if ($reward['coins'] > 0 && $hasCoinsColumn) {
$account->setCustomField('coins', (int)$account->getCustomField('coins') + $reward['coins']);
success(sprintf($reward['message'], $reward['coins'], 'coins'));
}
if ($reward['premium_days'] > 0) {
$account->setPremDays($account->getPremDays() + $reward['premium_days']);
$account->save();
success(sprintf($reward['message'], $reward['premium_days'], 'premium days'));
}

View File

@ -13,16 +13,18 @@ fi
if [ $1 = "prepare" ]; then
# define release version
version=`cat VERSION`
version=`php system/get_version_for_release.php`
echo "Preparing to release version $version of the MyAAC Project!"
# get myaac from git archive
git archive --format zip --output tmp/myaac.zip master
# make required directories
mkdir -p releases
mkdir -p tmp && cd tmp
mkdir -p tmp
# get myaac from git archive
git archive --format zip --output tmp/myaac.zip 0.8
cd tmp/ || exit
dir="myaac-$version"
if [ -d "$dir" ] ; then
@ -39,9 +41,9 @@ fi
if [ $1 = "pack" ]; then
# define release version
version=`cat VERSION`
version=`php system/get_version_for_release.php`
cd tmp
cd tmp || exit
# tar.gz
echo "Creating .tar.gz package.."
@ -60,4 +62,4 @@ if [ $1 = "pack" ]; then
echo "Done. Released files can be found in 'releases' directory."
exit
fi
fi

View File

@ -9,6 +9,11 @@ $loader->register();
// register the base directories for the namespace prefix
$loader->addNamespace('Composer\Semver', LIBS . 'semver');
$loader->addNamespace('Twig', LIBS . 'Twig');
$loader->addNamespace('Symfony\Polyfill\Mbstring', LIBS . 'polyfill-mbstring');
// load polyfill-mbstring bootstrap
require LIBS . 'polyfill-mbstring/bootstrap.php';
/**
* An example of a general-purpose implementation that includes the optional
* functionality of allowing multiple base directories for a single namespace
@ -203,4 +208,4 @@ class Psr4AutoloaderClass
}
return false;
}
}
}

View File

@ -9,7 +9,6 @@ require_once __DIR__ . '/../../common.php';
require_once SYSTEM . 'functions.php';
require_once SYSTEM . 'init.php';
require_once SYSTEM . 'hooks.php';
require_once LIBS . 'plugins.php';
if($argc !== 2) {
echo 'This command expects one parameter: zip file name (plugin)' . PHP_EOL;

View File

@ -9,7 +9,7 @@
*/
defined('MYAAC') or die('Direct access not allowed!');
$config['clients'] = array(
$config['clients'] = [
710,
740,
750,
@ -54,7 +54,9 @@ $config['clients'] = array(
1000,
1010,
1020,
1021,
1030,
1031,
1034,
1041,
@ -62,6 +64,7 @@ $config['clients'] = array(
1053,
1054,
1058,
1070,
1075,
1077,
1079,
@ -73,6 +76,37 @@ $config['clients'] = array(
1096,
1097,
1098,
1100,
);
?>
1102,
1140,
1150,
1180,
1200,
1202,
1215,
1220,
1230,
1240,
1251,
1260,
1270,
1280,
1285,
1286,
1290,
1291,
1300,
1310,
1311,
1312,
1316,
1320,
1321,
1322,
1330,
1332,
1340,
];

38
system/compat/classes.php Normal file
View File

@ -0,0 +1,38 @@
<?php
/**
* Compat classes (backward support for Gesior AAC)
*
* @package MyAAC
* @author Slawkens <slawkens@gmail.com>
* @copyright 2022 MyAAC
* @link https://my-aac.org
*/
defined('MYAAC') or die('Direct access not allowed!');
class Account extends OTS_Account {
public function loadById($id) {
$this->load($id);
}
public function loadByName($name) {
$this->find($name);
}
}
class Player extends OTS_Player {
public function loadById($id) {
$this->load($id);
}
public function loadByName($name) {
$this->find($name);
}
}
class Guild extends OTS_Guild {
public function loadById($id) {
$this->load($id);
}
public function loadByName($name) {
$this->find($name);
}
}
class GuildRank extends OTS_GuildRank {}
class House extends OTS_House {}

View File

@ -10,6 +10,14 @@
defined('MYAAC') or die('Direct access not allowed!');
switch($page)
{
case 'adminpanel':
header('Location: ' . ADMIN_URL);
die;
case 'archive':
$page = 'newsarchive';
break;
case 'whoisonline':
$page = 'online';
break;
@ -37,4 +45,3 @@ switch($page)
default:
break;
}
?>

View File

@ -51,4 +51,3 @@ else
updateDatabaseConfig('views_counter', $views_counter); // update counter
}
}
?>

View File

@ -116,5 +116,4 @@ defined('MYAAC') or die('Direct access not allowed!');
'<li>MySQL is not configured propertly in <i>config.lua</i>.</li>' .
'<li>MySQL server is not running.</li>' .
'</ul>' . $error->getMessage());
}

View File

@ -23,6 +23,8 @@ function exception_handler($exception) {
$backtrace_formatted = nl2br($exception->getTraceAsString());
$message = $message . "<br/><br/>File: {$exception->getFile()}<br/>Line: {$exception->getLine()}";
// display basic error message without template
// template is missing, why? probably someone deleted templates dir, or it wasn't downloaded right
$template_file = SYSTEM . 'templates/exception.html.twig';
@ -35,11 +37,13 @@ function exception_handler($exception) {
}
// display beautiful error message
// the file is .twig.html, but its not really parsed by Twig
// the file is .twig.html, but it's not really parsed by Twig
// we just replace some values manually
// cause in case Twig throws exception, we can show it too
$content = file_get_contents($template_file);
$content = str_replace(array('{{ BASE_URL }}', '{{ message }}', '{{ backtrace }}', '{{ powered_by }}'), array(BASE_URL, $message, $backtrace_formatted, base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4=')), $content);
$content = str_replace(array('{{ BASE_URL }}', '{{ exceptionClass }}', '{{ message }}', '{{ backtrace }}', '{{ powered_by }}'), array(BASE_URL, get_class($exception), $message, $backtrace_formatted, base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4=')), $content);
@ob_clean();
echo $content;
}

View File

@ -7,18 +7,26 @@
* @copyright 2019 MyAAC
* @link https://my-aac.org
*/
defined('MYAAC') or die('Direct access not allowed!');
use Twig\Loader\ArrayLoader as Twig_ArrayLoader;
defined('MYAAC') or die('Direct access not allowed!');
function message($message, $type, $return)
{
if($return)
return '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>';
if(IS_CLI) {
if($return) {
return $message;
}
echo '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>';
return true;
echo $message;
return true;
}
if($return)
return '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>';
echo '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>';
return true;
}
function success($message, $return = false) {
return message($message, 'success', $return);
@ -442,7 +450,7 @@ function tickers()
*/
function template_place_holder($type)
{
global $template_place_holders;
global $twig, $template_place_holders;
$ret = '';
if(array_key_exists($type, $template_place_holders) && is_array($template_place_holders[$type]))
@ -451,6 +459,9 @@ function template_place_holder($type)
if($type === 'head_start') {
$ret .= template_header();
}
elseif ($type === 'body_start') {
$ret .= $twig->render('browsehappy.html.twig');
}
elseif($type === 'body_end') {
$ret .= template_ga_code();
}
@ -745,10 +756,10 @@ function get_browser_languages()
{
$ret = array();
$acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
if(!isset($acceptLang[0]))
if(empty($_SERVER['HTTP_ACCEPT_LANGUAGE']))
return $ret;
$acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$languages = strtolower($acceptLang);
// $languages = 'pl,en-us;q=0.7,en;q=0.3 ';
// need to remove spaces from strings to avoid error
@ -786,7 +797,7 @@ function get_plugins()
$ret = array();
$path = PLUGINS;
foreach(scandir($path, 0) as $file) {
foreach(scandir($path, SCANDIR_SORT_ASCENDING) as $file) {
$file_ext = pathinfo($file, PATHINFO_EXTENSION);
$file_name = pathinfo($file, PATHINFO_FILENAME);
if ($file === '.' || $file === '..' || $file === 'disabled' || $file === 'example.json' || $file_ext !== 'json' || is_dir($path . $file))
@ -912,8 +923,8 @@ function load_config_lua($filename)
$config_file = $filename;
if(!@file_exists($config_file))
{
log_append('error.log', '[load_config_file] Fatal error: Cannot load config.lua (' . $filename . '). Error: ' . print_r(error_get_last(), true));
throw new RuntimeException('ERROR: Cannot find ' . $filename . ' file. More info in system/logs/error.log');
log_append('error.log', '[load_config_file] Fatal error: Cannot load config.lua (' . $filename . ').');
throw new RuntimeException('ERROR: Cannot find ' . $filename . ' file.');
}
$result = array();
@ -922,6 +933,12 @@ function load_config_lua($filename)
if(count($lines) > 0) {
foreach($lines as $ln => $line)
{
$line = trim($line);
if(@$line[0] === '{' || @$line[0] === '}') {
// arrays are not supported yet
// just ignore the error
continue;
}
$tmp_exp = explode('=', $line, 2);
if(strpos($line, 'dofile') !== false)
{
@ -948,18 +965,26 @@ function load_config_lua($filename)
$result[$key] = (string) substr(substr($value, 1), 0, -1);
elseif(in_array($value, array('true', 'false')))
$result[$key] = ($value === 'true') ? true : false;
elseif(@$value[0] === '{' && @$value[strlen($value) - 1] === '}') {
elseif(@$value[0] === '{') {
// arrays are not supported yet
// just ignore the error
continue;
}
else
{
foreach($result as $tmp_key => $tmp_value) // load values definied by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull
foreach($result as $tmp_key => $tmp_value) { // load values defined by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull
$value = str_replace($tmp_key, $tmp_value, $value);
$ret = @eval("return $value;");
if((string) $ret == '') // = parser error
{
throw new RuntimeException('ERROR: Loading config.lua file. Line <b>' . ($ln + 1) . '</b> of LUA config file is not valid [key: <b>' . $key . '</b>]');
}
try {
$ret = eval("return $value;");
}
catch (Throwable $e) {
throw new RuntimeException('ERROR: Loading config.lua file. Line: ' . ($ln + 1) . ' - Unable to parse value "' . $value . '" - ' . $e->getMessage());
}
if((string) $ret == '' && trim($value) !== '""') {
throw new RuntimeException('ERROR: Loading config.lua file. Line ' . ($ln + 1) . ' is not valid [key: ' . $key . ']');
}
$result[$key] = $ret;
}
@ -982,6 +1007,10 @@ function str_replace_first($search, $replace, $subject) {
}
function get_browser_real_ip() {
if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
}
if(isset($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['REMOTE_ADDR']))
return $_SERVER['REMOTE_ADDR'];
else if(isset($_SERVER['HTTP_CLIENT_IP']) && !empty($_SERVER['HTTP_CLIENT_IP']))
@ -1019,7 +1048,7 @@ function getTopPlayers($limit = 5) {
$deleted = 'deletion';
$is_tfs10 = $db->hasTable('players_online');
$players = $db->query('SELECT `id`, `name`, `level`, `experience`, `looktype`' . ($db->hasColumn('players', 'lookaddons') ? ', `lookaddons`' : '') . ', `lookhead`, `lookbody`, `looklegs`, `lookfeet`' . ($is_tfs10 ? '' : ', `online`') . ' FROM `players` WHERE `group_id` < ' . config('highscores_groups_hidden') . ' AND `id` NOT IN (' . implode(', ', config('highscores_ids_hidden')) . ') AND `' . $deleted . '` = 0 AND `account_id` != 1 ORDER BY `experience` DESC LIMIT ' . (int)$limit)->fetchAll();
$players = $db->query('SELECT `id`, `name`, `level`, `vocation`, `experience`, `looktype`' . ($db->hasColumn('players', 'lookaddons') ? ', `lookaddons`' : '') . ', `lookhead`, `lookbody`, `looklegs`, `lookfeet`' . ($is_tfs10 ? '' : ', `online`') . ' FROM `players` WHERE `group_id` < ' . config('highscores_groups_hidden') . ' AND `id` NOT IN (' . implode(', ', config('highscores_ids_hidden')) . ') AND `' . $deleted . '` = 0 AND `account_id` != 1 ORDER BY `experience` DESC LIMIT ' . (int)$limit)->fetchAll();
if($is_tfs10) {
foreach($players as &$player) {
@ -1124,9 +1153,30 @@ function clearCache()
if ($cache->fetch('failed_logins', $tmp))
$cache->delete('failed_logins');
global $template_name;
if ($cache->fetch('template_ini' . $template_name, $tmp))
$cache->delete('template_ini' . $template_name);
foreach (get_templates() as $template) {
if ($cache->fetch('template_ini_' . $template, $tmp)) {
$cache->delete('template_ini_' . $template);
}
}
if ($cache->fetch('template_menus', $tmp)) {
$cache->delete('template_menus');
}
if ($cache->fetch('database_tables', $tmp)) {
$cache->delete('database_tables');
}
if ($cache->fetch('database_columns', $tmp)) {
$cache->delete('database_columns');
}
if ($cache->fetch('database_checksum', $tmp)) {
$cache->delete('database_checksum');
}
if ($cache->fetch('hooks', $tmp)) {
$cache->delete('hooks');
}
if ($cache->fetch('last_kills', $tmp)) {
$cache->delete('last_kills');
}
}
deleteDirectory(CACHE . 'signatures', ['index.html'], true);
@ -1221,6 +1271,73 @@ function getCustomPage($page, &$success)
return $content;
}
function escapeHtml($html) {
return htmlspecialchars($html);
}
function getGuildNameById($id)
{
global $db;
$guild = $db->query('SELECT `name` FROM `guilds` WHERE `id` = ' . (int)$id);
if ($guild->rowCount() > 0) {
return $guild->fetchColumn();
}
return false;
}
function getGuildLogoById($id)
{
global $db;
$logo = 'default.gif';
$query = $db->query('SELECT `logo_name` FROM `guilds` WHERE `id` = ' . (int)$id);
if ($query->rowCount() == 1) {
$query = $query->fetch(PDO::FETCH_ASSOC);
$guildLogo = $query['logo_name'];
if (!empty($guildLogo) && file_exists('images/guilds/' . $guildLogo)) {
$logo = $guildLogo;
}
}
return BASE_URL . 'images/guilds/' . $logo;
}
function displayErrorBoxWithBackButton($errors, $action = null) {
global $twig;
$twig->display('error_box.html.twig', ['errors' => $errors]);
$twig->display('account.back_button.html.twig', [
'action' => $action ?: getLink('')
]);
}
function getDatabasePages($withHidden = false): array
{
global $db, $logged_access;
if (!isset($logged_access)) {
$logged_access = 1;
}
$pages = $db->query('SELECT `name` FROM ' . TABLE_PREFIX . 'pages WHERE ' . ($withHidden ? '' : '`hidden` != 1 AND ') . '`access` <= ' . $db->quote($logged_access));
$ret = [];
if ($pages->rowCount() < 1) {
return $ret;
}
foreach($pages->fetchAll() as $page) {
$ret[] = $page['name'];
}
return $ret;
}
// validator functions
require_once LIBS . 'validator.php';
require_once SYSTEM . 'compat.php';
require_once SYSTEM . 'compat/base.php';

View File

@ -0,0 +1,6 @@
<?php
require __DIR__ . '/../common.php';
if (IS_CLI) {
echo MYAAC_VERSION;
}

View File

@ -9,44 +9,60 @@
*/
defined('MYAAC') or die('Direct access not allowed!');
define('HOOK_STARTUP', 1);
define('HOOK_BEFORE_PAGE', 2);
define('HOOK_AFTER_PAGE', 3);
define('HOOK_FINISH', 4);
define('HOOK_TIBIACOM_ARTICLE', 5);
define('HOOK_TIBIACOM_BORDER_3', 6);
define('HOOK_CHARACTERS_BEFORE_INFORMATIONS', 7);
define('HOOK_CHARACTERS_AFTER_INFORMATIONS', 8);
define('HOOK_CHARACTERS_BEFORE_SIGNATURE', 9);
define('HOOK_CHARACTERS_AFTER_SIGNATURE', 10);
define('HOOK_CHARACTERS_AFTER_ACCOUNT', 11);
define('HOOK_CHARACTERS_AFTER_CHARACTERS', 12);
define('HOOK_LOGIN', 13);
define('HOOK_LOGIN_ATTEMPT', 14);
define('HOOK_LOGOUT', 15);
define('HOOK_ACCOUNT_CREATE_BEFORE_FORM', 16);
define('HOOK_ACCOUNT_CREATE_BEFORE_BOXES', 17);
define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_1', 18);
define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_2', 19);
define('HOOK_ACCOUNT_CREATE_AFTER_BOXES', 20);
define('HOOK_ACCOUNT_CREATE_BEFORE_ACCOUNT', 21);
define('HOOK_ACCOUNT_CREATE_AFTER_ACCOUNT', 22);
define('HOOK_ACCOUNT_CREATE_AFTER_EMAIL', 23);
define('HOOK_ACCOUNT_CREATE_AFTER_COUNTRY', 24);
define('HOOK_ACCOUNT_CREATE_AFTER_PASSWORDS', 25);
define('HOOK_ACCOUNT_CREATE_AFTER_RECAPTCHA', 26);
define('HOOK_ACCOUNT_CREATE_BEFORE_CHARACTER_NAME', 27);
define('HOOK_ACCOUNT_CREATE_AFTER_CHARACTER_NAME', 28);
define('HOOK_ACCOUNT_CREATE_AFTER_SEX', 29);
define('HOOK_ACCOUNT_CREATE_AFTER_VOCATION', 30);
define('HOOK_ACCOUNT_CREATE_AFTER_TOWNS', 31);
define('HOOK_ACCOUNT_CREATE_BEFORE_SUBMIT_BUTTON', 32);
define('HOOK_ACCOUNT_CREATE_AFTER_FORM', 33);
define('HOOK_ACCOUNT_CREATE_AFTER_SUBMIT', 34);
$i = 0;
define('HOOK_STARTUP', ++$i);
define('HOOK_BEFORE_PAGE', ++$i);
define('HOOK_AFTER_PAGE', ++$i);
define('HOOK_FINISH', ++$i);
define('HOOK_TIBIACOM_ARTICLE', ++$i);
define('HOOK_TIBIACOM_BORDER_3', ++$i);
define('HOOK_CHARACTERS_BEFORE_INFORMATIONS', ++$i);
define('HOOK_CHARACTERS_AFTER_INFORMATIONS', ++$i);
define('HOOK_CHARACTERS_BEFORE_SKILLS', ++$i);
define('HOOK_CHARACTERS_AFTER_SKILLS', ++$i);
define('HOOK_CHARACTERS_AFTER_QUESTS', ++$i);
define('HOOK_CHARACTERS_AFTER_EQUIPMENT', ++$i);
define('HOOK_CHARACTERS_BEFORE_DEATHS', ++$i);
define('HOOK_CHARACTERS_BEFORE_SIGNATURE', ++$i);
define('HOOK_CHARACTERS_AFTER_SIGNATURE', ++$i);
define('HOOK_CHARACTERS_AFTER_ACCOUNT', ++$i);
define('HOOK_CHARACTERS_AFTER_CHARACTERS', ++$i);
define('HOOK_LOGIN', ++$i);
define('HOOK_LOGIN_ATTEMPT', ++$i);
define('HOOK_LOGOUT', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_FORM', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_BOXES', ++$i);
define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_1', ++$i);
define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_2', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_BOXES', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_ACCOUNT', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_ACCOUNT', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_EMAIL', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_COUNTRY', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_PASSWORDS', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_RECAPTCHA', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_CHARACTER_NAME', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_CHARACTER_NAME', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_SEX', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_VOCATION', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_TOWNS', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_SUBMIT_BUTTON', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_FORM', ++$i);
define('HOOK_ACCOUNT_CREATE_POST', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_SUBMIT', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_SAVED', ++$i);
define('HOOK_ACCOUNT_MANAGE_BEFORE_GENERAL_INFORMATION', ++$i);
define('HOOK_ACCOUNT_MANAGE_BEFORE_PUBLIC_INFORMATION', ++$i);
define('HOOK_ACCOUNT_MANAGE_BEFORE_ACCOUNT_LOGS', ++$i);
define('HOOK_ACCOUNT_MANAGE_BEFORE_CHARACTERS', ++$i);
define('HOOK_EMAIL_CONFIRMED', ++$i);
define('HOOK_GUILDS_AFTER_INVITED_CHARACTERS', ++$i);
define('HOOK_FIRST', HOOK_STARTUP);
define('HOOK_LAST', HOOK_ACCOUNT_CREATE_AFTER_SUBMIT);
define('HOOK_LAST', HOOK_EMAIL_CONFIRMED);
require_once LIBS . 'plugins.php';
require_once LIBS . 'src/Plugins.php';
class Hook
{
private $_name, $_type, $_file;
@ -67,9 +83,7 @@ class Hook
}*/
global $db, $config, $template_path, $ots, $content, $twig;
if(file_exists(BASE . $this->_file)) {
$ret = require BASE . $this->_file;
}
$ret = include BASE . $this->_file;
return !isset($ret) || $ret == 1 || $ret;
}

View File

@ -28,12 +28,10 @@ if($config['gzip_output'] && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($
ob_start('ob_gzhandler');
// cache
require_once SYSTEM . 'libs/cache.php';
require_once LIBS . 'cache.php';
require_once LIBS . 'src/Cache.php';
$cache = Cache::getInstance();
// twig
require_once SYSTEM . 'twig.php';
// trim values we receive
if(isset($_POST))
{
@ -76,8 +74,8 @@ if($config_lua_reload) {
// cache config
if($cache->enabled()) {
$cache->set('config_lua', serialize($config['lua']), 120);
$cache->set('server_path', $config['server_path']);
$cache->set('config_lua', serialize($config['lua']), 2 * 60);
$cache->set('server_path', $config['server_path'], 10 * 60);
}
}
unset($tmp);
@ -114,16 +112,21 @@ if(!isset($foundValue)) {
$config['data_path'] = $foundValue;
unset($foundValue);
// new config values for compability
// new config values for compatibility
if(!isset($config['highscores_ids_hidden']) || count($config['highscores_ids_hidden']) == 0) {
$config['highscores_ids_hidden'] = array(0);
}
$config['account_mail_verify'] = config('account_mail_verify') && config('mail_enabled');
// POT
require_once SYSTEM . 'libs/pot/OTS.php';
$ots = POT::getInstance();
require_once SYSTEM . 'database.php';
// twig
require_once SYSTEM . 'twig.php';
define('USE_ACCOUNT_NAME', $db->hasColumn('accounts', 'name'));
// load vocation names
$tmp = '';
@ -140,10 +143,8 @@ else {
if(!@file_exists($file))
$file = $config['data_path'] . 'vocations.xml';
$vocations->load($file);
if(!$vocations)
throw new RuntimeException('ERROR: Cannot load <i>vocations.xml</i> file.');
if(!$vocations->load($file))
throw new RuntimeException('ERROR: Cannot load <i>vocations.xml</i> - the file is malformed. Check the file with xml syntax validator.');
$config['vocations'] = array();
foreach($vocations->getElementsByTagName('vocation') as $vocation) {
@ -180,7 +181,8 @@ else {
// load towns from database (TFS 1.3) //
////////////////////////////////////////
$towns = array();
$tmp = '';
$towns = [];
if($cache->enabled() && $cache->fetch('towns', $tmp)) {
$towns = unserialize($tmp);
}
@ -193,20 +195,14 @@ else {
}
unset($query);
if($cache->enabled()) {
$cache->set('towns', serialize($towns), 600);
}
}
else if($cache->enabled()) {
$cache->set('towns', serialize(array()), 600);
else {
$towns = config('towns');
}
}
$configTowns = config('towns');
if($configTowns !== null && (!isset($configTowns[1]) || $configTowns[1] !== 'Sample town')) {
$towns = array_replace(
$towns, $configTowns
);
if($cache->enabled()) {
$cache->set('towns', serialize($towns), 600);
}
}
config(['towns', $towns]);

View File

@ -58,4 +58,3 @@ function outputItem($id = 100, $count = 1)
$file_name = Items_Images::$outputDir . $file_name . '.gif';
readfile($file_name);
}
?>

View File

@ -11,6 +11,57 @@
class CreateCharacter
{
/**
* @param $name
* @param $errors
* @return bool
*/
public function checkName($name, &$errors)
{
$minLength = config('character_name_min_length');
$maxLength = config('character_name_max_length');
if(empty($name)) {
$errors['name'] = 'Please enter a name for your character!';
return false;
}
if(strlen($name) > $maxLength) {
$errors['name'] = 'Name is too long. Max. length <b>' . $maxLength . '</b> letters.';
return false;
}
if(strlen($name) < $minLength) {
$errors['name'] = 'Name is too short. Min. length <b>' . $minLength . '</b> letters.';
return false;
}
$name_length = strlen($name);
if(strspn($name, "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM- '") != $name_length) {
$errors['name'] = 'This name contains invalid letters, words or format. Please use only a-Z, - , \' and space.';
return false;
}
if(!preg_match("/[A-z ']/", $name)) {
$errors['name'] = 'Your name contains illegal characters.';
return false;
}
if(!admin() && !Validator::newCharacterName($name)) {
$errors['name'] = Validator::getLastError();
return false;
}
$player = new OTS_Player();
$player->find($name);
if($player->isLoaded()) {
$errors['name'] = 'Character with this name already exist.';
return false;
}
return empty($errors);
}
/**
* @param string $name
* @param int $sex
@ -19,42 +70,27 @@ class CreateCharacter
* @param array $errors
* @return bool
*/
public function check($name, $sex, &$vocation, &$town, &$errors) {
$minLength = config('character_name_min_length');
$maxLength = config('character_name_max_length');
public function check($name, $sex, &$vocation, &$town, &$errors)
{
$this->checkName($name, $errors);
if(empty($name))
$errors['name'] = 'Please enter a name for your character!';
else if(strlen($name) > $maxLength)
$errors['name'] = 'Name is too long. Max. lenght <b>'.$maxLength.'</b> letters.';
else if(strlen($name) < $minLength)
$errors['name'] = 'Name is too short. Min. lenght <b>'.$minLength.'</b> letters.';
else {
if(!admin() && !Validator::newCharacterName($name)) {
$errors['name'] = Validator::getLastError();
}
$exist = new OTS_Player();
$exist->find($name);
if($exist->isLoaded()) {
$errors['name'] = 'Character with this name already exist.';
}
}
if(empty($sex) && $sex != "0")
if(empty($sex) && $sex != "0") {
$errors['sex'] = 'Please select the sex for your character!';
}
if(count(config('character_samples')) > 1)
{
if(!isset($vocation))
$errors['vocation'] = 'Please select a vocation for your character.';
}
else
else {
$vocation = config('character_samples')[0];
}
if(count(config('character_towns')) > 1) {
if(!isset($town))
if(!isset($town)) {
$errors['town'] = 'Please select a town for your character.';
}
}
else {
$town = config('character_towns')[0];
@ -102,7 +138,7 @@ class CreateCharacter
if(empty($errors))
{
$number_of_players_on_account = $account->getPlayersList()->count();
$number_of_players_on_account = $account->getPlayersList(true)->count();
if($number_of_players_on_account >= config('characters_per_account'))
$errors[] = 'You have too many characters on your account <b>('.$number_of_players_on_account.'/'.config('characters_per_account').')</b>!';
}
@ -120,7 +156,7 @@ class CreateCharacter
return false;
}
global $db, $twig;
global $db;
if($sex == "0")
$char_to_copy->setLookType(136);
@ -157,8 +193,14 @@ class CreateCharacter
$player->setManaSpent($char_to_copy->getManaSpent());
$player->setSoul($char_to_copy->getSoul());
for($skill = POT::SKILL_FIRST; $skill <= POT::SKILL_LAST; $skill++)
$player->setSkill($skill, 10);
for($skill = POT::SKILL_FIRST; $skill <= POT::SKILL_LAST; $skill++) {
$value = 10;
if (config('use_character_sample_skills')) {
$value = $char_to_copy->getSkill($skill);
}
$player->setSkill($skill, $value);
}
$player->setLookBody($char_to_copy->getLookBody());
$player->setLookFeet($char_to_copy->getLookFeet());
@ -186,7 +228,7 @@ class CreateCharacter
}
$player->save();
$player->setCustomField("created", time());
$player->setCustomField('created', time());
$player = new OTS_Player();
$player->find($name);
@ -197,18 +239,28 @@ class CreateCharacter
}
if($db->hasTable('player_skills')) {
for($i=0; $i<7; $i++) {
$value = 10;
if (config('use_character_sample_skills')) {
$value = $char_to_copy->getSkill($i);
}
$skillExists = $db->query('SELECT `skillid` FROM `player_skills` WHERE `player_id` = ' . $player->getId() . ' AND `skillid` = ' . $i);
if($skillExists->rowCount() <= 0) {
$db->query('INSERT INTO `player_skills` (`player_id`, `skillid`, `value`, `count`) VALUES ('.$player->getId().', '.$i.', 10, 0)');
$db->query('INSERT INTO `player_skills` (`player_id`, `skillid`, `value`, `count`) VALUES ('.$player->getId().', '.$i.', ' . $value . ', 0)');
}
}
}
$loaded_items_to_copy = $db->query("SELECT * FROM player_items WHERE player_id = ".$char_to_copy->getId()."");
foreach($loaded_items_to_copy as $save_item)
$db->query("INSERT INTO `player_items` (`player_id` ,`pid` ,`sid` ,`itemtype`, `count`, `attributes`) VALUES ('".$player->getId()."', '".$save_item['pid']."', '".$save_item['sid']."', '".$save_item['itemtype']."', '".$save_item['count']."', '".$save_item['attributes']."');");
if ($db->hasTable('player_items') && $db->hasColumn('player_items', 'pid') && $db->hasColumn('player_items', 'sid') && $db->hasColumn('player_items', 'itemtype')) {
$loaded_items_to_copy = $db->query("SELECT * FROM player_items WHERE player_id = ".$char_to_copy->getId()."");
foreach($loaded_items_to_copy as $save_item) {
$blob = $db->quote($save_item['attributes']);
$db->query("INSERT INTO `player_items` (`player_id` ,`pid` ,`sid` ,`itemtype`, `count`, `attributes`) VALUES ('".$player->getId()."', '".$save_item['pid']."', '".$save_item['sid']."', '".$save_item['itemtype']."', '".$save_item['count']."', $blob);");
}
}
global $twig;
$twig->display('success.html.twig', array(
'title' => 'Character Created',
'description' => 'The character <b>' . $name . '</b> has been created.<br/>
@ -219,4 +271,4 @@ class CreateCharacter
$account->logAction('Created character <b>' . $name . '</b>.');
return true;
}
}
}

View File

@ -18,7 +18,7 @@ namespace Twig\Cache;
*/
class FilesystemCache implements CacheInterface
{
const FORCE_BYTECODE_INVALIDATION = 1;
public const FORCE_BYTECODE_INVALIDATION = 1;
private $directory;
private $options;
@ -35,7 +35,7 @@ class FilesystemCache implements CacheInterface
public function generateKey($name, $className)
{
$hash = hash('sha256', $className);
$hash = hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $className);
return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
}
@ -67,7 +67,7 @@ class FilesystemCache implements CacheInterface
if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) {
// Compile cached file into bytecode cache
if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) {
if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) {
@opcache_invalidate($key, true);
} elseif (\function_exists('apc_compile_file')) {
apc_compile_file($key);

View File

@ -14,11 +14,9 @@ namespace Twig\Cache;
/**
* Implements a no-cache strategy.
*
* @final
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class NullCache implements CacheInterface
final class NullCache implements CacheInterface
{
public function generateKey($name, $className)
{

View File

@ -12,23 +12,22 @@
namespace Twig;
use Twig\Node\ModuleNode;
use Twig\Node\Node;
/**
* Compiles a node to PHP code.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Compiler implements \Twig_CompilerInterface
class Compiler
{
protected $lastLine;
protected $source;
protected $indentation;
protected $env;
protected $debugInfo = [];
protected $sourceOffset;
protected $sourceLine;
protected $filename;
private $lastLine;
private $source;
private $indentation;
private $env;
private $debugInfo = [];
private $sourceOffset;
private $sourceLine;
private $varNameSalt = 0;
public function __construct(Environment $env)
@ -36,16 +35,6 @@ class Compiler implements \Twig_CompilerInterface
$this->env = $env;
}
/**
* @deprecated since 1.25 (to be removed in 2.0)
*/
public function getFilename()
{
@trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED);
return $this->filename;
}
/**
* Returns the environment instance related to this compiler.
*
@ -73,7 +62,7 @@ class Compiler implements \Twig_CompilerInterface
*
* @return $this
*/
public function compile(\Twig_NodeInterface $node, $indentation = 0)
public function compile(Node $node, $indentation = 0)
{
$this->lastLine = null;
$this->source = '';
@ -84,17 +73,12 @@ class Compiler implements \Twig_CompilerInterface
$this->indentation = $indentation;
$this->varNameSalt = 0;
if ($node instanceof ModuleNode) {
// to be removed in 2.0
$this->filename = $node->getTemplateName();
}
$node->compile($this);
return $this;
}
public function subcompile(\Twig_NodeInterface $node, $raw = true)
public function subcompile(Node $node, $raw = true)
{
if (false === $raw) {
$this->source .= str_repeat(' ', $this->indentation * 4);
@ -124,9 +108,8 @@ class Compiler implements \Twig_CompilerInterface
*
* @return $this
*/
public function write()
public function write(...$strings)
{
$strings = \func_get_args();
foreach ($strings as $string) {
$this->source .= str_repeat(' ', $this->indentation * 4).$string;
}
@ -134,22 +117,6 @@ class Compiler implements \Twig_CompilerInterface
return $this;
}
/**
* Appends an indentation to the current PHP code after compilation.
*
* @return $this
*
* @deprecated since 1.27 (to be removed in 2.0).
*/
public function addIndentation()
{
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use write(\'\') instead.', E_USER_DEPRECATED);
$this->source .= str_repeat(' ', $this->indentation * 4);
return $this;
}
/**
* Adds a quoted string to the compiled code.
*
@ -174,21 +141,21 @@ class Compiler implements \Twig_CompilerInterface
public function repr($value)
{
if (\is_int($value) || \is_float($value)) {
if (false !== $locale = setlocale(LC_NUMERIC, '0')) {
setlocale(LC_NUMERIC, 'C');
if (false !== $locale = setlocale(\LC_NUMERIC, '0')) {
setlocale(\LC_NUMERIC, 'C');
}
$this->raw(var_export($value, true));
if (false !== $locale) {
setlocale(LC_NUMERIC, $locale);
setlocale(\LC_NUMERIC, $locale);
}
} elseif (null === $value) {
$this->raw('null');
} elseif (\is_bool($value)) {
$this->raw($value ? 'true' : 'false');
} elseif (\is_array($value)) {
$this->raw('[');
$this->raw('array(');
$first = true;
foreach ($value as $key => $v) {
if (!$first) {
@ -199,7 +166,7 @@ class Compiler implements \Twig_CompilerInterface
$this->raw(' => ');
$this->repr($v);
}
$this->raw(']');
$this->raw(')');
} else {
$this->string($value);
}
@ -212,22 +179,12 @@ class Compiler implements \Twig_CompilerInterface
*
* @return $this
*/
public function addDebugInfo(\Twig_NodeInterface $node)
public function addDebugInfo(Node $node)
{
if ($node->getTemplateLine() != $this->lastLine) {
$this->write(sprintf("// line %d\n", $node->getTemplateLine()));
// when mbstring.func_overload is set to 2
// mb_substr_count() replaces substr_count()
// but they have different signatures!
if (((int) ini_get('mbstring.func_overload')) & 2) {
@trigger_error('Support for having "mbstring.func_overload" different from 0 is deprecated version 1.29 and will be removed in 2.0.', E_USER_DEPRECATED);
// this is much slower than the "right" version
$this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n");
} else {
$this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
}
$this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
$this->sourceOffset = \strlen($this->source);
$this->debugInfo[$this->sourceLine] = $node->getTemplateLine();
@ -281,7 +238,7 @@ class Compiler implements \Twig_CompilerInterface
public function getVarName()
{
return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->varNameSalt++));
return sprintf('__internal_compile_%d', $this->varNameSalt++);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -38,11 +38,9 @@ use Twig\Template;
*/
class Error extends \Exception
{
protected $lineno;
// to be renamed to name in 2.0
protected $filename;
protected $rawMessage;
private $lineno;
private $name;
private $rawMessage;
private $sourcePath;
private $sourceCode;
@ -57,22 +55,23 @@ class Error extends \Exception
* @param Source|string|null $source The source context where the error occurred
* @param \Exception $previous The previous exception
*/
public function __construct($message, $lineno = -1, $source = null, \Exception $previous = null)
public function __construct(string $message, int $lineno = -1, $source = null, \Exception $previous = null)
{
parent::__construct('', 0, $previous);
if (null === $source) {
$name = null;
} elseif (!$source instanceof Source) {
// for compat with the Twig C ext., passing the template name as string is accepted
} elseif (!$source instanceof Source && !$source instanceof \Twig_Source) {
@trigger_error(sprintf('Passing a string as a source to %s is deprecated since Twig 2.6.1; pass a Twig\Source instance instead.', __CLASS__), \E_USER_DEPRECATED);
$name = $source;
} else {
$name = $source->getName();
$this->sourceCode = $source->getCode();
$this->sourcePath = $source->getPath();
}
parent::__construct('', 0, $previous);
$this->lineno = $lineno;
$this->filename = $name;
$this->name = $name;
$this->rawMessage = $message;
$this->updateRepr();
}
@ -87,67 +86,6 @@ class Error extends \Exception
return $this->rawMessage;
}
/**
* Gets the logical name where the error occurred.
*
* @return string The name
*
* @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead.
*/
public function getTemplateFile()
{
@trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
return $this->filename;
}
/**
* Sets the logical name where the error occurred.
*
* @param string $name The name
*
* @deprecated since 1.27 (to be removed in 2.0). Use setSourceContext() instead.
*/
public function setTemplateFile($name)
{
@trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
$this->filename = $name;
$this->updateRepr();
}
/**
* Gets the logical name where the error occurred.
*
* @return string The name
*
* @deprecated since 1.29 (to be removed in 2.0). Use getSourceContext() instead.
*/
public function getTemplateName()
{
@trigger_error(sprintf('The "%s" method is deprecated since version 1.29 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
return $this->filename;
}
/**
* Sets the logical name where the error occurred.
*
* @param string $name The name
*
* @deprecated since 1.29 (to be removed in 2.0). Use setSourceContext() instead.
*/
public function setTemplateName($name)
{
@trigger_error(sprintf('The "%s" method is deprecated since version 1.29 and will be removed in 2.0. Use setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
$this->filename = $name;
$this->sourceCode = $this->sourcePath = null;
$this->updateRepr();
}
/**
* Gets the template line where the error occurred.
*
@ -177,7 +115,7 @@ class Error extends \Exception
*/
public function getSourceContext()
{
return $this->filename ? new Source($this->sourceCode, $this->filename, $this->sourcePath) : null;
return $this->name ? new Source($this->sourceCode, $this->name, $this->sourcePath) : null;
}
/**
@ -186,10 +124,10 @@ class Error extends \Exception
public function setSourceContext(Source $source = null)
{
if (null === $source) {
$this->sourceCode = $this->filename = $this->sourcePath = null;
$this->sourceCode = $this->name = $this->sourcePath = null;
} else {
$this->sourceCode = $source->getCode();
$this->filename = $source->getName();
$this->name = $source->getName();
$this->sourcePath = $source->getPath();
}
@ -208,10 +146,7 @@ class Error extends \Exception
$this->updateRepr();
}
/**
* @internal
*/
protected function updateRepr()
private function updateRepr()
{
$this->message = $this->rawMessage;
@ -234,11 +169,11 @@ class Error extends \Exception
$questionMark = true;
}
if ($this->filename) {
if (\is_string($this->filename) || (\is_object($this->filename) && method_exists($this->filename, '__toString'))) {
$name = sprintf('"%s"', $this->filename);
if ($this->name) {
if (\is_string($this->name) || (\is_object($this->name) && method_exists($this->name, '__toString'))) {
$name = sprintf('"%s"', $this->name);
} else {
$name = json_encode($this->filename);
$name = json_encode($this->name);
}
$this->message .= sprintf(' in %s', $name);
}
@ -256,20 +191,17 @@ class Error extends \Exception
}
}
/**
* @internal
*/
protected function guessTemplateInfo()
private function guessTemplateInfo()
{
$template = null;
$templateClass = null;
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
$backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS | \DEBUG_BACKTRACE_PROVIDE_OBJECT);
foreach ($backtrace as $trace) {
if (isset($trace['object']) && $trace['object'] instanceof Template && 'Twig_Template' !== \get_class($trace['object'])) {
if (isset($trace['object']) && $trace['object'] instanceof Template && 'Twig\Template' !== \get_class($trace['object'])) {
$currentClass = \get_class($trace['object']);
$isEmbedContainer = 0 === strpos($templateClass, $currentClass);
if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
$isEmbedContainer = null === $templateClass ? false : 0 === strpos($templateClass, $currentClass);
if (null === $this->name || ($this->name == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
$template = $trace['object'];
$templateClass = \get_class($trace['object']);
}
@ -277,8 +209,8 @@ class Error extends \Exception
}
// update template name
if (null !== $template && null === $this->filename) {
$this->filename = $template->getTemplateName();
if (null !== $template && null === $this->name) {
$this->name = $template->getTemplateName();
}
// update template path if any
@ -296,7 +228,7 @@ class Error extends \Exception
$file = $r->getFileName();
$exceptions = [$e = $this];
while ($e instanceof self && $e = $e->getPrevious()) {
while ($e = $e->getPrevious()) {
$exceptions[] = $e;
}

View File

@ -26,20 +26,6 @@ class SyntaxError extends Error
* @param array $items An array of possible items
*/
public function addSuggestions($name, array $items)
{
if (!$alternatives = self::computeAlternatives($name, $items)) {
return;
}
$this->appendMessage(sprintf(' Did you mean "%s"?', implode('", "', $alternatives)));
}
/**
* @internal
*
* To be merged with the addSuggestions() method in 2.0.
*/
public static function computeAlternatives($name, $items)
{
$alternatives = [];
foreach ($items as $item) {
@ -48,9 +34,14 @@ class SyntaxError extends Error
$alternatives[$item] = $lev;
}
}
if (!$alternatives) {
return;
}
asort($alternatives);
return array_keys($alternatives);
$this->appendMessage(sprintf(' Did you mean "%s"?', implode('", "', array_keys($alternatives))));
}
}

View File

@ -13,6 +13,7 @@
namespace Twig;
use Twig\Error\SyntaxError;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\ArrayExpression;
use Twig\Node\Expression\ArrowFunctionExpression;
use Twig\Node\Expression\AssignNameExpression;
@ -24,6 +25,7 @@ use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\MethodCallExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\ParentExpression;
use Twig\Node\Expression\TestExpression;
use Twig\Node\Expression\Unary\NegUnary;
use Twig\Node\Expression\Unary\NotUnary;
use Twig\Node\Expression\Unary\PosUnary;
@ -43,30 +45,20 @@ use Twig\Node\Node;
*/
class ExpressionParser
{
const OPERATOR_LEFT = 1;
const OPERATOR_RIGHT = 2;
protected $parser;
protected $unaryOperators;
protected $binaryOperators;
public const OPERATOR_LEFT = 1;
public const OPERATOR_RIGHT = 2;
private $parser;
private $env;
private $unaryOperators;
private $binaryOperators;
public function __construct(Parser $parser, $env = null)
public function __construct(Parser $parser, Environment $env)
{
$this->parser = $parser;
if ($env instanceof Environment) {
$this->env = $env;
$this->unaryOperators = $env->getUnaryOperators();
$this->binaryOperators = $env->getBinaryOperators();
} else {
@trigger_error('Passing the operators as constructor arguments to '.__METHOD__.' is deprecated since version 1.27. Pass the environment instead.', E_USER_DEPRECATED);
$this->env = $parser->getEnvironment();
$this->unaryOperators = func_get_arg(1);
$this->binaryOperators = func_get_arg(2);
}
$this->env = $env;
$this->unaryOperators = $env->getUnaryOperators();
$this->binaryOperators = $env->getBinaryOperators();
}
public function parseExpression($precedence = 0, $allowArrow = false)
@ -86,7 +78,7 @@ class ExpressionParser
} elseif ('is' === $token->getValue()) {
$expr = $this->parseTestExpression($expr);
} elseif (isset($op['callable'])) {
$expr = \call_user_func($op['callable'], $this->parser, $expr);
$expr = $op['callable']($this->parser, $expr);
} else {
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
$class = $op['class'];
@ -111,57 +103,57 @@ class ExpressionParser
$stream = $this->parser->getStream();
// short array syntax (one argument, no parentheses)?
if ($stream->look(1)->test(Token::ARROW_TYPE)) {
if ($stream->look(1)->test(/* Token::ARROW_TYPE */ 12)) {
$line = $stream->getCurrent()->getLine();
$token = $stream->expect(Token::NAME_TYPE);
$token = $stream->expect(/* Token::NAME_TYPE */ 5);
$names = [new AssignNameExpression($token->getValue(), $token->getLine())];
$stream->expect(Token::ARROW_TYPE);
$stream->expect(/* Token::ARROW_TYPE */ 12);
return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line);
}
// first, determine if we are parsing an arrow function by finding => (long form)
$i = 0;
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, '(')) {
if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
return null;
}
++$i;
while (true) {
// variable name
++$i;
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ',')) {
if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, ',')) {
break;
}
++$i;
}
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ')')) {
if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) {
return null;
}
++$i;
if (!$stream->look($i)->test(Token::ARROW_TYPE)) {
if (!$stream->look($i)->test(/* Token::ARROW_TYPE */ 12)) {
return null;
}
// yes, let's parse it properly
$token = $stream->expect(Token::PUNCTUATION_TYPE, '(');
$token = $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '(');
$line = $token->getLine();
$names = [];
while (true) {
$token = $stream->expect(Token::NAME_TYPE);
$token = $stream->expect(/* Token::NAME_TYPE */ 5);
$names[] = new AssignNameExpression($token->getValue(), $token->getLine());
if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) {
if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) {
break;
}
}
$stream->expect(Token::PUNCTUATION_TYPE, ')');
$stream->expect(Token::ARROW_TYPE);
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ')');
$stream->expect(/* Token::ARROW_TYPE */ 12);
return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line);
}
protected function getPrimary()
private function getPrimary(): AbstractExpression
{
$token = $this->parser->getCurrentToken();
@ -172,10 +164,10 @@ class ExpressionParser
$class = $operator['class'];
return $this->parsePostfixExpression(new $class($expr, $token->getLine()));
} elseif ($token->test(Token::PUNCTUATION_TYPE, '(')) {
} elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
$this->parser->getStream()->next();
$expr = $this->parseExpression();
$this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed');
$this->parser->getStream()->expect(/* Token::PUNCTUATION_TYPE */ 9, ')', 'An opened parenthesis is not properly closed');
return $this->parsePostfixExpression($expr);
}
@ -183,12 +175,12 @@ class ExpressionParser
return $this->parsePrimaryExpression();
}
protected function parseConditionalExpression($expr)
private function parseConditionalExpression($expr): AbstractExpression
{
while ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, '?')) {
if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) {
while ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, '?')) {
if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) {
$expr2 = $this->parseExpression();
if ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) {
if ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) {
$expr3 = $this->parseExpression();
} else {
$expr3 = new ConstantExpression('', $this->parser->getCurrentToken()->getLine());
@ -204,21 +196,21 @@ class ExpressionParser
return $expr;
}
protected function isUnary(Token $token)
private function isUnary(Token $token): bool
{
return $token->test(Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]);
return $token->test(/* Token::OPERATOR_TYPE */ 8) && isset($this->unaryOperators[$token->getValue()]);
}
protected function isBinary(Token $token)
private function isBinary(Token $token): bool
{
return $token->test(Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]);
return $token->test(/* Token::OPERATOR_TYPE */ 8) && isset($this->binaryOperators[$token->getValue()]);
}
public function parsePrimaryExpression()
{
$token = $this->parser->getCurrentToken();
switch ($token->getType()) {
case Token::NAME_TYPE:
case /* Token::NAME_TYPE */ 5:
$this->parser->getStream()->next();
switch ($token->getValue()) {
case 'true':
@ -247,17 +239,17 @@ class ExpressionParser
}
break;
case Token::NUMBER_TYPE:
case /* Token::NUMBER_TYPE */ 6:
$this->parser->getStream()->next();
$node = new ConstantExpression($token->getValue(), $token->getLine());
break;
case Token::STRING_TYPE:
case Token::INTERPOLATION_START_TYPE:
case /* Token::STRING_TYPE */ 7:
case /* Token::INTERPOLATION_START_TYPE */ 10:
$node = $this->parseStringExpression();
break;
case Token::OPERATOR_TYPE:
case /* Token::OPERATOR_TYPE */ 8:
if (preg_match(Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) {
// in this context, string operators are variable names
$this->parser->getStream()->next();
@ -267,10 +259,8 @@ class ExpressionParser
$class = $this->unaryOperators[$token->getValue()]['class'];
$ref = new \ReflectionClass($class);
$negClass = 'Twig\Node\Expression\Unary\NegUnary';
$posClass = 'Twig\Node\Expression\Unary\PosUnary';
if (!(\in_array($ref->getName(), [$negClass, $posClass, 'Twig_Node_Expression_Unary_Neg', 'Twig_Node_Expression_Unary_Pos'])
|| $ref->isSubclassOf($negClass) || $ref->isSubclassOf($posClass)
if (!(\in_array($ref->getName(), [NegUnary::class, PosUnary::class, 'Twig_Node_Expression_Unary_Neg', 'Twig_Node_Expression_Unary_Pos'])
|| $ref->isSubclassOf(NegUnary::class) || $ref->isSubclassOf(PosUnary::class)
|| $ref->isSubclassOf('Twig_Node_Expression_Unary_Neg') || $ref->isSubclassOf('Twig_Node_Expression_Unary_Pos'))
) {
throw new SyntaxError(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
@ -285,11 +275,11 @@ class ExpressionParser
// no break
default:
if ($token->test(Token::PUNCTUATION_TYPE, '[')) {
if ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '[')) {
$node = $this->parseArrayExpression();
} elseif ($token->test(Token::PUNCTUATION_TYPE, '{')) {
} elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '{')) {
$node = $this->parseHashExpression();
} elseif ($token->test(Token::OPERATOR_TYPE, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) {
} elseif ($token->test(/* Token::OPERATOR_TYPE */ 8, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) {
throw new SyntaxError(sprintf('Unexpected operator of value "%s". Did you try to use "===" or "!==" for strict comparison? Use "is same as(value)" instead.', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
} else {
throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s".', Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
@ -307,12 +297,12 @@ class ExpressionParser
// a string cannot be followed by another string in a single expression
$nextCanBeString = true;
while (true) {
if ($nextCanBeString && $token = $stream->nextIf(Token::STRING_TYPE)) {
if ($nextCanBeString && $token = $stream->nextIf(/* Token::STRING_TYPE */ 7)) {
$nodes[] = new ConstantExpression($token->getValue(), $token->getLine());
$nextCanBeString = false;
} elseif ($stream->nextIf(Token::INTERPOLATION_START_TYPE)) {
} elseif ($stream->nextIf(/* Token::INTERPOLATION_START_TYPE */ 10)) {
$nodes[] = $this->parseExpression();
$stream->expect(Token::INTERPOLATION_END_TYPE);
$stream->expect(/* Token::INTERPOLATION_END_TYPE */ 11);
$nextCanBeString = true;
} else {
break;
@ -330,16 +320,16 @@ class ExpressionParser
public function parseArrayExpression()
{
$stream = $this->parser->getStream();
$stream->expect(Token::PUNCTUATION_TYPE, '[', 'An array element was expected');
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '[', 'An array element was expected');
$node = new ArrayExpression([], $stream->getCurrent()->getLine());
$first = true;
while (!$stream->test(Token::PUNCTUATION_TYPE, ']')) {
while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {
if (!$first) {
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma');
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'An array element must be followed by a comma');
// trailing ,?
if ($stream->test(Token::PUNCTUATION_TYPE, ']')) {
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {
break;
}
}
@ -347,7 +337,7 @@ class ExpressionParser
$node->addElement($this->parseExpression());
}
$stream->expect(Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed');
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']', 'An opened array is not properly closed');
return $node;
}
@ -355,16 +345,16 @@ class ExpressionParser
public function parseHashExpression()
{
$stream = $this->parser->getStream();
$stream->expect(Token::PUNCTUATION_TYPE, '{', 'A hash element was expected');
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '{', 'A hash element was expected');
$node = new ArrayExpression([], $stream->getCurrent()->getLine());
$first = true;
while (!$stream->test(Token::PUNCTUATION_TYPE, '}')) {
while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, '}')) {
if (!$first) {
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma');
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'A hash value must be followed by a comma');
// trailing ,?
if ($stream->test(Token::PUNCTUATION_TYPE, '}')) {
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '}')) {
break;
}
}
@ -376,9 +366,18 @@ class ExpressionParser
// * a string -- 'a'
// * a name, which is equivalent to a string -- a
// * an expression, which must be enclosed in parentheses -- (1 + 2)
if (($token = $stream->nextIf(Token::STRING_TYPE)) || ($token = $stream->nextIf(Token::NAME_TYPE)) || $token = $stream->nextIf(Token::NUMBER_TYPE)) {
if ($token = $stream->nextIf(/* Token::NAME_TYPE */ 5)) {
$key = new ConstantExpression($token->getValue(), $token->getLine());
} elseif ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
// {a} is a shortcut for {a:a}
if ($stream->test(Token::PUNCTUATION_TYPE, [',', '}'])) {
$value = new NameExpression($key->getAttribute('value'), $key->getTemplateLine());
$node->addElement($value, $key);
continue;
}
} elseif (($token = $stream->nextIf(/* Token::STRING_TYPE */ 7)) || $token = $stream->nextIf(/* Token::NUMBER_TYPE */ 6)) {
$key = new ConstantExpression($token->getValue(), $token->getLine());
} elseif ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
$key = $this->parseExpression();
} else {
$current = $stream->getCurrent();
@ -386,12 +385,12 @@ class ExpressionParser
throw new SyntaxError(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $stream->getSourceContext());
}
$stream->expect(Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ':', 'A hash key must be followed by a colon (:)');
$value = $this->parseExpression();
$node->addElement($value, $key);
}
$stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed');
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '}', 'An opened hash is not properly closed');
return $node;
}
@ -400,7 +399,7 @@ class ExpressionParser
{
while (true) {
$token = $this->parser->getCurrentToken();
if (Token::PUNCTUATION_TYPE == $token->getType()) {
if (/* Token::PUNCTUATION_TYPE */ 9 == $token->getType()) {
if ('.' == $token->getValue() || '[' == $token->getValue()) {
$node = $this->parseSubscriptExpression($node);
} elseif ('|' == $token->getValue()) {
@ -474,22 +473,22 @@ class ExpressionParser
if ('.' == $token->getValue()) {
$token = $stream->next();
if (
Token::NAME_TYPE == $token->getType()
/* Token::NAME_TYPE */ 5 == $token->getType()
||
Token::NUMBER_TYPE == $token->getType()
/* Token::NUMBER_TYPE */ 6 == $token->getType()
||
(Token::OPERATOR_TYPE == $token->getType() && preg_match(Lexer::REGEX_NAME, $token->getValue()))
(/* Token::OPERATOR_TYPE */ 8 == $token->getType() && preg_match(Lexer::REGEX_NAME, $token->getValue()))
) {
$arg = new ConstantExpression($token->getValue(), $lineno);
if ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
$type = Template::METHOD_CALL;
foreach ($this->parseArguments() as $n) {
$arguments->addElement($n);
}
}
} else {
throw new SyntaxError('Expected name or number.', $lineno, $stream->getSourceContext());
throw new SyntaxError(sprintf('Expected name or number, got value "%s" of type %s.', $token->getValue(), Token::typeToEnglish($token->getType())), $lineno, $stream->getSourceContext());
}
if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
@ -499,11 +498,7 @@ class ExpressionParser
$name = $arg->getAttribute('value');
if ($this->parser->isReservedMacroName($name)) {
throw new SyntaxError(sprintf('"%s" cannot be called as macro as it is a reserved keyword.', $name), $token->getLine(), $stream->getSourceContext());
}
$node = new MethodCallExpression($node, 'get'.$name, $arguments, $lineno);
$node = new MethodCallExpression($node, 'macro_'.$name, $arguments, $lineno);
$node->setAttribute('safe', true);
return $node;
@ -513,19 +508,19 @@ class ExpressionParser
// slice?
$slice = false;
if ($stream->test(Token::PUNCTUATION_TYPE, ':')) {
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ':')) {
$slice = true;
$arg = new ConstantExpression(0, $token->getLine());
} else {
$arg = $this->parseExpression();
}
if ($stream->nextIf(Token::PUNCTUATION_TYPE, ':')) {
if ($stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) {
$slice = true;
}
if ($slice) {
if ($stream->test(Token::PUNCTUATION_TYPE, ']')) {
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {
$length = new ConstantExpression(null, $token->getLine());
} else {
$length = $this->parseExpression();
@ -535,12 +530,12 @@ class ExpressionParser
$arguments = new Node([$arg, $length]);
$filter = new $class($node, new ConstantExpression('slice', $token->getLine()), $arguments, $token->getLine());
$stream->expect(Token::PUNCTUATION_TYPE, ']');
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']');
return $filter;
}
$stream->expect(Token::PUNCTUATION_TYPE, ']');
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']');
}
return new GetAttrExpression($node, $arg, $arguments, $type, $lineno);
@ -556,10 +551,10 @@ class ExpressionParser
public function parseFilterExpressionRaw($node, $tag = null)
{
while (true) {
$token = $this->parser->getStream()->expect(Token::NAME_TYPE);
$token = $this->parser->getStream()->expect(/* Token::NAME_TYPE */ 5);
$name = new ConstantExpression($token->getValue(), $token->getLine());
if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '(')) {
if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
$arguments = new Node();
} else {
$arguments = $this->parseArguments(true, false, true);
@ -569,7 +564,7 @@ class ExpressionParser
$node = new $class($node, $name, $arguments, $token->getLine(), $tag);
if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '|')) {
if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '|')) {
break;
}
@ -594,21 +589,26 @@ class ExpressionParser
$args = [];
$stream = $this->parser->getStream();
$stream->expect(Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');
while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) {
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '(', 'A list of arguments must begin with an opening parenthesis');
while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) {
if (!empty($args)) {
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'Arguments must be separated by a comma');
// if the comma above was a trailing comma, early exit the argument parse loop
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) {
break;
}
}
if ($definition) {
$token = $stream->expect(Token::NAME_TYPE, null, 'An argument must be a name');
$token = $stream->expect(/* Token::NAME_TYPE */ 5, null, 'An argument must be a name');
$value = new NameExpression($token->getValue(), $this->parser->getCurrentToken()->getLine());
} else {
$value = $this->parseExpression(0, $allowArrow);
}
$name = null;
if ($namedArguments && $token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) {
if ($namedArguments && $token = $stream->nextIf(/* Token::OPERATOR_TYPE */ 8, '=')) {
if (!$value instanceof NameExpression) {
throw new SyntaxError(sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext());
}
@ -618,7 +618,7 @@ class ExpressionParser
$value = $this->parsePrimaryExpression();
if (!$this->checkConstantExpression($value)) {
throw new SyntaxError(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $stream->getSourceContext());
throw new SyntaxError('A default value for an argument must be a constant (a boolean, a string, a number, or an array).', $token->getLine(), $stream->getSourceContext());
}
} else {
$value = $this->parseExpression(0, $allowArrow);
@ -639,7 +639,7 @@ class ExpressionParser
}
}
}
$stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ')', 'A list of arguments must be closed by a parenthesis');
return new Node($args);
}
@ -650,19 +650,19 @@ class ExpressionParser
$targets = [];
while (true) {
$token = $this->parser->getCurrentToken();
if ($stream->test(Token::OPERATOR_TYPE) && preg_match(Lexer::REGEX_NAME, $token->getValue())) {
if ($stream->test(/* Token::OPERATOR_TYPE */ 8) && preg_match(Lexer::REGEX_NAME, $token->getValue())) {
// in this context, string operators are variable names
$this->parser->getStream()->next();
} else {
$stream->expect(Token::NAME_TYPE, null, 'Only variables can be assigned to');
$stream->expect(/* Token::NAME_TYPE */ 5, null, 'Only variables can be assigned to');
}
$value = $token->getValue();
if (\in_array(strtolower($value), ['true', 'false', 'none', 'null'])) {
if (\in_array(strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), ['true', 'false', 'none', 'null'])) {
throw new SyntaxError(sprintf('You cannot assign a value to "%s".', $value), $token->getLine(), $stream->getSourceContext());
}
$targets[] = new AssignNameExpression($value, $token->getLine());
if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) {
if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) {
break;
}
}
@ -675,7 +675,7 @@ class ExpressionParser
$targets = [];
while (true) {
$targets[] = $this->parseExpression();
if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ',')) {
if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) {
break;
}
}
@ -683,35 +683,42 @@ class ExpressionParser
return new Node($targets);
}
private function parseNotTestExpression(\Twig_NodeInterface $node)
private function parseNotTestExpression(Node $node): NotUnary
{
return new NotUnary($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine());
}
private function parseTestExpression(\Twig_NodeInterface $node)
private function parseTestExpression(Node $node): TestExpression
{
$stream = $this->parser->getStream();
list($name, $test) = $this->getTest($node->getTemplateLine());
$class = $this->getTestNodeClass($test);
$arguments = null;
if ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
$arguments = $this->parseArguments(true);
} elseif ($test->hasOneMandatoryArgument()) {
$arguments = new Node([0 => $this->parsePrimaryExpression()]);
}
if ('defined' === $name && $node instanceof NameExpression && null !== $alias = $this->parser->getImportedSymbol('function', $node->getAttribute('name'))) {
$node = new MethodCallExpression($alias['node'], $alias['name'], new ArrayExpression([], $node->getTemplateLine()), $node->getTemplateLine());
$node->setAttribute('safe', true);
}
return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine());
}
private function getTest($line)
private function getTest(int $line): array
{
$stream = $this->parser->getStream();
$name = $stream->expect(Token::NAME_TYPE)->getValue();
$name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue();
if ($test = $this->env->getTest($name)) {
return [$name, $test];
}
if ($stream->test(Token::NAME_TYPE)) {
if ($stream->test(/* Token::NAME_TYPE */ 5)) {
// try 2-words tests
$name = $name.' '.$this->parser->getCurrentToken()->getValue();
@ -728,11 +735,12 @@ class ExpressionParser
throw $e;
}
private function getTestNodeClass($test)
private function getTestNodeClass(TwigTest $test): string
{
if ($test instanceof TwigTest && $test->isDeprecated()) {
if ($test->isDeprecated()) {
$stream = $this->parser->getStream();
$message = sprintf('Twig Test "%s" is deprecated', $test->getName());
if (!\is_bool($test->getDeprecatedVersion())) {
$message .= sprintf(' since version %s', $test->getDeprecatedVersion());
}
@ -740,19 +748,15 @@ class ExpressionParser
$message .= sprintf('. Use "%s" instead', $test->getAlternative());
}
$src = $stream->getSourceContext();
$message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $stream->getCurrent()->getLine());
$message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $stream->getCurrent()->getLine());
@trigger_error($message, E_USER_DEPRECATED);
@trigger_error($message, \E_USER_DEPRECATED);
}
if ($test instanceof TwigTest) {
return $test->getNodeClass();
}
return $test instanceof \Twig_Test_Node ? $test->getClass() : 'Twig\Node\Expression\TestExpression';
return $test->getNodeClass();
}
protected function getFunctionNodeClass($name, $line)
private function getFunctionNodeClass(string $name, int $line): string
{
if (false === $function = $this->env->getFunction($name)) {
$e = new SyntaxError(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext());
@ -761,7 +765,7 @@ class ExpressionParser
throw $e;
}
if ($function instanceof TwigFunction && $function->isDeprecated()) {
if ($function->isDeprecated()) {
$message = sprintf('Twig Function "%s" is deprecated', $function->getName());
if (!\is_bool($function->getDeprecatedVersion())) {
$message .= sprintf(' since version %s', $function->getDeprecatedVersion());
@ -770,19 +774,15 @@ class ExpressionParser
$message .= sprintf('. Use "%s" instead', $function->getAlternative());
}
$src = $this->parser->getStream()->getSourceContext();
$message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line);
$message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $line);
@trigger_error($message, E_USER_DEPRECATED);
@trigger_error($message, \E_USER_DEPRECATED);
}
if ($function instanceof TwigFunction) {
return $function->getNodeClass();
}
return $function instanceof \Twig_Function_Node ? $function->getClass() : 'Twig\Node\Expression\FunctionExpression';
return $function->getNodeClass();
}
protected function getFilterNodeClass($name, $line)
private function getFilterNodeClass(string $name, int $line): string
{
if (false === $filter = $this->env->getFilter($name)) {
$e = new SyntaxError(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext());
@ -791,7 +791,7 @@ class ExpressionParser
throw $e;
}
if ($filter instanceof TwigFilter && $filter->isDeprecated()) {
if ($filter->isDeprecated()) {
$message = sprintf('Twig Filter "%s" is deprecated', $filter->getName());
if (!\is_bool($filter->getDeprecatedVersion())) {
$message .= sprintf(' since version %s', $filter->getDeprecatedVersion());
@ -800,20 +800,16 @@ class ExpressionParser
$message .= sprintf('. Use "%s" instead', $filter->getAlternative());
}
$src = $this->parser->getStream()->getSourceContext();
$message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line);
$message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $line);
@trigger_error($message, E_USER_DEPRECATED);
@trigger_error($message, \E_USER_DEPRECATED);
}
if ($filter instanceof TwigFilter) {
return $filter->getNodeClass();
}
return $filter instanceof \Twig_Filter_Node ? $filter->getClass() : 'Twig\Node\Expression\FilterExpression';
return $filter->getNodeClass();
}
// checks that the node only contains "constant" elements
protected function checkConstantExpression(\Twig_NodeInterface $node)
private function checkConstantExpression(Node $node): bool
{
if (!($node instanceof ConstantExpression || $node instanceof ArrayExpression
|| $node instanceof NegUnary || $node instanceof PosUnary

View File

@ -11,17 +11,8 @@
namespace Twig\Extension;
use Twig\Environment;
abstract class AbstractExtension implements ExtensionInterface
{
/**
* @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_InitRuntimeInterface instead
*/
public function initRuntime(Environment $environment)
{
}
public function getTokenParsers()
{
return [];
@ -51,22 +42,6 @@ abstract class AbstractExtension implements ExtensionInterface
{
return [];
}
/**
* @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_GlobalsInterface instead
*/
public function getGlobals()
{
return [];
}
/**
* @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
*/
public function getName()
{
return \get_class($this);
}
}
class_alias('Twig\Extension\AbstractExtension', 'Twig_Extension');

File diff suppressed because it is too large Load Diff

View File

@ -12,10 +12,7 @@
namespace Twig\Extension {
use Twig\TwigFunction;
/**
* @final
*/
class DebugExtension extends AbstractExtension
final class DebugExtension extends AbstractExtension
{
public function getFunctions()
{
@ -33,11 +30,6 @@ class DebugExtension extends AbstractExtension
new TwigFunction('dump', 'twig_var_dump', ['is_safe' => $isDumpOutputHtmlSafe ? ['html'] : [], 'needs_context' => true, 'needs_environment' => true, 'is_variadic' => true]),
];
}
public function getName()
{
return 'debug';
}
}
class_alias('Twig\Extension\DebugExtension', 'Twig_Extension_Debug');
@ -48,7 +40,7 @@ use Twig\Environment;
use Twig\Template;
use Twig\TemplateWrapper;
function twig_var_dump(Environment $env, $context, array $vars = [])
function twig_var_dump(Environment $env, $context, ...$vars)
{
if (!$env->isDebug()) {
return;
@ -66,9 +58,7 @@ function twig_var_dump(Environment $env, $context, array $vars = [])
var_dump($vars);
} else {
foreach ($vars as $var) {
var_dump($var);
}
var_dump(...$vars);
}
return ob_get_clean();

View File

@ -10,16 +10,21 @@
*/
namespace Twig\Extension {
use Twig\FileExtensionEscapingStrategy;
use Twig\NodeVisitor\EscaperNodeVisitor;
use Twig\TokenParser\AutoEscapeTokenParser;
use Twig\TwigFilter;
/**
* @final
*/
class EscaperExtension extends AbstractExtension
final class EscaperExtension extends AbstractExtension
{
protected $defaultStrategy;
private $defaultStrategy;
private $escapers = [];
/** @internal */
public $safeClasses = [];
/** @internal */
public $safeLookup = [];
/**
* @param string|false|callable $defaultStrategy An escaping strategy
@ -44,6 +49,8 @@ class EscaperExtension extends AbstractExtension
public function getFilters()
{
return [
new TwigFilter('escape', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
new TwigFilter('e', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
new TwigFilter('raw', 'twig_raw_filter', ['is_safe' => ['all']]),
];
}
@ -58,21 +65,8 @@ class EscaperExtension extends AbstractExtension
*/
public function setDefaultStrategy($defaultStrategy)
{
// for BC
if (true === $defaultStrategy) {
@trigger_error('Using "true" as the default strategy is deprecated since version 1.21. Use "html" instead.', E_USER_DEPRECATED);
$defaultStrategy = 'html';
}
if ('filename' === $defaultStrategy) {
@trigger_error('Using "filename" as the default strategy is deprecated since version 1.27. Use "name" instead.', E_USER_DEPRECATED);
$defaultStrategy = 'name';
}
if ('name' === $defaultStrategy) {
$defaultStrategy = ['\Twig\FileExtensionEscapingStrategy', 'guess'];
$defaultStrategy = [FileExtensionEscapingStrategy::class, 'guess'];
}
$this->defaultStrategy = $defaultStrategy;
@ -96,9 +90,47 @@ class EscaperExtension extends AbstractExtension
return $this->defaultStrategy;
}
public function getName()
/**
* Defines a new escaper to be used via the escape filter.
*
* @param string $strategy The strategy name that should be used as a strategy in the escape call
* @param callable $callable A valid PHP callable
*/
public function setEscaper($strategy, callable $callable)
{
return 'escaper';
$this->escapers[$strategy] = $callable;
}
/**
* Gets all defined escapers.
*
* @return callable[] An array of escapers
*/
public function getEscapers()
{
return $this->escapers;
}
public function setSafeClasses(array $safeClasses = [])
{
$this->safeClasses = [];
$this->safeLookup = [];
foreach ($safeClasses as $class => $strategies) {
$this->addSafeClass($class, $strategies);
}
}
public function addSafeClass(string $class, array $strategies)
{
$class = ltrim($class, '\\');
if (!isset($this->safeClasses[$class])) {
$this->safeClasses[$class] = [];
}
$this->safeClasses[$class] = array_merge($this->safeClasses[$class], $strategies);
foreach ($strategies as $strategy) {
$this->safeLookup[$strategy][$class] = true;
}
}
}
@ -106,6 +138,14 @@ class_alias('Twig\Extension\EscaperExtension', 'Twig_Extension_Escaper');
}
namespace {
use Twig\Environment;
use Twig\Error\RuntimeError;
use Twig\Extension\CoreExtension;
use Twig\Extension\EscaperExtension;
use Twig\Markup;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Node;
/**
* Marks a variable as being safe.
*
@ -117,4 +157,272 @@ function twig_raw_filter($string)
{
return $string;
}
/**
* Escapes a string.
*
* @param mixed $string The value to be escaped
* @param string $strategy The escaping strategy
* @param string $charset The charset
* @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
*
* @return string
*/
function twig_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
{
if ($autoescape && $string instanceof Markup) {
return $string;
}
if (!\is_string($string)) {
if (\is_object($string) && method_exists($string, '__toString')) {
if ($autoescape) {
$c = \get_class($string);
$ext = $env->getExtension(EscaperExtension::class);
if (!isset($ext->safeClasses[$c])) {
$ext->safeClasses[$c] = [];
foreach (class_parents($string) + class_implements($string) as $class) {
if (isset($ext->safeClasses[$class])) {
$ext->safeClasses[$c] = array_unique(array_merge($ext->safeClasses[$c], $ext->safeClasses[$class]));
foreach ($ext->safeClasses[$class] as $s) {
$ext->safeLookup[$s][$c] = true;
}
}
}
}
if (isset($ext->safeLookup[$strategy][$c]) || isset($ext->safeLookup['all'][$c])) {
return (string) $string;
}
}
$string = (string) $string;
} elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) {
return $string;
}
}
if ('' === $string) {
return '';
}
if (null === $charset) {
$charset = $env->getCharset();
}
switch ($strategy) {
case 'html':
// see https://www.php.net/htmlspecialchars
// Using a static variable to avoid initializing the array
// each time the function is called. Moving the declaration on the
// top of the function slow downs other escaping strategies.
static $htmlspecialcharsCharsets = [
'ISO-8859-1' => true, 'ISO8859-1' => true,
'ISO-8859-15' => true, 'ISO8859-15' => true,
'utf-8' => true, 'UTF-8' => true,
'CP866' => true, 'IBM866' => true, '866' => true,
'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
'1251' => true,
'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
'BIG5' => true, '950' => true,
'GB2312' => true, '936' => true,
'BIG5-HKSCS' => true,
'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
'EUC-JP' => true, 'EUCJP' => true,
'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
];
if (isset($htmlspecialcharsCharsets[$charset])) {
return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset);
}
if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
// cache the lowercase variant for future iterations
$htmlspecialcharsCharsets[$charset] = true;
return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset);
}
$string = twig_convert_encoding($string, 'UTF-8', $charset);
$string = htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8');
return iconv('UTF-8', $charset, $string);
case 'js':
// escape all non-alphanumeric characters
// into their \x or \uHHHH representations
if ('UTF-8' !== $charset) {
$string = twig_convert_encoding($string, 'UTF-8', $charset);
}
if (!preg_match('//u', $string)) {
throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
}
$string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', function ($matches) {
$char = $matches[0];
/*
* A few characters have short escape sequences in JSON and JavaScript.
* Escape sequences supported only by JavaScript, not JSON, are omitted.
* \" is also supported but omitted, because the resulting string is not HTML safe.
*/
static $shortMap = [
'\\' => '\\\\',
'/' => '\\/',
"\x08" => '\b',
"\x0C" => '\f',
"\x0A" => '\n',
"\x0D" => '\r',
"\x09" => '\t',
];
if (isset($shortMap[$char])) {
return $shortMap[$char];
}
$codepoint = mb_ord($char, 'UTF-8');
if (0x10000 > $codepoint) {
return sprintf('\u%04X', $codepoint);
}
// Split characters outside the BMP into surrogate pairs
// https://tools.ietf.org/html/rfc2781.html#section-2.1
$u = $codepoint - 0x10000;
$high = 0xD800 | ($u >> 10);
$low = 0xDC00 | ($u & 0x3FF);
return sprintf('\u%04X\u%04X', $high, $low);
}, $string);
if ('UTF-8' !== $charset) {
$string = iconv('UTF-8', $charset, $string);
}
return $string;
case 'css':
if ('UTF-8' !== $charset) {
$string = twig_convert_encoding($string, 'UTF-8', $charset);
}
if (!preg_match('//u', $string)) {
throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
}
$string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) {
$char = $matches[0];
return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : mb_ord($char, 'UTF-8'));
}, $string);
if ('UTF-8' !== $charset) {
$string = iconv('UTF-8', $charset, $string);
}
return $string;
case 'html_attr':
if ('UTF-8' !== $charset) {
$string = twig_convert_encoding($string, 'UTF-8', $charset);
}
if (!preg_match('//u', $string)) {
throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
}
$string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', function ($matches) {
/**
* This function is adapted from code coming from Zend Framework.
*
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://framework.zend.com/license/new-bsd New BSD License
*/
$chr = $matches[0];
$ord = \ord($chr);
/*
* The following replaces characters undefined in HTML with the
* hex entity for the Unicode replacement character.
*/
if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) {
return '&#xFFFD;';
}
/*
* Check if the current character to escape has a name entity we should
* replace it with while grabbing the hex value of the character.
*/
if (1 === \strlen($chr)) {
/*
* While HTML supports far more named entities, the lowest common denominator
* has become HTML5's XML Serialisation which is restricted to the those named
* entities that XML supports. Using HTML entities would result in this error:
* XML Parsing Error: undefined entity
*/
static $entityMap = [
34 => '&quot;', /* quotation mark */
38 => '&amp;', /* ampersand */
60 => '&lt;', /* less-than sign */
62 => '&gt;', /* greater-than sign */
];
if (isset($entityMap[$ord])) {
return $entityMap[$ord];
}
return sprintf('&#x%02X;', $ord);
}
/*
* Per OWASP recommendations, we'll use hex entities for any other
* characters where a named entity does not exist.
*/
return sprintf('&#x%04X;', mb_ord($chr, 'UTF-8'));
}, $string);
if ('UTF-8' !== $charset) {
$string = iconv('UTF-8', $charset, $string);
}
return $string;
case 'url':
return rawurlencode($string);
default:
// check the ones set on CoreExtension for BC (to be removed in 3.0)
$legacyEscapers = $env->getExtension(CoreExtension::class)->getEscapers(false);
if (array_key_exists($strategy, $legacyEscapers)) {
return $legacyEscapers[$strategy]($env, $string, $charset);
}
$escapers = $env->getExtension(EscaperExtension::class)->getEscapers();
if (array_key_exists($strategy, $escapers)) {
return $escapers[$strategy]($env, $string, $charset);
}
$escapers = array_merge($legacyEscapers, $escapers);
$validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers)));
throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
}
}
/**
* @internal
*/
function twig_escape_filter_is_safe(Node $filterArgs)
{
foreach ($filterArgs as $arg) {
if ($arg instanceof ConstantExpression) {
return [$arg->getAttribute('value')];
}
return [];
}
return ['html'];
}
}

View File

@ -11,7 +11,6 @@
namespace Twig\Extension;
use Twig\Environment;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\TokenParserInterface;
use Twig\TwigFilter;
@ -25,15 +24,6 @@ use Twig\TwigTest;
*/
interface ExtensionInterface
{
/**
* Initializes the runtime environment.
*
* This is where you can load some file that contains filter functions for instance.
*
* @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_InitRuntimeInterface instead
*/
public function initRuntime(Environment $environment);
/**
* Returns the token parser instances to add to the existing list.
*
@ -75,24 +65,6 @@ interface ExtensionInterface
* @return array<array> First array of unary operators, second array of binary operators
*/
public function getOperators();
/**
* Returns a list of global variables to add to the existing list.
*
* @return array An array of global variables
*
* @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_GlobalsInterface instead
*/
public function getGlobals();
/**
* Returns the name of the extension.
*
* @return string The extension name
*
* @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
*/
public function getName();
}
class_alias('Twig\Extension\ExtensionInterface', 'Twig_ExtensionInterface');

View File

@ -21,6 +21,12 @@ namespace Twig\Extension;
*/
interface GlobalsInterface
{
/**
* Returns a list of global variables to add to the existing list.
*
* @return array An array of global variables
*/
public function getGlobals();
}
class_alias('Twig\Extension\GlobalsInterface', 'Twig_Extension_GlobalsInterface');

View File

@ -11,6 +11,8 @@
namespace Twig\Extension;
use Twig\Environment;
/**
* Enables usage of the deprecated Twig\Extension\AbstractExtension::initRuntime() method.
*
@ -18,9 +20,17 @@ namespace Twig\Extension;
* deprecated initRuntime() method in your extensions.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since Twig 2.7, to be removed in 3.0
*/
interface InitRuntimeInterface
{
/**
* Initializes the runtime environment.
*
* This is where you can load some file that contains filter functions for instance.
*/
public function initRuntime(Environment $environment);
}
class_alias('Twig\Extension\InitRuntimeInterface', 'Twig_Extension_InitRuntimeInterface');

View File

@ -13,12 +13,9 @@ namespace Twig\Extension;
use Twig\NodeVisitor\OptimizerNodeVisitor;
/**
* @final
*/
class OptimizerExtension extends AbstractExtension
final class OptimizerExtension extends AbstractExtension
{
protected $optimizers;
private $optimizers;
public function __construct($optimizers = -1)
{
@ -29,11 +26,6 @@ class OptimizerExtension extends AbstractExtension
{
return [new OptimizerNodeVisitor($this->optimizers)];
}
public function getName()
{
return 'optimizer';
}
}
class_alias('Twig\Extension\OptimizerExtension', 'Twig_Extension_Optimizer');

View File

@ -41,12 +41,7 @@ class ProfilerExtension extends AbstractExtension
public function getNodeVisitors()
{
return [new ProfilerNodeVisitor(\get_class($this))];
}
public function getName()
{
return 'profiler';
return [new ProfilerNodeVisitor(static::class)];
}
}

View File

@ -12,17 +12,17 @@
namespace Twig\Extension;
use Twig\NodeVisitor\SandboxNodeVisitor;
use Twig\Sandbox\SecurityNotAllowedMethodError;
use Twig\Sandbox\SecurityNotAllowedPropertyError;
use Twig\Sandbox\SecurityPolicyInterface;
use Twig\Source;
use Twig\TokenParser\SandboxTokenParser;
/**
* @final
*/
class SandboxExtension extends AbstractExtension
final class SandboxExtension extends AbstractExtension
{
protected $sandboxedGlobally;
protected $sandboxed;
protected $policy;
private $sandboxedGlobally;
private $sandboxed;
private $policy;
public function __construct(SecurityPolicyInterface $policy, $sandboxed = false)
{
@ -77,33 +77,49 @@ class SandboxExtension extends AbstractExtension
}
}
public function checkMethodAllowed($obj, $method)
public function checkMethodAllowed($obj, $method, int $lineno = -1, Source $source = null)
{
if ($this->isSandboxed()) {
$this->policy->checkMethodAllowed($obj, $method);
try {
$this->policy->checkMethodAllowed($obj, $method);
} catch (SecurityNotAllowedMethodError $e) {
$e->setSourceContext($source);
$e->setTemplateLine($lineno);
throw $e;
}
}
}
public function checkPropertyAllowed($obj, $method)
public function checkPropertyAllowed($obj, $property, int $lineno = -1, Source $source = null)
{
if ($this->isSandboxed()) {
$this->policy->checkPropertyAllowed($obj, $method);
try {
$this->policy->checkPropertyAllowed($obj, $property);
} catch (SecurityNotAllowedPropertyError $e) {
$e->setSourceContext($source);
$e->setTemplateLine($lineno);
throw $e;
}
}
}
public function ensureToStringAllowed($obj)
public function ensureToStringAllowed($obj, int $lineno = -1, Source $source = null)
{
if ($this->isSandboxed() && \is_object($obj) && method_exists($obj, '__toString')) {
$this->policy->checkMethodAllowed($obj, '__toString');
try {
$this->policy->checkMethodAllowed($obj, '__toString');
} catch (SecurityNotAllowedMethodError $e) {
$e->setSourceContext($source);
$e->setTemplateLine($lineno);
throw $e;
}
}
return $obj;
}
public function getName()
{
return 'sandbox';
}
}
class_alias('Twig\Extension\SandboxExtension', 'Twig_Extension_Sandbox');

View File

@ -13,32 +13,32 @@ namespace Twig\Extension;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\TokenParserInterface;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Twig\TwigTest;
/**
* Internal class.
*
* This class is used by \Twig\Environment as a staging area and must not be used directly.
* Used by \Twig\Environment as a staging area.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @internal
*/
class StagingExtension extends AbstractExtension
final class StagingExtension extends AbstractExtension
{
protected $functions = [];
protected $filters = [];
protected $visitors = [];
protected $tokenParsers = [];
protected $globals = [];
protected $tests = [];
private $functions = [];
private $filters = [];
private $visitors = [];
private $tokenParsers = [];
private $tests = [];
public function addFunction($name, $function)
public function addFunction(TwigFunction $function)
{
if (isset($this->functions[$name])) {
@trigger_error(sprintf('Overriding function "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
if (isset($this->functions[$function->getName()])) {
throw new \LogicException(sprintf('Function "%s" is already registered.', $function->getName()));
}
$this->functions[$name] = $function;
$this->functions[$function->getName()] = $function;
}
public function getFunctions()
@ -46,13 +46,13 @@ class StagingExtension extends AbstractExtension
return $this->functions;
}
public function addFilter($name, $filter)
public function addFilter(TwigFilter $filter)
{
if (isset($this->filters[$name])) {
@trigger_error(sprintf('Overriding filter "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
if (isset($this->filters[$filter->getName()])) {
throw new \LogicException(sprintf('Filter "%s" is already registered.', $filter->getName()));
}
$this->filters[$name] = $filter;
$this->filters[$filter->getName()] = $filter;
}
public function getFilters()
@ -73,7 +73,7 @@ class StagingExtension extends AbstractExtension
public function addTokenParser(TokenParserInterface $parser)
{
if (isset($this->tokenParsers[$parser->getTag()])) {
@trigger_error(sprintf('Overriding tag "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $parser->getTag()), E_USER_DEPRECATED);
throw new \LogicException(sprintf('Tag "%s" is already registered.', $parser->getTag()));
}
$this->tokenParsers[$parser->getTag()] = $parser;
@ -84,34 +84,19 @@ class StagingExtension extends AbstractExtension
return $this->tokenParsers;
}
public function addGlobal($name, $value)
public function addTest(TwigTest $test)
{
$this->globals[$name] = $value;
}
public function getGlobals()
{
return $this->globals;
}
public function addTest($name, $test)
{
if (isset($this->tests[$name])) {
@trigger_error(sprintf('Overriding test "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
if (isset($this->tests[$test->getName()])) {
throw new \LogicException(sprintf('Test "%s" is already registered.', $test->getName()));
}
$this->tests[$name] = $test;
$this->tests[$test->getName()] = $test;
}
public function getTests()
{
return $this->tests;
}
public function getName()
{
return 'staging';
}
}
class_alias('Twig\Extension\StagingExtension', 'Twig_Extension_Staging');

View File

@ -12,10 +12,7 @@
namespace Twig\Extension {
use Twig\TwigFunction;
/**
* @final
*/
class StringLoaderExtension extends AbstractExtension
final class StringLoaderExtension extends AbstractExtension
{
public function getFunctions()
{
@ -23,11 +20,6 @@ class StringLoaderExtension extends AbstractExtension
new TwigFunction('template_from_string', 'twig_template_from_string', ['needs_environment' => true]),
];
}
public function getName()
{
return 'string_loader';
}
}
class_alias('Twig\Extension\StringLoaderExtension', 'Twig_Extension_StringLoader');
@ -47,7 +39,7 @@ use Twig\TemplateWrapper;
*
* @return TemplateWrapper
*/
function twig_template_from_string(Environment $env, $template, $name = null)
function twig_template_from_string(Environment $env, $template, string $name = null)
{
return $env->createTemplate((string) $template, $name);
}

View File

@ -0,0 +1,475 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig;
use Twig\Error\RuntimeError;
use Twig\Extension\ExtensionInterface;
use Twig\Extension\GlobalsInterface;
use Twig\Extension\InitRuntimeInterface;
use Twig\Extension\StagingExtension;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\TokenParserInterface;
/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @internal
*/
final class ExtensionSet
{
private $extensions;
private $initialized = false;
private $runtimeInitialized = false;
private $staging;
private $parsers;
private $visitors;
private $filters;
private $tests;
private $functions;
private $unaryOperators;
private $binaryOperators;
private $globals;
private $functionCallbacks = [];
private $filterCallbacks = [];
private $lastModified = 0;
public function __construct()
{
$this->staging = new StagingExtension();
}
/**
* Initializes the runtime environment.
*
* @deprecated since Twig 2.7
*/
public function initRuntime(Environment $env)
{
if ($this->runtimeInitialized) {
return;
}
$this->runtimeInitialized = true;
foreach ($this->extensions as $extension) {
if ($extension instanceof InitRuntimeInterface) {
$extension->initRuntime($env);
}
}
}
public function hasExtension(string $class): bool
{
$class = ltrim($class, '\\');
if (!isset($this->extensions[$class]) && class_exists($class, false)) {
// For BC/FC with namespaced aliases
$class = (new \ReflectionClass($class))->name;
}
return isset($this->extensions[$class]);
}
public function getExtension(string $class): ExtensionInterface
{
$class = ltrim($class, '\\');
if (!isset($this->extensions[$class]) && class_exists($class, false)) {
// For BC/FC with namespaced aliases
$class = (new \ReflectionClass($class))->name;
}
if (!isset($this->extensions[$class])) {
throw new RuntimeError(sprintf('The "%s" extension is not enabled.', $class));
}
return $this->extensions[$class];
}
/**
* @param ExtensionInterface[] $extensions
*/
public function setExtensions(array $extensions)
{
foreach ($extensions as $extension) {
$this->addExtension($extension);
}
}
/**
* @return ExtensionInterface[]
*/
public function getExtensions(): array
{
return $this->extensions;
}
public function getSignature(): string
{
return json_encode(array_keys($this->extensions));
}
public function isInitialized(): bool
{
return $this->initialized || $this->runtimeInitialized;
}
public function getLastModified(): int
{
if (0 !== $this->lastModified) {
return $this->lastModified;
}
foreach ($this->extensions as $extension) {
$r = new \ReflectionObject($extension);
if (file_exists($r->getFileName()) && ($extensionTime = filemtime($r->getFileName())) > $this->lastModified) {
$this->lastModified = $extensionTime;
}
}
return $this->lastModified;
}
public function addExtension(ExtensionInterface $extension)
{
$class = \get_class($extension);
if ($this->initialized) {
throw new \LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $class));
}
if (isset($this->extensions[$class])) {
throw new \LogicException(sprintf('Unable to register extension "%s" as it is already registered.', $class));
}
$this->extensions[$class] = $extension;
}
public function addFunction(TwigFunction $function)
{
if ($this->initialized) {
throw new \LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $function->getName()));
}
$this->staging->addFunction($function);
}
/**
* @return TwigFunction[]
*/
public function getFunctions(): array
{
if (!$this->initialized) {
$this->initExtensions();
}
return $this->functions;
}
/**
* @return TwigFunction|false
*/
public function getFunction(string $name)
{
if (!$this->initialized) {
$this->initExtensions();
}
if (isset($this->functions[$name])) {
return $this->functions[$name];
}
foreach ($this->functions as $pattern => $function) {
$pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
if ($count && preg_match('#^'.$pattern.'$#', $name, $matches)) {
array_shift($matches);
$function->setArguments($matches);
return $function;
}
}
foreach ($this->functionCallbacks as $callback) {
if (false !== $function = $callback($name)) {
return $function;
}
}
return false;
}
public function registerUndefinedFunctionCallback(callable $callable)
{
$this->functionCallbacks[] = $callable;
}
public function addFilter(TwigFilter $filter)
{
if ($this->initialized) {
throw new \LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $filter->getName()));
}
$this->staging->addFilter($filter);
}
/**
* @return TwigFilter[]
*/
public function getFilters(): array
{
if (!$this->initialized) {
$this->initExtensions();
}
return $this->filters;
}
/**
* @return TwigFilter|false
*/
public function getFilter(string $name)
{
if (!$this->initialized) {
$this->initExtensions();
}
if (isset($this->filters[$name])) {
return $this->filters[$name];
}
foreach ($this->filters as $pattern => $filter) {
$pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
if ($count && preg_match('#^'.$pattern.'$#', $name, $matches)) {
array_shift($matches);
$filter->setArguments($matches);
return $filter;
}
}
foreach ($this->filterCallbacks as $callback) {
if (false !== $filter = $callback($name)) {
return $filter;
}
}
return false;
}
public function registerUndefinedFilterCallback(callable $callable)
{
$this->filterCallbacks[] = $callable;
}
public function addNodeVisitor(NodeVisitorInterface $visitor)
{
if ($this->initialized) {
throw new \LogicException('Unable to add a node visitor as extensions have already been initialized.');
}
$this->staging->addNodeVisitor($visitor);
}
/**
* @return NodeVisitorInterface[]
*/
public function getNodeVisitors(): array
{
if (!$this->initialized) {
$this->initExtensions();
}
return $this->visitors;
}
public function addTokenParser(TokenParserInterface $parser)
{
if ($this->initialized) {
throw new \LogicException('Unable to add a token parser as extensions have already been initialized.');
}
$this->staging->addTokenParser($parser);
}
/**
* @return TokenParserInterface[]
*/
public function getTokenParsers(): array
{
if (!$this->initialized) {
$this->initExtensions();
}
return $this->parsers;
}
public function getGlobals(): array
{
if (null !== $this->globals) {
return $this->globals;
}
$globals = [];
foreach ($this->extensions as $extension) {
if (!$extension instanceof GlobalsInterface) {
continue;
}
$extGlobals = $extension->getGlobals();
if (!\is_array($extGlobals)) {
throw new \UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', \get_class($extension)));
}
$globals = array_merge($globals, $extGlobals);
}
if ($this->initialized) {
$this->globals = $globals;
}
return $globals;
}
public function addTest(TwigTest $test)
{
if ($this->initialized) {
throw new \LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $test->getName()));
}
$this->staging->addTest($test);
}
/**
* @return TwigTest[]
*/
public function getTests(): array
{
if (!$this->initialized) {
$this->initExtensions();
}
return $this->tests;
}
/**
* @return TwigTest|false
*/
public function getTest(string $name)
{
if (!$this->initialized) {
$this->initExtensions();
}
if (isset($this->tests[$name])) {
return $this->tests[$name];
}
foreach ($this->tests as $pattern => $test) {
$pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
if ($count) {
if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
array_shift($matches);
$test->setArguments($matches);
return $test;
}
}
}
return false;
}
public function getUnaryOperators(): array
{
if (!$this->initialized) {
$this->initExtensions();
}
return $this->unaryOperators;
}
public function getBinaryOperators(): array
{
if (!$this->initialized) {
$this->initExtensions();
}
return $this->binaryOperators;
}
private function initExtensions()
{
$this->parsers = [];
$this->filters = [];
$this->functions = [];
$this->tests = [];
$this->visitors = [];
$this->unaryOperators = [];
$this->binaryOperators = [];
foreach ($this->extensions as $extension) {
$this->initExtension($extension);
}
$this->initExtension($this->staging);
// Done at the end only, so that an exception during initialization does not mark the environment as initialized when catching the exception
$this->initialized = true;
}
private function initExtension(ExtensionInterface $extension)
{
// filters
foreach ($extension->getFilters() as $filter) {
$this->filters[$filter->getName()] = $filter;
}
// functions
foreach ($extension->getFunctions() as $function) {
$this->functions[$function->getName()] = $function;
}
// tests
foreach ($extension->getTests() as $test) {
$this->tests[$test->getName()] = $test;
}
// token parsers
foreach ($extension->getTokenParsers() as $parser) {
if (!$parser instanceof TokenParserInterface) {
throw new \LogicException('getTokenParsers() must return an array of \Twig\TokenParser\TokenParserInterface.');
}
$this->parsers[] = $parser;
}
// node visitors
foreach ($extension->getNodeVisitors() as $visitor) {
$this->visitors[] = $visitor;
}
// operators
if ($operators = $extension->getOperators()) {
if (!\is_array($operators)) {
throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array with operators, got "%s".', \get_class($extension), \is_object($operators) ? \get_class($operators) : \gettype($operators).(\is_resource($operators) ? '' : '#'.$operators)));
}
if (2 !== \count($operators)) {
throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', \get_class($extension), \count($operators)));
}
$this->unaryOperators = array_merge($this->unaryOperators, $operators[0]);
$this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
}
}
}
class_alias('Twig\ExtensionSet', 'Twig_ExtensionSet');

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