//
// The link controls:
// include_private — whether private outfits are visible
// can_wear — whether Wear/Remove buttons are shown
//
// Wear/Remove buttons are disabled (greyed out) when the HUD
// is offline or the outfit is RLV-locked, matching the
// behaviour of the owner's own dashboard.
//
// Visitors can never alter settings, outfit metadata, or
// anything else — read (and optionally wear) only.
// ============================================================
define('ENSEMBLE_THEME', 'default');
require_once __DIR__ . '/db.php';
// ── Resolve link ──────────────────────────────────────────────
$linkUUID = trim($_GET['id'] ?? '');
$notFound = false;
$link = null;
$owner = null;
$outfits = [];
$allTags = [];
if (!preg_match('/^[0-9a-f]{32}$/', $linkUUID)) {
$notFound = true;
}
if (!$notFound) {
try {
$pdo = db_connect();
$stmt = $pdo->prepare('
SELECT l.link_uuid, l.link_name, l.can_wear, l.include_private,
u.username, u.sim_url, u.last_seen
FROM links l
JOIN users u ON u.access_uuid = l.user_uuid
WHERE l.link_uuid = ?
');
$stmt->execute([$linkUUID]);
$link = $stmt->fetch();
if (!$link) {
$notFound = true;
} else {
// ── Load outfits ──────────────────────────────────
// If include_private=0, only public outfits are returned.
if ($link['include_private']) {
$stmtOutfits = $pdo->prepare('
SELECT id, outfit_name, folder_path, attachments,
has_space_warning, image_filename, created_at,
tags, access_level, wear_mode, locked
FROM outfits
WHERE user_uuid = (
SELECT user_uuid FROM links WHERE link_uuid = ?
)
ORDER BY created_at DESC
');
} else {
$stmtOutfits = $pdo->prepare('
SELECT id, outfit_name, folder_path, attachments,
has_space_warning, image_filename, created_at,
tags, access_level, wear_mode, locked
FROM outfits
WHERE user_uuid = (
SELECT user_uuid FROM links WHERE link_uuid = ?
)
AND access_level != \'private\'
ORDER BY created_at DESC
');
}
$stmtOutfits->execute([$linkUUID]);
$outfits = $stmtOutfits->fetchAll();
// ── Collect tags ──────────────────────────────────
$allTagsRaw = [];
foreach ($outfits as $o) {
$t = trim($o['tags'] ?? '');
if ($t !== '') {
foreach (array_map('trim', explode(',', $t)) as $tag) {
if ($tag !== '') $allTagsRaw[] = $tag;
}
}
}
$allTagsMap = [];
foreach ($allTagsRaw as $tag) {
$allTagsMap[strtolower($tag)] = $tag;
}
ksort($allTagsMap);
$allTags = array_values($allTagsMap);
}
} catch (Exception $e) {
error_log('Ensemble view.php error: ' . $e->getMessage());
$notFound = true;
}
}
// ── HUD status ────────────────────────────────────────────────
// Returns the same three-state status used by the owner dashboard:
// Online — last_seen < 3 min (HUD is actively heartbeating)
// Stale — last_seen < 10 min (HUD may still be reachable)
// Offline — everything else
// For can_wear links, buttons are disabled when status is Offline.
// Stale is treated as still-reachable (command is queued by WebRelay).
function view_hud_status(array $link): array
{
if (empty($link['sim_url']) || empty($link['last_seen'])) {
return ['label' => 'Offline', 'class' => 'status-offline', 'online' => false];
}
$age = time() - (int)$link['last_seen'];
if ($age < 180) {
return ['label' => 'Online', 'class' => 'status-online', 'online' => true];
}
if ($age < 600) {
return ['label' => 'Stale', 'class' => 'status-stale', 'online' => true];
}
return ['label' => 'Offline', 'class' => 'status-offline', 'online' => false];
}
?>
Link not found
This link doesn't exist or may have been removed by its owner.
Sign in to Ensemble
⚠
?
= htmlspecialchars($outfit['outfit_name']) ?>
' . htmlspecialchars($cardTags) . '';
} else {
echo '
'
. htmlspecialchars($outfit['folder_path']) . '
';
}
?>