Compare commits

...

303 Commits

Author SHA1 Message Date
slawkens
44a6400fcf protect this from direct execution 2022-11-28 12:52:03 +01:00
slawkens
847c3db625 Update templates.header.html.twig 2022-11-28 12:49:30 +01:00
slawkens
f30181d485 Merge branch 'develop' into feature/recaptcha-v3-plus-login 2022-11-28 12:46:32 +01:00
slawkens
118e8c487e Fixes for racaptcha 2022-11-28 12:41:29 +01:00
slawkens
c73e476e88 Reverted support only for recaptcha v3
v2 & v3 are now both supported
2022-11-28 12:36:23 +01:00
slawkens
ac5b864ea9 Small fixes 2022-11-28 12:26:34 +01:00
Gabriel Pedro
42d531838c feat: github actions phplint (#206)
* feat: php linter on pull requests

* test: breaking code

* Revert "test: breaking code"

This reverts commit 9d385a3421.
2022-11-24 08:19:09 +01:00
slawkens
2321cf84b0 patch changes & fixes from master branch
remove VERSION file
update rules
add 33 migration
add get_version_for_release.sh script
update schema
add use_character_sample_skills
2022-11-07 09:10:19 +01:00
slawkens
a570363fe0 Update README.md 2022-11-07 09:07:32 +01:00
slawkens
616b8eb61a some not-important changes 2022-10-28 17:16:17 +02:00
slawkens
e1d486c8c8 Add vocation into getTopPlayers 2022-10-28 14:41:59 +02:00
slawkens
b841c9f631 Fix typo in br locale 2022-10-28 14:41:43 +02:00
slawkens
e6c72efd18 Add more client versions 2022-10-28 14:41:28 +02:00
slawkens
9693fd260c Update account.change_mail.html.twig 2022-10-28 14:41:14 +02:00
slawkens
717b5fdd15 Add compat Gesior classes
To allow more custom pages be used with myaac
2022-09-27 10:06:03 +02:00
slawkens
32cf487128 Feature/recaptcha v3 plus login (#202)
* [WIP] New GoogleReCAPTCHA code
Support for v3
v2-invisible doesn't work yet

* Add some notice about recaptchas versions

* Lets support only ReCaptcha v3

Too much mess ;)

* Fixes
2022-08-31 11:16:48 +02:00
slawkens
a9941dea8a Fixes 2022-08-29 19:04:47 +02:00
slawkens
5c9737f281 Merge branch 'develop' into feature/recaptcha-v3-plus-login 2022-08-28 18:28:10 +02:00
slawkens
87a98531d9 Lets support only ReCaptcha v3
Too much mess ;)
2022-08-28 18:13:34 +02:00
slawkens
6d142dcbfe Merge branch 'develop' of https://github.com/otsoft/myaac into develop 2022-08-26 14:05:03 +02:00
slawkens
90f00e9960 Merge branch 'feature/login-by-email' into develop 2022-08-21 16:00:45 +02:00
thatmichaelguy
8711e178e9 Update change_rank.php (#194) 2022-07-04 16:54:19 +02:00
davi costa
eb28b38709 fix guild invite page (#196)
* fix guild invite

* removing var_dump

* sending error
2022-07-04 15:37:49 +02:00
slawkens
afea618867 Optimize code (account number generation)
Thanks kamil-karkus for suggestion
2022-05-31 15:13:59 +02:00
slawkens
0abb9384a6 Fix: create account by email
Now you can at least create an account if account_login_by_email is enabled :)
2022-05-31 15:09:48 +02:00
slawkens
2563583f84 Merge branch 'develop' into feature/login-by-email 2022-05-31 12:25:26 +02:00
slawkens
6acbbe3fa1 patch some changes from master (character name validate) 2022-05-31 12:22:55 +02:00
slawkens
6c157f3f6c Comment useless log line 2022-05-31 10:09:05 +02:00
slawkens
c4737eca72 Update login.php for latest TFS 1.x and otservbr
Works in both.
Thanks for Znote for rfc6238 lib.
2022-05-31 10:08:52 +02:00
thatmichaelguy
5428f5e2cf Update change_rank.php (#194) 2022-05-31 10:08:22 +02:00
slawkens
7d6d77cfbc 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-05-31 10:08:10 +02:00
slawkens
87eacd17c5 Add tables.headline
For future reference
2022-05-31 10:07:47 +02:00
slawkens
dd4420dcfd <div> should not be inside of <table> element 2022-05-31 10:07:35 +02:00
slawkens
b8843a29eb Revert "Include tinymce with NPM"
This reverts commit cedcd14550.
2022-03-16 17:27:30 +01:00
slawkens
a12262df55 Add tables.headline
For future reference
2022-03-16 14:30:39 +01:00
slawkens
091828e8f1 Add php_sessions to .gitignore 2022-01-14 19:41:11 +01:00
slawkens
8bca099037 Fix typo 2022-01-07 08:32:18 +01:00
slawkens
a43d641b5f Fixes (config.news_author, group_name|capitalize) 2022-01-02 07:32:15 +01:00
slawkens
46c058df25 Save php sessions in myaac dir
Instead of default PHP location
This fixes problem with permissions
2021-12-28 07:36:38 +01:00
slawkens
fa7c6497e6 Replace define with const 2021-12-27 21:21:24 +01:00
slawkens
82b41d4df5 Add browsehappy code 2021-12-22 07:03:34 +01:00
slawkens
fd2c2d552a Merge branch 'feature/admin-bar' into develop 2021-12-04 21:36:11 +01:00
slawkens
78ae456b45 Finish admin-bar
New ideas welcome. Please open an issue or contact me
2021-12-04 21:34:26 +01:00
slawkens
7e5528b7e1 Merge branch 'develop' into feature/admin-bar 2021-12-04 20:43:00 +01:00
xitobuh
a97f55e189 Add filters (#161)
Added this in what seemed like an empty table above the list. Untested but should work, please elaborate as you see fit
2021-10-28 21:42:33 +02:00
slawkens
50dd65c6de Fix eslint warnings 2021-10-23 15:00:52 +02:00
slawkens
16aeb12111 Comment unused variable 2021-10-23 15:00:30 +02:00
slawkens
96a7c43cb5 Delete cookies.js
was unused
2021-10-23 14:32:42 +02:00
slawkens
ee20ee2ecd Add contributors to credits 2021-10-23 14:25:46 +02:00
slawkens
efdd156d5e Bump twig version to latest release in 1.0 branch 2021-10-23 14:07:14 +02:00
slawkens
be41023005 Bump required PHP Version to ^7.2.5 || ^8.0 2021-10-23 13:59:38 +02:00
slawkens
d143f05bb1 Reorder requirements in composer 2021-10-23 13:57:17 +02:00
slawkens
fdc229b196 php gd extension is optional, so removing it from composer.json 2021-10-23 13:55:24 +02:00
slawkens
f6a5552296 Reorder checks 2021-10-23 13:54:31 +02:00
slawkens
62a4b4d3ec php extension zip is optional now 2021-10-23 13:52:34 +02:00
slawkens
ef24d6739a adjust EditorConfig for composer & npm 2021-10-23 13:35:43 +02:00
slawkens
1831198349 Revert "Adj."
This reverts commit ddf764e308.
2021-10-23 12:15:30 +02:00
slawkens
ddf764e308 Adj. 2021-10-23 11:57:49 +02:00
slawkens
988c757ca6 Small adjustments 2021-10-23 11:46:15 +02:00
slawkens
cedcd14550 Include tinymce with NPM
One will need to run: "npm install" as from now
More info here: https://nodejs.dev/learn/an-introduction-to-the-npm-package-manager
2021-10-23 10:23:37 +02:00
slawkens
0ff290f868 Update .gitignore 2021-10-23 09:52:11 +02:00
slawkens
1764ce0519 Fix: undefined variable notice on database_log enabled 2021-08-30 16:16:33 +02:00
slawkens
f3b49d7cba Experimental: change player_deaths entries on name change 2021-08-30 16:16:13 +02:00
slawkens
6d19d69d20 Fix some undefined notice 2021-07-05 17:15:48 +02:00
slawkens
9ad367370a Support for downgraded TFS 7.72 (with accounts.number)
Part 2
2021-06-09 01:54:22 +02:00
slawkens
eb091e487d Support for downgraded TFS 7.72 (with accounts.number)
Part one
2021-06-09 01:54:05 +02:00
slawkens
a5ccc794bc Add exception class to whoops screen 2021-06-09 01:46:08 +02:00
slawkens
1427dc3ede Move MyAAC table detection to proper place 2021-06-09 01:45:38 +02:00
slawkens
e47bb11883 Suggest account number option 2021-06-09 01:01:18 +02:00
slawkens
6f3ba9c34b Merge branch 'develop' into feature/login-by-email 2021-06-09 00:08:13 +02:00
slawkens
0f3d2424ce Fix release.sh (some warning) 2021-06-08 23:18:31 +02:00
slawkens
8ecd8a10c0 Remove unneeded escape 2021-06-08 19:19:12 +02:00
slawkens
3cc3e3a8e9 Fix guild back buttons (change logo & motd) 2021-06-08 19:19:02 +02:00
slawkens
be1086bcba small adjustment in news.php 2021-06-08 19:18:45 +02:00
slawkens
f9abe9a8e3 Fix create character when admin (any case is allowed now) 2021-06-08 19:18:25 +02:00
slawkens
632ecb6d20 Fix forum table style (boards & thread view) 2021-06-08 19:18:13 +02:00
slawkens
db554df041 Small improvement to plugins.enabled check 2021-06-08 19:18:01 +02:00
slawkens
7300e4f1ad Cleaning unused variable 2021-06-08 07:44:16 +02:00
slawkens
9b84532e57 Reformatting code (moving javascript to own file) 2021-06-08 07:43:06 +02:00
slawkens
14870d74df This was wrong.. 2021-06-08 07:41:28 +02:00
slawkens
9c7794fe13 Reformatting code 2021-06-08 07:29:04 +02:00
anyeor
d9526d4021 Update 404 response (#163)
Updating for new SPL standard.
2021-06-05 05:19:00 +02:00
slawkens
454e09ec3d Use reference instead of new array 2021-05-23 22:59:49 +02:00
slawkens
c687f64ced Fix color style for different templates 2021-05-23 03:14:17 +02:00
slawkens
80623580f2 Fix creating account if $npcCheck is enabled 2021-05-23 02:55:27 +02:00
slawkens
135f393fc4 Change team page title 2021-05-23 02:54:33 +02:00
slawkens
e03da2876c Fix forum boards white color style
So it works on all templates
2021-05-23 02:53:57 +02:00
slawkens
44fff9dcd1 Merge branch 'develop' into feature/admin-bar 2021-05-01 15:04:04 +02:00
slawkens
02f993baea feat: Login By Email 2021-05-01 10:16:43 +02:00
slawkens
f9302d4f9d Require Parsedown class through composer 2021-05-01 08:54:46 +02:00
slawkens
780f8d193f bcsub is not needed here
bcmath module is not required anymore
2021-05-01 01:21:11 +02:00
slawkens
4e85f857a4 Fix #158 Thanks @Misztrz 2021-05-01 01:04:42 +02:00
czbadaro
ea035136e1 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>

(cherry picked from commit 9d7854dda6)
2021-04-23 01:14:32 +02:00
Lee
9d8f398d9f update validator name check
-added character_name_npc_check to config.php
-added npc name check to validator
2021-03-09 23:42:21 +00:00
Lee
31f0050f4e Admin - Dataloader updates
-Npc Lib added
-Admin Dataloader updated to load NPC names into a cached array.
-Spinner Updated to a loading button.
2021-03-09 23:38:23 +00:00
Lee
7ce005341e Twig filter (timeago)
Added twig filter timeago for dates.
2021-03-09 23:17:24 +00:00
slawkens
84447ef178 Remove unsupported versions from travis 2021-02-23 23:22:03 +01:00
slawkens
b99d3b4960 Forgot this 2021-02-23 18:57:03 +01:00
slawkens
0fc64478e0 PHP 7.1 is now required
Older versions are outdated anyways
2021-02-23 15:15:34 +01:00
slawkens
e7a3d563aa Update .travis.yml
(cherry picked from commit 856507fb66)
2021-02-16 01:41:00 +01:00
slawkens
bda020ef93 Attempt to fix travis build
(cherry picked from commit d019fbc050)
2021-02-16 01:40:55 +01:00
slawkens
f0c136c421 More fixes for PHP 8.0 2021-02-16 01:40:24 +01:00
slawkens
5fa1321619 Fixes to database version and monsters table 2021-02-16 01:12:45 +01:00
slawkens
792ec17d18 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 00:56:25 +01:00
slawkens
19ffd57b34 Minimum PHP 5.6 is now required (composer part) 2021-02-16 00:32:16 +01:00
slawkens
ebda456862 Minimum PHP 5.6 is now required 2021-02-16 00:31:13 +01:00
slawkens
5bd5aa0edf Fix compatibility with PHP 8.0 (latest XAMPP)
Solution by doctrine developers
2021-02-16 00:28:54 +01:00
slawkens
6b07d56627 Fix setPremDays for latest TFS
Fixes editing account in admin panel
2021-02-15 21:05:08 +01:00
slawkens
15d381adfd Fixed account getPremDays() function for latest TFS
This fixes account management + signature
2021-02-15 20:57:24 +01:00
Lee
23b44d6c8a #142 Guildnick fix
Fixes the Guildnick not showing in the guild pages.
2021-02-14 12:50:10 +00:00
slawkens
e3f2abc06e Fix parsing empty strings in config.lua (with comments)
(cherry picked from commit eed490507c)
2021-02-13 22:57:11 +01:00
slawkens
3d73de13d8 Fix headling.php cannot find font 2021-02-13 22:34:46 +01:00
slawkens
71f7bb2e75 Fix typo 2021-02-13 22:07:54 +01:00
slawkens
ebe900fca8 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:53:40 +01:00
slawkens
5a8bcec014 Fix installer choosing name, when config.php is not loaded yet
It has been almost 1 month there, seems no one is using develop branch ;)
2021-02-13 21:44:49 +01:00
Lee
a1c7c2768c Delete char with house #149
oops, didn't mean to submit that last push, I hadn't added the empty check. (noob moment)
quick fix for #149
2021-02-02 14:42:30 +00:00
Lee
565e6e3a3d Delete char with house #149
Patch to stop players deleting themselves if they have a house
Fixes #149
2021-02-02 14:13:36 +00:00
slawkens
855e9aa3b9 Fix: do not count deleted characters
On create new character page
2021-01-20 19:46:07 +01:00
slawkens
a271edec47 Forgot this.. 2021-01-20 18:03:30 +01:00
slawkens
81b293a5a6 Fixes to character name validation
Admin should be able to create any name
Also fixes to config.character_name_min_length being ignored
2021-01-20 18:02:07 +01:00
slawkens
8b41e144f8 Open/Closed Website Status now changes dynamically 2021-01-18 02:17:39 +01:00
slawkens
a41f653e05 Move Admin module statistics to Twig 2021-01-18 02:00:46 +01:00
slawkens
f24ff295e8 Add alt="icon"
Don't like those warnings in IDE :P
2021-01-18 01:48:21 +01:00
slawkens
b399bee3ac Cancel button should return to menus main screen 2021-01-18 01:47:56 +01:00
slawkens
8f88c82a13 Add "table-responsive d-md-table" class
Makes tables in admin panel responsive
2021-01-18 01:47:11 +01:00
slawkens
d8ac88b7d9 Add more clients to clients.conf.php 2021-01-18 01:45:52 +01:00
slawkens
443c5a80b4 Add ext-dom to composer.json 2021-01-17 20:10:21 +01:00
slawkens
ba56ef5e33 Add some badges to README.md
(cherry picked from commit d3850280f4)
2021-01-17 17:45:32 +01:00
slawkens
b24370e7ed Fixed the check if vocations.xml were correctly loaded 2021-01-17 17:44:30 +01:00
slawkens
b2b0b31168 Remove facebook.js, replace with direct live link
This fixes some console errors
2021-01-07 23:37:47 +01:00
slawkens
1e969f8d8a Update index.php 2021-01-07 22:43:25 +01:00
slawkens
bca098e074 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:15 +01:00
slawkens
98bd51436b Unified naming 2021-01-07 11:47:57 +01:00
slawkens
1fa4b1e660 Remove some useless maxlength attribute 2021-01-04 22:06:50 +01:00
slawkens
04a36b1d11 Rename to Recovery Key 2021-01-04 22:06:26 +01:00
slawkens
611d6f505d Add option to send mail to account with Mailer
"Send Mail" link next to Email input in account editor
2021-01-04 21:34:52 +01:00
slawkens
62b485abf9 Update .gitignore 2021-01-04 19:28:18 +01:00
slawkens
61eae7d7c4 Add ./login.php to .gitignore 2021-01-04 19:17:30 +01:00
slawkens
af161b5143 Fix: PHPMailer is included through composer now 2021-01-04 19:17:07 +01:00
slawkens
d5880eac8c Use BASE_URL instead of template_path for icons
Cause they are located in images/news folder
2021-01-04 16:35:01 +01:00
slawkens
02d6ab5fe7 Add lastCheck full date to Server Status panel 2021-01-04 16:32:25 +01:00
Lee
5547ccffd6 Twig updates
Moved non-global functions from twig back into PHP.
2021-01-04 13:11:18 +00:00
Lee
8c06bd1738 function Truncate added
missing function added to functions lib.
2021-01-04 12:42:13 +00:00
Lee
469a8c1017 AdminPanel - Template Fix
Fixed a bug with $status['online'] not being filled and showing invalid badge due to error.
2021-01-04 12:24:23 +00:00
Lee
bb3602073c AdminPanel updates - changelog
-Admin menu updates
-Moved getchangelogtype/where to functions file and added to twig
-Added changelog editor to admin panel and updated changelog page
-Renamed the changelog md reader to clmd and edited the version file.
2021-01-04 12:23:36 +00:00
slawkens
6c6af59b22 Add ext-gd to composer.json 2021-01-04 08:27:45 +01:00
slawkens
a8a36c73e6 Fix notice about premend 2020-12-30 00:27:42 +01:00
slawkens
98b1d854f9 Refactor some line 2020-12-30 00:24:14 +01:00
slawkens
1ada2317fd Add support for accounts.premium_ends_at (Latest tfs 1.x) 2020-12-30 00:10:49 +01:00
slawkens
40722c8c30 Password can now contain any characters
Also added limit of 29 characters (client limitation)
2020-12-30 00:10:11 +01:00
slawkens
ff9e255f1b On prod it won't display any PHP errors
As suggested by PHP Manual
2020-12-29 22:11:40 +01:00
Lee
fbe9c31d10 config update
renamed and cleaned up the settings names for easier comprehension
2020-12-29 17:50:10 +00:00
Lee
0aed705a6a migration 31 fix.
Some mysql dbs won't allow text to have a default value.
2020-12-29 17:43:17 +00:00
Lee
06e864c954 added between()
Added missing function from creature update.
2020-12-29 17:36:16 +00:00
slawkens
1d68d013df Add json as required extension 2020-12-29 15:00:06 +01:00
Lee
8e6bc73ca6 Creature page overhaul
Updates the creature pages to show more information.
You will need to reload your creatures.
-modifies database with migration 31
-small customisations are allowed via config file.
-functions added, getMonsterLink, getItemRarity, getCreatureImgPath, left, right,
-added functions to twig.
-view elements, immunities, summons, voices, loot, pushables, canpush, canwalk, runonhealth,hostile,attackable,rewardboss,defense,armor
-filter bosses
-show list as picture preview or names list
2020-12-28 16:37:03 +00:00
Lee
7e0fded595 gitignore functions_custom
Adds system/functions_custom.php to the git ignore list.
2020-12-28 16:24:25 +00:00
Lee
c8443228fb Allow template pages
Checks if page exists in template directory and loads that page first instead of system/pages/
This allows users to make their own changes without modifying the original files for when doing updates.
2020-12-28 16:22:41 +00:00
slawkens
64fe0062ee Merge pull request #146 from fernandomatos/template/tibiacom
Fixes and improvements at template's network box
2020-12-26 23:33:44 +01:00
Fernando Matos
3b78516ef2 Add SSL on external image requests of items and outfits (#145) 2020-12-26 23:32:17 +01:00
Fernando Matos
8f345126f7 Add conditional script loading for social networks 2020-12-26 18:30:26 -03:00
Fernando Matos
daaa472dfe Remove unnecessary top player retrievement at network box 2020-12-26 18:14:36 -03:00
Fernando Matos
87f35da3b6 Fix network box showing without social networks specified 2020-12-26 18:04:22 -03:00
Fernando Matos
6f42a60e59 Add a brand new charming installation (for version 0.9) (#144)
* Add a brand new charming installation

* Fix alert position in setup requirements validation

* Add some missing definitions

* A distinction between bootstrap CSS classes and myaac classes

For CSS-styled messages

* Remove unused functions for messages

Co-authored-by: slawkens <slawkens@gmail.com>
2020-12-22 07:47:47 +01:00
slawkens
3beedc1747 New configurable: outfit_images_wrong_looktypes 2020-12-21 01:59:32 +01:00
slawkens
6603815a81 Update bans.php 2020-12-21 01:54:17 +01:00
slawkens
c1027d3663 Fixes on new highscores
Fixed link to next page on first visit default highscores
Fixed position of "No records yet."
Also count pages is starting from 1 now (not from 0 like before)
2020-12-21 01:54:00 +01:00
slawkens
6cec5ba5bf Add required extensions and PHP version to composer.json 2020-12-20 12:13:13 +01:00
slawkens
d70b70b63c Update .editorconfig 2020-12-20 12:12:41 +01:00
slawkens
7d73e3cd98 Refactor code in delete_character.php 2020-12-19 23:25:46 +01:00
slawkens
5087fc4a00 You cannot delete character more than twice (Thanks Okke)
(cherry picked from commit 7fd784b2f6)
2020-11-24 18:12:49 +01:00
Lee
30cdb1ba73 Dashboard modules updated
More flexibility and additions on the dashboard modules.

-Statistics: Accounts, Players, Monsters, Guilds, Houses
-Website Status: Maintenance
-Server Status: name, client, map, monsters loaded, MOTD,

default:
'admin_panel_modules' => 'statistics,web_status,server_status,lastlogin,created,points,coins,balance',
2020-11-07 14:36:12 +00:00
Lee
0f6612904e Updated Admin
Updated the frameworks to latest git versions
-AdminLTE v3.1.0-pre
-Bootstrap v4.5.2
No major changes just bug fixes
2020-11-07 14:13:20 +00:00
Lee
e5b5b4d3ef Template update
Adds server name to the side menu, this hides when the side menu collapses.
2020-11-07 13:56:59 +00:00
Lee
9bc63bb55c Datatables update
I noticed if the datatables files wasn't cached some pages that didn't use it would take slightly longer to load whilst it downloaded it.
This will only write the js and css files onto the page if $use_datatable = true;  is set on the page.
See accounts/news/players pages for examples.

It wasn't a massive performance loss for the user but not needed if those pages are hardly ever used.
2020-11-07 13:53:11 +00:00
Lee
dcf83d5608 Update functions.php
Moved to bottom of the page so users can call functions code from custom_functions.
2020-11-06 14:42:13 +00:00
Lee
8fe82bb5c0 adminmenus update
moved visitors links to the logs section.
2020-11-06 14:20:34 +00:00
Lee
6f74029d76 Update spells.php
$canEdit was removed in a previous git.
Removing this fixes the error.
2020-11-06 14:18:08 +00:00
Lee
01e3d366ba Update admin.menus.js.html.twig
replaced del.png with a font awesome icon to go with the style of the rest of the icons on page.
2020-11-06 14:17:40 +00:00
Lee
41d5b4a22f Update basic.css
edited focus as browsers are displaying a thick black line. this is now a thing dotted outline.
2020-11-06 14:17:19 +00:00
Lee
7814636caf Added custom_functions file
Added a custom functions file for users to include any of their custom functions.
2020-11-06 14:16:54 +00:00
slawkens
cf2c5e36bc Feature/better highscores (#141)
* Move highscores to twig

* Add highscores frags for TFS 1.x

* Change $config to config()

* Cache highscores

The most asked and long awaited feature? :>

* Fix highscores_per_page and rename configurable

* Fix next page link (some typo)

* Fix too many players being shown

* Fix when changing config.highscores_per_page

* Update system/pages/highscores.php

Co-authored-by: whiteblXK <krzys16001@gmail.com>

Co-authored-by: whiteblXK <krzys16001@gmail.com>
2020-11-02 23:34:29 +01:00
slawkens
5d5875d540 Bans page working for TFS 1.x + move to Twig (#140)
* Bans page working for TFS 1.x + move to Twig

* Remove some debug code

* Add some protection

* Better check.
2020-10-30 06:52:50 +01:00
slawkens
95c2adc02e Remove twig, phpmailer & semver 2020-10-27 07:35:00 +01:00
slawkens
73f1ba10f9 Use composer for some libraries (twig, phpmailer, semver) 2020-10-27 07:34:05 +01:00
slawkens
9fe419cfe7 Creatures and monsters are now reloaded in Admin Panel 2020-10-27 07:33:21 +01:00
slawkens
41e24ca535 Remove .gz extension if found in map file 2020-10-27 07:25:38 +01:00
slawkens
42a628731d move characters link to twig 2020-10-27 07:24:21 +01:00
slawkens
2ba702df21 Update .editorconfig 2020-10-26 23:44:25 +01:00
slawkens
0171962306 SET @myaac_database_version in schema.sql 2020-10-24 05:40:58 +02:00
slawkens
2daa42e124 Add accept=".zip" to plugin upload file 2020-10-24 05:30:04 +02:00
slawkens
abfd2c94f5 Move template_header and change_template to twig 2020-10-18 06:54:54 +02:00
slawkens
fd51fa7779 Add some notice about recaptchas versions 2020-10-16 20:28:05 +02:00
slawkens
1a36aa8904 [WIP] New GoogleReCAPTCHA code
Support for v3
v2-invisible doesn't work yet
2020-10-15 19:55:12 +02:00
slawkens
881a28138a More compress .png 2020-10-15 08:09:40 +02:00
slawkens
26fb1698b8 Compress .png files (Almost 40% savings) 2020-10-15 07:59:48 +02:00
slawkens
13d7dd98bd Remove unused files 2020-10-15 07:53:24 +02:00
slawkens
672a9f1712 Remove unused files 2020-10-15 07:37:32 +02:00
slawkens
2e560ac081 Added some var annotations 2020-10-14 13:11:03 +02:00
slawkens
39d1127cf1 You can now disable status checking for testing purposes
Useful for local testing when there is no server running
2020-10-14 13:10:22 +02:00
slawkens
13586e664f config.status_timeout can be floating number 2020-10-14 13:08:39 +02:00
slawkens
6e6db543f7 Compress background-artwork.jpg (from 534 KB to 275 KB)
Without losing the quality
2020-10-14 13:07:46 +02:00
slawkens
ea8ae2372e This is better inline solution to not displaying Admin Menus
by @Leesneaks
2020-10-12 22:46:44 +02:00
slawkens
928de13459 Fix two boxes being show on email_change_cancel
(cherry picked from commit 8518afe70d)
2020-10-12 22:31:31 +02:00
slawkens
e213c3e7d8 Fix when adding poll = template tibiacom broken
With Exception and red message
2020-10-12 21:58:20 +02:00
slawkens
65b4b2d183 Rename file to admin.data.html.twig 2020-10-09 23:38:33 +02:00
slawkens
94b145b215 New class: DataLoader (loads data from server)
Also combined some code responsible for loading server data
2020-10-09 23:37:24 +02:00
slawkens
6c9e6af154 Move installer.js to main tools 2020-10-09 23:18:22 +02:00
slawkens
b5736ad559 Rename items.php to server data.php 2020-10-09 22:29:19 +02:00
slawkens
ab3912b378 Save towns as plain PHP File in cache folder
+ Also load them on install + on reload items
= better performance when in dev mode
2020-10-09 22:27:48 +02:00
slawkens
3090989dea Fixed accounts editor for OTServ, which dont have accounts.group_id 2020-10-09 00:37:28 +02:00
slawkens
92314b8dac Fix some PhpStorm editor error message 2020-10-09 00:36:47 +02:00
slawkens
a52396008d Automatically load towns from .OTBM file
Takes up to 10 seconds for otservbr on my PC
Taken from old Smart AAC - class SpawnsReader.php
2020-10-09 00:12:07 +02:00
slawkens
ae7350e3a0 Fix typo: length 2020-10-08 00:15:50 +02:00
slawkens
c30300c368 New configurable: item_images_extension 2020-10-08 00:13:35 +02:00
slawkens
ed3d415c05 Change wrong table header: Description -> Version 2020-10-08 00:03:53 +02:00
slawkens
bb353d617a Admin Panel: Show Mailer menu only if config.mail_enabled 2020-10-07 23:54:03 +02:00
slawkens
d7f41748ad Forgot to translate requirements 2020-10-07 23:46:14 +02:00
slawkens
915ae47971 Add missing polish timezone translation on install 2020-10-07 23:40:33 +02:00
slawkens
40b151b4c5 Remove whitespaces 2020-10-07 23:36:21 +02:00
slawkens
1992410a7b This info is useless 2020-10-07 23:36:10 +02:00
slawkens
48874f5b07 Disable mail_enabled by default on clean install
Causes too many issues when no smpt server available and other options are not configured
2020-10-07 23:22:02 +02:00
slawkens
2144a4eb7c Add success message on config.local.php save 2020-10-07 23:19:10 +02:00
slawkens
cbdbf11edc Remove config.local.php from optional dirs 2020-10-07 23:09:33 +02:00
slawkens
515db04023 Some changes in installation optional and required dirs
system/logs and system/cache are required to be writable

config.local.php, images/guilds and images/gallery are optionally writable, without them some feature will not work
2020-10-07 22:48:06 +02:00
slawkens
d3811f1bf1 Forgot to remove 2020-10-07 22:44:57 +02:00
slawkens
815fedf8e7 Add 'gd' as optional extension
Without it player signature will not work
2020-10-07 22:44:43 +02:00
slawkens
929a7b9cfa Add pdo_mysql as required extension
+ Some code refactoring
2020-10-07 22:43:17 +02:00
slawkens
f85361dbc5 Some optimisations in Plugins::installMenus 2020-09-25 07:49:26 +02:00
slawkens
cb6509d09d Cache for 365 days if $ttl not set 2020-09-25 07:26:30 +02:00
slawkens
f09c129c6d Remove duplicated line (in $db->select)
Caused some errors when using $db->select function (news editing for example)
2020-09-17 23:17:59 +02:00
slawkens
602a4aa835 Feature/experience stages twig (#135)
* Move Experience Stages to Twig

* Change name to underline (standard)

* Update system/pages/experiencestages.php

Co-authored-by: whiteblXK <krzys16001@gmail.com>

Co-authored-by: whiteblXK <krzys16001@gmail.com>
2020-08-24 19:36:43 +02:00
slawkens
14d5c6311b Update config.php 2020-07-14 01:05:21 +02:00
slawkens
289dd3c170 New configurable: guild_description_default 2020-07-14 01:05:09 +02:00
slawkens
60eac97945 Fix creating very uncommon (bugged) account names
(cherry picked from commit 0326657d60)
2020-07-07 01:23:53 +02:00
slawkens
de1d6b9629 Fix #131
(cherry picked from commit fcff820858)
2020-07-07 00:54:34 +02:00
whiteblXK
722264a083 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>
(cherry picked from commit dc536f0fc0)
2020-07-07 00:54:29 +02:00
slawkens
357d487af7 Change hasTable -> hasColumn 2020-07-07 00:15:54 +02:00
whiteblXK
1b802b040d Fixed bug with showing hidden character
(cherry picked from commit f3061a0e74)
2020-07-07 00:02:33 +02:00
slawkens
25afbd935c Fix #132
(cherry picked from commit d4222e98e6)
2020-07-03 23:25:08 +02:00
slawkens
e61bfd2722 My fault (wrong email) forgot to change :P 2020-07-03 22:51:58 +02:00
slawkens
fe571cbef3 Fix account create when account_mail_verify is enabled 2020-07-03 22:43:53 +02:00
slawkens
a7c5cb8f5a Add some notice about Email validation 2020-07-03 22:15:04 +02:00
slawkens
dedb96ef4a Fix for CloudFlare IP detection
(cherry picked from commit b3b6d0ff5d)
2020-07-03 20:39:20 +02:00
slawkens
ee49efd215 This is better way of doing the check for blank & color 2020-06-26 23:47:14 +02:00
slawkens
56a35eb864 Fix network_twitter link in tibiacom template 2020-06-20 08:50:29 +02:00
slawkens
d478fe0c71 Move $menus to menus.php
Also fix active link when menu item has subpage
2020-06-09 01:32:00 +02:00
slawkens
03467ea64e Update change_password email to be more informal 2020-06-06 19:51:17 +02:00
slawkens
3368fbd058 New config: account_mail_block_plus_sign
Block emails with '+' signs like test+box@gmail.com (help protect against spamming accounts)
2020-06-06 19:37:05 +02:00
slawkens
e84c6f7a24 Fix XSS in character search
(cherry picked from commit dfc70c098f)
2020-06-06 18:33:38 +02:00
slawkens
a0006bad73 CHANGELOG.md cleanup for 0.9.0 2020-06-06 18:05:10 +02:00
slawkens
2458393d22 Add Plugins::installMenus function 2020-06-06 17:33:34 +02:00
slawkens
787416e552 Remove useless title_separator from config 2020-06-06 17:15:55 +02:00
slawkens
1c6b241239 Add $limit parameter to $db->select method 2020-06-06 17:13:55 +02:00
slawkens
7469d520c9 Add $member var annotation 2020-06-06 17:13:51 +02:00
slawkens
7e00e62427 Update version to 0.9.0-dev 2020-06-06 09:08:40 +02:00
slawkens
0e39a969c3 Move register DATABASE_VERSION into schema.sql
Caused migrations being fired when user manually imported database
2020-06-06 07:33:05 +02:00
slawkens
0ad1647930 Remove unused myaac_videos table 2020-06-06 07:21:35 +02:00
slawkens
d7fc45a72d Very first version of admin bar 2020-06-06 07:17:09 +02:00
slawkens
54dfb642b1 Fix #123 Guild Invite not working on otservbr-global 2020-06-03 21:38:17 +02:00
slawkens
40626d0f42 Revert some change I did
Causing "'" and "-" being accepted as first character in player name
2020-06-03 21:22:14 +02:00
slawkens
523afccb51 Avoid ERR_TOO_MANY_REDIRECTS on template change 2020-06-03 20:01:26 +02:00
slawkens
1087aefe0a One more hook 2020-06-01 23:09:27 +02:00
slawkens
9b66edc148 Add some additional hooks to characters.html.twig 2020-06-01 22:41:32 +02:00
slawkens
2c09b0ae86 Fix #128 (Remove MyISAM engine) from migration scripts 2020-06-01 09:47:53 +02:00
slawkens
8de8ad13bf Fix message() function when executed in CLI 2020-06-01 08:39:13 +02:00
slawkens
70bd442bb0 Add new constant: IS_CLI
Also fixed some warnings when running in CLI mode
2020-06-01 08:34:11 +02:00
slawkens
5250b3189b Fix #126 (Max count and chance not shown) 2020-05-27 22:21:45 +02:00
slawkens
2534651e20 dummy me.. thanks @gerotib 2020-05-27 01:32:15 +02:00
Lee
f46a42023f Update admin.login.html.twig
- fix for label not ticking the box when remember me text clicked
2020-05-25 23:34:04 +01:00
Lee
e2ab301340 Update version.php
- removed extra line that is added when using a newer version than official release.
2020-05-25 23:33:06 +01:00
slawkens
700f835243 Fix #125 (wrong mana of character samples)
Should be 90.
2020-05-26 00:15:28 +02:00
slawkens
9ce7162a04 Remove duplicated code 2020-05-25 09:03:40 +02:00
slawkens
af85a8b711 Display "Unknown Town" when town not found 2020-05-25 00:15:30 +02:00
slawkens
cd58008a0f Rewrite towns support for TFS 1.3
Won't show warning anymore
2020-05-25 00:09:06 +02:00
slawkens
1f6bd975d0 Add error_reporting in admin panel
Same as in main page
2020-05-25 00:04:41 +02:00
slawkens
b3556c008e Add note which template menus are being edited 2020-05-24 19:34:48 +02:00
slawkens
dbe83f8a74 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
2020-05-24 18:43:26 +02:00
Lee
d1c50f00a0 Admin template update
- Reformatted with short array syntax.
- Removed duplicate menu item
- Removed 'menu' from array - use 'link' as the array or link.
- Added logo
2020-05-23 03:03:15 +01:00
Lee
bd9d3154db Code cleanup
-Removed unneeded/duplicate dependency files.
2020-05-23 02:54:58 +01:00
Lee
1f0b4425a4 Code cleanup
- Relocated html5shiv.js (v3.7.3) and respond.js (v1.4.2)
- Removed unneeded/duplicate dependency files.
2020-05-23 02:47:12 +01:00
Lee
47bfea4c56 Dependency Update
Updated:
- AdminLTE (v3.0.4)
- Bootstrap (v4.4.1)
- DataTables (v1.10.21)
-- Split DataTables for admin pages and website to fix layout formatting issues.
2020-05-23 02:28:38 +01:00
slawkens
416de6b584 Update admin.items.html.twig 2020-05-14 23:16:05 +02:00
Lee
3d3d141b25 bootstrap JS fix
-fixes the editor tabs etc not working
2020-05-10 18:11:38 +01:00
slawkens
2ff56c17e3 Added some fancy spinner to items loader 2020-05-10 12:39:06 +02:00
slawkens
fb326d0354 Change input type of account_login to text
This fixes autofill by Chrome and other tools
2020-05-10 11:45:55 +02:00
slawkens
e84933cf26 Ignore index.html in logs viewer 2020-05-10 11:45:04 +02:00
slawkens
8e04328482 Add executing missing migration on install
This fixes missing rules on clean install
2020-05-09 14:07:53 +02:00
slawkens
d148b71f0f Fixes in create new character nick
+ fixed config.character_name_min/max_length being ignored in change_name.php
2020-05-09 14:07:53 +02:00
Lee
4e68838172 CreateChar Fix
-checks if name has double space on create character (#121)
2020-05-07 19:09:56 +01:00
Lee
d281fc588b Fixes
-As requested the datatables and bootstrap files have been moved into the tools folder
-Fixed spells and creatures datatable.
-Double space in name fix.
2020-05-07 01:36:56 +01:00
slawkens
1799ef42a7 Add system/data to .gitignore 2020-05-06 19:55:31 +02:00
slawkens
a0d5a863e0 Remove useless tabbing
Just removed tabs, nothing else..
2020-05-01 13:03:34 +02:00
slawkens
df59b104db Fix cancel change email request
Thanks to OtLand user anyeor
2020-04-21 00:22:50 +02:00
slawkens
e7e327c238 Fix some small typos on admin.menus 2020-04-20 23:31:06 +02:00
slawkens
ee6e68d0bf Fix exception when characters.frags enabled on TFS 1.x 2020-04-20 22:35:41 +02:00
Lee
d7333b3f21 Update accounts.php
Fix for accounts.php type not found.
2020-04-06 15:08:52 +01:00
Lee
375bd58a0c Admin Update
-Account limit fix
-Player editor full player table.
2020-04-02 20:54:20 +01:00
Lee
cddd915adf Admin fixes
-login/logout error fix
-debugging code removed
2020-04-02 18:44:15 +01:00
Lee
9e0ad271f6 Update adminlte.min.css
case sensitive fix
2020-04-02 15:54:16 +01:00
Lee
a0afeb2a7a Update accounts.php
-Bans table error
-Fix for #114
2020-04-02 14:52:38 +01:00
slawkens
7c208b38ed Add example quest 2020-04-02 15:26:39 +02:00
Lee
eaa11c68f3 Admin Panel Updates
- Updated Admin Panel to Bootstrap 4.
- Code cleanup
- Rewrote menu generation code
- Added top 10 coins, top 10 premium points, last 10 logins to modules page.
- Added full account list to Account editor
- Added load outfits from XML to player editor and lists all enabled outfits in editor (will default to textbox if array of outfits do not exist)
- Added tabs to account editor - account, characters,store history, bans(this is based off the bans.php page so will not work on TFS 0.2/1.0)
- Updated datepickers to display the actual date rather than unix time.
- Added last 10 posts to player editor
2020-03-31 02:03:16 +01:00
844 changed files with 16817 additions and 49318 deletions

View File

@@ -11,4 +11,9 @@ insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
indent_style = tab
[{composer.json,package.json}]
indent_style = space
[package.json]
indent_size = 2

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

@@ -0,0 +1,13 @@
name: PHP Linting
on:
pull_request:
branches: [master, develop]
push:
branches: [master]
jobs:
phplint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: michaelw90/PHP-Lint@master

23
.gitignore vendored
View File

@@ -1,11 +1,19 @@
Thumbs.db
.DS_Store
.idea
# composer
composer.lock
vendor
# npm
node_modules
# created by release.sh
releases
tmp
releases
config.local.php
PERSONAL_NOTES
# all custom templates
templates/*
@@ -27,6 +35,14 @@ system/cache/*
system/logs/*
!system/logs/index.html
# data
system/data/*
!system/data/index.html
# php sessions
system/php_sessions/*
!system/php_sessions/index.html
# plugins
plugins/*
!plugins/.htaccess
@@ -35,5 +51,8 @@ plugins/*
!plugins/account-create-hint
landing
# system
system/functions_custom.php
# others/rest
system/pages/downloads.php

View File

@@ -1,19 +1,18 @@
language: php
php:
- 5.6
- 7.0
- 7.1
- 7.2
- 7.3
- 7.4
- 8.0
cache:
directories:
- $HOME/.composer/cache
before_script:
- composer require jakub-onderka/php-parallel-lint --no-suggest --no-progress --no-interaction --no-ansi --quiet --optimize-autoloader
- composer require php-parallel-lint/php-parallel-lint --no-suggest --no-progress --no-interaction --no-ansi --quiet --optimize-autoloader
script:
- php vendor/bin/parallel-lint --no-progress --no-colors --exclude vendor .
- php vendor/bin/parallel-lint --no-progress --no-colors --exclude vendor --exclude "system/libs/pot/OTS_DB_PDOQuery.php" .

View File

@@ -1,652 +1,9 @@
# Changelog
## [0.8.2 - x.x.2020]
## [0.9.0 - x.x.2020]
### Added
* $_SERVER['REQUEST_URI'] to database.log
### Changed
* account_login input type from password to text
### Fixed
* Updating template menus on template change
* Account change info when config.account_country is disabled
* Show character indicator in check_name.js
* Houses list View button
* Fix OTS_House houseid parameter
## [0.8.1 - 10.03.2020]
### Added
* Support for Nostalrius OTS
### Changed
* Move TODO to wiki
* .tooltip css class to .item_image (bootstrap conflict)
### Fixed
* Reloading of creatures/monsters throwing an exception
* Loading custom pages with old Gesior variables [#108](https://github.com/slawkens/myaac/issues/107)
* Some weird behaviour with installation of plugins
* CHANGELOG.md loading in Admin Panel
* spells displaying when level = 0
* Some PHP warnings and notices
## [0.8.0 - 19.02.2020]
### Added:
* new Awesome Bootstrap Admin Panel by Lee (@Leesneaks)
* using Bootstrap 3
* all existing pages were adjusted
* new editor: Accounts
* improved editor: Players
* new Reports View page
* Modules directory, which can be added using Plugins (@Leesneaks, @whiteblXK)
* move News Management here (@whiteblXK)
* interactive player outfit chooser (@tobi132)
* added Highscores by balance
* possibility to define colors and "Open in New Tab" on Template Menus (needs to be supported by Template)
* support for database persistent and socket connections (performance boost)
* Team page - display outfits of the players (configurable)
* added clear_cache.php, send_email.php bin commands (@slawkens, @tobi132)
* added locale pt_br (@ivenspontes)
* added load time into items & weapons loading admin page
* new, beautiful exception handler
* added travisci to prevent mistype (@gpedro, #89)
* added showing database name into installation script (@tobi132)
* compatibility with old z_ gesior table (@tobi132, #46)
* added nginx-sample.conf, .editorconfig, VERSION
* database towns table support for TFS 1.3 (@tobi132)
* added enable_tinymce option to Pages editor
### Fixed:
* account login redirect with special chars (like '&' and '?')
* black skull info at serverInfo (@tornadia)
* set correct limit at lastkills page from config (anyeor from OtLand)
* myaac_monsters table column loot problem (#79)
* players column deleted install description (@gpedro, #91)
* experience table being to wide and buggy on some templates (@tobi132, #90)
* fix errors with .htaccess files
* added index.html to prevent indexing the folder by mod_index
### Changed:
* Environment is now configurable by env setting (Significantly better load times with 'prod')
* replace spells, monsters tables with JavaScript Sortable Tables - DataTables (@Leesneaks)
* change default MySQL Storage Engine to InnoDB and Default Character Set to utf8
* updated OTS_House class to support latest TFS 1.x (new columns)
* updated monster images to the original ones from tibia.com
* increased the minimum length (3 -> 4) and decreased the maximum length (25 -> 21) of the New Character Name (by @vankk)
* use $db->exec instead of $db->query optimisation
* move items from database to Cache_PHP (Much more faster load time)
* allow simultaneous loading of config.ini and config.php in templates
* updated copyright year and SSL link (@EPuncker, #88)
* move commands, rules and downloads pages into database (@tobi132)
* better view of guilds (new buttons, table look and feel) (@tobi132)
* remove stupid alerts on account create
* remove .dist extension from .htaccess
### New Configurables (config.php)
* env (Environment)
* account_create_auto_login (Auto Login after Create Account - Registration)
* account_create_character_create (Create Character directly on Create Account page) (@tobi132)
* footer_show_load_time (display load time of the page in the footer)
* database_socket (Connection via Unix Socket)
* database_persistent (Database Persistent Connection)
* database_log (Logging of Database Queries)
* admin_panel_modules (Modules displayed in Admin Panel Dashboard)
* status_timeout, status_interval
* smtp_debug (More info about SMTP errors in error.log)
* team_display_outfit (Display outfit of the team members on teams page)
* highscores_balance (Display highscores by balance)
* character_name_min/max_length (Minimum and maximum length of character name)
* characters.deleted (display deleted characters on characters page)
### Forum:
* show image in full screen on click
* show user avatar (outfit) in posts
* replaced forum actions links (move, remove, edit, quote) with images
* redirect directly to the thread on user login (on new reply)
### Installer:
* AJAX loader for the important stuff
* create admin account: ask for e-mail + character name
* load items & weapons
* check user IP on install to prevent install by random user
* remember status of the installation
* remember language on first step (welcome)
* ask user for timezone
* auto detected browser language in select language
### Plugins
* sandbox for plugins, don't install when requirements are not satisfied
* allow comments inside plugin json file (php style)
* new require options for plugins: (look into example.json)
* require database version, table or column of the MyAAC schema
* require php-extension
* require semantic-version (like in composer.json)
* new hooks: LOGIN, LOGIN_ATTEMPT, LOGOUT, HOOK_ACCOUNT_CREATE_*
### Cache
* php 7.x APCu cache support (faster cache engine)
* new cache engine: plain PHP (is good with pure php 7.0+ and opcache)
* cache lastkills.php, $db->hasTable, $db->hasColumn, hooks and template menus
* stop using global $cache variable, use Singleton pattern instead
### Twig
* move pages to Twig templates: team, lastkills, serverinfo, houses, guilds.list, guild.view, admin.logs, admin.reports (@whiteblXK, @tobi132)
* replace "$twig->render()" with "$this->display"
* move Twig functions to separate file
* move tibiacom boxes to Twig templates
* allow Pages to be loaded as Twig template (this allows using Twig variables in Pages) (@tobi132)
* allow string to be passed to hook twig function
### Functions
* config($key), configLua($key)
* clearCache()
* OTS_Account:
* getCountry()
* setLastLogin($lastlogin) (@Leesneaks)
* setWebFlags(webflags) (@Leesneaks)
* OTS_Player:
* getAccountId()
* countBlessings() (@Leesneaks)
* checkBlessings($count) (@Leesneaks)
* is_sub_dir (in system/libs/plugins.php)
* Twig:
* getPlayerLink($name, $generate = true)
* removed SQLquote and SQLquery from OTS_Base_DB
* Add optional $params param into log_append (will log arrays) (@tobi132)
### Internal
* moved clients list to the new file (clients.conf.php)
* changed tableExist and fieldExist to $db->hasTable(table) + $db->hasColumn(table, column)
* changed deprecated $ots->createObject() functions with their OTS_ equivalents
* add global helper config($key) function + twig binding
* use config() instead of global $config
* remove unnecessary parentheses in include/require PHP functions
* use __DIR__ instead of dirname(__FILE__) - since PHP 5.3.0
* change intval() function to (int) casting (up to 6x faster)
* add release.sh script (for GitHub releases)
* use curl as alternative option for reporting install
### Libraries
* updated Twig to version v1.35.0
* updated TinyMCE to version v4.7.4
### Deprecations
* change deprecated HTML <center> tag to <div style="text-align:center">
* replace deprecated HTML <font> tag with <span>
## [0.7.11 - 04.05.2019]
### Added:
* support for some old servers, where arrays are used in config.lua
* an additional text to the install page informing that user can reinstall MyAAC by deleting config.local.php
### Fixed:
* XSS in forum show_thread
* guilds - "Add new rank" function
* multiple mail recipients when using admin mailer function
* Admin Panel - MyAAC logs not shown if servers logs directory doesn't exist (#47)
* missing prefix for cache get() and delete() functions
* add fatal error message when myaac tables in database do not exist
* the mystical defect where "Create Account" button was not highlighted (on the account/manage page)
* bug where server_config table does not exist (OTHire as an example)
* database_name in Usage_Statistics
* forgot to open <head> in install template
### Changed:
* do not display software version
## [0.7.10 - 03.03.2018]
### Added:
* new configurable: smtp_secure
* robots.txt
### Fixed:
* editing an existing page that had php enabled
* chrome bug on save (when editing page) ERR_BLOCKED_BY_XSS_AUDITOR
* showing IP and Port in admin panel (#44, by miqueiaspenha)
* deleting plugin showing "You don't have rights to delete"
* some bug with PHPMailer not finding its language file
* default accounts.vote value
* saving some really high long ip addresses
### Changed:
* update config.highscores_ids_hidden on install when there are samples already in database
* auto add z_polls table on install
### Internal:
* changed mb_strtolower functions to strtolower()
* added new function: $hooks->exist($type)
## [0.7.9 - 13.01.2018]
* removed 6mb of trash (some useless things)
* (fix) TFS 1.x not showing promoted vocations in highscores
* otserv 0.6.x: fixed some warning (on the characters page) and fatal mysql error (on the mango signature)
* fixed default stamina on otserv 0.6.x engine (and some others perhaps)
* install: change permission check to is_writable
* changed highscores_groups_hidden to 3 (for TFS 1.x)
* updated background-artwork (tibiacom template) to the latest version, removed other ones
## [0.7.8 - 12.01.2018]
* fixed installation error " call to undefined method OTS_DB_MySQL::hasColumn()"
* updated tinymce to the latest (4.7.4) version
* enabled emoticons plugin in tinymce :)
* some security fixes
## [0.7.7 - 08.01.2018]
* important fix for servers with promotion column (caused player.vocation to be resetted when saving player, for example: on change name, accept invite to guild, leave guild)
* immediately reload config.lua when there's change in config.server_path detected
* added new forum option: "Enable HTML" (only for moderators)
* fixed othire default column value (#26)
* fixed saving custom vocations in admin panel (#36)
* fixed warning in highscores when vocation doesn't exist
* fixed characters page - config.characters.frags "Notice: Use of undefined constant"
* fixed getBoolean function when boolean is passed
* fixed empty success message on leave guild
* fixed displaying premium account days
* function OTS_Account:getPremDays will now return -1 if there's freePremium configurable enabled on the server
* fixed tr bgcolor in characters view (Frags) (#38)
* fixed some warning in guild show
* fixed PHP warning about country not existing on online and characters pages
* fixed forum bbcode parsing
* don't add extra <br/> to the TinyMCE news forum posts
* (internal) using $player->getVocationName() where possible instead of older method
## [0.7.6 - 05.01.2017]
* fixed othire account creating/installation
* fixed table name players -> players_online
* fixed unexpected error logging about email fail
* added max_execution_time to the install finish step
* some small fix regarding highscores vocation box
## [0.7.5 - 04.01.2017]
* fixed bug on othire with config.account_premium_days
* fixed bug on TFS 1.x when online_afk is enabled
* warning about leaving news page with changes
* added player status to tibiacom top 5 highscores box
* save detected country on create account in session
* fixed getPremDays and isPremium functions (newest 11.x engines are bugged when it comes to PACC, its not fault of MyAAC)
* fix when there are no changelogs or highscores yet
* small fix regarding getTopPlayers function which was ignoring $limit variable
* fixed news adding when type != ARTICLE
* fixed template path finding
* fixed displaying article_text when it was empty saved
## [0.7.4 - 24.12.2017]
* fixed mysql fatal error on tibiacom template - top 5 box
* fixed displaying of level percent bar on tibian signature
* inform user about Twig cache failure on installation, instead of http 500 error
* when dir system/cache is not writable by the webserver, then show some nice notice to the user about it instead of http 500 error
* remember client version select and usage stats checkbox in session on install
* automatically update highscores_ids_hidden for users who installed myaac before (migration)
## [0.7.3 - 18.12.2017]
* auto generate myaac cache & session prefix on install to be unique across installations
* fixed hiding shop system menu on tibiacom template when disabled in config
* prevent adding duplicated newses with installation
* some changes to sample characters: chanced town_id to 1, posx: 1000, posy: 1000, posz: 1000 and default group_id to 1 so you can change in-game outfits and they will be used
* added version 772 constant to install client choose (OTHire)
* better solution for hidding samples (configurable) - highscores_ids_hidden
* fixed account.login redirect not working on tibiacom template
* installation: warn about wrong admin account name/id and password
* fixed last menu closing in tibiacom template
* updated polish locale (translation) on install
* (internal) removed some duplicated code on install finish
* (internal) renamed installation step files to be in correct order
* added TODO file
## [0.7.1 - 13.12.2017]
* added changelog menu item to kathrine template
* fixed some php short tag in changelogs page
* fixed guild change description back button
* removed duplicated "Support List" menu item from tibiacom template
* changed some notice when version check is failed
* (internal) moved changelog to twig
## [0.7.0 - 20.11.2017]
* moved template menus to database, they're now dynamically loaded
* added anonymous usage statistics reporting (only if user agrees, first usage report will be send after 7 days)
* you can edit them in Admin Panel under 'Menus' option
* you can also add custom links, like http://google.pl
* added networks (facebook and twitter) and highscores (top 5) boxes to tibiacom template, configurable in templates/tibiacom/config.php
* added news ticker for kathrine template
* added featured article to tibiacom template (you can add them with add news button)
* added tinymce editor to 'Pages' in admin panel
* added links to edit/delete/hide custom page directly from page
* update forum post after editing news (when forum post has been created)
* enabled code plugin for tinymce which enabled raw html code editing
* removed videos pages, as it can be easily added using custom Menus and Pages with insert Media
* removed bug_report configurable, its now enabled by default
* log some error info when mail cannot be send on account create
* twig getLink function will now return with full url (BASE_URL included)
* verify install post values directly on config page and display error
* updated tinymce to version 4.7.2 (from 4.7.0)
* updated phpmailer to version 5.2.26 (from 5.2.23)
* (#30) (fix) recovering account on servers that doesn't support salts
* (fix) account email confirm function
* (fix) showing changelog with urls in Admin Panel
* (fix) uninstalling plugin
* (fix) polls box in tibiacom template
* (fix) remove hooks from db on plugin deinstall
* (fix) some weird include possibilities with forum and account actions (verify action name)
* (fix) loading hooks from plugin installed from command line
* (fix) some changelog PHP Notice warning
* (internal) moved uninstall logic to Plugins class
* (internal) moved tibiacom boxes to separate directory
* (internal) moved news tickers to twig template
* (internal) moved Forum class to separate file
* (internal) moved deprecated functions to compat.php
* (internal) added some compat functions that are used by shop system
* (internal) renamed constant TICKET -> TICKER
* (internal) shortened message functions
## [0.6.6 - 22.10.2017]
* fixed some php fatal error on spells page
* changed spells.vocations field in db size to 300
* please reload your spells after this update!
## [0.6.5 - 21.10.2017]
* fixed displaying custom pages
* fixed adding new group forum board
## [0.6.4 - 20.10.2017]
* reverted OTS_Account::getLastLogin() cause its used by tibia11-login plugin
## [0.6.3 - 20.10.2017]
* fixed creating account
* fixed viewing thread without being logged
* fixed showing premium account status
## [0.6.2 - 20.10.2017]
* added forums for guilds and groups
* added nice looking menu for my account page in default template
* new command line tool: install_plugin.php - can be used to install plugins from command line. Usage: "php install_plugin.php path_to_file"
* added new tooltip to view characters equipment item name and monster loot
* added items.xml loader class and weapons.xml loader class
* minimum PHP version to install AAC is now 5.3.0 cause of Anonymous functions used by Twig
* Added 'Are you sure?' popup when uninstalling plugin
* added some warnings when plugin json file is incomplete
* fixed showing in characters ban expires when is unlimited
* fixed displaying monster loot when item.name in loot is used instead of item.id
* load also runes into spells table
* display plugin uninstall option only if its possible
* after changing template you will be redirected to latest viewed page
* display gallery add image form only on main gallery page
* (internal) moved most of guilds html-in-php code to twig
* (internal) moved spells page to twig template
* (internal) removed useless spells.spell column that was duplicate of spells.words
* (internal) save monster loot in database in json format instead loading it every time from xml file
* (internal) store monster voices and immunities in json format
* (internal) moved buttons to separate template
* (internal) moved online search form to twig
* (internal) added new function getItemNameById($id)
* (internal) Moved plugin install logic to a new class: Plugins
* (internal) changed spells.vocations database field to store json data instead of comma separated
* (internal) removed $hook_types array, using defined() and constant() functions now
* (internal) removed useless monsters.gfx_name field from database
* (internal) renamed database field monsters.hide_creature to hidden
* (internal) renamed existing Items class to Items_Images
* (internal) optimized Spells class
* (internal) new function: OTS_Guild::hasMember(OTS_Player $player)
* (internal) new function: Forum::hasAccess($board_id)
## [0.6.1 - 17.10.2017]
* fixed signatures loading
* new configurable: session_prefix, to allow more websites on one machine (must be unique for every website on your dedicated server!)
* better error handling for monsters and spells loader (save errors to system/logs/error.log)
* check if file exist before loading (monsters and spells)
* (internal) Account::getAccess() = Account::getGroupId()
* (internal) moved account actions (pages) to account/ directory
* (internal) moved forum actions (pages) to forum/ directory
* (internal) moved forum.edit_post to twig templates
## [0.6.0 - 16.10.2017]
* added faq management - add/edit/move/hide/delete from website
* new account.login view for tibiacom template
* monsters and spells are now being loaded at the installation of the AAC
* fix for php versions under 5.5 where empty() function supported only variables
* added missing change email and change info buttons to account.management default template
* added new indicator icons for create account, create character and change character name
* fixed config loader when some inline comments are present
* fixed editing page in admin panel that contains some html code
* fixed forum new post on mac os and some specific mysql versions
* attempt to fix incorrect views counter behavior (its resetting to 0 in some cases)
* enabled cache http headers for signatures
* check if monster file exist before loading it
* fixed if plugin zip file name contains dot (.)
* renamed screenshots to gallery and movies to videos
* moved install pages to twig
* fixed Account::getGuildAccess function
* removed never used library from sources - dwoo
* moved check_* functions to class Validator
* from now all validators ajax requests will fire onblur instead of onkeyup
* ajax requests returns now json instead of xml
* added 404 response when file is not found
## [0.5.1 - 11.10.2017]
* fixed forum add/edit board
* new configurable: highscores_length, how much highscores to display
* fixed highscores links (ALL, previous and next page)
* update templates cache when installing/uninstalling plugin
* moved character deaths and frags table generation to twig
* fixed some bug when you uninstall plugin and then try to install again on the same page
* check if plugin exist before uninstalling
* fixed some warning in OTS_Base_DB
## [0.5.0 - 10.10.2017]
* moved .htaccess rules to plain php (index.php)
* updated tinymce to the latest (4.7.0) version, you can now embed code, for example youtube videos
* added option to uninstall plugin
* added option to require specified myaac, php or database version for plugins, without that plugin won't be installed
* change accountmanagement links to use friendly_urls
* fixed creating new forum thread
* sample characters are now assigned to admin account and have group_id 4 to not be shown on highscores
* added links loaded from database to admin panel - for future plugins
* print some info to error.log when can't find config.lua
* some fixes in account changecomment action
* show info when account name/number or password is empty on login
* fixed showing account login errors
* added few characters hooks
* fixed some kathrine template js bug when shop is disabled
* you can now use slash '/' in custom pages loaded from database
* added new twig function getLink that convert link taking into account config.friendly_urls
* internalLayoutLink -> getLink
## [0.4.3 - 05.10.2017]
* better config loader taken from latest gesior, you can now include files in your config by doing dofile('config.local.lua')
* fixed country detection in create account
* fixed showing of character deaths and frags
* fixed https://otland.net/threads/myaac-v0-0-1.251454/page-13#post-2466303
* fixed https://otland.net/threads/myaac-v0-0-1.251454/page-13#post-2466313
* fixed rook sample, which will now have level 1, 150 health, 0 mana, and 400 cap.
* fixed samples being deleted by tfs 1.0+ cause of 'deletion' field set to 1
* pages loaded from database have higher priority than normal .php pages, so they will be loaded first if they exist
* moved many pages to twig templates
* change download client links from clients.halfaway.net to tibia-clients.com
* added bugtracker to kathrine template
* added CREDITS file
## [0.4.2 - 14.09.2017]
* updated version number
## [0.4.1 - 13.09.2017]
* fixed log in to admin panel
* fixed File is not .zip plugin upload error
## [0.4.0 - 13.09.2017
* added option to add/edit/delete/hide/move forum boards
* moved some of HTML-in-PHP code to Twig templates
* added bug_report configurable which can enable/disable bug tracker
* log errors instead of showing them to users with system directories
* fix when $_SERVER['HTTP_ACCEPT_ENCODING'] is not set
* when it fails to load config.lua it will output error also to error.log
* automatically detect json file in .zip instead of basing on filename (admin panel - plugins)
* hopefully fixed the error with "The file you are trying to upload is not a .zip file. Please try again."
* fixed wrong name of table in bugtracker
* fixed some bugs in bugtracker
* added report bug link in templates
* fixed some rare error when user is logged in for longer than 15 minutes and tries to login again
* fixed some grammar errors
* some small improvements
* fixed some separators in kathrine template
## [0.3.0 - 28.08.2017]
* added administration panel for screenshots management with auto thumbnail generator and image auto-resizing
* added Twig template engine and moved some html-in-php code to it
* automatically detect player country based on user location (IP) on create account
* player sex (gender) is now configurable at $config['genders']
* fixed recovering account and changing password when salt is enabled
* fixed installing samples when for example Rook Sample already exist and other samples not
* fixed some mysql error when character you trying to create already exist
* fixed some warning when you select nonexistent country
* password change minimal/maximal length notice is now more precise
* added 'enabled' field in myaac_hooks table, which can enable or disable specified hook
* removed DEFAULT '' for TEXT field. It didn't worked under some systems like MAC OS X.
* minimum PHP version to install the MyAAC is now 5.2.0 cause of pathinfo (extension) function
* removed unused admin stylish template
* removed some unused cities field from myaac_spells table
* moved news adding at installation from schema.sql to finish.php
* some optimizations
## [0.2.4 - 09.06.2017]
* fixed invite to guild
* added id field on monsters, so you can delete them in phpmyadmin
* fixed adding some creatures with ' and "
* fixed when there are spaces at beginning of the file (creatures)
* fixed when file is unable to parse (creatures)
* fixed typo loss_items => loss_containers
* more elegant way of showing message on reload creatures and spells
## [0.2.3 - 31.05.2017]
* fixed guild management on OTHire 0.0.3
* set default skills to 10 when creating new character
* fixed displaying of "Create forum thread" in newses
* fixed deleting guild on servers that use players.rank_id field
* fixed phpmailer class loading (https://otland.net/threads/myaac-v0-0-1.251454/page-8#post-2445222)
* fixed displaying vocation amount on online page
* better support for custom vocations, you just need to set in config vocations_amount to yours.
* fixed huge space in player name (https://otland.net/threads/myaac-v0-0-1.251454/page-7#post-2444328)
* fixed Undefined variable (https://otland.net/threads/myaac-v0-0-1.251454/page-7#post-2444034)
* fixed Undefined offset (https://otland.net/threads/myaac-v0-0-1.251454/page-7#post-2444035)
## [0.2.2 - 22.05.2017]
* added missing cache/signature directory
* fixed https://otland.net/threads/myaac-v0-0-1.251454/page-7#post-2443868
## [0.2.1 - 21.05.2017]
* added Swedish translation by Sizaro
* fixed some bugs with installlation & characters & houses
## [0.2.0 - 21.05.2017]
* added option to change character sex for premium points
* moved site_closed to database, now you can close your site through admin panel
* added option to admin panel: clear cache
* added experiencetable_rows configurable
* optimized OTS_Account->getGroupId(), now its using like 20 queries less
* optimized OTS_Player->load($id) function, should be much faster now
* fixed displaying on highscores special outfits
* fixed skull images displaying
* fixed displaying unlimited premium account
* fixed bug where players.lookaddons doesn't exist (OTHire etc.) (https://otland.net/threads/myaac-v0-0-1.251454/page-6#post-2442407)
* fixed signature tibian for OTHire and other servers that doesnt use accounts.premdays field
* fixed when player name in signature containst space
* don't show "Create forum thread" when editing
* fixed red color table after create account
* updated download links, as clients.halfaway.net isn't working anymore
* fixed some bugs while installing when field `email_next` or `hidden` already exist
* fixed movies unexpected comment
* added template_place_holder('center_top') to kathrine template
## [0.1.5 - 13.05.2017]
* fixed bug with "Integrity constraint violation: 1048 Column 'ip' cannot be null"
## [0.1.4 - 13.05.2017]
* added outfit shower, in characters, online, and highscores
* updated database to version 2
* fixed item images (now using item-images.ots.me host by default)
* fixed news ticket and posting long newses (https://otland.net/threads/myaac-v0-0-1.251454/page-5#post-2442026)
* news body limit increased to 65535 (mysql text field)
* removed some unused code from my old server
* added spells & monsters to kathrine template
## [0.1.3 - 11.05.2017]
* this is just release to update version number
## [0.1.2 - 11.05.2017]
* forgot to update CHANGELOG and MYAAC_VERSION
## [0.1.1 - 11.05.2017]
* fixed updating myaac_config with database_version to 1
* fixed database updater
## [0.1.0 - 11.05.2017]
* added new feature: change character name for premium points (disabled by default, you can enable it in config under account_change_character_name in config.php)
* added automatic database updater (data migrations)
* renamed events to hooks
* moved hooks to database
* now you can use hooks in plugins
* set account.type field to 5 on install, if TFS 1.0+
* added example plugin
* new, latest google analytics code
* fixed bug with loading account.name that has numbers in it
* fixed many bugs in player editor in admin panel
* added error handling to plugin manager and some more verification in
* file has been correctly unpacked/uploaded
* fixed Statistics page in admin panel when using account.number
* fixed bug when creating/recovering account on servers with
* account.salt field (TFS 0.3 for example)
* fixed forum showing thread with html tags (added from news manager)
* new, latest code for youtube videos in movies page
* fixed showing vocation images when using $config['online_vocations_images']
* many fixes in polls (also importing proper schema)
* fixed hovering on buttons in kathrine template (on accountmanagement page)
* fixed signatures (many fixes)
* added missing gesior signature system
## [0.0.6 - 06.05.2017]
* fixed bug while installing (https://otland.net/threads/myaac-v0-0-1.251454/page-3#post-2440543)
* fixed bug when creating character (not showing errors) (one more time)
* fixed support for TFS 0.2 series
* added FAQ link
## [0.0.5 - 05.05.2017]
* fixed bug when creating character (not showing errors)
* Fixed characters loading with names that has been created with other AAC
* fixed links to shop in default template
* fixed some weird PHP 7.1 warnings/notices
* Fixed config loading with some weird comments
* fixed bug with status info utf8 encoding (https://otland.net/threads/myaac-v0-0-1.251454/page-2#post-2440259)
* fixed when ip in log_action is NULL (https://otland.net/threads/myaac-v0-0-1.251454/page-2#post-2440357)
* fixed bug when guild doesn't exist on characters page (https://otland.net/threads/myaac-v0-0-1.251454/page-2#post-2440320)
* disabled friendly_urls by default
* fixes when $config['database_*'] is set
* added CHANGELOG
## [0.0.3 - 03.05.2017]
* Full support for OTHire 0.0.3
* added support for otservers that doesn't use account.name field, instead just account number will be used
* fixed encryption detection on TFS 0.3
* fixed bug when server_config table doesn't exist
* (install) moved admin account creation to new step
* fixed news comment link
* by default, the installer creates now the Admin player, for admin account
* fixed installation errors
* fixed config.lua loading with some weird comments
## [0.0.2 - 02.05.2017]
* updated forum links to use friendly_urls
* some more info will be shown when cannot connect to database
* show more error infos when creating character
* fixed forum link on newses
* fixed spells loading when there's vocation name instead of id
* fixed bug when you have changed template but it doesn't exist anymore
* fixed vocations with promotion loading
* fixed support for gesior pages and templates
* added function OTS_Acount:getGroupId()
## [0.0.1 - 01.05.2017]
This is first official release of MyAAC.
Features are listed here
For more information, see the release announcement on OTLand: https://otland.net/threads/myaac-v0-0-1.251454/
### Fixed

14
CONTRIBUTORS.txt Normal file
View File

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

View File

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

View File

@@ -1,18 +1,26 @@
# myaac
# [MyAAC](https://my-aac.org)
[![Build Status Master](https://img.shields.io/travis/slawkens/myaac/master)](https://travis-ci.org/github/slawkens/myaac)
[![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)
[![PHP Versions](https://img.shields.io/travis/php-v/slawkens/myaac/master)](https://github.com/slawkens/myaac/blob/d8b3b4135827ee17e3c6d41f08a925e718c587ed/.travis.yml#L3)
[![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)
MyAAC is a free and open-source Automatic Account Creator (AAC) written in PHP. It is a fork of the [Gesior](https://github.com/gesior/Gesior2012) project. It supports only MySQL databases.
Official website: https://my-aac.org
### REQUIREMENTS
### Requirements
- PHP 5.5 or later
- PHP 5.6 or later
- MySQL database
- PDO PHP Extension
- XML PHP Extension
- ZIP PHP Extension
- (optional) mod_rewrite to use friendly_urls
### INSTALLATION AND CONFIGURATION
### Installation
Just decompress and untar the source (which you should have done by now,
if you're reading this), into your webserver's document root.
@@ -32,15 +40,40 @@ Official website: https://my-aac.org
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.
Use *config.local.php* for your local configuration changes.
### OTHER NOTES
### Branches
This repository follows the Git Flow Workflow.
Cheatsheet: [Git-Flow-Cheetsheet](https://danielkummer.github.io/git-flow-cheatsheet)
That means, we use:
* 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
### LICENSING
### License
This program and all associated files are released under the GNU Public
License, see LICENSE for details.
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.

View File

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

BIN
admin/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

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

48
admin/template/menus.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
return [
['name' => 'Dashboard', 'icon' => 'tachometer-alt', 'link' => 'dashboard'],
['name' => 'News', 'icon' => 'newspaper', 'link' =>
[
['name' => 'View', 'link' => 'news'],
['name' => 'Add news', 'link' => 'news&action=new&type=1'],
['name' => 'Add ticker', 'link' => 'news&action=new&type=2'],
['name' => 'Add article', 'link' => 'news&action=new&type=3'],
],
],
['name' => 'Changelogs', 'icon' => 'newspaper', 'link' =>
[
['name' => 'View', 'link' => 'changelog'],
['name' => 'Add', 'link' => 'changelog&action=new'],
],
],
['name' => 'Mailer', 'icon' => 'envelope', 'link' => 'mailer', 'disabled' => !config('mail_enabled')],
['name' => 'Pages', 'icon' => 'book', 'link' =>
[
['name' => 'View', 'link' => 'pages'],
['name' => 'Add', 'link' => 'pages&action=new'],
],
],
['name' => 'Menus', 'icon' => 'list', 'link' => 'menus'],
['name' => 'Plugins', 'icon' => 'plug', 'link' => 'plugins'],
['name' => 'Server Data', 'icon' => 'gavel', 'link' => 'data'],
['name' => 'Editor', 'icon' => 'edit', 'link' =>
[
['name' => 'Accounts', 'link' => 'accounts'],
['name' => 'Players', 'link' => 'players'],
],
],
['name' => 'Tools', 'icon' => 'tools', 'link' =>
[
['name' => 'Notepad', 'link' => 'notepad'],
['name' => 'phpinfo', 'link' => 'phpinfo'],
],
],
['name' => 'Logs', 'icon' => 'bug', 'link' =>
[
['name' => 'Logs', 'link' => 'logs'],
['name' => 'Reports', 'link' => 'reports'],
['name' => 'Visitors', 'icon' => 'user', 'link' => 'visitors'],
],
],
];

View File

@@ -1,44 +1,10 @@
.slidecontainer {
width: 100%;
.menu-text-li {color: #4b646f; background: #1a2226;}
.menu-text {
display: block;
padding: .5rem 1rem;
white-space: nowrap;
}
.slider {
-webkit-appearance: none;
width: 100%;
outline: none;
opacity: 0.7;
-webkit-transition: .2s;
transition: opacity .2s;
}
.slider:hover {
opacity: 1;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 15px;
height: 25px;
background: #3c8dbc;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 25px;
height: 25px;
background: #3c8dbc;
cursor: pointer;
}
td.details-control {
text-align: center;
color: forestgreen;
cursor: pointer;
}
tr.shown td.details-control {
text-align: center;
color: red;
.sidebar-mini.sidebar-collapse .menu-text {
display: none;
}

View File

@@ -1,229 +1,196 @@
<?php defined('MYAAC') or die('Direct access not allowed!'); ?>
<!DOCTYPE html>
<html>
<!doctype html>
<html lang="en">
<head>
<?php echo template_header(true);
$title_full = (isset($title) ? $title . $config['title_separator'] : '') . $config['lua']['serverName'];
?>
<title><?php echo $title_full ?></title>
<link rel="shortcut icon" href="<?php echo BASE_URL; ?>images/favicon.ico" type="image/x-icon" />
<link rel="icon" href="<?php echo BASE_URL; ?>images/favicon.ico" type="image/x-icon" />
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="stylesheet" href="<?php echo BASE_URL; ?>tools/css/bootstrap.min.css">
<link rel="stylesheet" href="<?php echo BASE_URL; ?>tools/css/AdminLTE.min.css">
<link rel="stylesheet" href="<?php echo BASE_URL; ?>tools/css/skins/skin-blue.min.css">
<?php echo template_header(true); ?>
<title><?php echo (isset($title) ? $title . ' - ' : '') . $config['lua']['serverName'];?></title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="<?php echo BASE_URL; ?>tools/css/adminlte.min.css">
<link rel="stylesheet" href="<?php echo BASE_URL; ?>tools/css/font-awesome.min.css">
<link rel="stylesheet" href="<?php echo BASE_URL; ?>tools/css/ionicons.min.css">
<link rel="stylesheet" href="<?php echo BASE_URL; ?>tools/css/jquery.dataTables.min.css">
<?php if (isset($use_datatable)) { ?>
<link rel="stylesheet" href="<?php echo BASE_URL; ?>tools/css/datatables.bs.min.css">
<?php } ?>
<link rel="stylesheet" type="text/css" href="<?php echo $template_path; ?>style.css"/>
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<script src="<?php echo BASE_URL; ?>tools/js/html5shiv.min.js"></script>
<script src="<?php echo BASE_URL; ?>tools/js/respond.min.js"></script>
<![endif]-->
<link rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
</head>
<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">
<?php
if ($logged && admin()) {
?>
<header class="main-header">
<a href="." class="logo">
<span class="logo-mini"><b>M</b>A</span>
<span class="logo-lg"><b>My</b>AAC</span>
</a>
<nav class="navbar navbar-static-top" role="navigation">
<a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
<span class="sr-only">Toggle navigation</span>
</a>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<li>
<a href="#" data-toggle="control-sidebar"><i class="fa fa-gears"></i></a>
</li>
</ul>
</div>
<body class="sidebar-mini ">
<?php if ($logged && admin()) { ?>
<div class="wrapper">
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" data-widget="pushmenu" href="#"><i class="fas fa-bars"></i></a>
</li>
<li class="nav-item d-none d-sm-inline-block">
<a href="<?php echo ADMIN_URL; ?>" class="nav-link">Home</a>
</li>
</ul>
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" data-widget="control-sidebar" data-slide="true" href="#"><i class="fas fa-th-large"></i></a>
</li>
</ul>
</nav>
</header>
<aside class="main-sidebar">
<section class="sidebar">
<ul class="sidebar-menu" data-widget="tree">
<li class="header">MyAAC</li>
<aside class="main-sidebar sidebar-dark-info elevation-4">
<a href="<?php echo ADMIN_URL; ?>" class="brand-link navbar-info">
<img src="<?php echo ADMIN_URL; ?>images/logo.png" class="brand-image img-circle elevation-3" style="opacity: .8">
<span class="brand-text"><b>My</b>AAC</span>
</a>
<div class="sidebar">
<nav class="mt-1">
<ul class="nav nav-pills nav-sidebar flex-column nav-legacy nav-child-indent" data-widget="treeview" data-accordion="false">
<li class="menu-text-li">
<span class="menu-text">
<a class="text-info" href="<?php echo BASE_URL; ?>" target="_blank">
<?php echo $config['lua']['serverName'] ?>
</a>
</span>
</li>
<?php
// name = Display name of link
// icon = fontawesome icon name without "fas fa-"
// link = Page link or use as array for sub items
$menus = require __DIR__ . '/menus.php';
<?php
$icons_a = array(
'dashboard','newspaper-o', 'envelope',
'book', 'list',
'plug', 'user',
'edit', 'gavel',
'wrench', 'edit', 'book', 'book',
);
$menus = array(
'Dashboard' => 'dashboard',
'News' => 'news',
'Mailer' => 'mailer',
'Pages' => 'pages',
'Menus' => 'menus',
'Plugins' => 'plugins',
'Visitors' => 'visitors',
'Editor' => array(
'Accounts' => 'accounts',
'Players' => 'players',
),
'Items' => 'items',
'Tools' => array(
'Notepad' => 'notepad',
'phpinfo' => 'phpinfo',
),
'Logs' => array(
'Logs' => 'logs',
'Reports' => 'reports',
),
);
$i = 0;
foreach ($menus as $_name => $_page) {
$has_child = is_array($_page);
if (!$has_child) {
echo '<li ';
if ($page == $_page) echo ' class="active"';
echo ">";
echo '<a href="?p=' . $_page . '"><i class="fa fa-' . (isset($icons_a[$i]) ? $icons_a[$i] : 'link') . '"></i> <span>' . $_name . '</span></a></li>';
}
if ($has_child) {
$used_menu = "";
$nav_construct = '';
foreach ($_page as $__name => $__page) {
$nav_construct = $nav_construct . '<li';
if ($page == $__page) {
$nav_construct = $nav_construct . ' class="active"';
$used_menu = true;
foreach ($menus as $category => $menu) {
if (isset($menu['disabled']) && $menu['disabled']) {
continue;
}
$has_child = is_array($menu['link']);
if (!$has_child) { ?>
<li class="nav-item">
<a class="nav-link<?php echo(strpos($menu['link'], $page) !== false ? ' active' : '') ?>" href="?p=<?php echo $menu['link'] ?>">
<i class="nav-icon fas fa-<?php echo(isset($menu['icon']) ? $menu['icon'] : 'link') ?>"></i>
<p><?php echo $menu['name'] ?></p>
</a>
</li>
<?php
} else if ($has_child) {
$used_menu = null;
$nav_construct = '';
foreach ($menu['link'] as $category => $sub_menu) {
$nav_construct .= '<li class="nav-item"><a href="?p=' . $sub_menu['link'] . '" class="nav-link';
if ($page == $sub_menu['link']) {
$nav_construct .= ' active';
$used_menu = true;
}
$nav_construct .= '"><i class="far fa-' . (isset($sub_menu['icon']) ? $sub_menu['icon'] : 'circle') . ' nav-icon"></i><p>' . $sub_menu['name'] . '</p></a></li>';
}
?>
<li class="nav-item has-treeview<?php echo($used_menu ? ' menu-open' : '') ?>">
<a href="#" class="nav-link<?php echo($used_menu ? ' active' : '') ?>">
<i class="nav-icon fas fa-<?php echo(isset($menu['icon']) ? $menu['icon'] : 'link') ?>"></i>
<p><?php echo $menu['name'] ?></p><i class="right fas fa-angle-left"></i>
</a>
<ul class="nav nav-treeview">
<?php echo $nav_construct; ?>
</ul>
</li>
<?php
}
$nav_construct = $nav_construct . '><a href="?p=' . $__page . '"><i class="fa fa-circle-o"></i> ' . $__name . '</a></li>';
}
echo '<li class="treeview' . (($used_menu) ? ' menu-open' : '') . '">
<a href="#"><i class="fa fa-' . (isset($icons_a[$i]) ? $icons_a[$i] : 'link') . '"></i> <span>' . $_name . '</span>
<span class="pull-right-container"><i class="fa fa-angle-left pull-right"></i></span></a>
<ul class="treeview-menu" style="' . (($used_menu) ? ' display: block' : ' display: none') . '">';
echo $nav_construct;
echo '</ul>
</li>';
}
$i++;
}
$query = $db->query('SELECT `name`, `page`, `flags` FROM `' . TABLE_PREFIX . 'admin_menu` ORDER BY `ordering`');
$menu_db = $query->fetchAll();
foreach ($menu_db as $item) {
if ($item['flags'] == 0 || hasFlag($item['flags'])) { ?>
<li class="nav-item">
<a class="nav-link<?php echo($page == $item['page'] ? ' active' : '') ?>" href="?p=<?php echo $item['page'] ?>">
<i class="nav-icon fas fa-link"></i>
<p><?php echo $item['name'] ?></p>
</a>
</li>
<?php
}
}
?>
</ul>
</nav>
</div>
</aside>
$query = $db->query('SELECT `name`, `page`, `flags` FROM `' . TABLE_PREFIX . 'admin_menu` ORDER BY `ordering`');
$menu_db = $query->fetchAll();
foreach ($menu_db as $item) {
if ($item['flags'] == 0 || hasFlag($item['flags'])) {
echo '<li ';
if ($page == $item['page']) echo ' class="active"';
echo ">";
echo '<a href="?p=' . $item['page'] . '"><i class="fa fa-link"></i> <span>' . $item['name'] . '</span></a></li>';
}
}
?>
</ul>
</section>
</aside>
<div class="content-wrapper">
<section class="content-header">
<h1><?php echo(isset($title) ? $title : ''); ?>
<small> - Admin Panel</small>
<div class="pull-right">
<span class="label label-<?php echo(($status['online']) ? 'success' : 'danger'); ?>"><?php echo $config['lua']['serverName'] ?></span>
<div class="content-wrapper" style="min-height: 823px;">
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h3 class="m-0 text-dark"><?php echo(isset($title) ? $title : ''); ?><small> - Admin Panel</small></h3>
</div>
<div class="col-sm-6">
<div class="float-sm-right d-none d-sm-inline">
<span class="p-2 right badge badge-<?php echo((isset($status['online']) and $status['online']) ? 'success' : 'danger'); ?>"><?php echo $config['lua']['serverName'] ?></span>
</div>
</div>
</div>
</div>
</h1>
</section>
<section class="content">
<?php echo $content; ?>
</section>
</div>
<div class="content">
<div class="container-fluid">
<?php echo $content; ?>
</div>
</div>
</div>
<aside class="control-sidebar control-sidebar-dark">
<div class="p-3">
<h4>Account:</h4>
<p><h5><a href="?action=logout"><i class="fas fa-sign-out-alt text-danger"></i> Log out</h5></a>
<small>This will log you out</small></p>
</div>
<div class="p-3">
<h4>Site:</h4>
<p><h5><a href="<?php echo BASE_URL; ?>" target="_blank"><i class="far fa-eye text-blue"></i> Preview</a></h5>
<small>This will open a new tab</small></p>
</div>
<div class="p-3">
<h4>Version:</h4>
<p><h5><a href="?p=version"><i class="fas fa-code-branch"></i> <?php echo MYAAC_VERSION; ?></a></h5>
<small>Check for updates</small></p>
</div>
<div class="p-3">
<h4>Site:</h4>
<p><h5><a href="https://github.com/slawkens/myaac" target="_blank"><i class="fab fa-github"></i> Github</a></h5>
<small>Goto GitHub Page</small></p>
<p><h5><a href="http://my-aac.org/" target="_blank"><i class="fas fa-shoe-prints"></i> MyAAC Official</a></h5>
<small>Goto MyAAC Official Website</small></p>
</div>
</aside>
<footer class="main-footer">
<div class="float-sm-right d-none d-sm-inline">
<span class="p-2 right badge badge-<?php echo((isset($status['online']) and $status['online']) ? 'success' : 'danger'); ?>"><?php echo $config['lua']['serverName'] ?></span>
</div>
<?php echo base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4='); ?>
</footer>
<div id="sidebar-overlay"></div>
</div>
<footer class="main-footer">
<div class="pull-right hidden-xs">
<div id="status">
<?php if ($status['online']): ?>
<p class="success" style="width: 120px; text-align: center;">Server Online</p>
<?php else: ?>
<p class="error" style="width: 120px; text-align: center;">Server Offline</p>
<?php endif; ?>
</div>
</div>
<?php echo base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4='); ?>
</footer>
<aside class="control-sidebar control-sidebar-dark">
<ul class="nav nav-tabs nav-justified control-sidebar-tabs">
<li class="active"><a href="#control-sidebar-home-tab" data-toggle="tab"><i class="fa fa-home"></i></a></li>
<li><a href="#control-sidebar-settings-tab" data-toggle="tab"><i class="fa fa-gears"></i></a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="control-sidebar-home-tab">
<h3 class="control-sidebar-heading">Account</h3>
<ul class="control-sidebar-menu">
<li>
<a href="?action=logout">
<i class="menu-icon fa fa-sign-out bg-red"></i>
<div class="menu-info">
<h4 class="control-sidebar-subheading">Log out</h4>
<p>This will log you out
of <?php echo(USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId()); ?></p>
</div>
</a>
</li>
</ul>
<h3 class="control-sidebar-heading">Site</h3>
<ul class="control-sidebar-menu">
<li>
<a href="<?php echo BASE_URL; ?>" target="_blank">
<i class="menu-icon fa fa-eye bg-blue"></i>
<div class="menu-info">
<h4 class="control-sidebar-subheading">Preview</h4>
<p>This will open a new tab</p>
</div>
</a>
</li>
</ul>
</div>
<div class="tab-pane" id="control-sidebar-settings-tab">
<form method="post">
<h3 class="control-sidebar-heading">Version</h3>
<div class="form-group">
<label class="control-sidebar-subheading">
<?php echo MYAAC_VERSION; ?> (<a href="?p=version">Check for updates</a>)<br/>
</label>
<label class="control-sidebar-subheading">
<p><a href="https://github.com/slawkens/myaac" target="_blank">Github</a></p>
</div>
</form>
</div>
</div>
</aside>
<div class="control-sidebar-bg"></div>
</div>
<?php }
if (!$logged && !admin()) {
<?php } else if (!$logged && !admin()) {
echo $content;
}
?>
<?php
/**
* @var OTS_Account $account_logged
*/
if ($logged && admin()) {
$twig->display('admin-bar.html.twig', [
'username' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId()
]);
}
?>
<script src="<?php echo BASE_URL; ?>tools/js/bootstrap.min.js"></script>
<script src="<?php echo BASE_URL; ?>tools/js/jquery-ui.min.js"></script>
<script src="<?php echo BASE_URL; ?>tools/js/jquery.dataTables.min.js"></script>
<?php if (isset($use_datatable)) { ?>
<script src="<?php echo BASE_URL; ?>tools/js/datatables.min.js"></script>
<script src="<?php echo BASE_URL; ?>tools/js/datatables.bs.min.js"></script>
<?php } ?>
<script src="<?php echo BASE_URL; ?>tools/js/adminlte.min.js"></script>
</body>
</html>
</html>

View File

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

View File

@@ -0,0 +1,46 @@
<?php
/**
* Project: MyAAC
* Automatic Account Creator for Open Tibia Servers
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* @package MyAAC
* @author Slawkens <slawkens@gmail.com>
* @copyright 2020 MyAAC
* @link https://my-aac.org
*/
define('MYAAC_ADMIN', true);
require '../../common.php';
require SYSTEM . 'functions.php';
require SYSTEM . 'init.php';
require SYSTEM . 'login.php';
if (!admin())
die('Access denied.');
ini_set('max_execution_time', 300);
ob_implicit_flush();
ob_end_flush();
header('X-Accel-Buffering: no');
require LIBS . 'DataLoader.php';
require LOCALE . 'en/main.php';
require LOCALE . 'en/install.php';
DataLoader::setLocale($locale);
DataLoader::load();

View File

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

View File

@@ -23,67 +23,74 @@
* @copyright 2019 MyAAC
* @link https://my-aac.org
*/
if (version_compare(phpversion(), '5.5', '<')) die('PHP version 5.5 or higher is required.');
session_start();
if (version_compare(phpversion(), '7.1', '<')) die('PHP version 7.1 or higher is required.');
define('MYAAC', true);
define('MYAAC_VERSION', '0.8.2-dev');
define('DATABASE_VERSION', 30);
define('TABLE_PREFIX', 'myaac_');
const MYAAC = true;
const MYAAC_VERSION = '0.9.0-dev';
const DATABASE_VERSION = 33;
const TABLE_PREFIX = 'myaac_';
define('START_TIME', microtime(true));
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX'));
define('IS_CLI', in_array(php_sapi_name(), ['cli', 'phpdb']));
// account flags
define('FLAG_ADMIN', 1);
define('FLAG_SUPER_ADMIN', 2);
define('FLAG_CONTENT_PAGES', 4);
define('FLAG_CONTENT_MAILER', 8);
define('FLAG_CONTENT_NEWS', 16);
define('FLAG_CONTENT_FORUM', 32);
define('FLAG_CONTENT_COMMANDS', 64);
define('FLAG_CONTENT_SPELLS', 128);
define('FLAG_CONTENT_MONSTERS', 256);
define('FLAG_CONTENT_GALLERY', 512);
define('FLAG_CONTENT_VIDEOS', 1024);
define('FLAG_CONTENT_FAQ', 2048);
define('FLAG_CONTENT_MENUS', 4096);
define('FLAG_CONTENT_PLAYERS', 8192);
const FLAG_ADMIN = 1;
const FLAG_SUPER_ADMIN = 2;
const FLAG_CONTENT_PAGES = 4;
const FLAG_CONTENT_MAILER = 8;
const FLAG_CONTENT_NEWS = 16;
const FLAG_CONTENT_FORUM = 32;
const FLAG_CONTENT_COMMANDS = 64;
const FLAG_CONTENT_SPELLS = 128;
const FLAG_CONTENT_MONSTERS = 256;
const FLAG_CONTENT_GALLERY = 512;
const FLAG_CONTENT_VIDEOS = 1024;
const FLAG_CONTENT_FAQ = 2048;
const FLAG_CONTENT_MENUS = 4096;
const FLAG_CONTENT_PLAYERS = 8192;
// news
define('NEWS', 1);
define('TICKER', 2);
define('ARTICLE', 3);
const NEWS = 1;
const TICKER = 2;
const ARTICLE = 3;
// directories
define('BASE', __DIR__ . '/');
define('ADMIN', BASE . 'admin/');
define('SYSTEM', BASE . 'system/');
define('CACHE', SYSTEM . 'cache/');
define('LOCALE', SYSTEM . 'locale/');
define('LIBS', SYSTEM . 'libs/');
define('LOGS', SYSTEM . 'logs/');
define('PAGES', SYSTEM . 'pages/');
define('PLUGINS', BASE . 'plugins/');
define('TEMPLATES', BASE . 'templates/');
define('TOOLS', BASE . 'tools/');
const BASE = __DIR__ . '/';
const ADMIN = BASE . 'admin/';
const SYSTEM = BASE . 'system/';
const CACHE = SYSTEM . 'cache/';
const LOCALE = SYSTEM . 'locale/';
const LIBS = SYSTEM . 'libs/';
const LOGS = SYSTEM . 'logs/';
const PAGES = SYSTEM . 'pages/';
const PLUGINS = BASE . 'plugins/';
const TEMPLATES = BASE . 'templates/';
const TOOLS = BASE . 'tools/';
const VENDOR = BASE . 'vendor/';
// menu categories
define('MENU_CATEGORY_NEWS', 1);
define('MENU_CATEGORY_ACCOUNT', 2);
define('MENU_CATEGORY_COMMUNITY', 3);
define('MENU_CATEGORY_FORUM', 4);
define('MENU_CATEGORY_LIBRARY', 5);
define('MENU_CATEGORY_SHOP', 6);
const MENU_CATEGORY_NEWS = 1;
const MENU_CATEGORY_ACCOUNT = 2;
const MENU_CATEGORY_COMMUNITY = 3;
const MENU_CATEGORY_FORUM = 4;
const MENU_CATEGORY_LIBRARY = 5;
const MENU_CATEGORY_SHOP = 6;
// otserv versions
define('OTSERV', 1);
define('OTSERV_06', 2);
define('OTSERV_FIRST', OTSERV);
define('OTSERV_LAST', OTSERV_06);
define('TFS_02', 3);
define('TFS_03', 4);
define('TFS_FIRST', TFS_02);
define('TFS_LAST', TFS_03);
const OTSERV = 1;
const OTSERV_06 = 2;
const OTSERV_FIRST = OTSERV;
const OTSERV_LAST = OTSERV_06;
const TFS_02 = 3;
const TFS_03 = 4;
const TFS_FIRST = TFS_02;
const TFS_LAST = TFS_03;
// other definitions
const ACCOUNT_NUMBER_LENGTH = 10;
session_save_path(SYSTEM . 'php_sessions');
session_start();
// basedir
$basedir = '';
@@ -95,23 +102,29 @@ for($i = 1; $i < $size; $i++)
$basedir = str_replace(array('/admin', '/install'), '', $basedir);
define('BASE_DIR', $basedir);
if(isset($_SERVER['HTTP_HOST'][0])) {
$baseHost = $_SERVER['HTTP_HOST'];
}
else {
if(isset($_SERVER['SERVER_NAME'][0])) {
$baseHost = $_SERVER['SERVER_NAME'];
}
else {
$baseHost = $_SERVER['SERVER_ADDR'];
if(!IS_CLI) {
if (isset($_SERVER['HTTP_HOST'][0])) {
$baseHost = $_SERVER['HTTP_HOST'];
} else {
if (isset($_SERVER['SERVER_NAME'][0])) {
$baseHost = $_SERVER['SERVER_NAME'];
} else {
$baseHost = $_SERVER['SERVER_ADDR'];
}
}
define('SERVER_URL', 'http' . (isset($_SERVER['HTTPS'][0]) && strtolower($_SERVER['HTTPS']) === 'on' ? 's' : '') . '://' . $baseHost);
define('BASE_URL', SERVER_URL . BASE_DIR . '/');
define('ADMIN_URL', SERVER_URL . BASE_DIR . '/admin/');
//define('CURRENT_URL', BASE_URL . $_SERVER['REQUEST_URI']);
require SYSTEM . 'exception.php';
}
define('SERVER_URL', 'http' . (isset($_SERVER['HTTPS'][0]) && strtolower($_SERVER['HTTPS']) === 'on' ? 's' : '') . '://' . $baseHost);
define('BASE_URL', SERVER_URL . BASE_DIR . '/');
define('ADMIN_URL', SERVER_URL . BASE_DIR . '/admin/');
$autoloadFile = VENDOR . 'autoload.php';
if (!is_file($autoloadFile)) {
throw new RuntimeException('The vendor folder is missing. Please download Composer: <a href="https://getcomposer.org/download">https://getcomposer.org/download</a>, install it and execute in the main MyAAC directory this command: <b>composer install</b>. Or download MyAAC from <a href="https://github.com/slawkens/myaac/releases">GitHub releases</a>, which includes Vendor folder.');
}
//define('CURRENT_URL', BASE_URL . $_SERVER['REQUEST_URI']);
require SYSTEM . 'exception.php';
require SYSTEM . 'autoload.php';
require $autoloadFile;

14
composer.json Normal file
View File

@@ -0,0 +1,14 @@
{
"require": {
"php": "^7.2.5 || ^8.0",
"ext-pdo": "*",
"ext-pdo_mysql": "*",
"ext-json": "*",
"ext-xml": "*",
"ext-dom": "*",
"phpmailer/phpmailer": "^6.1",
"composer/semver": "^3.2",
"twig/twig": "^1.0",
"erusev/parsedown": "^1.7"
}
}

View File

@@ -52,7 +52,6 @@ $config = array(
// head options (html)
'meta_description' => 'Tibia is a free massive multiplayer online role playing game (MMORPG).', // description of the site
'meta_keywords' => 'free online game, free multiplayer game, ots, open tibia server', // keywords list separated by commas
'title_separator' => ' - ',
// footer
'footer' => ''/*'<br/>Your Server &copy; 2016. All rights reserved.'*/,
@@ -86,15 +85,27 @@ $config = array(
),
// images
'outfit_images_url' => 'http://outfit-images.ots.me/outfit.php', // set to animoutfit.php for animated outfit
'item_images_url' => 'http://item-images.ots.me/1092/', // set to images/items if you host your own items in images folder
'outfit_images_url' => 'https://outfit-images.ots.me/outfit.php', // set to animoutfit.php for animated outfit
'outfit_images_wrong_looktypes' => [75, 126, 127, 266, 302], // this looktypes needs to have different margin-top and margin-left because they are wrong positioned
'item_images_url' => 'https://item-images.ots.me/1092/', // set to images/items if you host your own items in images folder
'item_images_extension' => '.gif',
// creatures
'creatures_images_url' => 'images/monsters/', // set to images/monsters if you host your own creatures in images folder
'creatures_images_extension' => '.gif',
'creatures_images_preview' => false, // set to true to allow picture previews for creatures
'creatures_items_url' => 'https://tibia.fandom.com/wiki/', // set to website which shows details about items.
'creatures_loot_percentage' => true, // set to true to show the loot tooltip percent
// account
'account_management' => true, // disable if you're using other method to manage users (fe. tfs account manager)
'account_login_by_email' => false, // use email instead of Account Name like in latest Tibia
'account_login_by_email_fallback' => false, // allow also additionally login by Account Name/Number (for users that might forget their email)
'account_create_auto_login' => false, // auto login after creating account?
'account_create_character_create' => true, // allow directly to create character on create account page?
'account_mail_verify' => false, // force users to confirm their email addresses when registering account
'account_mail_unique' => true, // email addresses cannot be duplicated? (one account = one email)
'account_mail_block_plus_sign' => true, // block email with '+' signs like test+box@gmail.com (help protect against spamming accounts)
'account_premium_days' => 0, // default premium days on new account
'account_premium_points' => 0, // default premium points on new account
'account_welcome_mail' => true, // send welcome email when user registers
@@ -124,11 +135,17 @@ $config = array(
'smtp_secure' => '', // What kind of encryption to use on the SMTP connection. Options: '', 'ssl' (GMail) or 'tls' (Microsoft Outlook)
'smtp_debug' => false, // set true to debug (you will see more info in error.log)
// reCAPTCHA (prevent spam bots)
// Google reCAPTCHA (prevent spam bots)
'recaptcha_enabled' => false, // enable recaptcha verification code
'recaptcha_type' => 'v3', // 'v2-checkbox', 'v2-invisible', 'v3'
'recaptcha_site_key' => '', // get your own site and secret keys at https://www.google.com/recaptcha
'recaptcha_secret_key' => '',
'recaptcha_theme' => 'light', // light, dark
// following option apply only for ReCaptcha v2-checkbox
'recaptcha_v2_theme' => 'light', // light, dark
// following option apply only for ReCaptcha v3
// min score for validation, between 0 - 1.0
// https://developers.google.com/recaptcha/docs/v3#interpreting_the_score
'recaptcha_v3_min_score' => 0.5,
//
'generate_new_reckey' => true, // let player generate new recovery key, he will receive e-mail with new rec key (not display on page, hacker can't generate rec key)
@@ -151,17 +168,25 @@ $config = array(
4 => 'Knight Sample'
),
'use_character_sample_skills' => false,
// it must show limited number of players after using search in character page
'characters_search_limit' => 15,
// town list used when creating character
// won't be displayed if there is only one item (rookgaard for example)
'character_towns' => array(1),
// characters lenght
// This is the minimum and the maximum length that a player can create a character. It is highly recommend the maximum lenght be 21.
// characters length
// This is the minimum and the maximum length that a player can create a character. It is highly recommend the maximum length to be 21.
'character_name_min_length' => 4,
'character_name_max_length' => 21,
'character_name_npc_check' => true,
// list of towns
// if you use TFS 1.3 with support for 'towns' table in database, then you can ignore this - it will be configured automatically (generated from your .OTBM map)
// if you use TFS 1.3 with support for 'towns' table in database, then you can ignore this - it will be configured automatically (from MySQL database - Table - towns)
// otherwise it will try to load from your .OTBM map file
// if you don't see towns on website, then you need to fill this out
'towns' => array(
0 => 'No town',
1 => 'Sample town'
@@ -172,6 +197,7 @@ $config = array(
'guild_need_level' => 1, // min. level to form a guild
'guild_need_premium' => true, // require premium account to form a guild?
'guild_image_size_kb' => 80, // maximum size of the guild logo image in KB (kilobytes)
'guild_description_default' => 'New guild. Leader must edit this text :)',
'guild_description_chars_limit' => 1000, // limit of guild description
'guild_description_lines_limit' => 6, // limit of lines, if description has more lines it will be showed as long text, without 'enters'
'guild_motd_chars_limit' => 150, // limit of MOTD (message of the day) that is shown later in the game on the guild channel
@@ -192,19 +218,19 @@ $config = array(
'team_display_outfit' => true,
// bans page
'bans_limit' => 50,
'bans_display_all' => true, // should all bans be displayed? (sorted page by page)
'bans_per_page' => 20,
// highscores page
'highscores_vocation_box' => true, // show 'Choose a vocation' box on the highscores (allowing peoples to sort highscores by vocation)?
'highscores_vocation' => true, // show player vocation under his nickname?
'highscores_frags' => false, // show 'Frags' tab (best fraggers on the server)? Only 0.3
'highscores_frags' => false, // show 'Frags' tab (best fraggers on the server)?
'highscores_balance' => false, // show 'Balance' tab (richest players on the server)
'highscores_outfit' => true, // show player outfit?
'highscores_country_box' => false, // doesnt work yet! (not implemented)
'highscores_groups_hidden' => 3, // this group id and higher won't be shown on the highscores
'highscores_ids_hidden' => array(0), // this ids of players will be hidden on the highscores (should be ids of samples)
'highscores_length' => 100, // how many records per page on highscores
'highscores_per_page' => 100, // how many records per page on highscores
'highscores_cache_ttl' => 15, // how often to update highscores from database in minutes (default 15 minutes)
// characters page
'characters' => array( // what things to display on character view page (true/false in each option)
@@ -221,7 +247,10 @@ $config = array(
'frags' => false,
'deleted' => false, // should deleted characters from same account be still listed on the list of characters? When enabled it will show that character is "[DELETED]"
),
'quests' => array(), // quests list (displayed in character view), name => storage
'quests' => array(
//'Some Quest' => 123,
//'Some Quest Two' => 456,
), // quests list (displayed in character view), name => storage
'signature_enabled' => true,
'signature_type' => 'tibian', // signature engine to use: tibian, mango, gesior
'signature_cache_time' => 5, // how long to store cached file (in minutes), default 5 minutes
@@ -252,9 +281,10 @@ $config = array(
'last_kills_limit' => 50, // max. number of deaths shown on the last kills page
// status, took automatically from config file if empty
'status_enabled' => true, // you can disable status checking by settings this to "false"
'status_ip' => '',
'status_port' => '',
'status_timeout' => 2, // how long to wait for the initial response from the server (default: 2 seconds)
'status_timeout' => 2.0, // how long to wait for the initial response from the server (default: 2 seconds)
// 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
@@ -262,7 +292,7 @@ $config = array(
'status_interval' => 60,
// admin panel
'admin_panel_modules' => 'lastlogin,points,coins',
'admin_panel_modules' => 'statistics,web_status,server_status,lastlogin,created,points,coins,balance', // default - statistics,web_status,server_status,lastlogin,created,points,coins,balance
// other
'anonymous_usage_statistics' => true,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 433 B

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 706 B

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1004 B

After

Width:  |  Height:  |  Size: 845 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 B

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 783 B

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 789 B

After

Width:  |  Height:  |  Size: 735 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 615 B

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 816 B

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 B

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 809 B

After

Width:  |  Height:  |  Size: 709 B

View File

@@ -38,7 +38,7 @@ else
$uri = str_replace(array('index.php/', '?'), '', $uri);
define('URI', $uri);
if(preg_match("/^[A-Za-z0-9-_%\'+]+\.png$/i", $uri)) {
if(preg_match("/^[A-Za-z0-9-_%'+]+\.png$/i", $uri)) {
$tmp = explode('.', $uri);
$_REQUEST['name'] = urldecode($tmp[0]);
@@ -48,7 +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'])) {
header('HTTP/1.0 404 Not Found');
http_response_code(404);
exit;
}
@@ -56,11 +56,17 @@ if(file_exists(BASE . 'config.local.php')) {
require_once BASE . 'config.local.php';
}
ini_set('log_errors', 1);
if(config('env') === 'dev') {
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
}
else {
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
}
if((!isset($config['installed']) || !$config['installed']) && file_exists(BASE . 'install'))
{
@@ -97,10 +103,12 @@ else {
'/^account\/character\/comment\/[A-Za-z0-9-_%+\']+\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'change_comment', 'name' => '$3'),
'/^account\/character\/comment\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'change_comment'),
'/^account\/confirm_email\/[A-Za-z0-9-_]+\/?$/' => array('subtopic' => 'accountmanagement', 'action' => 'confirm_email', 'v' => '$2'),
'/^bans\/[0-9]+\/?$/' => array('subtopic' => 'bans', 'page' => '$1'),
'/^characters\/[A-Za-z0-9-_%+\']+$/' => array('subtopic' => 'characters', 'name' => '$1'),
'/^changelog\/[0-9]+\/?$/' => array('subtopic' => 'changelog', 'page' => '$1'),
'/^commands\/add\/?$/' => array('subtopic' => 'commands', 'action' => 'add'),
'/^commands\/edit\/?$/' => array('subtopic' => 'commands', 'action' => 'edit'),
'/^creatures\/[A-Za-z0-9-_%+\']+$/' => array('subtopic' => 'creatures', 'creature' => '$1'),
'/^faq\/add\/?$/' => array('subtopic' => 'faq', 'action' => 'add'),
'/^faq\/edit\/?$/' => array('subtopic' => 'faq', 'action' => 'edit'),
'/^forum\/add_board\/?$/' => array('subtopic' => 'forum', 'action' => 'add_board'),#
@@ -170,6 +178,11 @@ $template_place_holders = array();
require_once SYSTEM . 'init.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.');
}
// event system
require_once SYSTEM . 'hooks.php';
$hooks = new Hooks();
@@ -181,31 +194,7 @@ require_once SYSTEM . 'status.php';
$twig->addGlobal('config', $config);
$twig->addGlobal('status', $status);
// verify myaac tables exists in database
if(!$db->hasTable('myaac_account_actions')) {
throw new RuntimeException('Seems that the table <strong>myaac_account_actions</strong> of MyAAC doesn\'t exist in the database. This is a fatal error. You can try to reinstall MyAAC by visiting <a href="' . BASE_URL . 'install">this</a> url.');
}
// database migrations
$tmp = '';
if(fetchDatabaseConfig('database_version', $tmp)) { // we got version
$tmp = (int)$tmp;
if($tmp < DATABASE_VERSION) { // import if older
$db->revalidateCache();
for($i = $tmp + 1; $i <= DATABASE_VERSION; $i++) {
require SYSTEM . 'migrations/' . $i . '.php';
updateDatabaseConfig('database_version', $i);
}
}
}
else { // register first version
registerDatabaseConfig('database_version', 0);
$db->revalidateCache();
for($i = 1; $i <= DATABASE_VERSION; $i++) {
require SYSTEM . 'migrations/' . $i . '.php';
updateDatabaseConfig('database_version', $i);
}
}
require SYSTEM . 'migrate.php';
$hooks->trigger(HOOK_STARTUP);
@@ -325,8 +314,10 @@ if($load_it)
if(SITE_CLOSED && admin())
$content .= '<p class="note">Site is under maintenance (closed mode). Only privileged users can see it.</p>';
if($config['backward_support'])
require SYSTEM . 'compat_pages.php';
if($config['backward_support']) {
require SYSTEM . 'compat/pages.php';
require SYSTEM . 'compat/classes.php';
}
$ignore = false;
@@ -346,11 +337,15 @@ if($load_it)
)) . $content;
}
} else {
$file = SYSTEM . 'pages/' . $page . '.php';
$file = $template_path . '/pages/' . $page . '.php';
if(!@file_exists($file))
{
$page = '404';
$file = SYSTEM . 'pages/404.php';
$file = SYSTEM . 'pages/' . $page . '.php';
if(!@file_exists($file))
{
$page = '404';
$file = SYSTEM . 'pages/404.php';
}
}
}
@@ -376,7 +371,15 @@ if($config['backward_support']) {
$topic = $title;
}
$title_full = (isset($title) ? $title . $config['title_separator'] : '') . $config['lua']['serverName'];
/**
* @var OTS_Account $account_logged
*/
if ($logged && admin()) {
$content .= $twig->render('admin-bar.html.twig', [
'username' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId()
]);
}
$title_full = (isset($title) ? $title . ' - ' : '') . $config['lua']['serverName'];
require $template_path . '/' . $template_index;
echo base64_decode('PCEtLSBQb3dlcmVkIGJ5IE15QUFDIDo6IGh0dHBzOi8vd3d3Lm15LWFhYy5vcmcvIC0tPg==') . PHP_EOL;

View File

@@ -6,12 +6,18 @@ $ots = POT::getInstance();
require SYSTEM . 'database.php';
if(!isset($db)) {
$database_error = $locale['step_database_error_mysql_connect'] . '<br/>' .
$locale['step_database_error_mysql_connect_2'] .
'<ul>' .
'<li>' . $locale['step_database_error_mysql_connect_3'] . '</li>' .
'<li>' . $locale['step_database_error_mysql_connect_4'] . '</li>' .
'</ul>' . '<br/>' . $error;
$database_error = '<p class="lead">' . $locale['step_database_error_mysql_connect'] . '</p>';
$database_error .= '<p>' . $locale['step_database_error_mysql_connect_2'] . '</p>';
$database_error .= '<ul class="list-group">' .
'<li class="list-group-item list-group-item-warning">' . $locale['step_database_error_mysql_connect_3'] . '</li>' .
'<li class="list-group-item list-group-item-warning">' . $locale['step_database_error_mysql_connect_4'] . '</li>' .
'</ul>';
$database_error .= '<div class="alert alert-danger mt-4">
<span>' . $error . '</span>
</div>';
}
else {
if($db->hasTable('accounts'))

View File

@@ -62,9 +62,9 @@ function next_buttons($previous = true, $next = true)
$ret .= '<input class="button" type="submit" onclick="document.getElementById(\'step\').value=\'' . $steps[$i + 1] . '\';" value="' . $locale['next'] . '" />';
*/
if($previous)
$ret .= '<input type="button" class="button" onclick="document.getElementById(\'step\').value=\'' . $steps[$i - 1] . '\'; this.form.submit();" value="&laquo; ' . $locale['previous'] . '" />';
$ret .= '<input type="button" class="button btn btn-primary m-2" onclick="document.getElementById(\'step\').value=\'' . $steps[$i - 1] . '\'; this.form.submit();" value="&laquo; ' . $locale['previous'] . '" />';
if($next)
$ret .= '<input type="button" class="button" onclick="document.getElementById(\'step\').value=\'' . $steps[$i + 1] . '\'; this.form.submit(); " value="' . $locale['next'] . ' &raquo;" />';
$ret .= '<input type="button" class="button btn btn-primary m-2" onclick="document.getElementById(\'step\').value=\'' . $steps[$i + 1] . '\'; this.form.submit(); " value="' . $locale['next'] . ' &raquo;" />';
$ret .= '</div>';
return $ret;

View File

@@ -1,3 +1,5 @@
SET @myaac_database_version = 33;
CREATE TABLE `myaac_account_actions`
(
`account_id` INT(11) NOT NULL,
@@ -57,6 +59,8 @@ CREATE TABLE `myaac_config`
UNIQUE (`name`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
INSERT INTO `myaac_config` (`name`, `value`) VALUES ('database_version', @myaac_database_version);
CREATE TABLE `myaac_faq`
(
`id` INT(11) NOT NULL AUTO_INCREMENT,
@@ -203,21 +207,24 @@ CREATE TABLE `myaac_monsters` (
`use_haste` tinyint(1) NOT NULL,
`voices` text NOT NULL,
`immunities` varchar(255) NOT NULL,
`elements` TEXT NOT NULL,
`summonable` tinyint(1) NOT NULL,
`convinceable` tinyint(1) NOT NULL,
`pushable` TINYINT(1) NOT NULL DEFAULT '0',
`canpushitems` TINYINT(1) NOT NULL DEFAULT '0',
`canwalkonenergy` TINYINT(1) NOT NULL DEFAULT '0',
`canwalkonpoison` TINYINT(1) NOT NULL DEFAULT '0',
`canwalkonfire` TINYINT(1) NOT NULL DEFAULT '0',
`runonhealth` TINYINT(1) NOT NULL DEFAULT '0',
`hostile` TINYINT(1) NOT NULL DEFAULT '0',
`attackable` TINYINT(1) NOT NULL DEFAULT '0',
`rewardboss` TINYINT(1) NOT NULL DEFAULT '0',
`defense` INT(11) NOT NULL DEFAULT '0',
`armor` INT(11) NOT NULL DEFAULT '0',
`canpushcreatures` TINYINT(1) NOT NULL DEFAULT '0',
`race` varchar(255) NOT NULL,
`loot` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
CREATE TABLE `myaac_videos`
(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`title` VARCHAR(100) NOT NULL DEFAULT '',
`youtube_id` VARCHAR(20) NOT NULL,
`author` VARCHAR(50) NOT NULL DEFAULT '',
`ordering` INT(11) NOT NULL DEFAULT 0,
`hidden` TINYINT(1) NOT NULL DEFAULT 0,
`summons` TEXT NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
@@ -320,9 +327,9 @@ CREATE TABLE `myaac_spells`
CREATE TABLE `myaac_visitors`
(
`ip` VARCHAR(16) NOT NULL,
`ip` VARCHAR(45) NOT NULL,
`lastvisit` INT(11) NOT NULL DEFAULT 0,
`page` VARCHAR(100) NOT NULL,
`page` VARCHAR(2048) NOT NULL,
UNIQUE (`ip`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;

View File

@@ -2,10 +2,10 @@ We have detected that you don't have access to write to the system/cache directo
<style type="text/css">
.console {
font-family:Courier;
font-family: Courier,serif;
color: #CCCCCC;
background: #000000;
border: 3px double #CCCCCC;
padding: 0px;
padding: 0;
}
</style>
</style>

View File

@@ -95,10 +95,6 @@ if($step == 'database') {
$errors[] = $locale['step_config_mail_admin_error'];
break;
}
else if($key == 'mail_address' && !Validator::email($value)) {
$errors[] = $locale['step_config_mail_address_error'];
break;
}
else if($key == 'timezone' && !in_array($value, DateTimeZone::listIdentifiers())) {
$errors[] = $locale['step_config_timezone_error'];
break;

View File

@@ -1,7 +1,7 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['saved'])) {
echo '<p class="warning">' . $locale['already_installed'] . '</p>';
echo '<div class="alert alert-warning"><span>' . $locale['already_installed'] . '</span></div>';
}
else {
unset($_SESSION['saved']);

View File

@@ -1,6 +1,23 @@
<?php
defined('MYAAC') or die('Direct access not allowed!');
// configuration
$dirs_required = [
'system/logs',
'system/cache',
];
$dirs_optional = [
'images/guilds' => $locale['step_requirements_warning_images_guilds'],
'images/gallery' => $locale['step_requirements_warning_images_gallery'],
];
$extensions_required = [
'pdo', 'pdo_mysql', 'json', 'xml'
];
$extensions_optional = [
'gd' => $locale['step_requirements_warning_player_signatures'],
'zip' => $locale['step_requirements_warning_install_plugins'],
];
/*
*
* @param string $name
@@ -10,11 +27,11 @@ defined('MYAAC') or die('Direct access not allowed!');
function version_check($name, $ok, $info = '', $warning = false)
{
global $failed;
echo '<p class="' . ($ok ? 'success' : ($warning ? 'warning' : 'error')) . '">' . $name;
echo '<div class="alert alert-' . ($ok ? 'success' : ($warning ? 'warning' : 'danger')) . '">' . $name;
if(!empty($info))
echo ': <b>' . $info . '</b>';
echo '</p>';
echo '</div>';
if(!$ok && !$warning)
$failed = true;
}
@@ -23,27 +40,42 @@ $failed = false;
// start validating
version_check($locale['step_requirements_php_version'], (PHP_VERSION_ID >= 50500), PHP_VERSION);
foreach(array('images/guilds', 'images/houses', 'images/gallery') as $value)
foreach ($dirs_required as $value)
{
$is_writable = is_writable(BASE . $value);
$is_writable = is_writable(BASE . $value) && (MYAAC_OS != 'WINDOWS' || win_is_writable(BASE . $value));
version_check($locale['step_requirements_write_perms'] . ': ' . $value, $is_writable);
}
foreach ($dirs_optional as $dir => $errorMsg) {
$is_writable = is_writable(BASE . $dir) && (MYAAC_OS != 'WINDOWS' || win_is_writable(BASE . $dir));
version_check($locale['step_requirements_write_perms'] . ': ' . $dir, $is_writable, $is_writable ? '' : $errorMsg, true);
}
$ini_register_globals = ini_get_bool('register_globals');
version_check('register_long_arrays', !$ini_register_globals, $ini_register_globals ? $locale['on'] : $locale['off']);
$ini_safe_mode = ini_get_bool('safe_mode');
version_check('safe_mode', !$ini_safe_mode, $ini_safe_mode ? $locale['on'] : $locale['off'], true);
version_check(str_replace('$EXTENSION$', 'PDO', $locale['step_requirements_extension']) , extension_loaded('pdo'), extension_loaded('pdo') ? $locale['loaded'] : $locale['not_loaded']);
version_check(str_replace('$EXTENSION$', 'XML', $locale['step_requirements_extension']), extension_loaded('xml'), extension_loaded('xml') ? $locale['loaded'] : $locale['not_loaded']);
version_check(str_replace('$EXTENSION$', 'ZIP', $locale['step_requirements_extension']), extension_loaded('zip'), extension_loaded('zip') ? $locale['loaded'] : $locale['not_loaded']);
if($failed)
{
echo '<br/><b>' . $locale['step_requirements_failed'];
echo next_form(true, false);
foreach ($extensions_required as $ext) {
$loaded = extension_loaded($ext);
version_check(str_replace('$EXTENSION$', strtoupper($ext), $locale['step_requirements_extension']) , $loaded, $loaded ? $locale['loaded'] : $locale['not_loaded']);
}
else
foreach ($extensions_optional as $ext => $errorMsg) {
$loaded = extension_loaded($ext);
version_check(str_replace('$EXTENSION$', strtoupper($ext), $locale['step_requirements_extension']) , $loaded, $loaded ? $locale['loaded'] : $locale['not_loaded'] . '. ' . $errorMsg, true);
}
echo '<div class="text-center m-3">';
if($failed) {
echo '<div class="alert alert-warning"><span>' . $locale['step_requirements_failed'] . '</span></div>';
echo next_form(true, false);
}else {
echo next_form(true, true);
?>
}
echo '</div>';
?>

View File

@@ -21,8 +21,6 @@ if(!$error) {
// user can disable when he wants
$content .= '$config[\'env\'] = \'prod\'; // dev or prod';
$content .= PHP_EOL;
$content .= '$config[\'mail_enabled\'] = true;';
$content .= PHP_EOL;
foreach($_SESSION as $key => $value)
{
if(strpos($key, 'var_') !== false)
@@ -67,10 +65,6 @@ if(!$error) {
error($locale['step_config_mail_admin_error']);
$error = true;
}
if(!Validator::email($_SESSION['var_mail_address'])) {
error($locale['step_config_mail_address_error']);
$error = true;
}
$content .= '$config[\'session_prefix\'] = \'myaac_' . generateRandomString(8, true, false, true, false) . '_\';';
$content .= PHP_EOL;
@@ -82,6 +76,7 @@ if(!$error) {
}
if($saved) {
success($locale['step_database_config_saved']);
if(!$error) {
$_SESSION['saved'] = true;
}
@@ -100,8 +95,10 @@ if(!$error) {
}
?>
<form action="<?php echo BASE_URL; ?>install/" method="post">
<input type="hidden" name="step" id="step" value="admin" />
<?php echo next_buttons(true, $error ? false : true);
?>
</form>
<div class="text-center m-3">
<form action="<?php echo BASE_URL; ?>install/" method="post">
<input type="hidden" name="step" id="step" value="admin" />
<?php echo next_buttons(true, $error ? false : true);
?>
</form>
</div>

View File

@@ -1,299 +1,13 @@
* {
margin: 0; padding: 0;
}
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400&display=swap');
body {
text-align: center;
font: 12px Verdana;
color: #000000;
background-color: #000000;
}
img {
border: 0;
font-family: 'Roboto', sans-serif;
}
.break {
font-size: 0;
width: 0; height: 0;
clear: both;
}
.alignleft {
float: left;
margin: 4px 10px 5px 0;
}
.alignright {
float: right;
margin: 4px 0 5px 10px;
}
.aligncenter {
text-align: center;
h1{
font-weight: 100 !important;
}
/** BEGIN wrapper **/
#wrapper {
background: #ffffff url(images/background.jpg) repeat-x 0 0;
width: 980px;
}
#header {
margin-bottom: 10px;
border-bottom: 1px solid #eee;
padding-bottom: 15px;
}
#footer {
padding-top: 15px;
border-top: 1px solid #eee;
margin-top: 10px;
text-align: right;
color: #555;
}
#header h1 {
font-weight: bold;
margin: 0;
padding: 0;
}
#header span {
font-size: 25px;
color: #000;
font-weight: bold;
padding-left: 40px;
line-height: 80px;
}
#version {
float: right;
color: #000;
font-size: 17px;
padding-top: 25px;
padding-right: 5px;
}
/** BEGIN body **/
#body {
background: url(images/wrapper.gif) repeat-y 0 0;
}
/** END body **/
/** BEGIN content **/
#content {
width: 642px;
float: left;
padding: 20px 18px 20px 20px;
color: #434242;
}
/** begin headers **/
h1, h2, h3, h4, h5, h6 {
font-family: Tahoma;
margin-bottom: 10px;
}
h2, h3, h4, h5, h6 {
margin-top: 30px;
}
h1 { font-size: 2em; }
h2 { font-size: 1.6em; }
h3 { font-size: 1.3em; }
h4, h5, h6 { font-size: 1em; }
/** end headers **/
/** begin messages **/
.error, .success, .note, .warning {
font-weight: bold;
font-size: 0.9em;
padding: 4px 10px 4px 24px;
background-repeat: no-repeat;
background-position: 5px 6px;
border-style: solid;
border-width: 1px;
line-height: 1.6em;
margin-bottom: 10px;
}
.error {
background-color: #FDD9D9;
background-image: url(images/error.gif);
border-color: #FBA3A3;
color: #D80303;
}
.success {
background-color: #E4FCD9;
background-image: url(images/success.gif);
border-color: #BFFDA3;
color: #35A502;
}
.note {
background-color: #DDEAFA;
background-image: url(images/note.gif);
border-color: #A3D8FD;
color: #026DA5;
}
.warning {
background-color: #FBF0B3;
background-image: url(images/warning.gif);
border-color: #FBBB95;
color: #FD6002;
}
/** end messages **/
/** begin form **/
form {
border: 1px solid #DDDDDD;
padding: 16px;
}
form .input {
padding-top: 12px;
clear: both;
}
form .first {
padding-top: 0;
}
form .input p {
margin-bottom: 7px !important;
}
form input {
margin-right: 5px;
}
form label {
margin-right: 10px;
color: #8B8B8B;
}
form input.text, form textarea {
border: 1px solid #BEBDBD;
font-size: 1em;
font-family: Verdana;
background-color: #F3F3F3;
color: #808080;
padding: 2px;
max-width: 100%;
}
.positive, .negative {
font-size: 0.9em;
font-weight: bold;
padding: 1px 0 0 20px;
background-repeat: no-repeat;
background-position: 0 0;
display: inline;
margin-top: 2px;
}
.positive {
background-image: url(images/positive.gif);
color: #35A502;
}
.negative {
background-image: url(images/negative.gif);
color: #D80303;
}
form textarea {
line-height: 1.6em;
}
form button, form input.button {
font-size: 0.9em;
font-family: Verdana;
font-weight: bold;
color: #ffffff;
background: #B6B4B4 url(images/button.gif) repeat-x 0 0;
border: 1px solid #B6B4B4;
padding: 5px 10px;
}
/** end form **/
/** begin table **/
table {
}
table th {
font-size: 0.9em;
color: #ffffff;
background-color: #679BC5;
padding: 2px 4px;
line-height: 1.6em;
}
table td {
line-height: 1.6em;
padding: 2px 4px;
}
table tr.odd td { background-color: #EEEEEE; }
table tr.even td { background-color: #E5E5E5; }
/** end table **/
/** begin paragraphs, lists, etc. **/
#content p {
line-height: 1.6em;
margin-bottom: 10px;
}
#content ul, #content ol {
list-style-position: inside;
}
#content li {
line-height: 1.6em;
padding: 2px 0 2px 0;
}
a {
color: #679BC5;
}
a:hover {
color: #ff0000;
text-decoration: none;
}
blockquote {
padding: 10px;
background-color: #eeeeee;
line-height: 1.6em;
border-width: 2px 0 1px;
border-style: solid;
border-color: #e0e0e0;
}
/** end paragraphs, lists, etc. **/
/** END content **/
/** BEGIN sidebar **/
#sidebar {
width: 300px;
float: right;
padding: 10px 0;
}
#sidebar h2 {
background: green url(images/sidehead.gif) no-repeat 0 0;
margin: 0 10px;
font-size: 1em;
color: #ffffff;
padding: 7px 10px;
}
#sidebar ul {
list-style-type: none;
background: #E0E0E0 url(images/sidebody.gif) no-repeat 0 bottom;
padding: 10px;
margin: 0 10px 10px;
}
#sidebar ul li {
padding: 4px 0 4px 14px;
background: none;
line-height: 1.6em;
font-size: 0.9em;
font-weight: bold;
}
#sidebar ul li a {
color: #000000;
text-decoration: none;
}
#sidebar ul li a:hover {
text-decoration: none;
color: #ff0000;
}
#sidebar ul li a:active {
text-decoration: none;
color: #ff0000;
}
#sidebar ul li current {
text-decoration: none;
color: #ff0000;
}
.current {
text-decoration: none;
color: #ff0000;
}
h3 {
font-weight: 300 !important;
}

View File

@@ -1,48 +1,74 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="<?php echo $locale['direction']; ?>" lang="<?php echo $locale['lang']; ?>" xml:lang="<?php echo $locale['lang']; ?>">
<!DOCTYPE html>
<html dir="<?php echo $locale['direction']; ?>" lang="<?php echo $locale['lang']; ?>" xml:lang="<?php echo $locale['lang']; ?>">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<?php echo $locale['encoding']; ?>" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MyAAC - <?php echo $locale['installation']; ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="template/style.css" />
<script type="text/javascript" src="<?php echo BASE_URL; ?>tools/js/jquery.min.js"></script>
</head>
<body>
<div id="wrapper">
<!--div class="buffer"-->
<div id="header">
<h1>MyAAC <?php echo $locale['installation']; ?></h1>
</div>
<div id="body">
<div id="body" class="container">
<header id="header" class="pt-5 pb-4 pb-sm-5">
<h1>MyAAC <?php echo $locale['installation']; ?></h1>
</header>
<div id="sidebar">
<h2><?php echo $locale['steps']; ?></h2>
<ul>
<?php
$i = 0;
foreach($steps as $key => $value)
echo '<li' . ($step == $value ? ' class="current"' : '') . '>' . ++$i . '. ' . $locale['step_' . $value] . '</li>';
?>
</ul>
</div>
<div class="row">
<div id="content">
<div id="sidebar" class="col-md-3">
<h3><?php echo $locale['steps']; ?></h3>
<ul class="list-group mt-4">
<?php
if(isset($locale['step_' . $step . '_title']))
echo '<h1>' . $locale['step_' . $step . '_title'] . '</h1>';
else
echo '<h1>' . $locale['step_' . $step] . '</h1>';
echo $content;
$i = 0;
foreach($steps as $key => $value){
if ($step == $value) {
$progress = ($i == 6) ? 100 : $i * 16;
}
echo '<li' . ($step == $value ? ' class="list-group-item active"' : ' class="list-group-item"') . '>' . ++$i . '. ' . $locale['step_' . $value] . '</li>';
}
?>
</div>
<div class="break"></div>
</ul>
</div>
<!--/div-->
<div id="content" class="col-md-9">
<?php
if(isset($locale['step_' . $step . '_title']))
echo '<h3 class="mb-4 mt-4 mt-md-0">' . $locale['step_' . $step . '_title'] . '</h3>';
else
echo '<h3 class="mb-4 mt-4 mt-md-0">' . $locale['step_' . $step] . '</h3>';
?>
<?php
if(!isset($config['installed'])):
?>
<div class="row">
<div class="col-md-12">
<div class="progress mb-2">
<div class="progress-bar progress-bar-striped progress-bar-animated" style="width: <?php echo $progress; ?>%" role="progressbar" aria-valuenow="<?php echo $progress; ?>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
<?php endif; ?>
<?php echo $content; ?>
</div>
</div>
<hr />
</div>
<div id="footer">
<footer id="footer" class="p-4">
<p style="text-align: center;"><?php echo base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4='); ?></p>
</div>
</footer>
</body>
</html>

View File

@@ -1,4 +1,6 @@
<?php
define('MYAAC_INSTALL', true);
require_once '../../common.php';
require SYSTEM . 'functions.php';
@@ -48,7 +50,6 @@ else {
try {
$db->query(file_get_contents(BASE . 'install/includes/schema.sql'));
registerDatabaseConfig('database_version', DATABASE_VERSION);
$locale['step_database_success_schema'] = str_replace('$PREFIX$', TABLE_PREFIX, $locale['step_database_success_schema']);
success($locale['step_database_success_schema']);
}
@@ -247,4 +248,4 @@ if($db->hasTable('z_forum')) {
success($locale['step_database_adding_field'] . ' z_forum.closed...');
}
}
}
}

View File

@@ -1,4 +1,6 @@
<?php
define('MYAAC_INSTALL', true);
require_once '../../common.php';
require SYSTEM . 'functions.php';
@@ -34,47 +36,18 @@ function insert_sample_if_not_exist($p) {
$success = true;
insert_sample_if_not_exist(array('name' => 'Rook Sample', 'level' => 1, 'vocation_id' => 0, 'health' => 150, 'healthmax' => 150, 'experience' => 0, 'looktype' => 130, 'mana' => 0, 'manamax' => 0, 'soul' => 100, 'cap' => 400));
insert_sample_if_not_exist(array('name' => 'Sorcerer Sample', 'level' => 8, 'vocation_id' => 1, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 35, 'manamax' => 35, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Druid Sample', 'level' => 8, 'vocation_id' => 2, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 35, 'manamax' => 35, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Paladin Sample', 'level' => 8, 'vocation_id' => 3, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 129, 'mana' => 35, 'manamax' => 35, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Knight Sample', 'level' => 8, 'vocation_id' => 4, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 131, 'mana' => 35, 'manamax' => 35, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Sorcerer Sample', 'level' => 8, 'vocation_id' => 1, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Druid Sample', 'level' => 8, 'vocation_id' => 2, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 130, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Paladin Sample', 'level' => 8, 'vocation_id' => 3, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 129, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
insert_sample_if_not_exist(array('name' => 'Knight Sample', 'level' => 8, 'vocation_id' => 4, 'health' => 185, 'healthmax' => 185, 'experience' => 4200, 'looktype' => 131, 'mana' => 90, 'manamax' => 90, 'soul' => 100, 'cap' => 470));
if($success) {
success($locale['step_database_imported_players']);
}
require LIBS . 'items.php';
if(Items::loadFromXML())
success($locale['step_database_loaded_items']);
else
error(Items::getError());
require LIBS . 'weapons.php';
if(Weapons::loadFromXML())
success($locale['step_database_loaded_weapons']);
else
error(Weapons::getError());
require LIBS . 'creatures.php';
if(Creatures::loadFromXML()) {
success($locale['step_database_loaded_monsters']);
if(Creatures::getMonstersList()->hasErrors()) {
$locale['step_database_error_monsters'] = str_replace('$LOG$', 'system/logs/error.log', $locale['step_database_error_monsters']);
warning($locale['step_database_error_monsters']);
}
}
else {
error(Creatures::getLastError());
}
require LIBS . 'spells.php';
if(Spells::loadFromXML()) {
success($locale['step_database_loaded_spells']);
}
else {
error(Spells::getLastError());
}
require LIBS . 'DataLoader.php';
DataLoader::setLocale($locale);
DataLoader::load();
// update config.highscores_ids_hidden
require_once SYSTEM . 'migrations/20.php';
@@ -91,9 +64,10 @@ require_once SYSTEM . 'migrations/22.php';
// add myaac_pages pages
require_once SYSTEM . 'migrations/27.php';
require_once SYSTEM . 'migrations/30.php';
$locale['step_finish_desc'] = str_replace('$ADMIN_PANEL$', generateLink(str_replace('tools/', '',ADMIN_URL), $locale['step_finish_admin_panel'], true), $locale['step_finish_desc']);
$locale['step_finish_desc'] = str_replace('$HOMEPAGE$', generateLink(str_replace('tools/', '', BASE_URL), $locale['step_finish_homepage'], true), $locale['step_finish_desc']);
$locale['step_finish_desc'] = str_replace('$LINK$', generateLink('https://my-aac.org', 'https://my-aac.org', true), $locale['step_finish_desc']);
success($locale['step_finish_desc']);
success($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

@@ -11,7 +11,7 @@ server {
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_read_timeout 240;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
}
location ~ /\.ht {
@@ -22,4 +22,4 @@ server {
deny all;
return 404;
}
}
}

View File

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

View File

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

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

@@ -0,0 +1,15 @@
<?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 Player extends OTS_Player {}
class Guild extends OTS_Guild {}
class GuildRank extends OTS_GuildRank {}
class House extends OTS_House {}

View File

@@ -9,35 +9,10 @@
*/
defined('MYAAC') or die('Direct access not allowed!');
if(!isset($config['database_user'][0], $config['database_password'][0], $config['database_name'][0]))
{
if(isset($config['lua']['sqlType'])) {// tfs 0.3
if(isset($config['lua']['mysqlHost'])) {// tfs 0.2
$config['otserv_version'] = TFS_02;
$config['database_type'] = 'mysql';
$config['database_host'] = $config['lua']['mysqlHost'];
$config['database_port'] = $config['lua']['mysqlPort'];
$config['database_user'] = $config['lua']['mysqlUser'];
$config['database_password'] = $config['lua']['mysqlPass'];
$config['database_name'] = $config['lua']['mysqlDatabase'];
$config['database_encryption'] = $config['lua']['passwordType'];
}
else {
$config['otserv_version'] = TFS_03;
$config['database_type'] = $config['lua']['sqlType'];
$config['database_host'] = $config['lua']['sqlHost'];
$config['database_port'] = $config['lua']['sqlPort'];
$config['database_user'] = $config['lua']['sqlUser'];
$config['database_password'] = $config['lua']['sqlPass'];
$config['database_name'] = $config['lua']['sqlDatabase'];
$config['database_encryption'] = $config['lua']['encryptionType'];
if(!isset($config['database_encryption']) || empty($config['database_encryption'])) // before 0.3.6
$config['database_encryption'] = $config['lua']['passwordType'];
}
}
else if(isset($config['lua']['mysqlHost'])) // tfs 1.0
{
if(!isset($config['database_user'][0], $config['database_password'][0], $config['database_name'][0]))
{
if(isset($config['lua']['sqlType'])) {// tfs 0.3
if(isset($config['lua']['mysqlHost'])) {// tfs 0.2
$config['otserv_version'] = TFS_02;
$config['database_type'] = 'mysql';
$config['database_host'] = $config['lua']['mysqlHost'];
@@ -45,76 +20,100 @@ defined('MYAAC') or die('Direct access not allowed!');
$config['database_user'] = $config['lua']['mysqlUser'];
$config['database_password'] = $config['lua']['mysqlPass'];
$config['database_name'] = $config['lua']['mysqlDatabase'];
if(!isset($config['database_socket'][0])) {
$config['database_socket'] = isset($config['lua']['mysqlSock']) ? trim($config['lua']['mysqlSock']) : '';
}
$config['database_encryption'] = 'sha1';
$config['database_encryption'] = $config['lua']['passwordType'];
}
else if(isset($config['lua']['database_type'])) // otserv
{
$config['otserv_version'] = OTSERV;
$config['database_type'] = $config['lua']['database_type'];
$config['database_host'] = $config['lua']['database_host'];
$config['database_port'] = $config['lua']['database_port'];
$config['database_user'] = $config['lua']['database_username'];
$config['database_password'] = $config['lua']['database_password'];
$config['database_name'] = $config['lua']['database_schema'];
$config['database_encryption'] = isset($config['lua']['passwordtype']) ? $config['lua']['passwordtype'] : $config['lua']['password_type'];
$config['database_salt'] = isset($config['lua']['passwordsalt']) ? $config['lua']['passwordsalt'] : $config['lua']['password_salt'];
}
else if(isset($config['lua']['sql_host'])) // otserv 0.6.3 / 0.6.4
{
$config['otserv_version'] = OTSERV_06;
$config['database_type'] = $config['lua']['sql_type'];
$config['database_host'] = $config['lua']['sql_host'];
$config['database_port'] = $config['lua']['sql_port'];
$config['database_user'] = $config['lua']['sql_user'];
$config['database_password'] = $config['lua']['sql_pass'];
$config['database_name'] = $config['lua']['sql_db'];
$config['database_encryption'] = isset($config['lua']['passwordtype']) ? $config['lua']['passwordtype'] : $config['lua']['password_type'];
$config['database_salt'] = isset($config['lua']['passwordsalt']) ? $config['lua']['passwordsalt'] : $config['lua']['password_salt'];
else {
$config['otserv_version'] = TFS_03;
$config['database_type'] = $config['lua']['sqlType'];
$config['database_host'] = $config['lua']['sqlHost'];
$config['database_port'] = $config['lua']['sqlPort'];
$config['database_user'] = $config['lua']['sqlUser'];
$config['database_password'] = $config['lua']['sqlPass'];
$config['database_name'] = $config['lua']['sqlDatabase'];
$config['database_encryption'] = $config['lua']['encryptionType'];
if(!isset($config['database_encryption']) || empty($config['database_encryption'])) // before 0.3.6
$config['database_encryption'] = $config['lua']['passwordType'];
}
}
if(isset($config['lua']['useMD5Passwords']) && getBoolean($config['lua']['useMD5Passwords']))
$config['database_encryption'] = 'md5';
if(!isset($config['database_log'])) {
$config['database_log'] = false;
}
if(!isset($config['database_socket'])) {
$config['database_socket'] = '';
}
try {
$ots->connect(array(
'host' => $config['database_host'],
'user' => $config['database_user'],
'password' => $config['database_password'],
'database' => $config['database_name'],
'log' => $config['database_log'],
'socket' => @$config['database_socket'],
'persistent' => @$config['database_persistent']
)
);
$db = POT::getInstance()->getDBHandle();
}
catch(PDOException $error) {
if(isset($cache) && $cache->enabled()) {
$cache->delete('config_lua');
else if(isset($config['lua']['mysqlHost'])) // tfs 1.0
{
$config['otserv_version'] = TFS_02;
$config['database_type'] = 'mysql';
$config['database_host'] = $config['lua']['mysqlHost'];
$config['database_port'] = $config['lua']['mysqlPort'];
$config['database_user'] = $config['lua']['mysqlUser'];
$config['database_password'] = $config['lua']['mysqlPass'];
$config['database_name'] = $config['lua']['mysqlDatabase'];
if(!isset($config['database_socket'][0])) {
$config['database_socket'] = isset($config['lua']['mysqlSock']) ? trim($config['lua']['mysqlSock']) : '';
}
$config['database_encryption'] = 'sha1';
}
else if(isset($config['lua']['database_type'])) // otserv
{
$config['otserv_version'] = OTSERV;
$config['database_type'] = $config['lua']['database_type'];
$config['database_host'] = $config['lua']['database_host'];
$config['database_port'] = $config['lua']['database_port'];
$config['database_user'] = $config['lua']['database_username'];
$config['database_password'] = $config['lua']['database_password'];
$config['database_name'] = $config['lua']['database_schema'];
$config['database_encryption'] = isset($config['lua']['passwordtype']) ? $config['lua']['passwordtype'] : $config['lua']['password_type'];
$config['database_salt'] = isset($config['lua']['passwordsalt']) ? $config['lua']['passwordsalt'] : $config['lua']['password_salt'];
}
else if(isset($config['lua']['sql_host'])) // otserv 0.6.3 / 0.6.4
{
$config['otserv_version'] = OTSERV_06;
$config['database_type'] = $config['lua']['sql_type'];
$config['database_host'] = $config['lua']['sql_host'];
$config['database_port'] = $config['lua']['sql_port'];
$config['database_user'] = $config['lua']['sql_user'];
$config['database_password'] = $config['lua']['sql_pass'];
$config['database_name'] = $config['lua']['sql_db'];
$config['database_encryption'] = isset($config['lua']['passwordtype']) ? $config['lua']['passwordtype'] : $config['lua']['password_type'];
$config['database_salt'] = isset($config['lua']['passwordsalt']) ? $config['lua']['passwordsalt'] : $config['lua']['password_salt'];
}
}
if(defined('MYAAC_INSTALL')) {
return; // installer will take care of this
}
if(isset($config['lua']['useMD5Passwords']) && getBoolean($config['lua']['useMD5Passwords']))
$config['database_encryption'] = 'md5';
throw new RuntimeException('ERROR: Cannot connect to MySQL database.<br/>' .
'Possible reasons:' .
'<ul>' .
'<li>MySQL is not configured propertly in <i>config.lua</i>.</li>' .
'<li>MySQL server is not running.</li>' .
'</ul>' . $error->getMessage());
if(!isset($config['database_log'])) {
$config['database_log'] = false;
}
}
if(!isset($config['database_socket'])) {
$config['database_socket'] = '';
}
try {
$ots->connect(array(
'host' => $config['database_host'],
'user' => $config['database_user'],
'password' => $config['database_password'],
'database' => $config['database_name'],
'log' => $config['database_log'],
'socket' => @$config['database_socket'],
'persistent' => @$config['database_persistent']
)
);
$db = POT::getInstance()->getDBHandle();
}
catch(PDOException $error) {
if(isset($cache) && $cache->enabled()) {
$cache->delete('config_lua');
}
if(defined('MYAAC_INSTALL')) {
return; // installer will take care of this
}
throw new RuntimeException('ERROR: Cannot connect to MySQL database.<br/>' .
'Possible reasons:' .
'<ul>' .
'<li>MySQL is not configured propertly in <i>config.lua</i>.</li>' .
'<li>MySQL server is not running.</li>' .
'</ul>' . $error->getMessage());
}

View File

@@ -39,7 +39,7 @@ function exception_handler($exception) {
// we just replace some values manually
// cause in case Twig throws exception, we can show it too
$content = file_get_contents($template_file);
$content = str_replace(array('{{ BASE_URL }}', '{{ message }}', '{{ backtrace }}', '{{ powered_by }}'), array(BASE_URL, $message, $backtrace_formatted, base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4=')), $content);
$content = str_replace(array('{{ BASE_URL }}', '{{ exceptionClass }}', '{{ message }}', '{{ backtrace }}', '{{ powered_by }}'), array(BASE_URL, get_class($exception), $message, $backtrace_formatted, base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4=')), $content);
echo $content;
}

View File

@@ -8,17 +8,29 @@
* @link https://my-aac.org
*/
use PHPMailer\PHPMailer\PHPMailer;
use Twig\Loader\ArrayLoader as Twig_ArrayLoader;
defined('MYAAC') or die('Direct access not allowed!');
function message($message, $type, $return)
{
if($return)
return '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>';
if(IS_CLI) {
if($return) {
return $message;
}
echo '<div class="' . $type . '" style="margin-bottom:10px;">' . $message . '</div>';
return true;
echo $message;
return true;
}
if($return) {
// for install and admin pages use bootstrap classes
return '<div class="' . ((defined('MYAAC_INSTALL') || defined('MYAAC_ADMIN')) ? 'alert alert-' : '') . $type . '" style="margin-bottom:10px;">' . $message . '</div>';
}
echo '<div class="' . ((defined('MYAAC_INSTALL') || defined('MYAAC_ADMIN')) ? 'alert alert-' : '') . $type . '" style="margin-bottom:10px;">' . $message . '</div>';
return true;
}
function success($message, $return = false) {
return message($message, 'success', $return);
@@ -30,28 +42,9 @@ function note($message, $return = false) {
return message($message, 'note', $return);
}
function error($message, $return = false) {
return message($message, 'error', $return);
return message($message, ((defined('MYAAC_INSTALL') || defined('MYAAC_ADMIN')) ? 'danger' : 'error'), $return);
}
function message1($head, $message, $type, $icon , $return)
{//return '<div class="' . $type . '">' . $message . '</div>';
if($return)
return '<div class="alert alert-'.$type.' alert-dismissible"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button><h4><i class="icon fa fa-'.$icon.'"></i> '.$head.':</h4>'.$message.'</div>';
echo '<div class="alert alert-'.$type.' alert-dismissible"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button><h4><i class="icon fa fa-'.$icon.'"></i> '.$head.':</h4>'.$message.'</div>';
return true;
}
function success1($message, $return = false) {
return message('Info', $message, 'success','success', $return);
}
function warning1($message, $return = false) {
return message('Warning',$message, 'warning','ban', $return);
}
function note1($message, $return = false) {
return message('Info',$message, 'info','info', $return);
}
function error1($message, $return = false) {
return message("Alert", $message, 'danger','check', $return);
}
function longToIp($ip)
{
$exp = explode(".", long2ip($ip));
@@ -103,6 +96,16 @@ function getPlayerLink($name, $generate = true)
return generateLink($url, $name);
}
function getMonsterLink($name, $generate = true)
{
global $config;
$url = BASE_URL . ($config['friendly_urls'] ? '' : '?') . 'creatures/' . urlencode($name);
if(!$generate) return $url;
return generateLink($url, $name);
}
function getHouseLink($name, $generate = true)
{
global $db, $config;
@@ -159,7 +162,24 @@ function getItemImage($id, $count = 1)
$file_name .= '-' . $count;
global $config;
return '<img src="' . $config['item_images_url'] . $file_name . '.gif"' . $tooltip . ' width="32" height="32" border="0" alt="' .$id . '" />';
return '<img src="' . $config['item_images_url'] . $file_name . config('item_images_extension') . '"' . $tooltip . ' width="32" height="32" border="0" alt="' .$id . '" />';
}
function getItemRarity($chance) {
if ($chance >= 21) {
return "common";
} elseif (between($chance, 8, 21)) {
return "uncommon";
} elseif (between($chance, 1.1, 8)) {
return "semi rare";
} elseif (between($chance, 0.4, 1.1)) {
return "rare";
} elseif (between($chance, 0.8, 0.4)) {
return "very rare";
} elseif ($chance <= 0.8) {
return "extremely rare";
}
return '';
}
function getFlagImage($country)
@@ -442,7 +462,7 @@ function tickers()
*/
function template_place_holder($type)
{
global $template_place_holders;
global $twig, $template_place_holders;
$ret = '';
if(array_key_exists($type, $template_place_holders) && is_array($template_place_holders[$type]))
@@ -451,6 +471,9 @@ function template_place_holder($type)
if($type === 'head_start') {
$ret .= template_header();
}
elseif ($type === 'body_start') {
$ret .= $twig->render('browsehappy.html.twig');
}
elseif($type === 'body_end') {
$ret .= template_ga_code();
}
@@ -463,33 +486,16 @@ function template_place_holder($type)
*/
function template_header($is_admin = false)
{
global $title_full, $config;
global $title_full, $config, $twig;
$charset = isset($config['charset']) ? $config['charset'] : 'utf-8';
$ret = '
<meta charset="' . $charset . '">
<meta http-equiv="content-language" content="' . $config['language'] . '" />
<meta http-equiv="content-type" content="text/html; charset=' . $charset . '" />';
if(!$is_admin)
$ret .= '
<base href="' . BASE_URL . '" />
<title>' . $title_full . '</title>';
$ret .= '
<meta name="description" content="' . $config['meta_description'] . '" />
<meta name="keywords" content="' . $config['meta_keywords'] . ', myaac, wodzaac" />
<meta name="generator" content="MyAAC" />
<link rel="stylesheet" type="text/css" href="' . BASE_URL . 'tools/css/messages.css" />
<script type="text/javascript" src="' . BASE_URL . 'tools/js/jquery.min.js"></script>
<noscript>
<div class="warning" style="text-align: center; font-size: 14px;">Your browser does not support JavaScript or its disabled!<br/>
Please turn it on, or be aware that some features on this website will not work correctly.</div>
</noscript>
';
if($config['recaptcha_enabled'])
$ret .= "<script src='https://www.google.com/recaptcha/api.js'></script>";
return $ret;
return $twig->render('templates.header.html.twig',
[
'charset' => $charset,
'title' => $title_full,
'is_admin' => $is_admin
]
);
}
/**
@@ -556,10 +562,8 @@ function template_form()
foreach($templates as $key => $value)
$options .= '<option ' . ($template_name == $value ? 'SELECTED' : '') . '>' . $value . '</option>';
return '<form method="get" action="' . BASE_URL . '">
<hidden name="subtopic" value="' . PAGE . '"/>
<select name="template" onchange="this.form.submit()">' . $options . '</select>
</form>';
global $twig;
return $twig->render('forms.change_template.html.twig', ['options' => $options]);
}
function getStyle($i)
@@ -818,13 +822,16 @@ function getWorldName($id)
*/
function _mail($to, $subject, $body, $altBody = '', $add_html_tags = true)
{
/** @var PHPMailer $mailer */
global $mailer, $config;
if (!config('mail_enabled')) {
log_append('mailer-error.log', '_mail() function has been used, but config.mail_enabled is disabled.');
}
if(!$mailer)
{
require SYSTEM . 'libs/phpmailer/PHPMailerAutoload.php';
$mailer = new PHPMailer();
$mailer->setLanguage('en', LIBS . 'phpmailer/language/');
//$mailer->setLanguage('en', LIBS . 'phpmailer/language/');
}
else {
$mailer->clearAllRecipients();
@@ -922,6 +929,12 @@ function load_config_lua($filename)
if(count($lines) > 0) {
foreach($lines as $ln => $line)
{
$line = trim($line);
if(@$line[0] === '{' || @$line[0] === '}') {
// arrays are not supported yet
// just ignore the error
continue;
}
$tmp_exp = explode('=', $line, 2);
if(strpos($line, 'dofile') !== false)
{
@@ -948,16 +961,17 @@ function load_config_lua($filename)
$result[$key] = (string) substr(substr($value, 1), 0, -1);
elseif(in_array($value, array('true', 'false')))
$result[$key] = ($value === 'true') ? true : false;
elseif(@$value[0] === '{' && @$value[strlen($value) - 1] === '}') {
elseif(@$value[0] === '{') {
// arrays are not supported yet
// just ignore the error
continue;
}
else
{
foreach($result as $tmp_key => $tmp_value) // load values definied by other keys, like: dailyFragsToBlackSkull = dailyFragsToRedSkull
$value = str_replace($tmp_key, $tmp_value, $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>]');
}
@@ -982,6 +996,10 @@ function str_replace_first($search, $replace, $subject) {
}
function get_browser_real_ip() {
if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
}
if(isset($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['REMOTE_ADDR']))
return $_SERVER['REMOTE_ADDR'];
else if(isset($_SERVER['HTTP_CLIENT_IP']) && !empty($_SERVER['HTTP_CLIENT_IP']))
@@ -1019,7 +1037,7 @@ function getTopPlayers($limit = 5) {
$deleted = 'deletion';
$is_tfs10 = $db->hasTable('players_online');
$players = $db->query('SELECT `id`, `name`, `level`, `experience`, `looktype`' . ($db->hasColumn('players', 'lookaddons') ? ', `lookaddons`' : '') . ', `lookhead`, `lookbody`, `looklegs`, `lookfeet`' . ($is_tfs10 ? '' : ', `online`') . ' FROM `players` WHERE `group_id` < ' . config('highscores_groups_hidden') . ' AND `id` NOT IN (' . implode(', ', config('highscores_ids_hidden')) . ') AND `' . $deleted . '` = 0 AND `account_id` != 1 ORDER BY `experience` DESC LIMIT ' . (int)$limit)->fetchAll();
$players = $db->query('SELECT `id`, `name`, `level`, `vocation`, `experience`, `looktype`' . ($db->hasColumn('players', 'lookaddons') ? ', `lookaddons`' : '') . ', `lookhead`, `lookbody`, `looklegs`, `lookfeet`' . ($is_tfs10 ? '' : ', `online`') . ' FROM `players` WHERE `group_id` < ' . config('highscores_groups_hidden') . ' AND `id` NOT IN (' . implode(', ', config('highscores_ids_hidden')) . ') AND `' . $deleted . '` = 0 AND `account_id` != 1 ORDER BY `experience` DESC LIMIT ' . (int)$limit)->fetchAll();
if($is_tfs10) {
foreach($players as &$player) {
@@ -1221,6 +1239,254 @@ function getCustomPage($page, &$success)
return $content;
}
function getBanReason($reasonId)
{
switch($reasonId)
{
case 0:
return "Offensive Name";
case 1:
return "Invalid Name Format";
case 2:
return "Unsuitable Name";
case 3:
return "Name Inciting Rule Violation";
case 4:
return "Offensive Statement";
case 5:
return "Spamming";
case 6:
return "Illegal Advertising";
case 7:
return "Off-Topic Public Statement";
case 8:
return "Non-English Public Statement";
case 9:
return "Inciting Rule Violation";
case 10:
return "Bug Abuse";
case 11:
return "Game Weakness Abuse";
case 12:
return "Using Unofficial Software to Play";
case 13:
return "Hacking";
case 14:
return "Multi-Clienting";
case 15:
return "Account Trading or Sharing";
case 16:
return "Threatening Gamemaster";
case 17:
return "Pretending to Have Influence on Rule Enforcement";
case 18:
return "False Report to Gamemaster";
case 19:
return "Destructive Behaviour";
case 20:
return "Excessive Unjustified Player Killing";
case 21:
return "Invalid Payment";
case 22:
return "Spoiling Auction";
}
return "Unknown Reason";
}
function getBanType($typeId)
{
switch($typeId)
{
case 1:
return "IP Banishment";
case 2:
return "Namelock";
case 3:
return "Banishment";
case 4:
return "Notation";
case 5:
return "Deletion";
}
return "Unknown Type";
}
function getChangelogType($v)
{
switch($v) {
case 1:
return 'added';
case 2:
return 'removed';
case 3:
return 'changed';
case 4:
return 'fixed';
}
return 'unknown';
}
function getChangelogWhere($v)
{
switch($v) {
case 1:
return 'server';
case 2:
return 'website';
}
return 'unknown';
}
function getPlayerNameByAccount($id)
{
global $vowels, $ots, $db;
if(is_numeric($id))
{
$player = new OTS_Player();
$player->load($id);
if($player->isLoaded())
return $player->getName();
else
{
$playerQuery = $db->query('SELECT `id` FROM `players` WHERE `account_id` = ' . $id . ' ORDER BY `lastlogin` DESC LIMIT 1;')->fetch();
$tmp = "*Error*";
/*
$acco = new OTS_Account();
$acco->load($id);
if(!$acco->isLoaded())
return "Unknown name";
foreach($acco->getPlayersList() as $p)
{
$player= new OTS_Player();
$player->find($p);*/
$player->load($playerQuery['id']);
//echo 'id gracza = ' . $p . '<br/>';
if($player->isLoaded())
$tmp = $player->getName();
// break;
//}
return $tmp;
}
}
return '';
}
function echo_success($message)
{
echo '<div class="col-12 success mb-2">' . $message . '</div>';
}
function echo_error($message)
{
global $error;
echo '<div class="col-12 error mb-2">' . $message . '</div>';
$error = true;
}
function verify_number($number, $name, $max_length)
{
if (!Validator::number($number))
echo_error($name . ' can contain only numbers.');
$number_length = strlen($number);
if ($number_length <= 0 || $number_length > $max_length)
echo_error($name . ' cannot be longer than ' . $max_length . ' digits.');
}
function Outfits_loadfromXML()
{
global $config;
$file_path = $config['data_path'] . 'XML/outfits.xml';
if (!file_exists($file_path)) { return null; }
$xml = new DOMDocument;
$xml->load($file_path);
$outfits = null;
foreach ($xml->getElementsByTagName('outfit') as $outfit) {
$outfits[] = Outfit_parseNode($outfit);
}
return $outfits;
}
function Outfit_parseNode($node) {
$looktype = (int)$node->getAttribute('looktype');
$type = (int)$node->getAttribute('type');
$lookname = $node->getAttribute('name');
$premium = $node->getAttribute('premium');
$unlocked = $node->getAttribute('unlocked');
$enabled = $node->getAttribute('enabled');
return array('id' => $looktype, 'type' => $type, 'name' => $lookname, 'premium' => $premium, 'unlocked' => $unlocked, 'enabled' => $enabled);
}
function left($str, $length) {
return substr($str, 0, $length);
}
function right($str, $length) {
return substr($str, -$length);
}
function getCreatureImgPath($creature){
$creature_path = config('creatures_images_url');
$creature_gfx_name = trim(strtolower($creature)) . config('creatures_images_extension');
if (!file_exists($creature_path . $creature_gfx_name)) {
$creature_gfx_name = str_replace(" ", "", $creature_gfx_name);
if (file_exists($creature_path . $creature_gfx_name)) {
return $creature_path . $creature_gfx_name;
} else {
return $creature_path . 'nophoto.png';
}
} else {
return $creature_path . $creature_gfx_name;
}
}
function between($x, $lim1, $lim2) {
if ($lim1 < $lim2) {
$lower = $lim1; $upper = $lim2;
}
else {
$lower = $lim2; $upper = $lim1;
}
return (($x >= $lower) && ($x <= $upper));
}
function truncate($string, $length)
{
if (strlen($string) > $length) {
$string = substr($string, 0, $length) . '...';
}
return $string;
}
function getAccountLoginByLabel()
{
$ret = '';
if (config('account_login_by_email')) {
$ret = 'Email Address';
if (config('account_login_by_email_fallback')) {
$ret .= ' or ';
}
}
if (!config('account_login_by_email') || config('account_login_by_email_fallback')) {
$ret .= 'Account ' . (USE_ACCOUNT_NAME ? 'Name' : 'Number');
}
return $ret;
}
// validator functions
require_once LIBS . 'validator.php';
require_once SYSTEM . 'compat.php';
require_once SYSTEM . 'compat/base.php';
// custom functions
require SYSTEM . 'functions_custom.php';

View File

@@ -0,0 +1,11 @@
<?php
/**
* Custom functions
*
* @package MyAAC
* @author Slawkens <slawkens@gmail.com>, Lee
* @copyright 2020 MyAAC
* @link https://my-aac.org
*/
// Insert your custom functions here.

View File

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

View File

@@ -9,40 +9,46 @@
*/
defined('MYAAC') or die('Direct access not allowed!');
define('HOOK_STARTUP', 1);
define('HOOK_BEFORE_PAGE', 2);
define('HOOK_AFTER_PAGE', 3);
define('HOOK_FINISH', 4);
define('HOOK_TIBIACOM_ARTICLE', 5);
define('HOOK_TIBIACOM_BORDER_3', 6);
define('HOOK_CHARACTERS_BEFORE_INFORMATIONS', 7);
define('HOOK_CHARACTERS_AFTER_INFORMATIONS', 8);
define('HOOK_CHARACTERS_BEFORE_SIGNATURE', 9);
define('HOOK_CHARACTERS_AFTER_SIGNATURE', 10);
define('HOOK_CHARACTERS_AFTER_ACCOUNT', 11);
define('HOOK_CHARACTERS_AFTER_CHARACTERS', 12);
define('HOOK_LOGIN', 13);
define('HOOK_LOGIN_ATTEMPT', 14);
define('HOOK_LOGOUT', 15);
define('HOOK_ACCOUNT_CREATE_BEFORE_FORM', 16);
define('HOOK_ACCOUNT_CREATE_BEFORE_BOXES', 17);
define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_1', 18);
define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_2', 19);
define('HOOK_ACCOUNT_CREATE_AFTER_BOXES', 20);
define('HOOK_ACCOUNT_CREATE_BEFORE_ACCOUNT', 21);
define('HOOK_ACCOUNT_CREATE_AFTER_ACCOUNT', 22);
define('HOOK_ACCOUNT_CREATE_AFTER_EMAIL', 23);
define('HOOK_ACCOUNT_CREATE_AFTER_COUNTRY', 24);
define('HOOK_ACCOUNT_CREATE_AFTER_PASSWORDS', 25);
define('HOOK_ACCOUNT_CREATE_AFTER_RECAPTCHA', 26);
define('HOOK_ACCOUNT_CREATE_BEFORE_CHARACTER_NAME', 27);
define('HOOK_ACCOUNT_CREATE_AFTER_CHARACTER_NAME', 28);
define('HOOK_ACCOUNT_CREATE_AFTER_SEX', 29);
define('HOOK_ACCOUNT_CREATE_AFTER_VOCATION', 30);
define('HOOK_ACCOUNT_CREATE_AFTER_TOWNS', 31);
define('HOOK_ACCOUNT_CREATE_BEFORE_SUBMIT_BUTTON', 32);
define('HOOK_ACCOUNT_CREATE_AFTER_FORM', 33);
define('HOOK_ACCOUNT_CREATE_AFTER_SUBMIT', 34);
$i = 0;
define('HOOK_STARTUP', ++$i);
define('HOOK_BEFORE_PAGE', ++$i);
define('HOOK_AFTER_PAGE', ++$i);
define('HOOK_FINISH', ++$i);
define('HOOK_TIBIACOM_ARTICLE', ++$i);
define('HOOK_TIBIACOM_BORDER_3', ++$i);
define('HOOK_CHARACTERS_BEFORE_INFORMATIONS', ++$i);
define('HOOK_CHARACTERS_AFTER_INFORMATIONS', ++$i);
define('HOOK_CHARACTERS_BEFORE_SKILLS', ++$i);
define('HOOK_CHARACTERS_AFTER_SKILLS', ++$i);
define('HOOK_CHARACTERS_AFTER_QUESTS', ++$i);
define('HOOK_CHARACTERS_AFTER_EQUIPMENT', ++$i);
define('HOOK_CHARACTERS_BEFORE_DEATHS', ++$i);
define('HOOK_CHARACTERS_BEFORE_SIGNATURE', ++$i);
define('HOOK_CHARACTERS_AFTER_SIGNATURE', ++$i);
define('HOOK_CHARACTERS_AFTER_ACCOUNT', ++$i);
define('HOOK_CHARACTERS_AFTER_CHARACTERS', ++$i);
define('HOOK_LOGIN', ++$i);
define('HOOK_LOGIN_ATTEMPT', ++$i);
define('HOOK_LOGOUT', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_FORM', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_BOXES', ++$i);
define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_1', ++$i);
define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_2', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_BOXES', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_ACCOUNT', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_ACCOUNT', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_EMAIL', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_COUNTRY', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_PASSWORDS', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_RECAPTCHA', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_CHARACTER_NAME', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_CHARACTER_NAME', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_SEX', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_VOCATION', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_TOWNS', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_SUBMIT_BUTTON', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_FORM', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_SUBMIT', ++$i);
define('HOOK_FIRST', HOOK_STARTUP);
define('HOOK_LAST', HOOK_ACCOUNT_CREATE_AFTER_SUBMIT);

View File

@@ -119,12 +119,16 @@ if(!isset($config['highscores_ids_hidden']) || count($config['highscores_ids_hid
$config['highscores_ids_hidden'] = array(0);
}
$config['account_create_character_create'] = config('account_create_character_create') && (!config('mail_enabled') || !config('account_mail_verify'));
// POT
require_once SYSTEM . 'libs/pot/OTS.php';
$ots = POT::getInstance();
require_once SYSTEM . 'database.php';
define('USE_ACCOUNT_NAME', $db->hasColumn('accounts', 'name'));
define('USE_ACCOUNT_NUMBER', $db->hasColumn('accounts', 'number'));
// load vocation names
$tmp = '';
if($cache->enabled() && $cache->fetch('vocations', $tmp)) {
@@ -140,10 +144,8 @@ else {
if(!@file_exists($file))
$file = $config['data_path'] . 'vocations.xml';
$vocations->load($file);
if(!$vocations)
throw new RuntimeException('ERROR: Cannot load <i>vocations.xml</i> file.');
if(!$vocations->load($file))
throw new RuntimeException('ERROR: Cannot load <i>vocations.xml</i> - the file is malformed. Check the file with xml syntax validator.');
$config['vocations'] = array();
foreach($vocations->getElementsByTagName('vocation') as $vocation) {
@@ -157,59 +159,5 @@ else {
}
unset($tmp, $id, $vocation);
// load towns
/* TODO: doesnt work
ini_set('memory_limit', '-1');
$tmp = '';
if($cache->enabled() && $cache->fetch('towns', $tmp)) {
$config['towns'] = unserialize($tmp);
}
else {
$towns = new OTS_OTBMFile();
$towns->loadFile('D:/Projekty/opentibia/wodzislawski/data/world/wodzislawski.otbm');
$config['towns'] = $towns->getTownsList();
if($cache->enabled()) {
$cache->set('towns', serialize($config['towns']), 120);
}
}
*/
////////////////////////////////////////
// load towns from database (TFS 1.3) //
////////////////////////////////////////
$towns = array();
if($cache->enabled() && $cache->fetch('towns', $tmp)) {
$towns = unserialize($tmp);
}
else {
if($db->hasTable('towns')) {
$query = $db->query('SELECT `id`, `name` FROM `towns`;')->fetchAll(PDO::FETCH_ASSOC);
foreach($query as $town) {
$towns[$town['id']] = $town['name'];
}
unset($query);
if($cache->enabled()) {
$cache->set('towns', serialize($towns), 600);
}
}
else if($cache->enabled()) {
$cache->set('towns', serialize(array()), 600);
}
}
$configTowns = config('towns');
if($configTowns !== null && (!isset($configTowns[1]) || $configTowns[1] !== 'Sample town')) {
$towns = array_replace(
$towns, $configTowns
);
}
config(['towns', $towns]);
//////////////////////////////////////////////
// END - load towns from database (TFS 1.3) //
//////////////////////////////////////////////
require LIBS . 'Towns.php';
Towns::load();

View File

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

113
system/libs/DataLoader.php Normal file
View File

@@ -0,0 +1,113 @@
<?php
/**
* Project: MyAAC
* Automatic Account Creator for Open Tibia Servers
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* @package MyAAC
* @author Slawkens <slawkens@gmail.com>
* @copyright 2020 MyAAC
* @link https://my-aac.org
*/
/**
* Class DataLoader
*/
class DataLoader
{
private static $locale;
private static $startTime;
/**
* Load data from server
*/
public static function load()
{
self::$startTime = microtime(true);
require LIBS . 'items.php';
if(Items::loadFromXML()) {
success(self::$locale['step_database_loaded_items'] . self::getLoadedTime());
}
else {
error(Items::getError());
}
self::$startTime = microtime(true);
require LIBS . 'creatures.php';
if(Creatures::loadFromXML()) {
success(self::$locale['step_database_loaded_monsters'] . self::getLoadedTime());
if(Creatures::getMonstersList()->hasErrors()) {
self::$locale['step_database_error_monsters'] = str_replace('$LOG$', 'system/logs/error.log', self::$locale['step_database_error_monsters']);
warning(self::$locale['step_database_error_monsters']);
}
}
else {
error(Creatures::getLastError());
}
self::$startTime = microtime(true);
require_once LIBS . 'npc.php';
if(NPCs::loadFromXML()) {
success(self::$locale['step_database_loaded_npcs'] . self::getLoadedTime());
}
else {
error(self::$locale['step_database_error_npcs']);
}
self::$startTime = microtime(true);
require LIBS . 'spells.php';
if(Spells::loadFromXML()) {
success(self::$locale['step_database_loaded_spells'] . self::getLoadedTime());
}
else {
error(Spells::getLastError());
}
self::$startTime = microtime(true);
if (Towns::save()) {
success(self::$locale['step_database_loaded_towns'] . self::getLoadedTime());
}
else {
warning(self::$locale['step_database_error_towns']);
}
self::$startTime = microtime(true);
require LIBS . 'weapons.php';
if(Weapons::loadFromXML()) {
success(self::$locale['step_database_loaded_weapons'] . self::getLoadedTime());
}
else {
error(Weapons::getError());
}
}
public static function setLocale($locale) {
self::$locale = $locale;
}
private static function getLoadedTime()
{
$endTime = round(microtime(true) - self::$startTime, 3);
return ' (' . str_replace('$TIME$', $endTime, self::$locale['loaded_in_ms']) . ')';
}
}

View File

@@ -0,0 +1,84 @@
<?php
class GoogleReCAPTCHA
{
private static $errorMessage = '';
private static $errorType;
const ERROR_MISSING_RESPONSE = 1;
const ERROR_INVALID_ACTION = 2;
const ERROR_LOW_SCORE = 3;
const ERROR_NO_SUCCESS = 4;
public static function verify($action = '')
{
if (!isset($_POST['g-recaptcha-response']) || empty($_POST['g-recaptcha-response'])) {
self::$errorType = self::ERROR_MISSING_RESPONSE;
self::$errorMessage = "Please confirm that you're not a robot.";
return false;
}
$recaptchaApiUrl = 'https://www.google.com/recaptcha/api/siteverify';
$secretKey = config('recaptcha_secret_key');
$recaptchaResponse = $_POST['g-recaptcha-response'];
$ip = $_SERVER['REMOTE_ADDR'];
$params = 'secret='.$secretKey.'&response='.$recaptchaResponse.'&remoteip='.$ip;
if (function_exists('curl_version')) {
$curl_connection = curl_init($recaptchaApiUrl);
curl_setopt($curl_connection, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($curl_connection, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl_connection, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl_connection, CURLOPT_FOLLOWLOCATION, 0);
curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $params);
$response = curl_exec($curl_connection);
curl_close($curl_connection);
} else {
$response = file_get_contents($recaptchaApiUrl . '?' . $params);
}
$json = json_decode($response);
$recaptchaType = config('recaptcha_type');
if ($recaptchaType === 'v3') { // score based
//log_append('recaptcha.log', 'recaptcha_score: ' . $json->score . ', action:' . $json->action);
if (!isset($json->action) || $json->action !== $action) {
self::$errorType = self::ERROR_INVALID_ACTION;
self::$errorMessage = 'Google ReCaptcha returned invalid action.';
return false;
}
if (!isset($json->score) || $json->score < config('recaptcha_v3_min_score')) {
self::$errorType = self::ERROR_LOW_SCORE;
self::$errorMessage = 'Your Google ReCaptcha score was too low.';
return false;
}
}
if (!isset($json->success) || !$json->success) {
self::$errorType = self::ERROR_NO_SUCCESS;
self::$errorMessage = "Please confirm that you're not a robot.";
return false;
}
return true;
}
/**
* @return string
*/
public static function getErrorMessage() {
return self::$errorMessage;
}
/**
* @return int
*/
public static function getErrorType() {
return self::$errorType;
}
}

File diff suppressed because it is too large Load Diff

138
system/libs/Towns.php Normal file
View File

@@ -0,0 +1,138 @@
<?php
/**
* Project: MyAAC
* Automatic Account Creator for Open Tibia Servers
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* @package MyAAC
* @author Slawkens <slawkens@gmail.com>
* @copyright 2020 MyAAC
* @link https://my-aac.org
*/
/**
* Class Towns
*/
class Towns
{
/**
* @var string
*/
private static $filename = CACHE . 'towns.php';
/**
* Determine towns
*
* @return array
*/
public static function determine()
{
global $db;
if($db->hasTable('towns')) {
return self::getFromDatabase();
}
return self::getFromOTBM();
}
/**
* Load cached towns file
*/
public static function load()
{
$towns = config('towns');
if (file_exists(self::$filename)) {
$towns = require self::$filename;
}
config(['towns', $towns]);
}
/**
* Save into cache file
*
* @return bool
*/
public static function save()
{
$towns = self::determine();
if (count($towns) > 0) {
file_put_contents(self::$filename, '<?php return ' . var_export($towns, true) . ';', LOCK_EX);
return true;
}
return false;
}
/**
* Load from OTBM map file
*
* @return array
*/
public static function getFromOTBM()
{
$mapName = configLua('mapName');
if (!isset($mapName)) {
$mapName = configLua('map');
$mapFile = config('server_path') . $mapName;
}
if (strpos($mapName, '.otbm') === false) {
$mapName .= '.otbm';
}
if (!isset($mapFile)) {
$mapFile = config('data_path') . 'world/' . $mapName;
}
if (strpos($mapFile, '.gz') !== false) {
$mapFile = str_replace('.gz', '', $mapFile);
}
$towns = [];
if (file_exists($mapFile)) {
ini_set('memory_limit', '-1');
require LIBS . 'TownsReader.php';
$townsReader = new TownsReader($mapFile);
$townsReader->load();
$towns = $townsReader->get();
}
return $towns;
}
/**
* Load from database
*
* @return array
*/
public static function getFromDatabase()
{
global $db;
$query = $db->query('SELECT `id`, `name` FROM `towns`;')->fetchAll(PDO::FETCH_ASSOC);
$towns = [];
foreach($query as $town) {
$towns[$town['id']] = $town['name'];
}
return $towns;
}
}

View File

@@ -0,0 +1,82 @@
<?php
/*
This file is part of OTSCMS (http://www.otscms.com/) project.
Copyright (C) 2005 - 2007 Wrzasq (wrzasq@gmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
This code bases on oryginal OTServ code for .otbm files - file iomapotbm.cpp rev.2141
*/
class TownsReader
{
// node bytes
const ESCAPE_CHAR = 0xFD;
const NODE_START = 0xFE;
// map node types
const OTBM_TOWN = 13;
// file handler
protected $file;
// towns
private $towns = [];
// loads map .otbm file
public function __construct($file)
{
// opens file for reading
$this->file = fopen($file, 'rb');
}
public function load()
{
// checks if file is opened correctly
if ($this->file) {
// skips version
fseek($this->file, 4);
// reads nodes chain
while (!feof($this->file)) {
// reads byte
switch (ord(fgetc($this->file))) {
// maybe a town node
case self::NODE_START:
// reads node type
if (ord(fgetc($this->file)) == self::OTBM_TOWN) {
$id = unpack('L', fread($this->file, 4));
$length = unpack('S', fread($this->file, 2));
// reads town name
$this->towns[$id[1]] = fread($this->file, $length[1]);
}
break;
// escape next character - it might be NODE_START character which is in fact not
case self::ESCAPE_CHAR:
fgetc($this->file);
break;
}
}
}
}
public function get() {
return $this->towns;
}
}

View File

@@ -1,55 +0,0 @@
<?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.
*/
/**
* Autoloads Twig classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.21 and will be removed in 2.0. Use Composer instead. 2.0.
*/
class Twig_Autoloader
{
/**
* Registers Twig_Autoloader as an SPL autoloader.
*
* @param bool $prepend whether to prepend the autoloader or not
*/
public static function register($prepend = false)
{
if (PHP_VERSION_ID < 50300) {
spl_autoload_register(array(__CLASS__, 'autoload'));
} else {
spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
}
}
/**
* Handles autoloading of classes.
*
* @param string $class a class name
*/
public static function autoload($class)
{
if (0 !== strpos($class, 'Twig')) {// || !isset($class[0])) {
return;
}
$file = __DIR__.'/../'.str_replace(array('_', "\0"), array('/', ''), $class).'.php';
$dev_mode = (config('env') === 'dev');
if($dev_mode && !is_file($file)) {
return;
}
require $file;
}
}

View File

@@ -1,60 +0,0 @@
<?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\Cache;
/**
* Interface implemented by cache classes.
*
* It is highly recommended to always store templates on the filesystem to
* benefit from the PHP opcode cache. This interface is mostly useful if you
* need to implement a custom strategy for storing templates on the filesystem.
*
* @author Andrew Tch <andrew@noop.lv>
*/
interface CacheInterface
{
/**
* Generates a cache key for the given template class name.
*
* @param string $name The template name
* @param string $className The template class name
*
* @return string
*/
public function generateKey($name, $className);
/**
* Writes the compiled template to cache.
*
* @param string $key The cache key
* @param string $content The template representation as a PHP class
*/
public function write($key, $content);
/**
* Loads a template from the cache.
*
* @param string $key The cache key
*/
public function load($key);
/**
* Returns the modification timestamp of a key.
*
* @param string $key The cache key
*
* @return int
*/
public function getTimestamp($key);
}
class_alias('Twig\Cache\CacheInterface', 'Twig_CacheInterface');

View File

@@ -1,93 +0,0 @@
<?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\Cache;
/**
* Implements a cache on the filesystem.
*
* @author Andrew Tch <andrew@noop.lv>
*/
class FilesystemCache implements CacheInterface
{
const FORCE_BYTECODE_INVALIDATION = 1;
private $directory;
private $options;
/**
* @param string $directory The root cache directory
* @param int $options A set of options
*/
public function __construct($directory, $options = 0)
{
$this->directory = rtrim($directory, '\/').'/';
$this->options = $options;
}
public function generateKey($name, $className)
{
$hash = hash('sha256', $className);
return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
}
public function load($key)
{
if (file_exists($key)) {
@include_once $key;
}
}
public function write($key, $content)
{
$dir = \dirname($key);
if (!is_dir($dir)) {
if (false === @mkdir($dir, 0777, true)) {
clearstatcache(true, $dir);
if (!is_dir($dir)) {
throw new \RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir));
}
}
} elseif (!is_writable($dir)) {
throw new \RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir));
}
$tmpFile = tempnam($dir, basename($key));
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $key)) {
@chmod($key, 0666 & ~umask());
if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) {
// Compile cached file into bytecode cache
if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) {
@opcache_invalidate($key, true);
} elseif (\function_exists('apc_compile_file')) {
apc_compile_file($key);
}
}
return;
}
throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $key));
}
public function getTimestamp($key)
{
if (!file_exists($key)) {
return 0;
}
return (int) @filemtime($key);
}
}
class_alias('Twig\Cache\FilesystemCache', 'Twig_Cache_Filesystem');

View File

@@ -1,42 +0,0 @@
<?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\Cache;
/**
* Implements a no-cache strategy.
*
* @final
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class NullCache implements CacheInterface
{
public function generateKey($name, $className)
{
return '';
}
public function write($key, $content)
{
}
public function load($key)
{
}
public function getTimestamp($key)
{
return 0;
}
}
class_alias('Twig\Cache\NullCache', 'Twig_Cache_Null');

View File

@@ -1,288 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
* (c) Armin Ronacher
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig;
use Twig\Node\ModuleNode;
/**
* Compiles a node to PHP code.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Compiler implements \Twig_CompilerInterface
{
protected $lastLine;
protected $source;
protected $indentation;
protected $env;
protected $debugInfo = [];
protected $sourceOffset;
protected $sourceLine;
protected $filename;
private $varNameSalt = 0;
public function __construct(Environment $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.
*
* @return Environment
*/
public function getEnvironment()
{
return $this->env;
}
/**
* Gets the current PHP code after compilation.
*
* @return string The PHP code
*/
public function getSource()
{
return $this->source;
}
/**
* Compiles a node.
*
* @param int $indentation The current indentation
*
* @return $this
*/
public function compile(\Twig_NodeInterface $node, $indentation = 0)
{
$this->lastLine = null;
$this->source = '';
$this->debugInfo = [];
$this->sourceOffset = 0;
// source code starts at 1 (as we then increment it when we encounter new lines)
$this->sourceLine = 1;
$this->indentation = $indentation;
$this->varNameSalt = 0;
if ($node instanceof ModuleNode) {
// to be removed in 2.0
$this->filename = $node->getTemplateName();
}
$node->compile($this);
return $this;
}
public function subcompile(\Twig_NodeInterface $node, $raw = true)
{
if (false === $raw) {
$this->source .= str_repeat(' ', $this->indentation * 4);
}
$node->compile($this);
return $this;
}
/**
* Adds a raw string to the compiled code.
*
* @param string $string The string
*
* @return $this
*/
public function raw($string)
{
$this->source .= $string;
return $this;
}
/**
* Writes a string to the compiled code by adding indentation.
*
* @return $this
*/
public function write()
{
$strings = \func_get_args();
foreach ($strings as $string) {
$this->source .= str_repeat(' ', $this->indentation * 4).$string;
}
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.
*
* @param string $value The string
*
* @return $this
*/
public function string($value)
{
$this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\"));
return $this;
}
/**
* Returns a PHP representation of a given value.
*
* @param mixed $value The value to convert
*
* @return $this
*/
public function repr($value)
{
if (\is_int($value) || \is_float($value)) {
if (false !== $locale = setlocale(LC_NUMERIC, '0')) {
setlocale(LC_NUMERIC, 'C');
}
$this->raw(var_export($value, true));
if (false !== $locale) {
setlocale(LC_NUMERIC, $locale);
}
} elseif (null === $value) {
$this->raw('null');
} elseif (\is_bool($value)) {
$this->raw($value ? 'true' : 'false');
} elseif (\is_array($value)) {
$this->raw('[');
$first = true;
foreach ($value as $key => $v) {
if (!$first) {
$this->raw(', ');
}
$first = false;
$this->repr($key);
$this->raw(' => ');
$this->repr($v);
}
$this->raw(']');
} else {
$this->string($value);
}
return $this;
}
/**
* Adds debugging information.
*
* @return $this
*/
public function addDebugInfo(\Twig_NodeInterface $node)
{
if ($node->getTemplateLine() != $this->lastLine) {
$this->write(sprintf("// line %d\n", $node->getTemplateLine()));
// when mbstring.func_overload is set to 2
// mb_substr_count() replaces substr_count()
// but they have different signatures!
if (((int) ini_get('mbstring.func_overload')) & 2) {
@trigger_error('Support for having "mbstring.func_overload" different from 0 is deprecated version 1.29 and will be removed in 2.0.', E_USER_DEPRECATED);
// this is much slower than the "right" version
$this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n");
} else {
$this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
}
$this->sourceOffset = \strlen($this->source);
$this->debugInfo[$this->sourceLine] = $node->getTemplateLine();
$this->lastLine = $node->getTemplateLine();
}
return $this;
}
public function getDebugInfo()
{
ksort($this->debugInfo);
return $this->debugInfo;
}
/**
* Indents the generated code.
*
* @param int $step The number of indentation to add
*
* @return $this
*/
public function indent($step = 1)
{
$this->indentation += $step;
return $this;
}
/**
* Outdents the generated code.
*
* @param int $step The number of indentation to remove
*
* @return $this
*
* @throws \LogicException When trying to outdent too much so the indentation would become negative
*/
public function outdent($step = 1)
{
// can't outdent by more steps than the current indentation level
if ($this->indentation < $step) {
throw new \LogicException('Unable to call outdent() as the indentation would become negative.');
}
$this->indentation -= $step;
return $this;
}
public function getVarName()
{
return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->varNameSalt++));
}
}
class_alias('Twig\Compiler', 'Twig_Compiler');

File diff suppressed because it is too large Load Diff

View File

@@ -1,325 +0,0 @@
<?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\Error;
use Twig\Source;
use Twig\Template;
/**
* Twig base exception.
*
* This exception class and its children must only be used when
* an error occurs during the loading of a template, when a syntax error
* is detected in a template, or when rendering a template. Other
* errors must use regular PHP exception classes (like when the template
* cache directory is not writable for instance).
*
* To help debugging template issues, this class tracks the original template
* name and line where the error occurred.
*
* Whenever possible, you must set these information (original template name
* and line number) yourself by passing them to the constructor. If some or all
* these information are not available from where you throw the exception, then
* this class will guess them automatically (when the line number is set to -1
* and/or the name is set to null). As this is a costly operation, this
* can be disabled by passing false for both the name and the line number
* when creating a new instance of this class.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Error extends \Exception
{
protected $lineno;
// to be renamed to name in 2.0
protected $filename;
protected $rawMessage;
private $sourcePath;
private $sourceCode;
/**
* Constructor.
*
* Set the line number to -1 to enable its automatic guessing.
* Set the name to null to enable its automatic guessing.
*
* @param string $message The error message
* @param int $lineno The template line where the error occurred
* @param Source|string|null $source The source context where the error occurred
* @param \Exception $previous The previous exception
*/
public function __construct($message, $lineno = -1, $source = null, \Exception $previous = null)
{
if (null === $source) {
$name = null;
} elseif (!$source instanceof Source) {
// for compat with the Twig C ext., passing the template name as string is accepted
$name = $source;
} else {
$name = $source->getName();
$this->sourceCode = $source->getCode();
$this->sourcePath = $source->getPath();
}
parent::__construct('', 0, $previous);
$this->lineno = $lineno;
$this->filename = $name;
$this->rawMessage = $message;
$this->updateRepr();
}
/**
* Gets the raw message.
*
* @return string The raw message
*/
public function getRawMessage()
{
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.
*
* @return int The template line
*/
public function getTemplateLine()
{
return $this->lineno;
}
/**
* Sets the template line where the error occurred.
*
* @param int $lineno The template line
*/
public function setTemplateLine($lineno)
{
$this->lineno = $lineno;
$this->updateRepr();
}
/**
* Gets the source context of the Twig template where the error occurred.
*
* @return Source|null
*/
public function getSourceContext()
{
return $this->filename ? new Source($this->sourceCode, $this->filename, $this->sourcePath) : null;
}
/**
* Sets the source context of the Twig template where the error occurred.
*/
public function setSourceContext(Source $source = null)
{
if (null === $source) {
$this->sourceCode = $this->filename = $this->sourcePath = null;
} else {
$this->sourceCode = $source->getCode();
$this->filename = $source->getName();
$this->sourcePath = $source->getPath();
}
$this->updateRepr();
}
public function guess()
{
$this->guessTemplateInfo();
$this->updateRepr();
}
public function appendMessage($rawMessage)
{
$this->rawMessage .= $rawMessage;
$this->updateRepr();
}
/**
* @internal
*/
protected function updateRepr()
{
$this->message = $this->rawMessage;
if ($this->sourcePath && $this->lineno > 0) {
$this->file = $this->sourcePath;
$this->line = $this->lineno;
return;
}
$dot = false;
if ('.' === substr($this->message, -1)) {
$this->message = substr($this->message, 0, -1);
$dot = true;
}
$questionMark = false;
if ('?' === substr($this->message, -1)) {
$this->message = substr($this->message, 0, -1);
$questionMark = true;
}
if ($this->filename) {
if (\is_string($this->filename) || (\is_object($this->filename) && method_exists($this->filename, '__toString'))) {
$name = sprintf('"%s"', $this->filename);
} else {
$name = json_encode($this->filename);
}
$this->message .= sprintf(' in %s', $name);
}
if ($this->lineno && $this->lineno >= 0) {
$this->message .= sprintf(' at line %d', $this->lineno);
}
if ($dot) {
$this->message .= '.';
}
if ($questionMark) {
$this->message .= '?';
}
}
/**
* @internal
*/
protected function guessTemplateInfo()
{
$template = null;
$templateClass = null;
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
foreach ($backtrace as $trace) {
if (isset($trace['object']) && $trace['object'] instanceof Template && 'Twig_Template' !== \get_class($trace['object'])) {
$currentClass = \get_class($trace['object']);
$isEmbedContainer = 0 === strpos($templateClass, $currentClass);
if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
$template = $trace['object'];
$templateClass = \get_class($trace['object']);
}
}
}
// update template name
if (null !== $template && null === $this->filename) {
$this->filename = $template->getTemplateName();
}
// update template path if any
if (null !== $template && null === $this->sourcePath) {
$src = $template->getSourceContext();
$this->sourceCode = $src->getCode();
$this->sourcePath = $src->getPath();
}
if (null === $template || $this->lineno > -1) {
return;
}
$r = new \ReflectionObject($template);
$file = $r->getFileName();
$exceptions = [$e = $this];
while ($e instanceof self && $e = $e->getPrevious()) {
$exceptions[] = $e;
}
while ($e = array_pop($exceptions)) {
$traces = $e->getTrace();
array_unshift($traces, ['file' => $e->getFile(), 'line' => $e->getLine()]);
while ($trace = array_shift($traces)) {
if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) {
continue;
}
foreach ($template->getDebugInfo() as $codeLine => $templateLine) {
if ($codeLine <= $trace['line']) {
// update template line
$this->lineno = $templateLine;
return;
}
}
}
}
}
}
class_alias('Twig\Error\Error', 'Twig_Error');

View File

@@ -1,23 +0,0 @@
<?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\Error;
/**
* Exception thrown when an error occurs during template loading.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class LoaderError extends Error
{
}
class_alias('Twig\Error\LoaderError', 'Twig_Error_Loader');

View File

@@ -1,24 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
* (c) Armin Ronacher
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig\Error;
/**
* Exception thrown when an error occurs at runtime.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class RuntimeError extends Error
{
}
class_alias('Twig\Error\RuntimeError', 'Twig_Error_Runtime');

View File

@@ -1,57 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
* (c) Armin Ronacher
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig\Error;
/**
* \Exception thrown when a syntax error occurs during lexing or parsing of a template.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class SyntaxError extends Error
{
/**
* Tweaks the error message to include suggestions.
*
* @param string $name The original name of the item that does not exist
* @param array $items An array of possible items
*/
public function addSuggestions($name, array $items)
{
if (!$alternatives = self::computeAlternatives($name, $items)) {
return;
}
$this->appendMessage(sprintf(' Did you mean "%s"?', implode('", "', $alternatives)));
}
/**
* @internal
*
* To be merged with the addSuggestions() method in 2.0.
*/
public static function computeAlternatives($name, $items)
{
$alternatives = [];
foreach ($items as $item) {
$lev = levenshtein($name, $item);
if ($lev <= \strlen($name) / 3 || false !== strpos($item, $name)) {
$alternatives[$item] = $lev;
}
}
asort($alternatives);
return array_keys($alternatives);
}
}
class_alias('Twig\Error\SyntaxError', 'Twig_Error_Syntax');

View File

@@ -1,834 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
* (c) Armin Ronacher
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig;
use Twig\Error\SyntaxError;
use Twig\Node\Expression\ArrayExpression;
use Twig\Node\Expression\ArrowFunctionExpression;
use Twig\Node\Expression\AssignNameExpression;
use Twig\Node\Expression\Binary\ConcatBinary;
use Twig\Node\Expression\BlockReferenceExpression;
use Twig\Node\Expression\ConditionalExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\MethodCallExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\ParentExpression;
use Twig\Node\Expression\Unary\NegUnary;
use Twig\Node\Expression\Unary\NotUnary;
use Twig\Node\Expression\Unary\PosUnary;
use Twig\Node\Node;
/**
* Parses expressions.
*
* This parser implements a "Precedence climbing" algorithm.
*
* @see https://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
* @see https://en.wikipedia.org/wiki/Operator-precedence_parser
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @internal
*/
class ExpressionParser
{
const OPERATOR_LEFT = 1;
const OPERATOR_RIGHT = 2;
protected $parser;
protected $unaryOperators;
protected $binaryOperators;
private $env;
public function __construct(Parser $parser, $env = null)
{
$this->parser = $parser;
if ($env instanceof Environment) {
$this->env = $env;
$this->unaryOperators = $env->getUnaryOperators();
$this->binaryOperators = $env->getBinaryOperators();
} else {
@trigger_error('Passing the operators as constructor arguments to '.__METHOD__.' is deprecated since version 1.27. Pass the environment instead.', E_USER_DEPRECATED);
$this->env = $parser->getEnvironment();
$this->unaryOperators = func_get_arg(1);
$this->binaryOperators = func_get_arg(2);
}
}
public function parseExpression($precedence = 0, $allowArrow = false)
{
if ($allowArrow && $arrow = $this->parseArrow()) {
return $arrow;
}
$expr = $this->getPrimary();
$token = $this->parser->getCurrentToken();
while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) {
$op = $this->binaryOperators[$token->getValue()];
$this->parser->getStream()->next();
if ('is not' === $token->getValue()) {
$expr = $this->parseNotTestExpression($expr);
} elseif ('is' === $token->getValue()) {
$expr = $this->parseTestExpression($expr);
} elseif (isset($op['callable'])) {
$expr = \call_user_func($op['callable'], $this->parser, $expr);
} else {
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
$class = $op['class'];
$expr = new $class($expr, $expr1, $token->getLine());
}
$token = $this->parser->getCurrentToken();
}
if (0 === $precedence) {
return $this->parseConditionalExpression($expr);
}
return $expr;
}
/**
* @return ArrowFunctionExpression|null
*/
private function parseArrow()
{
$stream = $this->parser->getStream();
// short array syntax (one argument, no parentheses)?
if ($stream->look(1)->test(Token::ARROW_TYPE)) {
$line = $stream->getCurrent()->getLine();
$token = $stream->expect(Token::NAME_TYPE);
$names = [new AssignNameExpression($token->getValue(), $token->getLine())];
$stream->expect(Token::ARROW_TYPE);
return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line);
}
// first, determine if we are parsing an arrow function by finding => (long form)
$i = 0;
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, '(')) {
return null;
}
++$i;
while (true) {
// variable name
++$i;
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ',')) {
break;
}
++$i;
}
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ')')) {
return null;
}
++$i;
if (!$stream->look($i)->test(Token::ARROW_TYPE)) {
return null;
}
// yes, let's parse it properly
$token = $stream->expect(Token::PUNCTUATION_TYPE, '(');
$line = $token->getLine();
$names = [];
while (true) {
$token = $stream->expect(Token::NAME_TYPE);
$names[] = new AssignNameExpression($token->getValue(), $token->getLine());
if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) {
break;
}
}
$stream->expect(Token::PUNCTUATION_TYPE, ')');
$stream->expect(Token::ARROW_TYPE);
return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line);
}
protected function getPrimary()
{
$token = $this->parser->getCurrentToken();
if ($this->isUnary($token)) {
$operator = $this->unaryOperators[$token->getValue()];
$this->parser->getStream()->next();
$expr = $this->parseExpression($operator['precedence']);
$class = $operator['class'];
return $this->parsePostfixExpression(new $class($expr, $token->getLine()));
} elseif ($token->test(Token::PUNCTUATION_TYPE, '(')) {
$this->parser->getStream()->next();
$expr = $this->parseExpression();
$this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed');
return $this->parsePostfixExpression($expr);
}
return $this->parsePrimaryExpression();
}
protected function parseConditionalExpression($expr)
{
while ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, '?')) {
if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) {
$expr2 = $this->parseExpression();
if ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) {
$expr3 = $this->parseExpression();
} else {
$expr3 = new ConstantExpression('', $this->parser->getCurrentToken()->getLine());
}
} else {
$expr2 = $expr;
$expr3 = $this->parseExpression();
}
$expr = new ConditionalExpression($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine());
}
return $expr;
}
protected function isUnary(Token $token)
{
return $token->test(Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]);
}
protected function isBinary(Token $token)
{
return $token->test(Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]);
}
public function parsePrimaryExpression()
{
$token = $this->parser->getCurrentToken();
switch ($token->getType()) {
case Token::NAME_TYPE:
$this->parser->getStream()->next();
switch ($token->getValue()) {
case 'true':
case 'TRUE':
$node = new ConstantExpression(true, $token->getLine());
break;
case 'false':
case 'FALSE':
$node = new ConstantExpression(false, $token->getLine());
break;
case 'none':
case 'NONE':
case 'null':
case 'NULL':
$node = new ConstantExpression(null, $token->getLine());
break;
default:
if ('(' === $this->parser->getCurrentToken()->getValue()) {
$node = $this->getFunctionNode($token->getValue(), $token->getLine());
} else {
$node = new NameExpression($token->getValue(), $token->getLine());
}
}
break;
case Token::NUMBER_TYPE:
$this->parser->getStream()->next();
$node = new ConstantExpression($token->getValue(), $token->getLine());
break;
case Token::STRING_TYPE:
case Token::INTERPOLATION_START_TYPE:
$node = $this->parseStringExpression();
break;
case Token::OPERATOR_TYPE:
if (preg_match(Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) {
// in this context, string operators are variable names
$this->parser->getStream()->next();
$node = new NameExpression($token->getValue(), $token->getLine());
break;
} elseif (isset($this->unaryOperators[$token->getValue()])) {
$class = $this->unaryOperators[$token->getValue()]['class'];
$ref = new \ReflectionClass($class);
$negClass = 'Twig\Node\Expression\Unary\NegUnary';
$posClass = 'Twig\Node\Expression\Unary\PosUnary';
if (!(\in_array($ref->getName(), [$negClass, $posClass, 'Twig_Node_Expression_Unary_Neg', 'Twig_Node_Expression_Unary_Pos'])
|| $ref->isSubclassOf($negClass) || $ref->isSubclassOf($posClass)
|| $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());
}
$this->parser->getStream()->next();
$expr = $this->parsePrimaryExpression();
$node = new $class($expr, $token->getLine());
break;
}
// no break
default:
if ($token->test(Token::PUNCTUATION_TYPE, '[')) {
$node = $this->parseArrayExpression();
} elseif ($token->test(Token::PUNCTUATION_TYPE, '{')) {
$node = $this->parseHashExpression();
} elseif ($token->test(Token::OPERATOR_TYPE, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) {
throw new SyntaxError(sprintf('Unexpected operator of value "%s". Did you try to use "===" or "!==" for strict comparison? Use "is same as(value)" instead.', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
} else {
throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s".', Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
}
}
return $this->parsePostfixExpression($node);
}
public function parseStringExpression()
{
$stream = $this->parser->getStream();
$nodes = [];
// a string cannot be followed by another string in a single expression
$nextCanBeString = true;
while (true) {
if ($nextCanBeString && $token = $stream->nextIf(Token::STRING_TYPE)) {
$nodes[] = new ConstantExpression($token->getValue(), $token->getLine());
$nextCanBeString = false;
} elseif ($stream->nextIf(Token::INTERPOLATION_START_TYPE)) {
$nodes[] = $this->parseExpression();
$stream->expect(Token::INTERPOLATION_END_TYPE);
$nextCanBeString = true;
} else {
break;
}
}
$expr = array_shift($nodes);
foreach ($nodes as $node) {
$expr = new ConcatBinary($expr, $node, $node->getTemplateLine());
}
return $expr;
}
public function parseArrayExpression()
{
$stream = $this->parser->getStream();
$stream->expect(Token::PUNCTUATION_TYPE, '[', 'An array element was expected');
$node = new ArrayExpression([], $stream->getCurrent()->getLine());
$first = true;
while (!$stream->test(Token::PUNCTUATION_TYPE, ']')) {
if (!$first) {
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma');
// trailing ,?
if ($stream->test(Token::PUNCTUATION_TYPE, ']')) {
break;
}
}
$first = false;
$node->addElement($this->parseExpression());
}
$stream->expect(Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed');
return $node;
}
public function parseHashExpression()
{
$stream = $this->parser->getStream();
$stream->expect(Token::PUNCTUATION_TYPE, '{', 'A hash element was expected');
$node = new ArrayExpression([], $stream->getCurrent()->getLine());
$first = true;
while (!$stream->test(Token::PUNCTUATION_TYPE, '}')) {
if (!$first) {
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma');
// trailing ,?
if ($stream->test(Token::PUNCTUATION_TYPE, '}')) {
break;
}
}
$first = false;
// a hash key can be:
//
// * a number -- 12
// * a string -- 'a'
// * a name, which is equivalent to a string -- a
// * an expression, which must be enclosed in parentheses -- (1 + 2)
if (($token = $stream->nextIf(Token::STRING_TYPE)) || ($token = $stream->nextIf(Token::NAME_TYPE)) || $token = $stream->nextIf(Token::NUMBER_TYPE)) {
$key = new ConstantExpression($token->getValue(), $token->getLine());
} elseif ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
$key = $this->parseExpression();
} else {
$current = $stream->getCurrent();
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 (:)');
$value = $this->parseExpression();
$node->addElement($value, $key);
}
$stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed');
return $node;
}
public function parsePostfixExpression($node)
{
while (true) {
$token = $this->parser->getCurrentToken();
if (Token::PUNCTUATION_TYPE == $token->getType()) {
if ('.' == $token->getValue() || '[' == $token->getValue()) {
$node = $this->parseSubscriptExpression($node);
} elseif ('|' == $token->getValue()) {
$node = $this->parseFilterExpression($node);
} else {
break;
}
} else {
break;
}
}
return $node;
}
public function getFunctionNode($name, $line)
{
switch ($name) {
case 'parent':
$this->parseArguments();
if (!\count($this->parser->getBlockStack())) {
throw new SyntaxError('Calling "parent" outside a block is forbidden.', $line, $this->parser->getStream()->getSourceContext());
}
if (!$this->parser->getParent() && !$this->parser->hasTraits()) {
throw new SyntaxError('Calling "parent" on a template that does not extend nor "use" another template is forbidden.', $line, $this->parser->getStream()->getSourceContext());
}
return new ParentExpression($this->parser->peekBlockStack(), $line);
case 'block':
$args = $this->parseArguments();
if (\count($args) < 1) {
throw new SyntaxError('The "block" function takes one argument (the block name).', $line, $this->parser->getStream()->getSourceContext());
}
return new BlockReferenceExpression($args->getNode(0), \count($args) > 1 ? $args->getNode(1) : null, $line);
case 'attribute':
$args = $this->parseArguments();
if (\count($args) < 2) {
throw new SyntaxError('The "attribute" function takes at least two arguments (the variable and the attributes).', $line, $this->parser->getStream()->getSourceContext());
}
return new GetAttrExpression($args->getNode(0), $args->getNode(1), \count($args) > 2 ? $args->getNode(2) : null, Template::ANY_CALL, $line);
default:
if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) {
$arguments = new ArrayExpression([], $line);
foreach ($this->parseArguments() as $n) {
$arguments->addElement($n);
}
$node = new MethodCallExpression($alias['node'], $alias['name'], $arguments, $line);
$node->setAttribute('safe', true);
return $node;
}
$args = $this->parseArguments(true);
$class = $this->getFunctionNodeClass($name, $line);
return new $class($name, $args, $line);
}
}
public function parseSubscriptExpression($node)
{
$stream = $this->parser->getStream();
$token = $stream->next();
$lineno = $token->getLine();
$arguments = new ArrayExpression([], $lineno);
$type = Template::ANY_CALL;
if ('.' == $token->getValue()) {
$token = $stream->next();
if (
Token::NAME_TYPE == $token->getType()
||
Token::NUMBER_TYPE == $token->getType()
||
(Token::OPERATOR_TYPE == $token->getType() && preg_match(Lexer::REGEX_NAME, $token->getValue()))
) {
$arg = new ConstantExpression($token->getValue(), $lineno);
if ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
$type = Template::METHOD_CALL;
foreach ($this->parseArguments() as $n) {
$arguments->addElement($n);
}
}
} else {
throw new SyntaxError('Expected name or number.', $lineno, $stream->getSourceContext());
}
if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
if (!$arg instanceof ConstantExpression) {
throw new SyntaxError(sprintf('Dynamic macro names are not supported (called on "%s").', $node->getAttribute('name')), $token->getLine(), $stream->getSourceContext());
}
$name = $arg->getAttribute('value');
if ($this->parser->isReservedMacroName($name)) {
throw new SyntaxError(sprintf('"%s" cannot be called as macro as it is a reserved keyword.', $name), $token->getLine(), $stream->getSourceContext());
}
$node = new MethodCallExpression($node, 'get'.$name, $arguments, $lineno);
$node->setAttribute('safe', true);
return $node;
}
} else {
$type = Template::ARRAY_CALL;
// slice?
$slice = false;
if ($stream->test(Token::PUNCTUATION_TYPE, ':')) {
$slice = true;
$arg = new ConstantExpression(0, $token->getLine());
} else {
$arg = $this->parseExpression();
}
if ($stream->nextIf(Token::PUNCTUATION_TYPE, ':')) {
$slice = true;
}
if ($slice) {
if ($stream->test(Token::PUNCTUATION_TYPE, ']')) {
$length = new ConstantExpression(null, $token->getLine());
} else {
$length = $this->parseExpression();
}
$class = $this->getFilterNodeClass('slice', $token->getLine());
$arguments = new Node([$arg, $length]);
$filter = new $class($node, new ConstantExpression('slice', $token->getLine()), $arguments, $token->getLine());
$stream->expect(Token::PUNCTUATION_TYPE, ']');
return $filter;
}
$stream->expect(Token::PUNCTUATION_TYPE, ']');
}
return new GetAttrExpression($node, $arg, $arguments, $type, $lineno);
}
public function parseFilterExpression($node)
{
$this->parser->getStream()->next();
return $this->parseFilterExpressionRaw($node);
}
public function parseFilterExpressionRaw($node, $tag = null)
{
while (true) {
$token = $this->parser->getStream()->expect(Token::NAME_TYPE);
$name = new ConstantExpression($token->getValue(), $token->getLine());
if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '(')) {
$arguments = new Node();
} else {
$arguments = $this->parseArguments(true, false, true);
}
$class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine());
$node = new $class($node, $name, $arguments, $token->getLine(), $tag);
if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '|')) {
break;
}
$this->parser->getStream()->next();
}
return $node;
}
/**
* Parses arguments.
*
* @param bool $namedArguments Whether to allow named arguments or not
* @param bool $definition Whether we are parsing arguments for a function definition
*
* @return Node
*
* @throws SyntaxError
*/
public function parseArguments($namedArguments = false, $definition = false, $allowArrow = false)
{
$args = [];
$stream = $this->parser->getStream();
$stream->expect(Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');
while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) {
if (!empty($args)) {
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
}
if ($definition) {
$token = $stream->expect(Token::NAME_TYPE, null, 'An argument must be a name');
$value = new NameExpression($token->getValue(), $this->parser->getCurrentToken()->getLine());
} else {
$value = $this->parseExpression(0, $allowArrow);
}
$name = null;
if ($namedArguments && $token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) {
if (!$value instanceof NameExpression) {
throw new SyntaxError(sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext());
}
$name = $value->getAttribute('name');
if ($definition) {
$value = $this->parsePrimaryExpression();
if (!$this->checkConstantExpression($value)) {
throw new SyntaxError(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $stream->getSourceContext());
}
} else {
$value = $this->parseExpression(0, $allowArrow);
}
}
if ($definition) {
if (null === $name) {
$name = $value->getAttribute('name');
$value = new ConstantExpression(null, $this->parser->getCurrentToken()->getLine());
}
$args[$name] = $value;
} else {
if (null === $name) {
$args[] = $value;
} else {
$args[$name] = $value;
}
}
}
$stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
return new Node($args);
}
public function parseAssignmentExpression()
{
$stream = $this->parser->getStream();
$targets = [];
while (true) {
$token = $this->parser->getCurrentToken();
if ($stream->test(Token::OPERATOR_TYPE) && preg_match(Lexer::REGEX_NAME, $token->getValue())) {
// in this context, string operators are variable names
$this->parser->getStream()->next();
} else {
$stream->expect(Token::NAME_TYPE, null, 'Only variables can be assigned to');
}
$value = $token->getValue();
if (\in_array(strtolower($value), ['true', 'false', 'none', 'null'])) {
throw new SyntaxError(sprintf('You cannot assign a value to "%s".', $value), $token->getLine(), $stream->getSourceContext());
}
$targets[] = new AssignNameExpression($value, $token->getLine());
if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) {
break;
}
}
return new Node($targets);
}
public function parseMultitargetExpression()
{
$targets = [];
while (true) {
$targets[] = $this->parseExpression();
if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ',')) {
break;
}
}
return new Node($targets);
}
private function parseNotTestExpression(\Twig_NodeInterface $node)
{
return new NotUnary($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine());
}
private function parseTestExpression(\Twig_NodeInterface $node)
{
$stream = $this->parser->getStream();
list($name, $test) = $this->getTest($node->getTemplateLine());
$class = $this->getTestNodeClass($test);
$arguments = null;
if ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
$arguments = $this->parseArguments(true);
}
return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine());
}
private function getTest($line)
{
$stream = $this->parser->getStream();
$name = $stream->expect(Token::NAME_TYPE)->getValue();
if ($test = $this->env->getTest($name)) {
return [$name, $test];
}
if ($stream->test(Token::NAME_TYPE)) {
// try 2-words tests
$name = $name.' '.$this->parser->getCurrentToken()->getValue();
if ($test = $this->env->getTest($name)) {
$stream->next();
return [$name, $test];
}
}
$e = new SyntaxError(sprintf('Unknown "%s" test.', $name), $line, $stream->getSourceContext());
$e->addSuggestions($name, array_keys($this->env->getTests()));
throw $e;
}
private function getTestNodeClass($test)
{
if ($test instanceof TwigTest && $test->isDeprecated()) {
$stream = $this->parser->getStream();
$message = sprintf('Twig Test "%s" is deprecated', $test->getName());
if (!\is_bool($test->getDeprecatedVersion())) {
$message .= sprintf(' since version %s', $test->getDeprecatedVersion());
}
if ($test->getAlternative()) {
$message .= sprintf('. Use "%s" instead', $test->getAlternative());
}
$src = $stream->getSourceContext();
$message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $stream->getCurrent()->getLine());
@trigger_error($message, E_USER_DEPRECATED);
}
if ($test instanceof TwigTest) {
return $test->getNodeClass();
}
return $test instanceof \Twig_Test_Node ? $test->getClass() : 'Twig\Node\Expression\TestExpression';
}
protected function getFunctionNodeClass($name, $line)
{
if (false === $function = $this->env->getFunction($name)) {
$e = new SyntaxError(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext());
$e->addSuggestions($name, array_keys($this->env->getFunctions()));
throw $e;
}
if ($function instanceof TwigFunction && $function->isDeprecated()) {
$message = sprintf('Twig Function "%s" is deprecated', $function->getName());
if (!\is_bool($function->getDeprecatedVersion())) {
$message .= sprintf(' since version %s', $function->getDeprecatedVersion());
}
if ($function->getAlternative()) {
$message .= sprintf('. Use "%s" instead', $function->getAlternative());
}
$src = $this->parser->getStream()->getSourceContext();
$message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line);
@trigger_error($message, E_USER_DEPRECATED);
}
if ($function instanceof TwigFunction) {
return $function->getNodeClass();
}
return $function instanceof \Twig_Function_Node ? $function->getClass() : 'Twig\Node\Expression\FunctionExpression';
}
protected function getFilterNodeClass($name, $line)
{
if (false === $filter = $this->env->getFilter($name)) {
$e = new SyntaxError(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext());
$e->addSuggestions($name, array_keys($this->env->getFilters()));
throw $e;
}
if ($filter instanceof TwigFilter && $filter->isDeprecated()) {
$message = sprintf('Twig Filter "%s" is deprecated', $filter->getName());
if (!\is_bool($filter->getDeprecatedVersion())) {
$message .= sprintf(' since version %s', $filter->getDeprecatedVersion());
}
if ($filter->getAlternative()) {
$message .= sprintf('. Use "%s" instead', $filter->getAlternative());
}
$src = $this->parser->getStream()->getSourceContext();
$message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line);
@trigger_error($message, E_USER_DEPRECATED);
}
if ($filter instanceof TwigFilter) {
return $filter->getNodeClass();
}
return $filter instanceof \Twig_Filter_Node ? $filter->getClass() : 'Twig\Node\Expression\FilterExpression';
}
// checks that the node only contains "constant" elements
protected function checkConstantExpression(\Twig_NodeInterface $node)
{
if (!($node instanceof ConstantExpression || $node instanceof ArrayExpression
|| $node instanceof NegUnary || $node instanceof PosUnary
)) {
return false;
}
foreach ($node as $n) {
if (!$this->checkConstantExpression($n)) {
return false;
}
}
return true;
}
}
class_alias('Twig\ExpressionParser', 'Twig_ExpressionParser');

View File

@@ -1,72 +0,0 @@
<?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\Extension;
use Twig\Environment;
abstract class AbstractExtension implements ExtensionInterface
{
/**
* @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_InitRuntimeInterface instead
*/
public function initRuntime(Environment $environment)
{
}
public function getTokenParsers()
{
return [];
}
public function getNodeVisitors()
{
return [];
}
public function getFilters()
{
return [];
}
public function getTests()
{
return [];
}
public function getFunctions()
{
return [];
}
public function getOperators()
{
return [];
}
/**
* @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_GlobalsInterface instead
*/
public function getGlobals()
{
return [];
}
/**
* @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
*/
public function getName()
{
return \get_class($this);
}
}
class_alias('Twig\Extension\AbstractExtension', 'Twig_Extension');

File diff suppressed because it is too large Load Diff

View File

@@ -1,76 +0,0 @@
<?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\Extension {
use Twig\TwigFunction;
/**
* @final
*/
class DebugExtension extends AbstractExtension
{
public function getFunctions()
{
// dump is safe if var_dump is overridden by xdebug
$isDumpOutputHtmlSafe = \extension_loaded('xdebug')
// false means that it was not set (and the default is on) or it explicitly enabled
&& (false === ini_get('xdebug.overload_var_dump') || ini_get('xdebug.overload_var_dump'))
// false means that it was not set (and the default is on) or it explicitly enabled
// xdebug.overload_var_dump produces HTML only when html_errors is also enabled
&& (false === ini_get('html_errors') || ini_get('html_errors'))
|| 'cli' === \PHP_SAPI
;
return [
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');
}
namespace {
use Twig\Environment;
use Twig\Template;
use Twig\TemplateWrapper;
function twig_var_dump(Environment $env, $context, array $vars = [])
{
if (!$env->isDebug()) {
return;
}
ob_start();
if (!$vars) {
$vars = [];
foreach ($context as $key => $value) {
if (!$value instanceof Template && !$value instanceof TemplateWrapper) {
$vars[$key] = $value;
}
}
var_dump($vars);
} else {
foreach ($vars as $var) {
var_dump($var);
}
}
return ob_get_clean();
}
}

View File

@@ -1,120 +0,0 @@
<?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\Extension {
use Twig\NodeVisitor\EscaperNodeVisitor;
use Twig\TokenParser\AutoEscapeTokenParser;
use Twig\TwigFilter;
/**
* @final
*/
class EscaperExtension extends AbstractExtension
{
protected $defaultStrategy;
/**
* @param string|false|callable $defaultStrategy An escaping strategy
*
* @see setDefaultStrategy()
*/
public function __construct($defaultStrategy = 'html')
{
$this->setDefaultStrategy($defaultStrategy);
}
public function getTokenParsers()
{
return [new AutoEscapeTokenParser()];
}
public function getNodeVisitors()
{
return [new EscaperNodeVisitor()];
}
public function getFilters()
{
return [
new TwigFilter('raw', 'twig_raw_filter', ['is_safe' => ['all']]),
];
}
/**
* Sets the default strategy to use when not defined by the user.
*
* The strategy can be a valid PHP callback that takes the template
* name as an argument and returns the strategy to use.
*
* @param string|false|callable $defaultStrategy An escaping strategy
*/
public function setDefaultStrategy($defaultStrategy)
{
// for BC
if (true === $defaultStrategy) {
@trigger_error('Using "true" as the default strategy is deprecated since version 1.21. Use "html" instead.', E_USER_DEPRECATED);
$defaultStrategy = 'html';
}
if ('filename' === $defaultStrategy) {
@trigger_error('Using "filename" as the default strategy is deprecated since version 1.27. Use "name" instead.', E_USER_DEPRECATED);
$defaultStrategy = 'name';
}
if ('name' === $defaultStrategy) {
$defaultStrategy = ['\Twig\FileExtensionEscapingStrategy', 'guess'];
}
$this->defaultStrategy = $defaultStrategy;
}
/**
* Gets the default strategy to use when not defined by the user.
*
* @param string $name The template name
*
* @return string|false The default strategy to use for the template
*/
public function getDefaultStrategy($name)
{
// disable string callables to avoid calling a function named html or js,
// or any other upcoming escaping strategy
if (!\is_string($this->defaultStrategy) && false !== $this->defaultStrategy) {
return \call_user_func($this->defaultStrategy, $name);
}
return $this->defaultStrategy;
}
public function getName()
{
return 'escaper';
}
}
class_alias('Twig\Extension\EscaperExtension', 'Twig_Extension_Escaper');
}
namespace {
/**
* Marks a variable as being safe.
*
* @param string $string A PHP variable
*
* @return string
*/
function twig_raw_filter($string)
{
return $string;
}
}

View File

@@ -1,101 +0,0 @@
<?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\Extension;
use Twig\Environment;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\TokenParserInterface;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Twig\TwigTest;
/**
* Interface implemented by extension classes.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
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.
*
* @return TokenParserInterface[]
*/
public function getTokenParsers();
/**
* Returns the node visitor instances to add to the existing list.
*
* @return NodeVisitorInterface[]
*/
public function getNodeVisitors();
/**
* Returns a list of filters to add to the existing list.
*
* @return TwigFilter[]
*/
public function getFilters();
/**
* Returns a list of tests to add to the existing list.
*
* @return TwigTest[]
*/
public function getTests();
/**
* Returns a list of functions to add to the existing list.
*
* @return TwigFunction[]
*/
public function getFunctions();
/**
* Returns a list of operators to add to the existing list.
*
* @return array<array> First array of unary operators, second array of binary operators
*/
public function getOperators();
/**
* Returns a list of global variables to add to the existing list.
*
* @return array An array of global variables
*
* @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_GlobalsInterface instead
*/
public function getGlobals();
/**
* Returns the name of the extension.
*
* @return string The extension name
*
* @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
*/
public function getName();
}
class_alias('Twig\Extension\ExtensionInterface', 'Twig_ExtensionInterface');
// Ensure that the aliased name is loaded to keep BC for classes implementing the typehint with the old aliased name.
class_exists('Twig\Environment');

View File

@@ -1,26 +0,0 @@
<?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\Extension;
/**
* Enables usage of the deprecated Twig\Extension\AbstractExtension::getGlobals() method.
*
* Explicitly implement this interface if you really need to implement the
* deprecated getGlobals() method in your extensions.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface GlobalsInterface
{
}
class_alias('Twig\Extension\GlobalsInterface', 'Twig_Extension_GlobalsInterface');

View File

@@ -1,26 +0,0 @@
<?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\Extension;
/**
* Enables usage of the deprecated Twig\Extension\AbstractExtension::initRuntime() method.
*
* Explicitly implement this interface if you really need to implement the
* deprecated initRuntime() method in your extensions.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface InitRuntimeInterface
{
}
class_alias('Twig\Extension\InitRuntimeInterface', 'Twig_Extension_InitRuntimeInterface');

View File

@@ -1,39 +0,0 @@
<?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\Extension;
use Twig\NodeVisitor\OptimizerNodeVisitor;
/**
* @final
*/
class OptimizerExtension extends AbstractExtension
{
protected $optimizers;
public function __construct($optimizers = -1)
{
$this->optimizers = $optimizers;
}
public function getNodeVisitors()
{
return [new OptimizerNodeVisitor($this->optimizers)];
}
public function getName()
{
return 'optimizer';
}
}
class_alias('Twig\Extension\OptimizerExtension', 'Twig_Extension_Optimizer');

View File

@@ -1,53 +0,0 @@
<?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\Extension;
use Twig\Profiler\NodeVisitor\ProfilerNodeVisitor;
use Twig\Profiler\Profile;
class ProfilerExtension extends AbstractExtension
{
private $actives = [];
public function __construct(Profile $profile)
{
$this->actives[] = $profile;
}
public function enter(Profile $profile)
{
$this->actives[0]->addProfile($profile);
array_unshift($this->actives, $profile);
}
public function leave(Profile $profile)
{
$profile->leave();
array_shift($this->actives);
if (1 === \count($this->actives)) {
$this->actives[0]->leave();
}
}
public function getNodeVisitors()
{
return [new ProfilerNodeVisitor(\get_class($this))];
}
public function getName()
{
return 'profiler';
}
}
class_alias('Twig\Extension\ProfilerExtension', 'Twig_Extension_Profiler');

View File

@@ -1,19 +0,0 @@
<?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\Extension;
/**
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*/
interface RuntimeExtensionInterface
{
}

View File

@@ -1,109 +0,0 @@
<?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\Extension;
use Twig\NodeVisitor\SandboxNodeVisitor;
use Twig\Sandbox\SecurityPolicyInterface;
use Twig\TokenParser\SandboxTokenParser;
/**
* @final
*/
class SandboxExtension extends AbstractExtension
{
protected $sandboxedGlobally;
protected $sandboxed;
protected $policy;
public function __construct(SecurityPolicyInterface $policy, $sandboxed = false)
{
$this->policy = $policy;
$this->sandboxedGlobally = $sandboxed;
}
public function getTokenParsers()
{
return [new SandboxTokenParser()];
}
public function getNodeVisitors()
{
return [new SandboxNodeVisitor()];
}
public function enableSandbox()
{
$this->sandboxed = true;
}
public function disableSandbox()
{
$this->sandboxed = false;
}
public function isSandboxed()
{
return $this->sandboxedGlobally || $this->sandboxed;
}
public function isSandboxedGlobally()
{
return $this->sandboxedGlobally;
}
public function setSecurityPolicy(SecurityPolicyInterface $policy)
{
$this->policy = $policy;
}
public function getSecurityPolicy()
{
return $this->policy;
}
public function checkSecurity($tags, $filters, $functions)
{
if ($this->isSandboxed()) {
$this->policy->checkSecurity($tags, $filters, $functions);
}
}
public function checkMethodAllowed($obj, $method)
{
if ($this->isSandboxed()) {
$this->policy->checkMethodAllowed($obj, $method);
}
}
public function checkPropertyAllowed($obj, $method)
{
if ($this->isSandboxed()) {
$this->policy->checkPropertyAllowed($obj, $method);
}
}
public function ensureToStringAllowed($obj)
{
if ($this->isSandboxed() && \is_object($obj) && method_exists($obj, '__toString')) {
$this->policy->checkMethodAllowed($obj, '__toString');
}
return $obj;
}
public function getName()
{
return 'sandbox';
}
}
class_alias('Twig\Extension\SandboxExtension', 'Twig_Extension_Sandbox');

View File

@@ -1,117 +0,0 @@
<?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\Extension;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\TokenParserInterface;
/**
* Internal class.
*
* This class is used by \Twig\Environment as a staging area and must not be used directly.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @internal
*/
class StagingExtension extends AbstractExtension
{
protected $functions = [];
protected $filters = [];
protected $visitors = [];
protected $tokenParsers = [];
protected $globals = [];
protected $tests = [];
public function addFunction($name, $function)
{
if (isset($this->functions[$name])) {
@trigger_error(sprintf('Overriding function "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
}
$this->functions[$name] = $function;
}
public function getFunctions()
{
return $this->functions;
}
public function addFilter($name, $filter)
{
if (isset($this->filters[$name])) {
@trigger_error(sprintf('Overriding filter "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
}
$this->filters[$name] = $filter;
}
public function getFilters()
{
return $this->filters;
}
public function addNodeVisitor(NodeVisitorInterface $visitor)
{
$this->visitors[] = $visitor;
}
public function getNodeVisitors()
{
return $this->visitors;
}
public function addTokenParser(TokenParserInterface $parser)
{
if (isset($this->tokenParsers[$parser->getTag()])) {
@trigger_error(sprintf('Overriding tag "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $parser->getTag()), E_USER_DEPRECATED);
}
$this->tokenParsers[$parser->getTag()] = $parser;
}
public function getTokenParsers()
{
return $this->tokenParsers;
}
public function addGlobal($name, $value)
{
$this->globals[$name] = $value;
}
public function getGlobals()
{
return $this->globals;
}
public function addTest($name, $test)
{
if (isset($this->tests[$name])) {
@trigger_error(sprintf('Overriding test "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
}
$this->tests[$name] = $test;
}
public function getTests()
{
return $this->tests;
}
public function getName()
{
return 'staging';
}
}
class_alias('Twig\Extension\StagingExtension', 'Twig_Extension_Staging');

View File

@@ -1,54 +0,0 @@
<?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\Extension {
use Twig\TwigFunction;
/**
* @final
*/
class StringLoaderExtension extends AbstractExtension
{
public function getFunctions()
{
return [
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');
}
namespace {
use Twig\Environment;
use Twig\TemplateWrapper;
/**
* Loads a template from a string.
*
* {{ include(template_from_string("Hello {{ name }}")) }}
*
* @param string $template A template as a string or object implementing __toString()
* @param string $name An optional name of the template to be used in error messages
*
* @return TemplateWrapper
*/
function twig_template_from_string(Environment $env, $template, $name = null)
{
return $env->createTemplate((string) $template, $name);
}
}

View File

@@ -1,62 +0,0 @@
<?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;
/**
* Default autoescaping strategy based on file names.
*
* This strategy sets the HTML as the default autoescaping strategy,
* but changes it based on the template name.
*
* Note that there is no runtime performance impact as the
* default autoescaping strategy is set at compilation time.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FileExtensionEscapingStrategy
{
/**
* Guesses the best autoescaping strategy based on the file name.
*
* @param string $name The template name
*
* @return string|false The escaping strategy name to use or false to disable
*/
public static function guess($name)
{
if (\in_array(substr($name, -1), ['/', '\\'])) {
return 'html'; // return html for directories
}
if ('.twig' === substr($name, -5)) {
$name = substr($name, 0, -5);
}
$extension = pathinfo($name, PATHINFO_EXTENSION);
switch ($extension) {
case 'js':
return 'js';
case 'css':
return 'css';
case 'txt':
return false;
default:
return 'html';
}
}
}
class_alias('Twig\FileExtensionEscapingStrategy', 'Twig_FileExtensionEscapingStrategy');

View File

@@ -1,534 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
* (c) Armin Ronacher
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig;
use Twig\Error\SyntaxError;
/**
* Lexes a template string.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Lexer implements \Twig_LexerInterface
{
protected $tokens;
protected $code;
protected $cursor;
protected $lineno;
protected $end;
protected $state;
protected $states;
protected $brackets;
protected $env;
// to be renamed to $name in 2.0 (where it is private)
protected $filename;
protected $options;
protected $regexes;
protected $position;
protected $positions;
protected $currentVarBlockLine;
private $source;
const STATE_DATA = 0;
const STATE_BLOCK = 1;
const STATE_VAR = 2;
const STATE_STRING = 3;
const STATE_INTERPOLATION = 4;
const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?([Ee][\+\-][0-9]+)?/A';
const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
const REGEX_DQ_STRING_DELIM = '/"/A';
const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
const PUNCTUATION = '()[]{}?:.,|';
public function __construct(Environment $env, array $options = [])
{
$this->env = $env;
$this->options = array_merge([
'tag_comment' => ['{#', '#}'],
'tag_block' => ['{%', '%}'],
'tag_variable' => ['{{', '}}'],
'whitespace_trim' => '-',
'whitespace_line_trim' => '~',
'whitespace_line_chars' => ' \t\0\x0B',
'interpolation' => ['#{', '}'],
], $options);
// when PHP 7.3 is the min version, we will be able to remove the '#' part in preg_quote as it's part of the default
$this->regexes = [
// }}
'lex_var' => '{
\s*
(?:'.
preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '#').'\s*'. // -}}\s*
'|'.
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_variable'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~}}[ \t\0\x0B]*
'|'.
preg_quote($this->options['tag_variable'][1], '#'). // }}
')
}Ax',
// %}
'lex_block' => '{
\s*
(?:'.
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*\n?'. // -%}\s*\n?
'|'.
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~%}[ \t\0\x0B]*
'|'.
preg_quote($this->options['tag_block'][1], '#').'\n?'. // %}\n?
')
}Ax',
// {% endverbatim %}
'lex_raw_data' => '{'.
preg_quote($this->options['tag_block'][0], '#'). // {%
'('.
$this->options['whitespace_trim']. // -
'|'.
$this->options['whitespace_line_trim']. // ~
')?\s*'.
'(?:end%s)'. // endraw or endverbatim
'\s*'.
'(?:'.
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*'. // -%}
'|'.
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~%}[ \t\0\x0B]*
'|'.
preg_quote($this->options['tag_block'][1], '#'). // %}
')
}sx',
'operator' => $this->getOperatorRegex(),
// #}
'lex_comment' => '{
(?:'.
preg_quote($this->options['whitespace_trim']).preg_quote($this->options['tag_comment'][1], '#').'\s*\n?'. // -#}\s*\n?
'|'.
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_comment'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~#}[ \t\0\x0B]*
'|'.
preg_quote($this->options['tag_comment'][1], '#').'\n?'. // #}\n?
')
}sx',
// verbatim %}
'lex_block_raw' => '{
\s*
(raw|verbatim)
\s*
(?:'.
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*'. // -%}\s*
'|'.
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~%}[ \t\0\x0B]*
'|'.
preg_quote($this->options['tag_block'][1], '#'). // %}
')
}Asx',
'lex_block_line' => '{\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '#').'}As',
// {{ or {% or {#
'lex_tokens_start' => '{
('.
preg_quote($this->options['tag_variable'][0], '#'). // {{
'|'.
preg_quote($this->options['tag_block'][0], '#'). // {%
'|'.
preg_quote($this->options['tag_comment'][0], '#'). // {#
')('.
preg_quote($this->options['whitespace_trim'], '#'). // -
'|'.
preg_quote($this->options['whitespace_line_trim'], '#'). // ~
')?
}sx',
'interpolation_start' => '{'.preg_quote($this->options['interpolation'][0], '#').'\s*}A',
'interpolation_end' => '{\s*'.preg_quote($this->options['interpolation'][1], '#').'}A',
];
}
public function tokenize($code, $name = null)
{
if (!$code instanceof Source) {
@trigger_error(sprintf('Passing a string as the $code argument of %s() is deprecated since version 1.27 and will be removed in 2.0. Pass a \Twig\Source instance instead.', __METHOD__), E_USER_DEPRECATED);
$this->source = new Source($code, $name);
} else {
$this->source = $code;
}
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);
}
if (\function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
$mbEncoding = mb_internal_encoding();
mb_internal_encoding('ASCII');
} else {
$mbEncoding = null;
}
$this->code = str_replace(["\r\n", "\r"], "\n", $this->source->getCode());
$this->filename = $this->source->getName();
$this->cursor = 0;
$this->lineno = 1;
$this->end = \strlen($this->code);
$this->tokens = [];
$this->state = self::STATE_DATA;
$this->states = [];
$this->brackets = [];
$this->position = -1;
// find all token starts in one go
preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE);
$this->positions = $matches;
while ($this->cursor < $this->end) {
// dispatch to the lexing functions depending
// on the current state
switch ($this->state) {
case self::STATE_DATA:
$this->lexData();
break;
case self::STATE_BLOCK:
$this->lexBlock();
break;
case self::STATE_VAR:
$this->lexVar();
break;
case self::STATE_STRING:
$this->lexString();
break;
case self::STATE_INTERPOLATION:
$this->lexInterpolation();
break;
}
}
$this->pushToken(Token::EOF_TYPE);
if (!empty($this->brackets)) {
list($expect, $lineno) = array_pop($this->brackets);
throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
}
if ($mbEncoding) {
mb_internal_encoding($mbEncoding);
}
return new TokenStream($this->tokens, $this->source);
}
protected function lexData()
{
// if no matches are left we return the rest of the template as simple text token
if ($this->position == \count($this->positions[0]) - 1) {
$this->pushToken(Token::TEXT_TYPE, substr($this->code, $this->cursor));
$this->cursor = $this->end;
return;
}
// Find the first token after the current cursor
$position = $this->positions[0][++$this->position];
while ($position[1] < $this->cursor) {
if ($this->position == \count($this->positions[0]) - 1) {
return;
}
$position = $this->positions[0][++$this->position];
}
// push the template text first
$text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor);
// trim?
if (isset($this->positions[2][$this->position][0])) {
if ($this->options['whitespace_trim'] === $this->positions[2][$this->position][0]) {
// whitespace_trim detected ({%-, {{- or {#-)
$text = rtrim($text);
} elseif ($this->options['whitespace_line_trim'] === $this->positions[2][$this->position][0]) {
// whitespace_line_trim detected ({%~, {{~ or {#~)
// don't trim \r and \n
$text = rtrim($text, " \t\0\x0B");
}
}
$this->pushToken(Token::TEXT_TYPE, $text);
$this->moveCursor($textContent.$position[0]);
switch ($this->positions[1][$this->position][0]) {
case $this->options['tag_comment'][0]:
$this->lexComment();
break;
case $this->options['tag_block'][0]:
// raw data?
if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, 0, $this->cursor)) {
$this->moveCursor($match[0]);
$this->lexRawData($match[1]);
// {% line \d+ %}
} elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, 0, $this->cursor)) {
$this->moveCursor($match[0]);
$this->lineno = (int) $match[1];
} else {
$this->pushToken(Token::BLOCK_START_TYPE);
$this->pushState(self::STATE_BLOCK);
$this->currentVarBlockLine = $this->lineno;
}
break;
case $this->options['tag_variable'][0]:
$this->pushToken(Token::VAR_START_TYPE);
$this->pushState(self::STATE_VAR);
$this->currentVarBlockLine = $this->lineno;
break;
}
}
protected function lexBlock()
{
if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, 0, $this->cursor)) {
$this->pushToken(Token::BLOCK_END_TYPE);
$this->moveCursor($match[0]);
$this->popState();
} else {
$this->lexExpression();
}
}
protected function lexVar()
{
if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, 0, $this->cursor)) {
$this->pushToken(Token::VAR_END_TYPE);
$this->moveCursor($match[0]);
$this->popState();
} else {
$this->lexExpression();
}
}
protected function lexExpression()
{
// whitespace
if (preg_match('/\s+/A', $this->code, $match, 0, $this->cursor)) {
$this->moveCursor($match[0]);
if ($this->cursor >= $this->end) {
throw new SyntaxError(sprintf('Unclosed "%s".', self::STATE_BLOCK === $this->state ? 'block' : 'variable'), $this->currentVarBlockLine, $this->source);
}
}
// arrow function
if ('=' === $this->code[$this->cursor] && '>' === $this->code[$this->cursor + 1]) {
$this->pushToken(Token::ARROW_TYPE, '=>');
$this->moveCursor('=>');
}
// operators
elseif (preg_match($this->regexes['operator'], $this->code, $match, 0, $this->cursor)) {
$this->pushToken(Token::OPERATOR_TYPE, preg_replace('/\s+/', ' ', $match[0]));
$this->moveCursor($match[0]);
}
// names
elseif (preg_match(self::REGEX_NAME, $this->code, $match, 0, $this->cursor)) {
$this->pushToken(Token::NAME_TYPE, $match[0]);
$this->moveCursor($match[0]);
}
// numbers
elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, 0, $this->cursor)) {
$number = (float) $match[0]; // floats
if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) {
$number = (int) $match[0]; // integers lower than the maximum
}
$this->pushToken(Token::NUMBER_TYPE, $number);
$this->moveCursor($match[0]);
}
// punctuation
elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) {
// opening bracket
if (false !== strpos('([{', $this->code[$this->cursor])) {
$this->brackets[] = [$this->code[$this->cursor], $this->lineno];
}
// closing bracket
elseif (false !== strpos(')]}', $this->code[$this->cursor])) {
if (empty($this->brackets)) {
throw new SyntaxError(sprintf('Unexpected "%s".', $this->code[$this->cursor]), $this->lineno, $this->source);
}
list($expect, $lineno) = array_pop($this->brackets);
if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) {
throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
}
}
$this->pushToken(Token::PUNCTUATION_TYPE, $this->code[$this->cursor]);
++$this->cursor;
}
// strings
elseif (preg_match(self::REGEX_STRING, $this->code, $match, 0, $this->cursor)) {
$this->pushToken(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)));
$this->moveCursor($match[0]);
}
// opening double quoted string
elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, 0, $this->cursor)) {
$this->brackets[] = ['"', $this->lineno];
$this->pushState(self::STATE_STRING);
$this->moveCursor($match[0]);
}
// unlexable
else {
throw new SyntaxError(sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source);
}
}
protected function lexRawData($tag)
{
if ('raw' === $tag) {
@trigger_error(sprintf('Twig Tag "raw" is deprecated since version 1.21. Use "verbatim" instead in %s at line %d.', $this->filename, $this->lineno), E_USER_DEPRECATED);
}
if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
throw new SyntaxError(sprintf('Unexpected end of file: Unclosed "%s" block.', $tag), $this->lineno, $this->source);
}
$text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
$this->moveCursor($text.$match[0][0]);
// trim?
if (isset($match[1][0])) {
if ($this->options['whitespace_trim'] === $match[1][0]) {
// whitespace_trim detected ({%-, {{- or {#-)
$text = rtrim($text);
} else {
// whitespace_line_trim detected ({%~, {{~ or {#~)
// don't trim \r and \n
$text = rtrim($text, " \t\0\x0B");
}
}
$this->pushToken(Token::TEXT_TYPE, $text);
}
protected function lexComment()
{
if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
throw new SyntaxError('Unclosed comment.', $this->lineno, $this->source);
}
$this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]);
}
protected function lexString()
{
if (preg_match($this->regexes['interpolation_start'], $this->code, $match, 0, $this->cursor)) {
$this->brackets[] = [$this->options['interpolation'][0], $this->lineno];
$this->pushToken(Token::INTERPOLATION_START_TYPE);
$this->moveCursor($match[0]);
$this->pushState(self::STATE_INTERPOLATION);
} elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, 0, $this->cursor) && \strlen($match[0]) > 0) {
$this->pushToken(Token::STRING_TYPE, stripcslashes($match[0]));
$this->moveCursor($match[0]);
} elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, 0, $this->cursor)) {
list($expect, $lineno) = array_pop($this->brackets);
if ('"' != $this->code[$this->cursor]) {
throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
}
$this->popState();
++$this->cursor;
} else {
// unlexable
throw new SyntaxError(sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source);
}
}
protected function lexInterpolation()
{
$bracket = end($this->brackets);
if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, 0, $this->cursor)) {
array_pop($this->brackets);
$this->pushToken(Token::INTERPOLATION_END_TYPE);
$this->moveCursor($match[0]);
$this->popState();
} else {
$this->lexExpression();
}
}
protected function pushToken($type, $value = '')
{
// do not push empty text tokens
if (Token::TEXT_TYPE === $type && '' === $value) {
return;
}
$this->tokens[] = new Token($type, $value, $this->lineno);
}
protected function moveCursor($text)
{
$this->cursor += \strlen($text);
$this->lineno += substr_count($text, "\n");
}
protected function getOperatorRegex()
{
$operators = array_merge(
['='],
array_keys($this->env->getUnaryOperators()),
array_keys($this->env->getBinaryOperators())
);
$operators = array_combine($operators, array_map('strlen', $operators));
arsort($operators);
$regex = [];
foreach ($operators as $operator => $length) {
// an operator that ends with a character must be followed by
// a whitespace or a parenthesis
if (ctype_alpha($operator[$length - 1])) {
$r = preg_quote($operator, '/').'(?=[\s()])';
} else {
$r = preg_quote($operator, '/');
}
// an operator with a space can be any amount of whitespaces
$r = preg_replace('/\s+/', '\s+', $r);
$regex[] = $r;
}
return '/'.implode('|', $regex).'/A';
}
protected function pushState($state)
{
$this->states[] = $this->state;
$this->state = $state;
}
protected function popState()
{
if (0 === \count($this->states)) {
throw new \LogicException('Cannot pop state without a previous state.');
}
$this->state = array_pop($this->states);
}
}
class_alias('Twig\Lexer', 'Twig_Lexer');

View File

@@ -1,102 +0,0 @@
<?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\Loader;
use Twig\Error\LoaderError;
use Twig\Source;
/**
* Loads a template from an array.
*
* When using this loader with a cache mechanism, you should know that a new cache
* key is generated each time a template content "changes" (the cache key being the
* source code of the template). If you don't want to see your cache grows out of
* control, you need to take care of clearing the old cache file by yourself.
*
* This loader should only be used for unit testing.
*
* @final
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ArrayLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface
{
protected $templates = [];
/**
* @param array $templates An array of templates (keys are the names, and values are the source code)
*/
public function __construct(array $templates = [])
{
$this->templates = $templates;
}
/**
* Adds or overrides a template.
*
* @param string $name The template name
* @param string $template The template source
*/
public function setTemplate($name, $template)
{
$this->templates[(string) $name] = $template;
}
public function getSource($name)
{
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', \get_class($this)), E_USER_DEPRECATED);
$name = (string) $name;
if (!isset($this->templates[$name])) {
throw new LoaderError(sprintf('Template "%s" is not defined.', $name));
}
return $this->templates[$name];
}
public function getSourceContext($name)
{
$name = (string) $name;
if (!isset($this->templates[$name])) {
throw new LoaderError(sprintf('Template "%s" is not defined.', $name));
}
return new Source($this->templates[$name], $name);
}
public function exists($name)
{
return isset($this->templates[(string) $name]);
}
public function getCacheKey($name)
{
$name = (string) $name;
if (!isset($this->templates[$name])) {
throw new LoaderError(sprintf('Template "%s" is not defined.', $name));
}
return $name.':'.$this->templates[$name];
}
public function isFresh($name, $time)
{
$name = (string) $name;
if (!isset($this->templates[$name])) {
throw new LoaderError(sprintf('Template "%s" is not defined.', $name));
}
return true;
}
}
class_alias('Twig\Loader\ArrayLoader', 'Twig_Loader_Array');

View File

@@ -1,164 +0,0 @@
<?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\Loader;
use Twig\Error\LoaderError;
use Twig\Source;
/**
* Loads templates from other loaders.
*
* @final
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ChainLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface
{
private $hasSourceCache = [];
protected $loaders = [];
/**
* @param LoaderInterface[] $loaders
*/
public function __construct(array $loaders = [])
{
foreach ($loaders as $loader) {
$this->addLoader($loader);
}
}
public function addLoader(LoaderInterface $loader)
{
$this->loaders[] = $loader;
$this->hasSourceCache = [];
}
/**
* @return LoaderInterface[]
*/
public function getLoaders()
{
return $this->loaders;
}
public function getSource($name)
{
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', \get_class($this)), E_USER_DEPRECATED);
$exceptions = [];
foreach ($this->loaders as $loader) {
if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
return $loader->getSource($name);
} catch (LoaderError $e) {
$exceptions[] = $e->getMessage();
}
}
throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
}
public function getSourceContext($name)
{
$exceptions = [];
foreach ($this->loaders as $loader) {
if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
if ($loader instanceof SourceContextLoaderInterface) {
return $loader->getSourceContext($name);
}
return new Source($loader->getSource($name), $name);
} catch (LoaderError $e) {
$exceptions[] = $e->getMessage();
}
}
throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
}
public function exists($name)
{
$name = (string) $name;
if (isset($this->hasSourceCache[$name])) {
return $this->hasSourceCache[$name];
}
foreach ($this->loaders as $loader) {
if ($loader instanceof ExistsLoaderInterface) {
if ($loader->exists($name)) {
return $this->hasSourceCache[$name] = true;
}
continue;
}
try {
if ($loader instanceof SourceContextLoaderInterface) {
$loader->getSourceContext($name);
} else {
$loader->getSource($name);
}
return $this->hasSourceCache[$name] = true;
} catch (LoaderError $e) {
}
}
return $this->hasSourceCache[$name] = false;
}
public function getCacheKey($name)
{
$exceptions = [];
foreach ($this->loaders as $loader) {
if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
return $loader->getCacheKey($name);
} catch (LoaderError $e) {
$exceptions[] = \get_class($loader).': '.$e->getMessage();
}
}
throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
}
public function isFresh($name, $time)
{
$exceptions = [];
foreach ($this->loaders as $loader) {
if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
return $loader->isFresh($name, $time);
} catch (LoaderError $e) {
$exceptions[] = \get_class($loader).': '.$e->getMessage();
}
}
throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
}
}
class_alias('Twig\Loader\ChainLoader', 'Twig_Loader_Chain');

View File

@@ -1,33 +0,0 @@
<?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\Loader;
/**
* Adds an exists() method for loaders.
*
* @author Florin Patan <florinpatan@gmail.com>
*
* @deprecated since 1.12 (to be removed in 3.0)
*/
interface ExistsLoaderInterface
{
/**
* Check if we have the source code of a template, given its name.
*
* @param string $name The name of the template to check if we can load
*
* @return bool If the template source code is handled by this loader or not
*/
public function exists($name);
}
class_alias('Twig\Loader\ExistsLoaderInterface', 'Twig_ExistsLoaderInterface');

View File

@@ -1,323 +0,0 @@
<?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\Loader;
use Twig\Error\LoaderError;
use Twig\Source;
/**
* Loads template from the filesystem.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface
{
/** Identifier of the main namespace. */
const MAIN_NAMESPACE = '__main__';
protected $paths = [];
protected $cache = [];
protected $errorCache = [];
private $rootPath;
/**
* @param string|array $paths A path or an array of paths where to look for templates
* @param string|null $rootPath The root path common to all relative paths (null for getcwd())
*/
public function __construct($paths = [], $rootPath = null)
{
$this->rootPath = (null === $rootPath ? getcwd() : $rootPath).\DIRECTORY_SEPARATOR;
if (false !== $realPath = realpath($rootPath)) {
$this->rootPath = $realPath.\DIRECTORY_SEPARATOR;
}
if ($paths) {
$this->setPaths($paths);
}
}
/**
* Returns the paths to the templates.
*
* @param string $namespace A path namespace
*
* @return array The array of paths where to look for templates
*/
public function getPaths($namespace = self::MAIN_NAMESPACE)
{
return isset($this->paths[$namespace]) ? $this->paths[$namespace] : [];
}
/**
* Returns the path namespaces.
*
* The main namespace is always defined.
*
* @return array The array of defined namespaces
*/
public function getNamespaces()
{
return array_keys($this->paths);
}
/**
* Sets the paths where templates are stored.
*
* @param string|array $paths A path or an array of paths where to look for templates
* @param string $namespace A path namespace
*/
public function setPaths($paths, $namespace = self::MAIN_NAMESPACE)
{
if (!\is_array($paths)) {
$paths = [$paths];
}
$this->paths[$namespace] = [];
foreach ($paths as $path) {
$this->addPath($path, $namespace);
}
}
/**
* Adds a path where templates are stored.
*
* @param string $path A path where to look for templates
* @param string $namespace A path namespace
*
* @throws LoaderError
*/
public function addPath($path, $namespace = self::MAIN_NAMESPACE)
{
// invalidate the cache
$this->cache = $this->errorCache = [];
$checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path;
if (!is_dir($checkPath)) {
throw new LoaderError(sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath));
}
$this->paths[$namespace][] = rtrim($path, '/\\');
}
/**
* Prepends a path where templates are stored.
*
* @param string $path A path where to look for templates
* @param string $namespace A path namespace
*
* @throws LoaderError
*/
public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
{
// invalidate the cache
$this->cache = $this->errorCache = [];
$checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path;
if (!is_dir($checkPath)) {
throw new LoaderError(sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath));
}
$path = rtrim($path, '/\\');
if (!isset($this->paths[$namespace])) {
$this->paths[$namespace][] = $path;
} else {
array_unshift($this->paths[$namespace], $path);
}
}
public function getSource($name)
{
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', \get_class($this)), E_USER_DEPRECATED);
if (null === ($path = $this->findTemplate($name)) || false === $path) {
return '';
}
return file_get_contents($path);
}
public function getSourceContext($name)
{
if (null === ($path = $this->findTemplate($name)) || false === $path) {
return new Source('', $name, '');
}
return new Source(file_get_contents($path), $name, $path);
}
public function getCacheKey($name)
{
if (null === ($path = $this->findTemplate($name)) || false === $path) {
return '';
}
$len = \strlen($this->rootPath);
if (0 === strncmp($this->rootPath, $path, $len)) {
return substr($path, $len);
}
return $path;
}
public function exists($name)
{
$name = $this->normalizeName($name);
if (isset($this->cache[$name])) {
return true;
}
try {
return null !== ($path = $this->findTemplate($name, false)) && false !== $path;
} catch (LoaderError $e) {
@trigger_error(sprintf('In %s::findTemplate(), you must accept a second argument that when set to "false" returns "false" instead of throwing an exception. Not supporting this argument is deprecated since version 1.27.', \get_class($this)), E_USER_DEPRECATED);
return false;
}
}
public function isFresh($name, $time)
{
// false support to be removed in 3.0
if (null === ($path = $this->findTemplate($name)) || false === $path) {
return false;
}
return filemtime($path) < $time;
}
/**
* Checks if the template can be found.
*
* @param string $name The template name
*
* @return string|false|null The template name or false/null
*/
protected function findTemplate($name)
{
$throw = \func_num_args() > 1 ? func_get_arg(1) : true;
$name = $this->normalizeName($name);
if (isset($this->cache[$name])) {
return $this->cache[$name];
}
if (isset($this->errorCache[$name])) {
if (!$throw) {
return false;
}
throw new LoaderError($this->errorCache[$name]);
}
try {
$this->validateName($name);
list($namespace, $shortname) = $this->parseName($name);
} catch (LoaderError $e) {
if (!$throw) {
return false;
}
throw $e;
}
if (!isset($this->paths[$namespace])) {
$this->errorCache[$name] = sprintf('There are no registered paths for namespace "%s".', $namespace);
if (!$throw) {
return false;
}
throw new LoaderError($this->errorCache[$name]);
}
foreach ($this->paths[$namespace] as $path) {
if (!$this->isAbsolutePath($path)) {
$path = $this->rootPath.$path;
}
if (is_file($path.'/'.$shortname)) {
if (false !== $realpath = realpath($path.'/'.$shortname)) {
return $this->cache[$name] = $realpath;
}
return $this->cache[$name] = $path.'/'.$shortname;
}
}
$this->errorCache[$name] = sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]));
if (!$throw) {
return false;
}
throw new LoaderError($this->errorCache[$name]);
}
protected function parseName($name, $default = self::MAIN_NAMESPACE)
{
if (isset($name[0]) && '@' == $name[0]) {
if (false === $pos = strpos($name, '/')) {
throw new LoaderError(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
}
$namespace = substr($name, 1, $pos - 1);
$shortname = substr($name, $pos + 1);
return [$namespace, $shortname];
}
return [$default, $name];
}
protected function normalizeName($name)
{
return preg_replace('#/{2,}#', '/', str_replace('\\', '/', (string) $name));
}
protected function validateName($name)
{
if (false !== strpos($name, "\0")) {
throw new LoaderError('A template name cannot contain NUL bytes.');
}
$name = ltrim($name, '/');
$parts = explode('/', $name);
$level = 0;
foreach ($parts as $part) {
if ('..' === $part) {
--$level;
} elseif ('.' !== $part) {
++$level;
}
if ($level < 0) {
throw new LoaderError(sprintf('Looks like you try to load a template outside configured directories (%s).', $name));
}
}
}
private function isAbsolutePath($file)
{
return strspn($file, '/\\', 0, 1)
|| (\strlen($file) > 3 && ctype_alpha($file[0])
&& ':' === substr($file, 1, 1)
&& strspn($file, '/\\', 2, 1)
)
|| null !== parse_url($file, PHP_URL_SCHEME)
;
}
}
class_alias('Twig\Loader\FilesystemLoader', 'Twig_Loader_Filesystem');

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