Use 4 spaces as tabs and reformat
This commit is contained in:
parent
df2d9520ca
commit
41234b34bf
34 changed files with 509 additions and 421 deletions
6
.editorconfig
Normal file
6
.editorconfig
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
25
CHANGELOG.md
25
CHANGELOG.md
|
@ -3,18 +3,22 @@
|
||||||
## 2.2.4 - UNRELEASED
|
## 2.2.4 - UNRELEASED
|
||||||
|
|
||||||
## 2.2.3 - 2021/01/08
|
## 2.2.3 - 2021/01/08
|
||||||
|
|
||||||
* Change docker base to alpine
|
* Change docker base to alpine
|
||||||
|
|
||||||
## 2.2.2 - 2020/03/22
|
## 2.2.2 - 2020/03/22
|
||||||
|
|
||||||
* Stop auto-sorting tables
|
* Stop auto-sorting tables
|
||||||
* Add bandwidth formatter
|
* Add bandwidth formatter
|
||||||
* Check PHP 7.4 compatibility
|
* Check PHP 7.4 compatibility
|
||||||
* Increase docker image base to PHP 7.4
|
* Increase docker image base to PHP 7.4
|
||||||
|
|
||||||
## 2.2.1 - 2019/11/10
|
## 2.2.1 - 2019/11/10
|
||||||
|
|
||||||
* Use separate JavaScript file to initialize DataTables
|
* Use separate JavaScript file to initialize DataTables
|
||||||
|
|
||||||
## 2.2.0 - 2019/11/10
|
## 2.2.0 - 2019/11/10
|
||||||
|
|
||||||
* Add version tag to footer
|
* Add version tag to footer
|
||||||
* Add sortable tables
|
* Add sortable tables
|
||||||
* Add search on tables
|
* Add search on tables
|
||||||
|
@ -23,32 +27,38 @@
|
||||||
* Fix dependency version
|
* Fix dependency version
|
||||||
|
|
||||||
## 2.1.4 - 2019/11/08
|
## 2.1.4 - 2019/11/08
|
||||||
|
|
||||||
* Use autofocus on username input field instead of the password field
|
* Use autofocus on username input field instead of the password field
|
||||||
* Fill missing cells on incorrect cell count in table views when only partial data is available
|
* Fill missing cells on incorrect cell count in table views when only partial data is available
|
||||||
|
|
||||||
## 2.1.3 - 2019/08/08
|
## 2.1.3 - 2019/08/08
|
||||||
|
|
||||||
* Fixed false rendering of forms
|
* Fixed false rendering of forms
|
||||||
* Fixed channel tree view showing the wrong virtual server after selection
|
* Fixed channel tree view showing the wrong virtual server after selection
|
||||||
* Minor code refactor
|
* Minor code refactor
|
||||||
|
|
||||||
## 2.1.2 - 2019/08/07
|
## 2.1.2 - 2019/08/07
|
||||||
|
|
||||||
* Minor refactoring
|
* Minor refactoring
|
||||||
* Update documentation
|
* Update documentation
|
||||||
|
|
||||||
## 2.1.1 - 2019/08/07
|
## 2.1.1 - 2019/08/07
|
||||||
|
|
||||||
* Updated translation
|
* Updated translation
|
||||||
|
|
||||||
## 2.1.0 - 2019/08/07
|
## 2.1.0 - 2019/08/07
|
||||||
|
|
||||||
* Fixed file handling on snapshots
|
* Fixed file handling on snapshots
|
||||||
* Cleaned up template links
|
* Cleaned up template links
|
||||||
* Updated documentation
|
* Updated documentation
|
||||||
* Added application log view
|
* Added application log view
|
||||||
* Fixed files view
|
* Fixed files view
|
||||||
* Added show total used space for files in a channel
|
* Added show total used space for files in a channel
|
||||||
* Added file delete action
|
* Added file delete action
|
||||||
* Removed about modal
|
* Removed about modal
|
||||||
|
|
||||||
## 2.0.0 - 2019/08/06
|
## 2.0.0 - 2019/08/06
|
||||||
|
|
||||||
* Replace material design with bootstrap4 theme
|
* Replace material design with bootstrap4 theme
|
||||||
* Add an about modal
|
* Add an about modal
|
||||||
* Add tree view for online clients
|
* Add tree view for online clients
|
||||||
|
@ -56,34 +66,45 @@
|
||||||
* Update dependencies and force PHP 7.3
|
* Update dependencies and force PHP 7.3
|
||||||
|
|
||||||
## 1.2.4 - 2019/01/18
|
## 1.2.4 - 2019/01/18
|
||||||
|
|
||||||
* No info text
|
* No info text
|
||||||
|
|
||||||
## 1.2.3 - 2019/01/18
|
## 1.2.3 - 2019/01/18
|
||||||
|
|
||||||
* No info text
|
* No info text
|
||||||
|
|
||||||
## 1.2.2 - 2018/09/01
|
## 1.2.2 - 2018/09/01
|
||||||
|
|
||||||
* No info text
|
* No info text
|
||||||
|
|
||||||
## 1.2.1 - 2018/06/04
|
## 1.2.1 - 2018/06/04
|
||||||
|
|
||||||
* No info text
|
* No info text
|
||||||
|
|
||||||
## 1.2.0 - 2018/06/04
|
## 1.2.0 - 2018/06/04
|
||||||
|
|
||||||
* No info text
|
* No info text
|
||||||
|
|
||||||
## 1.1.1 - 2018/05/04
|
## 1.1.1 - 2018/05/04
|
||||||
|
|
||||||
* No info text
|
* No info text
|
||||||
|
|
||||||
## 1.1.0 - 2018/05/04
|
## 1.1.0 - 2018/05/04
|
||||||
|
|
||||||
* No info text
|
* No info text
|
||||||
|
|
||||||
## 1.0.3 - 2018/04/04
|
## 1.0.3 - 2018/04/04
|
||||||
|
|
||||||
* No info text
|
* No info text
|
||||||
|
|
||||||
## 1.0.2 - 2018/04/04
|
## 1.0.2 - 2018/04/04
|
||||||
|
|
||||||
* No info text
|
* No info text
|
||||||
|
|
||||||
## 1.0.1 - 2018/04/03
|
## 1.0.1 - 2018/04/03
|
||||||
|
|
||||||
* No info text
|
* No info text
|
||||||
|
|
||||||
## 1.0.0 - 2018/04/03
|
## 1.0.0 - 2018/04/03
|
||||||
* No info text
|
|
||||||
|
* No info text
|
||||||
|
|
100
README.md
100
README.md
|
@ -1,45 +1,54 @@
|
||||||
# README
|
# README
|
||||||
|
|
||||||
ts3web is a free and open-source web interface for TeamSpeak 3 instances.
|
ts3web is a free and open-source web interface for TeamSpeak 3 instances.
|
||||||
|
|
||||||
The minimalistic approach of this application is intentional.
|
The minimalistic approach of this application is intentional.
|
||||||
|
|
||||||
* Docker images available on [https://hub.docker.com/r/varakh/ts3web](https://hub.docker.com/r/varakh/ts3web)
|
* Docker images available on [https://hub.docker.com/r/varakh/ts3web](https://hub.docker.com/r/varakh/ts3web)
|
||||||
* Sources are hosted on [https://github.com/v4rakh/ts3web](https://github.com/v4rakh/ts3web)
|
* Sources are hosted on [https://github.com/v4rakh/ts3web](https://github.com/v4rakh/ts3web)
|
||||||
|
|
||||||
There are many TeamSpeak 3 web interfaces out. Why should I pick ts3web?
|
There are many TeamSpeak 3 web interfaces out. Why should I pick ts3web? Free, simple, stateless, easy to extend,
|
||||||
Free, simple, stateless, easy to extend, standard bootstrap theme.
|
standard bootstrap theme.
|
||||||
|
|
||||||
## F.A.Q
|
## F.A.Q
|
||||||
|
|
||||||
Questions? Here you'll hopefully get the answer. Feel free to read before starting.
|
Questions? Here you'll hopefully get the answer. Feel free to read before starting.
|
||||||
|
|
||||||
<a name="flood"></a>
|
<a name="flood"></a>
|
||||||
|
|
||||||
###### I always get `flood client` message when clicking anywhere in the web interface.
|
###### I always get `flood client` message when clicking anywhere in the web interface.
|
||||||
|
|
||||||
ts3web makes heavy use of query commands. When your instance is up and running, you should be able to change
|
ts3web makes heavy use of query commands. When your instance is up and running, you should be able to change
|
||||||
`serverinstance_serverquery_flood_commands` to a high value, e.g. `100` and `serverinstance_serverquery_flood_time` to
|
`serverinstance_serverquery_flood_commands` to a high value, e.g. `100` and `serverinstance_serverquery_flood_time` to
|
||||||
`1` which is enough.
|
`1` which is enough.
|
||||||
|
|
||||||
<a name="whitelist"></a>
|
<a name="whitelist"></a>
|
||||||
|
|
||||||
###### I always get `TSException: Error: host isn't a ts3 instance!` when selecting a server.
|
###### I always get `TSException: Error: host isn't a ts3 instance!` when selecting a server.
|
||||||
You probably got query banned from your server. You need to properly define your [`whitelist.txt` file](#whitelisttxtexample)
|
|
||||||
|
You probably got query banned from your server. You need to properly define
|
||||||
|
your [`whitelist.txt` file](#whitelisttxtexample)
|
||||||
and include it in your TeamSpeak application.
|
and include it in your TeamSpeak application.
|
||||||
|
|
||||||
<a name="dockerperms"></a>
|
<a name="dockerperms"></a>
|
||||||
|
|
||||||
###### I always get `no write permissions` or something similar when trying to save snapshots or when a log entry is created.
|
###### I always get `no write permissions` or something similar when trying to save snapshots or when a log entry is created.
|
||||||
This probably happens when you're in the docker setup. Ensure that host binds have permissions set up properly.
|
|
||||||
The user which is used in the docker container is `nobody` with id `65534`. If, e.g. logs are host bound, then execute
|
This probably happens when you're in the docker setup. Ensure that host binds have permissions set up properly. The user
|
||||||
|
which is used in the docker container is `nobody` with id `65534`. If, e.g. logs are host bound, then execute
|
||||||
`chown -R 65534:65534 host/path/to/log`. The same holds true for snapshots.
|
`chown -R 65534:65534 host/path/to/log`. The same holds true for snapshots.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The main configuration file for the *web interface* is the `env` file located in `config/`. There's an example file
|
The main configuration file for the *web interface* is the `env` file located in `config/`. There's an example file
|
||||||
called `env.example` which you **need** to copy to `config/env`. Defaults will assume you're running your TeamSpeak
|
called `env.example` which you **need** to copy to `config/env`. Defaults will assume you're running your TeamSpeak
|
||||||
server on `localhost` with default port. Docker deployments can and *should* host bind this file into the container
|
server on `localhost` with default port. Docker deployments can and *should* host bind this file into the container
|
||||||
directly and just maintain the `env` file.
|
directly and just maintain the `env` file.
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
The application can be deployed two different ways. See below for more information. For each deployment type a running
|
|
||||||
TeamSpeak 3 instance is a prerequisite.
|
The application can be deployed two different ways. See below for more information. For each deployment type a running
|
||||||
|
TeamSpeak 3 instance is a prerequisite.
|
||||||
|
|
||||||
In the `docker-compose.yml` [example](#dockercompose), a setup together with a teamspeak server instance is shown.
|
In the `docker-compose.yml` [example](#dockercompose), a setup together with a teamspeak server instance is shown.
|
||||||
|
|
||||||
|
@ -50,17 +59,19 @@ In the `docker-compose.yml` [example](#dockercompose), a setup together with a t
|
||||||
1. [Setup write permissions if you're using host binds](#dockerperms)
|
1. [Setup write permissions if you're using host binds](#dockerperms)
|
||||||
2. [Ensure that you set `flood commands` to a higher value in your TeamSpeak](#flood).
|
2. [Ensure that you set `flood commands` to a higher value in your TeamSpeak](#flood).
|
||||||
3. [Use a `whitelist.txt` to ensure the web interface will not be query banned](#whitelist)
|
3. [Use a `whitelist.txt` to ensure the web interface will not be query banned](#whitelist)
|
||||||
4. Be aware that the web interface will not be able to use `localhost` as TeamSpeak 3 server address because it's not
|
4. Be aware that the web interface will not be able to use `localhost` as TeamSpeak 3 server address because it's not
|
||||||
available in a docker container. The public address also has to match the environment variable
|
available in a docker container. The public address also has to match the environment variable
|
||||||
`teamspeak_host=your-public-address` within the `env` file.
|
`teamspeak_host=your-public-address` within the `env` file.
|
||||||
|
|
||||||
<a name="dockerrun"></a>
|
<a name="dockerrun"></a>
|
||||||
|
|
||||||
#### docker run
|
#### docker run
|
||||||
|
|
||||||
The following section outlines a manual setup. Feel free to use the provided `docker-compose.yml` as quick setup.
|
The following section outlines a manual setup. Feel free to use the provided `docker-compose.yml` as quick setup.
|
||||||
|
|
||||||
1. Create docker volumes for `snapshots`, `log` and `env`. Alternative is to host bind them into your containers.
|
1. Create docker volumes for `snapshots`, `log` and `env`. Alternative is to host bind them into your containers.
|
||||||
2. Create a docker network with a fixed IP range or later use host network.
|
2. Create a docker network with a fixed IP range or later use host network.
|
||||||
3. Depending on your setup, you need to change `teamspeak_host` of your `env` file to point either to `your IP` or to a
|
3. Depending on your setup, you need to change `teamspeak_host` of your `env` file to point either to `your IP` or to a
|
||||||
`fixed docker IP` which your teamspeak uses. `localhost` is not valid if you're using it in docker. If you're unsure,
|
`fixed docker IP` which your teamspeak uses. `localhost` is not valid if you're using it in docker. If you're unsure,
|
||||||
please take a look at the example `docker-compose.yml` files.
|
please take a look at the example `docker-compose.yml` files.
|
||||||
4. Start a container using the docker image `varakh/ts3web` and provide the following bindings for volumes:
|
4. Start a container using the docker image `varakh/ts3web` and provide the following bindings for volumes:
|
||||||
|
@ -68,19 +79,24 @@ The following section outlines a manual setup. Feel free to use the provided `do
|
||||||
* `{snapshot_volume|host_folder}:/var/www/html/application/data/snapshots`
|
* `{snapshot_volume|host_folder}:/var/www/html/application/data/snapshots`
|
||||||
* `{log_volume|host_folder}:/var/www/html/application/log`
|
* `{log_volume|host_folder}:/var/www/html/application/log`
|
||||||
5. [Ensure that you're whitelisting the IP from which the webinterface will issue commands.](#whitelist)
|
5. [Ensure that you're whitelisting the IP from which the webinterface will issue commands.](#whitelist)
|
||||||
6. Run the `docker run` command including your settings, volumes and networks (if any): `docker run --name teamspeak_web -v ./env:/var/www/html/application/config/env -p 8181:80 varakh/ts3web:latest`.
|
6. Run the `docker run` command including your settings, volumes and networks (if
|
||||||
|
any): `docker run --name teamspeak_web -v ./env:/var/www/html/application/config/env -p 8181:80 varakh/ts3web:latest`
|
||||||
|
.
|
||||||
|
|
||||||
<a name="dockercompose"></a>
|
<a name="dockercompose"></a>
|
||||||
#### docker-compose
|
|
||||||
In order for TeamSpeak to show correct IP and country flags, the `network_mode = "host"` is advised. It's also
|
|
||||||
possible to set everything up [without using the host network mode and use fixed IPs](#withouthostmode).
|
|
||||||
|
|
||||||
The examples will use host binds for volumes. Feel free to adapt the `docker-compose.yml` template and use docker volumes
|
#### docker-compose
|
||||||
instead if you like.
|
|
||||||
|
In order for TeamSpeak to show correct IP and country flags, the `network_mode = "host"` is advised. It's also possible
|
||||||
|
to set everything up [without using the host network mode and use fixed IPs](#withouthostmode).
|
||||||
|
|
||||||
|
The examples will use host binds for volumes. Feel free to adapt the `docker-compose.yml` template and use docker
|
||||||
|
volumes instead if you like.
|
||||||
|
|
||||||
Ensure to [apply permissions](#dockerperms) for volumes though.
|
Ensure to [apply permissions](#dockerperms) for volumes though.
|
||||||
|
|
||||||
<a name="withhostmode"></a>
|
<a name="withhostmode"></a>
|
||||||
|
|
||||||
#### With 'host' mode
|
#### With 'host' mode
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -121,6 +137,7 @@ services:
|
||||||
```
|
```
|
||||||
|
|
||||||
<a name="withouthostmode"></a>
|
<a name="withouthostmode"></a>
|
||||||
|
|
||||||
#### Without 'host' mode
|
#### Without 'host' mode
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -169,13 +186,13 @@ services:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
<a name="whitelisttxtexample"></a>
|
<a name="whitelisttxtexample"></a>
|
||||||
|
|
||||||
#### whitelist.txt
|
#### whitelist.txt
|
||||||
|
|
||||||
The following illustrates a valid `whitelist.txt` file which can be used for the above `docker-compose` setups. You
|
The following illustrates a valid `whitelist.txt` file which can be used for the above `docker-compose` setups. You need
|
||||||
need to replace `your-public-ip` with the TeamSpeak's public IP address if required or remove the fixed internal
|
to replace `your-public-ip` with the TeamSpeak's public IP address if required or remove the fixed internal docker IP if
|
||||||
docker IP if you're on 'host' mode.
|
you're on 'host' mode.
|
||||||
|
|
||||||
```
|
```
|
||||||
127.0.0.1
|
127.0.0.1
|
||||||
|
@ -184,20 +201,23 @@ docker IP if you're on 'host' mode.
|
||||||
your-public-ip
|
your-public-ip
|
||||||
```
|
```
|
||||||
|
|
||||||
Now execute `docker-compose up -d` to start those containers. If you like to update, do `docker-compose down`,
|
Now execute `docker-compose up -d` to start those containers. If you like to update, do `docker-compose down`,
|
||||||
`docker-compose pull` and then `docker-compose up -d` again.
|
`docker-compose pull` and then `docker-compose up -d` again.
|
||||||
|
|
||||||
Your TeamSpeak 3 Server will be available under `public-server-ip:9987`. The web interface will be available on
|
Your TeamSpeak 3 Server will be available under `public-server-ip:9987`. The web interface will be available on
|
||||||
`127.0.0.1:8181`. You need to add a [reverse proxy](#reverseproxy) and probably you also want SSL configured if you expose it via domain.
|
`127.0.0.1:8181`. You need to add a [reverse proxy](#reverseproxy) and probably you also want SSL configured if you
|
||||||
For testing purposes, change `- 127.0.0.1:8181:80` to `- 8181:80`. The web interface will then be available under
|
expose it via domain. For testing purposes, change `- 127.0.0.1:8181:80` to `- 8181:80`. The web interface will then be
|
||||||
|
available under
|
||||||
`public-server-ip:8181`.
|
`public-server-ip:8181`.
|
||||||
|
|
||||||
This is **not recommended**! Secure your setup properly via [reverse proxy and SSL](#reverseproxy).
|
This is **not recommended**! Secure your setup properly via [reverse proxy and SSL](#reverseproxy).
|
||||||
|
|
||||||
### As native PHP application
|
### As native PHP application
|
||||||
|
|
||||||
**Prerequisite**: `php`, `composer` and probably `php-fpm` installed on the server.
|
**Prerequisite**: `php`, `composer` and probably `php-fpm` installed on the server.
|
||||||
|
|
||||||
#### Install:
|
#### Install:
|
||||||
|
|
||||||
* Clone repository
|
* Clone repository
|
||||||
* Change directory to project home
|
* Change directory to project home
|
||||||
* Execute `composer install`
|
* Execute `composer install`
|
||||||
|
@ -205,15 +225,19 @@ This is **not recommended**! Secure your setup properly via [reverse proxy and S
|
||||||
* Do the configuration by coping the `env.example` file (see information above)
|
* Do the configuration by coping the `env.example` file (see information above)
|
||||||
* Use a web server _or_ run directly via the embedded PHP server: `php -S localhost:8080 -t public public/index.php`.
|
* Use a web server _or_ run directly via the embedded PHP server: `php -S localhost:8080 -t public public/index.php`.
|
||||||
* Point your browser to [localhost:8080](http://localhost:8080)
|
* Point your browser to [localhost:8080](http://localhost:8080)
|
||||||
* Apply any [whitelist.txt](#whitelisttxtexample) changes if you configured `teamspeak_host` differently than `localhost`
|
* Apply any [whitelist.txt](#whitelisttxtexample) changes if you configured `teamspeak_host` differently
|
||||||
|
than `localhost`
|
||||||
|
|
||||||
#### Upgrade:
|
#### Upgrade:
|
||||||
|
|
||||||
* Change directory to project home
|
* Change directory to project home
|
||||||
* `git pull`
|
* `git pull`
|
||||||
* `composer update`
|
* `composer update`
|
||||||
|
|
||||||
<a name="reverseproxy"></a>
|
<a name="reverseproxy"></a>
|
||||||
|
|
||||||
### Reverse proxy
|
### Reverse proxy
|
||||||
|
|
||||||
Here's an example on how to configure a reverse proxy for the web interface docker container
|
Here's an example on how to configure a reverse proxy for the web interface docker container
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -256,12 +280,13 @@ supported:
|
||||||
If you're willing to contribute, here's some information.
|
If you're willing to contribute, here's some information.
|
||||||
|
|
||||||
### Release
|
### Release
|
||||||
|
|
||||||
* Set a date in the `CHANGELOG.md` file
|
* Set a date in the `CHANGELOG.md` file
|
||||||
* Remove `SNAPSHOT` from the version in `Constants.php`
|
* Remove `SNAPSHOT` from the version in `Constants.php`
|
||||||
* Build the docker image from the project
|
* Build the docker image from the project
|
||||||
* if necessary, add GitHub access token to let composer pull dependencies within the image correctly:
|
* if necessary, add GitHub access token to let composer pull dependencies within the image correctly:
|
||||||
add `&& composer config --global --auth github-oauth.github.com <token> \` before the `composer install` command,
|
add `&& composer config --global --auth github-oauth.github.com <token> \` before the `composer install` command,
|
||||||
where `<token>` can be retrieved from [GitHub settings](https://github.com/settings/tokens)
|
where `<token>` can be retrieved from [GitHub settings](https://github.com/settings/tokens)
|
||||||
* execute `sudo docker build --no-cache -t varakh/ts3web:latest .` to build
|
* execute `sudo docker build --no-cache -t varakh/ts3web:latest .` to build
|
||||||
* publish it
|
* publish it
|
||||||
* Tag the release git commit and create a new release in the VCS web interface
|
* Tag the release git commit and create a new release in the VCS web interface
|
||||||
|
@ -274,7 +299,9 @@ If you're willing to contribute, here's some information.
|
||||||
4. Don't forget to clean up all created branches
|
4. Don't forget to clean up all created branches
|
||||||
|
|
||||||
### Helpers
|
### Helpers
|
||||||
Attributes can be defined when including `table`, `keyvalues` and `form` templates of twig. This helps to generate tables and forms without the need to specify all attributes.
|
|
||||||
|
Attributes can be defined when including `table`, `keyvalues` and `form` templates of twig. This helps to generate
|
||||||
|
tables and forms without the need to specify all attributes.
|
||||||
|
|
||||||
```
|
```
|
||||||
hiddenDependingOnAttribute // hides a row depending on a value in a table
|
hiddenDependingOnAttribute // hides a row depending on a value in a table
|
||||||
|
@ -289,10 +316,15 @@ fields // define fields for a form
|
||||||
See example usage in the folder `View/bootstrap4`.
|
See example usage in the folder `View/bootstrap4`.
|
||||||
|
|
||||||
### Translations
|
### Translations
|
||||||
- This app uses Symfony Translator. It's bootstrapped in `Util\BootstrapHelper` and locales are placed under `data/locale/` and the data table `.json` file, e.g. `en_dataTable.json`. Adjust to your needs or help translating.
|
|
||||||
- Form fields (name/id should be the same) are also translated. For a field named `content` or `ConT enT` translate `form_field_content`.
|
- This app uses Symfony Translator. It's bootstrapped in `Util\BootstrapHelper` and locales are placed
|
||||||
|
under `data/locale/` and the data table `.json` file, e.g. `en_dataTable.json`. Adjust to your needs or help
|
||||||
|
translating.
|
||||||
|
- Form fields (name/id should be the same) are also translated. For a field named `content` or `ConT enT`
|
||||||
|
translate `form_field_content`.
|
||||||
|
|
||||||
### Theme
|
### Theme
|
||||||
Themes can be chosen in the `env` file by editing the `theme` variable. Templates are mapped to the corresponding view
|
|
||||||
folder in `src/View/<themeName>`. `.css`, `.js` and other style files like `.ttf` or `.woff2` for fonts should be placed
|
Themes can be chosen in the `env` file by editing the `theme` variable. Templates are mapped to the corresponding view
|
||||||
|
folder in `src/View/<themeName>`. `.css`, `.js` and other style files like `.ttf` or `.woff2` for fonts should be placed
|
||||||
in `public/theme/<themeName>` and accessed accordingly. See an example in `src/View/boostrap4/layout.twig`.
|
in `public/theme/<themeName>` and accessed accordingly. See an example in `src/View/boostrap4/layout.twig`.
|
||||||
|
|
|
@ -15,11 +15,11 @@ final class AuthLoginAction extends AbstractAction
|
||||||
$validator = new Validator();
|
$validator = new Validator();
|
||||||
$body = $validator->sanitize($body);
|
$body = $validator->sanitize($body);
|
||||||
$validator->filter_rules([
|
$validator->filter_rules([
|
||||||
'username' => 'trim',
|
'username' => 'trim',
|
||||||
]);
|
]);
|
||||||
$validator->validation_rules([
|
$validator->validation_rules([
|
||||||
'username' => 'required|min_len,1',
|
'username' => 'required|min_len,1',
|
||||||
'password' => 'required|min_len,1',
|
'password' => 'required|min_len,1',
|
||||||
]);
|
]);
|
||||||
if (!$validator->run($body)) {
|
if (!$validator->run($body)) {
|
||||||
$validator->addErrorsToFlashMessage($this->flash);
|
$validator->addErrorsToFlashMessage($this->flash);
|
||||||
|
@ -42,7 +42,7 @@ final class AuthLoginAction extends AbstractAction
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->render($response, 'login.twig', [
|
$this->view->render($response, 'login.twig', [
|
||||||
'title' => $this->translator->trans('login.title'),
|
'title' => $this->translator->trans('login.title'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ final class AuthLogoutAction extends AbstractAction
|
||||||
public function __invoke(Request $request, Response $response, $args)
|
public function __invoke(Request $request, Response $response, $args)
|
||||||
{
|
{
|
||||||
$this->flash->addMessage('success', $this->translator->trans('logout.flash.success'));
|
$this->flash->addMessage('success', $this->translator->trans('logout.flash.success'));
|
||||||
|
|
||||||
$this->ts->login($this->auth->getIdentity()['user'], $this->auth->getIdentity()['password']);
|
$this->ts->login($this->auth->getIdentity()['user'], $this->auth->getIdentity()['password']);
|
||||||
$this->ts->getInstance()->logout();
|
$this->ts->getInstance()->logout();
|
||||||
$this->auth->logout();
|
$this->auth->logout();
|
||||||
|
@ -16,4 +16,4 @@ final class AuthLogoutAction extends AbstractAction
|
||||||
|
|
||||||
return $response->withRedirect('/login');
|
return $response->withRedirect('/login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ final class ForbiddenAction extends AbstractAction
|
||||||
public function __invoke(Request $request, Response $response, $args)
|
public function __invoke(Request $request, Response $response, $args)
|
||||||
{
|
{
|
||||||
return $this->view->render($response, 'error.twig', [
|
return $this->view->render($response, 'error.twig', [
|
||||||
'title' => $this->translator->trans('error.403.title'),
|
'title' => $this->translator->trans('error.403.title'),
|
||||||
'content' => $this->translator->trans('error.403.content')
|
'content' => $this->translator->trans('error.403.content')
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@ final class InstanceAction extends AbstractAction
|
||||||
$data['data'] = array_merge($hostResult['data'], $instanceResult['data']);
|
$data['data'] = array_merge($hostResult['data'], $instanceResult['data']);
|
||||||
|
|
||||||
$this->view->render($response, 'instance.twig', [
|
$this->view->render($response, 'instance.twig', [
|
||||||
'title' => $this->translator->trans('instance.title'),
|
'title' => $this->translator->trans('instance.title'),
|
||||||
'data' => $this->ts->getInstance()->getElement('data', $data)
|
'data' => $this->ts->getInstance()->getElement('data', $data)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ final class InternalApplicationError extends AbstractAction
|
||||||
public function __invoke(Request $request, Response $response, $args)
|
public function __invoke(Request $request, Response $response, $args)
|
||||||
{
|
{
|
||||||
return $this->view->render($response, 'error.twig', [
|
return $this->view->render($response, 'error.twig', [
|
||||||
'title' => $this->translator->trans('error.500.title'),
|
'title' => $this->translator->trans('error.500.title'),
|
||||||
'content' => $this->translator->trans('error.500.content')
|
'content' => $this->translator->trans('error.500.content')
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ final class NotAuthorizedAction extends AbstractAction
|
||||||
public function __invoke(Request $request, Response $response, $args)
|
public function __invoke(Request $request, Response $response, $args)
|
||||||
{
|
{
|
||||||
return $this->view->render($response, 'error.twig', [
|
return $this->view->render($response, 'error.twig', [
|
||||||
'title' => $this->translator->trans('error.401.title'),
|
'title' => $this->translator->trans('error.401.title'),
|
||||||
'content' => $this->translator->trans('error.401.content')
|
'content' => $this->translator->trans('error.401.content')
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ final class NotFoundAction extends AbstractAction
|
||||||
public function __invoke(Request $request, Response $response, $args)
|
public function __invoke(Request $request, Response $response, $args)
|
||||||
{
|
{
|
||||||
return $this->view->render($response, 'error.twig', [
|
return $this->view->render($response, 'error.twig', [
|
||||||
'title' => $this->translator->trans('error.404.title'),
|
'title' => $this->translator->trans('error.404.title'),
|
||||||
'content' => $this->translator->trans('error.404.content')
|
'content' => $this->translator->trans('error.404.content')
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,8 @@ final class ProfileAction extends AbstractAction
|
||||||
$whoisResult = $this->ts->getInstance()->whoAmI();
|
$whoisResult = $this->ts->getInstance()->whoAmI();
|
||||||
|
|
||||||
$this->view->render($response, 'profile.twig', [
|
$this->view->render($response, 'profile.twig', [
|
||||||
'title' => $this->translator->trans('profile.title'),
|
'title' => $this->translator->trans('profile.title'),
|
||||||
'whois' => $this->ts->getInstance()->getElement('data', $whoisResult),
|
'whois' => $this->ts->getInstance()->getElement('data', $whoisResult),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Slim\Http\Request;
|
use Slim\Http\Request;
|
||||||
use Slim\Http\Response;
|
use Slim\Http\Response;
|
||||||
use Symfony\Component\Filesystem\Exception\IOException;
|
use Symfony\Component\Filesystem\Exception\IOException;
|
||||||
|
@ -39,4 +38,4 @@ final class SnapshotDeleteAction extends AbstractAction
|
||||||
|
|
||||||
return $response->withRedirect('/snapshots/' . $sid);
|
return $response->withRedirect('/snapshots/' . $sid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ class TSAuthAdapter extends \Zend\Authentication\Adapter\AbstractAdapter
|
||||||
if ($this->ts->login($user, $password)) {
|
if ($this->ts->login($user, $password)) {
|
||||||
$this->logger->debug(sprintf('Authenticated as %s', $user));
|
$this->logger->debug(sprintf('Authenticated as %s', $user));
|
||||||
|
|
||||||
$user = ['identity' => $user, 'user' => $user, 'password'=> $password, 'role' => ACL::ACL_DEFAULT_ROLE_ADMIN];
|
$user = ['identity' => $user, 'user' => $user, 'password' => $password, 'role' => ACL::ACL_DEFAULT_ROLE_ADMIN];
|
||||||
return new Result(Result::SUCCESS, $user, array());
|
return new Result(Result::SUCCESS, $user, array());
|
||||||
} else {
|
} else {
|
||||||
return new Result(
|
return new Result(
|
||||||
|
@ -57,4 +57,4 @@ class TSAuthAdapter extends \Zend\Authentication\Adapter\AbstractAdapter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,8 @@ class FileHelper
|
||||||
* @param int $decimals
|
* @param int $decimals
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function humanFileSize($bytes, $decimals = 2) {
|
public static function humanFileSize($bytes, $decimals = 2)
|
||||||
|
{
|
||||||
$sz = 'BKMGTP';
|
$sz = 'BKMGTP';
|
||||||
$factor = floor((strlen($bytes) - 1) / 3);
|
$factor = floor((strlen($bytes) - 1) / 3);
|
||||||
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
|
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
|
||||||
|
@ -57,9 +58,10 @@ class FileHelper
|
||||||
* @param int $decimals
|
* @param int $decimals
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function humanBandwidth($bytes, $decimals = 2) {
|
public static function humanBandwidth($bytes, $decimals = 2)
|
||||||
|
{
|
||||||
$sz = 'BKMGTP';
|
$sz = 'BKMGTP';
|
||||||
$factor = floor((strlen($bytes) - 1) / 3);
|
$factor = floor((strlen($bytes) - 1) / 3);
|
||||||
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor] . '/s';
|
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor] . '/s';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,14 @@ class TS3AdminProxy
|
||||||
* @param ts3admin $object
|
* @param ts3admin $object
|
||||||
* @param $logger LoggerInterface
|
* @param $logger LoggerInterface
|
||||||
*/
|
*/
|
||||||
public function __construct(ts3admin $object, $logger) {
|
public function __construct(ts3admin $object, $logger)
|
||||||
|
{
|
||||||
$this->object = $object;
|
$this->object = $object;
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __call($method, $args) {
|
public function __call($method, $args)
|
||||||
|
{
|
||||||
|
|
||||||
// hide sensitive args
|
// hide sensitive args
|
||||||
if (in_array($method, ['login'])) {
|
if (in_array($method, ['login'])) {
|
||||||
|
@ -67,4 +69,4 @@ class TS3AdminProxy
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,49 +51,6 @@ class TSInstance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Login
|
|
||||||
*
|
|
||||||
* @param $user
|
|
||||||
* @param $password
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function login($user, $password)
|
|
||||||
{
|
|
||||||
if (!empty($user) && !empty($password)) {
|
|
||||||
$this->ts->login($user, $password);
|
|
||||||
$this->logger->debug(sprintf('Logged in as %s', $user));
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException('User and password not provided');
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getHost()
|
|
||||||
{
|
|
||||||
return $this->host;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getQueryPort()
|
|
||||||
{
|
|
||||||
return $this->queryPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ts3admin
|
|
||||||
*/
|
|
||||||
public function getInstance()
|
|
||||||
{
|
|
||||||
return $this->ts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
|
@ -224,4 +181,47 @@ class TSInstance
|
||||||
|
|
||||||
return $arr;
|
return $arr;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Login
|
||||||
|
*
|
||||||
|
* @param $user
|
||||||
|
* @param $password
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function login($user, $password)
|
||||||
|
{
|
||||||
|
if (!empty($user) && !empty($password)) {
|
||||||
|
$this->ts->login($user, $password);
|
||||||
|
$this->logger->debug(sprintf('Logged in as %s', $user));
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentException('User and password not provided');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getHost()
|
||||||
|
{
|
||||||
|
return $this->host;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getQueryPort()
|
||||||
|
{
|
||||||
|
return $this->queryPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ts3admin
|
||||||
|
*/
|
||||||
|
public function getInstance()
|
||||||
|
{
|
||||||
|
return $this->ts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -13,52 +13,110 @@ class Validator extends GUMP
|
||||||
$this->translator = BootstrapHelper::bootTranslator();
|
$this->translator = BootstrapHelper::bootTranslator();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function validate_set_min_len($field, $input, $param = NULL)
|
/**
|
||||||
|
* Perform data validation against the provided ruleset
|
||||||
|
*
|
||||||
|
* Arrays as FIELDS are added here as a custom feature
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param mixed $input
|
||||||
|
* @param array $ruleset
|
||||||
|
* @return mixed
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function validate(array $input, array $ruleset)
|
||||||
{
|
{
|
||||||
|
$this->errors = [];
|
||||||
|
|
||||||
$err = [
|
foreach ($ruleset as $field => $rules) {
|
||||||
'field' => $field,
|
#if(!array_key_exists($field, $input))
|
||||||
'value' => $input[$field],
|
#{
|
||||||
'rule' => __FUNCTION__,
|
# continue;
|
||||||
'param' => $param,
|
#}
|
||||||
];
|
|
||||||
|
|
||||||
if (!is_array($input[$field])) {
|
$rules = explode('|', $rules);
|
||||||
return $err;
|
|
||||||
|
if (in_array("required", $rules) || (isset($input[$field]) && (is_array($input[$field]) || trim($input[$field]) != ''))) {
|
||||||
|
|
||||||
|
foreach ($rules as $rule) {
|
||||||
|
$method = NULL;
|
||||||
|
$param = NULL;
|
||||||
|
|
||||||
|
if (strstr($rule, ',') !== false) // has params
|
||||||
|
{
|
||||||
|
$rule = explode(',', $rule);
|
||||||
|
$method = 'validate_' . $rule[0];
|
||||||
|
$param = $rule[1];
|
||||||
|
$rule = $rule[0];
|
||||||
|
} else {
|
||||||
|
$method = 'validate_' . $rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
// array required
|
||||||
|
if ($rule === "required" && !isset($input[$field])) {
|
||||||
|
$result = $this->$method($field, $input, $param);
|
||||||
|
$this->errors[] = $result;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_callable([$this, $method])) {
|
||||||
|
$result = $this->$method($field, $input, $param);
|
||||||
|
|
||||||
|
if (is_array($result)) // Validation Failed
|
||||||
|
{
|
||||||
|
$this->errors[] = $result;
|
||||||
|
|
||||||
|
return $this->errors;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isset(self::$validation_methods[$rule])) {
|
||||||
|
if (isset($input[$field])) {
|
||||||
|
$result = call_user_func(self::$validation_methods[$rule], $field, $input, $param);
|
||||||
|
|
||||||
|
$result = $this->$method($field, $input, $param);
|
||||||
|
|
||||||
|
if (is_array($result)) // Validation Failed
|
||||||
|
{
|
||||||
|
$this->errors[] = $result;
|
||||||
|
|
||||||
|
return $this->errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new \Exception("Validator method '$method' does not exist.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// default value
|
return (count($this->errors) > 0) ? $this->errors : true;
|
||||||
if (empty($param)) $param = 1;
|
|
||||||
|
|
||||||
if (count($input[$field]) < $param) return $err;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Validates if $field content is equal to $param
|
public function filter_upper($value, $param = NULL)
|
||||||
* @param $field
|
|
||||||
* @param $input
|
|
||||||
* @param $param
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function validate_equals($field, $input, $param)
|
|
||||||
{
|
{
|
||||||
$err = [
|
return strtoupper($value);
|
||||||
'field' => $field,
|
}
|
||||||
'value' => $input[$field],
|
|
||||||
'rule' => __FUNCTION__,
|
|
||||||
'param' => $param,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!isset($input[$field]) || empty($input[$field]) || empty($param) || !isset($param)) {
|
public function filter_lower($value, $param = NULL)
|
||||||
return $err;
|
{
|
||||||
|
return strtolower($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts all error array into a single string
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addErrorsToFlashMessage($flash)
|
||||||
|
{
|
||||||
|
$errors = $this->get_errors_array(true);
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
foreach ($errors as $error) {
|
||||||
|
$flash->addMessage('error', $error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($input[$field] != $param || $input[$field] !== $param) {
|
|
||||||
return $err;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -171,112 +229,6 @@ class Validator extends GUMP
|
||||||
return $resp;
|
return $resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform data validation against the provided ruleset
|
|
||||||
*
|
|
||||||
* Arrays as FIELDS are added here as a custom feature
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param mixed $input
|
|
||||||
* @param array $ruleset
|
|
||||||
* @return mixed
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function validate(array $input, array $ruleset)
|
|
||||||
{
|
|
||||||
$this->errors = [];
|
|
||||||
|
|
||||||
foreach ($ruleset as $field => $rules) {
|
|
||||||
#if(!array_key_exists($field, $input))
|
|
||||||
#{
|
|
||||||
# continue;
|
|
||||||
#}
|
|
||||||
|
|
||||||
$rules = explode('|', $rules);
|
|
||||||
|
|
||||||
if (in_array("required", $rules) || (isset($input[$field]) && (is_array($input[$field]) || trim($input[$field]) != ''))) {
|
|
||||||
|
|
||||||
foreach ($rules as $rule) {
|
|
||||||
$method = NULL;
|
|
||||||
$param = NULL;
|
|
||||||
|
|
||||||
if (strstr($rule, ',') !== false) // has params
|
|
||||||
{
|
|
||||||
$rule = explode(',', $rule);
|
|
||||||
$method = 'validate_' . $rule[0];
|
|
||||||
$param = $rule[1];
|
|
||||||
$rule = $rule[0];
|
|
||||||
} else {
|
|
||||||
$method = 'validate_' . $rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
// array required
|
|
||||||
if ($rule === "required" && !isset($input[$field])) {
|
|
||||||
$result = $this->$method($field, $input, $param);
|
|
||||||
$this->errors[] = $result;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_callable([$this, $method])) {
|
|
||||||
$result = $this->$method($field, $input, $param);
|
|
||||||
|
|
||||||
if (is_array($result)) // Validation Failed
|
|
||||||
{
|
|
||||||
$this->errors[] = $result;
|
|
||||||
|
|
||||||
return $this->errors;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isset(self::$validation_methods[$rule])) {
|
|
||||||
if (isset($input[$field])) {
|
|
||||||
$result = call_user_func(self::$validation_methods[$rule], $field, $input, $param);
|
|
||||||
|
|
||||||
$result = $this->$method($field, $input, $param);
|
|
||||||
|
|
||||||
if (is_array($result)) // Validation Failed
|
|
||||||
{
|
|
||||||
$this->errors[] = $result;
|
|
||||||
|
|
||||||
return $this->errors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new \Exception("Validator method '$method' does not exist.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (count($this->errors) > 0) ? $this->errors : true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function filter_upper($value, $param = NULL)
|
|
||||||
{
|
|
||||||
return strtoupper($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function filter_lower($value, $param = NULL)
|
|
||||||
{
|
|
||||||
return strtolower($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts all error array into a single string
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addErrorsToFlashMessage($flash)
|
|
||||||
{
|
|
||||||
$errors = $this->get_errors_array(true);
|
|
||||||
|
|
||||||
if (!empty($errors)) {
|
|
||||||
foreach ($errors as $error) {
|
|
||||||
$flash->addMessage('error', $error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $input
|
* @param array $input
|
||||||
* @param array $fields
|
* @param array $fields
|
||||||
|
@ -285,7 +237,7 @@ class Validator extends GUMP
|
||||||
*/
|
*/
|
||||||
public function sanitize(array $input, array $fields = array(), $utf8_encode = true)
|
public function sanitize(array $input, array $fields = array(), $utf8_encode = true)
|
||||||
{
|
{
|
||||||
$magic_quotes = (bool) get_magic_quotes_gpc();
|
$magic_quotes = (bool)get_magic_quotes_gpc();
|
||||||
|
|
||||||
if (empty($fields)) {
|
if (empty($fields)) {
|
||||||
$fields = array_keys($input);
|
$fields = array_keys($input);
|
||||||
|
@ -324,4 +276,52 @@ class Validator extends GUMP
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
protected function validate_set_min_len($field, $input, $param = NULL)
|
||||||
|
{
|
||||||
|
|
||||||
|
$err = [
|
||||||
|
'field' => $field,
|
||||||
|
'value' => $input[$field],
|
||||||
|
'rule' => __FUNCTION__,
|
||||||
|
'param' => $param,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!is_array($input[$field])) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// default value
|
||||||
|
if (empty($param)) $param = 1;
|
||||||
|
|
||||||
|
if (count($input[$field]) < $param) return $err;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Validates if $field content is equal to $param
|
||||||
|
* @param $field
|
||||||
|
* @param $input
|
||||||
|
* @param $param
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validate_equals($field, $input, $param)
|
||||||
|
{
|
||||||
|
$err = [
|
||||||
|
'field' => $field,
|
||||||
|
'value' => $input[$field],
|
||||||
|
'rule' => __FUNCTION__,
|
||||||
|
'param' => $param,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!isset($input[$field]) || empty($input[$field]) || empty($param) || !isset($param)) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input[$field] != $param || $input[$field] !== $param) {
|
||||||
|
return $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
'uri': '/clients/ban/' ~ sid ~ '/' ~ cldbid,
|
'uri': '/clients/ban/' ~ sid ~ '/' ~ cldbid,
|
||||||
'uri_method': 'post',
|
'uri_method': 'post',
|
||||||
'fields': [
|
'fields': [
|
||||||
{'type': 'text', 'key': 'reason', 'label': 'client_info.ban.reason'|trans},
|
{'type': 'text', 'key': 'reason', 'label': 'client_info.ban.reason'|trans},
|
||||||
{'type': 'number', 'key': 'time', 'label': 'client_info.ban.time'|trans}
|
{'type': 'number', 'key': 'time', 'label': 'client_info.ban.time'|trans}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'header_label': 'client_info.send'|trans,
|
'header_label': 'client_info.send'|trans,
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if channelGroups|length > 0 %}
|
{% if channelGroups|length > 0 %}
|
||||||
<h5>{% trans %}client_info.h.channelgroups{% endtrans %}</h5>
|
<h5>{% trans %}client_info.h.channelgroups{% endtrans %}</h5>
|
||||||
{% include 'table.twig' with {'data': channelGroups,
|
{% include 'table.twig' with {'data': channelGroups,
|
||||||
'hiddenColumns': ['cldbid', 'cid', 'cgid'],
|
'hiddenColumns': ['cldbid', 'cid', 'cgid'],
|
||||||
'additional_links': [
|
'additional_links': [
|
||||||
|
@ -71,4 +71,4 @@
|
||||||
<h5>{% trans %}client_info.h.permissions{% endtrans %}</h5>
|
<h5>{% trans %}client_info.h.permissions{% endtrans %}</h5>
|
||||||
{% include 'table.twig' with {'data': permissions} %}
|
{% include 'table.twig' with {'data': permissions} %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
<td>{{ path }}</td>
|
<td>{{ path }}</td>
|
||||||
<td>{{ file.size == 0 ? '' : file.size|file }}</td>
|
<td>{{ file.size == 0 ? '' : file.size|file }}</td>
|
||||||
<td>{{ file.datetime|timestamp }}</td>
|
<td>{{ file.datetime|timestamp }}</td>
|
||||||
<td><a href="/channels/files/delete/{{ sid }}/{{ cid }}?file={{ path }}"><i class="fa fa-trash"></i></a></td>
|
<td><a href="/channels/files/delete/{{ sid }}/{{ cid }}?file={{ path }}"><i class="fa fa-trash"></i></a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -39,4 +40,4 @@
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% elseif field.type == 'checkbox' %}
|
{% elseif field.type == 'checkbox' %}
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-4 col-form-label" for="{{ field.key }}">{{ field.label }}</label>
|
<label class="col-4 col-form-label" for="{{ field.key }}">{{ field.label }}</label>
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<input class="form-control form-control-sm" type="hidden" name="{{ field.key }}"
|
<input class="form-control form-control-sm" type="hidden" name="{{ field.key }}"
|
||||||
id="{{ editable.key }}" value="0"/>
|
id="{{ editable.key }}" value="0"/>
|
||||||
|
@ -40,10 +40,10 @@
|
||||||
<label class="col-4 col-form-label" for="{{ field.key }}">{{ field.label }}</label>
|
<label class="col-4 col-form-label" for="{{ field.key }}">{{ field.label }}</label>
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<input
|
<input
|
||||||
class="form-control form-control-sm"
|
class="form-control form-control-sm"
|
||||||
name="{{ field.key }}"
|
name="{{ field.key }}"
|
||||||
id="{{ field.key }}"
|
id="{{ field.key }}"
|
||||||
type="{{ field.type }}"/>
|
type="{{ field.type }}"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -53,4 +53,4 @@
|
||||||
<button class="btn btn-primary btn-sm" type="submit">{{ form.label|raw }}</button>
|
<button class="btn btn-primary btn-sm" type="submit">{{ form.label|raw }}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -18,39 +18,39 @@
|
||||||
<input type="hidden" name="{{ editable.key }}"
|
<input type="hidden" name="{{ editable.key }}"
|
||||||
id="{{ editable.key }}" value="0"
|
id="{{ editable.key }}" value="0"
|
||||||
|
|
||||||
{% if editable.readOnly is defined and editable.readOnly == true %}
|
{% if editable.readOnly is defined and editable.readOnly == true %}
|
||||||
readonly
|
readonly
|
||||||
{% endif %}
|
{% endif %}
|
||||||
/>
|
/>
|
||||||
<input class="form-check-input" type="checkbox" name="{{ editable.key }}"
|
<input class="form-check-input" type="checkbox" name="{{ editable.key }}"
|
||||||
id="{{ editable.key }}" value="1" checked
|
id="{{ editable.key }}" value="1" checked
|
||||||
|
|
||||||
{% if editable.readOnly is defined and editable.readOnly == true %}
|
{% if editable.readOnly is defined and editable.readOnly == true %}
|
||||||
readonly
|
readonly
|
||||||
{% endif %}
|
{% endif %}
|
||||||
/>
|
/>
|
||||||
{% else %}
|
{% else %}
|
||||||
<input type="hidden" name="{{ editable.key }}"
|
<input type="hidden" name="{{ editable.key }}"
|
||||||
id="{{ editable.key }}" value="0"
|
id="{{ editable.key }}" value="0"
|
||||||
|
|
||||||
{% if editable.readOnly is defined and editable.readOnly == true %}
|
{% if editable.readOnly is defined and editable.readOnly == true %}
|
||||||
readonly
|
readonly
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
<input class="form-check-input" type="checkbox" name="{{ editable.key }}"
|
<input class="form-check-input" type="checkbox" name="{{ editable.key }}"
|
||||||
id="{{ editable.key }}" value="1"
|
id="{{ editable.key }}" value="1"
|
||||||
|
|
||||||
{% if editable.readOnly is defined and editable.readOnly == true %}
|
{% if editable.readOnly is defined and editable.readOnly == true %}
|
||||||
readonly
|
readonly
|
||||||
{% endif %}
|
{% endif %}
|
||||||
/>
|
/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elseif editable.type == 'select' %}
|
{% elseif editable.type == 'select' %}
|
||||||
<select class="form-control form-control-sm" name="{{ editable.key }}" id="{{ editable.key }}"
|
<select class="form-control form-control-sm" name="{{ editable.key }}" id="{{ editable.key }}"
|
||||||
{% if editable.readOnly is defined and editable.readOnly == true %}
|
{% if editable.readOnly is defined and editable.readOnly == true %}
|
||||||
readonly
|
readonly
|
||||||
{% endif %}
|
{% endif %}
|
||||||
>
|
>
|
||||||
|
|
||||||
{% for k,v in editable.options %}
|
{% for k,v in editable.options %}
|
||||||
|
@ -64,13 +64,13 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
<input class="form-control form-control-sm" name="{{ editable.key }}" id="{{ editable.key }}"
|
<input class="form-control form-control-sm" name="{{ editable.key }}" id="{{ editable.key }}"
|
||||||
type="{{ editable.type }}"
|
type="{{ editable.type }}"
|
||||||
{% if editable.blank is defined and editable.blank == true %}
|
{% if editable.blank is defined and editable.blank == true %}
|
||||||
{% else %}
|
{% else %}
|
||||||
value="{{ item }}"
|
value="{{ item }}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if editable.readOnly is defined and editable.readOnly == true %}
|
{% if editable.readOnly is defined and editable.readOnly == true %}
|
||||||
readonly
|
readonly
|
||||||
{% endif %}
|
{% endif %}
|
||||||
/>
|
/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -79,4 +79,4 @@
|
||||||
type="submit">{{ editable.submit_label|raw }}</button>
|
type="submit">{{ editable.submit_label|raw }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -13,11 +13,11 @@
|
||||||
'uri': '/servergroups/create/' ~ sid,
|
'uri': '/servergroups/create/' ~ sid,
|
||||||
'uri_method': 'post',
|
'uri_method': 'post',
|
||||||
'fields': [
|
'fields': [
|
||||||
{'type': 'text', 'key': 'name', 'label': 'groups.create.name'|trans},
|
{'type': 'text', 'key': 'name', 'label': 'groups.create.name'|trans},
|
||||||
{'type': 'select', 'key': 'type', 'options': groupTypes, 'label': 'groups.create.type'|trans},
|
{'type': 'select', 'key': 'type', 'options': groupTypes, 'label': 'groups.create.type'|trans},
|
||||||
{'type': 'checkbox', 'key': 'copy', 'label': 'groups.create.copy'|trans},
|
{'type': 'checkbox', 'key': 'copy', 'label': 'groups.create.copy'|trans},
|
||||||
{'type': 'select', 'key': 'template', 'options': serverGroupsTemplate, 'label': 'groups.create.template'|trans}
|
{'type': 'select', 'key': 'template', 'options': serverGroupsTemplate, 'label': 'groups.create.template'|trans}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
} %}
|
} %}
|
||||||
|
@ -104,4 +104,4 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
{% include 'no_entities.twig' %}
|
{% include 'no_entities.twig' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends 'layout.twig' %}
|
{% extends 'layout.twig' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h3 >{{ title }}</h3>
|
<h3>{{ title }}</h3>
|
||||||
{% include 'keyvalue.twig' with {'data': data,
|
{% include 'keyvalue.twig' with {'data': data,
|
||||||
'filters': [
|
'filters': [
|
||||||
{'key': 'instance_uptime', 'apply': 'timeInSeconds'},
|
{'key': 'instance_uptime', 'apply': 'timeInSeconds'},
|
||||||
|
@ -28,4 +28,4 @@
|
||||||
{'key': 'serverinstance_serverquery_flood_ban_time', 'type': 'number', 'uri': '/instance/edit', 'uri_method': 'post', 'submit_label': '<i class="fa fa-check"></i>'},
|
{'key': 'serverinstance_serverquery_flood_ban_time', 'type': 'number', 'uri': '/instance/edit', 'uri_method': 'post', 'submit_label': '<i class="fa fa-check"></i>'},
|
||||||
]
|
]
|
||||||
} %}
|
} %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
{% if editable is not empty %}
|
{% if editable is not empty %}
|
||||||
{% include 'form_inline.twig' with {'editable': editable} %}
|
{% include 'form_inline.twig' with {'editable': editable} %}
|
||||||
|
|
||||||
<!-- cell is static or a link-->
|
<!-- cell is static or a link-->
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ item }}
|
{{ item }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -43,4 +43,4 @@
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="username" class="sr-only">{% trans %}login.form.username{% endtrans %}</label>
|
<label for="username"
|
||||||
|
class="sr-only">{% trans %}login.form.username{% endtrans %}</label>
|
||||||
<input type="text" id="username" name="username" class="form-control form-control-sm"
|
<input type="text" id="username" name="username" class="form-control form-control-sm"
|
||||||
placeholder="{{ getenv('teamspeak_user') }}"
|
placeholder="{{ getenv('teamspeak_user') }}"
|
||||||
value="{{ getenv('teamspeak_user') }}"
|
value="{{ getenv('teamspeak_user') }}"
|
||||||
|
@ -28,7 +29,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password" class="sr-only">{% trans %}login.form.password{% endtrans %}</label>
|
<label for="password"
|
||||||
|
class="sr-only">{% trans %}login.form.password{% endtrans %}</label>
|
||||||
<input type="password" id="password" name="password"
|
<input type="password" id="password" name="password"
|
||||||
class="form-control form-control-sm"
|
class="form-control form-control-sm"
|
||||||
placeholder="{% trans %}login.form.password.placeholder{% endtrans %}" required>
|
placeholder="{% trans %}login.form.password.placeholder{% endtrans %}" required>
|
||||||
|
@ -44,4 +46,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if appLog|length > 0 %}
|
{% if appLog|length > 0 %}
|
||||||
<br />
|
<br/>
|
||||||
<h3>{% trans %}app_log.title{% endtrans %}</h3>
|
<h3>{% trans %}app_log.title{% endtrans %}</h3>
|
||||||
<div class="small">
|
<div class="small">
|
||||||
{% for line in appLog %}
|
{% for line in appLog %}
|
||||||
|
@ -24,4 +24,4 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -9,49 +9,67 @@
|
||||||
<ul class="navbar-nav ml-auto">
|
<ul class="navbar-nav ml-auto">
|
||||||
|
|
||||||
{% if currentUser is not empty %}
|
{% if currentUser is not empty %}
|
||||||
<li class="nav-item"><a class="nav-link" href="/instance"><i class="fa fa-info"></i> {% trans %}menu.instance{% endtrans %}</a>
|
<li class="nav-item"><a class="nav-link" href="/instance"><i
|
||||||
|
class="fa fa-info"></i> {% trans %}menu.instance{% endtrans %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/logs"><i class="fa fa-file"></i> {% trans %}menu.logs{% endtrans %}</a></li>
|
<li class="nav-item"><a class="nav-link" href="/logs"><i
|
||||||
|
class="fa fa-file"></i> {% trans %}menu.logs{% endtrans %}</a></li>
|
||||||
|
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
|
|
||||||
{% if session_exists('sid') %}
|
{% if session_exists('sid') %}
|
||||||
{% if session_exists('sport') %}
|
{% if session_exists('sport') %}
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="dropdownServer" data-toggle="dropdown"
|
<a class="nav-link dropdown-toggle" href="#" id="dropdownServer" data-toggle="dropdown"
|
||||||
aria-haspopup="true" aria-expanded="false"><i class="fa fa-star"></i> {{ session_get('sport') }}</a>
|
aria-haspopup="true" aria-expanded="false"><i
|
||||||
|
class="fa fa-star"></i> {{ session_get('sport') }}</a>
|
||||||
{% elseif session_exists('sname') %}
|
{% elseif session_exists('sname') %}
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="dropdownServer" data-toggle="dropdown"
|
<a class="nav-link dropdown-toggle" href="#" id="dropdownServer" data-toggle="dropdown"
|
||||||
aria-haspopup="true" aria-expanded="false"><i class="fa fa-star"></i> {{ session_get('sname') }}</a>
|
aria-haspopup="true" aria-expanded="false"><i
|
||||||
|
class="fa fa-star"></i> {{ session_get('sname') }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="dropdown-menu" aria-labelledby="dropdownServer">
|
<div class="dropdown-menu" aria-labelledby="dropdownServer">
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/servers/{{ session_get('sid') }}"><i class="fa fa-info"></i> {% trans %}menu.servers.info{% endtrans %}</a>
|
href="/servers/{{ session_get('sid') }}"><i
|
||||||
|
class="fa fa-info"></i> {% trans %}menu.servers.info{% endtrans %}</a>
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/online/{{ session_get('sid') }}"><i class="fa fa-signal"></i> {% trans %}menu.servers.online{% endtrans %}</a>
|
href="/online/{{ session_get('sid') }}"><i
|
||||||
|
class="fa fa-signal"></i> {% trans %}menu.servers.online{% endtrans %}</a>
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/clients/{{ session_get('sid') }}"><i class="fa fa-user-plus"></i> {% trans %}menu.servers.clients{% endtrans %}</a>
|
href="/clients/{{ session_get('sid') }}"><i
|
||||||
|
class="fa fa-user-plus"></i> {% trans %}menu.servers.clients{% endtrans %}</a>
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/groups/{{ session_get('sid') }}"><i class="fa fa-group"></i> {% trans %}menu.servers.groups{% endtrans %}</a>
|
href="/groups/{{ session_get('sid') }}"><i
|
||||||
|
class="fa fa-group"></i> {% trans %}menu.servers.groups{% endtrans %}</a>
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/channels/{{ session_get('sid') }}"><i class="fa fa-tv"></i> {% trans %}menu.servers.channels{% endtrans %}</a>
|
href="/channels/{{ session_get('sid') }}"><i
|
||||||
|
class="fa fa-tv"></i> {% trans %}menu.servers.channels{% endtrans %}</a>
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/bans/{{ session_get('sid') }}"><i class="fa fa-ban"></i> {% trans %}menu.servers.bans{% endtrans %}</a>
|
href="/bans/{{ session_get('sid') }}"><i
|
||||||
|
class="fa fa-ban"></i> {% trans %}menu.servers.bans{% endtrans %}</a>
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/complains/{{ session_get('sid') }}"><i class="fa fa-thumbs-down"></i> {% trans %}menu.servers.complains{% endtrans %}</a>
|
href="/complains/{{ session_get('sid') }}"><i
|
||||||
|
class="fa fa-thumbs-down"></i> {% trans %}menu.servers.complains{% endtrans %}
|
||||||
|
</a>
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/passwords/{{ session_get('sid') }}"><i class="fa fa-key"></i> {% trans %}menu.servers.passwords{% endtrans %}</a>
|
href="/passwords/{{ session_get('sid') }}"><i
|
||||||
|
class="fa fa-key"></i> {% trans %}menu.servers.passwords{% endtrans %}</a>
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/tokens/{{ session_get('sid') }}"><i class="fa fa-user-secret"></i> {% trans %}menu.servers.tokens{% endtrans %}</a>
|
href="/tokens/{{ session_get('sid') }}"><i
|
||||||
|
class="fa fa-user-secret"></i> {% trans %}menu.servers.tokens{% endtrans %}</a>
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/snapshots/{{ session_get('sid') }}"><i class="fa fa-save"></i> {% trans %}menu.servers.snapshots{% endtrans %}</a>
|
href="/snapshots/{{ session_get('sid') }}"><i
|
||||||
|
class="fa fa-save"></i> {% trans %}menu.servers.snapshots{% endtrans %}</a>
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/logs/{{ session_get('sid') }}"><i class="fa fa-file"></i> {% trans %}menu.servers.logs{% endtrans %}</a>
|
href="/logs/{{ session_get('sid') }}"><i
|
||||||
|
class="fa fa-file"></i> {% trans %}menu.servers.logs{% endtrans %}</a>
|
||||||
<hr/>
|
<hr/>
|
||||||
<a class="dropdown-item" href="/servers/deselect"><i class="fa fa-close"></i> {% trans %}menu.servers.deselect{% endtrans %}</a>
|
<a class="dropdown-item" href="/servers/deselect"><i
|
||||||
|
class="fa fa-close"></i> {% trans %}menu.servers.deselect{% endtrans %}</a>
|
||||||
<hr/>
|
<hr/>
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/servers"><i class="fa fa-server"></i> {% trans %}menu.servers{% endtrans %}</a>
|
href="/servers"><i class="fa fa-server"></i> {% trans %}menu.servers{% endtrans %}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
|
@ -61,7 +79,8 @@
|
||||||
aria-expanded="false">{% trans %}menu.servers.select{% endtrans %}</a>
|
aria-expanded="false">{% trans %}menu.servers.select{% endtrans %}</a>
|
||||||
<div class="dropdown-menu" aria-labelledby="dropdownServer">
|
<div class="dropdown-menu" aria-labelledby="dropdownServer">
|
||||||
<a class="dropdown-item"
|
<a class="dropdown-item"
|
||||||
href="/servers"><i class="fa fa-server"></i> {% trans %}menu.servers{% endtrans %}</a>
|
href="/servers"><i class="fa fa-server"></i> {% trans %}menu.servers{% endtrans %}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
|
@ -69,16 +88,20 @@
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="dropdownUser" data-toggle="dropdown"
|
<a class="nav-link dropdown-toggle" href="#" id="dropdownUser" data-toggle="dropdown"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-expanded="false"><i class="fa fa-user"></i> {% trans with {'%username%': currentUser.user } %}menu.currentlyloggedin{% endtrans %}</a>
|
aria-expanded="false"><i
|
||||||
|
class="fa fa-user"></i> {% trans with {'%username%': currentUser.user } %}menu.currentlyloggedin{% endtrans %}
|
||||||
|
</a>
|
||||||
<div class="dropdown-menu" aria-labelledby="dropdownUser">
|
<div class="dropdown-menu" aria-labelledby="dropdownUser">
|
||||||
<a class="dropdown-item" href="/logout"><i class="fa fa-sign-out"></i> {% trans %}menu.logout{% endtrans %}</a>
|
<a class="dropdown-item" href="/logout"><i
|
||||||
|
class="fa fa-sign-out"></i> {% trans %}menu.logout{% endtrans %}</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class="nav-item"><a class="nav-link" href="/login"><i class="fa fa-sign-in"></i> {% trans %}menu.login{% endtrans %}</a></li>
|
<li class="nav-item"><a class="nav-link" href="/login"><i
|
||||||
|
class="fa fa-sign-in"></i> {% trans %}menu.login{% endtrans %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if data|length >0 %}
|
{% if data|length >0 %}
|
||||||
{% include 'table.twig' with {'data': data,
|
{% include 'table.twig' with {'data': data,
|
||||||
'hiddenDependingOnAttribute': [{'key': 'client_type', 'values': ['1']}],
|
'hiddenDependingOnAttribute': [{'key': 'client_type', 'values': ['1']}],
|
||||||
|
@ -49,4 +49,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
'uri': '/online/poke/' ~ sid ~ '/' ~ clid,
|
'uri': '/online/poke/' ~ sid ~ '/' ~ clid,
|
||||||
'uri_method': 'post',
|
'uri_method': 'post',
|
||||||
'fields': [
|
'fields': [
|
||||||
{'type': 'text', 'key': 'message', 'label': 'online.poke.message'|trans},
|
{'type': 'text', 'key': 'message', 'label': 'online.poke.message'|trans},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'header_label': 'online.kick'|trans,
|
'header_label': 'online.kick'|trans,
|
||||||
|
@ -21,8 +21,8 @@
|
||||||
'uri': '/online/kick/' ~ sid ~ '/' ~ clid,
|
'uri': '/online/kick/' ~ sid ~ '/' ~ clid,
|
||||||
'uri_method': 'post',
|
'uri_method': 'post',
|
||||||
'fields': [
|
'fields': [
|
||||||
{'type': 'text', 'key': 'reason', 'label': 'online.kick.reason'|trans},
|
{'type': 'text', 'key': 'reason', 'label': 'online.kick.reason'|trans},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'header_label': 'online.ban'|trans,
|
'header_label': 'online.ban'|trans,
|
||||||
|
@ -30,9 +30,9 @@
|
||||||
'uri': '/online/ban/' ~ sid ~ '/' ~ clid,
|
'uri': '/online/ban/' ~ sid ~ '/' ~ clid,
|
||||||
'uri_method': 'post',
|
'uri_method': 'post',
|
||||||
'fields': [
|
'fields': [
|
||||||
{'type': 'text', 'key': 'reason', 'label': 'online.ban.reason'|trans},
|
{'type': 'text', 'key': 'reason', 'label': 'online.ban.reason'|trans},
|
||||||
{'type': 'number', 'key': 'time', 'label': 'online.ban.time'|trans}
|
{'type': 'number', 'key': 'time', 'label': 'online.ban.time'|trans}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'header_label': 'online.move'|trans,
|
'header_label': 'online.move'|trans,
|
||||||
|
@ -40,9 +40,9 @@
|
||||||
'uri': '/online/move/' ~ sid ~ '/' ~ clid,
|
'uri': '/online/move/' ~ sid ~ '/' ~ clid,
|
||||||
'uri_method': 'post',
|
'uri_method': 'post',
|
||||||
'fields': [
|
'fields': [
|
||||||
{'type': 'select', 'key': 'channel', 'options': channels,'label': 'online.move.channel'|trans},
|
{'type': 'select', 'key': 'channel', 'options': channels,'label': 'online.move.channel'|trans},
|
||||||
{'type': 'text', 'key': 'channel_password', 'label': 'online.move.channel_password'|trans}
|
{'type': 'text', 'key': 'channel_password', 'label': 'online.move.channel_password'|trans}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'header_label': 'online.send'|trans,
|
'header_label': 'online.send'|trans,
|
||||||
|
@ -50,8 +50,8 @@
|
||||||
'uri': '/online/send/' ~ sid ~ '/' ~ clid,
|
'uri': '/online/send/' ~ sid ~ '/' ~ clid,
|
||||||
'uri_method': 'post',
|
'uri_method': 'post',
|
||||||
'fields': [
|
'fields': [
|
||||||
{'type': 'text', 'key': 'message', 'label': 'online.send.message'|trans},
|
{'type': 'text', 'key': 'message', 'label': 'online.send.message'|trans},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
} %}
|
} %}
|
||||||
|
@ -78,4 +78,4 @@
|
||||||
{'key': 'connection_bytes_sent_total', 'apply': 'file'},
|
{'key': 'connection_bytes_sent_total', 'apply': 'file'},
|
||||||
{'key': 'connection_bytes_received_total', 'apply': 'file'},
|
{'key': 'connection_bytes_received_total', 'apply': 'file'},
|
||||||
]} %}
|
]} %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -13,12 +13,12 @@
|
||||||
'uri': '/passwords/add/' ~ sid,
|
'uri': '/passwords/add/' ~ sid,
|
||||||
'uri_method': 'post',
|
'uri_method': 'post',
|
||||||
'fields': [
|
'fields': [
|
||||||
{'type': 'text', 'key': 'password', 'label': 'passwords.add.password'|trans},
|
{'type': 'text', 'key': 'password', 'label': 'passwords.add.password'|trans},
|
||||||
{'type': 'number', 'key': 'duration', 'label': 'passwords.add.duration'|trans},
|
{'type': 'number', 'key': 'duration', 'label': 'passwords.add.duration'|trans},
|
||||||
{'type': 'text', 'key': 'description', 'label': 'passwords.add.description'|trans},
|
{'type': 'text', 'key': 'description', 'label': 'passwords.add.description'|trans},
|
||||||
{'type': 'select', 'key': 'channel', 'options': channels,'label': 'passwords.add.channel'|trans},
|
{'type': 'select', 'key': 'channel', 'options': channels,'label': 'passwords.add.channel'|trans},
|
||||||
{'type': 'text', 'key': 'channel_password', 'label': 'passwords.add.channel_password'|trans},
|
{'type': 'text', 'key': 'channel_password', 'label': 'passwords.add.channel_password'|trans},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
} %}
|
} %}
|
||||||
|
@ -49,4 +49,4 @@
|
||||||
]
|
]
|
||||||
} %}
|
} %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -11,15 +11,15 @@
|
||||||
<h5>{% trans %}server_info.h.actions{% endtrans %}</h5>
|
<h5>{% trans %}server_info.h.actions{% endtrans %}</h5>
|
||||||
{% include 'form.twig' with {
|
{% include 'form.twig' with {
|
||||||
'fields': [
|
'fields': [
|
||||||
{
|
{
|
||||||
'header_label': 'server_info.send'|trans,
|
'header_label': 'server_info.send'|trans,
|
||||||
'label': '<i class="fa fa-check"></i>',
|
'label': '<i class="fa fa-check"></i>',
|
||||||
'uri': '/servers/send/' ~ sid,
|
'uri': '/servers/send/' ~ sid,
|
||||||
'uri_method': 'post',
|
'uri_method': 'post',
|
||||||
'fields': [
|
'fields': [
|
||||||
{'type': 'text', 'key': 'message', 'label': 'server_info.send.message'|trans},
|
{'type': 'text', 'key': 'message', 'label': 'server_info.send.message'|trans},
|
||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
} %}
|
} %}
|
||||||
|
|
||||||
<h5>{% trans %}server_info.h.details{% endtrans %}</h5>
|
<h5>{% trans %}server_info.h.details{% endtrans %}</h5>
|
||||||
|
@ -90,4 +90,4 @@
|
||||||
]
|
]
|
||||||
} %}
|
} %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
'uri': '/servergroups/add/' ~ sid ~ '/' ~ sgid,
|
'uri': '/servergroups/add/' ~ sid ~ '/' ~ sgid,
|
||||||
'uri_method': 'post',
|
'uri_method': 'post',
|
||||||
'fields': [
|
'fields': [
|
||||||
{'type': 'text', 'key': 'cldbid', 'label': 'servergroup_info.add.cldbid'|trans},
|
{'type': 'text', 'key': 'cldbid', 'label': 'servergroup_info.add.cldbid'|trans},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
} %}
|
} %}
|
||||||
|
@ -41,4 +41,4 @@
|
||||||
{% include 'table.twig' with {'data': permissions} %}
|
{% include 'table.twig' with {'data': permissions} %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -5,42 +5,42 @@
|
||||||
|
|
||||||
<h5>{% trans %}servers.h.details{% endtrans %}</h5>
|
<h5>{% trans %}servers.h.details{% endtrans %}</h5>
|
||||||
{% if data|length > 0 %}
|
{% if data|length > 0 %}
|
||||||
{% include 'table.twig' with {'data': data,
|
{% include 'table.twig' with {'data': data,
|
||||||
'filters': [
|
'filters': [
|
||||||
{'key': 'virtualserver_uptime', 'apply': 'timeInSeconds'},
|
{'key': 'virtualserver_uptime', 'apply': 'timeInSeconds'},
|
||||||
],
|
],
|
||||||
'links': [
|
'links': [
|
||||||
{'key': 'virtualserver_port', 'uri': '/servers', 'uri_param': 'virtualserver_id'},
|
{'key': 'virtualserver_port', 'uri': '/servers', 'uri_param': 'virtualserver_id'},
|
||||||
{'key': 'virtualserver_clientsonline', 'uri': '/online', 'uri_param': 'virtualserver_id'}
|
{'key': 'virtualserver_clientsonline', 'uri': '/online', 'uri_param': 'virtualserver_id'}
|
||||||
],
|
],
|
||||||
'hiddenColumns': ['virtualserver_id', 'virtualserver_queryclientsonline', 'virtualserver_autostart', 'virtualserver_machine_id'],
|
'hiddenColumns': ['virtualserver_id', 'virtualserver_queryclientsonline', 'virtualserver_autostart', 'virtualserver_machine_id'],
|
||||||
'additional_links': [
|
'additional_links': [
|
||||||
{
|
{
|
||||||
'header_label': 'servers.select'|trans,
|
'header_label': 'servers.select'|trans,
|
||||||
'label': '<i class="fa fa-check"></i>',
|
'label': '<i class="fa fa-check"></i>',
|
||||||
'uri': '/servers/select',
|
'uri': '/servers/select',
|
||||||
'uri_param': 'virtualserver_id'
|
'uri_param': 'virtualserver_id'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'header_label': 'servers.start'|trans,
|
'header_label': 'servers.start'|trans,
|
||||||
'label': '<i class="fa fa-play"></i>',
|
'label': '<i class="fa fa-play"></i>',
|
||||||
'uri': '/servers/start',
|
'uri': '/servers/start',
|
||||||
'uri_param': 'virtualserver_id'
|
'uri_param': 'virtualserver_id'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'header_label': 'servers.stop'|trans,
|
'header_label': 'servers.stop'|trans,
|
||||||
'label': '<i class="fa fa-stop"></i>',
|
'label': '<i class="fa fa-stop"></i>',
|
||||||
'uri': '/servers/stop',
|
'uri': '/servers/stop',
|
||||||
'uri_param': 'virtualserver_id'
|
'uri_param': 'virtualserver_id'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'header_label': 'servers.delete'|trans,
|
'header_label': 'servers.delete'|trans,
|
||||||
'label': '<i class="fa fa-trash"></i>',
|
'label': '<i class="fa fa-trash"></i>',
|
||||||
'uri': '/servers/delete',
|
'uri': '/servers/delete',
|
||||||
'uri_param': 'virtualserver_id'
|
'uri_param': 'virtualserver_id'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
} %}
|
} %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% include 'no_entities.twig' %}
|
{% include 'no_entities.twig' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -54,11 +54,11 @@
|
||||||
'uri': '/servers/create',
|
'uri': '/servers/create',
|
||||||
'uri_method': 'post',
|
'uri_method': 'post',
|
||||||
'fields': [
|
'fields': [
|
||||||
{'type': 'text', 'key': 'VIRTUALSERVER_NAME', 'label': 'server_create.VIRTUALSERVER_NAME'|trans},
|
{'type': 'text', 'key': 'VIRTUALSERVER_NAME', 'label': 'server_create.VIRTUALSERVER_NAME'|trans},
|
||||||
{'type': 'text', 'key': 'VIRTUALSERVER_PASSWORD', 'label': 'server_create.VIRTUALSERVER_PASSWORD'|trans},
|
{'type': 'text', 'key': 'VIRTUALSERVER_PASSWORD', 'label': 'server_create.VIRTUALSERVER_PASSWORD'|trans},
|
||||||
{'type': 'number', 'key': 'VIRTUALSERVER_PORT', 'label': 'server_create.VIRTUALSERVER_PORT'|trans},
|
{'type': 'number', 'key': 'VIRTUALSERVER_PORT', 'label': 'server_create.VIRTUALSERVER_PORT'|trans},
|
||||||
{'type': 'number', 'key': 'VIRTUALSERVER_MAXCLIENTS', 'label': 'server_create.VIRTUALSERVER_MAXCLIENTS'|trans},
|
{'type': 'number', 'key': 'VIRTUALSERVER_MAXCLIENTS', 'label': 'server_create.VIRTUALSERVER_MAXCLIENTS'|trans},
|
||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
} %}
|
} %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -11,21 +11,21 @@
|
||||||
|
|
||||||
{% include 'table.twig' with {'data': data,
|
{% include 'table.twig' with {'data': data,
|
||||||
|
|
||||||
'additional_links': [
|
'additional_links': [
|
||||||
{
|
{
|
||||||
'header_label': 'snapshots.deploy'|trans,
|
'header_label': 'snapshots.deploy'|trans,
|
||||||
'label': '<i class="fa fa-check"></i>',
|
'label': '<i class="fa fa-check"></i>',
|
||||||
'uri': '/snapshots/deploy/' ~ sid,
|
'uri': '/snapshots/deploy/' ~ sid,
|
||||||
'uri_param': 'name'
|
'uri_param': 'name'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'header_label': 'snapshots.delete'|trans,
|
'header_label': 'snapshots.delete'|trans,
|
||||||
'label': '<i class="fa fa-trash"></i>',
|
'label': '<i class="fa fa-trash"></i>',
|
||||||
'uri': '/snapshots/delete/' ~ sid,
|
'uri': '/snapshots/delete/' ~ sid,
|
||||||
'uri_param': 'name'
|
'uri_param': 'name'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
} %}
|
} %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Reference in a new issue