Compare commits

..

140 Commits

Author SHA1 Message Date
slawkens
c247789adf <base> is not working properly, use full URL instead 2023-06-02 15:24:10 +02:00
slawkens
cd22f8def5 Use Whoops only if installed, otherwise use myaac exception handler 2023-06-02 15:20:07 +02:00
slawkens
52ac011556 Ignore cypress in git-export + install composer deps on release 2023-06-02 08:04:13 +02:00
slawkens
f34e5f2ac0 Release 0.9.0-alpha 2023-06-02 06:37:25 +02:00
slawkens
ca8db22639 Better news back button 2023-06-01 11:23:36 +02:00
slawkens
1846bf5255 Change button style (characters - view)
was causing issues in other templates
2023-06-01 09:57:27 +02:00
slawkens
dce0ac2f8f nothing important 2023-05-29 10:09:21 +02:00
slawkens
9cc60983d0 Update CHANGELOG.md 2023-05-29 08:55:41 +02:00
slawkens
7c2c88f780 Add Whoops exception handler (nicer debug info in dev mode)
On production = no errors
2023-05-29 08:55:26 +02:00
slawkens
7690811da3 Update install.php 2023-05-29 08:46:07 +02:00
slawkens
7dc2e404ed Fixed many links to admin panel, if ADMIN_PANEL_FOLDER is changed 2023-05-29 00:00:34 +02:00
slawkens
080ab56ea9 Update functions.php 2023-05-28 23:56:35 +02:00
slawkens
83915f080c Fixed when page is hidden 2023-05-28 23:56:26 +02:00
slawkens
2841f17729 fix images base url, uploaded by tinymce 2023-05-28 23:54:36 +02:00
slawkens
0187ba4938 New hook: HOOK_ACCOUNT_CREATE_AFTER_PASSWORD
for password strength meter
2023-05-26 22:49:24 +02:00
slawkens
bedfc0a2e0 Update bootstrap to v5.2.3 in install 2023-05-26 17:50:38 +02:00
slawkens
ea08c04963 nothing important 2023-05-26 16:49:28 +02:00
slawkens
067f2af3e5 Wait for success, then screenshot 2023-05-26 16:15:39 +02:00
slawkens
8d98306f8e optimize workflow 2023-05-26 16:12:50 +02:00
slawkens
09a045334c Update cypress.yml 2023-05-26 14:53:33 +02:00
slawkens
bc8e5fc144 Update .gitattributes 2023-05-26 14:51:33 +02:00
slawkens
77e0d28a9d Delete .travis.yml 2023-05-26 14:09:10 +02:00
slawkens
480a054f0c Fixed ADMIN_PANEL_FOLDER, can be 100% custom now 2023-05-26 14:04:52 +02:00
slawkens
26c895d475 Block access to some files [skip ci] 2023-05-26 13:51:05 +02:00
slawkens
5cbb55cfb1 Upload videos too 2023-05-26 13:11:39 +02:00
slawkens
dcb9506a1b admin is already created during install 2023-05-26 13:10:14 +02:00
slawkens
2acec4df12 bring back matrix, wasn't the issue 2023-05-26 13:05:35 +02:00
slawkens
4bd761c726 remove typo 2023-05-26 13:05:29 +02:00
slawkens
2f732b8411 wrong position 2023-05-26 13:00:26 +02:00
slawkens
5aa02055bf fix path 2023-05-26 12:58:28 +02:00
slawkens
6ed15565c8 Import TFS Schema 2023-05-26 12:55:08 +02:00
slawkens
77a2c55c87 Update cypress.yml 2023-05-26 12:50:37 +02:00
slawkens
4a9fa01eb7 Update cypress.yml 2023-05-26 12:49:11 +02:00
slawkens
bd031d8980 create database manually 2023-05-26 12:46:03 +02:00
slawkens
b76a037a94 last try to fix mysql 2023-05-26 12:22:06 +02:00
slawkens
e71daa2520 Update cypress.yml 2023-05-26 12:18:51 +02:00
slawkens
f372aeb067 Update cypress.yml 2023-05-26 12:13:23 +02:00
slawkens
ef37bbcb81 Update cypress.yml 2023-05-26 11:49:24 +02:00
slawkens
944457463e Update cypress.yml 2023-05-26 11:01:44 +02:00
slawkens
6f7f25bb46 Update cypress.yml 2023-05-26 10:54:57 +02:00
slawkens
d60d7f2250 test 5.7 mysql 2023-05-26 10:48:47 +02:00
slawkens
2b8c4b3eca test connect with root 2023-05-26 10:44:02 +02:00
slawkens
7039bda359 Update cypress.yml 2023-05-26 10:38:30 +02:00
slawkens
d346a8f73f Update cypress.yml 2023-05-26 10:27:38 +02:00
slawkens
523f2dee7c mysqlPass 2023-05-26 10:21:05 +02:00
slawkens
b33e39491b MySQL needs to be started manually 2023-05-26 10:19:13 +02:00
slawkens
317ebf4387 Update cypress.yml 2023-05-26 10:12:28 +02:00
slawkens
31ba780099 Update cypress.yml 2023-05-26 10:08:51 +02:00
slawkens
d1b30619e2 fix path to config.lua 2023-05-26 09:56:49 +02:00
slawkens
3fab52296a Update cypress.yml 2023-05-26 09:52:11 +02:00
slawkens
a6e109799a Update cypress.yml 2023-05-26 09:48:55 +02:00
slawkens
80af2cd691 cypress-workflow: config.lua move + replace values 2023-05-26 09:47:39 +02:00
slawkens
d911b55e25 Update cypress.yml 2023-05-26 09:24:49 +02:00
slawkens
eb73fc4538 try fix cypress 2023-05-26 09:19:46 +02:00
slawkens
75f77ec7a3 Fix: Run PHP Server 2023-05-26 09:11:04 +02:00
slawkens
a1d969bbfd fix env 2023-05-26 09:00:21 +02:00
slawkens
11f1ad6d76 fix path 2023-05-26 08:53:49 +02:00
slawkens
7facf0adad fix branch 2023-05-26 08:52:05 +02:00
slawkens
2b739c2b40 Add Cypress workflow 2023-05-26 08:49:28 +02:00
slawkens
269ae323e0 Add Cypress tests: install + create account 2023-05-26 08:49:04 +02:00
slawkens
0d0e5812dd Change step to $_REQUEST 2023-05-26 08:36:20 +02:00
slawkens
61c2661377 Fix warning when no header language set 2023-05-25 11:39:45 +02:00
slawkens
de710dff94 Add cypress/e2e/2-advanced-examples to .gitignore 2023-05-25 11:39:02 +02:00
slawkens
8c524171fb Add cypress.env.json to .gitignore 2023-05-25 09:31:29 +02:00
slawkens
946d24690c Update common.php 2023-05-25 09:30:44 +02:00
slawkens
bf137189c5 Update CHANGELOG.md 2023-05-15 00:22:51 +02:00
slawkens
da4e18cb69 Fixes to routing 2023-05-13 12:49:17 +02:00
slawkens
85769c1439 Empty URL = show news 2023-05-13 11:34:51 +02:00
slawkens
4d3ad4b6b9 Update jQuery to v3.6.4 and jQuery UI to v1.13.2 2023-04-19 23:15:28 +02:00
slawkens
e900a62e75 Print more info if character cannot be created 2023-04-12 12:52:14 +02:00
slawkens
c3969364aa Fix default news route if friendly_urls is disabled 2023-04-01 22:13:24 +02:00
slawkens
e9df9f10dc Add check for player_deaths columns 2023-04-01 15:11:12 +02:00
slawkens
f78f5b5361 OK, so phplint is working! 2023-03-31 10:23:10 +02:00
slawkens
c061438a35 test phplint 2023-03-31 10:21:27 +02:00
slawkens
8441dbe007 Add actions/checkout@v3 2023-03-31 10:18:48 +02:00
slawkens
e21a741a78 Use overtrue/phplint@8.2 for phplint 2023-03-31 10:17:21 +02:00
slawkens
955f437e6c test github actions 2023-03-31 09:35:05 +02:00
Matheus Collier
fd419076c2 [UPDATE] Adding monster looks to db (#220)
* [UPDATE] Adding monster looks to db

* small adjustments

add into schema.sql + change position in table

* add DEFAULT = ''

---------

Co-authored-by: slawkens <slawkens@gmail.com>
2023-03-31 09:04:13 +02:00
slawkens
7569536d56 Fix when server uses another items serializer 2023-03-26 00:27:16 +01:00
slawkens
3a6102900f fix small bug on install - please fill all input 2023-03-26 00:24:57 +01:00
slawkens
6dbc694409 Do not allow continue install when there is no server database imported 2023-03-26 00:23:50 +01:00
slawkens
7a3dcc4dc6 small fixes to account_login_by_email 2023-03-25 19:17:55 +01:00
slawkens
23393b5d3e Fix cannot go forward when config.local.php cannot be saved 2023-03-22 09:15:32 +01:00
slawkens
863f3ad510 Change from warning to error (config.local.php save error) 2023-03-22 09:15:05 +01:00
slawkens
e6d86ca280 plugins folder should be accessible from public 2023-03-15 17:51:24 +01:00
slawkens
c22e25e3d2 Update nginx-sample.conf 2023-03-06 08:26:29 +01:00
slawkens
52ffb195ec fix account/lost links 2023-02-19 08:01:02 +01:00
slawkens
92a51af638 Fix account number show 2023-02-19 07:31:51 +01:00
slawkens
d7a9158cf2 Update routes.php 2023-02-18 21:45:09 +01:00
slawkens
f0f84090d2 fix creatures post route, back button 2023-02-18 21:26:22 +01:00
slawkens
9d78a3b5cf more php 8.x compatibility 2023-02-18 21:22:54 +01:00
slawkens
2fc163af5a Update tables.headline.html.twig 2023-02-18 21:12:08 +01:00
slawkens
10be98e371 Create account.back_button.html.twig 2023-02-18 21:11:55 +01:00
slawkens
e0eb083e44 new buttons code for tibiacom template, can create button with any text 2023-02-18 21:11:35 +01:00
slawkens
e17cd78153 fix player save on tfs 1.5 with new ipv6 2023-02-18 20:57:44 +01:00
slawkens
0015f511f8 Preparation v0.9.0-alpha release (Updated CHANGELOG) 2023-02-18 13:05:53 +01:00
slawkens
f0f71c9f85 fixes 2023-02-18 09:33:04 +01:00
slawkens
0002543cca feature: Cache::remember($key, $ttl, $callback) + example usage 2023-02-18 08:53:42 +01:00
slawkens
c1096415aa Add check for tables in admin panel 2023-02-18 07:15:11 +01:00
slawkens
6625768228 Remove accounts.blocked 2023-02-18 07:14:51 +01:00
slawkens
a27f601fe8 feature: new functions: getGuildNameById + geGuildLogoById 2023-02-17 20:05:43 +01:00
slawkens
72a877d9ca Show more info about bot 2023-02-17 19:06:13 +01:00
slawkens
b7ba09a551 small fix 2023-02-17 18:48:01 +01:00
slawkens
a98cb66c53 feature: visitors counter shows now user browser, and also if its bot 2023-02-17 18:41:25 +01:00
slawkens
6785ecad1d Ignore case on plugin route method 2023-02-17 17:54:38 +01:00
slawkens
937af536b6 patch from master - long player ip caused error 2023-02-17 17:17:19 +01:00
slawkens
5487314230 preparation for guild wars 2023-02-17 17:10:01 +01:00
slawkens
51e9bb2a7f set default for optional parameter (twig functions) 2023-02-17 17:09:38 +01:00
slawkens
376bb981ae string is also spaces, lets admin that 2023-02-17 13:35:05 +01:00
slawkens
ed9d78d2f3 Fixes for config.account_create_auto_login 2023-02-17 13:15:13 +01:00
slawkens
3c4e50dbda formatting 2023-02-16 11:24:19 +01:00
slawkens
523f9dd95a New hook: HOOK_ACCOUNT_CHANGE_PASSWORD_POST 2023-02-16 11:24:00 +01:00
slawkens
a43742c8b1 rename hook 2023-02-16 11:22:12 +01:00
slawkens
c49e4fd63d add hook for disable accounts edit for next.my-aac.org (security) 2023-02-16 11:07:00 +01:00
slawkens
905cce7021 Update mailer.php 2023-02-16 10:53:15 +01:00
slawkens
7a49b5dedc Disable add php pages in admin panel for security. Option to disable plugins upload 2023-02-16 10:53:06 +01:00
slawkens
3a2870a6bb 760 is correct permission 2023-02-16 10:06:08 +01:00
slawkens
9a475f2c57 fix for othire where size is saved in houses.tiles 2023-02-16 08:44:17 +01:00
slawkens
58598742e8 change spaces to tabs 2023-02-16 08:43:21 +01:00
slawkens
d04e44f52f add info which plugin is going to be uninstalled 2023-02-16 07:21:38 +01:00
slawkens
c7ec1f44e9 Option to enable/disable plugin by renaming file + cleanup
new function: getAllPluginsJson
removeComments removed - json doesnt allow for comments anyway
2023-02-16 06:57:46 +01:00
slawkens
3ed9a5d3d8 add hook: HOOK_GUILDS_AFTER_INVITED_CHARACTERS, for guild wars 2023-02-16 05:16:22 +01:00
slawkens
61285b6b8c small fix to routes with string 2023-02-15 17:32:48 +01:00
slawkens
d17c547bca add $params as optional parameter to hook twig function 2023-02-15 17:12:56 +01:00
slawkens
7bc20b0993 change spaces to tabs 2023-02-15 17:12:30 +01:00
slawkens
6c4b3dea96 Delete autoload.php 2023-02-15 08:06:57 +01:00
slawkens
6ae1bf5814 Add missing header to some files 2023-02-15 08:06:08 +01:00
slawkens
8503135ce0 add some notice 2023-02-14 23:22:17 +01:00
slawkens
590fe0762d small fixes 2023-02-14 22:03:22 +01:00
slawkens
d565b90736 Update accounts.php 2023-02-14 21:51:54 +01:00
slawkens
c88156802a fix pages not found 2023-02-14 21:51:46 +01:00
slawkens
7d8dbcbde7 fixes to account number part 3 2023-02-14 19:40:55 +01:00
slawkens
66ec66b291 Allow TinyMCE to resize horizontally and vertically 2023-02-14 18:47:56 +01:00
slawkens
fc0eb0e793 add missing hook 2023-02-14 18:40:46 +01:00
slawkens
ed7e9e1eae fixes to account number part 2 2023-02-14 18:40:31 +01:00
slawkens
8985917a96 Fixes to account number 2023-02-14 18:28:31 +01:00
slawkens
3a3411c117 New hooks for admin page, for head, body, and before_page
+move LOGIN_POST to correct place
2023-02-07 16:27:02 +01:00
slawkens
1166ddfe87 Remove google recaptcha from code
will be included as plugin. This allows for custom recaptcha's
2023-02-07 15:20:24 +01:00
slawkens
574e361f90 fix warning 2023-02-07 12:03:18 +01:00
slawkens
f3745a2752 Feature/new router (#165)
* Remove unneeded escape

* Fix guild back buttons (change logo & motd)

* small adjustment in news.php

* Fix create character when admin (any case is allowed now)

* Fix forum table style (boards & thread view)

* Small improvement to plugins.enabled check

* [WIP] nikic/fast-route implementation

I will describe it more in Pull Request

* Optimisations & fixes.

* Fix path - should not be absolute

* Add PLUGINS to Twig path

* Don't hide "Install Plugin" Box by default

* Update package-lock.json

* nothing important, just early exit & fixes

Fix creature display

* fix premium_ends_at for tfs 1.3+

* Move pages

* Move pages tbc

* $db->select: make $where parameter optional, allows to get all records

* Add some error box to error

* fix parse error

* Rewriting the router v2

To be more flexible

* small fixes

* fix & add admin icons

* Move mass_* pages to correct folder

* fix logout hook 2

* Delete accountmanagement.php

* This code wasn't used

* Add missing var

* Add redirect_from && redirect_to to router options

+ Also add * for all methods shortcut

* Remove comments

Not allowed in normal json

* Allow admin pages included into plugins dir

* block access to some files

* Fix admin logout

* Fix #178

* feature: mail confirmed reward

Suggested by @EPuncker

# Conflicts:
#	system/hooks.php

* remove misleading comment

* adjust required version according to composer.json

* fix duplicated word

* Adjustments & fixed to mass actions

* Add password confirm, and change text type to password

* Add list of Open Source Software MyAAC is using

* Fix signature

* Show First, Second instead of numbers

* fix base dir detection

* fix double ACTION define + undefined URI in template

* new function> escapeHtml + fix css in admin menus

* fix changelog add

* fix news adding, rename const to NEWS_*

* Add verify to pages, add messages, limits, fix add

* fix "Please fill all input"

* add required input to admin pages

* shorten some expressions with ??

* shorten code + fix conversion (int)

* Move account_types to config, account.web_flags to common.php

* Update example.json

* feature: router aliases

* shorten some code + const convert

* remove wrong char

* fix signature on custom basedir

* fix: mass teleport position validation (#214)

* fix: mass teleport position validation

* fix: max position

* Fix execute in CLI

* fix warning in reload cache in dev mode

* Configurable admin panel folder

* feature: plugin require more options with comma

* $config_account_salt -> USE_ACCOUNT_SALT

* fix forum show_thread

* Update show_thread.php

---------

Co-authored-by: Gabriel Pedro <gpedro@users.noreply.github.com>
2023-02-07 11:41:05 +01:00
115 changed files with 4079 additions and 1885 deletions

6
.gitattributes vendored
View File

@@ -3,8 +3,12 @@
.gitignore export-ignore .gitignore export-ignore
.github export-ignore .github export-ignore
.editorconfig export-ignore .editorconfig export-ignore
.travis.yml export-ignore
_config.yml export-ignore _config.yml export-ignore
release.sh export-ignore release.sh export-ignore
# cypress
cypress export-ignore
cypress.config.js export-ignore
cypress.env.json
*.sh text eol=lf *.sh text eol=lf

120
.github/workflows/cypress.yml vendored Normal file
View File

@@ -0,0 +1,120 @@
name: Cypress
on:
pull_request:
branches: [develop]
push:
branches: [develop]
jobs:
cypress:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: myaac
MYSQL_USER: myaac
MYSQL_PASSWORD: myaac
ports:
- 3306/tcp
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: false
matrix:
php-versions: [ '7.4', '8.0', '8.1' ]
name: MyAAC on PHP ${{ matrix.php-versions }}
steps:
- name: 📌 MySQL Start & init & show db
run: |
sudo /etc/init.d/mysql start
mysql -e 'CREATE DATABASE myaac;' -uroot -proot
mysql -e "SHOW DATABASES" -uroot -proot
- name: Checkout MyAAC
uses: actions/checkout@v3
with:
ref: develop
- name: Checkout TFS
uses: actions/checkout@v3
with:
repository: otland/forgottenserver
ref: 1.4
path: tfs
- name: Import TFS Schema
run: |
mysql -uroot -proot myaac < tfs/schema.sql
- name: Rename config.lua
run: mv tfs/config.lua.dist tfs/config.lua
- name: Replace mysqlUser
uses: jacobtomlinson/gha-find-replace@v2
with:
find: 'mysqlUser = "forgottenserver"'
replace: 'mysqlUser = "root"'
regex: false
include: 'tfs/config.lua'
- name: Replace mysqlPass
uses: jacobtomlinson/gha-find-replace@v2
with:
find: 'mysqlPass = ""'
replace: 'mysqlPass = "root"'
regex: false
include: 'tfs/config.lua'
- name: Replace mysqlDatabase
uses: jacobtomlinson/gha-find-replace@v2
with:
find: 'mysqlDatabase = "forgottenserver"'
replace: 'mysqlDatabase = "myaac"'
regex: false
include: 'tfs/config.lua'
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, dom, fileinfo, mysql, json, xml, pdo, pdo_mysql
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Run PHP server
run: nohup php -S localhost:8080 > php.log 2>&1 &
- name: Cypress Run
uses: cypress-io/github-action@v5
env:
CYPRESS_URL: http://localhost:8080
CYPRESS_SERVER_PATH: /home/runner/work/myaac/myaac/tfs
- name: Save screenshots
uses: actions/upload-artifact@v3
if: always()
with:
name: cypress-screenshots
path: cypress/screenshots
- name: Upload Cypress Videos
uses: actions/upload-artifact@v3
if: always()
with:
name: cypress-videos
path: cypress/videos

View File

@@ -1,13 +1,16 @@
name: PHP Linting name: PHP Linting
on: on:
pull_request: pull_request:
branches: [master, develop] branches: [develop]
push: push:
branches: [master] branches: [develop]
jobs: jobs:
phplint: phplint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v3
- uses: michaelw90/PHP-Lint@master - uses: overtrue/phplint@8.2
with:
path: .
options: --exclude=*.log

4
.gitignore vendored
View File

@@ -9,6 +9,10 @@ vendor
# npm # npm
node_modules node_modules
# cypress
cypress.env.json
cypress/e2e/2-advanced-examples
# created by release.sh # created by release.sh
releases releases
tmp tmp

View File

@@ -6,10 +6,14 @@
Options -MultiViews Options -MultiViews
</IfModule> </IfModule>
<FilesMatch "^(CHANGELOG\.md|README\.md|composer\.json|composer\.lock|package\.json|package-lock\.json|cypress\.env\.json)$">
Require all denied
</FilesMatch>
<IfModule mod_rewrite.c> <IfModule mod_rewrite.c>
RewriteEngine On RewriteEngine On
#RewriteBase /myaac/ #RewriteBase /myaac/
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-d

View File

@@ -1,18 +0,0 @@
language: php
php:
- 7.1
- 7.2
- 7.3
- 7.4
- 8.0
cache:
directories:
- $HOME/.composer/cache
before_script:
- 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 --exclude "system/libs/pot/OTS_DB_PDOQuery.php" .

View File

@@ -1,9 +1,55 @@
# Changelog # Changelog
## [0.9.0 - x.x.2020] ## [0.9.0-alpha - 02.06.2023]
Minimum PHP version for this release is 7.2.5.
### Added ### Added
* reworked Admin Panel (@Leesneaks, @gpedro, @slawkens)
* updated to Bootstrap v4
* new Menu
* new Dashboard: statistics, server status
* new Admin Bar showed on top when admin logged in
* new page: Server Data, to reload server data
* new pages: mass account & teleport tools
* changelogs editor
* revised Accounts & Players editors
* option to add/modify menus with plugins
* option to enable/disable plugins
* better, updated TinyMCE editor (v6.x)
* with option to upload images
* list of open source libraries used in project
* brand new charming installation page (by @fernandomatos)
* using Bootstrap
* new pages router: nikic/fast-route, allowing for better customisation
* Guild Wars support (available as plugin)
* support for login and create account only by email (configurable)
* with no need for account name
* Google ReCAPTCHA v3 support (available as plugin)
* automatically load towns names from .OTBM file
* support for Account Number
* suggest account number option
* many new functions, hooks and configurables
* better Exception Handler (Whoops - https://github.com/filp/whoops)
* add Cypress testing
### Changed ### Changed
* Composer is now used for external libraries like: Twig, PHPMailer, fast-route etc.
* mail support is disabled on fresh install, can be manually enabled by user
* disable add php pages in admin panel for security. Option to disable plugins upload
* visitors counter shows now user browser, and also if its bot
* changes in required and optional PHP extensions
* reworked Pages:
* Bans
* works now for TFS 1.x
* Highscores
* frags works for TFS 1.x
* cached
* creatures
* moved pages to Twig:
* experience stages
* update player_deaths entries on name change
* change_password email to be more informal
### Fixed ### Fixed
* hundrets of bug fixes, mostly patched from 0.8, so it makes no sense writing them again here

View File

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

View File

@@ -36,7 +36,7 @@ Official website: https://my-aac.org
chmod 660 images/guilds chmod 660 images/guilds
chmod 660 images/houses chmod 660 images/houses
chmod 660 images/gallery chmod 660 images/gallery
chmod -R 770 system/cache chmod -R 760 system/cache
Visit http://your_domain/install (http://localhost/install) and follow instructions in the browser. Visit http://your_domain/install (http://localhost/install) and follow instructions in the browser.

View File

@@ -29,6 +29,11 @@ define('PAGE', $page);
require SYSTEM . 'functions.php'; require SYSTEM . 'functions.php';
require SYSTEM . 'init.php'; require 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.');
}
if(config('env') === 'dev') { if(config('env') === 'dev') {
ini_set('display_errors', 1); ini_set('display_errors', 1);
ini_set('display_startup_errors', 1); ini_set('display_startup_errors', 1);
@@ -70,7 +75,9 @@ if(!@file_exists($file)) {
} }
ob_start(); ob_start();
include($file); if($hooks->trigger(HOOK_ADMIN_BEFORE_PAGE)) {
require $file;
}
$content .= ob_get_contents(); $content .= ob_get_contents();
ob_end_clean(); ob_end_clean();

View File

@@ -10,12 +10,17 @@
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
$title = 'Account editor'; $title = 'Account editor';
$admin_base = BASE_URL . 'admin/?p=accounts'; $admin_base = ADMIN_URL . '?p=accounts';
$use_datatable = true; $use_datatable = true;
if ($config['account_country']) if ($config['account_country'])
require SYSTEM . 'countries.conf.php'; require SYSTEM . 'countries.conf.php';
$nameOrNumberColumn = 'name';
if (USE_ACCOUNT_NUMBER) {
$nameOrNumberColumn = 'number';
}
$hasSecretColumn = $db->hasColumn('accounts', 'secret'); $hasSecretColumn = $db->hasColumn('accounts', 'secret');
$hasCoinsColumn = $db->hasColumn('accounts', 'coins'); $hasCoinsColumn = $db->hasColumn('accounts', 'coins');
$hasPointsColumn = $db->hasColumn('accounts', 'premium_points'); $hasPointsColumn = $db->hasColumn('accounts', 'premium_points');
@@ -48,16 +53,16 @@ else if (isset($_REQUEST['search'])) {
if (strlen($search_account) < 3 && !Validator::number($search_account)) { if (strlen($search_account) < 3 && !Validator::number($search_account)) {
echo_error('Player name is too short.'); echo_error('Player name is too short.');
} else { } else {
$query = $db->query('SELECT `id` FROM `accounts` WHERE `name` = ' . $db->quote($search_account)); $query = $db->query('SELECT `id` FROM `accounts` WHERE `' . $nameOrNumberColumn . '` = ' . $db->quote($search_account));
if ($query->rowCount() == 1) { if ($query->rowCount() == 1) {
$query = $query->fetch(); $query = $query->fetch();
$id = (int)$query['id']; $id = (int)$query['id'];
} else { } else {
$query = $db->query('SELECT `id`, `name` FROM `accounts` WHERE `name` LIKE ' . $db->quote('%' . $search_account . '%')); $query = $db->query('SELECT `id`, `' . $nameOrNumberColumn . '` FROM `accounts` WHERE `' . $nameOrNumberColumn . '` LIKE ' . $db->quote('%' . $search_account . '%'));
if ($query->rowCount() > 0 && $query->rowCount() <= 10) { if ($query->rowCount() > 0 && $query->rowCount() <= 10) {
$str_construct = 'Do you mean?<ul class="mb-0">'; $str_construct = 'Do you mean?<ul class="mb-0">';
foreach ($query as $row) foreach ($query as $row)
$str_construct .= '<li><a href="' . $admin_base . '&id=' . $row['id'] . '">' . $row['name'] . '</a></li>'; $str_construct .= '<li><a href="' . $admin_base . '&id=' . $row['id'] . '">' . $row[$nameOrNumberColumn] . '</a></li>';
$str_construct .= '</ul>'; $str_construct .= '</ul>';
echo_error($str_construct); echo_error($str_construct);
} else if ($query->rowCount() > 10) } else if ($query->rowCount() > 10)
@@ -145,7 +150,7 @@ else if (isset($_REQUEST['search'])) {
$web_lastlogin = strtotime($_POST['web_lastlogin']); $web_lastlogin = strtotime($_POST['web_lastlogin']);
verify_number($web_lastlogin, 'Web Last login', 11); verify_number($web_lastlogin, 'Web Last login', 11);
if (!$error) { if (!$error && $hooks->trigger(HOOK_ADMIN_ACCOUNTS_SAVE_POST, ['account_id' => $account->getId(), 'account_email' => $account->getEMail()])) {
if (USE_ACCOUNT_NAME) { if (USE_ACCOUNT_NAME) {
$account->setName($name); $account->setName($name);
} }
@@ -203,7 +208,7 @@ else if (isset($_REQUEST['search'])) {
} }
} }
} else if ($id == 0) { } else if ($id == 0) {
$accounts_db = $db->query('SELECT `id`, `name`' . ($hasTypeColumn ? ',type' : ($hasGroupColumn ? ',group_id' : '')) . ' FROM `accounts` ORDER BY `id` ASC'); $accounts_db = $db->query('SELECT `id`, `' . $nameOrNumberColumn . '`' . ($hasTypeColumn ? ',type' : ($hasGroupColumn ? ',group_id' : '')) . ' FROM `accounts` ORDER BY `id` ASC');
?> ?>
<div class="col-12 col-sm-12 col-lg-10"> <div class="col-12 col-sm-12 col-lg-10">
<div class="card card-info card-outline"> <div class="card card-info card-outline">
@@ -215,7 +220,7 @@ else if (isset($_REQUEST['search'])) {
<thead> <thead>
<tr> <tr>
<th>ID</th> <th>ID</th>
<th>Name</th> <th><?= ($nameOrNumberColumn == 'number' ? 'Number' : 'Name'); ?></th>
<?php if($hasTypeColumn || $hasGroupColumn): ?> <?php if($hasTypeColumn || $hasGroupColumn): ?>
<th>Position</th> <th>Position</th>
<?php endif; ?> <?php endif; ?>
@@ -226,7 +231,7 @@ else if (isset($_REQUEST['search'])) {
<?php foreach ($accounts_db as $account_lst): ?> <?php foreach ($accounts_db as $account_lst): ?>
<tr> <tr>
<th><?php echo $account_lst['id']; ?></th> <th><?php echo $account_lst['id']; ?></th>
<td><?php echo $account_lst['name']; ?></a></td> <td><?php echo $account_lst[$nameOrNumberColumn]; ?></a></td>
<?php if($hasTypeColumn || $hasGroupColumn): ?> <?php if($hasTypeColumn || $hasGroupColumn): ?>
<td> <td>
<?php if ($hasTypeColumn) { <?php if ($hasTypeColumn) {
@@ -284,6 +289,11 @@ else if (isset($_REQUEST['search'])) {
<label for="name">Account Name:</label> <label for="name">Account Name:</label>
<input type="text" class="form-control" id="name" name="name" autocomplete="off" value="<?php echo $account->getName(); ?>"/> <input type="text" class="form-control" id="name" name="name" autocomplete="off" value="<?php echo $account->getName(); ?>"/>
</div> </div>
<?php elseif (USE_ACCOUNT_NUMBER): ?>
<div class="col-12 col-sm-12 col-lg-4">
<label for="name">Account Number:</label>
<input type="text" class="form-control" id="name" name="name" autocomplete="off" value="<?php echo $account->getNumber(); ?>"/>
</div>
<?php endif; ?> <?php endif; ?>
<div class="col-12 col-sm-12 col-lg-5"> <div class="col-12 col-sm-12 col-lg-5">
<div class="form-check"> <div class="form-check">

View File

@@ -10,6 +10,12 @@
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
$title = 'Login'; $title = 'Login';
require PAGES . 'account/login.php';
if ($logged) {
header('Location: ' . ADMIN_URL);
return;
}
$twig->display('admin.login.html.twig', [ $twig->display('admin.login.html.twig', [
'logout' => (ACTION == 'logout' ? 'You have been logged out!' : ''), 'logout' => (ACTION == 'logout' ? 'You have been logged out!' : ''),
'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number', 'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number',

View File

@@ -16,7 +16,7 @@ if (!hasFlag(FLAG_CONTENT_MAILER) && !superAdmin()) {
} }
if (!config('mail_enabled')) { if (!config('mail_enabled')) {
echo 'Mail support disabled.'; echo 'Mail support disabled in config.';
return; return;
} }

View File

@@ -76,18 +76,18 @@ if (!empty($action)) {
$enable_tinymce = $_page['enable_tinymce'] == '1'; $enable_tinymce = $_page['enable_tinymce'] == '1';
$access = $_page['access']; $access = $_page['access'];
} else { } else {
if(Pages::update($id, $name, $p_title, $body, $player_id, $php, $enable_tinymce, $access)) { if(Pages::update($id, $name, $p_title, $body, $player_id, $php, $enable_tinymce, $access, $errors)) {
$action = $name = $p_title = $body = ''; $action = $name = $p_title = $body = '';
$player_id = 1; $player_id = 1;
$access = 0; $access = 0;
$php = false; $php = false;
$enable_tinymce = true; $enable_tinymce = true;
success("Updated successful."); success('Updated successful.');
} }
} }
} else if ($action == 'hide') { } else if ($action == 'hide') {
Pages::toggleHidden($id, $errors, $status); Pages::toggleHidden($id, $errors, $status);
success(($status == 1 ? 'Show' : 'Hide') . " successful."); success(($status == 1 ? 'Show' : 'Hide') . ' successful.');
} }
if (!empty($errors)) if (!empty($errors))
@@ -152,6 +152,10 @@ class Pages
$errors[] = 'Enable PHP is wrong.'; $errors[] = 'Enable PHP is wrong.';
return false; return false;
} }
if ($php == 1 && !getBoolean(config('admin_pages_php_enable'))) {
$errors[] = 'PHP pages disabled on this server. To enable go to config.php and change admin_pages_php_enable to "yes".';
return false;
}
if(!isset($enable_tinymce) || ($enable_tinymce != 0 && $enable_tinymce != 1)) { if(!isset($enable_tinymce) || ($enable_tinymce != 0 && $enable_tinymce != 1)) {
$errors[] = 'Enable TinyMCE is wrong.'; $errors[] = 'Enable TinyMCE is wrong.';
return false; return false;
@@ -200,7 +204,7 @@ class Pages
return !count($errors); return !count($errors);
} }
static public function update($id, $name, $title, $body, $player_id, $php, $enable_tinymce, $access) static public function update($id, $name, $title, $body, $player_id, $php, $enable_tinymce, $access, &$errors)
{ {
if(!self::verify($name, $title, $body, $player_id, $php, $enable_tinymce, $access, $errors)) { if(!self::verify($name, $title, $body, $player_id, $php, $enable_tinymce, $access, $errors)) {
return false; return false;

View File

@@ -16,4 +16,4 @@ if (!function_exists('phpinfo')) { ?>
<?php return; <?php return;
} }
?> ?>
<iframe src="<?php echo BASE_URL; ?>admin/tools/phpinfo.php" width="1024" height="550"></iframe> <iframe src="<?php echo ADMIN_URL; ?>tools/phpinfo.php" width="1024" height="550"></iframe>

View File

@@ -10,7 +10,7 @@
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
$title = 'Player editor'; $title = 'Player editor';
$player_base = BASE_URL . 'admin/?p=players'; $player_base = ADMIN_URL . '?p=players';
$use_datatable = true; $use_datatable = true;
require_once LIBS . 'forum.php'; require_once LIBS . 'forum.php';
@@ -663,7 +663,14 @@ else if (isset($_REQUEST['search'])) {
</div> </div>
<div class="col-12 col-sm-12 col-lg-6"> <div class="col-12 col-sm-12 col-lg-6">
<label for="lastip" class="control-label">Last IP:</label> <label for="lastip" class="control-label">Last IP:</label>
<input type="text" class="form-control" id="lastip" name="lastip" autocomplete="off" maxlength="10" value="<?php echo longToIp($player->getLastIP()); ?>" readonly/> <input type="text" class="form-control" id="lastip" name="lastip" autocomplete="off" maxlength="10" value="<?php
if (strlen($player->getLastIP()) > 11) {
echo inet_ntop($player->getLastIP());
}
else {
echo longToIp($player->getLastIP());
}
?>" readonly/>
</div> </div>
</div> </div>
<?php if ($db->hasColumn('players', 'loss_experience')): ?> <?php if ($db->hasColumn('players', 'loss_experience')): ?>

View File

@@ -13,98 +13,119 @@ $use_datatable = true;
require_once LIBS . 'plugins.php'; require_once LIBS . 'plugins.php';
$twig->display('admin.plugins.form.html.twig'); if (!getBoolean(config('admin_plugins_manage_enable'))) {
warning('Plugin installation and management is disabled in config.<br/>If you wish to enable, go to config.php and change <b>admin_plugins_manage_enable</b> to "yes".');
}
else {
$twig->display('admin.plugins.form.html.twig');
if (isset($_REQUEST['uninstall'])) { if (isset($_REQUEST['uninstall'])) {
$uninstall = $_REQUEST['uninstall']; $uninstall = $_REQUEST['uninstall'];
if (Plugins::uninstall($uninstall)) { if (Plugins::uninstall($uninstall)) {
success('Successfully uninstalled plugin ' . $uninstall); success('Successfully uninstalled plugin ' . $uninstall);
} else { } else {
error('Error while uninstalling plugin ' . $uninstall . ': ' . Plugins::getError()); error('Error while uninstalling plugin ' . $uninstall . ': ' . Plugins::getError());
}
} else if (isset($_FILES["plugin"]["name"])) {
$file = $_FILES["plugin"];
$filename = $file["name"];
$tmp_name = $file["tmp_name"];
$type = $file["type"];
$name = explode(".", $filename);
$accepted_types = array('application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed', 'application/octet-stream', 'application/zip-compressed');
if (isset($file['error'])) {
$error = 'Error uploading file';
switch ($file['error']) {
case UPLOAD_ERR_OK:
$error = false;
break;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$error .= ' - file too large (limit of ' . ini_get('upload_max_filesize') . ' bytes). You can enlarge the limits by changing "upload_max_filesize" in php.ini';
break;
case UPLOAD_ERR_PARTIAL:
$error .= ' - file upload was not completed.';
break;
case UPLOAD_ERR_NO_FILE:
$error .= ' - zero-length file uploaded.';
break;
default:
$error .= ' - internal error #' . $file['error'];
break;
} }
} } else if (isset($_REQUEST['enable'])) {
$enable = $_REQUEST['enable'];
if (Plugins::enable($enable)) {
success('Successfully enabled plugin ' . $enable);
} else {
error('Error while enabling plugin ' . $enable . ': ' . Plugins::getError());
}
} else if (isset($_REQUEST['disable'])) {
$disable = $_REQUEST['disable'];
if (Plugins::disable($disable)) {
success('Successfully disabled plugin ' . $disable);
} else {
error('Error while disabling plugin ' . $disable . ': ' . Plugins::getError());
}
} else if (isset($_FILES['plugin']['name'])) {
$file = $_FILES['plugin'];
$filename = $file['name'];
$tmp_name = $file['tmp_name'];
$type = $file['type'];
if (isset($error) && $error != false) { $name = explode('.', $filename);
error($error); $accepted_types = array('application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed', 'application/octet-stream', 'application/zip-compressed');
} else {
if (is_uploaded_file($file['tmp_name'])) {
$filetype = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if ($filetype == 'zip') // check if it is zipped/compressed file
{
$tmp_filename = pathinfo($filename, PATHINFO_FILENAME);
$targetzip = BASE . 'plugins/' . $tmp_filename . '.zip';
if (move_uploaded_file($tmp_name, $targetzip)) { // move uploaded file if (isset($file['error'])) {
if (Plugins::install($targetzip)) { $error = 'Error uploading file';
foreach (Plugins::getWarnings() as $warning) { switch ($file['error']) {
warning($warning); case UPLOAD_ERR_OK:
$error = false;
break;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$error .= ' - file too large (limit of ' . ini_get('upload_max_filesize') . ' bytes). You can enlarge the limits by changing "upload_max_filesize" in php.ini';
break;
case UPLOAD_ERR_PARTIAL:
$error .= ' - file upload was not completed.';
break;
case UPLOAD_ERR_NO_FILE:
$error .= ' - zero-length file uploaded.';
break;
default:
$error .= ' - internal error #' . $file['error'];
break;
}
}
if (isset($error) && $error != false) {
error($error);
} else {
if (is_uploaded_file($file['tmp_name'])) {
$filetype = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if ($filetype == 'zip') // check if it is zipped/compressed file
{
$tmp_filename = pathinfo($filename, PATHINFO_FILENAME);
$targetzip = BASE . 'plugins/' . $tmp_filename . '.zip';
if (move_uploaded_file($tmp_name, $targetzip)) { // move uploaded file
if (Plugins::install($targetzip)) {
foreach (Plugins::getWarnings() as $warning) {
warning($warning);
}
$info = Plugins::getPluginJson();
success((isset($info['name']) ? '<strong>' . $info['name'] . '</strong> p' : 'P') . 'lugin has been successfully installed.');
} else {
$error = Plugins::getError();
error(!empty($error) ? $error : 'Unexpected error happened while installing plugin. Please try again later.');
} }
$info = Plugins::getPluginJson(); unlink($targetzip); // delete the Zipped file
success((isset($info['name']) ? '<strong>' . $info['name'] . '</strong> p' : 'P') . 'lugin has been successfully installed.'); } else
} else { error('There was a problem with the upload. Please try again.');
$error = Plugins::getError(); } else {
error(!empty($error) ? $error : 'Unexpected error happened while installing plugin. Please try again later.'); error('The file you are trying to upload is not a .zip file. Please try again.');
} }
unlink($targetzip); // delete the Zipped file
} else
error('There was a problem with the upload. Please try again.');
} else { } else {
error('The file you are trying to upload is not a .zip file. Please try again.'); error('Error uploading file - unknown error.');
} }
} else {
error('Error uploading file - unknown error.');
} }
} }
} }
$plugins = array(); $plugins = array();
foreach (get_plugins() as $plugin) { foreach (get_plugins(true) as $plugin) {
$string = file_get_contents(BASE . 'plugins/' . $plugin . '.json'); $string = file_get_contents(BASE . 'plugins/' . $plugin . '.json');
$string = Plugins::removeComments($string);
$plugin_info = json_decode($string, true); $plugin_info = json_decode($string, true);
if ($plugin_info == false) { if (!$plugin_info) {
warning('Cannot load plugin info ' . $plugin . '.json'); warning('Cannot load plugin info ' . $plugin . '.json');
} else { } else {
$disabled = (strpos($plugin, 'disabled.') !== false);
$pluginOriginal = ($disabled ? str_replace('disabled.', '', $plugin) : $plugin);
$plugins[] = array( $plugins[] = array(
'name' => isset($plugin_info['name']) ? $plugin_info['name'] : '', 'name' => $plugin_info['name'] ?? '',
'description' => isset($plugin_info['description']) ? $plugin_info['description'] : '', 'description' => $plugin_info['description'] ?? '',
'version' => isset($plugin_info['version']) ? $plugin_info['version'] : '', 'version' => $plugin_info['version'] ?? '',
'author' => isset($plugin_info['author']) ? $plugin_info['author'] : '', 'author' => $plugin_info['author'] ?? '',
'contact' => isset($plugin_info['contact']) ? $plugin_info['contact'] : '', 'contact' => $plugin_info['contact'] ?? '',
'file' => $plugin, 'file' => $pluginOriginal,
'enabled' => !$disabled,
'uninstall' => isset($plugin_info['uninstall']) 'uninstall' => isset($plugin_info['uninstall'])
); );
} }

View File

@@ -8,6 +8,11 @@
* @link https://my-aac.org * @link https://my-aac.org
*/ */
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
use DeviceDetector\DeviceDetector;
use DeviceDetector\Parser\Client\Browser;
use DeviceDetector\Parser\OperatingSystem;
$title = 'Visitors'; $title = 'Visitors';
$use_datatable = true; $use_datatable = true;
@@ -30,6 +35,31 @@ function compare($a, $b)
$tmp = $visitors->getVisitors(); $tmp = $visitors->getVisitors();
usort($tmp, 'compare'); usort($tmp, 'compare');
foreach ($tmp as &$visitor) {
$userAgent = $visitor['user_agent'] ?? '';
if (!strlen($userAgent) || $userAgent == 'unknown') {
$browser = 'Unknown';
}
else {
$dd = new DeviceDetector($userAgent);
$dd->parse();
if ($dd->isBot()) {
$bot = $dd->getBot();
$message = '(Bot) %s, <a href="%s" target="_blank">%s</a>';
$browser = sprintf($message, $bot['category'], $bot['url'], $bot['name']);
}
else {
$osFamily = OperatingSystem::getOsFamily($dd->getOs('name'));
$browserFamily = Browser::getBrowserFamily($dd->getClient('name'));
$browser = $osFamily . ', ' . $browserFamily;
}
}
$visitor['browser'] = $browser;
}
$twig->display('admin.visitors.html.twig', array( $twig->display('admin.visitors.html.twig', array(
'config_visitors_counter_ttl' => $config['visitors_counter_ttl'], 'config_visitors_counter_ttl' => $config['visitors_counter_ttl'],
'visitors' => $tmp 'visitors' => $tmp

View File

@@ -2,6 +2,7 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<?php $hooks->trigger(HOOK_ADMIN_HEAD_START); ?>
<?php echo template_header(true); ?> <?php echo template_header(true); ?>
<title><?php echo (isset($title) ? $title . ' - ' : '') . $config['lua']['serverName'];?></title> <title><?php echo (isset($title) ? $title . ' - ' : '') . $config['lua']['serverName'];?></title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
@@ -16,8 +17,10 @@
<script src="<?php echo BASE_URL; ?>tools/js/respond.min.js"></script> <script src="<?php echo BASE_URL; ?>tools/js/respond.min.js"></script>
<![endif]--> <![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">
<?php $hooks->trigger(HOOK_ADMIN_HEAD_END); ?>
</head> </head>
<body class="sidebar-mini "> <body class="sidebar-mini ">
<?php $hooks->trigger(HOOK_ADMIN_BODY_START); ?>
<?php if ($logged && admin()) { ?> <?php if ($logged && admin()) { ?>
<div class="wrapper"> <div class="wrapper">
<nav class="main-header navbar navbar-expand navbar-white navbar-light"> <nav class="main-header navbar navbar-expand navbar-white navbar-light">
@@ -195,5 +198,6 @@ if ($logged && admin()) {
<script src="<?php echo BASE_URL; ?>tools/js/datatables.bs.min.js"></script> <script src="<?php echo BASE_URL; ?>tools/js/datatables.bs.min.js"></script>
<?php } ?> <?php } ?>
<script src="<?php echo BASE_URL; ?>tools/js/adminlte.min.js"></script> <script src="<?php echo BASE_URL; ?>tools/js/adminlte.min.js"></script>
<?php $hooks->trigger(HOOK_ADMIN_BODY_END); ?>
</body> </body>
</html> </html>

View File

@@ -26,8 +26,8 @@
if (version_compare(phpversion(), '7.2.5', '<')) die('PHP version 7.2.5 or higher is required.'); if (version_compare(phpversion(), '7.2.5', '<')) die('PHP version 7.2.5 or higher is required.');
const MYAAC = true; const MYAAC = true;
const MYAAC_VERSION = '0.9.0-dev'; const MYAAC_VERSION = '0.9.0-alpha';
const DATABASE_VERSION = 33; const DATABASE_VERSION = 35;
const TABLE_PREFIX = 'myaac_'; const TABLE_PREFIX = 'myaac_';
define('START_TIME', microtime(true)); define('START_TIME', microtime(true));
define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX')); define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX'));
@@ -138,11 +138,9 @@ if(!IS_CLI) {
define('SERVER_URL', 'http' . (isset($_SERVER['HTTPS'][0]) && strtolower($_SERVER['HTTPS']) === 'on' ? 's' : '') . '://' . $baseHost); define('SERVER_URL', 'http' . (isset($_SERVER['HTTPS'][0]) && strtolower($_SERVER['HTTPS']) === 'on' ? 's' : '') . '://' . $baseHost);
define('BASE_URL', SERVER_URL . BASE_DIR . '/'); define('BASE_URL', SERVER_URL . BASE_DIR . '/');
define('ADMIN_URL', SERVER_URL . BASE_DIR . '/admin/'); define('ADMIN_URL', SERVER_URL . BASE_DIR . '/' . ADMIN_PANEL_FOLDER . '/');
//define('CURRENT_URL', BASE_URL . $_SERVER['REQUEST_URI']); //define('CURRENT_URL', BASE_URL . $_SERVER['REQUEST_URI']);
require SYSTEM . 'exception.php';
} }
$autoloadFile = VENDOR . 'autoload.php'; $autoloadFile = VENDOR . 'autoload.php';

View File

@@ -10,6 +10,10 @@
"composer/semver": "^3.2", "composer/semver": "^3.2",
"twig/twig": "^2.0", "twig/twig": "^2.0",
"erusev/parsedown": "^1.7", "erusev/parsedown": "^1.7",
"nikic/fast-route": "^1.3" "nikic/fast-route": "^1.3",
"matomo/device-detector": "^6.0"
},
"require-dev": {
"filp/whoops": "^2.15"
} }
} }

View File

@@ -142,18 +142,6 @@ $config = array(
'smtp_secure' => '', // What kind of encryption to use on the SMTP connection. Options: '', 'ssl' (GMail) or 'tls' (Microsoft Outlook) '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) 'smtp_debug' => false, // set true to debug (you will see more info in error.log)
// 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' => '',
// 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) '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)
'generate_new_reckey_price' => 20, // price for new recovery key 'generate_new_reckey_price' => 20, // price for new recovery key
@@ -311,6 +299,10 @@ $config = array(
'status_interval' => 60, 'status_interval' => 60,
// admin panel // admin panel
'admin_plugins_manage_enable' => 'yes', // you can disable possibility to upload and uninstall plugins, for security
// enable support for plain php pages in admin panel, for security
// existing pages still will be working, so you need to delete them manually
'admin_pages_php_enable' => 'no',
'admin_panel_modules' => 'statistics,web_status,server_status,lastlogin,created,points,coins,balance', // default - statistics,web_status,server_status,lastlogin,created,points,coins,balance '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 // other

9
cypress.config.js Normal file
View File

@@ -0,0 +1,9 @@
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});

View File

@@ -0,0 +1,75 @@
describe('Install MyAAC', () => {
beforeEach(() => {
// Cypress starts out with a blank slate for each test
// so we must tell it to visit our website with the `cy.visit()` command.
// Since we want to visit the same URL at the start of all our tests,
// we include it in our beforeEach function so that it runs before each test
cy.visit(Cypress.env('URL'))
})
it('Go through installer', () => {
cy.visit(Cypress.env('URL') + '/install/?step=welcome')
cy.wait(1000)
cy.screenshot('install-welcome')
// step 1 - Welcome
cy.get('select[name="lang"]').select('en')
//cy.get('input[type=button]').contains('Next »').click()
cy.get('form').submit()
// step 2 - License
// just skip
cy.contains('GNU/GPL License');
cy.get('form').submit()
// step 3 - Requirements
cy.contains('Requirements check');
cy.get('#step').then(elem => {
elem.val('config');
});
cy.get('form').submit()
// step 4 - Configuration
cy.contains('Basic configuration');
cy.get('#vars_server_path').click().clear().type(Cypress.env('SERVER_PATH'))
cy.get('#vars_mail_admin').click().clear().type('noone@example.net')
cy.get('[type="checkbox"]').uncheck() // usage statistics uncheck
cy.wait(1000)
cy.get('form').submit()
// check if there is any error
// step 5 - Import Schema
cy.contains('Import MySQL schema');
// AAC is not installed yet, this message should not come
cy.contains('Seems AAC is already installed. Skipping importing MySQL schema..').should('not.exist')
cy.contains('[class="alert alert-success"]', 'Local configuration has been saved into file: config.local.php').should('be.visible')
cy.get('form').submit()
// step 6 - Admin Account
cy.get('#vars_email').click().clear().type('admin@my-aac.org')
cy.get('#vars_account').click().clear().type('admin')
cy.get('#vars_password').click().clear().type('test1234')
cy.get('#vars_password_confirm').click().clear().type('test1234')
cy.get('#vars_player_name').click().clear().type('Admin')
cy.get('form').submit()
cy.contains('[class="alert alert-success"]', 'Congratulations', { timeout: 30000 }).should('be.visible')
cy.screenshot('install-finish')
})
})

View File

@@ -0,0 +1,33 @@
describe('Create Account Page', () => {
beforeEach(() => {
// Cypress starts out with a blank slate for each test
// so we must tell it to visit our website with the `cy.visit()` command.
// Since we want to visit the same URL at the start of all our tests,
// we include it in our beforeEach function so that it runs before each test
cy.visit(Cypress.env('URL') + '/index.php/account/create')
})
it('Create Test Account', () => {
cy.screenshot('create-account-page')
cy.get('#account_input').type('tester')
cy.get('#email').type('tester@example.com')
cy.get('#password').type('test1234')
cy.get('#password2').type('test1234')
cy.get('#character_name').type('Slaw')
cy.get('#sex1').check()
cy.get('#vocation1').check()
cy.get('#accept_rules').check()
cy.get('#createaccount').submit()
// no errors please
cy.contains('The Following Errors Have Occurred:').should('not.exist')
// ss of post page
cy.screenshot('create-account-page-post')
})
})

View File

@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

20
cypress/support/e2e.js Normal file
View File

@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@@ -1,4 +1,4 @@
SET @myaac_database_version = 33; SET @myaac_database_version = 35;
CREATE TABLE `myaac_account_actions` CREATE TABLE `myaac_account_actions`
( (
@@ -203,6 +203,7 @@ CREATE TABLE `myaac_monsters` (
`mana` int(11) NOT NULL DEFAULT 0, `mana` int(11) NOT NULL DEFAULT 0,
`exp` int(11) NOT NULL, `exp` int(11) NOT NULL,
`health` int(11) NOT NULL, `health` int(11) NOT NULL,
`look` VARCHAR(255) NOT NULL DEFAULT '',
`speed_lvl` int(11) NOT NULL default 1, `speed_lvl` int(11) NOT NULL default 1,
`use_haste` tinyint(1) NOT NULL, `use_haste` tinyint(1) NOT NULL,
`voices` text NOT NULL, `voices` text NOT NULL,
@@ -330,6 +331,7 @@ CREATE TABLE `myaac_visitors`
`ip` VARCHAR(45) NOT NULL, `ip` VARCHAR(45) NOT NULL,
`lastvisit` INT(11) NOT NULL DEFAULT 0, `lastvisit` INT(11) NOT NULL DEFAULT 0,
`page` VARCHAR(2048) NOT NULL, `page` VARCHAR(2048) NOT NULL,
`user_agent` VARCHAR(255) NOT NULL DEFAULT '',
UNIQUE (`ip`) UNIQUE (`ip`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;

View File

@@ -1,4 +1,4 @@
We have detected that you don't have access to write to the system/cache directory. Under linux you can fix it by using this two command, where first one should be enough (for apache):<br/><br/><span class="console">chown -R www-data.www-data /var/www/*</span><br/><span class="console">chmod -R 660 system/cache</span> We have detected that you don't have access to write to the system/cache directory. Under linux you can fix it by using this two command, where first one should be enough (for apache):<br/><br/><span class="console">chown -R www-data.www-data /var/www/*</span><br/><span class="console">chmod -R 760 system/cache</span>
<style type="text/css"> <style type="text/css">
.console { .console {

View File

@@ -26,13 +26,13 @@ $twig = new Twig_Environment($twig_loader, array(
)); ));
// load installation status // load installation status
$step = isset($_POST['step']) ? $_POST['step'] : 'welcome'; $step = $_REQUEST['step'] ?? 'welcome';
$install_status = array(); $install_status = array();
if(file_exists(CACHE . 'install.txt')) { if(file_exists(CACHE . 'install.txt')) {
$install_status = unserialize(file_get_contents(CACHE . 'install.txt')); $install_status = unserialize(file_get_contents(CACHE . 'install.txt'));
if(!isset($_POST['step'])) { if(!isset($_REQUEST['step'])) {
$step = isset($install_status['step']) ? $install_status['step'] : ''; $step = isset($install_status['step']) ? $install_status['step'] : '';
} }
} }
@@ -70,7 +70,7 @@ if($step == 'database') {
$key = str_replace('var_', '', $key); $key = str_replace('var_', '', $key);
if(in_array($key, array('account', 'password', 'password_confirm', 'email', 'player_name'))) { if(in_array($key, array('account', 'account_id', 'password', 'password_confirm', 'email', 'player_name'))) {
continue; continue;
} }
@@ -110,14 +110,12 @@ if($step == 'database') {
} }
} }
else if($step == 'admin') { else if($step == 'admin') {
$config_failed = true; if(!file_exists(BASE . 'config.local.php') || !isset($config['installed']) || !$config['installed']) {
if(file_exists(BASE . 'config.local.php') && isset($config['installed']) && $config['installed'] && isset($_SESSION['saved'])) {
$config_failed = false;
}
if($config_failed) {
$step = 'database'; $step = 'database';
} }
else {
$_SESSION['saved'] = true;
}
} }
else if($step == 'finish') { else if($step == 'finish') {
$email = $_SESSION['var_email']; $email = $_SESSION['var_email'];

View File

@@ -55,12 +55,30 @@ if(!$error) {
error($database_error); error($database_error);
} }
else { else {
$twig->display('install.installer.html.twig', array( if(!$db->hasTable('accounts')) {
'url' => 'tools/5-database.php', $tmp = str_replace('$TABLE$', 'accounts', $locale['step_database_error_table']);
'message' => $locale['loading_spinner'] error($tmp);
)); $error = true;
}
if(!$db->hasTable('players')) {
$tmp = str_replace('$TABLE$', 'players', $locale['step_database_error_table']);
error($tmp);
$error = true;
}
if(!$db->hasTable('guilds')) {
$tmp = str_replace('$TABLE$', 'guilds', $locale['step_database_error_table']);
error($tmp);
$error = true;
}
if(!$error) { if(!$error) {
$twig->display('install.installer.html.twig', array(
'url' => 'tools/5-database.php',
'message' => $locale['loading_spinner']
));
if(!Validator::email($_SESSION['var_mail_admin'])) { if(!Validator::email($_SESSION['var_mail_admin'])) {
error($locale['step_config_mail_admin_error']); error($locale['step_config_mail_admin_error']);
$error = true; $error = true;
@@ -86,7 +104,7 @@ if(!$error) {
unset($_SESSION['saved']); unset($_SESSION['saved']);
$locale['step_database_error_file'] = str_replace('$FILE$', '<b>' . BASE . 'config.local.php</b>', $locale['step_database_error_file']); $locale['step_database_error_file'] = str_replace('$FILE$', '<b>' . BASE . 'config.local.php</b>', $locale['step_database_error_file']);
warning($locale['step_database_error_file'] . '<br/> error($locale['step_database_error_file'] . '<br/>
<textarea cols="70" rows="10">' . $content . '</textarea>'); <textarea cols="70" rows="10">' . $content . '</textarea>');
} }
} }
@@ -98,7 +116,7 @@ if(!$error) {
<div class="text-center m-3"> <div class="text-center m-3">
<form action="<?php echo BASE_URL; ?>install/" method="post"> <form action="<?php echo BASE_URL; ?>install/" method="post">
<input type="hidden" name="step" id="step" value="admin" /> <input type="hidden" name="step" id="step" value="admin" />
<?php echo next_buttons(true, $error ? false : true); <?php echo next_buttons(true, !$error);
?> ?>
</form> </form>
</div> </div>

View File

@@ -8,7 +8,7 @@ if(isset($config['installed']) && $config['installed'] && !isset($_SESSION['save
else { else {
require SYSTEM . 'init.php'; require SYSTEM . 'init.php';
if(!$error) { if(!$error) {
if(USE_ACCOUNT_NAME) if(USE_ACCOUNT_NAME || USE_ACCOUNT_NUMBER)
$account = isset($_SESSION['var_account']) ? $_SESSION['var_account'] : null; $account = isset($_SESSION['var_account']) ? $_SESSION['var_account'] : null;
else else
$account_id = isset($_SESSION['var_account_id']) ? $_SESSION['var_account_id'] : null; $account_id = isset($_SESSION['var_account_id']) ? $_SESSION['var_account_id'] : null;
@@ -65,7 +65,6 @@ else {
$new_account->setPassword(encrypt($password)); $new_account->setPassword(encrypt($password));
$new_account->setEMail($email); $new_account->setEMail($email);
$new_account->unblock();
$new_account->save(); $new_account->save();
$new_account->setCustomField('created', time()); $new_account->setCustomField('created', time());

View File

@@ -4,14 +4,14 @@
<meta http-equiv="Content-Type" content="text/html; charset=<?php echo $locale['encoding']; ?>" /> <meta http-equiv="Content-Type" content="text/html; charset=<?php echo $locale['encoding']; ?>" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>MyAAC - <?php echo $locale['installation']; ?></title> <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 href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="template/style.css" /> <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> <script type="text/javascript" src="<?php echo BASE_URL; ?>tools/js/jquery.min.js"></script>
</head> </head>
<body> <body>
<div id="body" class="container"> <div id="body" class="container">
<header id="header" class="pt-5 pb-4 pb-sm-5"> <header id="header" class="pt-5 pb-4 pb-sm-5">
<h1>MyAAC <?php echo $locale['installation']; ?></h1> <h1>MyAAC <?php echo $locale['installation']; ?></h1>
</header> </header>
@@ -28,10 +28,10 @@
if ($step == $value) { if ($step == $value) {
$progress = ($i == 6) ? 100 : $i * 16; $progress = ($i == 6) ? 100 : $i * 16;
} }
echo '<li' . ($step == $value ? ' class="list-group-item active"' : ' class="list-group-item"') . '>' . ++$i . '. ' . $locale['step_' . $value] . '</li>'; echo '<li class="list-group-item' . ($step == $value ? ' active' : '') . '">' . ++$i . '. ' . $locale['step_' . $value] . '</li>';
} }
?> ?>
</ul> </ul>
</div> </div>
@@ -71,4 +71,4 @@
<p style="text-align: center;"><?php echo base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4='); ?></p> <p style="text-align: center;"><?php echo base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4='); ?></p>
</footer> </footer>
</body> </body>
</html> </html>

View File

@@ -23,24 +23,6 @@ if(!$error) {
} }
} }
if(!$db->hasTable('accounts')) {
$locale['step_database_error_table'] = str_replace('$TABLE$', 'accounts', $locale['step_database_error_table']);
error($locale['step_database_error_table']);
return;
}
if(!$db->hasTable('players')) {
$locale['step_database_error_table'] = str_replace('$TABLE$', 'players', $locale['step_database_error_table']);
error($locale['step_database_error_table']);
return;
}
if(!$db->hasTable('guilds')) {
$locale['step_database_error_table'] = str_replace('$TABLE$', 'guilds', $locale['step_database_error_table']);
error($locale['step_database_error_table']);
return;
}
if($db->hasTable(TABLE_PREFIX . 'account_actions')) { if($db->hasTable(TABLE_PREFIX . 'account_actions')) {
$locale['step_database_error_table_exist'] = str_replace('$TABLE$', TABLE_PREFIX . 'account_actions', $locale['step_database_error_table_exist']); $locale['step_database_error_table_exist'] = str_replace('$TABLE$', TABLE_PREFIX . 'account_actions', $locale['step_database_error_table_exist']);
warning($locale['step_database_error_table_exist']); warning($locale['step_database_error_table_exist']);
@@ -73,13 +55,8 @@ else {
success($locale['step_database_adding_field'] . ' accounts.key...'); success($locale['step_database_adding_field'] . ' accounts.key...');
} }
if(!$db->hasColumn('accounts', 'blocked')) {
if(query("ALTER TABLE `accounts` ADD `blocked` TINYINT(1) NOT NULL DEFAULT FALSE COMMENT 'internal usage' AFTER `key`;"))
success($locale['step_database_adding_field'] . ' accounts.blocked...');
}
if(!$db->hasColumn('accounts', 'created')) { if(!$db->hasColumn('accounts', 'created')) {
if(query("ALTER TABLE `accounts` ADD `created` INT(11) NOT NULL DEFAULT 0 AFTER `" . ($db->hasColumn('accounts', 'group_id') ? 'group_id' : 'blocked') . "`;")) if(query("ALTER TABLE `accounts` ADD `created` INT(11) NOT NULL DEFAULT 0 AFTER `" . ($db->hasColumn('accounts', 'group_id') ? 'group_id' : 'key') . "`;"))
success($locale['step_database_adding_field'] . ' accounts.created...'); success($locale['step_database_adding_field'] . ' accounts.created...');
} }

View File

@@ -7,6 +7,23 @@ server {
# increase max file upload # increase max file upload
client_max_body_size 10M; client_max_body_size 10M;
# this is very important, be sure its in your nginx conf - it prevents access to logs etc.
location ~ /system {
deny all;
return 404;
}
# block .htaccess
location ~ /\.ht {
deny all;
}
# block git files and folders
location ~ /\.git {
return 404;
deny all;
}
location / { location / {
try_files $uri $uri/ /index.php; try_files $uri $uri/ /index.php;
} }
@@ -15,15 +32,6 @@ server {
include snippets/fastcgi-php.conf; include snippets/fastcgi-php.conf;
fastcgi_read_timeout 240; fastcgi_read_timeout 240;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
# for ubuntu 22.04+ it will be php8.1-sock # for ubuntu 22.04+ it will be php8.1-fpm.-sock
}
location ~ /\.ht {
deny all;
}
location /system {
deny all;
return 404;
} }
} }

1927
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

5
package.json Normal file
View File

@@ -0,0 +1,5 @@
{
"devDependencies": {
"cypress": "^12.12.0"
}
}

View File

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

View File

@@ -1,3 +1,3 @@
To play on {{ config.lua.serverName }} you need an account. To play on {{ config.lua.serverName }} you need an account.
All you have to do to create your new account is to enter an account {% if constant('USE_ACCOUNT_NAME') %}name{% else %}number{% endif %}, password{% if config.recaptcha_enabled %}, confirm reCAPTCHA{% endif %}{% if config.account_country %}, country{% endif %} and your email address. All you have to do to create your new account is to enter an account {% if constant('USE_ACCOUNT_NAME') %}name{% else %}number{% endif %}, password{% if config.account_country %}, country{% endif %} and your email address.
Also you have to agree to the terms presented below. If you have done so, your account {% if constant('USE_ACCOUNT_NAME') %}name{% else %}number{% endif %} will be shown on the following page and your account password will be sent to your email address along with further instructions. If you do not receive the email with your password, please check your spam filter.<br/><br/> Also you have to agree to the terms presented below. If you have done so, your account {% if constant('USE_ACCOUNT_NAME') %}name{% else %}number{% endif %} will be shown on the following page and your account password will be sent to your email address along with further instructions. If you do not receive the email with your password, please check your spam filter.<br/><br/>

View File

@@ -22,7 +22,7 @@ if [ $1 = "prepare" ]; then
mkdir -p tmp mkdir -p tmp
# get myaac from git archive # get myaac from git archive
git archive --format zip --output tmp/myaac.zip master git archive --format zip --output tmp/myaac.zip 0.9
cd tmp/ || exit cd tmp/ || exit
@@ -35,6 +35,11 @@ if [ $1 = "prepare" ]; then
unzip -q myaac.zip -d $dir unzip -q myaac.zip -d $dir
rm myaac.zip rm myaac.zip
cd $dir || exit
# dependencies
composer install
echo "Now you can make changes to $dir. When you are ready, type 'release.sh pack'" echo "Now you can make changes to $dir. When you are ready, type 'release.sh pack'"
exit exit
fi fi
@@ -62,4 +67,4 @@ if [ $1 = "pack" ]; then
echo "Done. Released files can be found in 'releases' directory." echo "Done. Released files can be found in 'releases' directory."
exit exit
fi fi

View File

@@ -1,206 +0,0 @@
<?php
namespace MyAAC;
$loader = new \MyAAC\Psr4AutoloaderClass;
// register the autoloader
$loader->register();
// register the base directories for the namespace prefix
$loader->addNamespace('Composer\Semver', LIBS . 'semver');
$loader->addNamespace('Twig', LIBS . 'Twig');
/**
* An example of a general-purpose implementation that includes the optional
* functionality of allowing multiple base directories for a single namespace
* prefix.
*
* Given a foo-bar package of classes in the file system at the following
* paths ...
*
* /path/to/packages/foo-bar/
* src/
* Baz.php # Foo\Bar\Baz
* Qux/
* Quux.php # Foo\Bar\Qux\Quux
* tests/
* BazTest.php # Foo\Bar\BazTest
* Qux/
* QuuxTest.php # Foo\Bar\Qux\QuuxTest
*
* ... add the path to the class files for the \Foo\Bar\ namespace prefix
* as follows:
*
* <?php
* // instantiate the loader
* $loader = new \Example\Psr4AutoloaderClass;
*
* // register the autoloader
* $loader->register();
*
* // register the base directories for the namespace prefix
* $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/src');
* $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/tests');
*
* The following line would cause the autoloader to attempt to load the
* \Foo\Bar\Qux\Quux class from /path/to/packages/foo-bar/src/Qux/Quux.php:
*
* <?php
* new \Foo\Bar\Qux\Quux;
*
* The following line would cause the autoloader to attempt to load the
* \Foo\Bar\Qux\QuuxTest class from /path/to/packages/foo-bar/tests/Qux/QuuxTest.php:
*
* <?php
* new \Foo\Bar\Qux\QuuxTest;
*/
class Psr4AutoloaderClass
{
/**
* An associative array where the key is a namespace prefix and the value
* is an array of base directories for classes in that namespace.
*
* @var array
*/
protected $prefixes = array();
/**
* Register loader with SPL autoloader stack.
*
* @return void
*/
public function register()
{
spl_autoload_register(array($this, 'loadClass'));
}
/**
* Adds a base directory for a namespace prefix.
*
* @param string $prefix The namespace prefix.
* @param string $base_dir A base directory for class files in the
* namespace.
* @param bool $prepend If true, prepend the base directory to the stack
* instead of appending it; this causes it to be searched first rather
* than last.
* @return void
*/
public function addNamespace($prefix, $base_dir, $prepend = false)
{
// normalize namespace prefix
$prefix = trim($prefix, '\\') . '\\';
// normalize the base directory with a trailing separator
$base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';
// initialize the namespace prefix array
if (isset($this->prefixes[$prefix]) === false) {
$this->prefixes[$prefix] = array();
}
// retain the base directory for the namespace prefix
if ($prepend) {
array_unshift($this->prefixes[$prefix], $base_dir);
} else {
array_push($this->prefixes[$prefix], $base_dir);
}
}
/**
* Loads the class file for a given class name.
*
* @param string $class The fully-qualified class name.
* @return mixed The mapped file name on success, or boolean false on
* failure.
*/
public function loadClass($class)
{
if (0 === strpos($class, 'Twig_')) {
$file = LIBS . 'Twig/' . str_replace(array('_', "\0"), array('/', ''), $class).'.php';
if((config('env') === 'dev') && !is_file($file)) {
return false;
}
require $file;
return false;
}
// the current namespace prefix
$prefix = $class;
// work backwards through the namespace names of the fully-qualified
// class name to find a mapped file name
while (false !== $pos = strrpos($prefix, '\\')) {
// retain the trailing namespace separator in the prefix
$prefix = substr($class, 0, $pos + 1);
// the rest is the relative class name
$relative_class = substr($class, $pos + 1);
// try to load a mapped file for the prefix and relative class
$mapped_file = $this->loadMappedFile($prefix, $relative_class);
if ($mapped_file) {
return $mapped_file;
}
// remove the trailing namespace separator for the next iteration
// of strrpos()
$prefix = rtrim($prefix, '\\');
}
// never found a mapped file
return false;
}
/**
* Load the mapped file for a namespace prefix and relative class.
*
* @param string $prefix The namespace prefix.
* @param string $relative_class The relative class name.
* @return mixed Boolean false if no mapped file can be loaded, or the
* name of the mapped file that was loaded.
*/
protected function loadMappedFile($prefix, $relative_class)
{
// are there any base directories for this namespace prefix?
if (isset($this->prefixes[$prefix]) === false) {
return false;
}
// look through base directories for this namespace prefix
foreach ($this->prefixes[$prefix] as $base_dir) {
// replace the namespace prefix with the base directory,
// replace namespace separators with directory separators
// in the relative class name, append with .php
$file = $base_dir
. str_replace('\\', '/', $relative_class)
. '.php';
// if the mapped file exists, require it
if ($this->requireFile($file)) {
// yes, we're done
return $file;
}
}
// never found it
return false;
}
/**
* If a file exists, require it from the file system.
*
* @param string $file The file to require.
* @return bool True if the file exists, false if not.
*/
protected function requireFile($file)
{
if (config('env') !== 'dev' || file_exists($file)) {
require $file;
return true;
}
return false;
}
}

View File

@@ -1,4 +1,25 @@
<?php <?php
/**
* Exception handler
*
* @package MyAAC
* @author Slawkens <slawkens@gmail.com>
* @copyright 2023 MyAAC
* @link https://my-aac.org
*/
if (class_exists(\Whoops\Run::class)) {
$whoops = new \Whoops\Run;
if(IS_CLI) {
$whoops->pushHandler(new \Whoops\Handler\PlainTextHandler);
}
else {
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
}
$whoops->register();
return;
}
require LIBS . 'SensitiveException.php'; require LIBS . 'SensitiveException.php';
@@ -23,6 +44,8 @@ function exception_handler($exception) {
$backtrace_formatted = nl2br($exception->getTraceAsString()); $backtrace_formatted = nl2br($exception->getTraceAsString());
$message = $message . "<br/><br/>File: {$exception->getFile()}<br/>Line: {$exception->getLine()}";
// display basic error message without template // display basic error message without template
// template is missing, why? probably someone deleted templates dir, or it wasn't downloaded right // template is missing, why? probably someone deleted templates dir, or it wasn't downloaded right
$template_file = SYSTEM . 'templates/exception.html.twig'; $template_file = SYSTEM . 'templates/exception.html.twig';

View File

@@ -7,12 +7,11 @@
* @copyright 2019 MyAAC * @copyright 2019 MyAAC
* @link https://my-aac.org * @link https://my-aac.org
*/ */
defined('MYAAC') or die('Direct access not allowed!');
use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\PHPMailer;
use Twig\Loader\ArrayLoader as Twig_ArrayLoader; use Twig\Loader\ArrayLoader as Twig_ArrayLoader;
defined('MYAAC') or die('Direct access not allowed!');
function message($message, $type, $return) function message($message, $type, $return)
{ {
if(IS_CLI) { if(IS_CLI) {
@@ -126,14 +125,13 @@ function getHouseLink($name, $generate = true)
function getGuildLink($name, $generate = true) function getGuildLink($name, $generate = true)
{ {
global $db, $config; global $config;
if(is_numeric($name)) if(is_numeric($name)) {
{ $name = getGuildNameById($name);
$guild = $db->query( if ($name === false) {
'SELECT `name` FROM `guilds` WHERE `id` = ' . (int)$name); $name = 'Unknown';
if($guild->rowCount() > 0) }
$name = $guild->fetchColumn();
} }
$url = BASE_URL . ($config['friendly_urls'] ? '' : 'index.php/') . 'guilds/' . urlencode($name); $url = BASE_URL . ($config['friendly_urls'] ? '' : 'index.php/') . 'guilds/' . urlencode($name);
@@ -756,10 +754,10 @@ function get_browser_languages()
{ {
$ret = array(); $ret = array();
$acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE']; if(empty($_SERVER['HTTP_ACCEPT_LANGUAGE']))
if(!isset($acceptLang[0]))
return $ret; return $ret;
$acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$languages = strtolower($acceptLang); $languages = strtolower($acceptLang);
// $languages = 'pl,en-us;q=0.7,en;q=0.3 '; // $languages = 'pl,en-us;q=0.7,en;q=0.3 ';
// need to remove spaces from strings to avoid error // need to remove spaces from strings to avoid error
@@ -792,16 +790,21 @@ function get_templates()
* Generates list of installed plugins * Generates list of installed plugins
* @return array $plugins * @return array $plugins
*/ */
function get_plugins() function get_plugins($disabled = false): array
{ {
$ret = array(); $ret = [];
$path = PLUGINS; $path = PLUGINS;
foreach(scandir($path, 0) as $file) { foreach(scandir($path, SCANDIR_SORT_ASCENDING) as $file) {
$file_ext = pathinfo($file, PATHINFO_EXTENSION); $file_ext = pathinfo($file, PATHINFO_EXTENSION);
$file_name = pathinfo($file, PATHINFO_FILENAME); $file_name = pathinfo($file, PATHINFO_FILENAME);
if ($file === '.' || $file === '..' || $file === 'disabled' || $file === 'example.json' || $file_ext !== 'json' || is_dir($path . $file)) if ($file === '.' || $file === '..' || $file === 'example.json' || $file_ext !== 'json' || is_dir($path . $file)) {
continue; continue;
}
if (!$disabled && strpos($file, 'disabled.') !== false) {
continue;
}
$ret[] = str_replace('.json', '', $file_name); $ret[] = str_replace('.json', '', $file_name);
} }
@@ -1189,7 +1192,7 @@ function getCustomPageInfo($page)
return null; return null;
} }
function getCustomPage($page, &$success) function getCustomPage($page, &$success): string
{ {
global $db, $twig, $title, $ignore, $logged_access; global $db, $twig, $title, $ignore, $logged_access;
@@ -1544,6 +1547,39 @@ function escapeHtml($html) {
return htmlentities($html, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); return htmlentities($html, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
} }
function getGuildNameById($id)
{
global $db;
$guild = $db->query('SELECT `name` FROM `guilds` WHERE `id` = ' . (int)$id);
if($guild->rowCount() > 0) {
return $guild->fetchColumn();
}
return false;
}
function getGuildLogoById($id)
{
global $db;
$logo = 'default.gif';
$query = $db->query('SELECT `logo_name` FROM `guilds` WHERE `id` = ' . (int)$id);
if ($query->rowCount() == 1) {
$query = $query->fetch(PDO::FETCH_ASSOC);
$guildLogo = $query['logo_name'];
if (!empty($guildLogo) && file_exists(GUILD_IMAGES_DIR . $guildLogo)) {
$logo = $guildLogo;
}
}
return BASE_URL . GUILD_IMAGES_DIR . $logo;
}
// validator functions // validator functions
require_once LIBS . 'validator.php'; require_once LIBS . 'validator.php';
require_once SYSTEM . 'compat/base.php'; require_once SYSTEM . 'compat/base.php';

View File

@@ -30,6 +30,7 @@ define('HOOK_CHARACTERS_AFTER_CHARACTERS', ++$i);
define('HOOK_LOGIN', ++$i); define('HOOK_LOGIN', ++$i);
define('HOOK_LOGIN_ATTEMPT', ++$i); define('HOOK_LOGIN_ATTEMPT', ++$i);
define('HOOK_LOGOUT', ++$i); define('HOOK_LOGOUT', ++$i);
define('HOOK_ACCOUNT_CHANGE_PASSWORD_POST', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_FORM', ++$i); define('HOOK_ACCOUNT_CREATE_BEFORE_FORM', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_BOXES', ++$i); define('HOOK_ACCOUNT_CREATE_BEFORE_BOXES', ++$i);
define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_1', ++$i); define('HOOK_ACCOUNT_CREATE_BETWEEN_BOXES_1', ++$i);
@@ -39,8 +40,8 @@ define('HOOK_ACCOUNT_CREATE_BEFORE_ACCOUNT', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_ACCOUNT', ++$i); define('HOOK_ACCOUNT_CREATE_AFTER_ACCOUNT', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_EMAIL', ++$i); define('HOOK_ACCOUNT_CREATE_AFTER_EMAIL', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_COUNTRY', ++$i); define('HOOK_ACCOUNT_CREATE_AFTER_COUNTRY', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_PASSWORD', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_PASSWORDS', ++$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_BEFORE_CHARACTER_NAME', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_CHARACTER_NAME', ++$i); define('HOOK_ACCOUNT_CREATE_AFTER_CHARACTER_NAME', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_SEX', ++$i); define('HOOK_ACCOUNT_CREATE_AFTER_SEX', ++$i);
@@ -48,11 +49,30 @@ define('HOOK_ACCOUNT_CREATE_AFTER_VOCATION', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_TOWNS', ++$i); define('HOOK_ACCOUNT_CREATE_AFTER_TOWNS', ++$i);
define('HOOK_ACCOUNT_CREATE_BEFORE_SUBMIT_BUTTON', ++$i); define('HOOK_ACCOUNT_CREATE_BEFORE_SUBMIT_BUTTON', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_FORM', ++$i); define('HOOK_ACCOUNT_CREATE_AFTER_FORM', ++$i);
define('HOOK_ACCOUNT_CREATE_AFTER_SUBMIT', ++$i); define('HOOK_ACCOUNT_CREATE_POST', ++$i);
define('HOOK_ACCOUNT_LOGIN_BEFORE_PAGE', ++$i);
define('HOOK_ACCOUNT_LOGIN_BEFORE_ACCOUNT', ++$i);
define('HOOK_ACCOUNT_LOGIN_AFTER_ACCOUNT', ++$i);
define('HOOK_ACCOUNT_LOGIN_BEFORE_PASSWORD', ++$i);
define('HOOK_ACCOUNT_LOGIN_AFTER_PASSWORD', ++$i);
define('HOOK_ACCOUNT_LOGIN_AFTER_REMEMBER_ME', ++$i);
define('HOOK_ACCOUNT_LOGIN_AFTER_PAGE', ++$i);
define('HOOK_ACCOUNT_LOGIN_POST', ++$i);
define('HOOK_ADMIN_HEAD_END', ++$i);
define('HOOK_ADMIN_HEAD_START', ++$i);
define('HOOK_ADMIN_BODY_START', ++$i);
define('HOOK_ADMIN_BODY_END', ++$i);
define('HOOK_ADMIN_BEFORE_PAGE', ++$i);
define('HOOK_ADMIN_MENU', ++$i); define('HOOK_ADMIN_MENU', ++$i);
define('HOOK_ADMIN_LOGIN_AFTER_ACCOUNT', ++$i);
define('HOOK_ADMIN_LOGIN_AFTER_PASSWORD', ++$i);
define('HOOK_ADMIN_LOGIN_AFTER_SIGN_IN', ++$i);
define('HOOK_ADMIN_ACCOUNTS_SAVE_POST', ++$i);
define('HOOK_EMAIL_CONFIRMED', ++$i); define('HOOK_EMAIL_CONFIRMED', ++$i);
define('HOOK_FIRST', HOOK_STARTUP); define('HOOK_GUILDS_AFTER_INVITED_CHARACTERS', ++$i);
define('HOOK_LAST', HOOK_EMAIL_CONFIRMED);
const HOOK_FIRST = HOOK_STARTUP;
define('HOOK_LAST', $i);
require_once LIBS . 'plugins.php'; require_once LIBS . 'plugins.php';
class Hook class Hook

View File

@@ -18,6 +18,10 @@ if(!isset($config['installed']) || !$config['installed']) {
throw new RuntimeException('MyAAC has not been installed yet or there was error during installation. Please install again.'); throw new RuntimeException('MyAAC has not been installed yet or there was error during installation. Please install again.');
} }
if(config('env') === 'dev') {
require SYSTEM . 'exception.php';
}
date_default_timezone_set($config['date_timezone']); date_default_timezone_set($config['date_timezone']);
// take care of trailing slash at the end // take care of trailing slash at the end
if($config['server_path'][strlen($config['server_path']) - 1] !== '/') if($config['server_path'][strlen($config['server_path']) - 1] !== '/')

View File

@@ -251,10 +251,12 @@ class CreateCharacter
} }
} }
$loaded_items_to_copy = $db->query("SELECT * FROM player_items WHERE player_id = ".$char_to_copy->getId().""); if ($db->hasTable('player_items') && $db->hasColumn('player_items', 'pid') && $db->hasColumn('player_items', 'sid') && $db->hasColumn('player_items', 'itemtype')) {
foreach($loaded_items_to_copy as $save_item) { $loaded_items_to_copy = $db->query("SELECT * FROM player_items WHERE player_id = ".$char_to_copy->getId()."");
$blob = $db->quote($save_item['attributes']); 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']."', $blob);"); $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; global $twig;

View File

@@ -1,84 +0,0 @@
<?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;
}
}

View File

@@ -110,4 +110,21 @@ class Cache
* @return bool * @return bool
*/ */
public function enabled() {return false;} public function enabled() {return false;}
public static function remember($key, $ttl, $callback)
{
$cache = self::getInstance();
if(!$cache->enabled()) {
return $callback();
}
$value = null;
if ($cache->fetch($key, $value)) {
return unserialize($value);
}
$value = $callback();
$cache->set($key, serialize($value),$ttl);
return $value;
}
} }

View File

@@ -82,6 +82,9 @@ class Creatures {
$armor = $monster->getArmor(); $armor = $monster->getArmor();
$defensev = $monster->getDefense(); $defensev = $monster->getDefense();
//load look
$look = $monster->getLook();
//load monster flags //load monster flags
$flags = $monster->getFlags(); $flags = $monster->getFlags();
if(!isset($flags['summonable'])) if(!isset($flags['summonable']))
@@ -147,6 +150,7 @@ class Creatures {
'armor' => $armor, 'armor' => $armor,
'race' => $race, 'race' => $race,
'loot' => json_encode($loot), 'loot' => json_encode($loot),
'look' => json_encode($look),
'summons' => json_encode($summons) 'summons' => json_encode($summons)
)); ));

View File

@@ -10,7 +10,7 @@
*/ */
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
function is_sub_dir($path = NULL, $parent_folder = SITE_PATH) { function is_sub_dir($path = NULL, $parent_folder = BASE) {
//Get directory path minus last folder //Get directory path minus last folder
$dir = dirname($path); $dir = dirname($path);
@@ -41,9 +41,9 @@ function is_sub_dir($path = NULL, $parent_folder = SITE_PATH) {
use Composer\Semver\Semver; use Composer\Semver\Semver;
class Plugins { class Plugins {
private static $warnings = array(); private static $warnings = [];
private static $error = null; private static $error = null;
private static $plugin_json = array(); private static $plugin_json = [];
public static function getRoutes() public static function getRoutes()
{ {
@@ -56,22 +56,8 @@ class Plugins {
} }
$routes = []; $routes = [];
foreach(get_plugins() as $filename) { foreach(self::getAllPluginsJson() as $plugin) {
$string = file_get_contents(PLUGINS . $filename . '.json'); $warningPreTitle = 'Plugin: ' . $plugin['name'] . ' - ';
$string = self::removeComments($string);
$plugin = json_decode($string, true);
self::$plugin_json = $plugin;
if ($plugin == null) {
self::$warnings[] = 'Cannot load ' . $filename . '.json. File might be not a valid json code.';
continue;
}
if(isset($plugin['enabled']) && !getBoolean($plugin['enabled'])) {
self::$warnings[] = 'Skipping ' . $filename . '... The plugin is disabled.';
continue;
}
$warningPreTitle = 'Plugin: ' . $filename . ' - ';
if (isset($plugin['routes'])) { if (isset($plugin['routes'])) {
foreach ($plugin['routes'] as $_name => $info) { foreach ($plugin['routes'] as $_name => $info) {
@@ -80,7 +66,8 @@ class Plugins {
if ($method !== '*') { if ($method !== '*') {
$methods = is_string($method) ? explode(',', $info['method']) : $method; $methods = is_string($method) ? explode(',', $info['method']) : $method;
foreach ($methods as $method) { foreach ($methods as $method) {
if (!in_array($method, ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'])) { $method = strtolower($method);
if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete', 'head'])) {
self::$warnings[] = $warningPreTitle . 'Not allowed method ' . $method . '... Disabling this route...'; self::$warnings[] = $warningPreTitle . 'Not allowed method ' . $method . '... Disabling this route...';
} }
} }
@@ -161,28 +148,14 @@ class Plugins {
} }
$hooks = []; $hooks = [];
foreach(get_plugins() as $filename) { foreach(self::getAllPluginsJson() as $plugin) {
$string = file_get_contents(PLUGINS . $filename . '.json');
$string = self::removeComments($string);
$plugin = json_decode($string, true);
self::$plugin_json = $plugin;
if ($plugin == null) {
self::$warnings[] = 'Cannot load ' . $filename . '.json. File might be not a valid json code.';
continue;
}
if(isset($plugin['enabled']) && !getBoolean($plugin['enabled'])) {
self::$warnings[] = 'Skipping ' . $filename . '... The plugin is disabled.';
continue;
}
if (isset($plugin['hooks'])) { if (isset($plugin['hooks'])) {
foreach ($plugin['hooks'] as $_name => $info) { foreach ($plugin['hooks'] as $_name => $info) {
if (defined('HOOK_'. $info['type'])) { if (defined('HOOK_'. $info['type'])) {
$hook = constant('HOOK_'. $info['type']); $hook = constant('HOOK_'. $info['type']);
$hooks[] = ['name' => $_name, 'type' => $hook, 'file' => $info['file']]; $hooks[] = ['name' => $_name, 'type' => $hook, 'file' => $info['file']];
} else { } else {
self::$warnings[] = 'Plugin: ' . $filename . '. Unknown event type: ' . $info['type']; self::$warnings[] = 'Plugin: ' . $plugin['name'] . '. Unknown event type: ' . $info['type'];
} }
} }
} }
@@ -195,6 +168,41 @@ class Plugins {
return $hooks; return $hooks;
} }
public static function getAllPluginsJson($disabled = false)
{
$cache = Cache::getInstance();
if ($cache->enabled()) {
$tmp = '';
if ($cache->fetch('plugins', $tmp)) {
return unserialize($tmp);
}
}
$plugins = [];
foreach (get_plugins($disabled) as $filename) {
$string = file_get_contents(PLUGINS . $filename . '.json');
$plugin = json_decode($string, true);
self::$plugin_json = $plugin;
if ($plugin == null) {
self::$warnings[] = 'Cannot load ' . $filename . '.json. File might be not a valid json code.';
continue;
}
if (isset($plugin['enabled']) && !getBoolean($plugin['enabled'])) {
self::$warnings[] = 'Skipping ' . $filename . '... The plugin is disabled.';
continue;
}
$plugins[] = $plugin;
}
if ($cache->enabled()) {
$cache->set('plugins', serialize($plugins), 600);
}
return $plugins;
}
public static function install($file) { public static function install($file) {
global $db; global $db;
@@ -235,7 +243,6 @@ class Plugins {
} }
$string = file_get_contents($file_name); $string = file_get_contents($file_name);
$string = self::removeComments($string);
$plugin_json = json_decode($string, true); $plugin_json = json_decode($string, true);
self::$plugin_json = $plugin_json; self::$plugin_json = $plugin_json;
if ($plugin_json == null) { if ($plugin_json == null) {
@@ -435,7 +442,35 @@ class Plugins {
return false; return false;
} }
public static function uninstall($plugin_name) public static function enable($pluginFileName): bool
{
return self::enableDisable($pluginFileName, true);
}
public static function disable($pluginFileName): bool
{
return self::enableDisable($pluginFileName, false);
}
private static function enableDisable($pluginFileName, $enable): bool
{
$filenameJson = $pluginFileName . '.json';
$fileExist = is_file(PLUGINS . ($enable ? 'disabled.' : '') . $filenameJson);
if (!$fileExist) {
self::$error = 'Cannot ' . ($enable ? 'enable' : 'disable') . ' plugin: ' . $pluginFileName . '. File does not exist.';
return false;
}
$result = rename(PLUGINS . ($enable ? 'disabled.' : '') . $filenameJson, PLUGINS . ($enable ? '' : 'disabled.') . $filenameJson);
if (!$result) {
self::$error = 'Cannot ' . ($enable ? 'enable' : 'disable') . ' plugin: ' . $pluginFileName . '. Permission problem.';
return false;
}
return true;
}
public static function uninstall($plugin_name): bool
{ {
$filename = BASE . 'plugins/' . $plugin_name . '.json'; $filename = BASE . 'plugins/' . $plugin_name . '.json';
if(!file_exists($filename)) { if(!file_exists($filename)) {
@@ -443,9 +478,8 @@ class Plugins {
return false; return false;
} }
$string = file_get_contents($filename); $string = file_get_contents($filename);
$string = self::removeComments($string);
$plugin_info = json_decode($string, true); $plugin_info = json_decode($string, true);
if($plugin_info == false) { if(!$plugin_info) {
self::$error = 'Cannot load plugin info ' . $plugin_name . '.json'; self::$error = 'Cannot load plugin info ' . $plugin_name . '.json';
return false; return false;
} }
@@ -527,22 +561,6 @@ class Plugins {
return self::$plugin_json; return self::$plugin_json;
} }
public static function removeComments($string) {
$string = preg_replace('!/\*.*?\*/!s', '', $string);
$string = preg_replace('/\n\s*\n/', "\n", $string);
// Removes multi-line comments and does not create
// a blank line, also treats white spaces/tabs
$string = preg_replace('!^[ \t]*/\*.*?\*/[ \t]*[\r\n]!s', '', $string);
// Removes single line '//' comments, treats blank characters
$string = preg_replace('![ \t]*//.*[ \t]*[\r\n]!', '', $string);
// Strip blank lines
$string = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $string);
return $string;
}
/** /**
* Install menus * Install menus
* Helper function for plugins * Helper function for plugins

View File

@@ -21,7 +21,6 @@
* @property string $password Password. * @property string $password Password.
* @property string $eMail Email address. * @property string $eMail Email address.
* @property int $premiumEnd Timestamp of PACC end. * @property int $premiumEnd Timestamp of PACC end.
* @property bool $blocked Blocked flag state.
* @property bool $deleted Deleted flag state. * @property bool $deleted Deleted flag state.
* @property bool $warned Warned flag state. * @property bool $warned Warned flag state.
* @property bool $banned Ban state. * @property bool $banned Ban state.
@@ -39,7 +38,7 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
* @var array * @var array
* @version 0.1.5 * @version 0.1.5
*/ */
private $data = array('email' => '', 'blocked' => false, 'rlname' => '','location' => '', 'country' => '','web_flags' => 0, 'lastday' => 0, 'premdays' => 0, 'created' => 0); private $data = array('email' => '', 'rlname' => '','location' => '', 'country' => '','web_flags' => 0, 'lastday' => 0, 'premdays' => 0, 'created' => 0);
public static $cache = array(); public static $cache = array();
@@ -231,26 +230,22 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
* @param int $id Account number. * @param int $id Account number.
* @throws PDOException On PDO operation error. * @throws PDOException On PDO operation error.
*/ */
public function load($id, $fresh = false, $searchOnlyById = false) public function load($id, $fresh = false)
{ {
if(!$fresh && isset(self::$cache[$id])) { if(!$fresh && isset(self::$cache[$id])) {
$this->data = self::$cache[$id]; $this->data = self::$cache[$id];
return; return;
} }
$numberColumn = 'id';
$nameOrNumber = ''; $nameOrNumber = '';
if (!$searchOnlyById) { if (USE_ACCOUNT_NAME) {
if (USE_ACCOUNT_NAME) { $nameOrNumber = '`name`,';
$nameOrNumber = '`name`,'; } else if (USE_ACCOUNT_NUMBER) {
} else if (USE_ACCOUNT_NUMBER) { $nameOrNumber = '`number`,';
$nameOrNumber = '`number`,';
$numberColumn = 'number';
}
} }
// SELECT query on database // SELECT query on database
$this->data = $this->db->query('SELECT `id`, ' . $nameOrNumber . '`password`, `email`, `blocked`, `rlname`, `location`, `country`, `web_flags`, ' . ($this->db->hasColumn('accounts', 'premdays') ? '`premdays`, ' : '') . ($this->db->hasColumn('accounts', 'lastday') ? '`lastday`, ' : ($this->db->hasColumn('accounts', 'premend') ? '`premend`,' : ($this->db->hasColumn('accounts', 'premium_ends_at') ? '`premium_ends_at`,' : ''))) . '`created` FROM `accounts` WHERE `' . $numberColumn . '` = ' . (int) $id)->fetch(); $this->data = $this->db->query('SELECT `id`, ' . $nameOrNumber . '`password`, `email`, `rlname`, `location`, `country`, `web_flags`, ' . ($this->db->hasColumn('accounts', 'premdays') ? '`premdays`, ' : '') . ($this->db->hasColumn('accounts', 'lastday') ? '`lastday`, ' : ($this->db->hasColumn('accounts', 'premend') ? '`premend`,' : ($this->db->hasColumn('accounts', 'premium_ends_at') ? '`premium_ends_at`,' : ''))) . '`created` FROM `accounts` WHERE `id` = ' . (int) $id)->fetch();
self::$cache[$id] = $this->data; self::$cache[$id] = $this->data;
} }
@@ -268,8 +263,13 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
*/ */
public function find($name) public function find($name)
{ {
$nameOrNumberColumn = 'name';
if (USE_ACCOUNT_NUMBER) {
$nameOrNumberColumn = 'number';
}
// finds player's ID // finds player's ID
$id = $this->db->query('SELECT `id` FROM `accounts` WHERE `name` = ' . $this->db->quote($name) )->fetch(); $id = $this->db->query('SELECT `id` FROM `accounts` WHERE `' . $nameOrNumberColumn . '` = ' . $this->db->quote($name) )->fetch();
// if anything was found // if anything was found
if( isset($id['id']) ) if( isset($id['id']) )
@@ -345,7 +345,7 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
} }
// UPDATE query on database // UPDATE query on database
$this->db->exec('UPDATE `accounts` SET ' . ($this->db->hasColumn('accounts', 'name') ? '`name` = ' . $this->db->quote($this->data['name']) . ',' : '') . '`password` = ' . $this->db->quote($this->data['password']) . ', `email` = ' . $this->db->quote($this->data['email']) . ', `blocked` = ' . (int) $this->data['blocked'] . ', `rlname` = ' . $this->db->quote($this->data['rlname']) . ', `location` = ' . $this->db->quote($this->data['location']) . ', `country` = ' . $this->db->quote($this->data['country']) . ', `web_flags` = ' . (int) $this->data['web_flags'] . ', ' . ($this->db->hasColumn('accounts', 'premdays') ? '`premdays` = ' . (int) $this->data['premdays'] . ',' : '') . '`' . $field . '` = ' . (int) $this->data[$field] . ' WHERE `id` = ' . $this->data['id']); $this->db->exec('UPDATE `accounts` SET ' . ($this->db->hasColumn('accounts', 'name') ? '`name` = ' . $this->db->quote($this->data['name']) . ',' : '') . '`password` = ' . $this->db->quote($this->data['password']) . ', `email` = ' . $this->db->quote($this->data['email']) . ', `rlname` = ' . $this->db->quote($this->data['rlname']) . ', `location` = ' . $this->db->quote($this->data['location']) . ', `country` = ' . $this->db->quote($this->data['country']) . ', `web_flags` = ' . (int) $this->data['web_flags'] . ', ' . ($this->db->hasColumn('accounts', 'premdays') ? '`premdays` = ' . (int) $this->data['premdays'] . ',' : '') . '`' . $field . '` = ' . (int) $this->data[$field] . ' WHERE `id` = ' . $this->data['id']);
} }
/** /**
@@ -650,53 +650,6 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
$this->data['email'] = (string) $email; $this->data['email'] = (string) $email;
} }
/**
* Checks if account is blocked.
*
* <p>
* Note: Since 0.0.3 version this method throws {@link E_OTS_NotLoaded E_OTS_NotLoaded} exception instead of triggering E_USER_WARNING.
* </p>
*
* @version 0.0.3
* @return bool Blocked state.
* @throws E_OTS_NotLoaded If account is not loaded.
*/
public function isBlocked()
{
if( !isset($this->data['blocked']) )
{
throw new E_OTS_NotLoaded();
}
return $this->data['blocked'];
}
/**
* Unblocks account.
*
* <p>
* This method only updates object state. To save changes in database you need to use {@link OTS_Account::save() save() method} to flush changed to database.
* </p>
*/
public function unblock()
{
$this->data['blocked'] = false;
}
/**
* Blocks account.
*
* <p>
* This method only updates object state. To save changes in databaseed to use {@link OTS_Account::save() save() method} to flush changed to database.
* </p>
*/
public function block()
{
$this->data['blocked'] = true;
}
/** /**
* Reads custom field. * Reads custom field.
* *
@@ -1147,9 +1100,6 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
case 'playersList': case 'playersList':
return $this->getPlayersList(); return $this->getPlayersList();
case 'blocked':
return $this->isBlocked();
case 'deleted': case 'deleted':
return $this->isDeleted(); return $this->isDeleted();
@@ -1195,17 +1145,6 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable
$this->setPremiumEnd($value); $this->setPremiumEnd($value);
break; break;
case 'blocked':
if($value)
{
$this->block();
}
else
{
$this->unblock();
}
break;
case 'deleted': case 'deleted':
if($value) if($value)
{ {

View File

@@ -36,6 +36,7 @@
* @property-read int $armor Armor rate. * @property-read int $armor Armor rate.
* @property-read array $defenses List of defenses. * @property-read array $defenses List of defenses.
* @property-read array $attacks List of attacks. * @property-read array $attacks List of attacks.
* @property-read array $look List of looks.
*/ */
class OTS_Monster extends DOMDocument class OTS_Monster extends DOMDocument
{ {
@@ -273,6 +274,30 @@ class OTS_Monster extends DOMDocument
return $loot; return $loot;
} }
/**
* Returns look of the monster.
*
* @return array Look with all the attributes of the look.
* @throws DOMException On DOM operation error.
*/
public function getLook()
{
$look = array();
$element = $this->documentElement->getElementsByTagName('look')->item(0);
$look['type'] = $element->getAttribute('type');
$look['typeex'] = $element->getAttribute('typeex');
$look['head'] = $element->getAttribute('head');
$look['body'] = $element->getAttribute('body');
$look['legs'] = $element->getAttribute('legs');
$look['feet'] = $element->getAttribute('feet');
$look['addons'] = $element->getAttribute('addons');
$look['corpse'] = $element->getAttribute('corpse');
return $look;
}
/** /**
* Returns all monster summons. * Returns all monster summons.
* *
@@ -560,6 +585,9 @@ class OTS_Monster extends DOMDocument
case 'attacks': case 'attacks':
return $this->getAttacks(); return $this->getAttacks();
case 'look':
return $this->getLook();
default: default:
throw new OutOfBoundsException(); throw new OutOfBoundsException();
} }

View File

@@ -174,6 +174,7 @@ class OTS_MonstersList implements Iterator, Countable, ArrayAccess
* @return OTS_Monster Monster. * @return OTS_Monster Monster.
* @throws DOMException On DOM operation error. * @throws DOMException On DOM operation error.
*/ */
#[\ReturnTypeWillChange]
public function current() public function current()
{ {
return $this->getMonster( key($this->monsters) ); return $this->getMonster( key($this->monsters) );
@@ -187,7 +188,7 @@ class OTS_MonstersList implements Iterator, Countable, ArrayAccess
/** /**
* Moves to next iterator monster. * Moves to next iterator monster.
*/ */
public function next() public function next(): void
{ {
next($this->monsters); next($this->monsters);
} }
@@ -197,6 +198,7 @@ class OTS_MonstersList implements Iterator, Countable, ArrayAccess
* *
* @return string Current position key. * @return string Current position key.
*/ */
#[\ReturnTypeWillChange]
public function key() public function key()
{ {
return key($this->monsters); return key($this->monsters);
@@ -207,7 +209,7 @@ class OTS_MonstersList implements Iterator, Countable, ArrayAccess
* *
* @return bool If iterator has anything more. * @return bool If iterator has anything more.
*/ */
public function valid() public function valid(): bool
{ {
return key($this->monsters) !== null; return key($this->monsters) !== null;
} }
@@ -215,7 +217,7 @@ class OTS_MonstersList implements Iterator, Countable, ArrayAccess
/** /**
* Resets iterator index. * Resets iterator index.
*/ */
public function rewind() public function rewind(): void
{ {
reset($this->monsters); reset($this->monsters);
} }
@@ -226,6 +228,7 @@ class OTS_MonstersList implements Iterator, Countable, ArrayAccess
* @param string $offset Array key. * @param string $offset Array key.
* @return bool True if it's set. * @return bool True if it's set.
*/ */
#[\ReturnTypeWillChange]
public function offsetExists($offset) public function offsetExists($offset)
{ {
return isset($this->monsters[$offset]); return isset($this->monsters[$offset]);
@@ -239,6 +242,7 @@ class OTS_MonstersList implements Iterator, Countable, ArrayAccess
* @return OTS_Monster Monster instance. * @return OTS_Monster Monster instance.
* @throws DOMException On DOM operation error. * @throws DOMException On DOM operation error.
*/ */
#[\ReturnTypeWillChange]
public function offsetGet($offset) public function offsetGet($offset)
{ {
return $this->getMonster($offset); return $this->getMonster($offset);
@@ -251,6 +255,7 @@ class OTS_MonstersList implements Iterator, Countable, ArrayAccess
* @param mixed $value Field value. * @param mixed $value Field value.
* @throws E_OTS_ReadOnly Always - this class is read-only. * @throws E_OTS_ReadOnly Always - this class is read-only.
*/ */
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value) public function offsetSet($offset, $value)
{ {
throw new E_OTS_ReadOnly(); throw new E_OTS_ReadOnly();
@@ -262,6 +267,7 @@ class OTS_MonstersList implements Iterator, Countable, ArrayAccess
* @param string|int $offset Array key. * @param string|int $offset Array key.
* @throws E_OTS_ReadOnly Always - this class is read-only. * @throws E_OTS_ReadOnly Always - this class is read-only.
*/ */
#[\ReturnTypeWillChange]
public function offsetUnset($offset) public function offsetUnset($offset)
{ {
throw new E_OTS_ReadOnly(); throw new E_OTS_ReadOnly();

View File

@@ -398,7 +398,7 @@ class OTS_Player extends OTS_Row_DAO
} }
// UPDATE query on database // UPDATE query on database
$this->db->query('UPDATE ' . $this->db->tableName('players') . ' SET ' . $this->db->fieldName('name') . ' = ' . $this->db->quote($this->data['name']) . ', ' . $this->db->fieldName('account_id') . ' = ' . $this->data['account_id'] . ', ' . $this->db->fieldName('group_id') . ' = ' . $this->data['group_id'] . ', ' . $this->db->fieldName('sex') . ' = ' . $this->data['sex'] . ', ' . $this->db->fieldName('vocation') . ' = ' . $this->data['vocation'] . ', ' . $this->db->fieldName('experience') . ' = ' . $this->data['experience'] . ', ' . $this->db->fieldName('level') . ' = ' . $this->data['level'] . ', ' . $this->db->fieldName('maglevel') . ' = ' . $this->data['maglevel'] . ', ' . $this->db->fieldName('health') . ' = ' . $this->data['health'] . ', ' . $this->db->fieldName('healthmax') . ' = ' . $this->data['healthmax'] . ', ' . $this->db->fieldName('mana') . ' = ' . $this->data['mana'] . ', ' . $this->db->fieldName('manamax') . ' = ' . $this->data['manamax'] . ', ' . $this->db->fieldName('manaspent') . ' = ' . $this->data['manaspent'] . ', ' . $this->db->fieldName('soul') . ' = ' . $this->data['soul'] . ', ' . $this->db->fieldName('lookbody') . ' = ' . $this->data['lookbody'] . ', ' . $this->db->fieldName('lookfeet') . ' = ' . $this->data['lookfeet'] . ', ' . $this->db->fieldName('lookhead') . ' = ' . $this->data['lookhead'] . ', ' . $this->db->fieldName('looklegs') . ' = ' . $this->data['looklegs'] . ', ' . $this->db->fieldName('looktype') . ' = ' . $this->data['looktype'] . $lookaddons . ', ' . $this->db->fieldName('posx') . ' = ' . $this->data['posx'] . ', ' . $this->db->fieldName('posy') . ' = ' . $this->data['posy'] . ', ' . $this->db->fieldName('posz') . ' = ' . $this->data['posz'] . ', ' . $this->db->fieldName('cap') . ' = ' . $this->data['cap'] . ', ' . $this->db->fieldName('lastlogin') . ' = ' . $this->data['lastlogin'] . ', ' . $this->db->fieldName('lastlogout') . ' = ' . $this->data['lastlogout'] . ', ' . $this->db->fieldName('lastip') . ' = ' . $this->data['lastip'] . ', ' . $this->db->fieldName('save') . ' = ' . (int) $this->data['save'] . ', ' . $this->db->fieldName('conditions') . ' = ' . $this->db->quote($this->data['conditions']) . ', `' . $skull_time . '` = ' . $this->data['skulltime'] . ', `' . $skull_type . '` = ' . (int) $this->data['skull'] . $guild_info . ', ' . $this->db->fieldName('town_id') . ' = ' . $this->data['town_id'] . $loss . $loss_items . ', ' . $this->db->fieldName('balance') . ' = ' . $this->data['balance'] . $blessings . $stamina . $direction . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id']); $this->db->query('UPDATE ' . $this->db->tableName('players') . ' SET ' . $this->db->fieldName('name') . ' = ' . $this->db->quote($this->data['name']) . ', ' . $this->db->fieldName('account_id') . ' = ' . $this->data['account_id'] . ', ' . $this->db->fieldName('group_id') . ' = ' . $this->data['group_id'] . ', ' . $this->db->fieldName('sex') . ' = ' . $this->data['sex'] . ', ' . $this->db->fieldName('vocation') . ' = ' . $this->data['vocation'] . ', ' . $this->db->fieldName('experience') . ' = ' . $this->data['experience'] . ', ' . $this->db->fieldName('level') . ' = ' . $this->data['level'] . ', ' . $this->db->fieldName('maglevel') . ' = ' . $this->data['maglevel'] . ', ' . $this->db->fieldName('health') . ' = ' . $this->data['health'] . ', ' . $this->db->fieldName('healthmax') . ' = ' . $this->data['healthmax'] . ', ' . $this->db->fieldName('mana') . ' = ' . $this->data['mana'] . ', ' . $this->db->fieldName('manamax') . ' = ' . $this->data['manamax'] . ', ' . $this->db->fieldName('manaspent') . ' = ' . $this->data['manaspent'] . ', ' . $this->db->fieldName('soul') . ' = ' . $this->data['soul'] . ', ' . $this->db->fieldName('lookbody') . ' = ' . $this->data['lookbody'] . ', ' . $this->db->fieldName('lookfeet') . ' = ' . $this->data['lookfeet'] . ', ' . $this->db->fieldName('lookhead') . ' = ' . $this->data['lookhead'] . ', ' . $this->db->fieldName('looklegs') . ' = ' . $this->data['looklegs'] . ', ' . $this->db->fieldName('looktype') . ' = ' . $this->data['looktype'] . $lookaddons . ', ' . $this->db->fieldName('posx') . ' = ' . $this->data['posx'] . ', ' . $this->db->fieldName('posy') . ' = ' . $this->data['posy'] . ', ' . $this->db->fieldName('posz') . ' = ' . $this->data['posz'] . ', ' . $this->db->fieldName('cap') . ' = ' . $this->data['cap'] . ', ' . $this->db->fieldName('lastlogin') . ' = ' . $this->data['lastlogin'] . ', ' . $this->db->fieldName('lastlogout') . ' = ' . $this->data['lastlogout'] . ', ' . $this->db->fieldName('lastip') . ' = ' . $this->db->quote($this->data['lastip']) . ', ' . $this->db->fieldName('save') . ' = ' . (int) $this->data['save'] . ', ' . $this->db->fieldName('conditions') . ' = ' . $this->db->quote($this->data['conditions']) . ', `' . $skull_time . '` = ' . $this->data['skulltime'] . ', `' . $skull_type . '` = ' . (int) $this->data['skull'] . $guild_info . ', ' . $this->db->fieldName('town_id') . ' = ' . $this->data['town_id'] . $loss . $loss_items . ', ' . $this->db->fieldName('balance') . ' = ' . $this->data['balance'] . $blessings . $stamina . $direction . ' WHERE ' . $this->db->fieldName('id') . ' = ' . $this->data['id']);
} }
// creates new player // creates new player
else else
@@ -602,7 +602,7 @@ class OTS_Player extends OTS_Row_DAO
} }
$account = new OTS_Account(); $account = new OTS_Account();
$account->load($this->data['account_id'], false, true); $account->load($this->data['account_id']);
return $account; return $account;
} }

View File

@@ -308,7 +308,7 @@ class OTS_SpellsList implements IteratorAggregate, Countable
* @since 0.1.5 * @since 0.1.5
* @return AppendIterator Iterator for all spells. * @return AppendIterator Iterator for all spells.
*/ */
public function getIterator() public function getIterator(): Traversable
{ {
$iterator = new AppendIterator(); $iterator = new AppendIterator();
$iterator->append( new ArrayIterator($this->runes) ); $iterator->append( new ArrayIterator($this->runes) );

View File

@@ -34,10 +34,12 @@ class Visitors
$this->cleanVisitors(); $this->cleanVisitors();
$ip = $_SERVER['REMOTE_ADDR']; $ip = $_SERVER['REMOTE_ADDR'];
$userAgentShortened = substr($_SERVER['HTTP_USER_AGENT'] ?? 'unknown', 0, 255);
if($this->visitorExists($ip)) if($this->visitorExists($ip))
$this->updateVisitor($ip, $_SERVER['REQUEST_URI']); $this->updateVisitor($ip, $_SERVER['REQUEST_URI'], $userAgentShortened);
else else
$this->addVisitor($ip, $_SERVER['REQUEST_URI']); $this->addVisitor($ip, $_SERVER['REQUEST_URI'], $userAgentShortened);
} }
public function __destruct() public function __destruct()
@@ -75,26 +77,26 @@ class Visitors
$db->exec('DELETE FROM ' . $db->tableName(TABLE_PREFIX . 'visitors') . ' WHERE ' . $db->fieldName('lastvisit') . ' < ' . (time() - $this->sessionTime * 60)); $db->exec('DELETE FROM ' . $db->tableName(TABLE_PREFIX . 'visitors') . ' WHERE ' . $db->fieldName('lastvisit') . ' < ' . (time() - $this->sessionTime * 60));
} }
private function updateVisitor($ip, $page) private function updateVisitor($ip, $page, $userAgent)
{ {
if($this->cacheEnabled) { if($this->cacheEnabled) {
$this->data[$ip] = array('page' => $page, 'lastvisit' => time()); $this->data[$ip] = array('page' => $page, 'lastvisit' => time(), 'user_agent' => $userAgent);
return; return;
} }
global $db; global $db;
$db->exec('UPDATE ' . $db->tableName(TABLE_PREFIX . 'visitors') . ' SET ' . $db->fieldName('lastvisit') . ' = ' . time() . ', ' . $db->fieldName('page') . ' = ' . $db->quote($page) . ' WHERE ' . $db->fieldName('ip') . ' = ' . $db->quote($ip)); $db->update(TABLE_PREFIX . 'visitors', ['lastvisit' => time(), 'page' => $page, 'user_agent' => $userAgent], ['ip' => $ip]);
} }
private function addVisitor($ip, $page) private function addVisitor($ip, $page, $userAgent)
{ {
if($this->cacheEnabled) { if($this->cacheEnabled) {
$this->data[$ip] = array('page' => $page, 'lastvisit' => time()); $this->data[$ip] = array('page' => $page, 'lastvisit' => time(), 'user_agent' => $userAgent);
return; return;
} }
global $db; global $db;
$db->exec('INSERT INTO ' . $db->tableName(TABLE_PREFIX . 'visitors') . ' (' . $db->fieldName('ip') . ' ,' . $db->fieldName('lastvisit') . ', ' . $db->fieldName('page') . ') VALUE (' . $db->quote($ip) . ', ' . time() . ', ' . $db->quote($page) . ')'); $db->insert(TABLE_PREFIX . 'visitors', ['ip' => $ip, 'lastvisit' => time(), 'page' => $page, 'user_agent' => $userAgent]);
} }
public function getVisitors() public function getVisitors()
@@ -107,7 +109,7 @@ class Visitors
} }
global $db; global $db;
return $db->query('SELECT ' . $db->fieldName('ip') . ', ' . $db->fieldName('lastvisit') . ', ' . $db->fieldName('page') . ' FROM ' . $db->tableName(TABLE_PREFIX . 'visitors') . ' ORDER BY ' . $db->fieldName('lastvisit') . ' DESC')->fetchAll(); return $db->query('SELECT ' . $db->fieldName('ip') . ', ' . $db->fieldName('lastvisit') . ', ' . $db->fieldName('page') . ', ' . $db->fieldName('user_agent') . ' FROM ' . $db->tableName(TABLE_PREFIX . 'visitors') . ' ORDER BY ' . $db->fieldName('lastvisit') . ' DESC')->fetchAll();
} }
public function getAmountVisitors() public function getAmountVisitors()

View File

@@ -20,7 +20,7 @@ $locale['not_loaded'] = 'Nicht geladen';
$locale['loading_spinner'] = 'Bitte warten, installieren...'; $locale['loading_spinner'] = 'Bitte warten, installieren...';
$locale['importing_spinner'] = 'Bitte warte, Daten werden importiert...'; $locale['importing_spinner'] = 'Bitte warte, Daten werden importiert...';
$locale['please_fill_all'] = 'Bitte füllen Sie alle Felder aus!'; $locale['please_fill_all'] = 'Bitte füllen Sie alle Felder aus!';
$locale['already_installed'] = 'MyAAC wurde bereits installiert. Bitte löschen <b>install/<b/> Verzeichnis. Wenn Sie MyAAC neu installieren möchten, löschen Sie die Datei <strong>config.local.php</strong> aus dem Hauptverzeichnis und aktualisieren Sie die Seite.'; $locale['already_installed'] = 'MyAAC wurde bereits installiert. Bitte löschen <b>install/</b> Verzeichnis. Wenn Sie MyAAC neu installieren möchten, löschen Sie die Datei <strong>config.local.php</strong> aus dem Hauptverzeichnis und aktualisieren Sie die Seite.';
// welcome // welcome
$locale['step_welcome'] = 'Willkommen'; $locale['step_welcome'] = 'Willkommen';

View File

@@ -28,124 +28,6 @@ if($current_session !== false)
} }
} }
// new login with data from form
if(!$logged && isset($_POST['account_login'], $_POST['password_login']))
{
$login_account = $_POST['account_login'];
$login_password = $_POST['password_login'];
$remember_me = isset($_POST['remember_me']);
if(!empty($login_account) && !empty($login_password))
{
if($cache->enabled())
{
$tmp = '';
if($cache->fetch('failed_logins', $tmp))
{
$tmp = unserialize($tmp);
$to_remove = array();
foreach($tmp as $ip => $t)
{
if(time() - $t['last'] >= 5 * 60)
$to_remove[] = $ip;
}
foreach($to_remove as $ip)
unset($tmp[$ip]);
}
else
$tmp = array();
$ip = $_SERVER['REMOTE_ADDR'];
$t = $tmp[$ip] ?? null;
}
if(config('recaptcha_enabled') && !config('account_create_auto_login'))
{
require_once LIBS . 'GoogleReCAPTCHA.php';
if (!GoogleReCAPTCHA::verify('login')) {
$errors[] = GoogleReCAPTCHA::getErrorMessage();
}
}
$account_logged = new OTS_Account();
if (config('account_login_by_email')) {
$account_logged->findByEMail($login_account);
}
if (!config('account_login_by_email') || config('account_login_by_email_fallback')) {
if(USE_ACCOUNT_NAME) {
$account_logged->find($login_account);
} else {
$account_logged->load($login_account, true);
}
}
if($account_logged->isLoaded() && encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password) == $account_logged->getPassword()
&& (!isset($t) || $t['attempts'] < 5)
)
{
setSession('account', $account_logged->getNumber());
setSession('password', encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password));
if($remember_me) {
setSession('remember_me', true);
}
$logged = true;
$logged_flags = $account_logged->getWebFlags();
if(isset($_POST['admin']) && !admin()) {
$errors[] = 'This account has no admin privileges.';
unsetSession('account');
unsetSession('password');
unsetSession('remember_me');
$logged = false;
}
else {
$account_logged->setCustomField('web_lastlogin', time());
}
$hooks->trigger(HOOK_LOGIN, array('account' => $account_logged, 'password' => $login_password, 'remember_me' => $remember_me));
}
else
{
$hooks->trigger(HOOK_LOGIN_ATTEMPT, array('account' => $login_account, 'password' => $login_password, 'remember_me' => $remember_me));
$errorMessage = getAccountLoginByLabel() . ' or password is not correct.';
// temporary solution for blocking failed login attempts
if($cache->enabled())
{
if(isset($t))
{
$t['attempts']++;
$t['last'] = time();
if($t['attempts'] >= 5)
$errors[] = 'A wrong password has been entered 5 times in a row. You are unable to log into your account for the next 5 minutes. Please wait.';
else
$errors[] = $errorMessage;
}
else
{
$t = array('attempts' => 1, 'last' => time());
$errors[] = $errorMessage;
}
$tmp[$ip] = $t;
$cache->set('failed_logins', serialize($tmp), 60 * 60); // save for 1 hour
}
else {
$errors[] = $errorMessage;
}
}
}
else {
$errors[] = 'Please enter your ' . getAccountLoginByLabel() . ' and password.';
$hooks->trigger(HOOK_LOGIN_ATTEMPT, array('account' => $login_account, 'password' => $login_password, 'remember_me' => $remember_me));
}
}
if($logged) { if($logged) {
$logged_flags = $account_logged->getWebFlags(); $logged_flags = $account_logged->getWebFlags();
$twig->addGlobal('logged', true); $twig->addGlobal('logged', true);

View File

@@ -1,4 +1,13 @@
<?php <?php
/**
* Logout from account
*
* @package MyAAC
* @author Slawkens <slawkens@gmail.com>
* @copyright 2019 MyAAC
* @link https://my-aac.org
*/
defined('MYAAC') or die('Direct access not allowed!');
if(isset($account_logged) && $account_logged->isLoaded()) { if(isset($account_logged) && $account_logged->isLoaded()) {
if($hooks->trigger(HOOK_LOGOUT, ['account_id' => $account_logged->getId()])) { if($hooks->trigger(HOOK_LOGOUT, ['account_id' => $account_logged->getId()])) {

View File

@@ -1,4 +1,13 @@
<?php <?php
/**
* Database migrations
*
* @package MyAAC
* @author Slawkens <slawkens@gmail.com>
* @copyright 2019 MyAAC
* @link https://my-aac.org
*/
defined('MYAAC') or die('Direct access not allowed!');
// database migrations // database migrations
$tmp = ''; $tmp = '';
@@ -19,4 +28,4 @@ else { // register first version
require SYSTEM . 'migrations/' . $i . '.php'; require SYSTEM . 'migrations/' . $i . '.php';
updateDatabaseConfig('database_version', $i); updateDatabaseConfig('database_version', $i);
} }
} }

4
system/migrations/34.php Normal file
View File

@@ -0,0 +1,4 @@
<?php
// add user_agent column into visitors
$db->exec('ALTER TABLE `' . TABLE_PREFIX . "visitors` ADD `user_agent` VARCHAR(255) NOT NULL DEFAULT '';");

3
system/migrations/35.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
// add look column
$db->exec('ALTER TABLE `' . TABLE_PREFIX . "monsters` ADD `look` VARCHAR(255) NOT NULL DEFAULT '' AFTER `health`;");

View File

@@ -26,11 +26,11 @@ if(empty($new_password) && empty($new_password2) && empty($old_password)) {
else else
{ {
if(empty($new_password) || empty($new_password2) || empty($old_password)){ if(empty($new_password) || empty($new_password2) || empty($old_password)){
$errors[] = "Please fill in form."; $errors[] = 'Please fill in form.';
} }
$password_strlen = strlen($new_password); $password_strlen = strlen($new_password);
if($new_password != $new_password2) { if($new_password != $new_password2) {
$errors[] = "The new passwords do not match!"; $errors[] = 'The new passwords do not match!';
} }
if(empty($errors)) { if(empty($errors)) {
@@ -41,9 +41,12 @@ else
/** @var OTS_Account $account_logged */ /** @var OTS_Account $account_logged */
$old_password = encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $old_password); $old_password = encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $old_password);
if($old_password != $account_logged->getPassword()) { if($old_password != $account_logged->getPassword()) {
$errors[] = "Current password is incorrect!"; $errors[] = 'Current password is incorrect!';
} }
$hooks->trigger(HOOK_ACCOUNT_CHANGE_PASSWORD_POST);
} }
if(!empty($errors)){ if(!empty($errors)){
//show errors //show errors
$twig->display('error_box.html.twig', array('errors' => $errors)); $twig->display('error_box.html.twig', array('errors' => $errors));
@@ -51,12 +54,10 @@ else
//show form //show form
$twig->display('account.change_password.html.twig'); $twig->display('account.change_password.html.twig');
} }
else else {
{
$org_pass = $new_password; $org_pass = $new_password;
if(USE_ACCOUNT_SALT) if(USE_ACCOUNT_SALT) {
{
$salt = generateRandomString(10, false, true, true); $salt = generateRandomString(10, false, true, true);
$new_password = $salt . $new_password; $new_password = $salt . $new_password;
$account_logged->setCustomField('salt', $salt); $account_logged->setCustomField('salt', $salt);
@@ -68,17 +69,18 @@ else
$account_logged->logAction('Account password changed.'); $account_logged->logAction('Account password changed.');
$message = ''; $message = '';
if($config['mail_enabled'] && $config['send_mail_when_change_password']) if($config['mail_enabled'] && $config['send_mail_when_change_password']) {
{
$mailBody = $twig->render('mail.password_changed.html.twig', array( $mailBody = $twig->render('mail.password_changed.html.twig', array(
'new_password' => $org_pass, 'new_password' => $org_pass,
'ip' => get_browser_real_ip(), 'ip' => get_browser_real_ip(),
)); ));
if(_mail($account_logged->getEMail(), $config['lua']['serverName']." - Changed password", $mailBody)) if(_mail($account_logged->getEMail(), $config['lua']['serverName']." - Changed password", $mailBody)) {
$message = '<br/><small>Your new password were send on email address <b>'.$account_logged->getEMail().'</b>.</small>'; $message = '<br/><small>Your new password were send on email address <b>' . $account_logged->getEMail() . '</b>.</small>';
else }
else {
$message = '<br/><p class="error">An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log</p>'; $message = '<br/><p class="error">An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log</p>';
}
} }
$twig->display('success.html.twig', array( $twig->display('success.html.twig', array(

View File

@@ -26,8 +26,13 @@ if(config('account_create_character_create')) {
} }
$account_type = 'number'; $account_type = 'number';
if(USE_ACCOUNT_NAME) { if (config('account_login_by_email')) {
$account_type = 'name'; $account_type = 'Email Address';
}
else {
if(USE_ACCOUNT_NAME) {
$account_type = 'name';
}
} }
$errors = array(); $errors = array();
@@ -72,14 +77,6 @@ if($save)
$errors['country'] = 'Country is invalid.'; $errors['country'] = 'Country is invalid.';
} }
if(config('recaptcha_enabled'))
{
require_once LIBS . 'GoogleReCAPTCHA.php';
if (!GoogleReCAPTCHA::verify('register')) {
$errors['verification'] = GoogleReCAPTCHA::getErrorMessage();
}
}
// password // password
if(empty($password)) { if(empty($password)) {
$errors['password'] = 'Please enter the password for your new account.'; $errors['password'] = 'Please enter the password for your new account.';
@@ -149,7 +146,9 @@ if($save)
} }
} }
$hooks->trigger(HOOK_ACCOUNT_CREATE_AFTER_SUBMIT, $params); if (!$hooks->trigger(HOOK_ACCOUNT_CREATE_POST, $params)) {
return;
}
if(config('account_create_character_create')) { if(config('account_create_character_create')) {
$character_name = isset($_POST['name']) ? stripslashes(ucwords(strtolower($_POST['name']))) : null; $character_name = isset($_POST['name']) ? stripslashes(ucwords(strtolower($_POST['name']))) : null;
@@ -162,9 +161,12 @@ if($save)
if(empty($errors)) if(empty($errors))
{ {
$hasBeenCreatedByEMail = false;
$new_account = new OTS_Account(); $new_account = new OTS_Account();
if (config('account_login_by_email')) { if (config('account_login_by_email')) {
$new_account->createWithEmail($email); $new_account->createWithEmail($email);
$hasBeenCreatedByEMail = true;
} }
else { else {
if(USE_ACCOUNT_NAME) if(USE_ACCOUNT_NAME)
@@ -181,7 +183,6 @@ if($save)
$new_account->setPassword(encrypt($password)); $new_account->setPassword(encrypt($password));
$new_account->setEMail($email); $new_account->setEMail($email);
$new_account->unblock();
$new_account->save(); $new_account->save();
if(USE_ACCOUNT_SALT) if(USE_ACCOUNT_SALT)
@@ -253,14 +254,21 @@ if($save)
$character_created = $createCharacter->doCreate($character_name, $character_sex, $character_vocation, $character_town, $new_account, $errors); $character_created = $createCharacter->doCreate($character_name, $character_sex, $character_vocation, $character_town, $new_account, $errors);
if (!$character_created) { if (!$character_created) {
error('There was an error creating your character. Please create your character later in account management page.'); error('There was an error creating your character. Please create your character later in account management page.');
error(implode(' ', $errors));
} }
} }
if($config['account_create_auto_login']) { if(config('account_create_auto_login')) {
$_POST['account_login'] = USE_ACCOUNT_NAME ? $account_name : $account_id; if ($hasBeenCreatedByEMail) {
$_POST['account_login'] = $email;
}
else {
$_POST['account_login'] = USE_ACCOUNT_NAME ? $account_name : $account_id;
}
$_POST['password_login'] = $password2; $_POST['password_login'] = $password2;
require SYSTEM . 'login.php'; require PAGES . 'account/login.php';
header('Location: ' . getLink('account/manage')); header('Location: ' . getLink('account/manage'));
} }

View File

@@ -0,0 +1,124 @@
<?php
/**
* Login
*
* @package MyAAC
* @author Gesior <jerzyskalski@wp.pl>
* @author Slawkens <slawkens@gmail.com>
* @copyright 2023 MyAAC
* @link https://my-aac.org
*/
defined('MYAAC') or die('Direct access not allowed!');
$title = 'Login';
// new login with data from form
if(!$logged && isset($_POST['account_login'], $_POST['password_login']))
{
$login_account = $_POST['account_login'];
$login_password = $_POST['password_login'];
$remember_me = isset($_POST['remember_me']);
if(!empty($login_account) && !empty($login_password))
{
if($cache->enabled())
{
$tmp = '';
if($cache->fetch('failed_logins', $tmp))
{
$tmp = unserialize($tmp);
$to_remove = array();
foreach($tmp as $ip => $t)
{
if(time() - $t['last'] >= 5 * 60)
$to_remove[] = $ip;
}
foreach($to_remove as $ip)
unset($tmp[$ip]);
}
else
$tmp = array();
$ip = $_SERVER['REMOTE_ADDR'];
$t = $tmp[$ip] ?? null;
}
$account_logged = new OTS_Account();
if (config('account_login_by_email')) {
$account_logged->findByEMail($login_account);
}
if (!config('account_login_by_email') || config('account_login_by_email_fallback')) {
if(USE_ACCOUNT_NAME || USE_ACCOUNT_NUMBER) {
$account_logged->find($login_account);
} else {
$account_logged->load($login_account, true);
}
}
if($account_logged->isLoaded() && encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password) == $account_logged->getPassword()
&& (!isset($t) || $t['attempts'] < 5)
)
{
setSession('account', $account_logged->getId());
setSession('password', encrypt((USE_ACCOUNT_SALT ? $account_logged->getCustomField('salt') : '') . $login_password));
if($remember_me) {
setSession('remember_me', true);
}
$logged = true;
$logged_flags = $account_logged->getWebFlags();
if(isset($_POST['admin']) && !admin()) {
$errors[] = 'This account has no admin privileges.';
unsetSession('account');
unsetSession('password');
unsetSession('remember_me');
$logged = false;
}
else {
$account_logged->setCustomField('web_lastlogin', time());
}
$hooks->trigger(HOOK_LOGIN, array('account' => $account_logged, 'password' => $login_password, 'remember_me' => $remember_me));
}
else
{
$hooks->trigger(HOOK_LOGIN_ATTEMPT, array('account' => $login_account, 'password' => $login_password, 'remember_me' => $remember_me));
$errorMessage = getAccountLoginByLabel() . ' or password is not correct.';
// temporary solution for blocking failed login attempts
if($cache->enabled())
{
if(isset($t))
{
$t['attempts']++;
$t['last'] = time();
if($t['attempts'] >= 5)
$errors[] = 'A wrong password has been entered 5 times in a row. You are unable to log into your account for the next 5 minutes. Please wait.';
else
$errors[] = $errorMessage;
}
else
{
$t = array('attempts' => 1, 'last' => time());
$errors[] = $errorMessage;
}
$tmp[$ip] = $t;
$cache->set('failed_logins', serialize($tmp), 60 * 60); // save for 1 hour
}
else {
$errors[] = $errorMessage;
}
}
}
else {
$errors[] = 'Please enter your ' . getAccountLoginByLabel() . ' and password.';
$hooks->trigger(HOOK_LOGIN_ATTEMPT, array('account' => $login_account, 'password' => $login_password, 'remember_me' => $remember_me));
}
$hooks->trigger(HOOK_ACCOUNT_LOGIN_POST);
}

View File

@@ -40,7 +40,7 @@ elseif($action == 'step1' && $action_type == 'email')
{ {
if($account->getCustomField('email_next') < time()) if($account->getCustomField('email_next') < time())
echo 'Please enter e-mail to account with this character.<BR> echo 'Please enter e-mail to account with this character.<BR>
<form action="?subtopic=lostaccount&action=sendcode" method=post> <form action="' . getLink('account/lost') . '?action=sendcode" method=post>
<input type=hidden name="character"> <input type=hidden name="character">
<table cellspacing=1 cellpadding=4 border=0 width=100%> <table cellspacing=1 cellpadding=4 border=0 width=100%>
<TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Please enter e-mail to account</B></TD></TR> <TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Please enter e-mail to account</B></TD></TR>
@@ -68,7 +68,7 @@ elseif($action == 'step1' && $action_type == 'email')
else else
echo 'Invalid player name format. If you have other characters on account try with other name.'; echo 'Invalid player name format. If you have other characters on account try with other name.';
echo '<BR /><TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center"> echo '<BR /><TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
<a href="?subtopic=lostaccount" border="0"><IMG SRC="'.$template_path.'/images/global/buttons/sbutton_back.gif" NAME="Back" ALT="Back" BORDER=0 WIDTH=120 HEIGHT=18></a></div> <a href="' . getLink('account/lost') . '" border="0"><IMG SRC="'.$template_path.'/images/global/buttons/sbutton_back.gif" NAME="Back" ALT="Back" BORDER=0 WIDTH=120 HEIGHT=18></a></div>
</TD></TR></FORM></TABLE></TABLE>'; </TD></TR></FORM></TABLE></TABLE>';
} }
elseif($action == 'sendcode') elseif($action == 'sendcode')
@@ -95,8 +95,8 @@ elseif($action == 'sendcode')
<p>Account name: '.$account->getName().'</p> <p>Account name: '.$account->getName().'</p>
<br /> <br />
To do so, please click this link: To do so, please click this link:
<p><a href="' . BASE_URL . '?subtopic=lostaccount&action=checkcode&code='.$newcode.'&character='.urlencode($nick).'">'.BASE_URL.'/?subtopic=lostaccount&action=checkcode&code='.$newcode.'&character='.urlencode($nick).'</a></p> <p><a href="' . getLink('account/lost') . '?action=checkcode&code='.$newcode.'&character='.urlencode($nick).'">'.BASE_URL.'/?subtopic=lostaccount&action=checkcode&code='.$newcode.'&character='.urlencode($nick).'</a></p>
<p>or open page: <i>' . BASE_URL . '?subtopic=lostaccount&action=checkcode</i> and in field "code" write <b>'.$newcode.'</b></p> <p>or open page: <i>' . getLink('account/lost') . '?action=checkcode</i> and in field "code" write <b>'.$newcode.'</b></p>
<br/> <br/>
<p>If you did not request a password change, you may ignore this message and your password will remain unchanged.'; <p>If you did not request a password change, you may ignore this message and your password will remain unchanged.';
@@ -131,7 +131,7 @@ elseif($action == 'sendcode')
else else
echo 'Invalid player name format. If you have other characters on account try with other name.'; echo 'Invalid player name format. If you have other characters on account try with other name.';
echo '<BR /><TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center"> echo '<BR /><TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0 WIDTH=100%><TR><TD><div style="text-align:center">
<a href="?subtopic=lostaccount&action=step1&action_type=email&nick='.urlencode($nick).'" border="0"><IMG SRC="'.$template_path.'/images/global/buttons/sbutton_back.gif" NAME="Back" ALT="Back" BORDER=0 WIDTH=120 HEIGHT=18></a></div> <a href="' . getLink('account/lost') . '?action=step1&action_type=email&nick='.urlencode($nick).'" border="0"><IMG SRC="'.$template_path.'/images/global/buttons/sbutton_back.gif" NAME="Back" ALT="Back" BORDER=0 WIDTH=120 HEIGHT=18></a></div>
</TD></TR></FORM></TABLE></TABLE>'; </TD></TR></FORM></TABLE></TABLE>';
} }
elseif($action == 'step1' && $action_type == 'reckey') elseif($action == 'step1' && $action_type == 'reckey')
@@ -150,7 +150,7 @@ elseif($action == 'step1' && $action_type == 'reckey')
if(!empty($account_key)) if(!empty($account_key))
{ {
echo 'If you enter right recovery key you will see form to set new e-mail and password to account. To this e-mail will be send your new password and account name.<BR> echo 'If you enter right recovery key you will see form to set new e-mail and password to account. To this e-mail will be send your new password and account name.<BR>
<FORM ACTION="?subtopic=lostaccount&action=step2" METHOD=post> <FORM ACTION="' . getLink('account/lost') . '?action=step2" METHOD=post>
<TABLE CELLSPACING=1 CELLPADDING=4 BORDER=0 WIDTH=100%> <TABLE CELLSPACING=1 CELLPADDING=4 BORDER=0 WIDTH=100%>
<TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Please enter your recovery key</B></TD></TR> <TR><TD BGCOLOR="'.$config['vdarkborder'].'" class="white"><B>Please enter your recovery key</B></TD></TR>
<TR><TD BGCOLOR="'.$config['darkborder'].'"> <TR><TD BGCOLOR="'.$config['darkborder'].'">

View File

@@ -11,6 +11,7 @@
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
$title = 'Account Management'; $title = 'Account Management';
require __DIR__ . '/login.php';
require __DIR__ . '/base.php'; require __DIR__ . '/base.php';
if(!$logged) { if(!$logged) {
@@ -85,7 +86,7 @@ $twig->display('account.management.html.twig', array(
'email_request' => $email_request, 'email_request' => $email_request,
'email_new_time' => $email_new_time, 'email_new_time' => $email_new_time,
'email_new' => isset($email_new) ? $email_new : '', 'email_new' => isset($email_new) ? $email_new : '',
'account' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId(), 'account' => (USE_ACCOUNT_NAME ? $account_logged->getName() : (USE_ACCOUNT_NUMBER ? $account_logged->getNumber() : $account_logged->getId())),
'account_email' => $account_email, 'account_email' => $account_email,
'account_created' => $account_created, 'account_created' => $account_created,
'account_status' => $account_status, 'account_status' => $account_status,

View File

@@ -10,7 +10,7 @@
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
$title = 'Changelog'; $title = 'Changelog';
$_page = (int)$_GET['page'] ?? 0; $_page = isset($_GET['page']) ? (int)$_GET['page'] : 0;
$limit = 30; $limit = 30;
$offset = $_page * $limit; $offset = $_page * $limit;
$next_page = false; $next_page = false;

View File

@@ -201,8 +201,7 @@ if($player->isLoaded() && !$player->isDeleted())
unset($storage); unset($storage);
} }
if($config['characters']['equipment']) { if($config['characters']['equipment'] && $db->hasTable('player_items') && $db->hasColumn('player_items', 'pid') && $db->hasColumn('player_items', 'sid') && $db->hasColumn('player_items', 'itemtype')) {
global $db;
$eq_sql = $db->query('SELECT `pid`, `itemtype` FROM player_items WHERE player_id = '.$player->getId().' AND (`pid` >= 1 and `pid` <= 10)'); $eq_sql = $db->query('SELECT `pid`, `itemtype` FROM player_items WHERE player_id = '.$player->getId().' AND (`pid` >= 1 and `pid` <= 10)');
$equipment = array(); $equipment = array();
foreach($eq_sql as $eq) foreach($eq_sql as $eq)
@@ -285,7 +284,7 @@ WHERE killers.death_id = '".$death['id']."' ORDER BY killers.final_hit DESC, kil
$deaths[] = array('time' => $death['date'], 'description' => $description . '.'); $deaths[] = array('time' => $death['date'], 'description' => $description . '.');
} }
} }
} else { } else if ($db->hasColumn('player_deaths', 'time') && $db->hasColumn('player_deaths', 'level') && $db->hasColumn('player_deaths', 'killed_by') && $db->hasColumn('player_deaths', 'is_player')) {
$mostdamage = ''; $mostdamage = '';
if($db->hasColumn('player_deaths', 'mostdamage_by')) if($db->hasColumn('player_deaths', 'mostdamage_by'))
$mostdamage = ', `mostdamage_by`, `mostdamage_is_player`, `unjustified`, `mostdamage_unjustified`'; $mostdamage = ', `mostdamage_by`, `mostdamage_is_player`, `unjustified`, `mostdamage_unjustified`';

View File

@@ -101,14 +101,15 @@ if(isset($_GET['image']))
return; return;
} }
$images = $images = Cache::remember('gallery_' . ($canEdit ? '1' : '0'), 60, function () use ($db, $canEdit) {
$db->query('SELECT `id`, `comment`, `image`, `author`, `thumb`' . return $db->query('SELECT `id`, `comment`, `image`, `author`, `thumb`' .
($canEdit ? ', `hidden`, `ordering`' : '') . ($canEdit ? ', `hidden`, `ordering`' : '') .
' FROM `' . TABLE_PREFIX . 'gallery`' . ' FROM `' . TABLE_PREFIX . 'gallery`' .
(!$canEdit ? ' WHERE `hidden` != 1' : '') . (!$canEdit ? ' WHERE `hidden` != 1' : '') .
' ORDER BY `ordering`;'); ' ORDER BY `ordering`;')->fetchAll(PDO::FETCH_ASSOC);
});
$last = $images->rowCount(); $last = count($images);
if(!$last) if(!$last)
{ {
?> ?>

View File

@@ -19,23 +19,23 @@ $guilds_list->orderBy("name");
$guilds = array(); $guilds = array();
if(count($guilds_list) > 0) if(count($guilds_list) > 0)
{ {
foreach ($guilds_list as $guild) { foreach ($guilds_list as $guild) {
$guild_logo = $guild->getCustomField('logo_name'); $guild_logo = $guild->getCustomField('logo_name');
if (empty($guild_logo) || !file_exists(GUILD_IMAGES_DIR . $guild_logo)) if (empty($guild_logo) || !file_exists(GUILD_IMAGES_DIR . $guild_logo))
$guild_logo = "default.gif"; $guild_logo = "default.gif";
$description = $guild->getCustomField('description'); $description = $guild->getCustomField('description');
$description_with_lines = str_replace(array("\r\n", "\n", "\r"), '<br />', $description, $count); $description_with_lines = str_replace(array("\r\n", "\n", "\r"), '<br />', $description, $count);
if ($count < $config['guild_description_lines_limit']) if ($count < $config['guild_description_lines_limit'])
$description = wordwrap(nl2br($description), 60, "<br />", true); $description = wordwrap(nl2br($description), 60, "<br />", true);
$guildName = $guild->getName(); $guildName = $guild->getName();
$guilds[] = array('name' => $guildName, 'logo' => $guild_logo, 'link' => getGuildLink($guildName, false), 'description' => $description); $guilds[] = array('name' => $guildName, 'logo' => $guild_logo, 'link' => getGuildLink($guildName, false), 'description' => $description);
} }
}; };
$twig->display('guilds.list.html.twig', array( $twig->display('guilds.list.html.twig', array(
'guilds' => $guilds, 'guilds' => $guilds,
'logged' => isset($logged) ? $logged : false, 'logged' => isset($logged) ? $logged : false,
'isAdmin' => admin(), 'isAdmin' => admin(),
)); ));

View File

@@ -49,78 +49,78 @@ $players_from_account_in_guild = array();
$players_from_account_ids = array(); $players_from_account_ids = array();
if($logged) if($logged)
{ {
$account_players = $account_logged->getPlayers(); $account_players = $account_logged->getPlayers();
foreach($account_players as $player) foreach($account_players as $player)
{ {
$players_from_account_ids[] = $player->getId(); $players_from_account_ids[] = $player->getId();
$player_rank = $player->getRank(); $player_rank = $player->getRank();
if($player_rank->isLoaded()) if($player_rank->isLoaded())
{ {
foreach($rank_list as $rank_in_guild) foreach($rank_list as $rank_in_guild)
{ {
if($guild_owner->isLoaded() && $rank_in_guild->isLoaded() && $player_rank->isLoaded() && if($guild_owner->isLoaded() && $rank_in_guild->isLoaded() && $player_rank->isLoaded() &&
$rank_in_guild->getId() == $player_rank->getId()) $rank_in_guild->getId() == $player_rank->getId())
{ {
$players_from_account_in_guild[] = $player->getName(); $players_from_account_in_guild[] = $player->getName();
if($guild_owner->getId() == $player->getId()) if($guild_owner->getId() == $player->getId())
{ {
$guild_vice = true; $guild_vice = true;
$guild_leader = true; $guild_leader = true;
} }
else if($player_rank->getLevel() > 1) else if($player_rank->getLevel() > 1)
{ {
$guild_vice = true; $guild_vice = true;
$level_in_guild = $player_rank->getLevel(); $level_in_guild = $player_rank->getLevel();
} }
} }
} }
} }
} }
} }
//show guild page //show guild page
$guild_logo = $guild->getCustomField('logo_name'); $guild_logo = $guild->getCustomField('logo_name');
if(empty($guild_logo) || !file_exists(GUILD_IMAGES_DIR . $guild_logo)) if(empty($guild_logo) || !file_exists(GUILD_IMAGES_DIR . $guild_logo))
$guild_logo = "default.gif"; $guild_logo = "default.gif";
$description = $guild->getCustomField('description'); $description = $guild->getCustomField('description');
$description_with_lines = str_replace(array("\r\n", "\n", "\r"), '<br />', $description, $count); $description_with_lines = str_replace(array("\r\n", "\n", "\r"), '<br />', $description, $count);
if($count < $config['guild_description_lines_limit']) if($count < $config['guild_description_lines_limit'])
$description = wordwrap(nl2br($description), 60, "<br />", true); $description = wordwrap(nl2br($description), 60, "<br />", true);
//$description = $description_with_lines; //$description = $description_with_lines;
$guild_owner = $guild->getOwner(); $guild_owner = $guild->getOwner();
if($guild_owner->isLoaded()) if($guild_owner->isLoaded())
$guild_owner_name = $guild_owner->getName(); $guild_owner_name = $guild_owner->getName();
$guild_members = array(); $guild_members = array();
foreach($rank_list as $rank) foreach($rank_list as $rank)
{ {
if($db->hasTable(GUILD_MEMBERS_TABLE)) if($db->hasTable(GUILD_MEMBERS_TABLE))
$players_with_rank = $db->query('SELECT `players`.`id` as `id`, `' . GUILD_MEMBERS_TABLE . '`.`rank_id` as `rank_id` FROM `players`, `' . GUILD_MEMBERS_TABLE . '` WHERE `' . GUILD_MEMBERS_TABLE . '`.`rank_id` = ' . $rank->getId() . ' AND `players`.`id` = `' . GUILD_MEMBERS_TABLE . '`.`player_id` ORDER BY `name`;'); $players_with_rank = $db->query('SELECT `players`.`id` as `id`, `' . GUILD_MEMBERS_TABLE . '`.`rank_id` as `rank_id` FROM `players`, `' . GUILD_MEMBERS_TABLE . '` WHERE `' . GUILD_MEMBERS_TABLE . '`.`rank_id` = ' . $rank->getId() . ' AND `players`.`id` = `' . GUILD_MEMBERS_TABLE . '`.`player_id` ORDER BY `name`;');
else if($db->hasColumn('players', 'rank_id')) else if($db->hasColumn('players', 'rank_id'))
$players_with_rank = $db->query('SELECT `id`, `rank_id` FROM `players` WHERE `rank_id` = ' . $rank->getId() . ' AND `deleted` = 0;'); $players_with_rank = $db->query('SELECT `id`, `rank_id` FROM `players` WHERE `rank_id` = ' . $rank->getId() . ' AND `deleted` = 0;');
$players_with_rank_number = $players_with_rank->rowCount(); $players_with_rank_number = $players_with_rank->rowCount();
if($players_with_rank_number > 0) if($players_with_rank_number > 0)
{ {
$members = array(); $members = array();
foreach($players_with_rank as $result) foreach($players_with_rank as $result)
{ {
$player = new OTS_Player(); $player = new OTS_Player();
$player->load($result['id']); $player->load($result['id']);
if(!$player->isLoaded()) if(!$player->isLoaded())
continue; continue;
$members[] = $player; $members[] = $player;
} }
$guild_members[] = array( $guild_members[] = array(
'rank_name' => $rank->getName(), 'rank_name' => $rank->getName(),
'rank_level' => $rank->getLevel(), 'rank_level' => $rank->getLevel(),
'members' => $members 'members' => $members
); );
} }
} }
include(SYSTEM . 'libs/pot/InvitesDriver.php'); include(SYSTEM . 'libs/pot/InvitesDriver.php');
@@ -129,37 +129,39 @@ $invited_list = $guild->listInvites();
$show_accept_invite = 0; $show_accept_invite = 0;
if($logged && count($invited_list) > 0) if($logged && count($invited_list) > 0)
{ {
foreach($invited_list as $invited_player) foreach($invited_list as $invited_player)
{ {
if(count($account_players) > 0) if(count($account_players) > 0)
{ {
foreach($account_players as $player_from_acc) foreach($account_players as $player_from_acc)
{ {
if($player_from_acc->isLoaded() && $invited_player->isLoaded() && $player_from_acc->getName() == $invited_player->getName()) if($player_from_acc->isLoaded() && $invited_player->isLoaded() && $player_from_acc->getName() == $invited_player->getName())
$show_accept_invite++; $show_accept_invite++;
} }
} }
} }
} }
$useGuildNick = false; $useGuildNick = false;
if($db->hasColumn('players', 'guildnick')) if($db->hasColumn('players', 'guildnick'))
$useGuildNick = true; $useGuildNick = true;
$twig->display('guilds.view.html.twig', array( $twig->display('guilds.view.html.twig', array(
'logo' => $guild_logo, 'logo' => $guild_logo,
'guild_name' => $guild_name, 'guild' => $guild,
'description' => $description, 'guild_id' => $guild->getId(),
'guild_owner' => $guild_owner->isLoaded() ? $guild_owner : null, 'guild_name' => $guild_name,
'guild_creation_date' => $guild->getCreationData(), 'description' => $description,
'guild_members' => $guild_members, 'guild_owner' => $guild_owner->isLoaded() ? $guild_owner : null,
'players_from_account_ids' => $players_from_account_ids, 'guild_creation_date' => $guild->getCreationData(),
'players_from_account_in_guild' => $players_from_account_in_guild, 'guild_members' => $guild_members,
'level_in_guild' => $level_in_guild, 'players_from_account_ids' => $players_from_account_ids,
'isLeader' => $guild_leader, 'players_from_account_in_guild' => $players_from_account_in_guild,
'isVice' => $guild_vice, 'level_in_guild' => $level_in_guild,
'logged' => $logged, 'isLeader' => $guild_leader,
'invited_list' => $invited_list, 'isVice' => $guild_vice,
'show_accept_invite' => $show_accept_invite, 'logged' => $logged,
'useGuildNick' => $useGuildNick 'invited_list' => $invited_list,
'show_accept_invite' => $show_accept_invite,
'useGuildNick' => $useGuildNick
)); ));

View File

@@ -14,17 +14,17 @@ $title = 'Houses';
$errors = array(); $errors = array();
if(!$db->hasColumn('houses', 'name')) { if(!$db->hasColumn('houses', 'name')) {
$errors[] = 'Houses list is not available on this server.'; $errors[] = 'Houses list is not available on this server.';
$twig->display('houses.html.twig', array( $twig->display('houses.html.twig', array(
'errors' => $errors 'errors' => $errors
)); ));
return; return;
} }
$rentType = trim(strtolower($config['lua']['houseRentPeriod'])); $rentType = trim(strtolower($config['lua']['houseRentPeriod']));
if($rentType != 'yearly' && $rentType != 'monthly' && $rentType != 'weekly' && $rentType != 'daily') if($rentType != 'yearly' && $rentType != 'monthly' && $rentType != 'weekly' && $rentType != 'daily')
$rentType = 'never'; $rentType = 'never';
$state = ''; $state = '';
$order = ''; $order = '';
@@ -32,116 +32,116 @@ $type = '';
if(isset($_GET['page']) && $_GET['page'] == 'view' && isset($_REQUEST['house'])) if(isset($_GET['page']) && $_GET['page'] == 'view' && isset($_REQUEST['house']))
{ {
$beds = array("", "one", "two", "three", "fourth", "fifth"); $beds = array("", "one", "two", "three", "fourth", "fifth");
$houseName = $_REQUEST['house']; $houseName = $_REQUEST['house'];
$houseId = (Validator::number($_REQUEST['house']) ? $_REQUEST['house'] : -1); $houseId = (Validator::number($_REQUEST['house']) ? $_REQUEST['house'] : -1);
$selectHouse = $db->query('SELECT * FROM ' . $db->tableName('houses') . ' WHERE ' . $db->fieldName('name') . ' LIKE ' . $db->quote($houseName) . ' OR `id` = ' . $db->quote($houseId)); $selectHouse = $db->query('SELECT * FROM ' . $db->tableName('houses') . ' WHERE ' . $db->fieldName('name') . ' LIKE ' . $db->quote($houseName) . ' OR `id` = ' . $db->quote($houseId));
$house = array(); $house = array();
if($selectHouse->rowCount() > 0) if($selectHouse->rowCount() > 0)
{ {
$house = $selectHouse->fetch(); $house = $selectHouse->fetch();
$houseId = $house['id']; $houseId = $house['id'];
$title = $house['name'] . ' - ' . $title; $title = $house['name'] . ' - ' . $title;
$imgPath = 'images/houses/' . $houseId . '.gif'; $imgPath = 'images/houses/' . $houseId . '.gif';
if(!file_exists($imgPath)) { if(!file_exists($imgPath)) {
$imgPath = 'images/houses/default.jpg'; $imgPath = 'images/houses/default.jpg';
} }
$bedsMessage = null; $bedsMessage = null;
$houseBeds = $house['beds']; $houseBeds = $house['beds'];
if($houseBeds > 0) if($houseBeds > 0)
$bedsMessage = 'House have ' . (isset($beds[$houseBeds]) ? $beds[$houseBeds] : $houseBeds) . ' bed' . ($houseBeds > 1 ? 's' : ''); $bedsMessage = 'House have ' . (isset($beds[$houseBeds]) ? $beds[$houseBeds] : $houseBeds) . ' bed' . ($houseBeds > 1 ? 's' : '');
else else
$bedsMessage = 'This house dont have any beds'; $bedsMessage = 'This house dont have any beds';
$houseOwner = $house['owner']; $houseOwner = $house['owner'];
if($houseOwner > 0) if($houseOwner > 0)
{ {
$guild = NULL; $guild = NULL;
$owner = null; $owner = null;
if(isset($house['guild']) && $house['guild'] == 1) if(isset($house['guild']) && $house['guild'] == 1)
{ {
$guild = new OTS_Guild(); $guild = new OTS_Guild();
$guild->load($houseOwner); $guild->load($houseOwner);
$owner = getGuildLink($guild->getName()); $owner = getGuildLink($guild->getName());
} }
else else
$owner = getCreatureName($houseOwner); $owner = getCreatureName($houseOwner);
if($rentType != 'never' && $house['paid'] > 0) if($rentType != 'never' && $house['paid'] > 0)
{ {
$who = ''; $who = '';
if($guild) if($guild)
$who = $guild->getName(); $who = $guild->getName();
else else
{ {
$player = new OTS_Player(); $player = new OTS_Player();
$player->load($houseOwner); $player->load($houseOwner);
if($player->isLoaded()) if($player->isLoaded())
{ {
$sexs = array('She', 'He'); $sexs = array('She', 'He');
$who = $sexs[$player->getSex()]; $who = $sexs[$player->getSex()];
} }
} }
$owner .= ' ' . $who . ' has paid the rent until ' . date("M d Y, H:i:s", $house['paid']) . ' CEST.'; $owner .= ' ' . $who . ' has paid the rent until ' . date("M d Y, H:i:s", $house['paid']) . ' CEST.';
} }
} }
} }
else else
$errors[] = 'House with name ' . $houseName . ' does not exists.'; $errors[] = 'House with name ' . $houseName . ' does not exists.';
$twig->display('houses.view.html.twig', array( $twig->display('houses.view.html.twig', array(
'errors' => $errors, 'errors' => $errors,
'imgPath' => isset($imgPath) ? $imgPath : null, 'imgPath' => isset($imgPath) ? $imgPath : null,
'houseName' => isset($house['name']) ? $house['name'] : null, 'houseName' => isset($house['name']) ? $house['name'] : null,
'bedsMessage' => isset($bedsMessage) ? $bedsMessage : null, 'bedsMessage' => isset($bedsMessage) ? $bedsMessage : null,
'houseSize' => isset($house['size']) ? $house['size'] : null, 'houseSize' => isset($house['size']) ? $house['size'] : null,
'houseRent' => isset($house['rent']) ? $house['rent'] : null, 'houseRent' => isset($house['rent']) ? $house['rent'] : null,
'owner' => isset($owner) ? $owner : null, 'owner' => isset($owner) ? $owner : null,
'rentType' => isset($rentType) ? $rentType : null 'rentType' => isset($rentType) ? $rentType : null
)); ));
if (count($errors) > 0) { if (count($errors) > 0) {
return; return;
} }
} }
$cleanOldHouse = null; $cleanOldHouse = null;
if(isset($config['lua']['houseCleanOld'])) { if(isset($config['lua']['houseCleanOld'])) {
$cleanOldHouse = (int)(eval('return ' . $config['lua']['houseCleanOld'] . ';') / (24 * 60 * 60)); $cleanOldHouse = (int)(eval('return ' . $config['lua']['houseCleanOld'] . ';') / (24 * 60 * 60));
} }
$housesSearch = false; $housesSearch = false;
if(isset($_POST['town']) && isset($_POST['state']) && isset($_POST['order']) && (isset($_POST['type']) || !$db->hasColumn('houses', 'guild'))) if(isset($_POST['town']) && isset($_POST['state']) && isset($_POST['order']) && (isset($_POST['type']) || !$db->hasColumn('houses', 'guild')))
{ {
$townName = $config['towns'][$_POST['town']]; $townName = $config['towns'][$_POST['town']];
$order = $_POST['order']; $order = $_POST['order'];
$orderby = '`name`'; $orderby = '`name`';
if(!empty($order)) if(!empty($order))
{ {
if($order == 'size') if($order == 'size')
$orderby = '`size`'; $orderby = '`size`';
else if($order == 'rent') else if($order == 'rent')
$orderby = '`rent`'; $orderby = '`rent`';
} }
$town = 'town'; $town = 'town';
if($db->hasColumn('houses', 'town_id')) if($db->hasColumn('houses', 'town_id'))
$town = 'town_id'; $town = 'town_id';
else if($db->hasColumn('houses', 'townid')) else if($db->hasColumn('houses', 'townid'))
$town = 'townid'; $town = 'townid';
$whereby = '`' . $town . '` = ' .(int)$_POST['town']; $whereby = '`' . $town . '` = ' .(int)$_POST['town'];
$state = $_POST['state']; $state = $_POST['state'];
if(!empty($state)) if(!empty($state))
$whereby .= ' AND `owner` ' . ($state == 'free' ? '' : '!'). '= 0'; $whereby .= ' AND `owner` ' . ($state == 'free' ? '' : '!'). '= 0';
$type = isset($_POST['type']) ? $_POST['type'] : NULL; $type = isset($_POST['type']) ? $_POST['type'] : NULL;
if($type == 'guildhalls' && !$db->hasColumn('houses', 'guild')) if($type == 'guildhalls' && !$db->hasColumn('houses', 'guild'))
$type = 'all'; $type = 'all';
if (!empty($type) && $type != 'all') if (!empty($type) && $type != 'all')
{ {
@@ -158,49 +158,51 @@ if(isset($_POST['town']) && isset($_POST['state']) && isset($_POST['order']) &&
} }
} }
$houses_info = $db->query('SELECT * FROM `houses` WHERE ' . $whereby. ' ORDER BY ' . $orderby); $houses_info = $db->query('SELECT * FROM `houses` WHERE ' . $whereby. ' ORDER BY ' . $orderby);
$players_info = $db->query("SELECT `houses`.`id` AS `houseid` , `players`.`name` AS `ownername` FROM `houses` , `players` , `accounts` WHERE `players`.`id` = `houses`.`owner` AND `accounts`.`id` = `players`.`account_id`"); $players_info = $db->query("SELECT `houses`.`id` AS `houseid` , `players`.`name` AS `ownername` FROM `houses` , `players` , `accounts` WHERE `players`.`id` = `houses`.`owner` AND `accounts`.`id` = `players`.`account_id`");
$players = array(); $players = array();
foreach($players_info->fetchAll() as $player) foreach($players_info->fetchAll() as $player)
$players[$player['houseid']] = array('name' => $player['ownername']); $players[$player['houseid']] = array('name' => $player['ownername']);
$houses = array(); $hasTilesColumn = $db->hasColumn('houses', 'tiles');
foreach($houses_info->fetchAll() as $house)
{
$owner = isset($players[$house['id']]) ? $players[$house['id']] : array();
$houseRent = null; $houses = array();
if($db->hasColumn('houses', 'guild') && $house['guild'] == 1 && $house['owner'] != 0) foreach($houses_info->fetchAll() as $house)
{ {
$guild = new OTS_Guild(); $owner = isset($players[$house['id']]) ? $players[$house['id']] : array();
$guild->load($house['owner']);
$houseRent = 'Rented by ' . getGuildLink($guild->getName());
}
else
{
if(!empty($owner['name']))
$houseRent = 'Rented by ' . getPlayerLink($owner['name']);
else
$houseRent = 'Free';
}
$houses[] = array('owner' => $owner, 'name' => $house['name'], 'size' => $house['size'], 'rent' => $house['rent'], 'rentedBy' => $houseRent); $houseRent = null;
} if($db->hasColumn('houses', 'guild') && $house['guild'] == 1 && $house['owner'] != 0)
{
$guild = new OTS_Guild();
$guild->load($house['owner']);
$houseRent = 'Rented by ' . getGuildLink($guild->getName());
}
else
{
if(!empty($owner['name']))
$houseRent = 'Rented by ' . getPlayerLink($owner['name']);
else
$houseRent = 'Free';
}
$housesSearch = true; $houses[] = array('owner' => $owner, 'name' => $house['name'], 'size' => ($hasTilesColumn ? $house['tiles'] : $house['size']), 'rent' => $house['rent'], 'rentedBy' => $houseRent);
}
$housesSearch = true;
} }
$guild = $db->hasColumn('houses', 'guild') ? ' or guildhall' : ''; $guild = $db->hasColumn('houses', 'guild') ? ' or guildhall' : '';
$twig->display('houses.html.twig', array( $twig->display('houses.html.twig', array(
'state' => $state, 'state' => $state,
'order' => $order, 'order' => $order,
'type' => $type, 'type' => $type,
'houseType' => $type == 'guildhalls' ? 'Guildhalls' : 'Houses and Flats', 'houseType' => $type == 'guildhalls' ? 'Guildhalls' : 'Houses and Flats',
'townName' => isset($townName) ? $townName : null, 'townName' => isset($townName) ? $townName : null,
'townId' => isset($_POST['town']) ? $_POST['town'] : null, 'townId' => isset($_POST['town']) ? $_POST['town'] : null,
'guild' => $guild, 'guild' => $guild,
'cleanOldHouse' => isset($cleanOld) ? $cleanOld : null, 'cleanOldHouse' => isset($cleanOld) ? $cleanOld : null,
'housesSearch' => $housesSearch, 'housesSearch' => $housesSearch,
'houses' => isset($houses) ? $houses : null 'houses' => isset($houses) ? $houses : null
)); ));

View File

@@ -182,13 +182,13 @@ if(!$news_cached)
$admin_options = ''; $admin_options = '';
if($canEdit) if($canEdit)
{ {
$admin_options = '<br/><br/><a target="_blank" rel="noopener noreferrer" href="/admin/?p=news&action=edit&id=' . $news['id'] . '" title="Edit"> $admin_options = '<br/><br/><a target="_blank" rel="noopener noreferrer" href="' . ADMIN_URL . '?p=news&action=edit&id=' . $news['id'] . '" title="Edit">
<img src="images/edit.png"/>Edit <img src="images/edit.png"/>Edit
</a> </a>
<a id="delete" target="_blank" rel="noopener noreferrer" href="/admin/?p=news&action=delete&id=' . $news['id'] . '" onclick="return confirm(\'Are you sure?\');" title="Delete"> <a id="delete" target="_blank" rel="noopener noreferrer" href="' . ADMIN_URL . '?p=news&action=delete&id=' . $news['id'] . '" onclick="return confirm(\'Are you sure?\');" title="Delete">
<img src="images/del.png"/>Delete <img src="images/del.png"/>Delete
</a> </a>
<a target="_blank" rel="noopener noreferrer" href="/admin/?p=news&action=hide&id=' . $news['id'] . '" title="' . ($news['hidden'] != 1 ? 'Hide' : 'Show') . '"> <a target="_blank" rel="noopener noreferrer" href="' . ADMIN_URL . '?p=news&action=hide&id=' . $news['id'] . '" title="' . ($news['hidden'] != 1 ? 'Hide' : 'Show') . '">
<img src="images/' . ($news['hidden'] != 1 ? 'success' : 'error') . '.png"/> <img src="images/' . ($news['hidden'] != 1 ? 'success' : 'error') . '.png"/>
' . ($news['hidden'] != 1 ? 'Hide' : 'Show') . ' ' . ($news['hidden'] != 1 ? 'Hide' : 'Show') . '
</a>'; </a>';

View File

@@ -4,9 +4,10 @@
* *
* @package MyAAC * @package MyAAC
* @author Slawkens <slawkens@gmail.com> * @author Slawkens <slawkens@gmail.com>
* @copyright 2021 MyAAC * @copyright 2023 MyAAC
* @link https://my-aac.org * @link https://my-aac.org
*/ */
defined('MYAAC') or die('Direct access not allowed!');
if(!isset($content[0])) if(!isset($content[0]))
$content = ''; $content = '';
@@ -50,15 +51,18 @@ if (false !== $pos = strpos($uri, '?')) {
$uri = rawurldecode($uri); $uri = rawurldecode($uri);
if (BASE_DIR !== '') { if (BASE_DIR !== '') {
$tmp = str_replace_first('/', '', BASE_DIR); $tmp = str_replace_first('/', '', BASE_DIR);
$uri = str_replace_first($tmp . '/', '', $uri); $uri = str_replace_first($tmp, '', $uri);
}
if(0 === strpos($uri, '/')) {
$uri = str_replace_first('/', '', $uri);
} }
define('URI', $uri); define('URI', $uri);
/** @var boolean $load_it */
if(!$load_it) { if(!$load_it) {
// ignore warnings in some functions/plugins // ignore warnings in some functions/plugins
// page is not loaded anyways // page is not loaded anyway
define('PAGE', ''); define('PAGE', '');
return; return;
@@ -115,10 +119,22 @@ $dispatcher = FastRoute\cachedDispatcher(function (FastRoute\RouteCollector $r)
if ($route[0] === '*') { if ($route[0] === '*') {
$route[0] = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD']; $route[0] = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'];
} }
else {
if (is_string($route[0])) {
$route[0] = explode(',', $route[0]);
}
$toUpperCase = function(string $value): string {
return trim(strtoupper($value));
};
// convert to upper case, fast-route accepts only upper case
$route[0] = array_map($toUpperCase, $route[0]);
}
$aliases = [ $aliases = [
[':int', ':string', ':alphanum'], [':int', ':string', ':alphanum'],
[':\d+', ':[A-Za-z0-9-_%+\']+}', ':[A-Za-z0-9]+'], [':\d+', ':[A-Za-z0-9-_%+\' ]+', ':[A-Za-z0-9]+'],
]; ];
// apply aliases // apply aliases
@@ -147,6 +163,10 @@ $found = true;
// old support for pages like /?subtopic=accountmanagement // old support for pages like /?subtopic=accountmanagement
$page = $_REQUEST['p'] ?? ($_REQUEST['subtopic'] ?? ''); $page = $_REQUEST['p'] ?? ($_REQUEST['subtopic'] ?? '');
if(!empty($page) && preg_match('/^[A-z0-9\-]+$/', $page)) { if(!empty($page) && preg_match('/^[A-z0-9\-]+$/', $page)) {
if (isset($_REQUEST['p'])) { // some plugins may require this
$_REQUEST['subtopic'] = $_REQUEST['p'];
}
if (config('backward_support')) { if (config('backward_support')) {
require SYSTEM . 'compat/pages.php'; require SYSTEM . 'compat/pages.php';
} }
@@ -161,7 +181,6 @@ else {
switch ($routeInfo[0]) { switch ($routeInfo[0]) {
case FastRoute\Dispatcher::NOT_FOUND: case FastRoute\Dispatcher::NOT_FOUND:
// ... 404 Not Found // ... 404 Not Found
//var_dump('not found');
/** /**
* Fallback to load page from templates/ or system/pages/ directory * Fallback to load page from templates/ or system/pages/ directory
*/ */
@@ -266,9 +285,10 @@ if(config('backward_support')) {
unset($page); unset($page);
function getDatabasePages() { function getDatabasePages($withHidden = false): array
global $db; {
$pages = $db->query('SELECT `name` FROM ' . TABLE_PREFIX . 'pages'); global $db, $logged_access;
$pages = $db->query('SELECT `name` FROM ' . TABLE_PREFIX . 'pages WHERE ' . ($withHidden ? '' : '`hidden` != 1 AND ') . '`access` <= ' . $db->quote($logged_access));
$ret = []; $ret = [];
if ($pages->rowCount() < 1) { if ($pages->rowCount() < 1) {
@@ -282,7 +302,8 @@ function getDatabasePages() {
return $ret; return $ret;
} }
function loadPageFromFileSystem($page, &$found) { function loadPageFromFileSystem($page, &$found): string
{
$file = SYSTEM . 'pages/' . $page . '.php'; $file = SYSTEM . 'pages/' . $page . '.php';
if (!is_file($file)) { if (!is_file($file)) {
// feature: convert camelCase to snake_case // feature: convert camelCase to snake_case

View File

@@ -7,9 +7,10 @@
* @copyright 2021 MyAAC * @copyright 2021 MyAAC
* @link https://my-aac.org * @link https://my-aac.org
*/ */
defined('MYAAC') or die('Direct access not allowed!');
return [ return [
['GET', '', '__redirect__/news'], // redirect empty URL to news ['GET', '', 'news.php'], // empty URL = show news
['GET', 'news/archive/{id:int}[/]', 'news/archive.php'], ['GET', 'news/archive/{id:int}[/]', 'news/archive.php'],
// block access to some files // block access to some files
@@ -26,12 +27,12 @@ return [
[['GET', 'POST'], 'account/character/sex[/]', 'account/change_sex.php'], [['GET', 'POST'], 'account/character/sex[/]', 'account/change_sex.php'],
[['GET', 'POST'], 'account/character/delete[/]', 'account/delete_character.php'], [['GET', 'POST'], 'account/character/delete[/]', 'account/delete_character.php'],
[['GET', 'POST'], 'account/character/comment[/{name:[A-Za-z0-9-_%+\']+}]', 'account/change_comment.php'], [['GET', 'POST'], 'account/character/comment[/{name:[A-Za-z0-9-_%+\']+}]', 'account/change_comment.php'],
['GET', 'account/confirm_email/{hash:[A-Za-z0-9-_]+}[/]', 'account/confirm_email.php'], ['GET', 'account/confirm_email/{hash:alphanum}[/]', 'account/confirm_email.php'],
['GET', 'bans/{page:\d+}[/]', 'bans.php'], ['GET', 'bans/{page:int}[/]', 'bans.php'],
[['GET', 'POST'], 'characters[/{name:string]', 'characters.php'], [['GET', 'POST'], 'characters[/{name:string}]', 'characters.php'],
['GET', 'changelog[/{page:int}]', 'changelog.php'], ['GET', 'changelog[/{page:int}]', 'changelog.php'],
['GET', 'creatures[/{name:string}]', 'creatures.php'], [['GET', 'POST'], 'creatures[/{name:string}]', 'creatures.php'],
['GET', 'faq[/{action:string}]', 'faq.php'], ['GET', 'faq[/{action:string}]', 'faq.php'],

View File

@@ -0,0 +1,6 @@
{% if new_line is defined and new_line %}
<br/>
{% endif %}
<form action="{% if action is not defined %}{{ getLink('account/manage') }}{% else %}{{ action }}{% endif %}" method="post">
{{ include('buttons.back.html.twig') }}
</form>

View File

@@ -96,6 +96,9 @@
</td> </td>
</tr> </tr>
<tr><td></td><td><span id="password_error" class="FormFieldError">{% if errors.password is defined %}{{ errors.password }}{% endif %}</span></td></tr> <tr><td></td><td><span id="password_error" class="FormFieldError">{% if errors.password is defined %}{{ errors.password }}{% endif %}</span></td></tr>
{{ hook('HOOK_ACCOUNT_CREATE_AFTER_PASSWORD') }}
<tr> <tr>
<td class="LabelV" style="width: 150px"> <td class="LabelV" style="width: 150px">
<span{% if errors.password is defined %} class="red"{% endif %}>Repeat password:</span> <span{% if errors.password is defined %} class="red"{% endif %}>Repeat password:</span>
@@ -108,28 +111,6 @@
<tr><td></td><td><span id="password2_error" class="FormFieldError">{% if errors.password is defined %}{{ errors.password }}{% endif %}</span></td></tr> <tr><td></td><td><span id="password2_error" class="FormFieldError">{% if errors.password is defined %}{{ errors.password }}{% endif %}</span></td></tr>
{{ hook('HOOK_ACCOUNT_CREATE_AFTER_PASSWORDS') }} {{ hook('HOOK_ACCOUNT_CREATE_AFTER_PASSWORDS') }}
{% if config.recaptcha_enabled %}
{% if config.recaptcha_type == 'v3' %}
<input type="hidden" name="g-recaptcha-response" id="g-recaptcha-response" />
{% elseif config.recaptcha_type == 'v2-invisible' %}
<div class="g-recaptcha" data-sitekey="{{ config.recaptcha_site_key }}" data-bind="login-submit"></div>
{% elseif config.recaptcha_type == 'v2-checkbox' %}
<tr>
<td class="LabelV" style="width: 150px">
<span{% if errors.verification[0] is not null %} class="red"{% endif %}>Verification:</span>
</td>
<td>
<div class="g-recaptcha" data-sitekey="{{ config.recaptcha_site_key }}" data-theme="{{ config.recaptcha_v2_theme }}"></div>
</td>
</tr>
{% if errors.verification is defined %}
<tr><td></td><td><span class="FormFieldError">{{ errors.verification }}</span></td></tr>
{% endif %}
{% endif %}
{% endif %}
{{ hook('HOOK_ACCOUNT_CREATE_AFTER_RECAPTCHA') }}
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -344,11 +325,7 @@
</table> </table>
</form> </form>
{{ hook('HOOK_ACCOUNT_CREATE_AFTER_FORM') }} {{ hook('HOOK_ACCOUNT_CREATE_AFTER_FORM') }}
<script type="text/javascript" src="tools/check_name.js"></script> <script type="text/javascript" src="{{ constant('BASE_URL') }}tools/check_name.js"></script>
{% if config.recaptcha_enabled and config.recaptcha_type == 'v3' %}
{% set action = 'register' %}
{{ include('google_recaptcha_v3.html.twig') }}
{% endif %}
<style> <style>
#SuggestAccountNumber { #SuggestAccountNumber {
font-size: 7pt; font-size: 7pt;

View File

@@ -1,3 +1,4 @@
{{ hook('HOOK_ACCOUNT_LOGIN_BEFORE_PAGE') }}
Please enter your account {{ account|lower }} and your password.<br/><a href="{{ getLink('account/create') }}">Create an account</a> if you do not have one yet.<br/><br/> Please enter your account {{ account|lower }} and your password.<br/><a href="{{ getLink('account/create') }}">Create an account</a> if you do not have one yet.<br/><br/>
<form action="{{ getLink('account/manage') }}" method="post" > <form action="{{ getLink('account/manage') }}" method="post" >
{% if redirect is not null %} {% if redirect is not null %}
@@ -22,42 +23,27 @@ Please enter your account {{ account|lower }} and your password.<br/><a href="{{
<td> <td>
<div class="InnerTableContainer"> <div class="InnerTableContainer">
<table style="width:100%;" > <table style="width:100%;" >
{{ hook('HOOK_ACCOUNT_LOGIN_BEFORE_PASSWORD') }}
<tr> <tr>
<td class="LabelV" > <td class="LabelV" >
<span{% if error is not null %} class="red"{% endif %}>{{ account_login_by }}:</span> <span{% if error is not null %} class="red"{% endif %}>{{ account_login_by }}:</span>
</td> </td>
<td style="width:100%;" ><input type="text" name="account_login" size="30" maxlength="30" autofocus/></td> <td style="width:100%;" ><input type="text" name="account_login" size="30" maxlength="30" autofocus/></td>
</tr> </tr>
{{ hook('HOOK_ACCOUNT_LOGIN_AFTER_ACCOUNT') }}
<tr> <tr>
<td class="LabelV" > <td class="LabelV" >
<span{% if error is not null %} class="red"{% endif %}>Password:</span> <span{% if error is not null %} class="red"{% endif %}>Password:</span>
</td> </td>
<td><input type="password" name="password_login" size="30" maxlength="29" ></td> <td><input type="password" name="password_login" size="30" maxlength="29" ></td>
</tr> </tr>
{{ hook('HOOK_ACCOUNT_LOGIN_AFTER_PASSWORD') }}
<tr> <tr>
<td class="LabelV" ></td> <td class="LabelV" ></td>
<td><input type="checkbox" id="remember_me" name="remember_me" value="true" /> <td><input type="checkbox" id="remember_me" name="remember_me" value="true" />
<label for="remember_me"> Remember me</label></td> <label for="remember_me"> Remember me</label></td>
</tr> </tr>
{% if config.recaptcha_enabled %} {{ hook('HOOK_ACCOUNT_LOGIN_AFTER_REMEMBER_ME') }}
{% if config.recaptcha_type == 'v3' %}
<input type="hidden" name="g-recaptcha-response" id="g-recaptcha-response" />
{% elseif config.recaptcha_type == 'v2-invisible' %}
<div class="g-recaptcha" data-sitekey="{{ config.recaptcha_site_key }}" data-bind="login-submit"></div>
{% elseif config.recaptcha_type == 'v2-checkbox' %}
<tr>
<td class="LabelV" style="width: 150px">
<span{% if error is not null %} class="red"{% endif %}>Verification:</span>
</td>
<td>
<div class="g-recaptcha" data-sitekey="{{ config.recaptcha_site_key }}" data-theme="{{ config.recaptcha_v2_theme }}"></div>
</td>
</tr>
{% endif %}
{% endif %}
{% if error is not null %}
<tr><td></td><td><span class="FormFieldError">{{ error }}</span></td></tr>
{% endif %}
</table> </table>
</div> </div>
</td> </td>
@@ -90,7 +76,3 @@ Please enter your account {{ account|lower }} and your password.<br/><a href="{{
</td> </td>
</tr> </tr>
</table> </table>
{% if config.recaptcha_enabled and config.recaptcha_type == 'v3' %}
{% set action = 'login' %}
{{ include('google_recaptcha_v3.html.twig') }}
{% endif %}

View File

@@ -89,10 +89,12 @@
<a name="General+Information"></a> <a name="General+Information"></a>
<h2>General Information</h2> <h2>General Information</h2>
<table width="100%"> <table width="100%">
{% if not config.account_login_by_email or config.account_login_by_email_fallback %}
<tr style="background-color: {{ config.lightborder }};" > <tr style="background-color: {{ config.lightborder }};" >
<td style="width: 90px;">Account {% if constant('USE_ACCOUNT_NAME') %}Name{% else %}Number{% endif %}:</td> <td style="width: 90px;">Account {% if constant('USE_ACCOUNT_NAME') %}Name{% else %}Number{% endif %}:</td>
<td>{{ account }}</td> <td>{{ account }}</td>
</tr> </tr>
{% endif %}
<tr style="background-color: {{ config.darkborder }};" > <tr style="background-color: {{ config.darkborder }};" >
<td style="width: 90px;">Email Address:</td> <td style="width: 90px;">Email Address:</td>
<td>{{ account_email ~ email_change }} <td>{{ account_email ~ email_change }}
@@ -206,4 +208,4 @@
</tr> </tr>
</table> </table>
</div> </div>
</div> </div>

View File

@@ -25,7 +25,7 @@
</div> </div>
<input type="text" class="form-control" id="account-name-input" name="account_login" placeholder="{{ account_login_by }}" required autofocus> <input type="text" class="form-control" id="account-name-input" name="account_login" placeholder="{{ account_login_by }}" required autofocus>
</div> </div>
{{ hook('HOOK_ADMIN_LOGIN_AFTER_ACCOUNT') }}
<div class="input-group mb-3"> <div class="input-group mb-3">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-key"></i></span> <span class="input-group-text"><i class="fa fa-key"></i></span>
@@ -33,6 +33,7 @@
<input type="password" class="form-control" placeholder="Password" name="password_login" <input type="password" class="form-control" placeholder="Password" name="password_login"
placeholder="Password" required> placeholder="Password" required>
</div> </div>
{{ hook('HOOK_ADMIN_LOGIN_AFTER_PASSWORD') }}
<div class="row"> <div class="row">
<div class="col-8"> <div class="col-8">
<div class="icheck-primary"> <div class="icheck-primary">
@@ -45,6 +46,7 @@
<button type="submit" class="btn btn-info btn-block">Sign In</button> <button type="submit" class="btn btn-info btn-block">Sign In</button>
</div> </div>
</div> </div>
{{ hook('HOOK_ADMIN_LOGIN_AFTER_SIGN_IN') }}
</form> </form>
</div> </div>
</div> </div>

View File

@@ -1,14 +1,14 @@
<div style="text-align: right;"> <div style="text-align: right;">
<a href="admin/?p=pages&action=edit&id={{ page.id }}" title="Edit in Admin Panel" target="_blank"> <a href="?p=pages&action=edit&id={{ page.id }}" title="Edit in Admin Panel" target="_blank">
<img src="images/edit.png"/>Edit <img src="images/edit.png"/>Edit
</a> </a>
<a id="delete" href="admin/?p=pages&action=delete&id={{ page.id }}" onclick="return confirm('Are you sure?');" <a id="delete" href="?p=pages&action=delete&id={{ page.id }}" onclick="return confirm('Are you sure?');"
title="Delete in Admin Panel" target="_blank"> title="Delete in Admin Panel" target="_blank">
<img src="images/del.png"/>Delete <img src="images/del.png"/>Delete
</a> </a>
<a href="admin/?p=pages&action=hide&id={{ page.id }}" <a href="?p=pages&action=hide&id={{ page.id }}"
title="{% if page.hidden != 1 %}Hide{% else %}Show{% endif %} in Admin Panel" target="_blank"> title="{% if page.hidden != 1 %}Hide{% else %}Show{% endif %} in Admin Panel" target="_blank">
<img src="images/{% if page.hidden != 1 %}success{% else %}error{% endif %}.png"/>{% if page.hidden != 1 %}Hide{% else %}Show{% endif %} <img src="images/{% if page.hidden != 1 %}success{% else %}error{% endif %}.png"/>{% if page.hidden != 1 %}Hide{% else %}Show{% endif %}
</a> </a>
<br/> <br/>
</div> </div>

View File

@@ -6,6 +6,7 @@
<table class="table table-striped table-bordered table-responsive d-md-table" id="tb_plugins"> <table class="table table-striped table-bordered table-responsive d-md-table" id="tb_plugins">
<thead> <thead>
<tr> <tr>
<th>Enabled</th>
<th>Name</th> <th>Name</th>
<th>Version</th> <th>Version</th>
<th>Author</th> <th>Author</th>
@@ -16,6 +17,17 @@
<tbody> <tbody>
{% for plugin in plugins %} {% for plugin in plugins %}
<tr> <tr>
<td>
{% if plugin.enabled %}
<a href="?p=plugins&disable={{ plugin.file }}" class="btn btn-success" onclick="return confirm('Are you sure you want to disable plugin {{ plugin.name }}?');" title="Disable">
<i class="fas fa-check"></i> Enabled
</a>
{% else %}
<a href="?p=plugins&enable={{ plugin.file }}" class="btn btn-danger" onclick="return confirm('Are you sure you want to enable plugin {{ plugin.name }}?');" title="Enable">
<i class="fas fa-ban"></i> Disabled
</a>
{% endif %}
</td>
<td><b>{{ plugin.name }}</b><br> <td><b>{{ plugin.name }}</b><br>
<small>{{ plugin.description|raw }}</small> <small>{{ plugin.description|raw }}</small>
</td> </td>
@@ -26,10 +38,11 @@
<td>{{ plugin.file }}.json</td> <td>{{ plugin.file }}.json</td>
<td> <td>
{% if plugin.uninstall %} {% if plugin.uninstall %}
<a href="?p=plugins&uninstall={{ plugin.file }}" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?');" title="Uninstall"> <a href="?p=plugins&uninstall={{ plugin.file }}" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure you want to uninstall {{ plugin.name }}?');" title="Uninstall">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</a> </a>
{% endif %}</td> {% endif %}
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@@ -39,6 +52,6 @@
<script> <script>
$(function () { $(function () {
$('#tb_plugins').DataTable() $('#tb_plugins').DataTable();
}) })
</script> </script>

View File

@@ -9,6 +9,7 @@
<th>IP</th> <th>IP</th>
<th>Last visit</th> <th>Last visit</th>
<th>Page</th> <th>Page</th>
<th>Browser</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -17,6 +18,7 @@
<td>{{ visitor.ip }}</td> <td>{{ visitor.ip }}</td>
<td>{{ visitor.lastvisit|date("H:i:s") }}</td> <td>{{ visitor.lastvisit|date("H:i:s") }}</td>
<td><a href="{{ visitor.page }}">{{ visitor.page|slice(0, 50) }}</a></td> <td><a href="{{ visitor.page }}">{{ visitor.page|slice(0, 50) }}</a></td>
<td>{{ visitor.browser|raw }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@@ -11,7 +11,7 @@
<td> <td>
{{ hook(constant('HOOK_CHARACTERS_BEFORE_INFORMATIONS')) }} {{ hook(constant('HOOK_CHARACTERS_BEFORE_INFORMATIONS')) }}
{% if canEdit %} {% if canEdit %}
<a href="admin/?p=players&id={{ player.getId() }}" title="Edit in Admin Panel" target="_blank"> <a href="{{ constant('ADMIN_URL') }}?p=players&id={{ player.getId() }}" title="Edit in Admin Panel" target="_blank">
<img src="images/edit.png"/>Edit <img src="images/edit.png"/>Edit
</a> </a>
{% endif %} {% endif %}
@@ -404,7 +404,8 @@
<tr> <tr>
<td> <td>
<input type="hidden" name="name" value="{{ player.getName() }}"/> <input type="hidden" name="name" value="{{ player.getName() }}"/>
<input type="image" name="View {{ player.getName() }}" alt="View {{ player.getName() }}" src="{{ template_path }}/images/global/buttons/sbutton_view.gif" border="0" width="120" height="18"/> {% set button_name = "View" %}
{{ include('buttons.base.html.twig') }}
</td> </td>
</tr> </tr>
</form> </form>
@@ -418,7 +419,7 @@
{% endif %} {% endif %}
{{ hook(constant('HOOK_CHARACTERS_AFTER_CHARACTERS')) }} {{ hook(constant('HOOK_CHARACTERS_AFTER_CHARACTERS')) }}
{% if canEdit %} {% if canEdit %}
<a href="admin/?p=players&id={{ player.getId() }}" title="Edit in Admin Panel" target="_blank"> <a href="{{ constant('ADMIN_URL') }}?p=players&id={{ player.getId() }}" title="Edit in Admin Panel" target="_blank">
<img src="images/edit.png"/>Edit <img src="images/edit.png"/>Edit
</a> </a>
{% endif %} {% endif %}

View File

@@ -8,9 +8,8 @@
<meta name="description" content="myaac"> <meta name="description" content="myaac">
<meta name="generator" content="MyAAC"> <meta name="generator" content="MyAAC">
<link rel="stylesheet" href="tools/css/messages.css"> <link rel="stylesheet" href="{{ BASE_URL }}tools/css/messages.css">
<link rel="shortcut icon" href="images/error.ico"> <link rel="shortcut icon" href="{{ BASE_URL }}images/error.ico">
<base href="{{ BASE_URL }}" />
<style> <style>
body{ body{
@@ -57,7 +56,7 @@
} }
</style> </style>
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="tools/js/html5shiv.min.js"></script> <script src="{{ BASE_URL }}tools/js/html5shiv.min.js"></script>
<![endif]--> <![endif]-->
</head> </head>
<body> <body>

View File

@@ -1,11 +0,0 @@
<script>
$(document).ready(function() {
grecaptcha.ready(function() {
grecaptcha.execute('{{ config.recaptcha_site_key }}', {action: '{{ action }}'}).then(function(token) {
if (token) {
document.getElementById('g-recaptcha-response').value = token;
}
});
});
});
</script>

View File

@@ -1,11 +0,0 @@
<script>
$(document).ready(function() {
grecaptcha.ready(function() {
grecaptcha.execute('{{ config.recaptcha_site_key }}', {action: '{{ action }}'}).then(function(token) {
if (token) {
document.getElementById('g-recaptcha-response').value = token;
}
});
});
});
</script>

View File

@@ -1,162 +1,162 @@
<div class="TableContainer"> <div class="TableContainer">
<div class="CaptionContainer"> <div class="CaptionContainer">
<div class="CaptionInnerContainer"> <div class="CaptionInnerContainer">
<span class="CaptionEdgeLeftTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeLeftTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionEdgeRightTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionBorderTop" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span> <span class="CaptionBorderTop" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span>
<span class="CaptionVerticalLeft" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span> <span class="CaptionVerticalLeft" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span>
<div class="Text">Active Guilds on {{ config.lua.serverName }}</div> <div class="Text">Active Guilds on {{ config.lua.serverName }}</div>
<span class="CaptionVerticalRight" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span> <span class="CaptionVerticalRight" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span>
<span class="CaptionBorderBottom" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span> <span class="CaptionBorderBottom" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span>
<span class="CaptionEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
</div> </div>
</div> </div>
<table class="Table3"> <table class="Table3">
<tbody> <tbody>
<tr> <tr>
<td> <td>
<div class="InnerTableContainer"> <div class="InnerTableContainer">
<table style="width:100%;"> <table style="width:100%;">
<tbody> <tbody>
<tr> <tr>
<td> <td>
<div class="TableShadowContainerRightTop"> <div class="TableShadowContainerRightTop">
<div class="TableShadowRightTop" style="background-image:url({{ template_path }}/images/content/table-shadow-rt.gif);"></div> <div class="TableShadowRightTop" style="background-image:url({{ template_path }}/images/content/table-shadow-rt.gif);"></div>
</div> </div>
<div class="TableContentAndRightShadow" style="background-image:url({{ template_path }}/images/content/table-shadow-rm.gif);"> <div class="TableContentAndRightShadow" style="background-image:url({{ template_path }}/images/content/table-shadow-rm.gif);">
<div class="TableContentContainer"> <div class="TableContentContainer">
<table class="TableContent" width="100%"> <table class="TableContent" width="100%">
<tbody> <tbody>
{% if guilds|length > 0 %} {% if guilds|length > 0 %}
<tr bgcolor="{{ config.darkborder }}"> <tr bgcolor="{{ config.darkborder }}">
<td width="64"><b>Logo</b></td> <td width="64"><b>Logo</b></td>
<td><b>Description</b></td> <td><b>Description</b></td>
<td width="12%"><b>&nbsp;</b></td> <td width="12%"><b>&nbsp;</b></td>
</tr> </tr>
{% set i = 0 %} {% set i = 0 %}
{% for guild in guilds %} {% for guild in guilds %}
{% set i = i + 1 %} {% set i = i + 1 %}
<tr bgcolor="{{ getStyle(i) }}"> <tr bgcolor="{{ getStyle(i) }}">
<td> <td>
<img src="{{ constant('GUILD_IMAGES_DIR') }}{{ guild.logo }}" width="64" height="64"> <img src="{{ constant('GUILD_IMAGES_DIR') }}{{ guild.logo }}" width="64" height="64">
</td> </td>
<td> <td>
<span{% if guild.description is not empty %} valign="top"{% endif %}> <span{% if guild.description is not empty %} valign="top"{% endif %}>
<b>{{ guild.name }}</b>{% if isAdmin %}<a href="?subtopic=guilds&action=delete_by_admin&guild={{ guild.name }}"> - Delete this guild (for ADMIN only!)</a>{% endif %} <b>{{ guild.name }}</b>{% if isAdmin %}<a href="?subtopic=guilds&action=delete_by_admin&guild={{ guild.name }}"> - Delete this guild (for ADMIN only!)</a>{% endif %}
</span> </span>
{% if guild.description is not empty %} {% if guild.description is not empty %}
<br> <br>
<span>{{ guild.description }}</span> <span>{{ guild.description }}</span>
{% endif %} {% endif %}
</td> </td>
<td> <td>
<table border="0" cellspacing="0" cellpadding="0" width="100%"> <table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody> <tbody>
<tr> <tr>
<td style="border:0;"> <td style="border:0;">
<form action="{{ guild.link }}" method="post"> <form action="{{ guild.link }}" method="post">
{{ include('buttons.view.html.twig') }} {{ include('buttons.view.html.twig') }}
</form> </form>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
{% else %} {% else %}
<tr bgcolor="{{ config.darkborder }}"> <tr bgcolor="{{ config.darkborder }}">
<td valign="top"> <td valign="top">
<b>Create Guild</b> <b>Create Guild</b>
<br/> <br/>
Actually there is no guild on server.{% if logged %} Create first! Press button "Create Guild"{% endif %} Actually there is no guild on server.{% if logged %} Create first! Press button "Create Guild"{% endif %}
</td> </td>
{% if logged %} {% if logged %}
<td> <td>
<table border="0" cellpadding="0" cellspacing="0" width="100%"> <table border="0" cellpadding="0" cellspacing="0" width="100%">
<form action="?subtopic=guilds&action=create" method="post"> <form action="?subtopic=guilds&action=create" method="post">
<form action="?subtopic=guilds&action=create" method="post"> <form action="?subtopic=guilds&action=create" method="post">
{% set button_name = 'Found Guild' %} {% set button_name = 'Found Guild' %}
{% set button_image = '_sbutton_foundguild' %} {% set button_image = '_sbutton_foundguild' %}
{% include('buttons.base.html.twig') %} {% include('buttons.base.html.twig') %}
</form> </form>
</form> </form>
</table> </table>
</td> </td>
{% endif %} {% endif %}
</tr> </tr>
{% endif %} {% endif %}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
<div class="TableShadowContainer"> <div class="TableShadowContainer">
<div class="TableBottomShadow" style="background-image:url(https://static.tibia.com/images/global/content/table-shadow-bm.gif);"> <div class="TableBottomShadow" style="background-image:url(https://static.tibia.com/images/global/content/table-shadow-bm.gif);">
<div class="TableBottomLeftShadow" style="background-image:url({{ template_path }}/images/content/table-shadow-bl.gif);"></div> <div class="TableBottomLeftShadow" style="background-image:url({{ template_path }}/images/content/table-shadow-bl.gif);"></div>
<div class="TableBottomRightShadow" style="background-image:url({{ template_path }}/images/content/table-shadow-br.gif);"></div> <div class="TableBottomRightShadow" style="background-image:url({{ template_path }}/images/content/table-shadow-br.gif);"></div>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<br> <br>
<table border="0" cellspacing="1" cellpadding="4" width="100%"> <table border="0" cellspacing="1" cellpadding="4" width="100%">
<tbody> <tbody>
<tr> <tr>
<td align="center"> <td align="center">
<img src="{{ template_path }}images/general/blank.gif" width="80" height="1" border="0"> <img src="{{ template_path }}images/general/blank.gif" width="80" height="1" border="0">
</td> </td>
<td align="center"> <td align="center">
<table border="0" cellspacing="0" cellpadding="0"> <table border="0" cellspacing="0" cellpadding="0">
<tbody> <tbody>
{% if logged %} {% if logged %}
No guild found that suits your needs? No guild found that suits your needs?
<form action="?subtopic=guilds&action=create" method="post"> <form action="?subtopic=guilds&action=create" method="post">
{% set button_name = 'Found Guild' %} {% set button_name = 'Found Guild' %}
{% set button_image = '_sbutton_foundguild' %} {% set button_image = '_sbutton_foundguild' %}
{% include('buttons.base.html.twig') %} {% include('buttons.base.html.twig') %}
</form> </form>
{% else %} {% else %}
<b>Before you can create a guild you must login.</b> <b>Before you can create a guild you must login.</b>
<br/> <br/>
<form action="?subtopic=accountmanagement&redirect={{ getLink('guilds') }}" method="post"> <form action="?subtopic=accountmanagement&redirect={{ getLink('guilds') }}" method="post">
{% include('buttons.login.html.twig') %} {% include('buttons.login.html.twig') %}
</form> </form>
{% endif %} {% endif %}
</tbody> </tbody>
</table> </table>
{% if logged %} {% if logged %}
<br/> <br/>
If you have any problem with guilds try: If you have any problem with guilds try:
<br/> <br/>
<a href="?subtopic=guilds&action=cleanup_players">Cleanup players</a> - can't join guild/be invited? Can't create guild? Try cleanup players. <a href="?subtopic=guilds&action=cleanup_players">Cleanup players</a> - can't join guild/be invited? Can't create guild? Try cleanup players.
<br/> <br/>
<a href="?subtopic=guilds&action=cleanup_guilds">Cleanup guilds</a> - made guild, you are a leader, but you are not on players list? Cleanup guilds! <a href="?subtopic=guilds&action=cleanup_guilds">Cleanup guilds</a> - made guild, you are a leader, but you are not on players list? Cleanup guilds!
{% endif %} {% endif %}
</td> </td>
<td align="center"> <td align="center">
<img src="{{ template_path }}/images/general/blank.gif" width="80" height="1" border="0"> <img src="{{ template_path }}/images/general/blank.gif" width="80" height="1" border="0">
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -1,347 +1,349 @@
<div class="Border_2"> <div class="Border_2">
<div class="Border_3"> <div class="Border_3">
<div class="BoxContent"> <div class="BoxContent">
<table border="0" width="100%"> <table border="0" width="100%">
<tbody> <tbody>
<tr> <tr>
<td width="64"> <td width="64">
<img src="{{ constant('GUILD_IMAGES_DIR') }}{{ logo }}" width="64" height="64"> <img src="{{ constant('GUILD_IMAGES_DIR') }}{{ logo }}" width="64" height="64">
</td> </td>
<td align="center" width="100%"><h1>{{ guild_name }}</h1></td> <td align="center" width="100%"><h1>{{ guild_name }}</h1></td>
<td width="64"> <td width="64">
<img src="{{ constant('GUILD_IMAGES_DIR') }}{{ logo }}" width="64" height="64"> <img src="{{ constant('GUILD_IMAGES_DIR') }}{{ logo }}" width="64" height="64">
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<br> <br>
<table width="100%"> <table width="100%">
<tbody> <tbody>
<tr> <tr>
<td style="vertical-align:top;"> <td style="vertical-align:top;">
<div class="TableContainer"> <div class="TableContainer">
<div class="TableContainer"> <div class="TableContainer">
<div class="CaptionContainer"> <div class="CaptionContainer">
<div class="CaptionInnerContainer"> <div class="CaptionInnerContainer">
<span class="CaptionEdgeLeftTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeLeftTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionEdgeRightTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionBorderTop" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span> <span class="CaptionBorderTop" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span>
<span class="CaptionVerticalLeft" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span> <span class="CaptionVerticalLeft" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span>
<div class="Text">Guild Information</div> <div class="Text">Guild Information</div>
<span class="CaptionVerticalRight" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span> <span class="CaptionVerticalRight" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span>
<span class="CaptionBorderBottom" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span> <span class="CaptionBorderBottom" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span>
<span class="CaptionEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
</div> </div>
</div> </div>
</div> </div>
<table class="Table1" cellpadding="0" cellspacing="0"> <table class="Table1" cellpadding="0" cellspacing="0">
<tbody> <tbody>
<tr> <tr>
<td> <td>
<div class="InnerTableContainer"> <div class="InnerTableContainer">
<table style="width:100%;"> <table style="width:100%;">
<tbody> <tbody>
<tr> <tr>
<td> <td>
<div id="GuildInformationContainer"> <div id="GuildInformationContainer">
{% if descriptions is not empty %} {% if descriptions is not empty %}
{{ description }} {{ description }}
<br> <br>
<br> <br>
{% endif %} {% endif %}
{% if guild_owner is not empty %} {% if guild_owner is not empty %}
{% set guildOwnerName = guild_owner.getName() %} {% set guildOwnerName = guild_owner.getName() %}
<a href="{{ getPlayerLink(guildOwnerName, false) }}"><b>{{ guildOwnerName }}</b></a> is guild leader of <b>{{ guild_name }}</b>. <a href="{{ getPlayerLink(guildOwnerName, false) }}"><b>{{ guildOwnerName }}</b></a> is guild leader of <b>{{ guild_name }}</b>.
<br> <br>
{% endif %} {% endif %}
The guild was founded on {{ config.lua.serverName }} on {{ guild_creation_date|date("j F Y") }}. The guild was founded on {{ config.lua.serverName }} on {{ guild_creation_date|date("j F Y") }}.
{% if isLeader %} {% if isLeader %}
<a href="?subtopic=guilds&action=manager&guild={{ guild_name }}" style="float: right;"> <a href="?subtopic=guilds&action=manager&guild={{ guild_name }}" style="float: right;">
<img src="{{ template_path }}/images/global/buttons/sbutton_manageguild.png" style="width: 120px; height: 20px;" alt="Manage Guild"> <img src="{{ template_path }}/images/global/buttons/sbutton_manageguild.png" style="width: 120px; height: 20px;" alt="Manage Guild">
</a> </a>
{% endif %} {% endif %}
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<br> <br>
<div class="TableContainer"> <div class="TableContainer">
<div class="TableContainer"> <div class="TableContainer">
<div class="CaptionContainer"> <div class="CaptionContainer">
<div class="CaptionInnerContainer"> <div class="CaptionInnerContainer">
<span class="CaptionEdgeLeftTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeLeftTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionEdgeRightTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionBorderTop" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span> <span class="CaptionBorderTop" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span>
<span class="CaptionVerticalLeft" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span> <span class="CaptionVerticalLeft" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span>
<div class="Text">Guild Members</div> <div class="Text">Guild Members</div>
<span class="CaptionVerticalRight" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span> <span class="CaptionVerticalRight" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span>
<span class="CaptionBorderBottom" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span> <span class="CaptionBorderBottom" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span>
<span class="CaptionEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
</div> </div>
</div> </div>
</div> </div>
<table class="Table3" cellpadding="0" cellspacing="0"> <table class="Table3" cellpadding="0" cellspacing="0">
<tbody> <tbody>
<tr> <tr>
<td> <td>
<div class="InnerTableContainer"> <div class="InnerTableContainer">
<table style="width:100%;"> <table style="width:100%;">
<tbody> <tbody>
<tr> <tr>
<td> <td>
<div class="TableContentAndRightShadow"> <div class="TableContentAndRightShadow">
<div class="TableContentContainer"> <div class="TableContentContainer">
<table class="TableContent" width="100%"> <table class="TableContent" width="100%">
<tbody> <tbody>
<tr class="LabelH"> <tr class="LabelH">
<td>Rank</td> <td>Rank</td>
<td>Name{% if useGuildNick %} and Title{% endif %}</td> <td>Name{% if useGuildNick %} and Title{% endif %}</td>
<td>Vocation</td> <td>Vocation</td>
<td>Level</td> <td>Level</td>
<td>Status</td> <td>Status</td>
</tr> </tr>
{% set showedRank, i = false, 0 %} {% set showedRank, i = false, 0 %}
{% for rank in guild_members if rank.members|length > 0 %} {% for rank in guild_members if rank.members|length > 0 %}
{% set rankStyle, i = getStyle(i), i + 1 %} {% set rankStyle, i = getStyle(i), i + 1 %}
{% for player in rank.members %} {% for player in rank.members %}
<tr bgcolor="{{ rankStyle }}"> <tr bgcolor="{{ rankStyle }}">
<td> <td>
{% if not showedRank %}{{ rank.rank_name }}{% endif %} {% if not showedRank %}{{ rank.rank_name }}{% endif %}
{% set showedRank = true %} {% set showedRank = true %}
</td> </td>
<td> <td>
{% set playerName = player.getName() %} {% set playerName = player.getName() %}
<form action="?subtopic=guilds&action=change_nick&name={{ playerName }}&guild={{ guild_name }}" method="post"> <form action="?subtopic=guilds&action=change_nick&name={{ playerName }}&guild={{ guild_name }}" method="post">
{{ getPlayerLink(playerName, true)|raw }} {{ getPlayerLink(playerName, true)|raw }}
{% set showGuildNick = false %} {% set showGuildNick = false %}
{% if player.getGuildNick() is not empty %} {% if player.getGuildNick() is not empty %}
{% set showGuildNick = true %} {% set showGuildNick = true %}
{% set guildNickRaw = player.getGuildNick()|raw %} {% set guildNickRaw = player.getGuildNick()|raw %}
{% endif %} {% endif %}
{% if logged %} {% if logged %}
{% if player.getId() in players_from_account_ids %} {% if player.getId() in players_from_account_ids %}
(<input type="text" name="nick" value="{{ guildNickRaw }}"><input type="submit" value="Change">) (<input type="text" name="nick" value="{{ guildNickRaw }}"><input type="submit" value="Change">)
{% else %} {% else %}
{% if showGuildNick %} ({{ guildNickRaw }}){% endif %} {% if showGuildNick %} ({{ guildNickRaw }}){% endif %}
{% endif %} {% endif %}
{% if level_in_guild > rank.rank_level or isLeader %} {% if level_in_guild > rank.rank_level or isLeader %}
{% if guildOwnerName != playerName %} {% if guildOwnerName != playerName %}
<span style="font-size: 10px; float: right"> <span style="font-size: 10px; float: right">
{<a href="?subtopic=guilds&action=kick_player&guild={{ guild_name|url_encode }}&name={{ playerName|url_encode }}">KICK</a>} {<a href="?subtopic=guilds&action=kick_player&guild={{ guild_name|url_encode }}&name={{ playerName|url_encode }}">KICK</a>}
</span> </span>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% else %} {% else %}
{% if showGuildNick %} ({{ guildNickRaw }}){% endif %} {% if showGuildNick %} ({{ guildNickRaw }}){% endif %}
{% endif %} {% endif %}
</form> </form>
</td> </td>
<td>{{ player.getVocationName() }}</td> <td>{{ player.getVocationName() }}</td>
<td>{{ player.getLevel() }}</td> <td>{{ player.getLevel() }}</td>
<td> <td>
<span style="color: {% if player.isOnline() %} green;"><b>Online{% else %} red;"><b>Offline{% endif %}</b></span> <span style="color: {% if player.isOnline() %} green;"><b>Online{% else %} red;"><b>Offline{% endif %}</b></span>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
{% set showedRank = false %} {% set showedRank = false %}
{% else %} {% else %}
<tr bgcolor="{{ getStyle(i) }}"> <tr bgcolor="{{ getStyle(i) }}">
<td colspan="5">No guild members found.</td> <td colspan="5">No guild members found.</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<br> <br>
<div class="TableContainer"> <div class="TableContainer">
<div class="CaptionContainer"> <div class="CaptionContainer">
<div class="CaptionInnerContainer"> <div class="CaptionInnerContainer">
<span class="CaptionEdgeLeftTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeLeftTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionEdgeRightTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionBorderTop" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span> <span class="CaptionBorderTop" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span>
<span class="CaptionVerticalLeft" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span> <span class="CaptionVerticalLeft" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span>
<div class="Text">Invited Characters</div> <div class="Text">Invited Characters</div>
<span class="CaptionVerticalRight" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span> <span class="CaptionVerticalRight" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span>
<span class="CaptionBorderBottom" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span> <span class="CaptionBorderBottom" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span>
<span class="CaptionEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
</div> </div>
</div> </div>
<table class="Table3" cellpadding="0" cellspacing="0"> <table class="Table3" cellpadding="0" cellspacing="0">
<tbody> <tbody>
<tr> <tr>
<td> <td>
<div class="InnerTableContainer"> <div class="InnerTableContainer">
<table style="width:100%;"> <table style="width:100%;">
<tbody> <tbody>
<tr> <tr>
<td> <td>
<div class="TableContentContainer"> <div class="TableContentContainer">
<table class="TableContent" width="100%"> <table class="TableContent" width="100%">
<tbody> <tbody>
<tr class="LabelH"> <tr class="LabelH">
<td><b>Name</b></td> <td><b>Name</b></td>
</tr> </tr>
{% set i = 0 %} {% set i = 0 %}
{% for invited_player in invited_list if invited_list|length > 0 %} {% for invited_player in invited_list if invited_list|length > 0 %}
{% if invited_player.isLoaded() %} {% if invited_player.isLoaded() %}
<tr bgcolor="{{ getStyle(i) }}"> <tr bgcolor="{{ getStyle(i) }}">
{% set i = i + 1 %} {% set i = i + 1 %}
<td> <td>
{{ getPlayerLink(invited_player.getName(), true)|raw }} {{ getPlayerLink(invited_player.getName(), true)|raw }}
{% if isVice %} {% if isVice %}
<div style="float: right"> <div style="float: right">
{<a href="?subtopic=guilds&action=delete_invite&guild={{ guild_name|url_encode }}&name={{ invited_player.getName()|url_encode }}">Cancel Invitation</a>} {<a href="?subtopic=guilds&action=delete_invite&guild={{ guild_name|url_encode }}&name={{ invited_player.getName()|url_encode }}">Cancel Invitation</a>}
</div> </div>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
{% else %} {% else %}
<tr bgcolor="{{ getStyle(i) }}"> <tr bgcolor="{{ getStyle(i) }}">
<td> <td>
No invited characters found. No invited characters found.
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<br> <br>
<div class="TableContainer"> {{ hook(constant('HOOK_GUILDS_AFTER_INVITED_CHARACTERS'), { 'guild': guild, 'isLeader': isLeader }) }}
<table class="Table3" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td>
<div class="InnerTableContainer">
<table style="width:100%;">
<tbody>
<tr>
<td>
<div class="TableContentContainer">
<table class="TableContent" width="100%">
<tbody>
<tr>
{% if not logged %}
<form action="?subtopic=accountmanagement&redirect={{ getGuildLink(guild_name|url_encode, false) }}" method="post">
<td>
{{ include('buttons.login.html.twig') }}
</td>
</form>
{% else %}
{% if show_accept_invite > 0 %}
<form action="?subtopic=guilds&action=accept_invite&guild={{ guild_name|url_encode }}" method="post">
<td>
<input type="image" name="Accept Invite" alt="Accept Invite" src="{{ template_path }}/images/global/buttons/sbutton_acceptinvite.png" style="width: 120px; height: 20px;">
</td>
</form>
{% endif %}
{% if isVice %} <div class="TableContainer">
<form action="?subtopic=guilds&action=invite&guild={{ guild_name|url_encode }}" method="post"> <table class="Table3" cellpadding="0" cellspacing="0">
<td> <tbody>
{% set button_name = 'Invite Character' %} <tr>
{% set button_image = '_sbutton_invitecharacter' %} <td>
{% include('buttons.base.html.twig') %} <div class="InnerTableContainer">
</td> <table style="width:100%;">
</form> <tbody>
<tr>
<td>
<div class="TableContentContainer">
<table class="TableContent" width="100%">
<tbody>
<tr>
{% if not logged %}
<form action="?subtopic=accountmanagement&redirect={{ getGuildLink(guild_name|url_encode, false) }}" method="post">
<td>
{{ include('buttons.login.html.twig') }}
</td>
</form>
{% else %}
{% if show_accept_invite > 0 %}
<form action="?subtopic=guilds&action=accept_invite&guild={{ guild_name|url_encode }}" method="post">
<td>
<input type="image" name="Accept Invite" alt="Accept Invite" src="{{ template_path }}/images/global/buttons/sbutton_acceptinvite.png" style="width: 120px; height: 20px;">
</td>
</form>
{% endif %}
<form action="?subtopic=guilds&action=change_rank&guild={{ guild_name|url_encode }}" method="post"> {% if isVice %}
<td> <form action="?subtopic=guilds&action=invite&guild={{ guild_name|url_encode }}" method="post">
{% set button_name = 'Edit Ranks' %} <td>
{% set button_image = '_sbutton_editranks' %} {% set button_name = 'Invite Character' %}
{% include('buttons.base.html.twig') %} {% set button_image = '_sbutton_invitecharacter' %}
</td> {% include('buttons.base.html.twig') %}
</form> </td>
{% endif %} </form>
{% if players_from_account_in_guild|length > 0 %} <form action="?subtopic=guilds&action=change_rank&guild={{ guild_name|url_encode }}" method="post">
<form action="?subtopic=guilds&action=leave_guild&guild={{ guild_name|url_encode }}" method="post"> <td>
<td> {% set button_name = 'Edit Ranks' %}
{% set button_name = 'Leave Guild' %} {% set button_image = '_sbutton_editranks' %}
{% set button_image = '_sbutton_leaveguild' %} {% include('buttons.base.html.twig') %}
{% include('buttons.base.html.twig') %} </td>
</td> </form>
</form> {% endif %}
{% endif %}
{% endif %}
<form action="{{ getLink('guilds') }}" method="post"> {% if players_from_account_in_guild|length > 0 %}
<td style="float: right"> <form action="?subtopic=guilds&action=leave_guild&guild={{ guild_name|url_encode }}" method="post">
{{ include('buttons.back.html.twig') }} <td>
</td> {% set button_name = 'Leave Guild' %}
</form> {% set button_image = '_sbutton_leaveguild' %}
</tr> {% include('buttons.base.html.twig') %}
</tbody> </td>
</table> </form>
</div> {% endif %}
</td> {% endif %}
</tr>
</tbody> <form action="{{ getLink('guilds') }}" method="post">
</table> <td style="float: right">
</div> {{ include('buttons.back.html.twig') }}
</td> </td>
</tr> </form>
</tbody> </tr>
</table> </tbody>
</div> </table>
</div> </div>
</div> </td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div> </div>

View File

@@ -1,171 +1,171 @@
<div class="TableContainer"> <div class="TableContainer">
{% if errors is not empty %} {% if errors is not empty %}
{% for error in errors %} {% for error in errors %}
<p class="error">{{ error }}</p> <p class="error">{{ error }}</p>
{% endfor %} {% endfor %}
{% else %} {% else %}
<table class="Table1" cellpadding="0" cellspacing="0" style="background-color: {{ config.lightborder }}"> <table class="Table1" cellpadding="0" cellspacing="0" style="background-color: {{ config.lightborder }}">
<div class="CaptionContainer"> <div class="CaptionContainer">
<div class="CaptionInnerContainer"> <div class="CaptionInnerContainer">
<span class="CaptionEdgeLeftTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeLeftTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionEdgeRightTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionBorderTop" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span> <span class="CaptionBorderTop" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span>
<span class="CaptionVerticalLeft" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span> <span class="CaptionVerticalLeft" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span>
<div class="Text" >House Search</div> <div class="Text" >House Search</div>
<span class="CaptionVerticalRight" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span> <span class="CaptionVerticalRight" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span>
<span class="CaptionBorderBottom" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span> <span class="CaptionBorderBottom" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span>
<span class="CaptionEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
</div> </div>
</div> </div>
<tr> <tr>
<td> <td>
<div class="InnerTableContainer"> <div class="InnerTableContainer">
Here you can see the list of all available houses, flats{{ guildString }}. Here you can see the list of all available houses, flats{{ guildString }}.
Click on any view button to get more information about a house or adjust Click on any view button to get more information about a house or adjust
the search criteria and start a new search. the search criteria and start a new search.
<br><br> <br><br>
{% if cleanOldHouse is not empty or rentType != 'never' %} {% if cleanOldHouse is not empty or rentType != 'never' %}
Every morning during global server save there is automatic house cleaning. Server delete house owners who have not logged in last {{ cleanOldHouse }} days{% if rentType != 'never' %} or have not paid {{ rentType }} house rent. Remember to leave money for a rent in {% if config.lua.bankSystem is not empty %}your house bank or {% else %}depo in same city where you have house!{% endif %}{% else %}.{% endif %} Every morning during global server save there is automatic house cleaning. Server delete house owners who have not logged in last {{ cleanOldHouse }} days{% if rentType != 'never' %} or have not paid {{ rentType }} house rent. Remember to leave money for a rent in {% if config.lua.bankSystem is not empty %}your house bank or {% else %}depo in same city where you have house!{% endif %}{% else %}.{% endif %}
<br><br> <br><br>
{% endif %} {% endif %}
{% if houses is not empty or housesSearch %} {% if houses is not empty or housesSearch %}
<table border="0" cellspacing="1" cellpadding="4" width="100%"> <table border="0" cellspacing="1" cellpadding="4" width="100%">
<tbody> <tbody>
<tr bgcolor="{{ config.vdarkborder }}"> <tr bgcolor="{{ config.vdarkborder }}">
<td colspan="6" class="white"><b>Available {{ houseType }}{% if townName is not empty %} in {{ townName }}{% endif %} on <b>{{ config.lua.serverName }}</b></b></td> <td colspan="6" class="white"><b>Available {{ houseType }}{% if townName is not empty %} in {{ townName }}{% endif %} on <b>{{ config.lua.serverName }}</b></b></td>
</tr> </tr>
<tr bgcolor="{{ config.darkborder }}"> <tr bgcolor="{{ config.darkborder }}">
{% if houses is not empty %} {% if houses is not empty %}
<td width="40%"><b>Name</b></td> <td width="40%"><b>Name</b></td>
<td width="10%"><b>Size</b></td> <td width="10%"><b>Size</b></td>
<td width="10%"><b>Rent</b></td> <td width="10%"><b>Rent</b></td>
<td width="40%"><b>Status</b></td> <td width="40%"><b>Status</b></td>
<td>&nbsp;</td> <td>&nbsp;</td>
{% elseif housesSearch %} {% elseif housesSearch %}
<td>No {{ houseType }} with specified criterias.</td> <td>No {{ houseType }} with specified criterias.</td>
{% endif %} {% endif %}
</tr> </tr>
{% if houses is not empty %} {% if houses is not empty %}
{% set i = 0 %} {% set i = 0 %}
{% for house in houses %} {% for house in houses %}
{% set i = i + 1 %} {% set i = i + 1 %}
<tr bgcolor="{{ getStyle(i) }}"> <tr bgcolor="{{ getStyle(i) }}">
<td width="40%"> <td width="40%">
{{ house.name }} {{ house.name }}
</td> </td>
<td width="10%"> <td width="10%">
{{ house.size }} {{ house.size }}
</td> </td>
<td width="10%"> <td width="10%">
{{ house.rent }} golds {{ house.rent }} golds
</td> </td>
<td width="40%"> <td width="40%">
{{ house.rentedBy|raw }} {{ house.rentedBy|raw }}
</td> </td>
<td> <td>
<form action="{{ getLink('houses/view') }}" method="post"> <form action="{{ getLink('houses/view') }}" method="post">
<input type="hidden" name="house" value="{{ house.name }}"> <input type="hidden" name="house" value="{{ house.name }}">
{{ include('buttons.view.html.twig') }} {{ include('buttons.view.html.twig') }}
</form> </form>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
</tbody> </tbody>
</table> </table>
<br> <br>
{% endif %} {% endif %}
<form action="{{ getLink('houses') }}" method="post"> <form action="{{ getLink('houses') }}" method="post">
<table border="0" cellspacing="1" cellpadding="4" width="100%"> <table border="0" cellspacing="1" cellpadding="4" width="100%">
<tbody> <tbody>
<tr bgcolor="{{ config.vdarkborder }}"> <tr bgcolor="{{ config.vdarkborder }}">
<td colspan="4" class="white"><b>House Search</b></td> <td colspan="4" class="white"><b>House Search</b></td>
</tr> </tr>
<tr bgcolor="{{ config.darkborder }}"> <tr bgcolor="{{ config.darkborder }}">
<td width="25%"><b>Town</b></td> <td width="25%"><b>Town</b></td>
<td width="25%"><b>Status</b></td> <td width="25%"><b>Status</b></td>
<td width="25%"><b>Order</b></td> <td width="25%"><b>Order</b></td>
</tr> </tr>
<tr bgcolor="{{ config.darkborder }}"> <tr bgcolor="{{ config.darkborder }}">
<td valign="top" rowspan="2"> <td valign="top" rowspan="2">
{% set checked = false %} {% set checked = false %}
{% for id, name in config.towns if id > 0 %} {% for id, name in config.towns if id > 0 %}
{% if ((townId is empty and name is not empty) or id == townId) and not checked %} {% if ((townId is empty and name is not empty) or id == townId) and not checked %}
{% set variable = "checked" %} {% set variable = "checked" %}
{% set checked = true %} {% set checked = true %}
{% else %} {% else %}
{% set variable = "" %} {% set variable = "" %}
{% endif %} {% endif %}
<input type="radio" name="town" id="town_{{ id }}" value="{{ id }}"{% if variable is defined %} {{ variable }}{% endif %}> <input type="radio" name="town" id="town_{{ id }}" value="{{ id }}"{% if variable is defined %} {{ variable }}{% endif %}>
<label for="town_{{ id }}"> {{ name }}</label> <label for="town_{{ id }}"> {{ name }}</label>
<br> <br>
{% endfor %} {% endfor %}
</td> </td>
<td valign="top"> <td valign="top">
<input type="radio" name="state" id="state_all" value=""{% if state is empty %} checked{% endif %}> <input type="radio" name="state" id="state_all" value=""{% if state is empty %} checked{% endif %}>
<label for="state_all">all states</label><br> <label for="state_all">all states</label><br>
<input type="radio" name="state" id="state_free" value="free"{% if state == 'free' %} checked{% endif %}> <input type="radio" name="state" id="state_free" value="free"{% if state == 'free' %} checked{% endif %}>
<label for="state_free">free</label><br> <label for="state_free">free</label><br>
<input type="radio" name="state" id="state_rented" value="rented"{% if state == 'rented' %} checked{% endif %}> <input type="radio" name="state" id="state_rented" value="rented"{% if state == 'rented' %} checked{% endif %}>
<label for="state_rented">rented</label><br> <label for="state_rented">rented</label><br>
</td> </td>
<td valign="top" rowspan="2"> <td valign="top" rowspan="2">
<input type="radio" name="order" id="order_name" value=""{% if order is empty %} checked{% endif %}> <input type="radio" name="order" id="order_name" value=""{% if order is empty %} checked{% endif %}>
<label for="order_name"> by name</label><br> <label for="order_name"> by name</label><br>
<input type="radio" name="order" id="order_size" value="size"{% if order == 'size' %} checked{% endif %}> <input type="radio" name="order" id="order_size" value="size"{% if order == 'size' %} checked{% endif %}>
<label for="order_size"> by size</label><br> <label for="order_size"> by size</label><br>
<input type="radio" name="order" id="order_rent" value="rent"{% if order == 'rent' %} checked{% endif %}> <input type="radio" name="order" id="order_rent" value="rent"{% if order == 'rent' %} checked{% endif %}>
<label for="order_rent"> by rent</label><br> <label for="order_rent"> by rent</label><br>
</td> </td>
</tr> </tr>
<tr bgcolor="{{ config.darkborder }}"> <tr bgcolor="{{ config.darkborder }}">
<td valign="top"> <td valign="top">
<input type="radio" name="type" id="type_all" value=""{% if type is empty %} checked{% endif %}> <input type="radio" name="type" id="type_all" value=""{% if type is empty %} checked{% endif %}>
<label for="type"> all</label><br> <label for="type"> all</label><br>
<input type="radio" name="type" id="type_houses" value="houses"{% if type == 'houses' %} checked{% endif %}> <input type="radio" name="type" id="type_houses" value="houses"{% if type == 'houses' %} checked{% endif %}>
<label for="type_houses"> houses and flats</label><br> <label for="type_houses"> houses and flats</label><br>
<input type="radio" name="type" id="type_guildhalls" value="guildhalls"{% if type == 'guildhalls' %} checked{% endif %}> <input type="radio" name="type" id="type_guildhalls" value="guildhalls"{% if type == 'guildhalls' %} checked{% endif %}>
<label for="type_guildhalls"> guildhalls</label><br> <label for="type_guildhalls"> guildhalls</label><br>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<br> <br>
<table border="0" cellspacing="0" cellpadding="0" width="100%"> <table border="0" cellspacing="0" cellpadding="0" width="100%">
<tbody> <tbody>
<tr align="center"> <tr align="center">
<td> <td>
{{ include('buttons.submit.html.twig') }} {{ include('buttons.submit.html.twig') }}
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</form> </form>
</div> </div>
</td> </td>
</tr> </tr>
</table> </table>
{% endif %} {% endif %}
</div> </div>

View File

@@ -1,51 +1,51 @@
<div class="TableContainer"> <div class="TableContainer">
{% if errors is not empty %} {% if errors is not empty %}
{% for error in errors %} {% for error in errors %}
<p class="error">{{ error }}</p> <p class="error">{{ error }}</p>
{% endfor %} {% endfor %}
{% else %} {% else %}
<table class="Table1" cellpadding="0" cellspacing="0" style="background-color: {{ config.lightborder }}"> <table class="Table1" cellpadding="0" cellspacing="0" style="background-color: {{ config.lightborder }}">
<div class="CaptionContainer"> <div class="CaptionContainer">
<div class="CaptionInnerContainer"> <div class="CaptionInnerContainer">
<span class="CaptionEdgeLeftTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeLeftTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionEdgeRightTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightTop" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionBorderTop" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span> <span class="CaptionBorderTop" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span>
<span class="CaptionVerticalLeft" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span> <span class="CaptionVerticalLeft" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span>
<div class="Text" >{{ houseName }}</div> <div class="Text" >{{ houseName }}</div>
<span class="CaptionVerticalRight" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span> <span class="CaptionVerticalRight" style="background-image:url({{ template_path }}/images/content/box-frame-vertical.gif);"></span>
<span class="CaptionBorderBottom" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span> <span class="CaptionBorderBottom" style="background-image:url({{ template_path }}/images/content/table-headline-border.gif);"></span>
<span class="CaptionEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeLeftBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
<span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
</div> </div>
</div> </div>
<tr> <tr>
<td> <td>
<div class="InnerTableContainer"> <div class="InnerTableContainer">
<table border="0" cellpadding="4" cellspacing="1" width="100%"> <table border="0" cellpadding="4" cellspacing="1" width="100%">
<tr style="display: inline-flex;"> <tr style="display: inline-flex;">
<td><img src="{{ imgPath }}"></td> <td><img src="{{ imgPath }}"></td>
<td> <td>
{{ bedsMessage }} and has a size of <b>{{ houseSize }} square meters</b>. {{ bedsMessage }} and has a size of <b>{{ houseSize }} square meters</b>.
{% if rentType != 'never' %} {% if rentType != 'never' %}
The {{ rentType }} is <b>{{ houseRent }} gold</b> and will be debited to the bank account on <b>{{ config.lua.serverName }}</b>. The {{ rentType }} is <b>{{ houseRent }} gold</b> and will be debited to the bank account on <b>{{ config.lua.serverName }}</b>.
{% endif %} {% endif %}
<br> <br>
{% if owner is not empty %} {% if owner is not empty %}
The house has been rented by <b>{{ owner|raw }}</b>. The house has been rented by <b>{{ owner|raw }}</b>.
{% else %} {% else %}
No one has bought this house yet. No one has bought this house yet.
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
</td> </td>
</tr> </tr>
</table> </table>
{% endif %} {% endif %}
</div> </div>
<br><br> <br><br>

View File

@@ -12,7 +12,7 @@
{% for value in ['server_path', 'mail_admin'] %} {% for value in ['server_path', 'mail_admin'] %}
<div class="form-group mb-2"> <div class="form-group mb-2">
<label for="vars_{{ value }}">{{ locale['step_config_' ~ value] }}</label> <label for="vars_{{ value }}">{{ locale['step_config_' ~ value] }}</label>
<input class="form-control" type="text" name="vars[{{ value }}]" id="vars_{{ value }}"{% if session['var_' ~ value] is not null %} value="{{ session['var_' ~ value] }}"{% endif %}/> <input class="form-control" type="{% if value == 'mail_admin' %}email{% else %}text{% endif %}" name="vars[{{ value }}]" id="vars_{{ value }}"{% if session['var_' ~ value] is not null %} value="{{ session['var_' ~ value] }}"{% endif %}/>
<small class="form-text text-muted">{{ locale['step_config_' ~ value ~ '_desc'] }}</small> <small class="form-text text-muted">{{ locale['step_config_' ~ value ~ '_desc'] }}</small>
</div> </div>
{% endfor %} {% endfor %}
@@ -43,9 +43,9 @@
<input type="checkbox" name="vars[usage]" id="vars_usage" value="1"{% if session['var_usage'] is null or session['var_usage'] == 1 %} checked{% endif %}/> <input type="checkbox" name="vars[usage]" id="vars_usage" value="1"{% if session['var_usage'] is null or session['var_usage'] == 1 %} checked{% endif %}/>
<small class="form-text text-muted d-block">{{ locale.step_config_usage_desc }}</small> <small class="form-text text-muted d-block">{{ locale.step_config_usage_desc }}</small>
</div> </div>
<div class="text-center m-3"> <div class="text-center m-3">
{{ buttons|raw }} {{ buttons|raw }}
</div> </div>
</form> </form>

View File

@@ -4,10 +4,11 @@
<tbody> <tbody>
<tr> <tr>
<td> <td>
<input width="120" height="18" border="0" type="image" src="{{ template_path }}/images/global/buttons/sbutton_back.gif" alt="Back" name="Back" /> {% set button_name = 'Back' %}
{{ include('buttons.base.html.twig') }}
</td> </td>
</tr> </tr>
</tbody> </tbody>
</form> </form>
</table> </table>
</div> </div>

View File

@@ -12,7 +12,7 @@
<span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span> <span class="CaptionEdgeRightBottom" style="background-image:url({{ template_path }}/images/content/box-frame-edge.gif);"></span>
</div> </div>
</div> </div>
<table class="Table1" cellpadding="0" cellspacing="0" style="background-color: {{ config.lightborder }}"> <table class="Table5" cellpadding="0" cellspacing="0" style="background-color: {{ config.lightborder }}">
<tr> <tr>
<td> <td>
<div class="InnerTableContainer"> <div class="InnerTableContainer">

View File

@@ -16,6 +16,3 @@
Please turn it on, or be aware that some features on this website will not work correctly. Please turn it on, or be aware that some features on this website will not work correctly.
</div> </div>
</noscript> </noscript>
{% if config.recaptcha_enabled %}
<script src="https://www.google.com/recaptcha/api.js{% if config('recaptcha_type') == 'v3' %}?render={{ config.recaptcha_site_key }}{% endif %}"></script>
{% endif %}

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