diff --git a/README.md b/README.md
index c7dceea..4874c15 100755
--- a/README.md
+++ b/README.md
@@ -2,15 +2,13 @@
**ts3web** is a webinterface for any TeamSpeak 3 Server used with serverQuery login.
-This webinterface aims to be as simple as possible. It does not provide complex features. Instead, it only supports features considered useful for a TeamSpeak 3 web interface. **The minimalistic approach is intentional!**
+This webinterface aims to be as simple as possible. The minimalistic approach is intentional.
-If you like to help (to translate or implement missing features), open an issue first. You should use existing code to implement new features. PRs will be merged after a code review.
+If you like to help (to translate or implement features), open an issue first. If possible, you should use existing code to implement new features. PRs will be merged after a code review.
-Things you **cannot** do:
-- Permissions Management (add, edit, delete for servergroups, channelgroups, clients)
-- File Management (except viewing)
-- Temporary passwords
-- Move online clients
+Features which are currently not supported:
+- Permissions Management (except for viewing)
+- File Management (except for viewing)
## Install ##
diff --git a/config/ACL.php b/config/ACL.php
index e828dcc..b38b9de 100644
--- a/config/ACL.php
+++ b/config/ACL.php
@@ -56,6 +56,7 @@ class ACL extends \Zend\Permissions\Acl\Acl
'/online/kick/{sid}/{clid}',
'/online/ban/{sid}/{clid}',
'/online/send/{sid}/{clid}',
+ '/online/move/{sid}/{clid}',
'/clients/{sid}',
'/clients/{sid}/{cldbid}',
diff --git a/config/routes.php b/config/routes.php
index bc1130a..87c9eb3 100644
--- a/config/routes.php
+++ b/config/routes.php
@@ -175,9 +175,13 @@ $app->post('/online/ban/{sid}/{clid}', OnlineBanAction::class);
$container[OnlineSendAction::class] = function ($container) {
return new OnlineSendAction($container);
};
-
$app->post('/online/send/{sid}/{clid}', OnlineSendAction::class);
+$container[OnlineMoveAction::class] = function ($container) {
+ return new OnlineMoveAction($container);
+};
+$app->post('/online/move/{sid}/{clid}', OnlineMoveAction::class);
+
// group
$container[GroupsAction::class] = function ($container) {
return new GroupsAction($container);
diff --git a/data/locale/en.yml b/data/locale/en.yml
index 3dcf57a..c386226 100644
--- a/data/locale/en.yml
+++ b/data/locale/en.yml
@@ -213,6 +213,10 @@ bans.delete: "Delete"
complains.delete: "Delete"
# online
+online.move: "Move"
+online.move.channel: "Channel"
+online.move.channel_password: "Channel password"
+online.moved.success: "Moved client %clid%."
online.kick: "Kick"
online.kick.reason: "Reason"
online.poke: "Poke"
diff --git a/src/Control/Actions/OnlineInfoAction.php b/src/Control/Actions/OnlineInfoAction.php
index c803924..d2691a2 100644
--- a/src/Control/Actions/OnlineInfoAction.php
+++ b/src/Control/Actions/OnlineInfoAction.php
@@ -15,10 +15,19 @@ final class OnlineInfoAction extends AbstractAction
$dataResult = $this->ts->getInstance()->clientInfo($clid);
+ $channelsResult = $this->ts->getInstance()->channelList();
+ $channelsResult = $this->ts->getInstance()->getElement('data', $channelsResult);
+ $channels = [];
+
+ foreach ($channelsResult as $channel) {
+ $channels[$channel['channel_name']] = $channel['cid'];
+ }
+
// render GET
$this->view->render($response, 'online_info.twig', [
'title' => $this->translator->trans('online_info.title') . ' ' . $clid,
'data' => $this->ts->getInstance()->getElement('data', $dataResult),
+ 'channels' => $channels,
'sid' => $sid,
'clid' => $clid,
]);
diff --git a/src/Control/Actions/OnlineMoveAction.php b/src/Control/Actions/OnlineMoveAction.php
new file mode 100644
index 0000000..3c06444
--- /dev/null
+++ b/src/Control/Actions/OnlineMoveAction.php
@@ -0,0 +1,29 @@
+getParsedBody();
+
+ $channel = $body['channel'];
+ $channelPassword = null;
+ if (array_key_exists('channel_password', $body)) $channelPassword = $body['channel_password'];
+
+ $this->ts->login($this->auth->getIdentity()['user'], $this->auth->getIdentity()['password']);
+ $selectResult = $this->ts->getInstance()->selectServer($sid, 'serverId');
+
+ if (empty($channelPassword)) $dataResult = $this->ts->getInstance()->clientMove($clid, $channel);
+ else $dataResult = $this->ts->getInstance()->clientMove($clid, $channel, $channelPassword);
+
+
+ $this->flash->addMessage('success', $this->translator->trans('online.moved.success', ['%clid%' => $clid]));
+ return $response->withRedirect('/online/' . $sid . '/' . $clid);
+ }
+}
\ No newline at end of file
diff --git a/src/View/material/online_info.twig b/src/View/material/online_info.twig
index cc07b96..5ba56ef 100644
--- a/src/View/material/online_info.twig
+++ b/src/View/material/online_info.twig
@@ -34,6 +34,16 @@
{'type': 'number', 'key': 'time', 'label': 'online.ban.time'|trans}
]
},
+ {
+ 'header_label': 'online.move'|trans,
+ 'label': 'check_circle',
+ 'uri': '/online/move/' ~ sid ~ '/' ~ clid,
+ 'uri_method': 'post',
+ 'fields': [
+ {'type': 'select', 'key': 'channel', 'options': channels,'label': 'online.move.channel'|trans},
+ {'type': 'text', 'key': 'channel_password', 'label': 'online.move.channel_password'|trans}
+ ]
+ },
{
'header_label': 'online.send'|trans,
'label': 'check_circle',