Initial commit

This commit is contained in:
billz 2024-11-05 09:12:19 -08:00
parent a86c637f04
commit 262ff6a00e
3 changed files with 249 additions and 0 deletions

View file

@ -0,0 +1,31 @@
<?php
/**
* Plugin Interface
*
* @description Basic plugin interface for RaspAP
* @author Bill Zimmerman <billzimmerman@gmail.com>
* @license https://github.com/raspap/raspap-webgui/blob/master/LICENSE
* @see
*/
declare(strict_types=1);
namespace RaspAP\Plugins;
use RaspAP\UI\Sidebar;
interface PluginInterface {
/**
* Initialize the plugin
* @param Sidebar $sidebar Sidebar instance for adding items
*/
public function initialize(Sidebar $sidebar): void;
/**
* Provide template data for rendering
* @return array
*/
public function getTemplateData(): array;
}

View file

@ -0,0 +1,120 @@
<?php
/**
* Plugin Manager class
*
* @description Architecture to support user plugins for RaspAP
* @author Bill Zimmerman <billzimmerman@gmail.com>
* @license https://github.com/raspap/raspap-webgui/blob/master/LICENSE
*/
declare(strict_types=1);
namespace RaspAP\Plugins;
use RaspAP\UI\Sidebar;
class PluginManager {
private static $instance = null;
private $plugins = [];
private $sidebar;
private function __construct() {
$this->pluginPath = 'plugins';
$this->sidebar = new Sidebar();
$this->autoloadPlugins(); // autoload plugins on instantiation
}
// Get the single instance of PluginManager
public static function getInstance(): PluginManager {
if (self::$instance === null) {
self::$instance = new PluginManager();
}
return self::$instance;
}
// Autoload plugins found in pluginPath
private function autoloadPlugins(): void {
if (!is_dir($this->pluginPath)) {
return;
}
$directories = array_filter(glob($this->pluginPath . '/*'), 'is_dir');
foreach ($directories as $dir) {
$pluginName = basename($dir);
$pluginFile = "$dir/$pluginName.php";
if (file_exists($pluginFile)) {
require_once $pluginFile;
$className = "RaspAP\\Plugins\\$pluginName\\$pluginName"; // Fully qualified class name
if (class_exists($className)) {
$plugin = new $className();
$this->registerPlugin($plugin);
}
}
}
}
// Registers a plugin by its interface implementation
private function registerPlugin(PluginInterface $plugin) {
$plugin->initialize($this->sidebar); // pass sidebar to initialize method
$this->plugins[] = $plugin; // store the plugin instance
}
/**
* Renders a template from inside a plugin directory
* @param string $pluginName
* @param string $templateName
* @param array $__data
*/
public function renderTemplate(string $pluginName, string $templateName, array $__data = []): string {
// Construct the file path for the template
$templateFile = "{$this->pluginPath}/{$pluginName}/templates/{$templateName}.php";
if (!file_exists($templateFile)) {
return "Template file {$templateFile} not found.";
}
// Extract the data for use in the template
if (!empty($__data)) {
extract($__data);
}
// Start output buffering to capture the template output
ob_start();
include $templateFile;
return ob_get_clean(); // return the output
}
// Returns the sidebar
public function getSidebar(): Sidebar {
return $this->sidebar;
}
// Forwards the page to the responsible plugin
public function handlePageAction(string $page): void {
foreach ($this->getInstalledPlugins() as $plugin) {
if (str_starts_with($page, "/plugin__" . $plugin . "__")) {
require_once($this->pluginPath . "/" . $plugin . "/functions.php");
$function = '\\' . $plugin . '\\pluginHandlePageAction';
$function($page);
}
}
}
// Returns all installed plugins
public function getInstalledPlugins(): array {
$plugins = [];
if (file_exists($this->pluginPath)) {
$files = scandir($this->pluginPath);
foreach ($files as $file) {
if ($file === "." || $file === "..") continue;
$filePath = $this->pluginPath . '/' . $file;
if (is_dir($filePath)) {
$plugins[] = $file;
}
}
}
return $plugins;
}
}

98
src/RaspAP/UI/Sidebar.php Normal file
View file

@ -0,0 +1,98 @@
<?php
/**
* Sidebar UI class
*
* @description A sidebar class for the RaspAP UI, extendable by user plugins
* @author Bill Zimmerman <billzimmerman@gmail.com>
* @license https://github.com/raspap/raspap-webgui/blob/master/LICENSE
*/
namespace RaspAP\UI;
class Sidebar {
private $items = [];
public function __construct() {
// Load default sidebar items
$this->addItem(_('Dashboard'), 'fa-solid fa-gauge-high', 'wlan0_info', 10);
$this->addItem(_('Hotspot'), 'far fa-dot-circle', 'hostapd_conf', 20,
fn() => defined('RASPI_HOTSPOT_ENABLED') && RASPI_HOTSPOT_ENABLED
);
$this->addItem(_('DHCP Server'), 'fas fa-exchange-alt', 'dhcpd_conf', 30,
fn() => defined('RASPI_DHCP_ENABLED') && RASPI_DHCP_ENABLED && !$_SESSION["bridgedEnabled"]
);
$this->addItem(_('Ad Blocking'), 'far fa-hand-paper', 'adblock_conf', 40,
fn() => defined('RASPI_ADBLOCK_ENABLED') && RASPI_HOTSPOT_ENABLED && !$_SESSION["bridgedEnabled"]
);
$this->addItem(_('Networking'), 'fas fa-network-wired', 'network_conf', 50,
fn() => defined('RASPI_NETWORK_ENABLED') && RASPI_NETWORK_ENABLED
);
$this->addItem(_('WiFi client'), 'fas fa-wifi', 'wpa_conf', 60,
fn() => defined('RASPI_WIFICLIENT_ENABLED') && RASPI_WIFICLIENT_ENABLED && !$_SESSION["bridgedEnabled"]
);
$this->addItem(_('OpenVPN'), 'fas fa-key', 'openvpn_conf', 70,
fn() => defined('RASPI_OPENVPN_ENABLED') && RASPI_OPENVPN_ENABLED
);
$this->addItem(_('WireGuard'), 'ra-wireguard', 'wg_conf', 80,
fn() => defined('RASPI_WIREGUARD_ENABLED') && RASPI_WIREGUARD_ENABLED
);
$this->addItem(_(getProviderValue($_SESSION["providerID"], "name")), 'fas fa-shield-alt', 'provider_conf', 90,
fn() => defined('RASPI_VPN_PROVIDER_ENABLED') && RASPI_VPN_PROVIDER_ENABLED
);
$this->addItem(_('Authentication'), 'fas fa-user-lock', 'auth_conf', 100,
fn() => defined('RASPI_CONFAUTH_ENABLED') && RASPI_CONFAUTH_ENABLED
);
$this->addItem(_('Data usage'), 'fas fa-chart-area', 'data_use', 110,
fn() => defined('RASPI_VNSTAT_ENABLED') && RASPI_VNSTAT_ENABLED
);
$this->addItem(_('RestAPI'), 'fas fa-puzzle-piece', 'restapi_conf', 120,
fn() => defined('RASPI_VNSTAT_ENABLED') && RASPI_VNSTAT_ENABLED
);
$this->addItem(_('System'), 'fas fa-cube', 'data_use', 130,
fn() => defined('RASPI_SYSTEM_ENABLED') && RASPI_SYSTEM_ENABLED
);
$this->addItem(_('About RaspAP'), 'fas fa-info-circle', 'about', 140);
}
/**
* Adds an item to the sidebar
* @param string $label
* @param string $iconClass
* @param string $link
* @param int $priority
* @param callable $condition
*/
public function addItem(string $label, string $iconClass, string $link, int $priority, callable $condition = null) {
$this->items[] = [
'label' => $label,
'icon' => $iconClass,
'link' => $link,
'priority' => $priority,
'condition' => $condition
];
}
public function getItems(): array {
// Sort items by priority and filter by enabled condition
$filteredItems = array_filter($this->items, function ($item) {
return $item['condition'] === null || call_user_func($item['condition']);
});
usort($filteredItems, function ($a, $b) {
return $a['priority'] <=> $b['priority'];
});
return $filteredItems;
}
public function render() {
foreach ($this->getItems() as $item) {
echo "<div class=\"sb-nav-link-icon px-2\">
<a class=\"nav-link\" href=\"{$item['link']}\">
<i class=\"sb-nav-link-icon {$item['icon']} fa-fw mr-2\"></i>
<span class=\"nav-label\">{$item['label']}</span>
</a>
</div>";
}
}
}