diff --git a/ajax/plugins/do_plugin_install.php b/ajax/plugins/do_plugin_install.php
new file mode 100755
index 000000000..08bf8a408
--- /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;
+}
+
diff --git a/app/js/custom.js b/app/js/custom.js
index 53ea7ae35..eac5e17bd 100644
--- a/app/js/custom.js
+++ b/app/js/custom.js
@@ -468,6 +468,95 @@ $('#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'
+ );
+ $('#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');
+ }
+ 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 successText = $('
').text(successHtml).text();
+ 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');
+
+ 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);
+ });
+ }
+});
+
+$('#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') {
+ 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 +596,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);
diff --git a/config/config.php b/config/config.php
index 2950632db..afd386a20 100755
--- a/config/config.php
+++ b/config/config.php
@@ -38,6 +38,9 @@
// 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);
diff --git a/includes/footer.php b/includes/footer.php
old mode 100644
new mode 100755
diff --git a/includes/functions.php b/includes/functions.php
index 3099defc2..02cce1c6d 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;
+}
+
diff --git a/includes/restapi.php b/includes/restapi.php
old mode 100644
new mode 100755
diff --git a/includes/system.php b/includes/system.php
index 31feaf798..7167cbd1a 100755
--- a/includes/system.php
+++ b/includes/system.php
@@ -9,6 +9,7 @@
function DisplaySystem(&$extraFooterScripts)
{
$status = new \RaspAP\Messages\StatusMessage;
+ $pluginInstaller = \RaspAP\Plugins\PluginInstaller::getInstance();
if (isset($_POST['SaveLanguage'])) {
if (isset($_POST['locale'])) {
@@ -85,53 +86,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 +117,21 @@ function DisplaySystem(&$extraFooterScripts)
$extraFooterScripts[] = array('src'=>'app/js/huebee.js', 'defer'=>false);
$logLimit = isset($_SESSION['log_limit']) ? $_SESSION['log_limit'] : RASPI_LOG_SIZE_LIMIT;
+ try {
+ $plugins = callbackTimeout(fn() => $pluginInstaller->getUserPlugins(), 3000);
+ $pluginsTable = $pluginInstaller->getHTMLPluginsTable($plugins);
+ } catch (\Exception $e) {
+ $errResponse = sprintf(
+ '
%s: %s. %s %s.
',
+ _('Network error'),
+ _('Unable to load plugins'),
+ _('Reload'),
+ _('and try again')
+ );
+ $errResponse.= '
@@ -105,3 +107,83 @@
+
+