ソースを参照

TeamSpeak 3 PHP Framework update

- Updating from v1.1.32 to v1.1.33

Main site: https://www.planetteamspeak.com/
GitHub: https://github.com/planetteamspeak/ts3phpframework

===============================================================================
   + Added feature or noticable improvement
   - Bug fix or something removed
   * Changed or Information
   ! Important - Take note!
===============================================================================

=== Release 1.1.33 - WIP ===
 ! upgrading your servers to 3.2.0 (build >= 1525708231) is recommended
 + added TeamSpeak3_Viewer_Json which builds a JSON struct similar to what is
   provided by the Planet TeamSpeak Simple REST API (servernodes) and is fully
   compatible to the jquery-ts3viewer plugin
 + added support for optional server shutdown messages
 + added TeamSpeak3_Adapter_ServerQuery_Exception::hasReturnCode()
 + added TeamSpeak3_Adapter_ServerQuery_Exception::getReturnCode()
 + added TeamSpeak3_Node_Server::customSet()
 + added TeamSpeak3_Node_Server::customDelete()
 + added TeamSpeak3_Node_Client::customSet()
 + added TeamSpeak3_Node_Client::customDelete()
 + added TeamSpeak3_Node_Client::getBadges()
 + added TeamSpeak3_Node_Client::hasOverwolf()
 * TeamSpeak3_Viewer_Html interface now displays client_is_recording status
 * documentation fixes
Rocco27 8 年 前
コミット
29728bfcc6

+ 44 - 1
protocol/TeamSpeak3/Adapter/ServerQuery/Exception.php

@@ -26,4 +26,47 @@
  * @class TeamSpeak3_Adapter_ServerQuery_Exception
  * @class TeamSpeak3_Adapter_ServerQuery_Exception
  * @brief Enhanced exception class for TeamSpeak3_Adapter_ServerQuery objects.
  * @brief Enhanced exception class for TeamSpeak3_Adapter_ServerQuery objects.
  */
  */
-class TeamSpeak3_Adapter_ServerQuery_Exception extends TeamSpeak3_Adapter_Exception {}
+class TeamSpeak3_Adapter_ServerQuery_Exception extends TeamSpeak3_Adapter_Exception
+{
+  /**
+   * Stores the optional return code for ServerQuery errors.
+   *
+   * @var string
+   */
+  protected $return_code = null;
+
+  /**
+   * The TeamSpeak3_Adapter_ServerQuery_Exception constructor.
+   *
+   * @param  string  $mesg
+   * @param  integer $code
+   * @param  string  $return_code
+   * @return TeamSpeak3_Adapter_ServerQuery_Exception
+   */
+  public function __construct($mesg, $code = 0x00, $return_code = null)
+  {
+    parent::__construct($mesg, $code);
+
+    $this->return_code = $return_code;
+  }
+
+  /**
+   * Returns TRUE if the exception provides a return code for ServerQuery errors.
+   *
+   * @return boolean
+   */
+  public function hasReturnCode()
+  {
+    return $this->return_code !== null;
+  }
+
+  /**
+   * Returns the optional return code for ServerQuery errors.
+   *
+   * @return string
+   */
+  public function getReturnCode()
+  {
+    return $this->return_code;
+  }
+}

+ 1 - 1
protocol/TeamSpeak3/Adapter/ServerQuery/Reply.php

@@ -313,7 +313,7 @@ class TeamSpeak3_Adapter_ServerQuery_Reply
         $suffix = "";
         $suffix = "";
       }
       }
       
       
-      throw new TeamSpeak3_Adapter_ServerQuery_Exception($this->getErrorProperty("msg") . $suffix, $this->getErrorProperty("id"));
+      throw new TeamSpeak3_Adapter_ServerQuery_Exception($this->getErrorProperty("msg") . $suffix, $this->getErrorProperty("id"), $this->getErrorProperty("return_code"));
     }
     }
   }
   }
 
 

+ 37 - 0
protocol/TeamSpeak3/Exception.php

@@ -28,6 +28,20 @@
  */
  */
 class TeamSpeak3_Exception extends Exception
 class TeamSpeak3_Exception extends Exception
 {
 {
+  /**
+   * Stores the original error code.
+   *
+   * @var integer
+   */
+  protected $raw_code = 0x00;
+
+  /**
+   * Stores the original error message.
+   *
+   * @var string
+   */
+  protected $raw_mesg = null;
+
   /**
   /**
    * Stores custom error messages.
    * Stores custom error messages.
    *
    *
@@ -46,6 +60,9 @@ class TeamSpeak3_Exception extends Exception
   {
   {
     parent::__construct($mesg, $code);
     parent::__construct($mesg, $code);
 
 
+    $this->raw_code = $code;
+    $this->raw_mesg = $mesg;
+
     if(array_key_exists((int) $code, self::$messages))
     if(array_key_exists((int) $code, self::$messages))
     {
     {
       $this->message = $this->prepareCustomMessage(self::$messages[intval($code)]);
       $this->message = $this->prepareCustomMessage(self::$messages[intval($code)]);
@@ -112,6 +129,26 @@ class TeamSpeak3_Exception extends Exception
     unset(self::$messages[(int) $code]);
     unset(self::$messages[(int) $code]);
   }
   }
 
 
+  /**
+   * Returns the original error code.
+   *
+   * @return integer
+   */
+  public function getRawCode()
+  {
+    return $this->raw_code;
+  }
+
+  /**
+   * Returns the original error message.
+   *
+   * @return integer
+   */
+  public function getRawMessage()
+  {
+    return $this->raw_mesg;
+  }
+
   /**
   /**
    * Returns the class from which the exception was thrown.
    * Returns the class from which the exception was thrown.
    *
    *

+ 7 - 1
protocol/TeamSpeak3/Helper/Convert.php

@@ -36,11 +36,14 @@ class TeamSpeak3_Helper_Convert
    */
    */
   public static function bytes($bytes)
   public static function bytes($bytes)
   {
   {
+    // @todo: Fix precision lost from multiple rounding
     $kbytes = sprintf("%.02f", $bytes/1024);
     $kbytes = sprintf("%.02f", $bytes/1024);
     $mbytes = sprintf("%.02f", $kbytes/1024);
     $mbytes = sprintf("%.02f", $kbytes/1024);
     $gbytes = sprintf("%.02f", $mbytes/1024);
     $gbytes = sprintf("%.02f", $mbytes/1024);
     $tbytes = sprintf("%.02f", $gbytes/1024);
     $tbytes = sprintf("%.02f", $gbytes/1024);
-
+  
+    // @todo: Fix assuming non-negative $bytes value, without validation
+    // Recommend something like: if( (float)$xbytes != 0 )
     if($tbytes >= 1)
     if($tbytes >= 1)
       return $tbytes . " TB";
       return $tbytes . " TB";
     if($gbytes >= 1)
     if($gbytes >= 1)
@@ -55,6 +58,9 @@ class TeamSpeak3_Helper_Convert
 
 
   /**
   /**
    * Converts seconds/milliseconds to a human readable value.
    * Converts seconds/milliseconds to a human readable value.
+   * 
+   * Note: Assumes non-negative integer, but no validation
+   * @todo: Handle negative integer $seconds, or invalidate
    *
    *
    * @param  integer $seconds
    * @param  integer $seconds
    * @param  boolean $is_ms
    * @param  boolean $is_ms

+ 2 - 0
protocol/TeamSpeak3/Helper/Crypt.php

@@ -23,6 +23,8 @@
  */
  */
 
 
 /**
 /**
+ * @todo: Replace this class with native encryption
+ *
  * @class TeamSpeak3_Helper_Crypt
  * @class TeamSpeak3_Helper_Crypt
  * @brief Helper class for data encryption.
  * @brief Helper class for data encryption.
  */
  */

+ 6 - 3
protocol/TeamSpeak3/Helper/Signal.php

@@ -44,6 +44,9 @@ class TeamSpeak3_Helper_Signal
 
 
   /**
   /**
    * Emits a signal with a given set of parameters.
    * Emits a signal with a given set of parameters.
+   * 
+   * @todo: Confirm / fix $return is set to last $slot->call() return value.
+   *      It appears all previous calls before last are lost / ignored.
    *
    *
    * @param  string $signal
    * @param  string $signal
    * @param  mixed  $params
    * @param  mixed  $params
@@ -75,7 +78,7 @@ class TeamSpeak3_Helper_Signal
    *
    *
    * @param  mixed  $callback
    * @param  mixed  $callback
    * @param  string
    * @param  string
-   * @return void
+   * @return string
    */
    */
   public function getCallbackHash($callback)
   public function getCallbackHash($callback)
   {
   {
@@ -171,7 +174,7 @@ class TeamSpeak3_Helper_Signal
    */
    */
   public function getHandlers($signal)
   public function getHandlers($signal)
   {
   {
-    if(!$this->hasHandlers($signal))
+    if($this->hasHandlers($signal))
     {
     {
       return $this->sigslots[$signal];
       return $this->sigslots[$signal];
     }
     }
@@ -187,7 +190,7 @@ class TeamSpeak3_Helper_Signal
    */
    */
   public function clearHandlers($signal)
   public function clearHandlers($signal)
   {
   {
-    if(!$this->hasHandlers($signal))
+    if($this->hasHandlers($signal))
     {
     {
       unset($this->sigslots[$signal]);
       unset($this->sigslots[$signal]);
     }
     }

+ 6 - 0
protocol/TeamSpeak3/Helper/Uri.php

@@ -322,7 +322,10 @@ class TeamSpeak3_Helper_Uri
   /**
   /**
    * Returns TRUE if the host is valid.
    * Returns TRUE if the host is valid.
    *
    *
+   * @todo: Implement check for host URI segment
+   *
    * @param string $host
    * @param string $host
+   *
    * @return boolean
    * @return boolean
    */
    */
   public function checkHost($host = null)
   public function checkHost($host = null)
@@ -359,7 +362,10 @@ class TeamSpeak3_Helper_Uri
   /**
   /**
    * Returns TRUE if the port is valid.
    * Returns TRUE if the port is valid.
    *
    *
+   * @todo: Implement check for port URI segment
+   *
    * @param  integer $port
    * @param  integer $port
+   *
    * @return boolean
    * @return boolean
    */
    */
   public function checkPort($port = null)
   public function checkPort($port = null)

+ 5 - 0
protocol/TeamSpeak3/Node/Abstract.php

@@ -206,6 +206,11 @@ abstract class TeamSpeak3_Node_Abstract implements RecursiveIterator, ArrayAcces
       $html .= $viewer->fetchObject($node, $siblings);
       $html .= $viewer->fetchObject($node, $siblings);
     }
     }
 
 
+    if(empty($html) && method_exists($viewer, "toString"))
+    {
+      return $viewer->toString();
+    }
+
     return $html;
     return $html;
   }
   }
 
 

+ 53 - 1
protocol/TeamSpeak3/Node/Client.php

@@ -164,6 +164,29 @@ class TeamSpeak3_Node_Client extends TeamSpeak3_Node_Abstract
     return $this->getParent()->customInfo($this["client_database_id"]);
     return $this->getParent()->customInfo($this["client_database_id"]);
   }
   }
 
 
+  /**
+   * Creates or updates a custom property for the client.
+   *
+   * @param  string $ident
+   * @param  string $value
+   * @return void
+   */
+  public function customSet($ident, $value)
+  {
+    $this->getParent()->customSet($this["client_database_id"], $ident, $value);
+  }
+
+  /**
+   * Removes a custom property from the client.
+   *
+   * @param  string $ident
+   * @return void
+   */
+  public function customDelete($ident)
+  {
+    $this->getParent()->customDelete($this["client_database_id"], $ident);
+  }
+
   /**
   /**
    * Returns an array containing the permission overview of the client.
    * Returns an array containing the permission overview of the client.
    *
    *
@@ -300,6 +323,36 @@ class TeamSpeak3_Node_Client extends TeamSpeak3_Node_Abstract
     return $this->execute("clientgetids", array("cluid" => $this["client_unique_identifier"]))->toAssocArray("clid");
     return $this->execute("clientgetids", array("cluid" => $this["client_unique_identifier"]))->toAssocArray("clid");
   }
   }
 
 
+  /**
+   * Returns TRUE if the client is using Overwolf.
+   *
+   * @return boolean
+   */
+  public function hasOverwolf()
+  {
+    return strstr($this["client_badges"], "overwolf=1") !== FALSE;
+  }
+
+  /**
+   * Returns a list of equipped badges for this client.
+   *
+   * @return array
+   */
+  public function getBadges()
+  {
+    $badges = array();
+
+    foreach(explode(":", $this["client_badges"]) as $set)
+    {
+      if(substr($set, 0, 7) == "badges=")
+      {
+        $badges[] = array_map("trim", explode(",", substr($set, 7)));
+      }
+    }
+
+    return $badges;
+  }
+
   /**
   /**
    * Returns the revision/build number from the clients version string.
    * Returns the revision/build number from the clients version string.
    *
    *
@@ -438,4 +491,3 @@ class TeamSpeak3_Node_Client extends TeamSpeak3_Node_Abstract
     return (string) $this["client_nickname"];
     return (string) $this["client_nickname"];
   }
   }
 }
 }
-

+ 6 - 5
protocol/TeamSpeak3/Node/Host.php

@@ -362,16 +362,17 @@ class TeamSpeak3_Node_Host extends TeamSpeak3_Node_Abstract
    * Stops the virtual server specified by ID.
    * Stops the virtual server specified by ID.
    *
    *
    * @param  integer $sid
    * @param  integer $sid
+   * @param  string  $msg
    * @return void
    * @return void
    */
    */
-  public function serverStop($sid)
+  public function serverStop($sid, $msg = null)
   {
   {
     if($sid == $this->serverSelectedId())
     if($sid == $this->serverSelectedId())
     {
     {
       $this->serverDeselect();
       $this->serverDeselect();
     }
     }
 
 
-    $this->execute("serverstop", array("sid" => $sid));
+    $this->execute("serverstop", array("sid" => $sid, "reasonmsg" => $msg));
     $this->serverListReset();
     $this->serverListReset();
 
 
     TeamSpeak3_Helper_Signal::getInstance()->emit("notifyServerstopped", $this, $sid);
     TeamSpeak3_Helper_Signal::getInstance()->emit("notifyServerstopped", $this, $sid);
@@ -380,13 +381,14 @@ class TeamSpeak3_Node_Host extends TeamSpeak3_Node_Abstract
   /**
   /**
    * Stops the entire TeamSpeak 3 Server instance by shutting down the process.
    * Stops the entire TeamSpeak 3 Server instance by shutting down the process.
    *
    *
+   * @param  string $msg
    * @return void
    * @return void
    */
    */
-  public function serverStopProcess()
+  public function serverStopProcess($msg = null)
   {
   {
     TeamSpeak3_Helper_Signal::getInstance()->emit("notifyServershutdown", $this);
     TeamSpeak3_Helper_Signal::getInstance()->emit("notifyServershutdown", $this);
 
 
-    $this->execute("serverprocessstop");
+    $this->execute("serverprocessstop", array("reasonmsg" => $msg));
   }
   }
 
 
   /**
   /**
@@ -1162,4 +1164,3 @@ class TeamSpeak3_Node_Host extends TeamSpeak3_Node_Abstract
     return (string) $this->getAdapterHost();
     return (string) $this->getAdapterHost();
   }
   }
 }
 }
-

+ 31 - 5
protocol/TeamSpeak3/Node/Server.php

@@ -638,7 +638,7 @@ class TeamSpeak3_Node_Server extends TeamSpeak3_Node_Abstract
    * Returns an array filled with TeamSpeak3_Node_Client objects.
    * Returns an array filled with TeamSpeak3_Node_Client objects.
    *
    *
    * @param  array $filter
    * @param  array $filter
-   * @return array | TeamSpeak3_Node_Client[]
+   * @return array|TeamSpeak3_Node_Client[]
    */
    */
   public function clientList(array $filter = array())
   public function clientList(array $filter = array())
   {
   {
@@ -1036,7 +1036,7 @@ class TeamSpeak3_Node_Server extends TeamSpeak3_Node_Abstract
    * Returns a list of server groups available.
    * Returns a list of server groups available.
    *
    *
    * @param  filter $filter
    * @param  filter $filter
-   * @return array | TeamSpeak3_Node_Servergroup[]
+   * @return array|TeamSpeak3_Node_Servergroup[]
    */
    */
   public function serverGroupList(array $filter = array())
   public function serverGroupList(array $filter = array())
   {
   {
@@ -1364,7 +1364,7 @@ class TeamSpeak3_Node_Server extends TeamSpeak3_Node_Abstract
    * Returns a list of channel groups available.
    * Returns a list of channel groups available.
    *
    *
    * @param  array $filter
    * @param  array $filter
-   * @return array | TeamSpeak3_Node_Channelgroup[]
+   * @return array|TeamSpeak3_Node_Channelgroup[]
    */
    */
   public function channelGroupList(array $filter = array())
   public function channelGroupList(array $filter = array())
   {
   {
@@ -2108,6 +2108,31 @@ class TeamSpeak3_Node_Server extends TeamSpeak3_Node_Abstract
     return $this->execute("custominfo", array("cldbid" => $cldbid))->toArray();
     return $this->execute("custominfo", array("cldbid" => $cldbid))->toArray();
   }
   }
 
 
+  /**
+   * Creates or updates a custom property for the client specified by $cldbid.
+   *
+   * @param  integer $cldbid
+   * @param  string  $ident
+   * @param  string  $value
+   * @return void
+   */
+  public function customSet($cldbid, $ident, $value)
+  {
+    $this->execute("customset", array("cldbid" => $cldbid, "ident" => $ident, "value" => $value));
+  }
+
+  /**
+   * Removes a custom property from the client specified by $cldbid.
+   *
+   * @param  integer $cldbid
+   * @param  string  $ident
+   * @return void
+   */
+  public function customDelete($cldbid, $ident)
+  {
+    $this->execute("customdelete", array("cldbid" => $cldbid, "ident" => $ident));
+  }
+
   /**
   /**
    * Returns a list of active bans on the selected virtual server.
    * Returns a list of active bans on the selected virtual server.
    *
    *
@@ -2325,11 +2350,12 @@ class TeamSpeak3_Node_Server extends TeamSpeak3_Node_Abstract
   /**
   /**
    * Stops the virtual server.
    * Stops the virtual server.
    *
    *
+   * @param  string $msg
    * @return void
    * @return void
    */
    */
-  public function stop()
+  public function stop($msg = null)
   {
   {
-    $this->getParent()->serverStop($this->getId());
+    $this->getParent()->serverStop($this->getId(), $msg);
   }
   }
   
   
   /**
   /**

+ 1 - 1
protocol/TeamSpeak3/TeamSpeak3.php

@@ -66,7 +66,7 @@ class TeamSpeak3
   /**
   /**
    * TeamSpeak 3 PHP Framework version.
    * TeamSpeak 3 PHP Framework version.
    */
    */
-  const LIB_VERSION = "1.1.32";
+  const LIB_VERSION = "1.1.33";
 
 
   /*@
   /*@
    * TeamSpeak 3 protocol separators.
    * TeamSpeak 3 protocol separators.

+ 20 - 8
protocol/TeamSpeak3/Viewer/Html.php

@@ -43,7 +43,7 @@ class TeamSpeak3_Viewer_Html implements TeamSpeak3_Viewer_Interface
   protected $currObj = null;
   protected $currObj = null;
 
 
   /**
   /**
-   * An array filled with siblingsfor the  TeamSpeak3_Node_Abstract object which is currently
+   * An array filled with siblings for the TeamSpeak3_Node_Abstract object which is currently
    * processed.
    * processed.
    *
    *
    * @var array
    * @var array
@@ -274,6 +274,10 @@ class TeamSpeak3_Viewer_Html implements TeamSpeak3_Viewer_Interface
           break;
           break;
       }
       }
     }
     }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client && $this->currObj->client_is_recording)
+    {
+      $extras .= " recording";
+    }
 
 
     return "corpus " . $this->currObj->getClass(null) . $extras;
     return "corpus " . $this->currObj->getClass(null) . $extras;
   }
   }
@@ -344,17 +348,25 @@ class TeamSpeak3_Viewer_Html implements TeamSpeak3_Viewer_Interface
       $before = array();
       $before = array();
       $behind = array();
       $behind = array();
 
 
-      foreach($this->currObj->memberOf() as $group)
+      if(!$this->currObj->client_is_recording)
       {
       {
-        if($group->getProperty("namemode") == TeamSpeak3::GROUP_NAMEMODE_BEFORE)
+        foreach($this->currObj->memberOf() as $group)
         {
         {
-          $before[] = "[" . htmlspecialchars($group["name"]) . "]";
-        }
-        elseif($group->getProperty("namemode") == TeamSpeak3::GROUP_NAMEMODE_BEHIND)
-        {
-          $behind[] = "[" . htmlspecialchars($group["name"]) . "]";
+          if($group->getProperty("namemode") == TeamSpeak3::GROUP_NAMEMODE_BEFORE)
+          {
+            $before[] = "[" . htmlspecialchars($group["name"]) . "]";
+          }
+          elseif($group->getProperty("namemode") == TeamSpeak3::GROUP_NAMEMODE_BEHIND)
+          {
+            $behind[] = "[" . htmlspecialchars($group["name"]) . "]";
+          }
         }
         }
       }
       }
+      else
+      {
+        $before[] = "***";
+        $behind[] = "*** [RECORDING]";
+      }
 
 
       return implode("", $before) . " " . htmlspecialchars($this->currObj) . " " . implode("", $behind);
       return implode("", $before) . " " . htmlspecialchars($this->currObj) . " " . implode("", $behind);
     }
     }

+ 494 - 0
protocol/TeamSpeak3/Viewer/Json.php

@@ -0,0 +1,494 @@
+<?php
+
+/**
+ * @file
+ * TeamSpeak 3 PHP Framework
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   TeamSpeak3
+ * @author    Sven 'ScP' Paulsen
+ * @copyright Copyright (c) Planet TeamSpeak. All rights reserved.
+ */
+
+/**
+ * @class TeamSpeak3_Viewer_Json
+ * @brief Generates a JSON struct used in JS-based TeamSpeak 3 viewers.
+ */
+class TeamSpeak3_Viewer_Json implements TeamSpeak3_Viewer_Interface
+{
+  /**
+   * Stores an array of data parsed from TeamSpeak3_Node_Abstract objects.
+   *
+   * @var array
+   */
+  protected $data = null;
+
+  /**
+   * The TeamSpeak3_Node_Abstract object which is currently processed.
+   *
+   * @var TeamSpeak3_Node_Abstract
+   */
+  protected $currObj = null;
+
+  /**
+   * An array filled with siblings for the TeamSpeak3_Node_Abstract object which is currently
+   * processed.
+   *
+   * @var array
+   */
+  protected $currSib = null;
+
+  /**
+   * An internal counter indicating the depth of the TeamSpeak3_Node_Abstract object previously
+   * processed.
+   *
+   * @var integer
+   */
+  protected $lastLvl = 0;
+
+  /**
+   * The TeamSpeak3_Viewer_Json constructor.
+   *
+   * @param  array $data
+   * @return TeamSpeak3_Viewer_Json
+   */
+  public function __construct(array &$data = array())
+  {
+    $this->data = &$data;
+  }
+
+  /**
+   * Assembles an stdClass object for the current element.
+   *
+   * @param  TeamSpeak3_Node_Abstract $node
+   * @param  array $siblings
+   * @return void
+   */
+  public function fetchObject(TeamSpeak3_Node_Abstract $node, array $siblings = array())
+  {
+    $this->currObj = $node;
+    $this->currSib = $siblings;
+
+    $obj = new stdClass();
+
+    $obj->ident    = $this->getId();
+    $obj->parent   = $this->getParent();
+    $obj->children = $node->count();
+    $obj->level    = $this->getLevel();
+    $obj->first    = (bool) ($obj->level != $this->lastLvl);
+    $obj->last     = (bool) array_pop($siblings);
+    $obj->siblings = array_map("boolval", $siblings);
+    $obj->class    = $this->getType();
+    $obj->name     = $this->getName();
+    $obj->image    = $this->getImage();
+    $obj->props    = $this->getProps();
+
+    $this->data[]  = $obj;
+    $this->lastLvl = $obj->level;
+  }
+
+  /**
+   * Returns the ID of the current element.
+   *
+   * @return mixed
+   */
+  protected function getId()
+  {
+    if($this->currObj instanceof TeamSpeak3_Node_Server)
+    {
+      return "ts3_s" . $this->currObj->virtualserver_id;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Channel)
+    {
+      return "ts3_c" . $this->currObj->cid;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client)
+    {
+      return "ts3_u" . $this->currObj->clid;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Returns the parent ID of the current element.
+   *
+   * @return mixed
+   */
+  protected function getParent()
+  {
+    if($this->currObj instanceof TeamSpeak3_Node_Channel)
+    {
+      return $this->currObj->pid ? "ts3_c" . $this->currObj->pid : "ts3_s" . $this->currObj->getParent()->getId();
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client)
+    {
+      return $this->currObj->cid ? "ts3_c" . $this->currObj->cid : "ts3_s" . $this->currObj->getParent()->getId();
+    }
+
+    return "ts3";
+  }
+
+  /**
+   * Returns the level of the current element.
+   *
+   * @return integer
+   */
+  protected function getLevel()
+  {
+    if($this->currObj instanceof TeamSpeak3_Node_Channel)
+    {
+      return $this->currObj->getLevel()+2;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client)
+    {
+      return $this->currObj->channelGetById($this->currObj->cid)->getLevel()+3;
+    }
+
+    return 1;
+  }
+
+  /**
+   * Returns a single type identifier for the current element.
+   *
+   * @return string
+   */
+  protected function getType()
+  {
+    if($this->currObj instanceof TeamSpeak3_Node_Server)
+    {
+      return "server";
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Channel)
+    {
+      return "channel";
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client)
+    {
+      return "client";
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Servergroup || $this->currObj instanceof TeamSpeak3_Node_Channelgroup)
+    {
+      return "group";
+    }
+
+    return "host";
+  }
+
+  /**
+   * Returns a string for the current corpus element which can be used as a HTML class
+   * property. If the current node is a channel spacer the class string will contain
+   * additional class names to allow further customization of the content via CSS.
+   *
+   * @return string
+   */
+  protected function getClass()
+  {
+    $extras = "";
+
+    if($this->currObj instanceof TeamSpeak3_Node_Channel && $this->currObj->isSpacer())
+    {
+      switch($this->currObj->spacerGetType())
+      {
+        case (string) TeamSpeak3::SPACER_SOLIDLINE:
+          $extras .= " solidline";
+          break;
+
+        case (string) TeamSpeak3::SPACER_DASHLINE:
+          $extras .= " dashline";
+          break;
+
+        case (string) TeamSpeak3::SPACER_DASHDOTLINE:
+          $extras .= " dashdotline";
+          break;
+
+        case (string) TeamSpeak3::SPACER_DASHDOTDOTLINE:
+          $extras .= " dashdotdotline";
+          break;
+
+        case (string) TeamSpeak3::SPACER_DOTLINE:
+          $extras .= " dotline";
+          break;
+      }
+
+      switch($this->currObj->spacerGetAlign())
+      {
+        case TeamSpeak3::SPACER_ALIGN_REPEAT:
+          $extras .= " repeat";
+          break;
+
+        case TeamSpeak3::SPACER_ALIGN_CENTER:
+          $extras .= " center";
+          break;
+
+        case TeamSpeak3::SPACER_ALIGN_RIGHT:
+          $extras .= " right";
+          break;
+
+        case TeamSpeak3::SPACER_ALIGN_LEFT:
+          $extras .= " left";
+          break;
+      }
+    }
+
+    return $this->currObj->getClass(null) . $extras;
+  }
+
+  /**
+   * Returns an individual type for a spacer.
+   *
+   * @return string
+   */
+  protected function getSpacerType()
+  {
+    $type = "";
+
+    if(!$this->currObj instanceof TeamSpeak3_Node_Channel || !$this->currObj->isSpacer())
+    {
+      return "none";
+    }
+
+    switch($this->currObj->spacerGetType())
+    {
+      case (string) TeamSpeak3::SPACER_SOLIDLINE:
+        $type .= "solidline";
+        break;
+
+      case (string) TeamSpeak3::SPACER_DASHLINE:
+        $type .= "dashline";
+        break;
+
+      case (string) TeamSpeak3::SPACER_DASHDOTLINE:
+        $type .= "dashdotline";
+        break;
+
+      case (string) TeamSpeak3::SPACER_DASHDOTDOTLINE:
+        $type .= "dashdotdotline";
+        break;
+
+      case (string) TeamSpeak3::SPACER_DOTLINE:
+        $type .= "dotline";
+        break;
+
+      default:
+        $type .= "custom";
+    }
+
+    if($type == "custom")
+    {
+      switch($this->currObj->spacerGetAlign())
+      {
+        case TeamSpeak3::SPACER_ALIGN_REPEAT:
+          $type .= "repeat";
+          break;
+
+        case TeamSpeak3::SPACER_ALIGN_CENTER:
+          $type .= "center";
+          break;
+
+        case TeamSpeak3::SPACER_ALIGN_RIGHT:
+          $type .= "right";
+          break;
+
+        default:
+          $type .= "left";
+      }
+    }
+
+    return $type;
+  }
+
+  /**
+   * Returns a string for the current corpus element which contains the display name
+   * for the current TeamSpeak_Node_Abstract object.
+   *
+   * @return string
+   */
+  protected function getName()
+  {
+    if($this->currObj instanceof TeamSpeak3_Node_Channel && $this->currObj->isSpacer())
+    {
+      return $this->currObj["channel_name"]->section("]", 1, 99)->toString();
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client)
+    {
+      $before = array();
+      $behind = array();
+
+      foreach($this->currObj->memberOf() as $group)
+      {
+        if($group->getProperty("namemode") == TeamSpeak3::GROUP_NAMEMODE_BEFORE)
+        {
+          $before[] = "[" . $group["name"] . "]";
+        }
+        elseif($group->getProperty("namemode") == TeamSpeak3::GROUP_NAMEMODE_BEHIND)
+        {
+          $behind[] = "[" . $group["name"] . "]";
+        }
+      }
+
+      return trim(implode("", $before) . " " . $this->currObj . " " . implode("", $behind));
+    }
+
+    return $this->currObj->toString();
+  }
+
+  /**
+   * Returns the parent ID of the current element.
+   *
+   * @return stdClass
+   */
+  protected function getProps()
+  {
+    $props = new stdClass();
+
+    if($this->currObj instanceof TeamSpeak3_Node_Host)
+    {
+      $this->id        = 0;
+      $this->icon      = 0;
+      $props->version  = $this->currObj->version("version")->toString();
+      $props->platform = $this->currObj->version("platform")->toString();
+      $props->users    = $this->currObj->virtualservers_total_clients_online;
+      $props->slots    = $this->currObj->virtualservers_total_maxclients;
+      $props->flags    = 0;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Server)
+    {
+      $props->id       = $this->currObj->getId();
+      $props->icon     = $this->currObj->virtualserver_icon_id < 0 ? pow(2, 32)-($this->currObj->virtualserver_icon_id*-1) : $this->currObj->virtualserver_icon_id;
+      $props->welcmsg  = strlen($this->currObj->virtualserver_welcomemessage) ? trim($this->currObj->virtualserver_welcomemessage) : null;
+      $props->hostmsg  = strlen($this->currObj->virtualserver_hostmessage) ? trim($this->currObj->virtualserver_hostmessage) : null;
+      $props->version  = TeamSpeak3_Helper_Convert::versionShort($this->currObj->virtualserver_version)->toString();
+      $props->platform = $this->currObj->virtualserver_platform->toString();
+      $props->country  = null;
+      $props->users    = $this->currObj->clientCount();
+      $props->slots    = $this->currObj->virtualserver_maxclients;
+      $props->flags    = 0;
+
+      $props->flags += $this->currObj->virtualserver_status == "online"   ? 1  : 0;
+      $props->flags += $this->currObj->virtualserver_flag_password        ? 2  : 0;
+      $props->flags += $this->currObj->virtualserver_autostart            ? 4  : 0;
+      $props->flags += $this->currObj->virtualserver_weblist_enabled      ? 8  : 0;
+      $props->flags += $this->currObj->virtualserver_ask_for_privilegekey ? 16 : 0;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Channel)
+    {
+      $props->id       = $this->currObj->getId();
+      $props->icon     = $this->currObj->isSpacer() ? 0 : $this->currObj->channel_icon_id < 0 ? pow(2, 32)-($this->currObj->channel_icon_id*-1) : $this->currObj->channel_icon_id;
+      $props->path     = trim($this->currObj->getPathway());
+      $props->topic    = strlen($this->currObj->channel_topic) ? trim($this->currObj->channel_topic) : null;
+      $props->codec    = $this->currObj->channel_codec;
+      $props->users    = $this->currObj->total_clients == -1 ? 0 : $this->currObj->total_clients;
+      $props->slots    = $this->currObj->channel_maxclients == -1 ? $this->currObj->getParent()->virtualserver_maxclients : $this->currObj->channel_maxclients;
+      $props->famusers = $this->currObj->total_clients_family == -1 ? 0 : $this->currObj->total_clients_family;
+      $props->famslots = $this->currObj->channel_maxfamilyclients == -1 ? $this->currObj->getParent()->virtualserver_maxclients : $this->currObj->channel_maxfamilyclients;
+      $props->spacer   = $this->getSpacerType();
+      $props->flags    = 0;
+
+      $props->flags += $this->currObj->channel_flag_default           ? 1   : 0;
+      $props->flags += $this->currObj->channel_flag_password          ? 2   : 0;
+      $props->flags += $this->currObj->channel_flag_permanent         ? 4   : 0;
+      $props->flags += $this->currObj->channel_flag_semi_permanent    ? 8   : 0;
+      $props->flags += ($props->codec == 3 || $props->codec == 5)     ? 16  : 0;
+      $props->flags += $this->currObj->channel_needed_talk_power != 0 ? 32  : 0;
+      $props->flags += $this->currObj->total_clients != -1            ? 64  : 0;
+      $props->flags += $this->currObj->isSpacer()                     ? 128 : 0;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client)
+    {
+      $props->id       = $this->currObj->getId();
+      $props->icon     = $this->currObj->client_icon_id < 0 ? pow(2, 32)-($this->currObj->client_icon_id*-1) : $this->currObj->client_icon_id;
+      $props->version  = TeamSpeak3_Helper_Convert::versionShort($this->currObj->client_version)->toString();
+      $props->platform = $this->currObj->client_platform->toString();
+      $props->country  = strlen($this->currObj->client_country) ? trim($this->currObj->client_country) : null;
+      $props->awaymesg = strlen($this->currObj->client_away_message) ? trim($this->currObj->client_away_message) : null;
+      $props->memberof = array();
+      $props->badges   = $this->currObj->getBadges();
+      $props->flags    = 0;
+
+      foreach($this->currObj->memberOf() as $num => $group)
+      {
+        $props->memberof[$num] = new stdClass();
+
+        $props->memberof[$num]->name  = trim($group->name);
+        $props->memberof[$num]->icon  = $group->iconid < 0 ? pow(2, 32)-($group->iconid*-1) : $group->iconid;
+        $props->memberof[$num]->order = $group->sortid;
+        $props->memberof[$num]->flags = 0;
+
+        $props->memberof[$num]->flags += $group->namemode;
+        $props->memberof[$num]->flags += $group->type == 2                             ? 4  : 0;
+        $props->memberof[$num]->flags += $group->type == 0                             ? 8  : 0;
+        $props->memberof[$num]->flags += $group->savedb                                ? 16 : 0;
+        $props->memberof[$num]->flags += $group instanceof TeamSpeak3_Node_Servergroup ? 32 : 0;
+      }
+
+      $props->flags += $this->currObj->client_away                                                                                                                             ? 1   : 0;
+      $props->flags += $this->currObj->client_is_recording                                                                                                                     ? 2   : 0;
+      $props->flags += $this->currObj->client_is_channel_commander                                                                                                             ? 4   : 0;
+      $props->flags += $this->currObj->client_is_priority_speaker                                                                                                              ? 8   : 0;
+      $props->flags += $this->currObj->client_is_talker                                                                                                                        ? 16  : 0;
+      $props->flags += $this->currObj->channelGetById($this->currObj->cid)->channel_needed_talk_power > $this->currObj->client_talk_power && !$this->currObj->client_is_talker ? 32  : 0;
+      $props->flags += $this->currObj->client_input_muted || !$this->currObj->client_input_hardware                                                                            ? 64  : 0;
+      $props->flags += $this->currObj->client_output_muted || !$this->currObj->client_output_hardware                                                                          ? 128 : 0;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Servergroup || $this->currObj instanceof TeamSpeak3_Node_Channelgroup)
+    {
+      $props->id     = $this->currObj->getId();
+      $props->icon   = $this->currObj->iconid < 0 ? pow(2, 32)-($this->currObj->iconid*-1) : $this->currObj->iconid;
+      $props->order  = $this->currObj->sortid;
+      $props->n_map  = $this->currObj->n_member_addp;
+      $props->n_mrp  = $this->currObj->n_member_removep;
+      $props->flags  = 0;
+
+      $props->flags += $this->currObj->namemode;
+      $props->flags += $this->currObj->type == 2                             ? 4  : 0;
+      $props->flags += $this->currObj->type == 0                             ? 8  : 0;
+      $props->flags += $this->currObj->savedb                                ? 16 : 0;
+      $props->flags += $this->currObj instanceof TeamSpeak3_Node_Servergroup ? 32 : 0;
+    }
+
+    return $props;
+  }
+
+  /**
+   * Returns the status icon URL of the current element.
+   *
+   * @return string
+   */
+  protected function getImage()
+  {
+    return str_replace("_", "-", $this->currObj->getIcon());
+  }
+
+  /**
+   * Returns a string representation of this node.
+   *
+   * @return string
+   */
+  public function toString()
+  {
+    return $this->__toString();
+  }
+
+  /**
+   * Returns a string representation of this node.
+   *
+   * @return string
+   */
+  public function __toString()
+  {
+    return json_encode($this->data);
+  }
+}