Skip to content

Commit 906616e

Browse files
committed
Added PluginNav and meta.json files to Plugins/*
1 parent fa6e70e commit 906616e

File tree

10 files changed

+433
-51
lines changed

10 files changed

+433
-51
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ phpliteadmin.*
1515
sysadm/
1616
vendor/*
1717
public/php_error.log
18+
cache/*

src/PluginNav.php

+232
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
// Created: 20150101 - Updated: 20250205
5+
// Copyright (C) 2015-2025 Mark Constable <markc@renta.net> (AGPL-3.0)
6+
7+
namespace HCP;
8+
9+
class PluginNav
10+
{
11+
private string $pluginsDir;
12+
private string $cacheFile;
13+
private int $cacheExpiry = 3600; // Cache expiry in seconds (1 hour)
14+
15+
public function __construct(?string $baseDir = null)
16+
{
17+
Util::elog(__METHOD__);
18+
19+
$this->pluginsDir = $baseDir ?? dirname(__DIR__) . '/Plugins';
20+
$this->cacheFile = dirname(__DIR__) . '/cache/plugins.json';
21+
}
22+
23+
public function scanPlugins(): array
24+
{
25+
Util::elog(__METHOD__);
26+
27+
// Check if cache exists and is valid
28+
if ($this->isCacheValid())
29+
{
30+
return $this->loadCache();
31+
}
32+
33+
// If no valid cache exists, scan directories and create cache
34+
$navigation = $this->performScan();
35+
$this->saveCache($navigation);
36+
37+
return $navigation;
38+
}
39+
40+
private function isCacheValid(): bool
41+
{
42+
Util::elog(__METHOD__);
43+
44+
if (!file_exists($this->cacheFile))
45+
{
46+
return false;
47+
}
48+
49+
// Check if cache is expired
50+
$cacheTime = filemtime($this->cacheFile);
51+
if (time() - $cacheTime > $this->cacheExpiry)
52+
{
53+
return false;
54+
}
55+
56+
// Check if any plugin directory or meta.json has been modified
57+
$directories = glob($this->pluginsDir . '/*');
58+
foreach ($directories as $dir)
59+
{
60+
if (!is_dir($dir)) continue;
61+
62+
// Check directory modification time
63+
if (filemtime($dir) > $cacheTime)
64+
{
65+
return false;
66+
}
67+
68+
// Check meta.json modification time
69+
$metaFile = $dir . '/meta.json';
70+
if (file_exists($metaFile) && filemtime($metaFile) > $cacheTime)
71+
{
72+
return false;
73+
}
74+
}
75+
76+
return true;
77+
}
78+
79+
private function loadCache(): array
80+
{
81+
Util::elog(__METHOD__);
82+
83+
$cache = json_decode(file_get_contents($this->cacheFile), true);
84+
if (json_last_error() === JSON_ERROR_NONE)
85+
{
86+
return $cache;
87+
}
88+
// If cache is corrupted, perform fresh scan
89+
return $this->performScan();
90+
}
91+
92+
private function saveCache(array $data): void
93+
{
94+
Util::elog(__METHOD__);
95+
96+
$cacheDir = dirname($this->cacheFile);
97+
if (!is_dir($cacheDir))
98+
{
99+
mkdir($cacheDir, 0755, true);
100+
}
101+
file_put_contents($this->cacheFile, json_encode($data, JSON_PRETTY_PRINT));
102+
}
103+
104+
private function performScan(): array
105+
{
106+
Util::elog(__METHOD__);
107+
108+
$directories = array_filter(glob($this->pluginsDir . '/*'), 'is_dir');
109+
$plugins = array_map(
110+
function ($dir)
111+
{
112+
$meta = $this->getPluginMeta($dir);
113+
return [
114+
'name' => $meta['name'],
115+
'url' => "?plugin=" . basename($dir),
116+
'icon' => $meta['icon'],
117+
'order' => $meta['order'],
118+
'group' => $meta['group']
119+
];
120+
},
121+
$directories
122+
);
123+
124+
// Sort plugins by order
125+
usort($plugins, function ($a, $b)
126+
{
127+
return $a['order'] <=> $b['order'];
128+
});
129+
130+
// Group plugins
131+
$grouped = [];
132+
$ungrouped = [];
133+
foreach ($plugins as $plugin)
134+
{
135+
if ($plugin['group'])
136+
{
137+
if (!isset($grouped[$plugin['group']]))
138+
{
139+
$grouped[$plugin['group']] = [];
140+
}
141+
$grouped[$plugin['group']][] = [
142+
$plugin['name'],
143+
$plugin['url'],
144+
$plugin['icon']
145+
];
146+
}
147+
else
148+
{
149+
$ungrouped[] = [
150+
$plugin['name'],
151+
$plugin['url'],
152+
$plugin['icon']
153+
];
154+
}
155+
}
156+
157+
// Calculate minimum order for each group
158+
$groupOrders = [];
159+
foreach ($grouped as $groupName => $items)
160+
{
161+
$minOrder = PHP_INT_MAX;
162+
foreach ($items as $item)
163+
{
164+
foreach ($plugins as $plugin)
165+
{
166+
if ($plugin['name'] === $item[0])
167+
{
168+
$minOrder = min($minOrder, $plugin['order']);
169+
break;
170+
}
171+
}
172+
}
173+
$groupOrders[$groupName] = $minOrder;
174+
}
175+
176+
// Build navigation items with order information
177+
$navItems = [];
178+
179+
// Add grouped plugins with their order
180+
foreach ($grouped as $groupName => $items)
181+
{
182+
$navItems[] = [
183+
'type' => 'group',
184+
'order' => $groupOrders[$groupName],
185+
'content' => [
186+
$groupName,
187+
$items,
188+
'bi bi-collection fw'
189+
]
190+
];
191+
}
192+
193+
// Add ungrouped plugins with their order
194+
foreach ($ungrouped as $plugin)
195+
{
196+
$navItems[] = [
197+
'type' => 'single',
198+
'order' => array_values(array_filter($plugins, fn($p) => $p['name'] === $plugin[0]))[0]['order'],
199+
'content' => $plugin
200+
];
201+
}
202+
203+
// Sort all items by order
204+
usort($navItems, fn($a, $b) => $a['order'] <=> $b['order']);
205+
206+
// Extract final navigation structure
207+
return array_map(fn($item) => $item['content'], $navItems);
208+
}
209+
210+
private function getPluginMeta(string $dir): array
211+
{
212+
Util::elog(__METHOD__);
213+
214+
$metaFile = $dir . '/meta.json';
215+
if (file_exists($metaFile))
216+
{
217+
$meta = json_decode(file_get_contents($metaFile), true);
218+
return [
219+
'name' => $meta['name'] ?? basename($dir),
220+
'icon' => $meta['icon'] ?? 'bi bi-box-seam fw',
221+
'order' => $meta['order'] ?? 999,
222+
'group' => $meta['group'] ?? null
223+
];
224+
}
225+
return [
226+
'name' => basename($dir),
227+
'icon' => 'bi bi-box-seam fw',
228+
'order' => 999,
229+
'group' => null
230+
];
231+
}
232+
}

src/Plugins/Accounts/meta.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "Accounts",
3+
"icon": "bi bi-people",
4+
"order": 10,
5+
"group": "Admin"
6+
}

src/Plugins/Auth/meta.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "Auth",
3+
"icon": "bi bi-people",
4+
"order": 10,
5+
"group": "Admin"
6+
}

src/Plugins/Example/meta.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "Example",
3-
"description": "A Simple Example Plugin",
4-
"icon": "bi bi-house fw",
5-
"order": 100
3+
"icon": "bi bi-house",
4+
"order": 100,
5+
"group": ""
66
}

src/Plugins/Home/meta.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "Home",
3-
"description": "The main home page plugin",
4-
"icon": "bi bi-house fw",
5-
"order": 1
3+
"icon": "bi bi-house",
4+
"order": 1,
5+
"group": ""
66
}

src/Plugins/InfoSys/meta.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "System Info",
3+
"icon": "bi bi-speedometer",
4+
"order": 3,
5+
"group": "Tools"
6+
}

src/Plugins/Processes/meta.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "Processes",
3+
"icon": "bi bi-diagram-2",
4+
"order": 5,
5+
"group": "Tools"
6+
}

0 commit comments

Comments
 (0)