Archived
1
0
Fork 0

Add tokens

This commit is contained in:
Varakh 2018-04-05 21:20:29 +02:00
parent c05b1e8d06
commit eacd690b18
20 changed files with 381 additions and 83 deletions

View file

@ -94,6 +94,22 @@ location ~ \.php$ {
* start server with `php -S localhost:8080 -t public public/index.php`
* point browser to [localhost:8080](http://localhost:8080) to have a preview
### 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.
```
hiddenDependingOnAttribute // hides a row depending on a value in a table
hiddenColumns // hides an entire column depending on a key in a table
links // generates a link for a specific cell in a table or keyvalue
additional_links // generates extra columns in a table
filters // applies filters depending on a key in a table or key value view
attributesEditable // define editable attributes in the key value view
fields // define fields for a form
```
See example usage in the folder `View/material`.
## Translations ##
- This app uses Symfony Translator. It's bootstrapped in `Util\BootstrapHelper` and locales are placed under `data/locale/`. 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`.

View file

@ -41,6 +41,10 @@ class ACL extends \Zend\Permissions\Acl\Acl
'/servers/send/{sid}',
'/servers/edit/{sid}',
'/tokens/{sid}',
'/tokens/add/{sid}',
'/tokens/delete/{sid}/{token}',
'/online/{sid}',
'/online/{sid}/{clid}',
'/online/poke/{sid}/{clid}',

View file

@ -263,3 +263,19 @@ $container[ChannelSendAction::class] = function ($container) {
return new ChannelSendAction($container);
};
$app->post('/channels/send/{sid}/{cid}', ChannelSendAction::class);
// tokens
$container[TokensAction::class] = function ($container) {
return new TokensAction($container);
};
$app->get('/tokens/{sid}', TokensAction::class);
$container[TokenAddAction::class] = function ($container) {
return new TokenAddAction($container);
};
$app->post('/tokens/add/{sid}', TokenAddAction::class);
$container[TokenDeleteAction::class] = function ($container) {
return new TokenDeleteAction($container);
};
$app->get('/tokens/delete/{sid}/{token}', TokenDeleteAction::class);

View file

@ -63,6 +63,7 @@ menu.servers.clients: "Clients"
menu.servers.groups: "Groups"
menu.servers.bans: "Bans"
menu.servers.complains: "Complains"
menu.servers.tokens: "Tokens"
menu.servers.logs: "Log"
# titles
@ -81,6 +82,7 @@ complains.title: "Complains"
groups.title: "Groups"
group_info.title: "Group Info"
profile.title: "Profile"
tokens.title: "Tokens"
# dynamic render of key value pairs
key: "Attribute"
@ -202,4 +204,17 @@ online.send: "Send a message"
online.send.message: "Message"
online.poked.success: "Poked client %clid%."
online.kicked.success: "Kicked client %clid%."
online.banned.success: "Banned client %clid%."
online.banned.success: "Banned client %clid%."
# tokens
tokens.delete: "Delete"
tokens.h.details: "Details"
tokens.h.add: "Add"
tokens.add: "Add a token"
tokens.add.tokentype: "Type"
tokens.add.serverGroup: "Servergroup (type SERVER has to be selected)"
tokens.add.channelGroup: "Channelgroup (type CHANNEL has to be selected)"
tokens.add.channel: "Channel (type CHANNEL has to be selected)"
tokens.add.description: "Description"
tokens.type.servergroup: "Server: "
tokens.type.channelgroup: "Channel: "

View file

@ -110,34 +110,15 @@ $container['view'] = function ($container) use ($app) {
));
$view->addExtension(new Twig_Extension_Debug());
// file size
$fileSizeFilter = new Twig_SimpleFilter('file', function($bytes, $decimals = 2) {
$sz = 'BKMGTP';
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
});
$view->getEnvironment()->addFilter($fileSizeFilter);
// time in seconds to human readable
$timeInSecondsFilter = new Twig_SimpleFilter('timeInSeconds', function($seconds) use ($container) {
return $container['ts']->getInstance()->convertSecondsToStrTime($seconds);
});
$view->getEnvironment()->addFilter($timeInSecondsFilter);
$timeInMillisFilter = new Twig_SimpleFilter('timeInMillis', function($millis) use ($container) {
return $container['ts']->getInstance()->convertSecondsToStrTime(floor($millis/1000));
});
$view->getEnvironment()->addFilter($timeInMillisFilter);
// timestamp to carbon
$timestampFilter = new Twig_SimpleFilter('timestamp', function($timestamp) {
return Carbon::createFromTimestamp($timestamp);
});
$view->getEnvironment()->addFilter($timestampFilter);
// dynamically apply filters
$view->getEnvironment()->addExtension(new ApplyFilterExtension());
// encode url
$encodeUrl = new Twig_SimpleFilter('escape_url', function($str) {
return urlencode($str);
});
$view->getEnvironment()->addFilter($encodeUrl);
// translation
$view->addExtension(new \Symfony\Bridge\Twig\Extension\TranslationExtension($container['translator']));
$view->getEnvironment()->getExtension('Twig_Extension_Core')->setDateFormat(getenv('site_date_format'));
@ -158,6 +139,55 @@ $container['view'] = function ($container) use ($app) {
return $container['session']->get($key);
}));
// ts specific: file size
$fileSizeFilter = new Twig_SimpleFilter('file', function($bytes, $decimals = 2) {
$sz = 'BKMGTP';
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
});
$view->getEnvironment()->addFilter($fileSizeFilter);
// ts specific: time in seconds to human readable
$timeInSecondsFilter = new Twig_SimpleFilter('timeInSeconds', function($seconds) use ($container) {
return $container['ts']->getInstance()->convertSecondsToStrTime($seconds);
});
$view->getEnvironment()->addFilter($timeInSecondsFilter);
$timeInMillisFilter = new Twig_SimpleFilter('timeInMillis', function($millis) use ($container) {
return $container['ts']->getInstance()->convertSecondsToStrTime(floor($millis/1000));
});
$view->getEnvironment()->addFilter($timeInMillisFilter);
// ts specific: timestamp to carbon
$timestampFilter = new Twig_SimpleFilter('timestamp', function($timestamp) {
return Carbon::createFromTimestamp($timestamp);
});
$view->getEnvironment()->addFilter($timestampFilter);
// ts specific: token type
$tokenTypeFilter = new Twig_SimpleFilter('tokentype', function($type) {
$tokenTypes = TSInstance::getTokenTypes();
foreach ($tokenTypes as $name => $tokenType) {
if ($type == $tokenType) return $name;
}
return $type;
});
$view->getEnvironment()->addFilter($tokenTypeFilter);
// ts specific: group type
$groupTypeFilter = new Twig_SimpleFilter('permgrouptype', function($type) {
$groupTypes = TSInstance::getPermGroupTypes();
foreach ($groupTypes as $name => $groupType) {
if ($type == $groupType) return $name;
}
return $type;
});
$view->getEnvironment()->addFilter($groupTypeFilter);
// flash
$view['flash'] = $container['flash'];

View file

@ -0,0 +1,36 @@
<?php
use Slim\Http\Request;
use Slim\Http\Response;
final class TokenAddAction extends AbstractAction
{
public function __invoke(Request $request, Response $response, $args)
{
$sid = $args['sid'];
$body = $request->getParsedBody();
$type = $body['tokentype'];
$serverGroup = $body['serverGroup'];
$channelGroup = $body['channelGroup'];
$channel = $body['channel'];
$description = $body['description'];
$this->logger->debug('Body', $body);
$this->ts->login($this->auth->getIdentity()['user'], $this->auth->getIdentity()['password']);
$selectResult = $this->ts->getInstance()->selectServer($sid, 'serverId');
$tokenAddResult = $this->ts->getInstance()->tokenAdd(
$type,
($type == ts3admin::TokenServerGroup ? $serverGroup : $channelGroup),
$channel,
$description
);
$this->flash->addMessage('success', $this->translator->trans('added'));
return $response->withRedirect('/tokens/' . $sid);
}
}

View file

@ -0,0 +1,22 @@
<?php
use Slim\Http\Request;
use Slim\Http\Response;
final class TokenDeleteAction extends AbstractAction
{
public function __invoke(Request $request, Response $response, $args)
{
$sid = $args['sid'];
$token = rawurldecode($args['token']);
$this->ts->login($this->auth->getIdentity()['user'], $this->auth->getIdentity()['password']);
$selectResult = $this->ts->getInstance()->selectServer($sid, 'serverId');
$tokenDeleteResult = $this->ts->getInstance()->tokenDelete($token);
$this->flash->addMessage('success', $this->translator->trans('done'));
return $response->withRedirect('/tokens/' . $sid);
}
}

View file

@ -0,0 +1,56 @@
<?php
use Slim\Http\Request;
use Slim\Http\Response;
final class TokensAction extends AbstractAction
{
public function __invoke(Request $request, Response $response, $args)
{
$sid = $args['sid'];
$this->ts->login($this->auth->getIdentity()['user'], $this->auth->getIdentity()['password']);
$selectResult = $this->ts->getInstance()->selectServer($sid, 'serverId');
$dataResult = $this->ts->getInstance()->tokenList();
// channels
$channelsResult = $this->ts->getInstance()->channelList();
$channelsResult = $this->ts->getInstance()->getElement('data', $channelsResult);
$channels = [];
foreach ($channelsResult as $channel) {
$channels[$channel['channel_name']] = $channel['cid'];
}
// groups
$serverGroups = [];
$serverGroupsResult = $this->ts->getInstance()->serverGroupList();
$serverGroupsResult = $this->ts->getInstance()->getElement('data', $serverGroupsResult);
foreach ($serverGroupsResult as $group) {
$serverGroups[$group['name']] = $group['sgid'];
}
arsort($serverGroups);
$channelGroups = [];
$channelGroupsResult = $this->ts->getInstance()->channelGroupList();
$channelGroupsResult = $this->ts->getInstance()->getElement('data', $channelGroupsResult);
foreach ($channelGroupsResult as $group) {
$channelGroups[$group['name']] = $group['cgid'];
}
arsort($channelGroups);
// render GET
$this->view->render($response, 'tokens.twig', [
'title' => $this->translator->trans('tokens.title'),
'data' => $this->ts->getInstance()->getElement('data', $dataResult),
'tokentypes' => TSInstance::getTokenTypes(),
'channels' => $channels,
'serverGroups' => $serverGroups,
'channelGroups' => $channelGroups,
'sid' => $sid
]);
}
}

View file

@ -178,4 +178,31 @@ class TSInstance
return $arr;
}
/**
* @return array
*/
public static function getTokenTypes()
{
$arr = [];
$arr['TokenServerGroup'] = ts3admin::TokenServerGroup;
$arr['TokenChannelGroup'] = ts3admin::TokenChannelGroup;
return $arr;
}
/**
* @return array
*/
public static function getPermGroupTypes()
{
$arr = [];
$arr['PermGroupTypeServerGroup'] = ts3admin::PermGroupTypeServerGroup;
$arr['PermGroupTypeGlobalClient'] = ts3admin::PermGroupTypeGlobalClient;
$arr['PermGroupTypeChannel'] = ts3admin::PermGroupTypeChannel;
$arr['PermGroupTypeChannelGroup'] = ts3admin::PermGroupTypeChannelGroup;
$arr['PermGroupTypeChannelClient'] = ts3admin::PermGroupTypeChannelClient;
return $arr;
}
}

View file

@ -5,7 +5,7 @@
<h2 class="header">{% trans %}channel_info.h.actions{% endtrans %}</h2>
{% include 'form.twig' with {
'additional_forms': [
'fields': [
{
'header_label': 'channel_info.send'|trans,
'label': '<i class="material-icons">check_circle</i>',
@ -20,7 +20,7 @@
{% if clients|length > 0 %}
<h2 class="header">{% trans %}channel_info.h.clients{% endtrans %}</h2>
{% include 'table.twig' with {'data': clients,
'hide': [{'key': 'client_type', 'values': ['1']}],
'hiddenDependingOnAttribute': [{'key': 'client_type', 'values': ['1']}],
'links': [
{'key': 'clid', 'uri': '/online/' ~ sid},
{'key': 'client_database_id', 'uri': '/clients/' ~ sid, 'uri_param': 'client_database_id'}

View file

@ -4,7 +4,7 @@
<h1 class="header">{{ title }}</h1>
<h2 class="header">{% trans %}client_info.h.actions{% endtrans %}</h2>
{% include 'form.twig' with {
'additional_forms': [
'fields': [
{
'header_label': 'client_info.ban'|trans,
'label': '<i class="material-icons">check_circle</i>',

View file

@ -1,4 +1,4 @@
{% for form in additional_forms %}
{% for form in fields %}
<h3 class="header">{{ form.header_label }}</h3>
{% set item = "#{form.uri}" %}
@ -16,12 +16,23 @@
{% for field in form.fields %}
<div class="form-group">
<label class="col-md-2 control-label" for="{{ field.key }}">{{ field.label }}</label>
<div class="col-md-10">
<input
class="form-control"
name="{{ field.key }}"
id="{{ field.key }}"
type="{{ field.type }}"/>
{% if field.type == 'select' %}
<select class="form-control" name="{{ field.key }}" id="{{ field.key }}">
{% for k,v in field.options %}
<option class="form-control" value="{{ v }}">{{ k }}</option>
{% endfor %}
</select>
{% else %}
<input
class="form-control"
name="{{ field.key }}"
id="{{ field.key }}"
type="{{ field.type }}"/>
{% endif %}
</div>
</div>
{% endfor %}

View file

@ -5,7 +5,7 @@
<h2 class="header">{% trans %}group_info.h.clients_add{% endtrans %}</h2>
{% include 'form.twig' with {
'additional_forms': [
'fields': [
{
'header_label': 'group_info.add'|trans,
'label': '<i class="material-icons">check_circle</i>',

View file

@ -37,6 +37,9 @@
<a href="/complains/{{ session_get('sid') }}">
<li class="active withripple" data-target="#page">{% trans %}menu.servers.complains{% endtrans %}</li>
</a>
<a href="/tokens/{{ session_get('sid') }}">
<li class="active withripple" data-target="#page">{% trans %}menu.servers.tokens{% endtrans %}</li>
</a>
<a href="/logs/{{ session_get('sid') }}">
<li class="active withripple" data-target="#page">{% trans %}menu.servers.logs{% endtrans %}</li>
</a>

View file

@ -10,7 +10,8 @@
<div class="col-md-8">
{% if data|length >0 %}
{% include 'table.twig' with {'data': data,
'hide': [{'key': 'client_type', 'values': ['1']}],
'hiddenDependingOnAttribute': [{'key': 'client_type', 'values': ['1']}],
'hiddenColumns': ['client_type'],
'filters': [
{'key': 'client_idle_time', 'apply': 'timeInMillis'},
{'key': 'connection_connected_time', 'apply': 'timeInSeconds'},

View file

@ -5,7 +5,7 @@
<h2 class="header">{% trans %}online_info.h.actions{% endtrans %}</h2>
{% include 'form.twig' with {
'additional_forms': [
'fields': [
{
'header_label': 'online.poke'|trans,
'label': '<i class="material-icons">check_circle</i>',

View file

@ -10,7 +10,7 @@
<h2 class="header">{% trans %}server_info.h.actions{% endtrans %}</h2>
{% include 'form.twig' with {
'additional_forms': [
'fields': [
{
'header_label': 'server_info.send'|trans,
'label': '<i class="material-icons">check_circle</i>',

View file

@ -46,7 +46,7 @@
<h2 class="header">{% trans %}servers.h.create{% endtrans %}</h2>
{% include 'form.twig' with {
'additional_forms': [
'fields': [
{
'header_label': 'server_create.label'|trans,
'label': '<i class="material-icons">check_circle</i>',

View file

@ -8,7 +8,9 @@
{% endfor %}
{% for key in added %}
<th>{{ key|replace({'_' : ' '})|title }}</th>
{% if key not in hiddenColumns %}
<th>{{ key|replace({'_' : ' '})|title }}</th>
{% endif %}
{% endfor %}
{% for link in additional_links %}
@ -19,62 +21,77 @@
<tbody>
{% for arr in data %}
{% set show = true %}
{% for hidden in hide %}
{% if hidden.key in arr|keys and attribute(arr, hidden.key) in hidden.values %}
{% set show = false %}
{% endif %}
{% endfor %}
{% set show = true %}
{% if show == true %}
<tr>
{% for key, value in arr %}
{% set value = value %}
{% for hidden in hiddenDependingOnAttribute %}
{% if hidden.key in arr|keys and attribute(arr, hidden.key) in hidden.values %}
{% set show = false %}
{% endif %}
{% endfor %}
{% for filter in filters %}
{% if filter.key == key %}
{% set value = value|apply_filter(filter.apply)|raw %}
{% if show == true %}
<tr>
{% for key, value in arr %}
{% set value = value %}
{% for filter in filters %}
{% if filter.key == key %}
{% set value = value|apply_filter(filter.apply)|raw %}
{% endif %}
{% endfor %}
{% set showColumn = true %}
{% if key in hiddenColumns %}
{% set showColumn = false %}
{% endif %}
{% if showColumn %}
{% set item = '<td>' ~ value ~ '</td>' %}
{% for link in links %}
{% if link.key == key %}
{% if link.uri_param is not empty %}
{% for searchingKey, searchingValue in arr %}
{% if searchingKey == link.uri_param %}
{% set item = "<td><a href=\"#{link.uri}\/#{searchingValue}\">#{value}</a></td>" %}
{% endif %}
{% endfor %}
{% else %}
{% set item = "<td><a href=\"#{link.uri}\/#{value}\">#{value}</a></td>" %}
{% endif %}
{% endif %}
{% endfor %}
{{ item|raw }}
{% endif %}
{% endfor %}
{% set item = '<td>' ~ value ~ '</td>' %}
{% for link in additional_links %}
<td>
{% set item = "<a href=\"#{link.uri}\">#{link.label}" %}
{% for link in links %}
{% if link.key == key %}
{% if link.uri_param is not empty %}
{% for searchingKey, searchingValue in arr %}
{% if searchingKey == link.uri_param %}
{% set item = "<td><a href=\"#{link.uri}\/#{searchingValue}\">#{value}</a></td>" %}
{% set shownValue = searchingValue %}
{% if link.apply is not empty %}
{% set shownValue = shownValue|apply_filter(link.apply)|raw %}
{% endif %}
{% set item = "<a href=\"#{link.uri}\/#{shownValue}\">#{link.label}</a>" %}
{% endif %}
{% endfor %}
{% else %}
{% set item = "<td><a href=\"#{link.uri}\/#{value}\">#{value}</a></td>" %}
{% endif %}
{% endif %}
{{ item|raw }}
</td>
{% endfor %}
{{ item|raw }}
{% endfor %}
{% for link in additional_links %}
<td>
{% set item = "<a href=\"#{link.uri}\">#{link.label}" %}
{% if link.uri_param is not empty %}
{% for searchingKey, searchingValue in arr %}
{% if searchingKey == link.uri_param %}
{% set item = "<a href=\"#{link.uri}\/#{searchingValue}\">#{link.label}</a>" %}
{% endif %}
{% endfor %}
{% endif %}
{{ item|raw }}
</td>
{% endfor %}
</tr>
{% endif %}
</tr>
{% endif %}
{% endfor %}
</tbody>

View file

@ -0,0 +1,44 @@
{% extends 'layout.twig' %}
{% block content %}
<h1 class="header">{{ title }}</h1>
<h2 class="header">{% trans %}tokens.h.add{% endtrans %}</h2>
{% include 'form.twig' with {
'fields': [
{
'header_label': 'tokens.add'|trans,
'label': '<i class="material-icons">check_circle</i>',
'uri': '/tokens/add/' ~ sid,
'uri_method': 'post',
'fields': [
{'type': 'select', 'key': 'tokentype', 'options': tokentypes, 'label': 'tokens.add.tokentype'|trans},
{'type': 'select', 'key': 'serverGroup', 'options': serverGroups, 'label': 'tokens.add.serverGroup'|trans},
{'type': 'select', 'key': 'channelGroup', 'options': channelGroups, 'label': 'tokens.add.channelGroup'|trans},
{'type': 'select', 'key': 'channel', 'options': channels, 'label': 'tokens.add.channel'|trans},
{'type': 'text', 'key': 'description', 'label': 'tokens.add.description'|trans},
]
}
]
} %}
{% if data|length >0 %}
<h2 class="header">{% trans %}tokens.h.details{% endtrans %}</h2>
{% include 'table.twig' with {'data': data,
'filters': [
{'key': 'token_created', 'apply': 'timestamp'},
{'key': 'token_type', 'apply': 'tokentype'},
],
'hiddenColumns': ['token_id1','token_id2'],
'additional_links': [
{
'header_label': 'tokens.delete'|trans,
'label': '<i class="material-icons">delete</i>',
'uri': '/tokens/delete/' ~ sid,
'uri_param': 'token',
'apply': 'escape_url'
}
],
} %}
{% endif %}
{% endblock %}