Compare commits

...

380 Commits

Author SHA1 Message Date
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
645 changed files with 11022 additions and 6778 deletions

1
.gitattributes vendored
View File

@ -8,3 +8,4 @@ _config.yml export-ignore
release.sh export-ignore release.sh export-ignore
*.sh text eol=lf *.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"

41
.gitignore vendored
View File

@ -1,11 +1,28 @@
Thumbs.db Thumbs.db
.DS_Store .DS_Store
.idea .idea
#
/.htaccess
# 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 tmp
releases
config.local.php config.local.php
PERSONAL_NOTES
# all custom templates # all custom templates
templates/* templates/*
@ -16,6 +33,16 @@ templates/*
images/guilds/* images/guilds/*
!images/guilds/default.gif !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 # cache
system/cache/* system/cache/*
!system/cache/index.html !system/cache/index.html
@ -23,16 +50,26 @@ system/cache/*
!system/cache/signatures/index.html !system/cache/signatures/index.html
!system/cache/plugins/index.html !system/cache/plugins/index.html
# php sessions
system/php_sessions/*
!system/php_sessions/index.html
# logs # logs
system/logs/* system/logs/*
!system/logs/index.html !system/logs/index.html
# data
system/data/*
!system/data/index.html
# plugins # plugins
plugins/* plugins/*
!plugins/.htaccess !plugins/.htaccess
!plugins/example.json !plugins/example.json
!plugins/account-create-hint.json !plugins/account-create-hint.json
!plugins/account-create-hint !plugins/account-create-hint
!plugins/email-confirmed-reward.json
!plugins/email-confirmed-reward
landing landing
# others/rest # others/rest

View File

@ -7,13 +7,14 @@ php:
- 7.2 - 7.2
- 7.3 - 7.3
- 7.4 - 7.4
- 8.0
cache: cache:
directories: directories:
- $HOME/.composer/cache - $HOME/.composer/cache
before_script: 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: 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,311 @@
# Changelog # Changelog
## [0.8.2 - x.x.2020] ## [0.8.19 - 19.11.2024]
### Added ### 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 * $_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 ### Changed
* account_login input type from password to text * account_login input type from password to text
### Fixed ### 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 * 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 * Show character indicator in check_name.js
* Houses list View button * Houses list View button was wrong (was from bootstrap)
* Fix OTS_House houseid parameter * 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] ## [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) * Gesior.pl (2007 - 2008)
* Slawkens (2009 - 2020) * Slawkens (2009 - 2023)
* Contributors listed in CONTRIBUTORS.txt

View File

@ -1,18 +1,31 @@
# 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. 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 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 |
|:--------|:-----------------------|:--------|:---------------|
| **1.x** | **Active development** | develop | **PHP >= 8.1** |
| 0.9.x | Not developed anymore | 0.9 | PHP >= 7.2.5 |
| 0.8.x | Active support | master | PHP >= 7.2.5 |
| 0.7.x | End Of Life | 0.7 | PHP >= 5.3.3 |
### Requirements
- PHP 5.5 or later
- MySQL database - MySQL database
- PDO PHP Extension - PHP Extensions: pdo, xml, json
- XML PHP Extension - (optional) apache2 mod_rewrite (to use friendly_urls)
- ZIP PHP Extension - (optional) zip PHP Extension (to install plugins)
- (optional) mod_rewrite to use friendly_urls - (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, Just decompress and untar the source (which you should have done by now,
if you're reading this), into your webserver's document root. if you're reading this), into your webserver's document root.
@ -28,19 +41,51 @@ Official website: https://my-aac.org
chmod 660 images/guilds chmod 660 images/guilds
chmod 660 images/houses chmod 660 images/houses
chmod 660 images/gallery 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. Visit http://your_domain/install (http://localhost/install) and follow instructions in the browser.
### KNOWN PROBLEMS ### Configuration
- none - Check *config.php* to get more informations. (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-Cheetsheet](https://danielkummer.github.io/git-flow-cheatsheet)
This program and all associated files are released under the GNU Public That means, we use:
License, see LICENSE for details. * master branch, for current stable release
* develop branch, for development version (next release)
* feature branches, for features etc.
### Known Problems
- Some compatibility issues with some exotical distibutions.
### 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 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 // few things we'll need
require '../common.php'; require '../common.php';
define('ADMIN_PANEL', true);
define('MYAAC_ADMIN', true);
if(file_exists(BASE . 'config.local.php')) { if(file_exists(BASE . 'config.local.php')) {
require_once 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!'); 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 = ''; $content = '';
// validate page // validate page
@ -27,6 +28,12 @@ define('PAGE', $page);
require SYSTEM . 'functions.php'; require SYSTEM . 'functions.php';
require SYSTEM . 'init.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 // event system
require_once SYSTEM . 'hooks.php'; require_once SYSTEM . 'hooks.php';
$hooks = new Hooks(); $hooks = new Hooks();
@ -34,6 +41,7 @@ $hooks->load();
require SYSTEM . 'status.php'; require SYSTEM . 'status.php';
require SYSTEM . 'login.php'; require SYSTEM . 'login.php';
require SYSTEM . 'migrate.php';
require ADMIN . 'includes/functions.php'; require ADMIN . 'includes/functions.php';
$twig->addGlobal('config', $config); $twig->addGlobal('config', $config);
@ -45,7 +53,7 @@ if(!$logged || !admin()) {
} }
// include our page // include our page
$file = SYSTEM . 'pages/admin/' . $page . '.php'; $file = ADMIN . 'pages/' . $page . '.php';
if(!@file_exists($file)) { if(!@file_exists($file)) {
$page = '404'; $page = '404';
$file = SYSTEM . 'pages/404.php'; $file = SYSTEM . 'pages/404.php';
@ -60,4 +68,4 @@ ob_end_clean();
// template // template
$template_path = 'template/'; $template_path = 'template/';
require ADMIN . $template_path . 'template.php'; require ADMIN . $template_path . 'template.php';
?>

View File

@ -182,7 +182,7 @@ if ($id > 0) {
} }
$lastDay = 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(); $lastDay = time();
} else if ($lastDay != 0) { } else if ($lastDay != 0) {
$lastDay = 0; $lastDay = 0;
@ -279,7 +279,13 @@ else if ($id > 0 && isset($account) && $account->isLoaded()) {
<?php <?php
$acc_group = $account->getAccGroupId(); $acc_group = $account->getAccGroupId();
if ($hasTypeColumn) { 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"> <div class="col-xs-6">
<label for="group" class="control-label">Account Type:</label> <label for="group" class="control-label">Account Type:</label>
<select name="group" id="group" class="form-control"> <select name="group" id="group" class="form-control">
@ -420,7 +426,7 @@ else if ($id > 0 && isset($account) && $account->isLoaded()) {
<div class="box-body"> <div class="box-body">
<form action="<?php echo $base; ?>" method="post"> <form action="<?php echo $base; ?>" method="post">
<div class="input-group input-group-sm"> <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"> maxlength="32" size="32">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="submit" type="button" class="btn btn-info btn-flat">Search</button> <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!'); defined('MYAAC') or die('Direct access not allowed!');
$title = 'Load items.xml'; $title = 'Load items.xml';
require LIBS . 'items.php'; require_once LIBS . 'items.php';
require LIBS . 'weapons.php'; require_once LIBS . 'weapons.php';
$twig->display('admin.items.html.twig'); $twig->display('admin.items.html.twig');

View File

@ -89,7 +89,7 @@ if (isset($_REQUEST['template'])) {
if (isset($menus[$id])) { if (isset($menus[$id])) {
$i = 0; $i = 0;
foreach ($menus[$id] as $menu) { 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'] . '"/> <label>Link:</label><input type="text" name="menu_link[' . $id . '][]" value="' . $menu['link'] . '"/>
<input type="hidden" name="menu_blank[' . $id . '][]" value="0" /> <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> <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( $twig->display('admin.menus.form.html.twig', array(
'templates' => $templates 'templates' => $templates
)); ));
} }

View File

@ -117,7 +117,7 @@ if($action == 'edit' || $action == 'new') {
'news_link_form' => '?p=news&action=' . ($action == 'edit' ? 'edit' : 'add'), 'news_link_form' => '?p=news&action=' . ($action == 'edit' ? 'edit' : 'add'),
'news_id' => isset($id) ? $id : null, 'news_id' => isset($id) ? $id : null,
'title' => isset($p_title) ? $p_title : '', '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, 'type' => isset($type) ? $type : null,
'player' => isset($player) && $player->isLoaded() ? $player : null, 'player' => isset($player) && $player->isLoaded() ? $player : null,
'player_id' => isset($player_id) ? $player_id : 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, 'title' => $p_title,
'php' => $php, 'php' => $php,
'enable_tinymce' => $enable_tinymce, 'enable_tinymce' => $enable_tinymce,
'body' => isset($body) ? htmlentities($body, ENT_COMPAT, 'UTF-8') : '', 'body' => isset($body) ? escapeHtml($body) : '',
'groups' => $groups->getGroups(), 'groups' => $groups->getGroups(),
'access' => $access 'access' => $access
)); ));
@ -196,5 +196,3 @@ class Pages
return !count($errors); return !count($errors);
} }
} }
?>

View File

@ -210,7 +210,7 @@ if ($id > 0) {
if ($hasBlessingsColumn) { if ($hasBlessingsColumn) {
$blessings = $_POST['blessings']; $blessings = $_POST['blessings'];
verify_number($blessings, 'Blessings', 2); verify_number($blessings, 'Blessings', 3);
} }
$balance = $_POST['balance']; $balance = $_POST['balance'];
@ -650,7 +650,7 @@ else if ($id > 0 && isset($player) && $player->isLoaded())
<label for="look_feet" class="control-label">Feet: <span <label for="look_feet" class="control-label">Feet: <span
id="look_feet_val"></span></label> id="look_feet_val"></span></label>
<input type="range" min="0" max="132" <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"> class="slider form-control" id="look_feet" name="look_feet">
</div> </div>
</div> </div>
@ -697,7 +697,14 @@ else if ($id > 0 && isset($player) && $player->isLoaded())
<label for="lastip" class="control-label">Last IP:</label> <label for="lastip" class="control-label">Last IP:</label>
<input type="text" class="form-control" id="lastip" name="lastip" <input type="text" class="form-control" id="lastip" name="lastip"
autocomplete="off" 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/> readonly/>
</div> </div>
</div> </div>
@ -777,7 +784,7 @@ else if ($id > 0 && isset($player) && $player->isLoaded())
<div class="box-body"> <div class="box-body">
<form action="<?php echo $base; ?>" method="post"> <form action="<?php echo $base; ?>" method="post">
<div class="input-group input-group-sm"> <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"> maxlength="32" size="32">
<span class="input-group-btn"> <span class="input-group-btn">
<button type="submit" type="button" class="btn btn-info btn-flat">Search</button> <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_feet = $('#look_feet').val();
var look_type = $('#look_type').val(); var look_type = $('#look_type').val();
var look_addons = '';
<?php if($hasLookAddons): ?> <?php if($hasLookAddons): ?>
var look_addons = '&addons=' + $('#look_addons').val(); look_addons = '&addons=' + $('#look_addons').val();
<?php
else: ?>
var look_addons = '';
<?php endif; ?> <?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; 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); $("#player_outfit").attr("src", new_outfit);
console.log(new_outfit); console.log(new_outfit);
} }
</script> </script>

View File

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

View File

@ -36,4 +36,3 @@ $twig->display('admin.statistics.html.twig', array(
'account_type' => (USE_ACCOUNT_NAME ? 'name' : 'number'), 'account_type' => (USE_ACCOUNT_NAME ? 'name' : 'number'),
'points' => $points '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. success('MyAAC latest version is ' . $myaac_version . '. You\'re using the latest version.
<br/>View CHANGELOG ' . generateLink(ADMIN_URL . '?p=changelog', 'here')); <br/>View CHANGELOG ' . generateLink(ADMIN_URL . '?p=changelog', 'here'));
} else if ($version_compare < 0) { } 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')); <br/>View CHANGELOG ' . generateLink(ADMIN_URL . '?p=changelog', 'here'));
} else { } else {
warning('You\'re using outdated version.<br/> warning('You\'re using outdated version.<br/>
@ -47,4 +47,3 @@ function version_revert($version)
$release = $version; $release = $version;
return $major . '.' . $minor . '.' . $release; return $major . '.' . $minor . '.' . $release;
}*/ }*/
?>

View File

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

View File

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

View File

@ -23,15 +23,15 @@
* @copyright 2019 MyAAC * @copyright 2019 MyAAC
* @link https://my-aac.org * @link https://my-aac.org
*/ */
if (version_compare(phpversion(), '5.5', '<')) die('PHP version 5.5 or higher is required.'); if (version_compare(phpversion(), '7.2.5', '<')) die('PHP version 7.2.5 or higher is required.');
session_start();
define('MYAAC', true); define('MYAAC', true);
define('MYAAC_VERSION', '0.8.2-dev'); define('MYAAC_VERSION', '0.8.19');
define('DATABASE_VERSION', 30); define('DATABASE_VERSION', 33);
define('TABLE_PREFIX', 'myaac_'); define('TABLE_PREFIX', 'myaac_');
define('START_TIME', microtime(true)); define('START_TIME', microtime(true));
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX')); 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 // account flags
define('FLAG_ADMIN', 1); define('FLAG_ADMIN', 1);
@ -85,6 +85,11 @@ define('TFS_03', 4);
define('TFS_FIRST', TFS_02); define('TFS_FIRST', TFS_02);
define('TFS_LAST', TFS_03); define('TFS_LAST', TFS_03);
if (!IS_CLI) {
session_save_path(SYSTEM . 'php_sessions');
session_start();
}
// basedir // basedir
$basedir = ''; $basedir = '';
$tmp = explode('/', $_SERVER['SCRIPT_NAME']); $tmp = explode('/', $_SERVER['SCRIPT_NAME']);
@ -92,26 +97,39 @@ $size = count($tmp) - 1;
for($i = 1; $i < $size; $i++) for($i = 1; $i < $size; $i++)
$basedir .= '/' . $tmp[$i]; $basedir .= '/' . $tmp[$i];
$basedir = str_replace(array('/admin', '/install'), '', $basedir); $basedir = str_replace(array('/admin', '/install', '/tools'), '', $basedir);
define('BASE_DIR', $basedir); define('BASE_DIR', $basedir);
if(isset($_SERVER['HTTP_HOST'][0])) { if (file_exists(BASE . 'config.local.php') && !defined('MYAAC_INSTALL')) {
$baseHost = $_SERVER['HTTP_HOST']; require BASE . 'config.local.php';
}
else {
if(isset($_SERVER['SERVER_NAME'][0])) {
$baseHost = $_SERVER['SERVER_NAME'];
}
else {
$baseHost = $_SERVER['SERVER_ADDR'];
}
} }
define('SERVER_URL', 'http' . (isset($_SERVER['HTTPS'][0]) && strtolower($_SERVER['HTTPS']) === 'on' ? 's' : '') . '://' . $baseHost); if(!IS_CLI) {
define('BASE_URL', SERVER_URL . BASE_DIR . '/'); if (isset($_SERVER['HTTP_HOST'][0])) {
define('ADMIN_URL', SERVER_URL . BASE_DIR . '/admin/'); $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'; 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_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_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_persistent' => false, // use database permanent connection (like server), may speed up your site
'database_encryption' => 'sha1',
// multiworld system (only TFS 0.3) // multiworld system (only TFS 0.3)
'multiworld' => false, // use multiworld system? 'multiworld' => false, // use multiworld system?
@ -86,14 +87,21 @@ $config = array(
), ),
// images // images
'outfit_images_url' => 'http://outfit-images.ots.me/outfit.php', // set to animoutfit.php for animated outfit 'outfit_images_url' => 'https://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 'item_images_url' => 'https://item-images.ots.me/1092/', // set to images/items if you host your own items in images folder
// account // account
'account_management' => true, // disable if you're using other method to manage users (fe. tfs account manager) '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_auto_login' => false, // auto login after creating account?
'account_create_character_create' => true, // allow directly to create character on create account page? '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_mail_unique' => true, // email addresses cannot be duplicated? (one account = one email)
'account_premium_days' => 0, // default premium days on new account 'account_premium_days' => 0, // default premium days on new account
'account_premium_points' => 0, // default premium points on new account 'account_premium_points' => 0, // default premium points on new account
@ -151,12 +159,17 @@ $config = array(
4 => 'Knight Sample' 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 // town list used when creating character
// won't be displayed if there is only one item (rookgaard for example) // won't be displayed if there is only one item (rookgaard for example)
'character_towns' => array(1), 'character_towns' => array(1),
// characters lenght // characters length
// This is the minimum and the maximum length that a player can create a character. It is highly recommend the maximum lenght be 21. // 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_min_length' => 4,
'character_name_max_length' => 21, 'character_name_max_length' => 21,
@ -221,7 +234,10 @@ $config = array(
'frags' => false, '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]" '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_enabled' => true,
'signature_type' => 'tibian', // signature engine to use: tibian, mango, gesior '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 '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 'last_kills_limit' => 50, // max. number of deaths shown on the last kills page
// status, took automatically from config file if empty // 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_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) // 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 // 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 '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 '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

@ -38,7 +38,7 @@ else
$uri = str_replace(array('index.php/', '?'), '', $uri); $uri = str_replace(array('index.php/', '?'), '', $uri);
define('URI', $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); $tmp = explode('.', $uri);
$_REQUEST['name'] = urldecode($tmp[0]); $_REQUEST['name'] = urldecode($tmp[0]);
@ -48,7 +48,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'])) { 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; exit;
} }
@ -56,11 +56,17 @@ if(file_exists(BASE . 'config.local.php')) {
require_once BASE . 'config.local.php'; require_once BASE . 'config.local.php';
} }
ini_set('log_errors', 1);
if(config('env') === 'dev') { if(config('env') === 'dev') {
ini_set('display_errors', 1); ini_set('display_errors', 1);
ini_set('display_startup_errors', 1); ini_set('display_startup_errors', 1);
error_reporting(E_ALL); 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')) if((!isset($config['installed']) || !$config['installed']) && file_exists(BASE . 'install'))
{ {
@ -68,6 +74,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!'); 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; $found = false;
if(empty($uri) || isset($_REQUEST['template'])) { if(empty($uri) || isset($_REQUEST['template'])) {
$_REQUEST['p'] = 'news'; $_REQUEST['p'] = 'news';
@ -75,7 +89,11 @@ if(empty($uri) || isset($_REQUEST['template'])) {
} }
else { else {
$tmp = strtolower($uri); $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; $_REQUEST['p'] = $uri;
$found = true; $found = true;
} }
@ -127,13 +145,13 @@ else {
'/^houses\/view\/?$/' => array('subtopic' => 'houses', 'page' => 'view') '/^houses\/view\/?$/' => array('subtopic' => 'houses', 'page' => 'view')
); );
foreach($rules as $rule => $redirect) { foreach ($rules as $rule => $redirect) {
if (preg_match($rule, $uri)) { if (preg_match($rule, $uri)) {
$tmp = explode('/', $uri); $tmp = explode('/', $uri);
/* @var $redirect array */ /* @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); $value = str_replace('$' . $value[1], $tmp[$value[1]], $value);
} }
@ -148,6 +166,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 // define page visited, so it can be used within events system
$page = isset($_REQUEST['subtopic']) ? $_REQUEST['subtopic'] : (isset($_REQUEST['p']) ? $_REQUEST['p'] : ''); $page = isset($_REQUEST['subtopic']) ? $_REQUEST['subtopic'] : (isset($_REQUEST['p']) ? $_REQUEST['p'] : '');
if(empty($page) || !preg_match('/^[A-z0-9\_\-]+$/', $page)) { if(empty($page) || !preg_match('/^[A-z0-9\_\-]+$/', $page)) {
@ -168,44 +192,17 @@ define('PAGE', $page);
$template_place_holders = array(); $template_place_holders = array();
require_once SYSTEM . 'init.php';
// event system // event system
require_once SYSTEM . 'hooks.php'; require_once SYSTEM . 'hooks.php';
$hooks = new Hooks(); $hooks = new Hooks();
$hooks->load(); $hooks->load();
require_once SYSTEM . 'template.php';
require_once SYSTEM . 'login.php'; require_once SYSTEM . 'login.php';
require_once SYSTEM . 'status.php'; require_once SYSTEM . 'status.php';
$twig->addGlobal('config', $config); $twig->addGlobal('config', $config);
$twig->addGlobal('status', $status); $twig->addGlobal('status', $status);
// verify myaac tables exists in database require SYSTEM . 'migrate.php';
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);
}
}
$hooks->trigger(HOOK_STARTUP); $hooks->trigger(HOOK_STARTUP);
@ -301,6 +298,7 @@ if($config['backward_support']) {
$config['site'] = &$config; $config['site'] = &$config;
$config['server'] = &$config['lua']; $config['server'] = &$config['lua'];
$config['site']['shop_system'] = $config['gifts_system']; $config['site']['shop_system'] = $config['gifts_system'];
$config['site']['gallery_page'] = true;
if(!isset($config['vdarkborder'])) if(!isset($config['vdarkborder']))
$config['vdarkborder'] = '#505050'; $config['vdarkborder'] = '#505050';
@ -325,8 +323,10 @@ if($load_it)
if(SITE_CLOSED && admin()) if(SITE_CLOSED && admin())
$content .= '<p class="note">Site is under maintenance (closed mode). Only privileged users can see it.</p>'; $content .= '<p class="note">Site is under maintenance (closed mode). Only privileged users can see it.</p>';
if($config['backward_support']) if($config['backward_support']) {
require SYSTEM . 'compat_pages.php'; require SYSTEM . 'compat/pages.php';
require SYSTEM . 'compat/classes.php';
}
$ignore = false; $ignore = false;
@ -346,11 +346,13 @@ if($load_it)
)) . $content; )) . $content;
} }
} else { } else {
$file = SYSTEM . 'pages/' . $page . '.php'; $file = TEMPLATES . "$template_name/pages/$page.php";
if(!@file_exists($file)) if(!@file_exists($file) || preg_match('/[^A-z0-9_\-]/', $page)) {
{ $file = SYSTEM . "pages/$page.php";
$page = '404'; if(!@file_exists($file) || preg_match('/[^A-z0-9_\-]/', $page)) {
$file = SYSTEM . 'pages/404.php'; $page = '404';
$file = SYSTEM . 'pages/404.php';
}
} }
} }

View File

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

View File

@ -1,3 +1,5 @@
SET @myaac_database_version = 33;
CREATE TABLE `myaac_account_actions` CREATE TABLE `myaac_account_actions`
( (
`account_id` INT(11) NOT NULL, `account_id` INT(11) NOT NULL,
@ -57,6 +59,8 @@ CREATE TABLE `myaac_config`
UNIQUE (`name`) UNIQUE (`name`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
INSERT INTO `myaac_config` (`name`, `value`) VALUES ('database_version', @myaac_database_version);
CREATE TABLE `myaac_faq` CREATE TABLE `myaac_faq`
( (
`id` INT(11) NOT NULL AUTO_INCREMENT, `id` INT(11) NOT NULL AUTO_INCREMENT,
@ -320,9 +324,9 @@ CREATE TABLE `myaac_spells`
CREATE TABLE `myaac_visitors` CREATE TABLE `myaac_visitors`
( (
`ip` VARCHAR(16) NOT NULL, `ip` VARCHAR(45) NOT NULL,
`lastvisit` INT(11) NOT NULL DEFAULT 0, `lastvisit` INT(11) NOT NULL DEFAULT 0,
`page` VARCHAR(100) NOT NULL, `page` VARCHAR(2048) NOT NULL,
UNIQUE (`ip`) UNIQUE (`ip`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; ) 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"> <style type="text/css">
.console { .console {
font-family:Courier; font-family: Courier,serif;
color: #CCCCCC; color: #CCCCCC;
background: #000000; background: #000000;
border: 3px double #CCCCCC; border: 3px double #CCCCCC;
padding: 0px; padding: 0;
} }
</style> </style>

View File

@ -70,7 +70,7 @@ if($step == 'database') {
$key = str_replace('var_', '', $key); $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; continue;
} }
@ -114,14 +114,12 @@ if($step == 'database') {
} }
} }
else if($step == 'admin') { else if($step == 'admin') {
$config_failed = true; if(!file_exists(BASE . 'config.local.php') || !isset($config['installed']) || !$config['installed']) {
if(file_exists(BASE . 'config.local.php') && isset($config['installed']) && $config['installed'] && isset($_SESSION['saved'])) {
$config_failed = false;
}
if($config_failed) {
$step = 'database'; $step = 'database';
} }
else {
$_SESSION['saved'] = true;
}
} }
else if($step == 'finish') { else if($step == 'finish') {
$email = $_SESSION['var_email']; $email = $_SESSION['var_email'];
@ -191,14 +189,14 @@ clearstatcache();
if(is_writable(CACHE) && (MYAAC_OS != 'WINDOWS' || win_is_writable(CACHE))) { if(is_writable(CACHE) && (MYAAC_OS != 'WINDOWS' || win_is_writable(CACHE))) {
if(!file_exists(BASE . 'install/ip.txt')) { 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/> $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 { else {
$file_content = trim(file_get_contents(BASE . 'install/ip.txt')); $file_content = trim(file_get_contents(BASE . 'install/ip.txt'));
$allow = false; $allow = false;
$listIP = preg_split('/\s+/', $file_content); $listIP = preg_split('/\s+/', $file_content);
foreach($listIP as $ip) { foreach($listIP as $ip) {
if($_SERVER['REMOTE_ADDR'] == $ip) { if(get_browser_real_ip() == $ip) {
$allow = true; $allow = true;
} }
} }

View File

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

View File

@ -1,6 +1,10 @@
<?php <?php
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
// configuration
$extensions_required = [
'pdo', 'pdo_mysql', 'xml', 'zip'
];
/* /*
* *
* @param string $name * @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'); $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('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']); foreach ($extensions_required as $ext) {
version_check(str_replace('$EXTENSION$', 'XML', $locale['step_requirements_extension']), extension_loaded('xml'), extension_loaded('xml') ? $locale['loaded'] : $locale['not_loaded']); $loaded = extension_loaded($ext);
version_check(str_replace('$EXTENSION$', 'ZIP', $locale['step_requirements_extension']), extension_loaded('zip'), extension_loaded('zip') ? $locale['loaded'] : $locale['not_loaded']); version_check(str_replace('$EXTENSION$', strtoupper($ext), $locale['step_requirements_extension']) , $loaded, $loaded ? $locale['loaded'] : $locale['not_loaded']);
}
if($failed) if($failed)
{ {

View File

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

View File

@ -57,16 +57,35 @@ if(!$error) {
error($database_error); error($database_error);
} }
else { else {
$twig->display('install.installer.html.twig', array( if(!$db->hasTable('accounts')) {
'url' => 'tools/5-database.php', $tmp = str_replace('$TABLE$', 'accounts', $locale['step_database_error_table']);
'message' => $locale['loading_spinner'] 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) { 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'])) { if(!Validator::email($_SESSION['var_mail_admin'])) {
error($locale['step_config_mail_admin_error']); error($locale['step_config_mail_admin_error']);
$error = true; $error = true;
} }
if(!Validator::email($_SESSION['var_mail_address'])) { if(!Validator::email($_SESSION['var_mail_address'])) {
error($locale['step_config_mail_address_error']); error($locale['step_config_mail_address_error']);
$error = true; $error = true;
@ -82,6 +101,7 @@ if(!$error) {
} }
if($saved) { if($saved) {
success($locale['step_database_config_saved']);
if(!$error) { if(!$error) {
$_SESSION['saved'] = true; $_SESSION['saved'] = true;
} }
@ -91,7 +111,7 @@ if(!$error) {
unset($_SESSION['saved']); unset($_SESSION['saved']);
$locale['step_database_error_file'] = str_replace('$FILE$', '<b>' . BASE . 'config.local.php</b>', $locale['step_database_error_file']); $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>'); <textarea cols="70" rows="10">' . $content . '</textarea>');
} }
} }
@ -102,6 +122,6 @@ if(!$error) {
<form action="<?php echo BASE_URL; ?>install/" method="post"> <form action="<?php echo BASE_URL; ?>install/" method="post">
<input type="hidden" name="step" id="step" value="admin" /> <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->setPassword(encrypt($password));
$new_account->setEMail($email); $new_account->setEMail($email);
$new_account->unblock();
$new_account->save(); $new_account->save();
$new_account->setCustomField('created', time()); $new_account->setCustomField('created', time());
@ -80,10 +79,12 @@ else {
$account_used->setCustomField('web_flags', FLAG_ADMIN + FLAG_SUPER_ADMIN); $account_used->setCustomField('web_flags', FLAG_ADMIN + FLAG_SUPER_ADMIN);
$account_used->setCustomField('country', 'us'); $account_used->setCustomField('country', 'us');
$account_used->setCustomField('email_verified', 1);
if($db->hasColumn('accounts', 'group_id')) if($db->hasColumn('accounts', 'group_id'))
$account_used->setCustomField('group_id', $groups->getHighestId()); $account_used->setCustomField('group_id', $groups->getHighestId());
if($db->hasColumn('accounts', 'type')) if($db->hasColumn('accounts', 'type'))
$account_used->setCustomField('type', 5); $account_used->setCustomField('type', 6);
if(!$player_db->isLoaded()) if(!$player_db->isLoaded())
$player->setAccountId($account_used->getId()); $player->setAccountId($account_used->getId());

View File

@ -1,4 +1,6 @@
<?php <?php
define('MYAAC_INSTALL', true);
require_once '../../common.php'; require_once '../../common.php';
require SYSTEM . 'functions.php'; require SYSTEM . 'functions.php';
@ -9,8 +11,10 @@ $error = false;
require BASE . 'install/includes/config.php'; require BASE . 'install/includes/config.php';
ini_set('max_execution_time', 300); ini_set('max_execution_time', 300);
@ob_end_flush();
ob_implicit_flush(); ob_implicit_flush();
ob_end_flush();
header('X-Accel-Buffering: no'); header('X-Accel-Buffering: no');
if(!$error) { 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')) { 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']); $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']); warning($locale['step_database_error_table_exist']);
@ -48,7 +34,6 @@ else {
try { try {
$db->query(file_get_contents(BASE . 'install/includes/schema.sql')); $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']); $locale['step_database_success_schema'] = str_replace('$PREFIX$', TABLE_PREFIX, $locale['step_database_success_schema']);
success($locale['step_database_success_schema']); success($locale['step_database_success_schema']);
} }
@ -72,13 +57,8 @@ else {
success($locale['step_database_adding_field'] . ' accounts.key...'); 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(!$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...'); 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...'); success($locale['step_database_adding_field'] . ' z_forum.closed...');
} }
} }
} }

View File

@ -1,4 +1,6 @@
<?php <?php
define('MYAAC_INSTALL', true);
require_once '../../common.php'; require_once '../../common.php';
require SYSTEM . 'functions.php'; require SYSTEM . 'functions.php';
@ -6,8 +8,10 @@ require BASE . 'install/includes/functions.php';
require BASE . 'install/includes/locale.php'; require BASE . 'install/includes/locale.php';
ini_set('max_execution_time', 300); ini_set('max_execution_time', 300);
@ob_end_flush();
ob_implicit_flush(); ob_implicit_flush();
ob_end_flush();
header('X-Accel-Buffering: no'); header('X-Accel-Buffering: no');
if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['saved'])) { if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['saved'])) {
@ -34,10 +38,10 @@ function insert_sample_if_not_exist($p) {
$success = true; $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' => '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' => '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' => 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' => 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' => 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' => 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' => 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' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
if($success) { if($success) {
success($locale['step_database_imported_players']); success($locale['step_database_imported_players']);
@ -91,6 +95,7 @@ require_once SYSTEM . 'migrations/22.php';
// add myaac_pages pages // add myaac_pages pages
require_once SYSTEM . 'migrations/27.php'; 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('$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']); $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,40 @@
server { server {
listen 80; listen 80;
root /home/otserv/www/public; root /home/otserv/www/public;
index index.php; index index.php;
server_name your-domain.com; server_name your-domain.com;
location / { # increase max file upload
try_files $uri $uri/ /index.php; client_max_body_size 10M;
}
location ~ \.php$ { # this is very important, be sure its in your nginx conf - it prevents access to logs etc.
include snippets/fastcgi-php.conf; location ~ /system {
fastcgi_read_timeout 240; deny all;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; }
}
location ~ /\.ht { location /vendor {
deny all; deny all;
} }
location /system { # block .htaccess, CHANGELOG.md, composer.json etc.
deny all; # this is to prevent finding software versions
return 404; location ~\.(ht|md|json|dist)$ {
} deny all;
}
# 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> <IfModule mod_autoindex.c>
Options -Indexes Options -Indexes
</IfModule> </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 if [ $1 = "prepare" ]; then
# define release version # define release version
version=`cat VERSION` version=`php system/get_version_for_release.php`
echo "Preparing to release version $version of the MyAAC Project!" echo "Preparing to release version $version of the MyAAC Project!"
# make required directories
mkdir -p releases
mkdir -p tmp
# get myaac from git archive # get myaac from git archive
git archive --format zip --output tmp/myaac.zip master git archive --format zip --output tmp/myaac.zip master
# make required directories cd tmp/ || exit
mkdir -p releases
mkdir -p tmp && cd tmp
dir="myaac-$version" dir="myaac-$version"
if [ -d "$dir" ] ; then if [ -d "$dir" ] ; then
@ -39,9 +41,9 @@ fi
if [ $1 = "pack" ]; then if [ $1 = "pack" ]; then
# define release version # define release version
version=`cat VERSION` version=`php system/get_version_for_release.php`
cd tmp cd tmp || exit
# tar.gz # tar.gz
echo "Creating .tar.gz package.." echo "Creating .tar.gz package.."
@ -60,4 +62,4 @@ if [ $1 = "pack" ]; then
echo "Done. Released files can be found in 'releases' directory." echo "Done. Released files can be found in 'releases' directory."
exit exit
fi fi

View File

@ -9,6 +9,11 @@ $loader->register();
// register the base directories for the namespace prefix // register the base directories for the namespace prefix
$loader->addNamespace('Composer\Semver', LIBS . 'semver'); $loader->addNamespace('Composer\Semver', LIBS . 'semver');
$loader->addNamespace('Twig', LIBS . 'Twig'); $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 * An example of a general-purpose implementation that includes the optional
* functionality of allowing multiple base directories for a single namespace * functionality of allowing multiple base directories for a single namespace
@ -203,4 +208,4 @@ class Psr4AutoloaderClass
} }
return false; return false;
} }
} }

View File

@ -9,7 +9,6 @@ require_once __DIR__ . '/../../common.php';
require_once SYSTEM . 'functions.php'; require_once SYSTEM . 'functions.php';
require_once SYSTEM . 'init.php'; require_once SYSTEM . 'init.php';
require_once SYSTEM . 'hooks.php'; require_once SYSTEM . 'hooks.php';
require_once LIBS . 'plugins.php';
if($argc !== 2) { if($argc !== 2) {
echo 'This command expects one parameter: zip file name (plugin)' . PHP_EOL; 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!'); defined('MYAAC') or die('Direct access not allowed!');
$config['clients'] = array( $config['clients'] = [
710, 710,
740, 740,
750, 750,
@ -54,7 +54,9 @@ $config['clients'] = array(
1000, 1000,
1010, 1010,
1020,
1021, 1021,
1030,
1031, 1031,
1034, 1034,
1041, 1041,
@ -62,6 +64,7 @@ $config['clients'] = array(
1053, 1053,
1054, 1054,
1058, 1058,
1070,
1075, 1075,
1077, 1077,
1079, 1079,
@ -73,6 +76,37 @@ $config['clients'] = array(
1096, 1096,
1097, 1097,
1098, 1098,
1100, 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!'); defined('MYAAC') or die('Direct access not allowed!');
switch($page) switch($page)
{ {
case 'adminpanel':
header('Location: ' . ADMIN_URL);
die;
case 'archive':
$page = 'newsarchive';
break;
case 'whoisonline': case 'whoisonline':
$page = 'online'; $page = 'online';
break; break;
@ -37,4 +45,3 @@ switch($page)
default: default:
break; break;
} }
?>

View File

@ -51,4 +51,3 @@ else
updateDatabaseConfig('views_counter', $views_counter); // update counter 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 is not configured propertly in <i>config.lua</i>.</li>' .
'<li>MySQL server is not running.</li>' . '<li>MySQL server is not running.</li>' .
'</ul>' . $error->getMessage()); '</ul>' . $error->getMessage());
} }

View File

@ -23,6 +23,8 @@ function exception_handler($exception) {
$backtrace_formatted = nl2br($exception->getTraceAsString()); $backtrace_formatted = nl2br($exception->getTraceAsString());
$message = $message . "<br/><br/>File: {$exception->getFile()}<br/>Line: {$exception->getLine()}";
// display basic error message without template // display basic error message without template
// template is missing, why? probably someone deleted templates dir, or it wasn't downloaded right // template is missing, why? probably someone deleted templates dir, or it wasn't downloaded right
$template_file = SYSTEM . 'templates/exception.html.twig'; $template_file = SYSTEM . 'templates/exception.html.twig';
@ -39,7 +41,7 @@ function exception_handler($exception) {
// we just replace some values manually // we just replace some values manually
// cause in case Twig throws exception, we can show it too // cause in case Twig throws exception, we can show it too
$content = file_get_contents($template_file); $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);
echo $content; echo $content;
} }

View File

@ -7,18 +7,26 @@
* @copyright 2019 MyAAC * @copyright 2019 MyAAC
* @link https://my-aac.org * @link https://my-aac.org
*/ */
defined('MYAAC') or die('Direct access not allowed!');
use Twig\Loader\ArrayLoader as Twig_ArrayLoader; use Twig\Loader\ArrayLoader as Twig_ArrayLoader;
defined('MYAAC') or die('Direct access not allowed!');
function message($message, $type, $return) function message($message, $type, $return)
{ {
if($return) if(IS_CLI) {
return '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>'; if($return) {
return $message;
}
echo '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>'; echo $message;
return true; 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) { function success($message, $return = false) {
return message($message, 'success', $return); return message($message, 'success', $return);
@ -442,7 +450,7 @@ function tickers()
*/ */
function template_place_holder($type) function template_place_holder($type)
{ {
global $template_place_holders; global $twig, $template_place_holders;
$ret = ''; $ret = '';
if(array_key_exists($type, $template_place_holders) && is_array($template_place_holders[$type])) 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') { if($type === 'head_start') {
$ret .= template_header(); $ret .= template_header();
} }
elseif ($type === 'body_start') {
$ret .= $twig->render('browsehappy.html.twig');
}
elseif($type === 'body_end') { elseif($type === 'body_end') {
$ret .= template_ga_code(); $ret .= template_ga_code();
} }
@ -745,10 +756,10 @@ function get_browser_languages()
{ {
$ret = array(); $ret = array();
$acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE']; if(empty($_SERVER['HTTP_ACCEPT_LANGUAGE']))
if(!isset($acceptLang[0]))
return $ret; return $ret;
$acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$languages = strtolower($acceptLang); $languages = strtolower($acceptLang);
// $languages = 'pl,en-us;q=0.7,en;q=0.3 '; // $languages = 'pl,en-us;q=0.7,en;q=0.3 ';
// need to remove spaces from strings to avoid error // need to remove spaces from strings to avoid error
@ -786,7 +797,7 @@ function get_plugins()
$ret = array(); $ret = array();
$path = PLUGINS; $path = PLUGINS;
foreach(scandir($path, 0) as $file) { foreach(scandir($path, SCANDIR_SORT_ASCENDING) as $file) {
$file_ext = pathinfo($file, PATHINFO_EXTENSION); $file_ext = pathinfo($file, PATHINFO_EXTENSION);
$file_name = pathinfo($file, PATHINFO_FILENAME); $file_name = pathinfo($file, PATHINFO_FILENAME);
if ($file === '.' || $file === '..' || $file === 'disabled' || $file === 'example.json' || $file_ext !== 'json' || is_dir($path . $file)) 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; $config_file = $filename;
if(!@file_exists($config_file)) 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)); log_append('error.log', '[load_config_file] Fatal error: Cannot load config.lua (' . $filename . ').');
throw new RuntimeException('ERROR: Cannot find ' . $filename . ' file. More info in system/logs/error.log'); throw new RuntimeException('ERROR: Cannot find ' . $filename . ' file.');
} }
$result = array(); $result = array();
@ -922,6 +933,12 @@ function load_config_lua($filename)
if(count($lines) > 0) { if(count($lines) > 0) {
foreach($lines as $ln => $line) 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); $tmp_exp = explode('=', $line, 2);
if(strpos($line, 'dofile') !== false) if(strpos($line, 'dofile') !== false)
{ {
@ -948,16 +965,17 @@ function load_config_lua($filename)
$result[$key] = (string) substr(substr($value, 1), 0, -1); $result[$key] = (string) substr(substr($value, 1), 0, -1);
elseif(in_array($value, array('true', 'false'))) elseif(in_array($value, array('true', 'false')))
$result[$key] = ($value === 'true') ? true : false; $result[$key] = ($value === 'true') ? true : false;
elseif(@$value[0] === '{' && @$value[strlen($value) - 1] === '}') { elseif(@$value[0] === '{') {
// arrays are not supported yet // arrays are not supported yet
// just ignore the error // just ignore the error
continue;
} }
else 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 definied by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull
$value = str_replace($tmp_key, $tmp_value, $value); $value = str_replace($tmp_key, $tmp_value, $value);
$ret = @eval("return $value;"); $ret = @eval("return $value;");
if((string) $ret == '') // = parser error if((string) $ret == '' && trim($value) !== '""') // = 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>]'); throw new RuntimeException('ERROR: Loading config.lua file. Line <b>' . ($ln + 1) . '</b> of LUA config file is not valid [key: <b>' . $key . '</b>]');
} }
@ -982,6 +1000,10 @@ function str_replace_first($search, $replace, $subject) {
} }
function get_browser_real_ip() { 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'])) if(isset($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['REMOTE_ADDR']))
return $_SERVER['REMOTE_ADDR']; return $_SERVER['REMOTE_ADDR'];
else if(isset($_SERVER['HTTP_CLIENT_IP']) && !empty($_SERVER['HTTP_CLIENT_IP'])) else if(isset($_SERVER['HTTP_CLIENT_IP']) && !empty($_SERVER['HTTP_CLIENT_IP']))
@ -1019,7 +1041,7 @@ function getTopPlayers($limit = 5) {
$deleted = 'deletion'; $deleted = 'deletion';
$is_tfs10 = $db->hasTable('players_online'); $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) { if($is_tfs10) {
foreach($players as &$player) { foreach($players as &$player) {
@ -1124,9 +1146,30 @@ function clearCache()
if ($cache->fetch('failed_logins', $tmp)) if ($cache->fetch('failed_logins', $tmp))
$cache->delete('failed_logins'); $cache->delete('failed_logins');
global $template_name; foreach (get_templates() as $template) {
if ($cache->fetch('template_ini' . $template_name, $tmp)) if ($cache->fetch('template_ini_' . $template, $tmp)) {
$cache->delete('template_ini' . $template_name); $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); deleteDirectory(CACHE . 'signatures', ['index.html'], true);
@ -1221,6 +1264,73 @@ function getCustomPage($page, &$success)
return $content; 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 // validator functions
require_once LIBS . 'validator.php'; 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!'); defined('MYAAC') or die('Direct access not allowed!');
define('HOOK_STARTUP', 1); $i = 0;
define('HOOK_BEFORE_PAGE', 2); define('HOOK_STARTUP', ++$i);
define('HOOK_AFTER_PAGE', 3); define('HOOK_BEFORE_PAGE', ++$i);
define('HOOK_FINISH', 4); define('HOOK_AFTER_PAGE', ++$i);
define('HOOK_TIBIACOM_ARTICLE', 5); define('HOOK_FINISH', ++$i);
define('HOOK_TIBIACOM_BORDER_3', 6); define('HOOK_TIBIACOM_ARTICLE', ++$i);
define('HOOK_CHARACTERS_BEFORE_INFORMATIONS', 7); define('HOOK_TIBIACOM_BORDER_3', ++$i);
define('HOOK_CHARACTERS_AFTER_INFORMATIONS', 8); define('HOOK_CHARACTERS_BEFORE_INFORMATIONS', ++$i);
define('HOOK_CHARACTERS_BEFORE_SIGNATURE', 9); define('HOOK_CHARACTERS_AFTER_INFORMATIONS', ++$i);
define('HOOK_CHARACTERS_AFTER_SIGNATURE', 10); define('HOOK_CHARACTERS_BEFORE_SKILLS', ++$i);
define('HOOK_CHARACTERS_AFTER_ACCOUNT', 11); define('HOOK_CHARACTERS_AFTER_SKILLS', ++$i);
define('HOOK_CHARACTERS_AFTER_CHARACTERS', 12); define('HOOK_CHARACTERS_AFTER_QUESTS', ++$i);
define('HOOK_LOGIN', 13); define('HOOK_CHARACTERS_AFTER_EQUIPMENT', ++$i);
define('HOOK_LOGIN_ATTEMPT', 14); define('HOOK_CHARACTERS_BEFORE_DEATHS', ++$i);
define('HOOK_LOGOUT', 15); define('HOOK_CHARACTERS_BEFORE_SIGNATURE', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_FORM', 16); define('HOOK_CHARACTERS_AFTER_SIGNATURE', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_BOXES', 17); define('HOOK_CHARACTERS_AFTER_ACCOUNT', ++$i);
define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_1', 18); define('HOOK_CHARACTERS_AFTER_CHARACTERS', ++$i);
define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_2', 19); define('HOOK_LOGIN', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_BOXES', 20); define('HOOK_LOGIN_ATTEMPT', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_ACCOUNT', 21); define('HOOK_LOGOUT', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_ACCOUNT', 22); define('HOOK_ACCOUNT_CREATE_BEFORE_FORM', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_EMAIL', 23); define('HOOK_ACCOUNT_CREATE_BEFORE_BOXES', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_COUNTRY', 24); define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_1', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_PASSWORDS', 25); define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_2', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_RECAPTCHA', 26); define('HOOK_ACCOUNT_CREATE_AFTER_BOXES', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_CHARACTER_NAME', 27); define('HOOK_ACCOUNT_CREATE_BEFORE_ACCOUNT', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_CHARACTER_NAME', 28); define('HOOK_ACCOUNT_CREATE_AFTER_ACCOUNT', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_SEX', 29); define('HOOK_ACCOUNT_CREATE_AFTER_EMAIL', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_VOCATION', 30); define('HOOK_ACCOUNT_CREATE_AFTER_COUNTRY', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_TOWNS', 31); define('HOOK_ACCOUNT_CREATE_AFTER_PASSWORDS', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_SUBMIT_BUTTON', 32); define('HOOK_ACCOUNT_CREATE_AFTER_RECAPTCHA', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_FORM', 33); define('HOOK_ACCOUNT_CREATE_BEFORE_CHARACTER_NAME', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_SUBMIT', 34); 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_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 . 'plugins.php';
require_once LIBS . 'src/plugins.php';
class Hook class Hook
{ {
private $_name, $_type, $_file; private $_name, $_type, $_file;
@ -67,9 +83,7 @@ class Hook
}*/ }*/
global $db, $config, $template_path, $ots, $content, $twig; global $db, $config, $template_path, $ots, $content, $twig;
if(file_exists(BASE . $this->_file)) { $ret = include BASE . $this->_file;
$ret = require BASE . $this->_file;
}
return !isset($ret) || $ret == 1 || $ret; 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'); ob_start('ob_gzhandler');
// cache // cache
require_once SYSTEM . 'libs/cache.php'; require_once LIBS . 'cache.php';
require_once LIBS . 'src/Cache.php';
$cache = Cache::getInstance(); $cache = Cache::getInstance();
// twig
require_once SYSTEM . 'twig.php';
// trim values we receive // trim values we receive
if(isset($_POST)) if(isset($_POST))
{ {
@ -114,16 +112,21 @@ if(!isset($foundValue)) {
$config['data_path'] = $foundValue; $config['data_path'] = $foundValue;
unset($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) { if(!isset($config['highscores_ids_hidden']) || count($config['highscores_ids_hidden']) == 0) {
$config['highscores_ids_hidden'] = array(0); $config['highscores_ids_hidden'] = array(0);
} }
$config['account_mail_verify'] = config('account_mail_verify') && config('mail_enabled');
// POT // POT
require_once SYSTEM . 'libs/pot/OTS.php'; require_once SYSTEM . 'libs/pot/OTS.php';
$ots = POT::getInstance(); $ots = POT::getInstance();
require_once SYSTEM . 'database.php'; require_once SYSTEM . 'database.php';
// twig
require_once SYSTEM . 'twig.php';
define('USE_ACCOUNT_NAME', $db->hasColumn('accounts', 'name')); define('USE_ACCOUNT_NAME', $db->hasColumn('accounts', 'name'));
// load vocation names // load vocation names
$tmp = ''; $tmp = '';
@ -140,10 +143,8 @@ else {
if(!@file_exists($file)) if(!@file_exists($file))
$file = $config['data_path'] . 'vocations.xml'; $file = $config['data_path'] . 'vocations.xml';
$vocations->load($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.');
if(!$vocations)
throw new RuntimeException('ERROR: Cannot load <i>vocations.xml</i> file.');
$config['vocations'] = array(); $config['vocations'] = array();
foreach($vocations->getElementsByTagName('vocation') as $vocation) { foreach($vocations->getElementsByTagName('vocation') as $vocation) {
@ -180,7 +181,8 @@ else {
// load towns from database (TFS 1.3) // // load towns from database (TFS 1.3) //
//////////////////////////////////////// ////////////////////////////////////////
$towns = array(); $tmp = '';
$towns = [];
if($cache->enabled() && $cache->fetch('towns', $tmp)) { if($cache->enabled() && $cache->fetch('towns', $tmp)) {
$towns = unserialize($tmp); $towns = unserialize($tmp);
} }
@ -193,20 +195,14 @@ else {
} }
unset($query); unset($query);
if($cache->enabled()) {
$cache->set('towns', serialize($towns), 600);
}
} }
else if($cache->enabled()) { else {
$cache->set('towns', serialize(array()), 600); $towns = config('towns');
} }
}
$configTowns = config('towns'); if($cache->enabled()) {
if($configTowns !== null && (!isset($configTowns[1]) || $configTowns[1] !== 'Sample town')) { $cache->set('towns', serialize($towns), 600);
$towns = array_replace( }
$towns, $configTowns
);
} }
config(['towns', $towns]); config(['towns', $towns]);

View File

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

View File

@ -11,6 +11,57 @@
class CreateCharacter 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 string $name
* @param int $sex * @param int $sex
@ -19,42 +70,27 @@ class CreateCharacter
* @param array $errors * @param array $errors
* @return bool * @return bool
*/ */
public function check($name, $sex, &$vocation, &$town, &$errors) { public function check($name, $sex, &$vocation, &$town, &$errors)
$minLength = config('character_name_min_length'); {
$maxLength = config('character_name_max_length'); $this->checkName($name, $errors);
if(empty($name)) if(empty($sex) && $sex != "0") {
$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")
$errors['sex'] = 'Please select the sex for your character!'; $errors['sex'] = 'Please select the sex for your character!';
}
if(count(config('character_samples')) > 1) if(count(config('character_samples')) > 1)
{ {
if(!isset($vocation)) if(!isset($vocation))
$errors['vocation'] = 'Please select a vocation for your character.'; $errors['vocation'] = 'Please select a vocation for your character.';
} }
else else {
$vocation = config('character_samples')[0]; $vocation = config('character_samples')[0];
}
if(count(config('character_towns')) > 1) { if(count(config('character_towns')) > 1) {
if(!isset($town)) if(!isset($town)) {
$errors['town'] = 'Please select a town for your character.'; $errors['town'] = 'Please select a town for your character.';
}
} }
else { else {
$town = config('character_towns')[0]; $town = config('character_towns')[0];
@ -102,7 +138,7 @@ class CreateCharacter
if(empty($errors)) 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')) 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>!'; $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; return false;
} }
global $db, $twig; global $db;
if($sex == "0") if($sex == "0")
$char_to_copy->setLookType(136); $char_to_copy->setLookType(136);
@ -157,8 +193,14 @@ class CreateCharacter
$player->setManaSpent($char_to_copy->getManaSpent()); $player->setManaSpent($char_to_copy->getManaSpent());
$player->setSoul($char_to_copy->getSoul()); $player->setSoul($char_to_copy->getSoul());
for($skill = POT::SKILL_FIRST; $skill <= POT::SKILL_LAST; $skill++) for($skill = POT::SKILL_FIRST; $skill <= POT::SKILL_LAST; $skill++) {
$player->setSkill($skill, 10); $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->setLookBody($char_to_copy->getLookBody());
$player->setLookFeet($char_to_copy->getLookFeet()); $player->setLookFeet($char_to_copy->getLookFeet());
@ -186,7 +228,7 @@ class CreateCharacter
} }
$player->save(); $player->save();
$player->setCustomField("created", time()); $player->setCustomField('created', time());
$player = new OTS_Player(); $player = new OTS_Player();
$player->find($name); $player->find($name);
@ -197,18 +239,28 @@ class CreateCharacter
} }
if($db->hasTable('player_skills')) { if($db->hasTable('player_skills')) {
for($i=0; $i<7; $i++) { 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); $skillExists = $db->query('SELECT `skillid` FROM `player_skills` WHERE `player_id` = ' . $player->getId() . ' AND `skillid` = ' . $i);
if($skillExists->rowCount() <= 0) { 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().""); if ($db->hasTable('player_items') && $db->hasColumn('player_items', 'pid') && $db->hasColumn('player_items', 'sid') && $db->hasColumn('player_items', 'itemtype')) {
foreach($loaded_items_to_copy as $save_item) $loaded_items_to_copy = $db->query("SELECT * FROM player_items WHERE player_id = ".$char_to_copy->getId()."");
$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']."');"); 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( $twig->display('success.html.twig', array(
'title' => 'Character Created', 'title' => 'Character Created',
'description' => 'The character <b>' . $name . '</b> has been created.<br/> 'description' => 'The character <b>' . $name . '</b> has been created.<br/>
@ -219,4 +271,4 @@ class CreateCharacter
$account->logAction('Created character <b>' . $name . '</b>.'); $account->logAction('Created character <b>' . $name . '</b>.');
return true; return true;
} }
} }

View File

@ -18,7 +18,7 @@ namespace Twig\Cache;
*/ */
class FilesystemCache implements CacheInterface class FilesystemCache implements CacheInterface
{ {
const FORCE_BYTECODE_INVALIDATION = 1; public const FORCE_BYTECODE_INVALIDATION = 1;
private $directory; private $directory;
private $options; private $options;
@ -35,7 +35,7 @@ class FilesystemCache implements CacheInterface
public function generateKey($name, $className) 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'; 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)) { if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) {
// Compile cached file into bytecode cache // 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); @opcache_invalidate($key, true);
} elseif (\function_exists('apc_compile_file')) { } elseif (\function_exists('apc_compile_file')) {
apc_compile_file($key); apc_compile_file($key);

View File

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

View File

@ -12,23 +12,22 @@
namespace Twig; namespace Twig;
use Twig\Node\ModuleNode; use Twig\Node\Node;
/** /**
* Compiles a node to PHP code. * Compiles a node to PHP code.
* *
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
*/ */
class Compiler implements \Twig_CompilerInterface class Compiler
{ {
protected $lastLine; private $lastLine;
protected $source; private $source;
protected $indentation; private $indentation;
protected $env; private $env;
protected $debugInfo = []; private $debugInfo = [];
protected $sourceOffset; private $sourceOffset;
protected $sourceLine; private $sourceLine;
protected $filename;
private $varNameSalt = 0; private $varNameSalt = 0;
public function __construct(Environment $env) public function __construct(Environment $env)
@ -36,16 +35,6 @@ class Compiler implements \Twig_CompilerInterface
$this->env = $env; $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. * Returns the environment instance related to this compiler.
* *
@ -73,7 +62,7 @@ class Compiler implements \Twig_CompilerInterface
* *
* @return $this * @return $this
*/ */
public function compile(\Twig_NodeInterface $node, $indentation = 0) public function compile(Node $node, $indentation = 0)
{ {
$this->lastLine = null; $this->lastLine = null;
$this->source = ''; $this->source = '';
@ -84,17 +73,12 @@ class Compiler implements \Twig_CompilerInterface
$this->indentation = $indentation; $this->indentation = $indentation;
$this->varNameSalt = 0; $this->varNameSalt = 0;
if ($node instanceof ModuleNode) {
// to be removed in 2.0
$this->filename = $node->getTemplateName();
}
$node->compile($this); $node->compile($this);
return $this; return $this;
} }
public function subcompile(\Twig_NodeInterface $node, $raw = true) public function subcompile(Node $node, $raw = true)
{ {
if (false === $raw) { if (false === $raw) {
$this->source .= str_repeat(' ', $this->indentation * 4); $this->source .= str_repeat(' ', $this->indentation * 4);
@ -124,9 +108,8 @@ class Compiler implements \Twig_CompilerInterface
* *
* @return $this * @return $this
*/ */
public function write() public function write(...$strings)
{ {
$strings = \func_get_args();
foreach ($strings as $string) { foreach ($strings as $string) {
$this->source .= str_repeat(' ', $this->indentation * 4).$string; $this->source .= str_repeat(' ', $this->indentation * 4).$string;
} }
@ -134,22 +117,6 @@ class Compiler implements \Twig_CompilerInterface
return $this; 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. * Adds a quoted string to the compiled code.
* *
@ -174,21 +141,21 @@ class Compiler implements \Twig_CompilerInterface
public function repr($value) public function repr($value)
{ {
if (\is_int($value) || \is_float($value)) { if (\is_int($value) || \is_float($value)) {
if (false !== $locale = setlocale(LC_NUMERIC, '0')) { if (false !== $locale = setlocale(\LC_NUMERIC, '0')) {
setlocale(LC_NUMERIC, 'C'); setlocale(\LC_NUMERIC, 'C');
} }
$this->raw(var_export($value, true)); $this->raw(var_export($value, true));
if (false !== $locale) { if (false !== $locale) {
setlocale(LC_NUMERIC, $locale); setlocale(\LC_NUMERIC, $locale);
} }
} elseif (null === $value) { } elseif (null === $value) {
$this->raw('null'); $this->raw('null');
} elseif (\is_bool($value)) { } elseif (\is_bool($value)) {
$this->raw($value ? 'true' : 'false'); $this->raw($value ? 'true' : 'false');
} elseif (\is_array($value)) { } elseif (\is_array($value)) {
$this->raw('['); $this->raw('array(');
$first = true; $first = true;
foreach ($value as $key => $v) { foreach ($value as $key => $v) {
if (!$first) { if (!$first) {
@ -199,7 +166,7 @@ class Compiler implements \Twig_CompilerInterface
$this->raw(' => '); $this->raw(' => ');
$this->repr($v); $this->repr($v);
} }
$this->raw(']'); $this->raw(')');
} else { } else {
$this->string($value); $this->string($value);
} }
@ -212,22 +179,12 @@ class Compiler implements \Twig_CompilerInterface
* *
* @return $this * @return $this
*/ */
public function addDebugInfo(\Twig_NodeInterface $node) public function addDebugInfo(Node $node)
{ {
if ($node->getTemplateLine() != $this->lastLine) { if ($node->getTemplateLine() != $this->lastLine) {
$this->write(sprintf("// line %d\n", $node->getTemplateLine())); $this->write(sprintf("// line %d\n", $node->getTemplateLine()));
// when mbstring.func_overload is set to 2 $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
// 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->sourceOffset = \strlen($this->source); $this->sourceOffset = \strlen($this->source);
$this->debugInfo[$this->sourceLine] = $node->getTemplateLine(); $this->debugInfo[$this->sourceLine] = $node->getTemplateLine();
@ -281,7 +238,7 @@ class Compiler implements \Twig_CompilerInterface
public function getVarName() 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 class Error extends \Exception
{ {
protected $lineno; private $lineno;
// to be renamed to name in 2.0 private $name;
protected $filename; private $rawMessage;
protected $rawMessage;
private $sourcePath; private $sourcePath;
private $sourceCode; private $sourceCode;
@ -57,22 +55,23 @@ class Error extends \Exception
* @param Source|string|null $source The source context where the error occurred * @param Source|string|null $source The source context where the error occurred
* @param \Exception $previous The previous exception * @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) { if (null === $source) {
$name = null; $name = null;
} elseif (!$source instanceof Source) { } elseif (!$source instanceof Source && !$source instanceof \Twig_Source) {
// for compat with the Twig C ext., passing the template name as string is accepted @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; $name = $source;
} else { } else {
$name = $source->getName(); $name = $source->getName();
$this->sourceCode = $source->getCode(); $this->sourceCode = $source->getCode();
$this->sourcePath = $source->getPath(); $this->sourcePath = $source->getPath();
} }
parent::__construct('', 0, $previous);
$this->lineno = $lineno; $this->lineno = $lineno;
$this->filename = $name; $this->name = $name;
$this->rawMessage = $message; $this->rawMessage = $message;
$this->updateRepr(); $this->updateRepr();
} }
@ -87,67 +86,6 @@ class Error extends \Exception
return $this->rawMessage; 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. * Gets the template line where the error occurred.
* *
@ -177,7 +115,7 @@ class Error extends \Exception
*/ */
public function getSourceContext() 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) public function setSourceContext(Source $source = null)
{ {
if (null === $source) { if (null === $source) {
$this->sourceCode = $this->filename = $this->sourcePath = null; $this->sourceCode = $this->name = $this->sourcePath = null;
} else { } else {
$this->sourceCode = $source->getCode(); $this->sourceCode = $source->getCode();
$this->filename = $source->getName(); $this->name = $source->getName();
$this->sourcePath = $source->getPath(); $this->sourcePath = $source->getPath();
} }
@ -208,10 +146,7 @@ class Error extends \Exception
$this->updateRepr(); $this->updateRepr();
} }
/** private function updateRepr()
* @internal
*/
protected function updateRepr()
{ {
$this->message = $this->rawMessage; $this->message = $this->rawMessage;
@ -234,11 +169,11 @@ class Error extends \Exception
$questionMark = true; $questionMark = true;
} }
if ($this->filename) { if ($this->name) {
if (\is_string($this->filename) || (\is_object($this->filename) && method_exists($this->filename, '__toString'))) { if (\is_string($this->name) || (\is_object($this->name) && method_exists($this->name, '__toString'))) {
$name = sprintf('"%s"', $this->filename); $name = sprintf('"%s"', $this->name);
} else { } else {
$name = json_encode($this->filename); $name = json_encode($this->name);
} }
$this->message .= sprintf(' in %s', $name); $this->message .= sprintf(' in %s', $name);
} }
@ -256,20 +191,17 @@ class Error extends \Exception
} }
} }
/** private function guessTemplateInfo()
* @internal
*/
protected function guessTemplateInfo()
{ {
$template = null; $template = null;
$templateClass = 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) { 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']); $currentClass = \get_class($trace['object']);
$isEmbedContainer = 0 === strpos($templateClass, $currentClass); $isEmbedContainer = null === $templateClass ? false : 0 === strpos($templateClass, $currentClass);
if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) { if (null === $this->name || ($this->name == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
$template = $trace['object']; $template = $trace['object'];
$templateClass = \get_class($trace['object']); $templateClass = \get_class($trace['object']);
} }
@ -277,8 +209,8 @@ class Error extends \Exception
} }
// update template name // update template name
if (null !== $template && null === $this->filename) { if (null !== $template && null === $this->name) {
$this->filename = $template->getTemplateName(); $this->name = $template->getTemplateName();
} }
// update template path if any // update template path if any
@ -296,7 +228,7 @@ class Error extends \Exception
$file = $r->getFileName(); $file = $r->getFileName();
$exceptions = [$e = $this]; $exceptions = [$e = $this];
while ($e instanceof self && $e = $e->getPrevious()) { while ($e = $e->getPrevious()) {
$exceptions[] = $e; $exceptions[] = $e;
} }

View File

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

View File

@ -11,17 +11,8 @@
namespace Twig\Extension; namespace Twig\Extension;
use Twig\Environment;
abstract class AbstractExtension implements ExtensionInterface 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() public function getTokenParsers()
{ {
return []; return [];
@ -51,22 +42,6 @@ abstract class AbstractExtension implements ExtensionInterface
{ {
return []; 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'); 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 { namespace Twig\Extension {
use Twig\TwigFunction; use Twig\TwigFunction;
/** final class DebugExtension extends AbstractExtension
* @final
*/
class DebugExtension extends AbstractExtension
{ {
public function getFunctions() 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]), 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'); class_alias('Twig\Extension\DebugExtension', 'Twig_Extension_Debug');
@ -48,7 +40,7 @@ use Twig\Environment;
use Twig\Template; use Twig\Template;
use Twig\TemplateWrapper; use Twig\TemplateWrapper;
function twig_var_dump(Environment $env, $context, array $vars = []) function twig_var_dump(Environment $env, $context, ...$vars)
{ {
if (!$env->isDebug()) { if (!$env->isDebug()) {
return; return;
@ -66,9 +58,7 @@ function twig_var_dump(Environment $env, $context, array $vars = [])
var_dump($vars); var_dump($vars);
} else { } else {
foreach ($vars as $var) { var_dump(...$vars);
var_dump($var);
}
} }
return ob_get_clean(); return ob_get_clean();

View File

@ -10,16 +10,21 @@
*/ */
namespace Twig\Extension { namespace Twig\Extension {
use Twig\FileExtensionEscapingStrategy;
use Twig\NodeVisitor\EscaperNodeVisitor; use Twig\NodeVisitor\EscaperNodeVisitor;
use Twig\TokenParser\AutoEscapeTokenParser; use Twig\TokenParser\AutoEscapeTokenParser;
use Twig\TwigFilter; 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 * @param string|false|callable $defaultStrategy An escaping strategy
@ -44,6 +49,8 @@ class EscaperExtension extends AbstractExtension
public function getFilters() public function getFilters()
{ {
return [ 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']]), new TwigFilter('raw', 'twig_raw_filter', ['is_safe' => ['all']]),
]; ];
} }
@ -58,21 +65,8 @@ class EscaperExtension extends AbstractExtension
*/ */
public function setDefaultStrategy($defaultStrategy) 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) { if ('name' === $defaultStrategy) {
$defaultStrategy = ['\Twig\FileExtensionEscapingStrategy', 'guess']; $defaultStrategy = [FileExtensionEscapingStrategy::class, 'guess'];
} }
$this->defaultStrategy = $defaultStrategy; $this->defaultStrategy = $defaultStrategy;
@ -96,9 +90,47 @@ class EscaperExtension extends AbstractExtension
return $this->defaultStrategy; 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 { 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. * Marks a variable as being safe.
* *
@ -117,4 +157,272 @@ function twig_raw_filter($string)
{ {
return $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; namespace Twig\Extension;
use Twig\Environment;
use Twig\NodeVisitor\NodeVisitorInterface; use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\TokenParserInterface; use Twig\TokenParser\TokenParserInterface;
use Twig\TwigFilter; use Twig\TwigFilter;
@ -25,15 +24,6 @@ use Twig\TwigTest;
*/ */
interface ExtensionInterface 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. * 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 * @return array<array> First array of unary operators, second array of binary operators
*/ */
public function getOperators(); 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'); class_alias('Twig\Extension\ExtensionInterface', 'Twig_ExtensionInterface');

View File

@ -21,6 +21,12 @@ namespace Twig\Extension;
*/ */
interface GlobalsInterface 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'); class_alias('Twig\Extension\GlobalsInterface', 'Twig_Extension_GlobalsInterface');

View File

@ -11,6 +11,8 @@
namespace Twig\Extension; namespace Twig\Extension;
use Twig\Environment;
/** /**
* Enables usage of the deprecated Twig\Extension\AbstractExtension::initRuntime() method. * Enables usage of the deprecated Twig\Extension\AbstractExtension::initRuntime() method.
* *
@ -18,9 +20,17 @@ namespace Twig\Extension;
* deprecated initRuntime() method in your extensions. * deprecated initRuntime() method in your extensions.
* *
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since Twig 2.7, to be removed in 3.0
*/ */
interface InitRuntimeInterface 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'); class_alias('Twig\Extension\InitRuntimeInterface', 'Twig_Extension_InitRuntimeInterface');

View File

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

View File

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

View File

@ -12,17 +12,17 @@
namespace Twig\Extension; namespace Twig\Extension;
use Twig\NodeVisitor\SandboxNodeVisitor; use Twig\NodeVisitor\SandboxNodeVisitor;
use Twig\Sandbox\SecurityNotAllowedMethodError;
use Twig\Sandbox\SecurityNotAllowedPropertyError;
use Twig\Sandbox\SecurityPolicyInterface; use Twig\Sandbox\SecurityPolicyInterface;
use Twig\Source;
use Twig\TokenParser\SandboxTokenParser; use Twig\TokenParser\SandboxTokenParser;
/** final class SandboxExtension extends AbstractExtension
* @final
*/
class SandboxExtension extends AbstractExtension
{ {
protected $sandboxedGlobally; private $sandboxedGlobally;
protected $sandboxed; private $sandboxed;
protected $policy; private $policy;
public function __construct(SecurityPolicyInterface $policy, $sandboxed = false) 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()) { 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()) { 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')) { 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; return $obj;
} }
public function getName()
{
return 'sandbox';
}
} }
class_alias('Twig\Extension\SandboxExtension', 'Twig_Extension_Sandbox'); class_alias('Twig\Extension\SandboxExtension', 'Twig_Extension_Sandbox');

View File

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

View File

@ -12,10 +12,7 @@
namespace Twig\Extension { namespace Twig\Extension {
use Twig\TwigFunction; use Twig\TwigFunction;
/** final class StringLoaderExtension extends AbstractExtension
* @final
*/
class StringLoaderExtension extends AbstractExtension
{ {
public function getFunctions() public function getFunctions()
{ {
@ -23,11 +20,6 @@ class StringLoaderExtension extends AbstractExtension
new TwigFunction('template_from_string', 'twig_template_from_string', ['needs_environment' => true]), 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'); class_alias('Twig\Extension\StringLoaderExtension', 'Twig_Extension_StringLoader');
@ -47,7 +39,7 @@ use Twig\TemplateWrapper;
* *
* @return 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); 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');

View File

@ -41,7 +41,7 @@ class FileExtensionEscapingStrategy
$name = substr($name, 0, -5); $name = substr($name, 0, -5);
} }
$extension = pathinfo($name, PATHINFO_EXTENSION); $extension = pathinfo($name, \PATHINFO_EXTENSION);
switch ($extension) { switch ($extension) {
case 'js': case 'js':

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