From 07ec56227d43d18a600e3fcb6fb18c45201cc608 Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 20 Dec 2024 11:45:15 -0800 Subject: [PATCH 01/49] Minor: mode change --- includes/footer.php | 0 includes/restapi.php | 0 templates/system/plugins.php | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+) mode change 100644 => 100755 includes/footer.php mode change 100644 => 100755 includes/restapi.php create mode 100644 templates/system/plugins.php diff --git a/includes/footer.php b/includes/footer.php old mode 100644 new mode 100755 diff --git a/includes/restapi.php b/includes/restapi.php old mode 100644 new mode 100755 diff --git a/templates/system/plugins.php b/templates/system/plugins.php new file mode 100644 index 00000000..beef0bfa --- /dev/null +++ b/templates/system/plugins.php @@ -0,0 +1,34 @@ + +
+

+ + + +
+
+ +
+ +
+
+
+ +
+
+ +
+ +
+
+
+ + + +
+ From 36b02851582234413a608196f240fb557ccc42c7 Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 20 Dec 2024 13:33:46 -0800 Subject: [PATCH 02/49] Define RASPI_PLUGINS_URL --- config/config.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/config.php b/config/config.php index 2950632d..afd386a2 100755 --- a/config/config.php +++ b/config/config.php @@ -38,6 +38,9 @@ define('RASPI_ACCESS_CHECK_DNS', 'one.one.one.one'); // Constant for the GitHub API latest release endpoint define('RASPI_API_ENDPOINT', 'https://api.github.com/repos/RaspAP/raspap-webgui/releases/latest'); +// Constant for the GitHub plugin submodules URL +define("RASPI_PLUGINS_URL", "https://raw.githubusercontent.com/RaspAP/plugins"); + // Constant for the 5GHz wireless regulatory domain define("RASPI_5GHZ_CHANNEL_MIN", 100); define("RASPI_5GHZ_CHANNEL_MAX", 192); From 66e35c564c38df2840c541e871df19c02da55218 Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 20 Dec 2024 13:34:05 -0800 Subject: [PATCH 03/49] Initial commit --- templates/system/plugins.php | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/templates/system/plugins.php b/templates/system/plugins.php index beef0bfa..2595198d 100644 --- a/templates/system/plugins.php +++ b/templates/system/plugins.php @@ -7,28 +7,15 @@
-
- +
+ Install to download and activate a plugin from the list. Uninstall removes an existing plugin."); ?>
+
-
-
- -
- -
-
-
- -
From 117370efcff60ccd3eeba7ce20a19e17db17a4ea Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 20 Dec 2024 13:34:58 -0800 Subject: [PATCH 04/49] Add plugins tab, render system/plugins template --- templates/system.php | 2 ++ templates/system/tools.php | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/system.php b/templates/system.php index 29950e39..4c17bcf2 100755 --- a/templates/system.php +++ b/templates/system.php @@ -18,6 +18,7 @@ +
@@ -26,6 +27,7 @@ +
diff --git a/templates/system/tools.php b/templates/system/tools.php index a30601db..32eb19ca 100644 --- a/templates/system/tools.php +++ b/templates/system/tools.php @@ -1,4 +1,4 @@ - +

@@ -36,4 +36,3 @@
- From 89c4f16e45a315118f973e058bc40c7df2e7866e Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 20 Dec 2024 13:42:53 -0800 Subject: [PATCH 05/49] Get plugin submodules, fetch manifest details + format output --- includes/system.php | 236 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 192 insertions(+), 44 deletions(-) diff --git a/includes/system.php b/includes/system.php index 31feaf79..868082d5 100755 --- a/includes/system.php +++ b/includes/system.php @@ -85,53 +85,22 @@ function DisplaySystem(&$extraFooterScripts) $kernel = $system->kernelVersion(); $systime = $system->systime(); $revision = $system->rpiRevision(); - - // mem used + + // memory use $memused = $system->usedMemory(); - $memused_status = "primary"; - if ($memused > 90) { - $memused_status = "danger"; - $memused_led = "service-status-down"; - } elseif ($memused > 75) { - $memused_status = "warning"; - $memused_led = "service-status-warn"; - } elseif ($memused > 0) { - $memused_status = "success"; - $memused_led = "service-status-up"; - } + $memStatus = getMemStatus($memused); + $memused_status = $memStatus['status']; + $memused_led = $memStatus['led']; // cpu load $cpuload = $system->systemLoadPercentage(); - if ($cpuload > 90) { - $cpuload_status = "danger"; - } elseif ($cpuload > 75) { - $cpuload_status = "warning"; - } elseif ($cpuload >= 0) { - $cpuload_status = "success"; - } + $cpuload_status = getCPULoadStatus($cpuload); // cpu temp $cputemp = $system->systemTemperature(); - if ($cputemp > 70) { - $cputemp_status = "danger"; - $cputemp_led = "service-status-down"; - } elseif ($cputemp > 50) { - $cputemp_status = "warning"; - $cputemp_led = "service-status-warn"; - } else { - $cputemp_status = "success"; - $cputemp_led = "service-status-up"; - } - - // hostapd status - $hostapd = $system->hostapdStatus(); - if ($hostapd[0] == 1) { - $hostapd_status = "active"; - $hostapd_led = "service-status-up"; - } else { - $hostapd_status = "inactive"; - $hostapd_led = "service-status-down"; - } + $cpuStatus = getCPUTempStatus($cputemp); + $cputemp_status = $cpuStatus['status']; + $cputemp_led = $cpuStatus['led']; // theme options $themes = [ @@ -147,6 +116,9 @@ function DisplaySystem(&$extraFooterScripts) $extraFooterScripts[] = array('src'=>'app/js/huebee.js', 'defer'=>false); $logLimit = isset($_SESSION['log_limit']) ? $_SESSION['log_limit'] : RASPI_LOG_SIZE_LIMIT; + $plugins = getUserPlugins(); + $pluginsTable = getHTMLPluginsTable($plugins); + echo renderTemplate("system", compact( "arrLocales", "status", @@ -167,11 +139,187 @@ function DisplaySystem(&$extraFooterScripts) "cputemp", "cputemp_status", "cputemp_led", - "hostapd", - "hostapd_status", - "hostapd_led", "themes", "selectedTheme", - "logLimit" + "logLimit", + "pluginsTable" )); } + +/** + * Returns user plugin details from associated manifest.json files + * + * @return array $plugins + */ +function getUserPlugins() +{ + try { + $submodules = getSubmodules(RASPI_PLUGINS_URL); + $plugins = []; + foreach ($submodules as $submodule) { + $manifestUrl = $submodule['url'] .'/blob/master/manifest.json?raw=true'; + $manifest = getPluginManifest($manifestUrl); + + if ($manifest) { + $plugins[] = [ + 'version' => $manifest['version'] ?? 'unknown', + 'name' => $manifest['name'] ?? 'unknown', + 'description' => $manifest['description'] ?? 'No description provided', + 'plugin_uri' => $manifest['plugin_uri'] ?? $submodule['url'], + 'fa-icon' => $manifest['icon'] ?? 'fas fa-plug', + ]; + } + } + return $plugins; + } catch (Exception $e) { + echo "An error occured: " .$e->getMessage(); + } +} + +/** + * Returns git submodules for the specified repository + * + * @param string $repoURL + * @return array $submodules + */ +function getSubmodules(string $repoUrl): array +{ + $gitmodulesUrl = $repoUrl . '/refs/heads/master/.gitmodules'; + $gitmodulesContent = file_get_contents($gitmodulesUrl); + + if ($gitmodulesContent === false) { + throw new Exception('Unable to fetch .gitmodules file from the repository'); + } + + $submodules = []; + $lines = explode("\n", $gitmodulesContent); + $currentSubmodule = []; + + foreach ($lines as $line) { + $line = trim($line); + + if (strpos($line, '[submodule "') === 0) { + if (!empty($currentSubmodule)) { + $submodules[] = $currentSubmodule; + } + $currentSubmodule = []; + } elseif (strpos($line, 'path = ') === 0) { + $currentSubmodule['path'] = substr($line, strlen('path = ')); + } elseif (strpos($line, 'url = ') === 0) { + $currentSubmodule['url'] = substr($line, strlen('url = ')); + } + } + + if (!empty($currentSubmodule)) { + $submodules[] = $currentSubmodule; + } + + return $submodules; +} + +/** + * Returns a plugin's associated manifest in JSON format + * + * @param string $url + * @return array $json + */ +function getPluginManifest(string $url): ?array +{ + $options = [ + 'http' => [ + 'method' => 'GET', + 'follow_location' => 1, + ], + ]; + + $context = stream_context_create($options); + $content= file_get_contents($url, false, $context); + + if ($content === false) { + return null; + } + $json = json_decode($content, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + return null; + } + return $json; +} + +/** + * Returns a list of available plugins formatted as an HTML table + * + * @param array $plugins + * @return string $html + */ +function getHTMLPluginsTable(array $plugins): string +{ + $html = ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + + foreach ($plugins as $plugin) { + $name = ''. htmlspecialchars($plugin['name']). ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + $html .= '
NameVersionDescription
' . $name . '' . htmlspecialchars($plugin['version']) . '' . htmlspecialchars($plugin['description']) . '
'; + return $html; +} + +function getMemStatus($memused): array +{ + $memused_status = "primary"; + $memused_led = ""; + + if ($memused > 90) { + $memused_status = "danger"; + $memused_led = "service-status-down"; + } elseif ($memused > 75) { + $memused_status = "warning"; + $memused_led = "service-status-warn"; + } elseif ($memused > 0) { + $memused_status = "success"; + $memused_led = "service-status-up"; + } + + return [ + 'status' => $memused_status, + 'led' => $memused_led + ]; +} + +function getCPULoadStatus($cpuload): string +{ + if ($cpuload > 90) { + $status = "danger"; + } elseif ($cpuload > 75) { + $status = "warning"; + } elseif ($cpuload >= 0) { + $status = "success"; + } + return $status; +} + +function getCPUTempStatus($cputemp): array +{ + if ($cputemp > 70) { + $cputemp_status = "danger"; + $cputemp_led = "service-status-down"; + } elseif ($cputemp > 50) { + $cputemp_status = "warning"; + $cputemp_led = "service-status-warn"; + } else { + $cputemp_status = "success"; + $cputemp_led = "service-status-up"; + } + return [ + 'status' => $cputemp_status, + 'led' => $cputemp_led + ]; +} + From 247b35b254fe32c329bb20407aaff58a15a1611f Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 20 Dec 2024 18:52:09 -0800 Subject: [PATCH 06/49] Initial commit --- src/RaspAP/Plugins/PluginInstaller.php | 52 ++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/RaspAP/Plugins/PluginInstaller.php diff --git a/src/RaspAP/Plugins/PluginInstaller.php b/src/RaspAP/Plugins/PluginInstaller.php new file mode 100644 index 00000000..74a90d70 --- /dev/null +++ b/src/RaspAP/Plugins/PluginInstaller.php @@ -0,0 +1,52 @@ + + * @license https://github.com/raspap/raspap-webgui/blob/master/LICENSE + */ + +declare(strict_types=1); + +namespace RaspAP\Plugins; + +class PluginInstaller +{ + private static $instance = null; + + public function __construct() + { + $this->pluginPath = 'plugins'; + } + + // Returns a single instance of PluginInstaller + public static function getInstance(): PluginInstaller + { + if (self::$instance === null) { + self::$instance = new PluginInstaller(); + } + return self::$instance; + } + + public function getPlugins(): array + { + $plugins = []; + if (file_exists($this->pluginPath)) { + $directories = scandir($this->pluginPath); + + foreach ($directories as $directory) { + $pluginClass = "RaspAP\\Plugins\\$directory\\$directory"; + $pluginFile = $this->pluginPath . "/$directory/$directory.php"; + + if (file_exists($pluginFile) && class_exists($pluginClass)) { + $plugins[] = $pluginClass; + } + } + } + return $plugins; + } + +} + From 9b087f88a75be9e24d41fed73573eb110073bf60 Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 20 Dec 2024 18:53:35 -0800 Subject: [PATCH 07/49] Call PluginInstaller::getInstance(), set install option --- includes/system.php | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/includes/system.php b/includes/system.php index 868082d5..09467cf8 100755 --- a/includes/system.php +++ b/includes/system.php @@ -153,6 +153,9 @@ function DisplaySystem(&$extraFooterScripts) */ function getUserPlugins() { + $pluginInstaller = \RaspAP\Plugins\PluginInstaller::getInstance(); + $installedPlugins = $pluginInstaller->getPlugins(); + try { $submodules = getSubmodules(RASPI_PLUGINS_URL); $plugins = []; @@ -161,12 +164,24 @@ function getUserPlugins() $manifest = getPluginManifest($manifestUrl); if ($manifest) { + $namespace = $manifest['namespace'] ?? ''; + $installed = false; + + foreach ($installedPlugins as $plugin) { + if (str_contains($plugin, $namespace)) { + $installed = true; + break; + } + } + $plugins[] = [ 'version' => $manifest['version'] ?? 'unknown', 'name' => $manifest['name'] ?? 'unknown', 'description' => $manifest['description'] ?? 'No description provided', 'plugin_uri' => $manifest['plugin_uri'] ?? $submodule['url'], + 'namespace' => $namespace, 'fa-icon' => $manifest['icon'] ?? 'fas fa-plug', + 'installed' => $installed ]; } } @@ -218,7 +233,8 @@ function getSubmodules(string $repoUrl): array } /** - * Returns a plugin's associated manifest in JSON format + * Decodes a plugin's associated manifest JSON. + * Returns an array of key-value pairs * * @param string $url * @return array $json @@ -233,7 +249,7 @@ function getPluginManifest(string $url): ?array ]; $context = stream_context_create($options); - $content= file_get_contents($url, false, $context); + $content = file_get_contents($url, false, $context); if ($content === false) { return null; @@ -259,13 +275,26 @@ function getHTMLPluginsTable(array $plugins): string $html .= 'Name'; $html .= 'Version'; $html .= 'Description'; + $html .= ''; $html .= ''; foreach ($plugins as $plugin) { - $name = ''. htmlspecialchars($plugin['name']). ''; - $html .= '' . $name . ''; - $html .= '' . htmlspecialchars($plugin['version']) . ''; - $html .= '' . htmlspecialchars($plugin['description']) . ''; + $installed = $plugin['installed']; + if ($installed === true ) { + $status = 'Installed'; + } else { + $status = ''; + } + $name = '' + . htmlspecialchars($plugin['name']). ''; + $html .= '' .$name. ''; + $html .= '' .htmlspecialchars($plugin['version']). ''; + $html .= '' .htmlspecialchars($plugin['description']). ''; + $html .= '' .$status. ''; } $html .= ''; return $html; From ee38614334676c3abe3ead428e3a412f1833ad97 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 21 Dec 2024 10:39:46 -0800 Subject: [PATCH 08/49] Initial commit --- ajax/plugins/get_manifest.php | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100755 ajax/plugins/get_manifest.php diff --git a/ajax/plugins/get_manifest.php b/ajax/plugins/get_manifest.php new file mode 100755 index 00000000..70b18c9f --- /dev/null +++ b/ajax/plugins/get_manifest.php @@ -0,0 +1,32 @@ +getPluginManifest($manifestUrl); + if ($manifest) { + echo json_encode($manifest); + } else { + http_response_code(404); + echo json_encode(['error' => 'Plugin manifest not found']); + } + } catch (Exception $e) { + http_response_code(500); + echo json_encode(['error' => 'An unexpected error occurred']); + } +} else { + http_response_code(400); + echo json_encode(['error' => 'Plugin URI is required']); + exit; +} + From 6785cc1104ed658be06befcea7326e03613f27f6 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 21 Dec 2024 10:40:41 -0800 Subject: [PATCH 09/49] Move getPluginManifest() to PluginInstaller class --- includes/system.php | 36 +++----------------------- src/RaspAP/Plugins/PluginInstaller.php | 34 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/includes/system.php b/includes/system.php index 09467cf8..b0f7e21d 100755 --- a/includes/system.php +++ b/includes/system.php @@ -161,7 +161,7 @@ function getUserPlugins() $plugins = []; foreach ($submodules as $submodule) { $manifestUrl = $submodule['url'] .'/blob/master/manifest.json?raw=true'; - $manifest = getPluginManifest($manifestUrl); + $manifest = $pluginInstaller->getPluginManifest($manifestUrl); if ($manifest) { $namespace = $manifest['namespace'] ?? ''; @@ -232,36 +232,6 @@ function getSubmodules(string $repoUrl): array return $submodules; } -/** - * Decodes a plugin's associated manifest JSON. - * Returns an array of key-value pairs - * - * @param string $url - * @return array $json - */ -function getPluginManifest(string $url): ?array -{ - $options = [ - 'http' => [ - 'method' => 'GET', - 'follow_location' => 1, - ], - ]; - - $context = stream_context_create($options); - $content = file_get_contents($url, false, $context); - - if ($content === false) { - return null; - } - $json = json_decode($content, true); - - if (json_last_error() !== JSON_ERROR_NONE) { - return null; - } - return $json; -} - /** * Returns a list of available plugins formatted as an HTML table * @@ -284,8 +254,8 @@ function getHTMLPluginsTable(array $plugins): string $status = 'Installed'; } else { $status = ''; + name="install-plugin" data-bs-toggle="modal" data-bs-target="#install-user-plugin" + data-record-id="'.htmlspecialchars($plugin['plugin_uri']).'" />' . _("Install now") .''; } $name = 'profile)` : '') : 'Unknown' + ); + $('#plugin-license').text(manifestData.license || 'Unknown'); + $('#plugin-locale').text(manifestData.default_locale || 'Unknown'); + $('#plugin-configuration').html(formatProperty(manifestData.configuration || {})); + $('#plugin-dependencies').html(formatProperty(manifestData.dependencies || {})); + $('#plugin-sudoers').html(formatProperty(manifestData.sudoers || [])); + $('#plugin-user-name').html(manifestData.user_nonprivileged.name || {}); + console.log(manifestData); + } +}); + +function formatProperty(prop) { + if (Array.isArray(prop)) { + if (typeof prop[0] === 'object') { + return prop.map(item => { + return Object.entries(item) + .map(([key, value]) => `${key}: ${value}`) + .join('
'); + }).join('

'); + } + return prop.map(line => `${line}
`).join(''); + } + if (typeof prop === 'object') { + return Object.entries(prop) + .map(([key, value]) => `${key}: ${value}`) + .join('
'); + } + return prop || 'None'; +} + $(document).ready(function(){ $("#PanelManual").hide(); $('.ip_address').mask('0ZZ.0ZZ.0ZZ.0ZZ', { @@ -507,7 +549,6 @@ $('#wg-upload,#wg-manual').on('click', function (e) { } }); -// Add the following code if you want the name of the file appear on select $(".custom-file-input").on("change", function() { var fileName = $(this).val().split("\\").pop(); $(this).siblings(".custom-file-label").addClass("selected").html(fileName); From c3968ba42e62f14ab45c5850091cfd8c66d622a5 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 21 Dec 2024 22:40:55 -0800 Subject: [PATCH 11/49] Encapsulate plugin related functions in PluginInstaller class --- includes/system.php | 129 +------------------------ src/RaspAP/Plugins/PluginInstaller.php | 123 ++++++++++++++++++++++- 2 files changed, 124 insertions(+), 128 deletions(-) diff --git a/includes/system.php b/includes/system.php index b0f7e21d..6ef6b74e 100755 --- a/includes/system.php +++ b/includes/system.php @@ -9,6 +9,7 @@ require_once 'config.php'; function DisplaySystem(&$extraFooterScripts) { $status = new \RaspAP\Messages\StatusMessage; + $pluginInstaller = \RaspAP\Plugins\PluginInstaller::getInstance(); if (isset($_POST['SaveLanguage'])) { if (isset($_POST['locale'])) { @@ -116,8 +117,8 @@ function DisplaySystem(&$extraFooterScripts) $extraFooterScripts[] = array('src'=>'app/js/huebee.js', 'defer'=>false); $logLimit = isset($_SESSION['log_limit']) ? $_SESSION['log_limit'] : RASPI_LOG_SIZE_LIMIT; - $plugins = getUserPlugins(); - $pluginsTable = getHTMLPluginsTable($plugins); + $plugins = $pluginInstaller->getUserPlugins(); + $pluginsTable = $pluginInstaller->getHTMLPluginsTable($plugins); echo renderTemplate("system", compact( "arrLocales", @@ -146,130 +147,6 @@ function DisplaySystem(&$extraFooterScripts) )); } -/** - * Returns user plugin details from associated manifest.json files - * - * @return array $plugins - */ -function getUserPlugins() -{ - $pluginInstaller = \RaspAP\Plugins\PluginInstaller::getInstance(); - $installedPlugins = $pluginInstaller->getPlugins(); - - try { - $submodules = getSubmodules(RASPI_PLUGINS_URL); - $plugins = []; - foreach ($submodules as $submodule) { - $manifestUrl = $submodule['url'] .'/blob/master/manifest.json?raw=true'; - $manifest = $pluginInstaller->getPluginManifest($manifestUrl); - - if ($manifest) { - $namespace = $manifest['namespace'] ?? ''; - $installed = false; - - foreach ($installedPlugins as $plugin) { - if (str_contains($plugin, $namespace)) { - $installed = true; - break; - } - } - - $plugins[] = [ - 'version' => $manifest['version'] ?? 'unknown', - 'name' => $manifest['name'] ?? 'unknown', - 'description' => $manifest['description'] ?? 'No description provided', - 'plugin_uri' => $manifest['plugin_uri'] ?? $submodule['url'], - 'namespace' => $namespace, - 'fa-icon' => $manifest['icon'] ?? 'fas fa-plug', - 'installed' => $installed - ]; - } - } - return $plugins; - } catch (Exception $e) { - echo "An error occured: " .$e->getMessage(); - } -} - -/** - * Returns git submodules for the specified repository - * - * @param string $repoURL - * @return array $submodules - */ -function getSubmodules(string $repoUrl): array -{ - $gitmodulesUrl = $repoUrl . '/refs/heads/master/.gitmodules'; - $gitmodulesContent = file_get_contents($gitmodulesUrl); - - if ($gitmodulesContent === false) { - throw new Exception('Unable to fetch .gitmodules file from the repository'); - } - - $submodules = []; - $lines = explode("\n", $gitmodulesContent); - $currentSubmodule = []; - - foreach ($lines as $line) { - $line = trim($line); - - if (strpos($line, '[submodule "') === 0) { - if (!empty($currentSubmodule)) { - $submodules[] = $currentSubmodule; - } - $currentSubmodule = []; - } elseif (strpos($line, 'path = ') === 0) { - $currentSubmodule['path'] = substr($line, strlen('path = ')); - } elseif (strpos($line, 'url = ') === 0) { - $currentSubmodule['url'] = substr($line, strlen('url = ')); - } - } - - if (!empty($currentSubmodule)) { - $submodules[] = $currentSubmodule; - } - - return $submodules; -} - -/** - * Returns a list of available plugins formatted as an HTML table - * - * @param array $plugins - * @return string $html - */ -function getHTMLPluginsTable(array $plugins): string -{ - $html = ''; - $html .= ''; - $html .= ''; - $html .= ''; - $html .= ''; - $html .= ''; - $html .= ''; - - foreach ($plugins as $plugin) { - $installed = $plugin['installed']; - if ($installed === true ) { - $status = 'Installed'; - } else { - $status = ''; - } - $name = '' - . htmlspecialchars($plugin['name']). ''; - $html .= ''; - $html .= ''; - $html .= ''; - $html .= ''; - } - $html .= '
NameVersionDescription
' .$name. '' .htmlspecialchars($plugin['version']). '' .htmlspecialchars($plugin['description']). '' .$status. '
'; - return $html; -} - function getMemStatus($memused): array { $memused_status = "primary"; diff --git a/src/RaspAP/Plugins/PluginInstaller.php b/src/RaspAP/Plugins/PluginInstaller.php index 20188b98..f6a0f96f 100644 --- a/src/RaspAP/Plugins/PluginInstaller.php +++ b/src/RaspAP/Plugins/PluginInstaller.php @@ -31,8 +31,45 @@ class PluginInstaller } /** - * Decodes a plugin's associated manifest JSON. - * Returns an array of key-value pairs + * Returns user plugin details from associated manifest.json files + * + * @return array $plugins + */ + public function getUserPlugins() + { + $installedPlugins = $this->getPlugins(); + + try { + $submodules = $this->getSubmodules(RASPI_PLUGINS_URL); + $plugins = []; + foreach ($submodules as $submodule) { + $manifestUrl = $submodule['url'] .'/blob/master/manifest.json?raw=true'; + $manifest = $this->getPluginManifest($manifestUrl); + + if ($manifest) { + $installed = false; + + foreach ($installedPlugins as $plugin) { + if (str_contains($plugin, $plugins['manifest']['namespace'])) { + $installed = true; + break; + } + } + + $plugins[] = [ + 'manifest' => $manifest, + 'installed' => $installed + ]; + } + } + return $plugins; + } catch (Exception $e) { + echo "An error occured: " .$e->getMessage(); + } + } + + /** + * Retrieves a plugin's associated manifest JSON * * @param string $url * @return array $json @@ -60,6 +97,88 @@ class PluginInstaller return $json; } + /** + * Returns git submodules for the specified repository + * + * @param string $repoURL + * @return array $submodules + */ + public function getSubmodules(string $repoUrl): array + { + $gitmodulesUrl = $repoUrl . '/refs/heads/master/.gitmodules'; + $gitmodulesContent = file_get_contents($gitmodulesUrl); + + if ($gitmodulesContent === false) { + throw new Exception('Unable to fetch .gitmodules file from the repository'); + } + + $submodules = []; + $lines = explode("\n", $gitmodulesContent); + $currentSubmodule = []; + + foreach ($lines as $line) { + $line = trim($line); + + if (strpos($line, '[submodule "') === 0) { + if (!empty($currentSubmodule)) { + $submodules[] = $currentSubmodule; + } + $currentSubmodule = []; + } elseif (strpos($line, 'path = ') === 0) { + $currentSubmodule['path'] = substr($line, strlen('path = ')); + } elseif (strpos($line, 'url = ') === 0) { + $currentSubmodule['url'] = substr($line, strlen('url = ')); + } + } + + if (!empty($currentSubmodule)) { + $submodules[] = $currentSubmodule; + } + + return $submodules; + } + + /** + * Returns a list of available plugins formatted as an HTML table + * + * @param array $plugins + * @return string $html + */ + public function getHTMLPluginsTable(array $plugins): string + { + $html = ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + + foreach ($plugins as $plugin) { + + $manifest = htmlspecialchars(json_encode($plugin['manifest']), ENT_QUOTES, 'UTF-8'); + $installed = $plugin['installed']; + if ($installed === true ) { + $status = 'Installed'; + } else { + $button = ''; + } + $name = '' + . htmlspecialchars($plugin['manifest']['name']). ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + $html .= '
NameVersionDescription
' .$name. '' .htmlspecialchars($plugin['manifest']['version']). '' .htmlspecialchars($plugin['manifest']['description']). '' .$button. '
'; + return $html; + } + + /** Returns an array of installed plugins in pluginPath * * @return array $plugins From 2cb66660c5d810aac843eb079515e12afa921373 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 21 Dec 2024 22:42:34 -0800 Subject: [PATCH 12/49] Create modal install-user-plugin dialog, update template text --- ajax/plugins/get_manifest.php | 32 ------------------- templates/system.php | 59 +++++++++++++++++++++++++++++++++++ templates/system/plugins.php | 6 ++-- 3 files changed, 61 insertions(+), 36 deletions(-) delete mode 100755 ajax/plugins/get_manifest.php diff --git a/ajax/plugins/get_manifest.php b/ajax/plugins/get_manifest.php deleted file mode 100755 index 70b18c9f..00000000 --- a/ajax/plugins/get_manifest.php +++ /dev/null @@ -1,32 +0,0 @@ -getPluginManifest($manifestUrl); - if ($manifest) { - echo json_encode($manifest); - } else { - http_response_code(404); - echo json_encode(['error' => 'Plugin manifest not found']); - } - } catch (Exception $e) { - http_response_code(500); - echo json_encode(['error' => 'An unexpected error occurred']); - } -} else { - http_response_code(400); - echo json_encode(['error' => 'Plugin URI is required']); - exit; -} - diff --git a/templates/system.php b/templates/system.php index 4c17bcf2..dcc4679d 100755 --- a/templates/system.php +++ b/templates/system.php @@ -107,3 +107,62 @@ + + + diff --git a/templates/system/plugins.php b/templates/system/plugins.php index 2595198d..71677d19 100644 --- a/templates/system/plugins.php +++ b/templates/system/plugins.php @@ -3,19 +3,17 @@

-
-
- Install to download and activate a plugin from the list. Uninstall removes an existing plugin."); ?> +
+ Details for more information and to install a plugin. Uninstall removes an existing plugin."); ?>
-
From e5987a6b591a8b0e537642719233672632372b14 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 22 Dec 2024 09:27:40 -0800 Subject: [PATCH 13/49] Update w/ link to plugin_uri --- app/js/custom.js | 5 ++++- templates/system.php | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/js/custom.js b/app/js/custom.js index 354f9cd6..1b73de31 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -473,6 +473,10 @@ $('#install-user-plugin').on('shown.bs.modal', function (e) { var manifestData = button.data('plugin-manifest'); if (manifestData) { + $('#plugin-uri').html(manifestData.plugin_uri + ? `${manifestData.plugin_uri}` + : 'Unknown' + ); $('#plugin-icon').attr('class', `${manifestData.icon || 'fas fa-plug'} link-secondary h5 me-2`); $('#plugin-name').text(manifestData.name || 'Unknown'); $('#plugin-version').text(manifestData.version || 'Unknown'); @@ -487,7 +491,6 @@ $('#install-user-plugin').on('shown.bs.modal', function (e) { $('#plugin-dependencies').html(formatProperty(manifestData.dependencies || {})); $('#plugin-sudoers').html(formatProperty(manifestData.sudoers || [])); $('#plugin-user-name').html(manifestData.user_nonprivileged.name || {}); - console.log(manifestData); } }); diff --git a/templates/system.php b/templates/system.php index dcc4679d..bee431d8 100755 --- a/templates/system.php +++ b/templates/system.php @@ -112,7 +112,7 @@ From 92ba7df9c615c658ed970d190620401e82282386 Mon Sep 17 00:00:00 2001 From: billz Date: Wed, 25 Dec 2024 19:03:41 -0800 Subject: [PATCH 22/49] Create plugin install event handler --- app/js/custom.js | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/app/js/custom.js b/app/js/custom.js index 1b73de31..30f397b5 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -490,10 +490,42 @@ $('#install-user-plugin').on('shown.bs.modal', function (e) { $('#plugin-configuration').html(formatProperty(manifestData.configuration || {})); $('#plugin-dependencies').html(formatProperty(manifestData.dependencies || {})); $('#plugin-sudoers').html(formatProperty(manifestData.sudoers || [])); - $('#plugin-user-name').html(manifestData.user_nonprivileged.name || {}); + $('#plugin-user-name').html(manifestData.user_nonprivileged.name || 'None'); } }); +$('#js-install-plugin-confirm').on('click', function (e) { + var progressText = $('#js-install-plugin-confirm').attr('data-message'); + var successHtml = $('#plugin-install-message').attr('data-message'); + var closeHtml = $('#js-system-reset-cancel').attr('data-message'); + var pluginUri = $('#plugin-uri a').attr('href'); + var pluginVersion = $('#plugin-version').text(); + var csrfToken = $('meta[name=csrf_token]').attr('content'); + + $("#install-user-plugin").modal('hide'); + $("#install-plugin-progress").modal('show'); + + $.post('ajax/plugins/do_plugin_install.php?',{'plugin_uri': pluginUri, 'plugin_version': pluginVersion, 'csrf_token': csrfToken},function(data){ + setTimeout(function(){ + response = JSON.parse(data); + if(response === true) { + $('#plugin-install-message').text(successHtml); + $('#plugin-install-message').find('i').removeClass('fas fa-cog fa-spin link-secondary').addClass('fas fa-check'); + $('#js-install-plugin-ok').removeAttr("disabled"); + } else { + $('#plugin-install-message').text('An error occurred installing the plugin.'); + $('#plugin-install-message').find('i').removeClass('fas fa-cog fa-spin link-secondary'); + $('#js-install-plugin-ok').removeAttr("disabled"); + } + },300); + }); +}); + +$('#js-install-plugin-ok').on('click', function (e) { + $("#install-plugin-progress").modal('hide'); + window.location.reload(); +}); + function formatProperty(prop) { if (Array.isArray(prop)) { if (typeof prop[0] === 'object') { From ad3669522474cd9fc2370d5d682c280ed8cb48b1 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 26 Dec 2024 08:04:16 -0800 Subject: [PATCH 23/49] Set/read plugin-installed flag --- app/js/custom.js | 81 +++++++++++++++----------- installers/plugin_helper.sh | 2 +- src/RaspAP/Plugins/PluginInstaller.php | 2 +- templates/system.php | 2 +- 4 files changed, 49 insertions(+), 38 deletions(-) diff --git a/app/js/custom.js b/app/js/custom.js index 30f397b5..d68c94f1 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -471,54 +471,65 @@ $('#js-sys-reboot, #js-sys-shutdown').on('click', function (e) { $('#install-user-plugin').on('shown.bs.modal', function (e) { var button = $(e.relatedTarget); var manifestData = button.data('plugin-manifest'); + var installed = button.data('plugin-installed'); - if (manifestData) { - $('#plugin-uri').html(manifestData.plugin_uri - ? `${manifestData.plugin_uri}` - : 'Unknown' + if (manifestData) { + $('#plugin-uri').html(manifestData.plugin_uri + ? `${manifestData.plugin_uri}` + : 'Unknown' + ); + $('#plugin-icon').attr('class', `${manifestData.icon || 'fas fa-plug'} link-secondary h5 me-2`); + $('#plugin-name').text(manifestData.name || 'Unknown'); + $('#plugin-version').text(manifestData.version || 'Unknown'); + $('#plugin-description').text(manifestData.description || 'No description provided'); + $('#plugin-author').html(manifestData.author + ? manifestData.author + (manifestData.author_uri + ? ` (profile)` : '') : 'Unknown' ); - $('#plugin-icon').attr('class', `${manifestData.icon || 'fas fa-plug'} link-secondary h5 me-2`); - $('#plugin-name').text(manifestData.name || 'Unknown'); - $('#plugin-version').text(manifestData.version || 'Unknown'); - $('#plugin-description').text(manifestData.description || 'No description provided'); - $('#plugin-author').html(manifestData.author - ? manifestData.author + (manifestData.author_uri - ? ` (profile)` : '') : 'Unknown' - ); - $('#plugin-license').text(manifestData.license || 'Unknown'); - $('#plugin-locale').text(manifestData.default_locale || 'Unknown'); - $('#plugin-configuration').html(formatProperty(manifestData.configuration || {})); - $('#plugin-dependencies').html(formatProperty(manifestData.dependencies || {})); - $('#plugin-sudoers').html(formatProperty(manifestData.sudoers || [])); - $('#plugin-user-name').html(manifestData.user_nonprivileged.name || 'None'); - } + $('#plugin-license').text(manifestData.license || 'Unknown'); + $('#plugin-locale').text(manifestData.default_locale || 'Unknown'); + $('#plugin-configuration').html(formatProperty(manifestData.configuration || {})); + $('#plugin-dependencies').html(formatProperty(manifestData.dependencies || {})); + $('#plugin-sudoers').html(formatProperty(manifestData.sudoers || [])); + $('#plugin-user-name').html(manifestData.user_nonprivileged.name || 'None'); + } + if (installed) { + $('#js-install-plugin-confirm').html('OK'); + } else { + $('#js-install-plugin-confirm').html('Install now'); + } }); $('#js-install-plugin-confirm').on('click', function (e) { var progressText = $('#js-install-plugin-confirm').attr('data-message'); var successHtml = $('#plugin-install-message').attr('data-message'); - var closeHtml = $('#js-system-reset-cancel').attr('data-message'); var pluginUri = $('#plugin-uri a').attr('href'); var pluginVersion = $('#plugin-version').text(); var csrfToken = $('meta[name=csrf_token]').attr('content'); $("#install-user-plugin").modal('hide'); - $("#install-plugin-progress").modal('show'); - $.post('ajax/plugins/do_plugin_install.php?',{'plugin_uri': pluginUri, 'plugin_version': pluginVersion, 'csrf_token': csrfToken},function(data){ - setTimeout(function(){ - response = JSON.parse(data); - if(response === true) { - $('#plugin-install-message').text(successHtml); - $('#plugin-install-message').find('i').removeClass('fas fa-cog fa-spin link-secondary').addClass('fas fa-check'); - $('#js-install-plugin-ok').removeAttr("disabled"); - } else { - $('#plugin-install-message').text('An error occurred installing the plugin.'); - $('#plugin-install-message').find('i').removeClass('fas fa-cog fa-spin link-secondary'); - $('#js-install-plugin-ok').removeAttr("disabled"); - } - },300); - }); + if ($('#js-install-plugin-confirm').text() === 'Install now') { + $("#install-plugin-progress").modal('show'); + + $.post('ajax/plugins/do_plugin_install.php?',{'plugin_uri': pluginUri, + 'plugin_version': pluginVersion, 'csrf_token': csrfToken},function(data){ + setTimeout(function(){ + response = JSON.parse(data); + if (response === true) { + $('#plugin-install-message').contents().first().replaceWith(successHtml); + $('#plugin-install-message').find('i') + .removeClass('fas fa-cog fa-spin link-secondary') + .addClass('fas fa-check'); + $('#js-install-plugin-ok').removeAttr("disabled"); + } else { + $('#plugin-install-message').contents().first().replaceWith('An error occurred installing the plugin.'); + $('#plugin-install-message').find('i').removeClass('fas fa-cog fa-spin link-secondary'); + $('#js-install-plugin-ok').removeAttr("disabled"); + } + },200); + }); + } }); $('#js-install-plugin-ok').on('click', function (e) { diff --git a/installers/plugin_helper.sh b/installers/plugin_helper.sh index 08c424a3..2d946e36 100755 --- a/installers/plugin_helper.sh +++ b/installers/plugin_helper.sh @@ -1,7 +1,7 @@ #!/bin/bash # # PluginInstaller helper for RaspAP -# # @author billz +# @author billz # license: GNU General Public License v3.0 # Exit on error diff --git a/src/RaspAP/Plugins/PluginInstaller.php b/src/RaspAP/Plugins/PluginInstaller.php index 3157e3fe..b5ba43af 100644 --- a/src/RaspAP/Plugins/PluginInstaller.php +++ b/src/RaspAP/Plugins/PluginInstaller.php @@ -428,7 +428,7 @@ class PluginInstaller if ($installed === true ) { $button = ''; + data-plugin-manifest="' .$manifest. '" data-plugin-installed="' .$installed. '"> ' . _("Installed") .''; } else { $button = ' - + From 361a2f75311cfa1c9b9de0cbf942a1c43ba89cf2 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 26 Dec 2024 09:17:12 -0800 Subject: [PATCH 24/49] Use text() instead of replaceWith() --- app/js/custom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/js/custom.js b/app/js/custom.js index d68c94f1..396313e6 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -517,7 +517,7 @@ $('#js-install-plugin-confirm').on('click', function (e) { setTimeout(function(){ response = JSON.parse(data); if (response === true) { - $('#plugin-install-message').contents().first().replaceWith(successHtml); + $('#plugin-install-message').contents().first().text(successHtml); $('#plugin-install-message').find('i') .removeClass('fas fa-cog fa-spin link-secondary') .addClass('fas fa-check'); From 3c61954971ba4c9f55a1191c738940bd9a4d558d Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 26 Dec 2024 09:17:33 -0800 Subject: [PATCH 25/49] Minor: update label --- templates/system/plugins.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/system/plugins.php b/templates/system/plugins.php index 71677d19..194e161e 100644 --- a/templates/system/plugins.php +++ b/templates/system/plugins.php @@ -9,7 +9,7 @@
- Details for more information and to install a plugin. Uninstall removes an existing plugin."); ?> + Details for more information and to install a plugin."); ?>
From 9bb2075b77f1141455c66362a6a514f0f17817b8 Mon Sep 17 00:00:00 2001 From: billz Date: Thu, 26 Dec 2024 09:41:52 -0800 Subject: [PATCH 26/49] Add strings to en_US locale, update template --- app/js/custom.js | 3 +- locale/en_US/LC_MESSAGES/messages.mo | Bin 42621 -> 43927 bytes locale/en_US/LC_MESSAGES/messages.po | 63 +++++++++++++++++++++++++++ templates/system.php | 2 +- 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/app/js/custom.js b/app/js/custom.js index 396313e6..eac5e17b 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -503,6 +503,7 @@ $('#install-user-plugin').on('shown.bs.modal', function (e) { $('#js-install-plugin-confirm').on('click', function (e) { var progressText = $('#js-install-plugin-confirm').attr('data-message'); var successHtml = $('#plugin-install-message').attr('data-message'); + var successText = $('
').text(successHtml).text(); var pluginUri = $('#plugin-uri a').attr('href'); var pluginVersion = $('#plugin-version').text(); var csrfToken = $('meta[name=csrf_token]').attr('content'); @@ -517,7 +518,7 @@ $('#js-install-plugin-confirm').on('click', function (e) { setTimeout(function(){ response = JSON.parse(data); if (response === true) { - $('#plugin-install-message').contents().first().text(successHtml); + $('#plugin-install-message').contents().first().replaceWith(successText); $('#plugin-install-message').find('i') .removeClass('fas fa-cog fa-spin link-secondary') .addClass('fas fa-check'); diff --git a/locale/en_US/LC_MESSAGES/messages.mo b/locale/en_US/LC_MESSAGES/messages.mo index fd8a0a6552a77a33ce2529228bd94e52e68d9042..23d44ab78cdf1a7d985ab1637c4cea170dd46994 100644 GIT binary patch delta 12251 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 NsJq|){~q&w{tu+gcD?`r diff --git a/locale/en_US/LC_MESSAGES/messages.po b/locale/en_US/LC_MESSAGES/messages.po index 95fa1281..b6b6e88b 100644 --- a/locale/en_US/LC_MESSAGES/messages.po +++ b/locale/en_US/LC_MESSAGES/messages.po @@ -920,6 +920,69 @@ msgstr "Changing log limit size to %s KB" msgid "Information provided by raspap.sysinfo" msgstr "Information provided by raspap.sysinfo" +msgid "The following user plugins are available to extend RaspAP's functionality." +msgstr "The following user plugins are available to extend RaspAP's functionality." + +msgid "Choose Details for more information and to install a plugin." +msgstr "Choose Details for more information and to install a plugin." + +msgid "Plugins" +msgstr "Plugins" + +msgid "Plugin details" +msgstr "Plugin details" + +msgid "Name" +msgstr "Name" + +msgid "Version" +msgstr "Version" + +msgid "Description" +msgstr "Description" + +msgid "Plugin source" +msgstr "Plugin source" + +msgid "Author" +msgstr "Author" + +msgid "License" +msgstr "License" + +msgid "Language locale" +msgstr "Language locale" + +msgid "Configuration files" +msgstr "Configuration files" + +msgid "Dependencies" +msgstr "Dependencies" + +msgid "Permissions" +msgstr "Permissions" + +msgid "Non-privileged users" +msgstr "Non-privileged users" + +msgid "Install now" +msgstr "Install now" + +msgid "Installing plugin" +msgstr "Installing plugin" + +msgid "Plugin installation in progress..." +msgstr "Plugin installation in progress..." + +msgid "Plugin install completed." +msgstr "Plugin install completed." + +msgid "Details" +msgstr "Details" + +msgid "Installed" +msgstr "Installed" + #: includes/data_usage.php msgid "Data usage" msgstr "Data usage" diff --git a/templates/system.php b/templates/system.php index cd4dbaf1..1891bda0 100755 --- a/templates/system.php +++ b/templates/system.php @@ -164,7 +164,7 @@
From 7dcc177424a7088991268e52da5caedec00e190b Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 4 Jan 2025 09:33:01 -0800 Subject: [PATCH 27/49] Initial commit --- ajax/plugins/do_plugin_install.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100755 ajax/plugins/do_plugin_install.php diff --git a/ajax/plugins/do_plugin_install.php b/ajax/plugins/do_plugin_install.php new file mode 100755 index 00000000..08bf8a40 --- /dev/null +++ b/ajax/plugins/do_plugin_install.php @@ -0,0 +1,29 @@ +installPlugin($archiveUrl); + echo json_encode($return); + + } catch (Exception $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + } +} else { + http_response_code(400); + echo json_encode(['error' => 'Plugin URI and version are required']); + exit; +} + From 3e91f50966c94693b42120352e6cca4b5c2b9385 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 5 Jan 2025 02:12:45 -0800 Subject: [PATCH 28/49] Create callbackTimeout() --- includes/functions.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/includes/functions.php b/includes/functions.php index 3099defc..02cce1c6 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -1036,3 +1036,25 @@ function renderStatus($hostapd_led, $hostapd_status, $memused_led, $memused, $cp $interval) { + throw new \Exception('Operation timed out'); + } + + return $result; +} + From ff7e674b2eaffba291d43030b786496a653073c8 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 5 Jan 2025 02:14:16 -0800 Subject: [PATCH 29/49] Use callbackTimeout w/ getUserPlugins(), handle err if >2000ms --- includes/system.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/includes/system.php b/includes/system.php index 6ef6b74e..1bf9c3b5 100755 --- a/includes/system.php +++ b/includes/system.php @@ -117,8 +117,20 @@ function DisplaySystem(&$extraFooterScripts) $extraFooterScripts[] = array('src'=>'app/js/huebee.js', 'defer'=>false); $logLimit = isset($_SESSION['log_limit']) ? $_SESSION['log_limit'] : RASPI_LOG_SIZE_LIMIT; - $plugins = $pluginInstaller->getUserPlugins(); - $pluginsTable = $pluginInstaller->getHTMLPluginsTable($plugins); + try { + $plugins = callbackTimeout(fn() => $pluginInstaller->getUserPlugins(), 2000); + $pluginsTable = $pluginInstaller->getHTMLPluginsTable($plugins); + } catch (\Exception $e) { + $errResponse = sprintf( + '
%s: %s. %s %s.
', + _('Error'), + _('Unable to load plugins'), + _('Reload'), + _('and try again') + ); + $errResponse.= ''; @@ -434,14 +377,23 @@ class PluginInstaller name="install-plugin" data-bs-toggle="modal" data-bs-target="#install-user-plugin" data-plugin-manifest="' .$manifest. '"> ' . _("Details") .''; } - $name = '' - . htmlspecialchars($plugin['manifest']['name']). ''; + . $nameText. ''; + + $version = htmlspecialchars($manifestData['version'] ?? 'N/A'); + $description = htmlspecialchars($manifestData['description'] ?? 'No description available'); + $html .= '' .$name. ''; - $html .= '' .htmlspecialchars($plugin['manifest']['version']). ''; - $html .= '' .htmlspecialchars($plugin['manifest']['description']). ''; - $html .= '' .$button. ''; + $html .= '' .$version. ''; + $html .= '' .$description. ''; + $html .= '' .$button. ''; } $html .= ''; return $html; From 36c3e5036fd103688cfc6c2b956120ef14d0402a Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 25 Jan 2025 09:42:17 -0800 Subject: [PATCH 37/49] Ignore compiled .mo files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 12888e08..18aba9ae 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ plugins/ rootCA.pem vendor .env +locale/**/*.mo From 75bb3e4a3451401597f7f9ae921a9b398e1690c1 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 25 Jan 2025 09:48:58 -0800 Subject: [PATCH 38/49] Resolve conflict: update .mo from master + ignore --- locale/en_US/LC_MESSAGES/messages.mo | Bin 44147 -> 43019 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/locale/en_US/LC_MESSAGES/messages.mo b/locale/en_US/LC_MESSAGES/messages.mo index 5706506b09ab0bc1f31855a381f403e9f90179fa..d1c38e1f3fe2e4aea23d38c09db8dde0ac69c255 100644 GIT binary patch delta 11525 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 delta 12553 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 Date: Sun, 26 Jan 2025 01:23:42 -0800 Subject: [PATCH 39/49] Catch HTTP 442 unprocessable content response --- ajax/plugins/do_plugin_install.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ajax/plugins/do_plugin_install.php b/ajax/plugins/do_plugin_install.php index 010cc535..f05ccb75 100755 --- a/ajax/plugins/do_plugin_install.php +++ b/ajax/plugins/do_plugin_install.php @@ -17,13 +17,12 @@ if (isset($plugin_uri) && isset($plugin_version)) { try { $return = $pluginInstaller->installPlugin($archiveUrl); echo json_encode($return); - } catch (Exception $e) { - http_response_code(500); + http_response_code(422); // Unprocessable Content echo json_encode(['error' => $e->getMessage()]); } } else { - http_response_code(400); + http_response_code(400); // Bad Request echo json_encode(['error' => 'Plugin URI and version are required']); exit; } From 47c509277c5fc9eb0d4de27f9c2a38d4e7eb5746 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 26 Jan 2025 01:25:12 -0800 Subject: [PATCH 40/49] Add checks for empty vars, improve error propagation --- src/RaspAP/Plugins/PluginInstaller.php | 43 ++++++++++++++++++-------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/RaspAP/Plugins/PluginInstaller.php b/src/RaspAP/Plugins/PluginInstaller.php index 976b2c3f..9fcaf11a 100644 --- a/src/RaspAP/Plugins/PluginInstaller.php +++ b/src/RaspAP/Plugins/PluginInstaller.php @@ -84,7 +84,8 @@ class PluginInstaller } return $plugins; } catch (\Exception $e) { - echo "An error occurred: " . $e->getMessage(); + error_log("An error occurred: " . $e->getMessage()); + throw $e; // re-throw to global ExceptionHandler return []; } } @@ -117,9 +118,14 @@ class PluginInstaller * * @param string $archiveUrl * @return boolean + * @throws \Exception */ public function installPlugin($archiveUrl): bool { + $tempFile = null; + $extractDir = null; + $pluginDir = null; + try { list($tempFile, $extractDir, $pluginDir) = $this->getPluginArchive($archiveUrl); @@ -151,18 +157,19 @@ class PluginInstaller } catch (\Exception $e) { //$this->rollback($rollbackStack, $manifest, $pluginDir); + throw new \Exception('Installation step failed: ' . $e->getMessage()); error_log('Plugin installation failed: ' . $e->getMessage()); - return false; } } catch (\Exception $e) { - throw new \Exception('error: ' .$e->getMessage()); + error_log('An error occured: ' .$e->getMessage()); + throw new \Exception( $e->getMessage()); + //throw $e; } finally { - // cleanup tmp files - if (file_exists($tempFile)) { + if (!empty($tempFile) && file_exists($tempFile)) { unlink($tempFile); } - if (is_dir($extractDir)) { + if (!empty($extractDir) && is_dir($extractDir)) { $this->deleteDir($extractDir); } } @@ -295,17 +302,21 @@ class PluginInstaller * * @param string $archiveUrl * @return array + * @throws \Exception */ private function getPluginArchive(string $archiveUrl): array { - try { + $tempFile = ''; + $extractDir = ''; + try { $tempFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('plugin_', true) . '.zip'; $extractDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('plugin_', true); - $data = file_get_contents($archiveUrl); + $data = @file_get_contents($archiveUrl); // suppress PHP warnings for better exception handling if ($data === false) { - throw new \Exception('Failed to download archive.'); + $error = error_get_last(); + throw new \Exception('Failed to download archive: ' . ($error['message'] ?? 'Unknown error')); } file_put_contents($tempFile, $data); @@ -317,19 +328,25 @@ class PluginInstaller $cmd = escapeshellcmd("unzip -o $tempFile -d $extractDir"); $output = shell_exec($cmd); if ($output === null) { - throw new \Exception('Failed to extract archive.'); + throw new \Exception('Failed to extract plugin archive.'); } $extractedDirs = glob($extractDir . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR); if (empty($extractedDirs)) { - throw new \Exception('No directories found in archive.'); + throw new \Exception('No directories found in plugin archive.'); } + $pluginDir = $extractedDirs[0]; return [$tempFile, $extractDir, $pluginDir]; - } catch (\Exception $e) { - throw new \Exception('Error occurred: ' .$e->getMessage()); + if (!empty($tempFile) && file_exists($tempFile)) { + unlink($tempFile); + } + if (!empty($extractDir) && is_dir($extractDir)) { + rmdir($extractDir); + } + throw new \Exception('Error occurred during plugin archive retrieval: ' . $e->getMessage()); } } From e423b7f4d37ce1992647846e712080930472dde9 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 26 Jan 2025 01:31:40 -0800 Subject: [PATCH 41/49] Display diagnostic error message on plugin install fail --- app/js/custom.js | 57 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/app/js/custom.js b/app/js/custom.js index eac5e17b..d0b83762 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -513,22 +513,47 @@ $('#js-install-plugin-confirm').on('click', function (e) { if ($('#js-install-plugin-confirm').text() === 'Install now') { $("#install-plugin-progress").modal('show'); - $.post('ajax/plugins/do_plugin_install.php?',{'plugin_uri': pluginUri, - 'plugin_version': pluginVersion, 'csrf_token': csrfToken},function(data){ - setTimeout(function(){ - response = JSON.parse(data); - if (response === true) { - $('#plugin-install-message').contents().first().replaceWith(successText); - $('#plugin-install-message').find('i') - .removeClass('fas fa-cog fa-spin link-secondary') - .addClass('fas fa-check'); - $('#js-install-plugin-ok').removeAttr("disabled"); - } else { - $('#plugin-install-message').contents().first().replaceWith('An error occurred installing the plugin.'); - $('#plugin-install-message').find('i').removeClass('fas fa-cog fa-spin link-secondary'); - $('#js-install-plugin-ok').removeAttr("disabled"); - } - },200); + $.post( + 'ajax/plugins/do_plugin_install.php', + { + 'plugin_uri': pluginUri, + 'plugin_version': pluginVersion, + 'csrf_token': csrfToken + }, + function (data) { + setTimeout(function () { + response = JSON.parse(data); + if (response === true) { + $('#plugin-install-message').contents().first().replaceWith(successText); + $('#plugin-install-message') + .find('i') + .removeClass('fas fa-cog fa-spin link-secondary') + .addClass('fas fa-check'); + $('#js-install-plugin-ok').removeAttr("disabled"); + } else { + const errorMessage = jsonData.error || 'An unknown error occurred.'; + var errorLog = ''; + $('#plugin-install-message') + .contents() + .first() + .replaceWith('An error occurred installing the plugin:'); + $('#plugin-install-message').append(errorLog); + $('#plugin-install-message').find('i').removeClass('fas fa-cog fa-spin link-secondary'); + $('#js-install-plugin-ok').removeAttr("disabled"); + } + }, 200); + } + ).fail(function (xhr) { + const jsonData = JSON.parse(xhr.responseText); + const errorMessage = jsonData.error || 'An unknown error occurred.'; + $('#plugin-install-message') + .contents() + .first() + .replaceWith('An error occurred installing the plugin:'); + var errorLog = ''; + $('#plugin-install-message').append(errorLog); + $('#plugin-install-message').find('i').removeClass('fas fa-cog fa-spin link-secondary'); + $('#js-install-plugin-ok').removeAttr("disabled"); }); } }); From a8bd85cc80a2a4fd5e2eedcafac227c161bfa3e3 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 26 Jan 2025 01:32:18 -0800 Subject: [PATCH 42/49] Create textarea.plugin-log class --- app/css/all.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/css/all.css b/app/css/all.css index 7efc2f20..e4c8c56c 100644 --- a/app/css/all.css +++ b/app/css/all.css @@ -332,3 +332,15 @@ button > i.fas { animation: heart 1000ms infinite; } +textarea.plugin-log { + width: 100%; + height: 150px; + resize: none; + border: 1px solid #dee2e6; + border-radius: 0.25rem; + padding: 0.5rem; + background-color: #f8f9fa; + font-family: monospace; + font-size: 0.9rem; +} + From 0b0f4bc06dd3e5b1616ea0554e75b397ca3f4437 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 26 Jan 2025 02:45:47 -0800 Subject: [PATCH 43/49] Update plugin installed check --- src/RaspAP/Plugins/PluginInstaller.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/RaspAP/Plugins/PluginInstaller.php b/src/RaspAP/Plugins/PluginInstaller.php index 9fcaf11a..0da29cf2 100644 --- a/src/RaspAP/Plugins/PluginInstaller.php +++ b/src/RaspAP/Plugins/PluginInstaller.php @@ -67,12 +67,13 @@ class PluginInstaller $installedPlugins = $this->getPlugins(); $plugins = []; + foreach ($manifestData as $pluginManifest) { $installed = false; // Check if the plugin is installed foreach ($installedPlugins as $plugin) { - if (str_contains($plugin, $pluginManifest['namespace'])) { + if (str_contains($plugin, $pluginManifest[0]['namespace'])) { $installed = true; break; } From 5d01fa59b1e47998daf2a036fe393b98fc4ffa66 Mon Sep 17 00:00:00 2001 From: Bill Zimmerman Date: Sun, 26 Jan 2025 11:50:33 +0100 Subject: [PATCH 44/49] Fix for DOM text reinterpreted as HTML Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- app/js/custom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/js/custom.js b/app/js/custom.js index 842494b8..cbf26fa9 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -524,7 +524,7 @@ $('#js-install-plugin-confirm').on('click', function (e) { setTimeout(function () { response = JSON.parse(data); if (response === true) { - $('#plugin-install-message').contents().first().replaceWith(successText); + $('#plugin-install-message').contents().first().text(successText); $('#plugin-install-message') .find('i') .removeClass('fas fa-cog fa-spin link-secondary') From 538e37ceb707fd370d12fbb8012c8987a5a80252 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 27 Jan 2025 04:29:07 -0800 Subject: [PATCH 45/49] Add --recurse-submodules to git clone operation --- installers/common.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/installers/common.sh b/installers/common.sh index bd78ef00..3c7f034e 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -583,14 +583,14 @@ function _download_latest_files() { if [ "$repo" == "RaspAP/raspap-insiders" ]; then if [ -n "$username" ] && [ -n "$acctoken" ]; then insiders_source_url="https://${username}:${acctoken}@github.com/$repo" - git clone --branch $branch --depth 1 -c advice.detachedHead=false $insiders_source_url $source_dir || clone=false + git clone --branch $branch --depth 1 --recurse-submodules -c advice.detachedHead=false $insiders_source_url $source_dir || clone=false else _install_status 3 echo "Insiders please read this: https://docs.raspap.com/insiders/#authentication" fi fi if [ -z "$insiders_source_url" ]; then - git clone --branch $branch --depth 1 -c advice.detachedHead=false $git_source_url $source_dir || clone=false + git clone --branch $branch --depth 1 --recurse-submodules -c advice.detachedHead=false $git_source_url $source_dir || clone=false fi if [ "$clone" = false ]; then _install_status 1 "Unable to download files from GitHub" From 7def2d6da1c89407a088bb0dbc119788a3ea9953 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 27 Jan 2025 04:43:01 -0800 Subject: [PATCH 46/49] Remove plugins/ to allow submodule usage --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 18aba9ae..a0642d6e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ node_modules yarn-error.log *.swp includes/config.php -plugins/ rootCA.pem vendor .env From 5fbafeb4552368506ed03c94186cbbc8dab395a8 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 27 Jan 2025 04:49:31 -0800 Subject: [PATCH 47/49] Re-add plugins submodule --- plugins | 1 + 1 file changed, 1 insertion(+) create mode 160000 plugins diff --git a/plugins b/plugins new file mode 160000 index 00000000..825f74ed --- /dev/null +++ b/plugins @@ -0,0 +1 @@ +Subproject commit 825f74edf49ae34705deb4625d3f3c571dc69bda From 0efbe2b3260bf4e5d26b4161decb14e7b82fd837 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 4 Feb 2025 23:44:04 -0800 Subject: [PATCH 48/49] Define RASPI_PLUGINS_ENABLED, add condition to template --- config/config.php | 1 + includes/defaults.php | 1 + templates/system.php | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/config/config.php b/config/config.php index 17c40cb4..178081b1 100755 --- a/config/config.php +++ b/config/config.php @@ -63,6 +63,7 @@ define('RASPI_VNSTAT_ENABLED', true); define('RASPI_SYSTEM_ENABLED', true); define('RASPI_MONITOR_ENABLED', false); define('RASPI_RESTAPI_ENABLED', false); +define('RASPI_PLUGINS_ENABLED', true); // Locale settings define('LOCALE_ROOT', 'locale'); diff --git a/includes/defaults.php b/includes/defaults.php index 4ed1860d..b54ec3fa 100755 --- a/includes/defaults.php +++ b/includes/defaults.php @@ -65,6 +65,7 @@ $defaults = [ 'RASPI_SYSTEM_ENABLED' => true, 'RASPI_MONITOR_ENABLED' => false, 'RASPI_RESTAPI_ENABLED' => false, + 'RASPI_PLUGINS_ENABLED' => true, // Locale settings 'LOCALE_ROOT' => 'locale', diff --git a/templates/system.php b/templates/system.php index 1891bda0..7f22be09 100755 --- a/templates/system.php +++ b/templates/system.php @@ -18,7 +18,9 @@ + +
@@ -27,7 +29,9 @@ + +
From 8a8be213f740a887bfee69534fe15c440ae6fee1 Mon Sep 17 00:00:00 2001 From: billz Date: Tue, 4 Feb 2025 23:45:06 -0800 Subject: [PATCH 49/49] Display plugins in monitor mode, suppress details dialog --- src/RaspAP/Plugins/PluginInstaller.php | 2 +- templates/system/plugins.php | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/RaspAP/Plugins/PluginInstaller.php b/src/RaspAP/Plugins/PluginInstaller.php index 0da29cf2..6ff6ebf8 100644 --- a/src/RaspAP/Plugins/PluginInstaller.php +++ b/src/RaspAP/Plugins/PluginInstaller.php @@ -390,7 +390,7 @@ class PluginInstaller $button = ''; - } else { + } elseif (!RASPI_MONITOR_ENABLED) { $button = ''; diff --git a/templates/system/plugins.php b/templates/system/plugins.php index 194e161e..4d875c49 100644 --- a/templates/system/plugins.php +++ b/templates/system/plugins.php @@ -1,19 +1,19 @@

+ +
+
+ - -
-
- -
- Details for more information and to install a plugin."); ?> -
- -
+
+ Details for more information and to install a plugin."); ?>
+ +
+