Modified channel files, removed about modal
This commit is contained in:
parent
cae20ac477
commit
3bb5e51b30
30 changed files with 292 additions and 103 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -1,5 +1,15 @@
|
|||
# CHANGELOG
|
||||
|
||||
## 2.1.0 - 2019/08/07
|
||||
* Fixed file handling on snapshots
|
||||
* Cleaned up template links
|
||||
* Updated documentation
|
||||
* Added application log view
|
||||
* Fixed files view
|
||||
* Added show total used space for files in a channel
|
||||
* Added file delete action
|
||||
* Removed about modal
|
||||
|
||||
## 2.0.0 - 2019/08/06
|
||||
* Replace material design with bootstrap4 theme
|
||||
* Add an about modal
|
||||
|
|
53
README.md
53
README.md
|
@ -1,28 +1,46 @@
|
|||
# README
|
||||
ts3web is a free and open-source web interface for TeamSpeak 3 instances.
|
||||
|
||||
ts3web is a web interface for one TeamSpeak 3 Server. It's using serverquery to login.
|
||||
The minimalistic approach of this application is intentional.
|
||||
|
||||
This web interface aims to be as simple as possible. The minimalistic approach is intentional.
|
||||
* Docker images available on https://hub.docker.com/r/varakh/ts3web
|
||||
* Sources are hosted on https://git.myservermanager.com/alexander.schaeferdiek/ts3web
|
||||
|
||||
Feel free to submit pull requests if you like to help. More information are here: [https://hub.docker.com/r/varakh/ts3web](https://hub.docker.com/r/varakh/ts3web)
|
||||
|
||||
Features which are currently **not supported**:
|
||||
## Limitations
|
||||
Features which are currently not supported:
|
||||
|
||||
* upload files (only viewing and deleting)
|
||||
* modify permissions (only viewing)
|
||||
* modify files (only viewing)
|
||||
|
||||
**ts3web** can be deployed in different ways. See below for more information. For each deployment type a running
|
||||
TeamSpeak 3 server is a prerequisite (except for the `docker-compose.yml` type which will start also the server if
|
||||
needed).
|
||||
## F.A.Q
|
||||
|
||||
###### There are lots of TeamSpeak 3 web interfaces out. Why should I pick ts3web?
|
||||
Free, simple, stateless, easy to extend, standard bootstrap theme.
|
||||
|
||||
###### 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 and include it in
|
||||
your TeamSpeak application.
|
||||
|
||||
## Configuration
|
||||
|
||||
The main configuration file is the `env` file located in `config/`. There's an example file called `env.example`
|
||||
which you can copy to `config/env`. Defaults will assume you're running your TeamSpeak server on `localhost` with
|
||||
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 host bind this file into the container directly and just maintain the `env` file.
|
||||
|
||||
## Usage with docker-compose
|
||||
## Deployment
|
||||
The application can be deployed in different ways. See below for more information. For each deployment type a running
|
||||
TeamSpeak 3 instance is a prerequisite (except for the `docker-compose.yml` type which will start also the server if
|
||||
needed).
|
||||
|
||||
### Exposed volumes on docker images
|
||||
* Snapshots are saved in `/var/www/html/application/data/snapshots`. You should create a volume for this location if
|
||||
you're using docker as deployment type.
|
||||
* Logs are saved in `/var/www/html/application/log` for docker containers. You should create a volume
|
||||
for this location if you're using docker as deployment type.
|
||||
|
||||
**Important**: Ensure that host binds have permissions set up properly. The user which is used in the docker container is `www-data` with
|
||||
id `82`. If, e.g. logs are host bound, then execute `chown -R 82:82 host/path/to/log`. The same holds true for snapshots.
|
||||
|
||||
### Usage with docker-compose
|
||||
The recommended way is to use docker-compose. The `network_mode = "host"` is required in order to show correct IP
|
||||
addresses of connected users.
|
||||
|
||||
|
@ -59,6 +77,7 @@ services:
|
|||
volumes:
|
||||
- ./env:/var/www/html/application/config/env
|
||||
- ./snapshots:/var/www/html/application/data/snapshots
|
||||
- ./log:/var/www/html/application/log
|
||||
ports:
|
||||
- 127.0.0.1:8181:80
|
||||
depends_on:
|
||||
|
@ -76,16 +95,13 @@ Your TeamSpeak 3 Server will be available under `public-server-ip:9987`. The web
|
|||
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`. This is **not recommended**! Secure your setup properly via reverse proxy and SSL.
|
||||
|
||||
Snapshots are saved in `/var/www/html/application/data/snapshots`. You should create a volume for this location.
|
||||
|
||||
## Usage as single docker container
|
||||
|
||||
### Usage as single docker container
|
||||
* Copy `env.example` to `env` and adjust to your needs. It's recommended to make it persistent outside of the container.
|
||||
* Create a container with the image, e.g. `docker run --name teamspeak_web -v ./env:/var/www/html/application/config/env -p 8181:80 varakh/ts3web:latest`.
|
||||
* Make sure that if teamspeak and ts3web share the same docker instance they should be put into one network and the subnet **needs be added to teamspeak's query whitelist**.
|
||||
* Point your browser to `8181` to see the web interface.
|
||||
|
||||
## Usage as native application
|
||||
### Usage as native application
|
||||
**Prerequisite**: `php`, `composer` and probably `php-fpm` installed on the server.
|
||||
|
||||
To install:
|
||||
|
@ -100,7 +116,7 @@ To upgrade:
|
|||
* `git pull`
|
||||
* `composer update`
|
||||
|
||||
## Web server setup
|
||||
### Web server setup
|
||||
* Example `nginx.conf` for **standalone** deployment without SSL:
|
||||
|
||||
```
|
||||
|
@ -153,7 +169,6 @@ To upgrade:
|
|||
* Tag the release git commit and create a new release in the VCS web interface
|
||||
|
||||
### 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.
|
||||
|
||||
```
|
||||
|
|
|
@ -70,6 +70,7 @@ class ACL extends \Zend\Permissions\Acl\Acl
|
|||
'/channels/edit/{sid}/{cid}',
|
||||
'/channels/delete/{sid}/{cid}',
|
||||
'/channels/send/{sid}/{cid}',
|
||||
'/channels/files/delete/{sid}/{cid}',
|
||||
|
||||
'/groups/{sid}',
|
||||
|
||||
|
|
|
@ -50,6 +50,11 @@ class EnvConstants
|
|||
*/
|
||||
const TEAMSPEAK_USER = "teamspeak_user";
|
||||
|
||||
/**
|
||||
* TeamSpeak log lines
|
||||
*/
|
||||
const TEAMSPEAK_LOG_LINES = "teamspeak_log_lines";
|
||||
|
||||
/**
|
||||
* Log name
|
||||
*/
|
||||
|
|
|
@ -12,6 +12,7 @@ teamspeak_host="localhost" # 'localhost' or 'name_of_docker_container' if runnin
|
|||
teamspeak_query_port=10011
|
||||
teamspeak_user="serveradmin"
|
||||
teamspeak_tree_view="true" # show a tree view in the details of online clients if a server has been selected
|
||||
teamspeak_log_lines=100 # show this amount of latest log lines
|
||||
|
||||
# log
|
||||
log_name="ts3web" # values: all strings
|
||||
|
|
|
@ -294,6 +294,11 @@ $container[ChannelSendAction::class] = function ($container) {
|
|||
};
|
||||
$app->post('/channels/send/{sid}/{cid}', ChannelSendAction::class);
|
||||
|
||||
$container[ChannelFilesDeleteAction::class] = function ($container) {
|
||||
return new ChannelFilesDeleteAction($container);
|
||||
};
|
||||
$app->get('/channels/files/delete/{sid}/{cid}', ChannelFilesDeleteAction::class);
|
||||
|
||||
// tokens
|
||||
$container[TokensAction::class] = function ($container) {
|
||||
return new TokensAction($container);
|
||||
|
|
|
@ -58,7 +58,7 @@ validate_valid_url: "The %field% field is required to be a valid URL."
|
|||
# menu
|
||||
menu.instance: "Instance"
|
||||
menu.servers: "Servers"
|
||||
menu.logs: "Instance Log"
|
||||
menu.logs: "Logs"
|
||||
menu.profile: "Profile"
|
||||
|
||||
menu.servers.info: "Info"
|
||||
|
@ -76,7 +76,6 @@ menu.servers.logs: "Log"
|
|||
# titles
|
||||
instance.title: "Instance"
|
||||
servers.title: "Servers"
|
||||
logs.title: "Latest 100 Log Entries"
|
||||
server_info.title: "Server Info"
|
||||
online.title: "Online Clients"
|
||||
online_info.title: "Online Info"
|
||||
|
@ -93,6 +92,9 @@ profile.title: "Profile"
|
|||
tokens.title: "Tokens"
|
||||
snapshots.title: "Snapshots"
|
||||
passwords.title: "Passwords"
|
||||
instance_logs.title: "Instance log: latest 100 entries"
|
||||
server_logs.title: "Server log: latest 100 entries"
|
||||
app_log.title: "Application log"
|
||||
|
||||
# dynamic render of key value pairs
|
||||
key: "Attribute"
|
||||
|
@ -157,16 +159,23 @@ channels.create.parent: "Parent"
|
|||
channel_info.h.files: "Files"
|
||||
channel_info.h.actions: "Actions"
|
||||
channel_info.h.details: "Details"
|
||||
channel_info.h.clients: "Clients"
|
||||
channel_info.h.clients: "Current clients"
|
||||
channel_info.send: "Send a message"
|
||||
channel_info.send.message: "Message"
|
||||
channel_info.client: "Client"
|
||||
channel_info.files.delete: "Delete"
|
||||
|
||||
channel_info.files.h.path: "Path"
|
||||
channel_info.files.h.type: "Type"
|
||||
channel_info.files.h.size: "Size"
|
||||
channel_info.files.h.datetime: "Datetime"
|
||||
channel_info.files.h.delete: "Delete"
|
||||
channel_info.files.delete.success: "Deleted %file%."
|
||||
# groups
|
||||
groups.delete: "Delete"
|
||||
groups.h.servergroups: "Server Groups"
|
||||
groups.h.channelgroups: "Channel Groups"
|
||||
groups.servergroup: "Server Group"
|
||||
groups.channelgroup: "Channel Group"
|
||||
|
||||
# groups create/copy
|
||||
groups.create: "Create or copy"
|
||||
|
@ -262,6 +271,9 @@ snapshots.create: "Create a new snapshot"
|
|||
snapshots.h.details: "Details"
|
||||
snapshots.deploy: "Deploy"
|
||||
snapshots.delete: "Delete"
|
||||
snapshots.error.create: "An error occurred when creating a snapshot. Please view the application log."
|
||||
snapshots.error.delete: "An error occurred when deleting a snapshot. Please view the application log."
|
||||
snapshots.error.deploy: "An error occurred when deploying a snapshot. Please view the application log."
|
||||
|
||||
# passwords
|
||||
passwords.h.actions: "Actions"
|
||||
|
@ -272,10 +284,4 @@ passwords.add.duration: "Duration (in seconds)"
|
|||
passwords.add.description: "Description"
|
||||
passwords.add.channel: "Channel (user joins)"
|
||||
passwords.add.channel_password: "Channelpassword"
|
||||
|
||||
# about
|
||||
about.header: "About %name%"
|
||||
about.body: |
|
||||
ts3web is a web interface for one TeamSpeak 3 Server. It's using serverquery to login. This web interface aims to be as simple as possible. The minimalistic approach is intentional.
|
||||
|
||||
Feel free to submit pull requests if you like to help. More information are here: https://hub.docker.com/r/varakh/ts3web
|
||||
passwords.channel: "Channel"
|
|
@ -19,7 +19,8 @@ RUN mkdir -p /var/www/html/application/bin/ \
|
|||
&& chmod -R 777 /var/www/html/application/data/ \
|
||||
&& mkdir -p /var/www/html/application/log/ \
|
||||
&& touch /var/www/html/application/log/application.log \
|
||||
&& chmod 777 /var/www/html/application/log/application.log
|
||||
&& chmod 777 /var/www/html/application/log/application.log \
|
||||
&& chown -R www-data:www-data /var/www/html/application
|
||||
|
||||
# initialize app
|
||||
RUN cd /var/www/html/application/ \
|
||||
|
|
|
@ -10,8 +10,13 @@ error_reporting(E_ALL);
|
|||
*/
|
||||
|
||||
use Carbon\Carbon;
|
||||
use JeremyKendall\Slim\Auth\ServiceProvider\SlimAuthProvider;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Slim\Middleware\Session;
|
||||
use Slim\Views\Twig;
|
||||
use Slim\Views\TwigExtension;
|
||||
use SlimSession\Helper;
|
||||
|
||||
// To help the built-in PHP dev server, check if the request was actually for
|
||||
// something which should probably be served as a static file
|
||||
|
@ -77,17 +82,17 @@ $container['authAdapter'] = function ($container) {
|
|||
$container['acl'] = function () {
|
||||
return new ACL();
|
||||
};
|
||||
$container->register(new \JeremyKendall\Slim\Auth\ServiceProvider\SlimAuthProvider());
|
||||
$container->register(new SlimAuthProvider());
|
||||
$app->add($app->getContainer()->get('slimAuthRedirectMiddleware'));
|
||||
|
||||
// session
|
||||
$app->add(new \Slim\Middleware\Session([
|
||||
$app->add(new Session([
|
||||
'name' => 'dummy_session',
|
||||
'autorefresh' => true,
|
||||
'lifetime' => '1 hour'
|
||||
]));
|
||||
$container['session'] = function () {
|
||||
return new \SlimSession\Helper;
|
||||
return new Helper;
|
||||
};
|
||||
|
||||
// view
|
||||
|
@ -103,8 +108,8 @@ $container['view'] = function ($container) use ($app) {
|
|||
$themeCacheDir = false;
|
||||
}
|
||||
|
||||
$view = new \Slim\Views\Twig($themeDir, ['cache' => $themeCacheDir]);
|
||||
$view->addExtension(new \Slim\Views\TwigExtension(
|
||||
$view = new Twig($themeDir, ['cache' => $themeCacheDir]);
|
||||
$view->addExtension(new TwigExtension(
|
||||
$container['router'],
|
||||
$container['request']->getUri()
|
||||
));
|
||||
|
|
27
src/Control/Actions/ChannelFilesDeleteAction.php
Normal file
27
src/Control/Actions/ChannelFilesDeleteAction.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
final class ChannelFilesDeleteAction extends AbstractAction
|
||||
{
|
||||
public function __invoke(Request $request, Response $response, $args)
|
||||
{
|
||||
$sid = $args['sid'];
|
||||
$cid = $args['cid'];
|
||||
$file = null;
|
||||
|
||||
if (array_key_exists('file', $request->getQueryParams())) {
|
||||
$file = urldecode($request->getQueryParams()['file']);
|
||||
}
|
||||
|
||||
$this->ts->login($this->auth->getIdentity()['user'], $this->auth->getIdentity()['password']);
|
||||
$this->ts->getInstance()->selectServer($sid, 'serverId');
|
||||
|
||||
$files = [$file];
|
||||
$this->ts->getInstance()->ftDeleteFile($cid, '', $files);
|
||||
$this->flash->addMessage('success', $this->translator->trans('channel_info.files.delete.success', ['%file%' => $file]));
|
||||
|
||||
return $response->withRedirect('/channels/' . $sid . '/' . $cid);
|
||||
}
|
||||
}
|
|
@ -45,12 +45,12 @@ final class ChannelInfoAction extends AbstractAction
|
|||
if (!empty($foundFiles)) {
|
||||
foreach ($foundFiles as $file) {
|
||||
|
||||
if ($file['type'] !== "0") {
|
||||
if ($file['type'] !== "0") { // a file
|
||||
$file['path'] = $path;
|
||||
$files[] = $file;
|
||||
}
|
||||
|
||||
if ($file['type'] === "0") {
|
||||
if ($file['type'] === "0") { // a directory
|
||||
|
||||
if ($path === '/') {
|
||||
$newPath = $path . $file['name'];
|
||||
|
@ -58,6 +58,9 @@ final class ChannelInfoAction extends AbstractAction
|
|||
$newPath = $path . '/' . $file['name'];
|
||||
}
|
||||
|
||||
$file['path'] = $path;
|
||||
$files[] = $file;
|
||||
|
||||
$files = $this->getAllFilesIn($sid, $cid, $newPath, $files);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,17 +11,21 @@ final class LogsAction extends AbstractAction
|
|||
if (array_key_exists('sid', $args)) $sid = $args['sid'];
|
||||
|
||||
$this->ts->login($this->auth->getIdentity()['user'], $this->auth->getIdentity()['password']);
|
||||
|
||||
$appLog = [];
|
||||
if (empty($sid)) {
|
||||
$dataResult = $this->ts->getInstance()->logView(100, 1, 1);
|
||||
$dataResult = $this->ts->getInstance()->logView(getenv(EnvConstants::TEAMSPEAK_LOG_LINES), 1, 1);
|
||||
$appLog = explode("\n", file_get_contents(BootstrapHelper::getLogFile()));
|
||||
} else {
|
||||
$selectResult = $this->ts->getInstance()->selectServer($sid, 'serverId');
|
||||
$dataResult = $this->ts->getInstance()->logView(100, 1, 0);
|
||||
$dataResult = $this->ts->getInstance()->logView(getenv(EnvConstants::TEAMSPEAK_LOG_LINES), 1, 0);
|
||||
}
|
||||
|
||||
// render GET
|
||||
$this->view->render($response, 'logs.twig', [
|
||||
'title' => $this->translator->trans('logs.title'),
|
||||
'data' => $this->ts->getInstance()->getElement('data', $dataResult),
|
||||
'title' => empty($sid) ? $this->translator->trans('instance_logs.title') : $this->translator->trans('server_logs.title'),
|
||||
'log' => $this->ts->getInstance()->getElement('data', $dataResult),
|
||||
'appLog' => $appLog,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
use Carbon\Carbon;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
final class SnapshotCreateAction extends AbstractAction
|
||||
|
@ -23,8 +24,13 @@ final class SnapshotCreateAction extends AbstractAction
|
|||
if ($fileSystem->exists($path)) {
|
||||
$this->flash->addMessage('error', $this->translator->trans('file.exists'));
|
||||
} else {
|
||||
try {
|
||||
$fileSystem->appendToFile($path, trim($snapshotCreateResult['data']));
|
||||
$this->flash->addMessage('success', $this->translator->trans('done'));
|
||||
} catch (IOException $e) {
|
||||
$this->logger->error('Could not write to ' . $path . '. Cause: ' . $e->getMessage());
|
||||
$this->flash->addMessage('error', $this->translator->trans('snapshots.error.create'));
|
||||
}
|
||||
}
|
||||
|
||||
return $response->withRedirect('/snapshots/' . $sid);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use Carbon\Carbon;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
final class SnapshotDeleteAction extends AbstractAction
|
||||
|
@ -21,8 +22,8 @@ final class SnapshotDeleteAction extends AbstractAction
|
|||
if (!$fileSystem->exists($path)) {
|
||||
$this->flash->addMessage('error', $this->translator->trans('file.notexists'));
|
||||
} else {
|
||||
try {
|
||||
$fileSystem->remove($path);
|
||||
|
||||
$serverPath = FileHelper::SNAPSHOTS_PATH . DIRECTORY_SEPARATOR . $sid;
|
||||
|
||||
if (count(FileHelper::getFiles($serverPath)) == 0) {
|
||||
|
@ -30,6 +31,10 @@ final class SnapshotDeleteAction extends AbstractAction
|
|||
}
|
||||
|
||||
$this->flash->addMessage('success', $this->translator->trans('done'));
|
||||
} catch (IOException $e) {
|
||||
$this->logger->error('Could not delete ' . $path . '. Cause: ' . $e->getMessage());
|
||||
$this->flash->addMessage('error', $this->translator->trans('snapshots.error.delete'));
|
||||
}
|
||||
}
|
||||
|
||||
return $response->withRedirect('/snapshots/' . $sid);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<?php
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
@ -21,9 +20,14 @@ final class SnapshotDeployAction extends AbstractAction
|
|||
if (!$fileSystem->exists($path)) {
|
||||
$this->flash->addMessage('error', $this->translator->trans('file.notexists'));
|
||||
} else {
|
||||
try {
|
||||
$snapshotData = file_get_contents($path);
|
||||
$this->ts->getInstance()->serverSnapshotDeploy($snapshotData, true);
|
||||
$this->flash->addMessage('success', $this->translator->trans('done'));
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error('Could not deploy ' . $path . '. Cause: ' . $e->getMessage());
|
||||
$this->flash->addMessage('error', $this->translator->trans('snapshots.error.deploy'));
|
||||
}
|
||||
}
|
||||
|
||||
return $response->withRedirect('/snapshots/' . $sid);
|
||||
|
|
|
@ -45,7 +45,7 @@ class TSAuthAdapter extends \Zend\Authentication\Adapter\AbstractAdapter
|
|||
$password = $this->getCredential();
|
||||
|
||||
if ($this->ts->login($user, $password)) {
|
||||
$this->logger->info(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];
|
||||
return new Result(Result::SUCCESS, $user, array());
|
||||
|
|
|
@ -5,6 +5,8 @@ use Monolog\Handler\ErrorLogHandler;
|
|||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Processor\UidProcessor;
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\Translation\Loader\YamlFileLoader;
|
||||
use Symfony\Component\Translation\MessageSelector;
|
||||
use Symfony\Component\Translation\Translator;
|
||||
|
@ -34,6 +36,7 @@ class BootstrapHelper
|
|||
EnvConstants::TEAMSPEAK_HOST,
|
||||
EnvConstants::TEAMSPEAK_QUERY_PORT,
|
||||
EnvConstants::TEAMSPEAK_USER,
|
||||
EnvConstants::TEAMSPEAK_LOG_LINES,
|
||||
EnvConstants::LOG_NAME,
|
||||
EnvConstants::LOG_LEVEL
|
||||
]);
|
||||
|
@ -109,9 +112,43 @@ class BootstrapHelper
|
|||
$logger->pushProcessor(new UidProcessor());
|
||||
$logger->pushHandler(new ErrorLogHandler(NULL, $logLevelTranslated));
|
||||
|
||||
$logPath = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'log' . DIRECTORY_SEPARATOR . 'application.log';
|
||||
$logger->pushHandler(new StreamHandler($logPath, $logLevelTranslated));
|
||||
$dir = self::getLogDir();
|
||||
$path = self::getLogFile();
|
||||
|
||||
try {
|
||||
$fileSystem = new Filesystem();
|
||||
|
||||
if (!$fileSystem->exists($dir)) {
|
||||
$fileSystem->mkdir($dir);
|
||||
}
|
||||
|
||||
if (!$fileSystem->exists($path)) {
|
||||
$fileSystem->touch($path);
|
||||
}
|
||||
} catch (IOException $e) {
|
||||
die('Could not create logger. Cause: ' . $e->getMessage() . '. Trace: ' . $e->getTraceAsString());
|
||||
}
|
||||
|
||||
$logger->pushHandler(new StreamHandler($path, $logLevelTranslated));
|
||||
|
||||
return $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns log dir
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getLogDir() {
|
||||
return __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'log';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns log file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getLogFile() {
|
||||
return self::getLogDir() . DIRECTORY_SEPARATOR . 'application.log';
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
<div class="modal" id="aboutModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">{% trans with {'%name%': getenv('site_title') } %}about.header{% endtrans %}</h4>
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
{{ 'about.body'|trans|nl2br }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -8,6 +8,10 @@
|
|||
{'key': 'duration', 'apply': 'timeInSeconds'},
|
||||
{'key': 'created', 'apply': 'timestamp'}
|
||||
],
|
||||
'hiddenColumns': ['banid', 'name', 'invokercldbid', 'invokeruid'],
|
||||
'links': [
|
||||
{'key': 'invokername', 'uri': '/clients/' ~ sid, 'uri_param': 'invokercldbid'},
|
||||
],
|
||||
'additional_links': [
|
||||
{
|
||||
'header_label': 'bans.delete'|trans,
|
||||
|
|
|
@ -39,13 +39,9 @@
|
|||
|
||||
{% if files|length > 0 %}
|
||||
<h5>{% trans %}channel_info.h.files{% endtrans %}</h5>
|
||||
{% include 'table.twig' with {'data': files,
|
||||
'filters': [
|
||||
{'key': 'datetime', 'apply': 'timestamp'},
|
||||
{'key': 'size', 'apply': 'file'},
|
||||
],
|
||||
'hiddenColumns': ['cid', 'type']
|
||||
} %}
|
||||
{% if files|length > 0 %}
|
||||
{% include 'files.twig' with {'data': files} %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<h5>{% trans %}channel_info.h.details{% endtrans %}</h5>
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
{% if channels|length > 0 %}
|
||||
<h5>{% trans %}channels.h.details{% endtrans %}</h5>
|
||||
{% include 'table.twig' with {'data': channels,
|
||||
'links': [{'key': 'cid', 'uri': '/channels/' ~ sid}],
|
||||
'links': [{'key': 'channel_name', 'uri': '/channels/' ~ sid, 'uri_param': 'cid'}, {'key': 'pid', 'uri': '/channels/' ~ sid, 'uri_param': 'pid'}],
|
||||
'hiddenColumns': ['cid'],
|
||||
'additional_links': [
|
||||
{
|
||||
'header_label': 'channels.delete'|trans,
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
{'key': 'client_lastconnected', 'apply': 'timestamp'},
|
||||
],
|
||||
'links': [
|
||||
{'key': 'client_unique_identifier', 'uri': '/clients/' ~ sid, 'uri_param': 'cldbid'},
|
||||
{'key': 'client_nickname', 'uri': '/clients/' ~ sid, 'uri_param': 'cldbid'},
|
||||
],
|
||||
'hiddenColumns': ['cldbid'],
|
||||
'hiddenColumns': ['cldbid', 'client_unique_identifier'],
|
||||
'additional_links': [
|
||||
{
|
||||
'header_label': 'clients.delete'|trans,
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
'filters': [
|
||||
{'key': 'timestamp', 'apply': 'timestamp'},
|
||||
],
|
||||
'hiddenColumns': ['tcldbid', 'fcldbid'],
|
||||
'links': [
|
||||
{'key': 'tname', 'uri': '/clients/' ~ sid, 'uri_param': 'tcldbid'},
|
||||
{'key': 'fname', 'uri': '/clients/' ~ sid, 'uri_param': 'fcldbid'}
|
||||
],
|
||||
'additional_links': [
|
||||
{
|
||||
'header_label': 'complains.delete'|trans,
|
||||
|
|
42
src/View/bootstrap4/files.twig
Normal file
42
src/View/bootstrap4/files.twig
Normal file
|
@ -0,0 +1,42 @@
|
|||
<div class="table-responsive">
|
||||
<table class="table table-sm small sortable-bootstrap table-striped table-bordered" data-sortable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{% trans %}channel_info.files.h.type{% endtrans %}</th>
|
||||
<th scope="col">{% trans %}channel_info.files.h.path{% endtrans %}</th>
|
||||
<th scope="col">{% trans %}channel_info.files.h.size{% endtrans %}</th>
|
||||
<th scope="col">{% trans %}channel_info.files.h.datetime{% endtrans %}</th>
|
||||
<th scope="col">{% trans %}channel_info.files.h.delete{% endtrans %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{% set total = 0 %}
|
||||
{% for file in data %}
|
||||
{% set total = total + file.size %}
|
||||
{% if file.path is empty %}
|
||||
{% set path = file.name %}
|
||||
{% elseif file.path == '/' %}
|
||||
{% set path = '/' ~ file.name %}
|
||||
{% else %}
|
||||
{% set path = file.path ~ '/' ~ file.name %}
|
||||
{% endif %}
|
||||
|
||||
<tr>
|
||||
<td>{{ file.type == 0 ? '<i class="fa fa-folder"></i>' : '<i class="fa fa-file-o"></i>' }}</td>
|
||||
<td>{{ path }}</td>
|
||||
<td>{{ file.size == 0 ? '' : file.size|file }}</td>
|
||||
<td>{{ file.datetime|timestamp }}</td>
|
||||
<td><a href="/channels/files/delete/{{ sid }}/{{ cid }}?file={{ path }}"><i class="fa fa-trash"></i></a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="font-weight-bold">{{ total|file }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
|
@ -24,9 +24,14 @@
|
|||
|
||||
{% if serverGroups|length > 0 %}
|
||||
{% include 'table.twig' with {'data': serverGroups,
|
||||
'links': [{'key': 'sgid', 'uri': '/servergroups/' ~ sid}],
|
||||
'hiddenColumns': ['savedb', 'sortid', 'namemode', 'n_modifyp', 'n_member_addp', 'n_member_removep'],
|
||||
'hiddenColumns': ['sgid', 'savedb', 'sortid', 'namemode', 'n_modifyp', 'n_member_addp', 'n_member_removep'],
|
||||
'additional_links': [
|
||||
{
|
||||
'header_label': 'groups.servergroup'|trans,
|
||||
'label': '<i class="fa fa-info"></i>',
|
||||
'uri': '/servergroups/' ~ sid,
|
||||
'uri_param': 'sgid'
|
||||
},
|
||||
{
|
||||
'header_label': 'groups.delete'|trans,
|
||||
'label': '<i class="fa fa-trash"></i>',
|
||||
|
@ -70,9 +75,14 @@
|
|||
|
||||
{% if channelGroups|length > 0 %}
|
||||
{% include 'table.twig' with {'data': channelGroups,
|
||||
'links': [{'key': 'cgid', 'uri': '/channelgroups/' ~ sid}],
|
||||
'hiddenColumns': ['savedb', 'sortid', 'namemode', 'n_modifyp', 'n_member_addp', 'n_member_removep'],
|
||||
'hiddenColumns': ['cgid', 'savedb', 'sortid', 'namemode', 'n_modifyp', 'n_member_addp', 'n_member_removep'],
|
||||
'additional_links': [
|
||||
{
|
||||
'header_label': 'groups.channelgroup'|trans,
|
||||
'label': '<i class="fa fa-info"></i>',
|
||||
'uri': '/channelgroups/' ~ sid,
|
||||
'uri_param': 'cgid'
|
||||
},
|
||||
{
|
||||
'header_label': 'channelgroups.delete'|trans,
|
||||
'label': '<i class="fa fa-trash"></i>',
|
||||
|
|
|
@ -58,13 +58,10 @@
|
|||
</main><!-- /.container -->
|
||||
|
||||
<!-- Footer -->
|
||||
{% include 'about.twig' %}
|
||||
|
||||
<footer class="footer">
|
||||
<div class="text-center">
|
||||
<span class="text-muted">
|
||||
<i class="fa fa-copyright"></i> {{ "now"|date("Y") }} {{ getenv('site_title') }} |
|
||||
<a data-toggle="modal" data-target="#aboutModal"><i class="fa fa-question"></i></a>
|
||||
<i class="fa fa-copyright"></i> {{ "now"|date("Y") }} {{ getenv('site_title') }}
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
<div class="container-fluid h-100">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">{{ title }}</div>
|
||||
<div class="card-body">
|
||||
|
@ -12,25 +12,25 @@
|
|||
<form class="form-signin" role="form" name="login" id="login" method="post">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="host" class="col-4 col-form-label">{% trans %}login.form.host{% endtrans %}</label>
|
||||
<input type="text" id="host" name="host" class="form-control form-control-lg"
|
||||
<label for="host" class="sr-only">{% trans %}login.form.host{% endtrans %}</label>
|
||||
<input type="text" id="host" name="host" class="form-control form-control-sm"
|
||||
placeholder="{{ getenv('teamspeak_host') ~ ':' ~ getenv('teamspeak_query_port') }}"
|
||||
value="{{ getenv('teamspeak_host') ~ ':' ~ getenv('teamspeak_query_port') }}"
|
||||
required disabled>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="username" class="col-4 col-form-label">{% trans %}login.form.username{% endtrans %}</label>
|
||||
<input type="text" id="username" name="username" class="form-control form-control-lg"
|
||||
<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"
|
||||
placeholder="{{ getenv('teamspeak_user') }}"
|
||||
value="{{ getenv('teamspeak_user') }}"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password" class="col-4 col-form-label">{% 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"
|
||||
class="form-control form-control-lg"
|
||||
class="form-control form-control-sm"
|
||||
placeholder="{% trans %}login.form.password.placeholder{% endtrans %}" autofocus required>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
{% if log|length > 0 %}
|
||||
|
||||
<div class="small">
|
||||
{% for log in data %}
|
||||
<code>{{ log.l }}</code>
|
||||
{% for line in log %}
|
||||
<code>{{ line.l }}</code><br/>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
@ -15,4 +15,13 @@
|
|||
{% include 'no_entities.twig' %}
|
||||
{% endif %}
|
||||
|
||||
{% if appLog|length > 0 %}
|
||||
<br />
|
||||
<h3>{% trans %}app_log.title{% endtrans %}</h3>
|
||||
<div class="small">
|
||||
{% for line in appLog %}
|
||||
<code>{{ line }}</code><br/>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -27,10 +27,15 @@
|
|||
{% if data|length > 0 %}
|
||||
<h5>{% trans %}passwords.h.details{% endtrans %}</h5>
|
||||
{% include 'table.twig' with {'data': data,
|
||||
'hiddenColumns': ['uid', 'tcpw'],
|
||||
'hiddenColumns': ['tcid', 'uid', 'tcpw'],
|
||||
'filters': [{'key': 'start', 'apply': 'timestamp'}, {'key': 'end', 'apply': 'timestamp'}],
|
||||
'links': [
|
||||
{'key': 'tcid', 'uri': '/channels/' ~ sid, 'uri_param': 'tcid'},
|
||||
'additional_links': [
|
||||
{
|
||||
'header_label': 'passwords.channel'|trans,
|
||||
'label': '<i class="fa fa-tv"></i>',
|
||||
'uri': '/channels/' ~ sid,
|
||||
'uri_param': 'tcid'
|
||||
}
|
||||
],
|
||||
'attributesEditable': [
|
||||
{
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
'links': [
|
||||
{'key': 'client_nickname', 'uri': '/clients/' ~ sid, 'uri_param': 'cldbid'}
|
||||
],
|
||||
'hiddenColumns': ['cldbid'],
|
||||
'hiddenColumns': ['cldbid', 'client_unique_identifier'],
|
||||
'additional_links': [
|
||||
{
|
||||
'header_label': 'servergroup_info.remove'|trans,
|
||||
|
|
Reference in a new issue