diff --git a/AdditionalSites/data.php b/AdditionalSites/data.php
new file mode 100644
index 0000000..5f72fb2
--- /dev/null
+++ b/AdditionalSites/data.php
@@ -0,0 +1,137 @@
+ ['PrincipalID','AccessPrefs','HoverHeight','Language','LanguageIsPublic','PermEveryone','PermGroup','PermNextOwner'],
+ 'assets' => ['name','description','assetType','local','temporary','data','id','create_time','access_time','asset_flags','CreatorID'],
+ 'auth' => ['UUID','passwordHash','passwordSalt','webLoginKey','accountType'],
+ 'Avatars' => ['PrincipalID','Name','Value'],
+ 'balances' => ['user','balance','status','type'],
+ 'classifieds' => ['classifieduuid','creatoruuid','creationdate','expirationdate','category','name','description','parceluuid','parentestate','snapshotuuid','simname','posglobal','parcelname','classifiedflags','priceforlisting'],
+ 'Friends' => ['PrincipalID','Friend','Flags','Offered'],
+ 'GridUser' => ['UserID','HomeRegionID','HomePosition','HomeLookAt','LastRegionID','LastPosition','LastLookAt','Online','Login','Logout'],
+ 'hg_traveling_data' => ['SessionID','UserID','GridExternalName','ServiceToken','ClientIPAddress','MyIPAddress','TMStamp'],
+ 'im_offline' => ['ID','PrincipalID','FromID','Message','TMStamp'],
+ 'inventoryfolders' => ['folderName','type','version','folderID','agentID','parentFolderID'],
+ 'inventoryitems' => ['assetID','assetType','inventoryName','inventoryDescription','inventoryNextPermissions','inventoryCurrentPermissions','invType','creatorID','inventoryBasePermissions','inventoryEveryOnePermissions','salePrice','saleType','creationDate','groupID','groupOwned','flags','inventoryID','avatarID','parentFolderID','inventoryGroupPermissions'],
+ 'migrations' => ['name','version'],
+ 'MuteList' => ['AgentID','MuteID','MuteName','MuteType','MuteFlags','Stamp'],
+ 'os_groups_groups' => ['GroupID','Location','Name','Charter','InsigniaID','FounderID','MembershipFee','OpenEnrollment','ShowInList','AllowPublish','MaturePublish','OwnerRoleID'],
+ 'os_groups_invites' => ['InviteID','GroupID','RoleID','PrincipalID','TMStamp'],
+ 'os_groups_membership' => ['GroupID','PrincipalID','SelectedRoleID','Contribution','ListInProfile','AcceptNotices','AccessToken'],
+ 'os_groups_notices' => ['GroupID','NoticeID','TMStamp','FromName','Subject','Message','HasAttachment','AttachmentType','AttachmentName','AttachmentItemID','AttachmentOwnerID'],
+ 'os_groups_principals' => ['PrincipalID','ActiveGroupID'],
+ 'os_groups_rolemembership' => ['GroupID','RoleID','PrincipalID'],
+ 'os_groups_roles' => ['GroupID','RoleID','Name','Description','Title','Powers'],
+ 'Presence' => ['UserID','RegionID','SessionID','SecureSessionID','LastSeen'],
+ 'regions' => ['uuid','regionHandle','regionName','regionRecvKey','regionSendKey','regionSecret','regionDataURI','serverIP','serverPort','serverURI','locX','locY','locZ','eastOverrideHandle','westOverrideHandle','southOverrideHandle','northOverrideHandle','regionAssetURI','regionAssetRecvKey','regionAssetSendKey','regionUserURI','regionUserRecvKey','regionUserSendKey','regionMapTexture','serverHttpPort','serverRemotingPort','owner_uuid','originUUID','access','ScopeID','sizeX','sizeY','flags','last_seen','PrincipalID','Token','parcelMapTexture'],
+ 'tokens' => ['UUID','token','validity'],
+ 'totalsales' => ['UUID','user','objectUUID','type','TotalCount','TotalAmount','time'],
+ 'transactions' => ['UUID','sender','receiver','amount','senderBalance','receiverBalance','objectUUID','objectName','regionHandle','regionUUID','type','time','secure','status','commonName','description'],
+ 'UserAccounts' => ['PrincipalID','ScopeID','FirstName','LastName','Email','ServiceURLs','Created','UserLevel','UserFlags','UserTitle','active'],
+ 'userdata' => ['UserId','TagId','DataKey','DataVal'],
+ 'userinfo' => ['user','simip','avatar','pass','type','class','serverurl'],
+ 'usernotes' => ['useruuid','targetuuid','notes'],
+ 'userpicks' => ['pickuuid','creatoruuid','toppick','parceluuid','name','description','snapshotuuid','user','originalname','simname','posglobal','sortorder','enabled','gatekeeper'],
+ 'userprofile' => ['useruuid','profilePartner','profileAllowPublish','profileMaturePublish','profileURL','profileWantToMask','profileWantToText','profileSkillsMask','profileSkillsText','profileLanguages','profileImage','profileAboutText','profileFirstImage','profileFirstText'],
+ 'usersettings' => ['useruuid','imviaemail','visible','email'],
+];
+// Datenbank-Utility für alle Tabellen der Robust-Datenbank (OpenSim)
+// Wiederverwendbar in anderen Skripten via require/include
+
+class RobustDB {
+ private $pdo;
+
+ /**
+ * Erstelle RobustDB-Instanz.
+ * @param string|PDO $host Hostname oder PDO-Objekt
+ * @param string|null $dbname
+ * @param string|null $user
+ * @param string|null $pass
+ */
+ public function __construct($host, $dbname = null, $user = null, $pass = null) {
+ if ($host instanceof PDO) {
+ $this->pdo = $host;
+ } else {
+ // Prüfe, ob datainc.php eine $pdo-Variable bereitstellt
+ if ($host === 'datainc') {
+ require_once __DIR__ . '/datainc.php';
+ if (isset($pdo) && $pdo instanceof PDO) {
+ $this->pdo = $pdo;
+ return;
+ } else {
+ throw new Exception('Keine gültige PDO-Verbindung in datainc.php gefunden.');
+ }
+ }
+ $dsn = "mysql:host=$host;dbname=$dbname;charset=utf8mb4";
+ $options = [
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ ];
+ $this->pdo = new PDO($dsn, $user, $pass, $options);
+ }
+ }
+
+ // Alle Tabellen auflisten
+ public function listTables() {
+ $stmt = $this->pdo->query("SHOW TABLES");
+ return $stmt->fetchAll(PDO::FETCH_COLUMN);
+ }
+
+ // Alle Einträge einer Tabelle lesen
+ public function getAll($table) {
+ $stmt = $this->pdo->query("SELECT * FROM `" . addslashes($table) . "`");
+ return $stmt->fetchAll();
+ }
+
+ // Einzelnen Eintrag lesen (nach Primärschlüssel)
+ public function getById($table, $idColumn, $idValue) {
+ $sql = "SELECT * FROM `" . addslashes($table) . "` WHERE `$idColumn` = :id LIMIT 1";
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->execute(['id' => $idValue]);
+ return $stmt->fetch();
+ }
+
+ // Eintrag einfügen
+ public function insert($table, $data) {
+ $columns = array_keys($data);
+ $placeholders = array_map(function($col) { return ":$col"; }, $columns);
+ $sql = "INSERT INTO `" . addslashes($table) . "` (" . implode(",", $columns) . ") VALUES (" . implode(",", $placeholders) . ")";
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->execute($data);
+ return $this->pdo->lastInsertId();
+ }
+
+ // Eintrag aktualisieren
+ public function update($table, $idColumn, $idValue, $data) {
+ $set = [];
+ foreach ($data as $col => $val) {
+ $set[] = "`$col` = :$col";
+ }
+ $sql = "UPDATE `" . addslashes($table) . "` SET " . implode(", ", $set) . " WHERE `$idColumn` = :id";
+ $data['id'] = $idValue;
+ $stmt = $this->pdo->prepare($sql);
+ return $stmt->execute($data);
+ }
+
+ // Eintrag löschen
+ public function delete($table, $idColumn, $idValue) {
+ $sql = "DELETE FROM `" . addslashes($table) . "` WHERE `$idColumn` = :id";
+ $stmt = $this->pdo->prepare($sql);
+ return $stmt->execute(['id' => $idValue]);
+ }
+
+ // Beliebige Abfrage (z.B. für komplexe Filter)
+ public function query($sql, $params = []) {
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->execute($params);
+ return $stmt->fetchAll();
+ }
+}
+
+// Beispiel für die Nutzung in anderen Skripten:
+// require_once 'statistics/data.php';
+// $db = new RobustDB($host, $dbname, $user, $pass);
+// $result = $db->getAll('UserAccounts');
+
+?>
\ No newline at end of file
diff --git a/AdditionalSites/datainc.php.example b/AdditionalSites/datainc.php.example
new file mode 100644
index 0000000..e35355d
--- /dev/null
+++ b/AdditionalSites/datainc.php.example
@@ -0,0 +1,30 @@
+ true,
+ 'cookie_secure' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on',
+ 'cookie_samesite' => 'Strict',
+ ]);
+}
+
+// Datenbankverbindung für Statistiksoftware
+$dsn = 'mysql:host=localhost;dbname=opensim_database;charset=utf8mb4';
+$user = 'dein_db_user';
+$pass = 'dein_db_password';
+$options = [
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ PDO::ATTR_EMULATE_PREPARES => false,
+];
+
+try {
+ $pdo = new PDO($dsn, $user, $pass, $options);
+} catch (PDOException $e) {
+ // Im Produktivbetrieb keine Details ausgeben!
+ http_response_code(500);
+ exit('Interner Fehler.');
+}
\ No newline at end of file
diff --git a/AdditionalSites/datatest.php b/AdditionalSites/datatest.php
new file mode 100644
index 0000000..db1e471
--- /dev/null
+++ b/AdditionalSites/datatest.php
@@ -0,0 +1,35 @@
+ $columns) {
+ // Für assets: alle Spalten außer 'data' abfragen
+ if ($table === 'assets') {
+ $cols = array_filter($columns, function($c) { return $c !== 'data'; });
+ $sql = "SELECT " . implode(", ", $cols) . " FROM `$table` LIMIT 1";
+ $result = $db->query($sql);
+ echo "
$table
\n";
+ if ($result && count($result) > 0) {
+ $row = $result[0];
+ $row['[BLOB]'] = '[BLOB ausgeblendet]';
+ echo '' . htmlspecialchars(print_r($row, true)) . '
';
+ } else {
+ echo 'Keine Einträge gefunden.';
+ }
+ } else {
+ $result = $db->query("SELECT * FROM `$table` LIMIT 1");
+ echo "$table
\n";
+ if ($result && count($result) > 0) {
+ echo '' . htmlspecialchars(print_r($result[0], true)) . '
';
+ } else {
+ echo 'Keine Einträge gefunden.';
+ }
+ }
+}
\ No newline at end of file
diff --git a/AdditionalSites/ssinc.php.example b/AdditionalSites/ssinc.php.example
new file mode 100644
index 0000000..6161785
--- /dev/null
+++ b/AdditionalSites/ssinc.php.example
@@ -0,0 +1,30 @@
+ true,
+ 'cookie_secure' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on',
+ 'cookie_samesite' => 'Strict',
+ ]);
+}
+
+// Datenbankverbindung für Statistiksoftware
+$dsn = 'mysql:host=localhost;dbname=opensim_stats;charset=utf8mb4';
+$user = 'dein_db_user';
+$pass = 'dein_db_passwort';
+$options = [
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ PDO::ATTR_EMULATE_PREPARES => false,
+];
+
+try {
+ $pdo = new PDO($dsn, $user, $pass, $options);
+} catch (PDOException $e) {
+ // Im Produktivbetrieb keine Details ausgeben!
+ http_response_code(500);
+ exit('Interner Fehler.');
+}
\ No newline at end of file
diff --git a/AdditionalSites/statistic.php b/AdditionalSites/statistic.php
new file mode 100644
index 0000000..322ff36
--- /dev/null
+++ b/AdditionalSites/statistic.php
@@ -0,0 +1,409 @@
+ true,
+ 'cookie_secure' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on',
+ 'cookie_samesite' => 'Strict',
+ ]);
+}
+
+// CSRF-Basisschutz für POST-Formulare (falls später genutzt)
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
+ http_response_code(403);
+ exit('Ungültiges CSRF-Token.');
+ }
+}
+
+// Regionen laden
+$sql_regions = 'SELECT uuid, regionName, locX, locY, sizeX, sizeY, serverIP, serverPort FROM regions ORDER BY regionName';
+$regions = $pdo->query($sql_regions)->fetchAll(PDO::FETCH_ASSOC);
+
+// Online-User (Presence, falls leer: keine online)
+$sql_online = 'SELECT p.UserID, ua.FirstName, ua.LastName, p.RegionID, r.regionName, p.LastSeen
+FROM Presence p
+LEFT JOIN UserAccounts ua ON p.UserID = ua.PrincipalID
+LEFT JOIN regions r ON p.RegionID = r.uuid
+ORDER BY p.LastSeen DESC';
+$online = $pdo->query($sql_online)->fetchAll(PDO::FETCH_ASSOC);
+
+// GridUser mit Status (Online/Offline)
+$sql_griduser = 'SELECT gu.UserID, gu.LastRegionID, gu.Login, gu.Logout, gu.Online, ua.FirstName, ua.LastName
+FROM GridUser gu
+LEFT JOIN UserAccounts ua ON gu.UserID = ua.PrincipalID';
+$gridusers = $pdo->query($sql_griduser)->fetchAll(PDO::FETCH_ASSOC);
+
+
+// MuteList laden
+$sql_mute = 'SELECT m.AgentID, m.MuteID, m.MuteName, m.MuteType, ua.FirstName, ua.LastName FROM MuteList m LEFT JOIN UserAccounts ua ON m.MuteID = ua.PrincipalID';
+$mutelist = $pdo->query($sql_mute)->fetchAll(PDO::FETCH_ASSOC);
+
+
+// Gruppen laden
+$sql_groups = 'SELECT * FROM os_groups_groups ORDER BY Name ASC, Charter ASC';
+$groups = $pdo->query($sql_groups)->fetchAll(PDO::FETCH_ASSOC);
+
+// Benutzerinformationen laden
+$sql_userinfo = 'SELECT * FROM userinfo ORDER BY simip ASC, avatar ASC, serverurl DESC';
+$userinfo = $pdo->query($sql_userinfo)->fetchAll(PDO::FETCH_ASSOC);
+
+?>
+
+
+
+
+ OpenSimulator Statistik
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ OpenSimulator Statistik Dashboard
+
+
+
+
+
OpenSimulator Region Statistik
+
Live-Statistiken zu Regionen und Benutzern
+
+
+
+
+
+
Regionen im Grid
+
+
+
+
+ | Name |
+ UUID |
+ Position |
+ Größe |
+ Server-IP |
+ Port |
+
+
+
+
+ | Zur Zeit sind keine Regionen im Grid. |
+
+
+
+ | = htmlspecialchars($region['regionName']) ?> |
+ = htmlspecialchars($region['uuid']) ?> |
+ = htmlspecialchars($region['locX']) ?>, = htmlspecialchars($region['locY']) ?> |
+ = htmlspecialchars($region['sizeX']) ?> x = htmlspecialchars($region['sizeY']) ?> |
+ = htmlspecialchars($region['serverIP']) ?> |
+ = htmlspecialchars($region['serverPort']) ?> |
+
+
+
+
+
+
+
+
+
+
+
Gruppen im Grid
+
+
+
Zur Zeit sind keine Gruppen im Grid.
+
+
+
+
+ | Name |
+ Gruppenbeschreibung (Charter) |
+ Gründer (FounderID) |
+ Location |
+ InsigniaID |
+ Mitgliedsbeitrag |
+ Offen |
+
+
+
+
+
+ | = htmlspecialchars($group['Name']) ?> |
+ = htmlspecialchars($group['Charter']) ?> |
+ = htmlspecialchars($group['FounderID']) ?> |
+ = htmlspecialchars($group['Location']) ?> |
+ = htmlspecialchars($group['InsigniaID']) ?> |
+ = htmlspecialchars($group['MembershipFee']) ?> |
+ = htmlspecialchars($group['OpenEnrollment']) ?> |
+
+
+
+
+
+
+
+
+
+
+
Online-Mitglieder
+
+
+
Zur Zeit befindet sich kein Mitglied im Grid.
+
+
+
+
+ | Name |
+ UserID |
+ Region |
+ Region-Name |
+ Letzte Aktivität |
+
+
+
+
+
+ |
+
+ |
+ = htmlspecialchars($user['UserID']) ?> |
+ = htmlspecialchars($user['RegionID']) ?> |
+ = htmlspecialchars($user['regionName']) ?> |
+ = htmlspecialchars($user['LastSeen']) ?> |
+
+
+
+
+
+
+
+
+
+
+
Benutzerinformationen
+
+
+
Zur Zeit sind keine Benutzerinformationen im Grid.
+
+
+
+
+ | Avatar |
+ Server-URL |
+
+
+
+
+
+ | = htmlspecialchars($info['avatar']) ?> |
+ = htmlspecialchars($info['serverurl']) ?> |
+
+
+
+
+
+
+
+
+
+
+
Alle GridUser
+
+
+
+
+ | Status |
+ Name |
+ Benutzerkennung |
+ Heimatadresse |
+ Vollständiger Name |
+ Letzte Region |
+ Login |
+ Logout |
+
+
+
+
+ | Zur Zeit gibt es keine GridUser im Grid. |
+
+
+
+
+ |
+
+
+ Online
+
+ Offline
+
+ |
+ = htmlspecialchars(($user['FirstName'] ?? '') . ' ' . ($user['LastName'] ?? '')) ?> |
+ = htmlspecialchars($kennung) ?> |
+ = htmlspecialchars($heimat) ?> |
+ = htmlspecialchars($vollname) ?> |
+ = htmlspecialchars($user['LastRegionID']) ?> |
+ = htmlspecialchars($user['Login']) ?> |
+ = htmlspecialchars($user['Logout']) ?> |
+
+
+
+
+
+
+
+
+
+
+
Stummgeschaltete Nutzer (MuteList)
+
+
+
Im Grid ist zur Zeit niemand stummgeschaltet.
+
+
+
+
+ | Stummschaltender (AgentID) |
+ Stummgeschaltet (MuteID) |
+ Name |
+ MuteName |
+ MuteType |
+
+
+
+
+
+ | = htmlspecialchars($mute['AgentID']) ?> |
+ = htmlspecialchars($mute['MuteID']) ?> |
+ = htmlspecialchars(trim(($mute['FirstName'] ?? '') . ' ' . ($mute['LastName'] ?? ''))) ?> |
+ = htmlspecialchars($mute['MuteName']) ?> |
+ = htmlspecialchars($mute['MuteType']) ?> |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+