Files
kane-diagnostics/hubzilla/addon/vs01/Widget/CivicNav.php
2026-06-07 12:37:32 -04:00

142 lines
4.6 KiB
PHP

<?php
namespace Zotlabs\Widget;
/**
* CivicNav — Civic Diagnostics section navigation widget.
*
* Renders in right_aside on:
* - /channel/{assoc-slug} — via load_pdl hook injected by vs01_load()
* - /vs01/{assoc-slug} — via mod_vs01.pdl
* - /dsc01/{assoc-slug} — via mod_dsc01.pdl
* - /scn01/{assoc-slug} — via mod_scn01.pdl
*
* Reads addon/vs01/civicnav.json for the registered addon list.
* Slug is resolved from the URL (argv(1) for addon modules) or from
* App::$profile['channel_address'] for the channel module.
* URLs use z_root() so they survive Zot6/Nomad channel migration.
*
* Returns empty string if no slug or slug not in vs01 config.
*/
class CivicNav {
public function widget($arr) {
$slug = $this->resolve_slug();
if (!$slug) {
return '';
}
// Only render on registered association channels.
if (!$this->is_registered_association($slug)) {
return '';
}
$addons = $this->load_registry();
if (empty($addons)) {
return '';
}
$module = \App::$module ?? '';
$slug_safe = htmlspecialchars($slug, ENT_QUOTES, 'UTF-8');
if (function_exists('head_add_css')) {
head_add_css('/addon/vs01/view/css/civicnav.css');
}
$out = '<div class="civicnav-widget">';
$out .= '<h5 class="civicnav-heading">Diagnostic Record</h5>';
$out .= '<ul class="civicnav-list">';
foreach ($addons as $addon) {
if (empty($addon['enabled'])) {
continue;
}
$mod = $addon['module'] ?? '';
$label = htmlspecialchars($addon['label'] ?? $mod, ENT_QUOTES, 'UTF-8');
if (!$mod) {
continue;
}
$is_active = ($module === $mod);
$url = z_root() . '/' . $mod . '/' . $slug_safe;
$out .= '<li class="civicnav-item' . ($is_active ? ' civicnav-active' : '') . '">';
if ($is_active) {
$out .= '<span class="civicnav-link civicnav-current">' . $label . '</span>';
} else {
$out .= '<a href="' . $url . '" class="civicnav-link">' . $label . '</a>';
}
$out .= '</li>';
}
$out .= '</ul>';
$out .= '</div>';
return $out;
}
// -------------------------------------------------------------------------
// SLUG RESOLUTION
// -------------------------------------------------------------------------
private function resolve_slug() {
$module = \App::$module ?? '';
// On the channel page: slug is the local part of channel_address.
if ($module === 'channel') {
$addr = \App::$profile['channel_address'] ?? '';
if ($addr) {
// channel_address may be bare (kingsrow-wdca) or full (kingsrow-wdca@node).
return explode('@', $addr)[0];
}
return '';
}
// On any registered addon module page: slug is argv(1).
// Module list is driven by the civicnav registry — no hardcoded names.
$addons = $this->load_registry();
foreach ($addons as $addon) {
if (($addon['module'] ?? '') === $module) {
return \argv(1) ?? '';
}
}
return '';
}
// -------------------------------------------------------------------------
// ASSOCIATION GUARD
// -------------------------------------------------------------------------
private function is_registered_association($slug) {
$raw = @file_get_contents('addon/vs01/config.json');
if ($raw === false) {
return false;
}
$config = json_decode($raw, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return false;
}
return isset($config['associations'][$slug]);
}
// -------------------------------------------------------------------------
// REGISTRY LOADER
// -------------------------------------------------------------------------
private function load_registry() {
$raw = @file_get_contents('addon/vs01/civicnav.json');
if ($raw === false) {
// Fall back to template defaults if runtime file not present.
$raw = @file_get_contents('addon/vs01/civicnav.json.template');
}
if ($raw === false) {
return [];
}
$data = json_decode($raw, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return [];
}
return $data['addons'] ?? [];
}
}