Option to enable/disable plugin by renaming file + cleanup

new function: getAllPluginsJson
removeComments removed - json doesnt allow for comments anyway
This commit is contained in:
slawkens 2023-02-16 06:57:46 +01:00
parent 3ed9a5d3d8
commit c7ec1f44e9
4 changed files with 129 additions and 76 deletions

View File

@ -23,13 +23,29 @@ if (isset($_REQUEST['uninstall'])) {
} else { } else {
error('Error while uninstalling plugin ' . $uninstall . ': ' . Plugins::getError()); error('Error while uninstalling plugin ' . $uninstall . ': ' . Plugins::getError());
} }
} else if (isset($_FILES["plugin"]["name"])) { }
$file = $_FILES["plugin"]; else if (isset($_REQUEST['enable'])) {
$filename = $file["name"]; $enable = $_REQUEST['enable'];
$tmp_name = $file["tmp_name"]; if (Plugins::enable($enable)) {
$type = $file["type"]; success('Successfully enabled plugin ' . $enable);
} else {
error('Error while enabling plugin ' . $enable . ': ' . Plugins::getError());
}
}
else if (isset($_REQUEST['disable'])) {
$disable = $_REQUEST['disable'];
if (Plugins::disable($disable)) {
success('Successfully disabled plugin ' . $disable);
} else {
error('Error while disabling plugin ' . $disable . ': ' . Plugins::getError());
}
} else if (isset($_FILES['plugin']['name'])) {
$file = $_FILES['plugin'];
$filename = $file['name'];
$tmp_name = $file['tmp_name'];
$type = $file['type'];
$name = explode(".", $filename); $name = explode('.', $filename);
$accepted_types = array('application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed', 'application/octet-stream', 'application/zip-compressed'); $accepted_types = array('application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed', 'application/octet-stream', 'application/zip-compressed');
if (isset($file['error'])) { if (isset($file['error'])) {
@ -90,21 +106,23 @@ if (isset($_REQUEST['uninstall'])) {
} }
$plugins = array(); $plugins = array();
foreach (get_plugins() as $plugin) { foreach (get_plugins(true) as $plugin) {
$string = file_get_contents(BASE . 'plugins/' . $plugin . '.json'); $string = file_get_contents(BASE . 'plugins/' . $plugin . '.json');
$string = Plugins::removeComments($string);
$plugin_info = json_decode($string, true); $plugin_info = json_decode($string, true);
if ($plugin_info == false) { if (!$plugin_info) {
warning('Cannot load plugin info ' . $plugin . '.json'); warning('Cannot load plugin info ' . $plugin . '.json');
} else { } else {
$disabled = (strpos($plugin, 'disabled.') !== false);
$pluginOriginal = ($disabled ? str_replace('disabled.', '', $plugin) : $plugin);
$plugins[] = array( $plugins[] = array(
'name' => isset($plugin_info['name']) ? $plugin_info['name'] : '', 'name' => $plugin_info['name'] ?? '',
'description' => isset($plugin_info['description']) ? $plugin_info['description'] : '', 'description' => $plugin_info['description'] ?? '',
'version' => isset($plugin_info['version']) ? $plugin_info['version'] : '', 'version' => $plugin_info['version'] ?? '',
'author' => isset($plugin_info['author']) ? $plugin_info['author'] : '', 'author' => $plugin_info['author'] ?? '',
'contact' => isset($plugin_info['contact']) ? $plugin_info['contact'] : '', 'contact' => $plugin_info['contact'] ?? '',
'file' => $plugin, 'file' => $pluginOriginal,
'enabled' => !$disabled,
'uninstall' => isset($plugin_info['uninstall']) 'uninstall' => isset($plugin_info['uninstall'])
); );
} }

View File

@ -791,16 +791,21 @@ function get_templates()
* Generates list of installed plugins * Generates list of installed plugins
* @return array $plugins * @return array $plugins
*/ */
function get_plugins() function get_plugins($disabled = false): array
{ {
$ret = array(); $ret = [];
$path = PLUGINS; $path = PLUGINS;
foreach(scandir($path, 0) as $file) { foreach(scandir($path, SCANDIR_SORT_DESCENDING) as $file) {
$file_ext = pathinfo($file, PATHINFO_EXTENSION); $file_ext = pathinfo($file, PATHINFO_EXTENSION);
$file_name = pathinfo($file, PATHINFO_FILENAME); $file_name = pathinfo($file, PATHINFO_FILENAME);
if ($file === '.' || $file === '..' || $file === 'disabled' || $file === 'example.json' || $file_ext !== 'json' || is_dir($path . $file)) if ($file === '.' || $file === '..' || $file === 'example.json' || $file_ext !== 'json' || is_dir($path . $file)) {
continue; continue;
}
if (!$disabled && strpos($file, 'disabled.') !== false) {
continue;
}
$ret[] = str_replace('.json', '', $file_name); $ret[] = str_replace('.json', '', $file_name);
} }

View File

@ -10,7 +10,7 @@
*/ */
defined('MYAAC') or die('Direct access not allowed!'); defined('MYAAC') or die('Direct access not allowed!');
function is_sub_dir($path = NULL, $parent_folder = SITE_PATH) { function is_sub_dir($path = NULL, $parent_folder = BASE) {
//Get directory path minus last folder //Get directory path minus last folder
$dir = dirname($path); $dir = dirname($path);
@ -41,9 +41,9 @@ function is_sub_dir($path = NULL, $parent_folder = SITE_PATH) {
use Composer\Semver\Semver; use Composer\Semver\Semver;
class Plugins { class Plugins {
private static $warnings = array(); private static $warnings = [];
private static $error = null; private static $error = null;
private static $plugin_json = array(); private static $plugin_json = [];
public static function getRoutes() public static function getRoutes()
{ {
@ -56,22 +56,8 @@ class Plugins {
} }
$routes = []; $routes = [];
foreach(get_plugins() as $filename) { foreach(self::getAllPluginsJson() as $plugin) {
$string = file_get_contents(PLUGINS . $filename . '.json'); $warningPreTitle = 'Plugin: ' . $plugin['name'] . ' - ';
$string = self::removeComments($string);
$plugin = json_decode($string, true);
self::$plugin_json = $plugin;
if ($plugin == null) {
self::$warnings[] = 'Cannot load ' . $filename . '.json. File might be not a valid json code.';
continue;
}
if(isset($plugin['enabled']) && !getBoolean($plugin['enabled'])) {
self::$warnings[] = 'Skipping ' . $filename . '... The plugin is disabled.';
continue;
}
$warningPreTitle = 'Plugin: ' . $filename . ' - ';
if (isset($plugin['routes'])) { if (isset($plugin['routes'])) {
foreach ($plugin['routes'] as $_name => $info) { foreach ($plugin['routes'] as $_name => $info) {
@ -161,9 +147,39 @@ class Plugins {
} }
$hooks = []; $hooks = [];
foreach(get_plugins() as $filename) { foreach(self::getAllPluginsJson() as $plugin) {
if (isset($plugin['hooks'])) {
foreach ($plugin['hooks'] as $_name => $info) {
if (defined('HOOK_'. $info['type'])) {
$hook = constant('HOOK_'. $info['type']);
$hooks[] = ['name' => $_name, 'type' => $hook, 'file' => $info['file']];
} else {
self::$warnings[] = 'Plugin: ' . $plugin['name'] . '. Unknown event type: ' . $info['type'];
}
}
}
}
if ($cache->enabled()) {
$cache->set('plugins_hooks', serialize($hooks), 600);
}
return $hooks;
}
public static function getAllPluginsJson($disabled = false)
{
$cache = Cache::getInstance();
if ($cache->enabled()) {
$tmp = '';
if ($cache->fetch('plugins', $tmp)) {
return unserialize($tmp);
}
}
$plugins = [];
foreach (get_plugins($disabled) as $filename) {
$string = file_get_contents(PLUGINS . $filename . '.json'); $string = file_get_contents(PLUGINS . $filename . '.json');
$string = self::removeComments($string);
$plugin = json_decode($string, true); $plugin = json_decode($string, true);
self::$plugin_json = $plugin; self::$plugin_json = $plugin;
if ($plugin == null) { if ($plugin == null) {
@ -176,23 +192,14 @@ class Plugins {
continue; continue;
} }
if (isset($plugin['hooks'])) { $plugins[] = $plugin;
foreach ($plugin['hooks'] as $_name => $info) {
if (defined('HOOK_'. $info['type'])) {
$hook = constant('HOOK_'. $info['type']);
$hooks[] = ['name' => $_name, 'type' => $hook, 'file' => $info['file']];
} else {
self::$warnings[] = 'Plugin: ' . $filename . '. Unknown event type: ' . $info['type'];
}
}
}
} }
if ($cache->enabled()) { if ($cache->enabled()) {
$cache->set('plugins_hooks', serialize($hooks), 600); $cache->set('plugins', serialize($plugins), 600);
} }
return $hooks; return $plugins;
} }
public static function install($file) { public static function install($file) {
@ -235,7 +242,6 @@ class Plugins {
} }
$string = file_get_contents($file_name); $string = file_get_contents($file_name);
$string = self::removeComments($string);
$plugin_json = json_decode($string, true); $plugin_json = json_decode($string, true);
self::$plugin_json = $plugin_json; self::$plugin_json = $plugin_json;
if ($plugin_json == null) { if ($plugin_json == null) {
@ -435,7 +441,35 @@ class Plugins {
return false; return false;
} }
public static function uninstall($plugin_name) public static function enable($pluginFileName): bool
{
return self::enableDisable($pluginFileName, true);
}
public static function disable($pluginFileName): bool
{
return self::enableDisable($pluginFileName, false);
}
private static function enableDisable($pluginFileName, $enable): bool
{
$filenameJson = $pluginFileName . '.json';
$fileExist = is_file(PLUGINS . ($enable ? 'disabled.' : '') . $filenameJson);
if (!$fileExist) {
self::$error = 'Cannot ' . ($enable ? 'enable' : 'disable') . ' plugin: ' . $pluginFileName . '. File does not exist.';
return false;
}
$result = rename(PLUGINS . ($enable ? 'disabled.' : '') . $filenameJson, PLUGINS . ($enable ? '' : 'disabled.') . $filenameJson);
if (!$result) {
self::$error = 'Cannot ' . ($enable ? 'enable' : 'disable') . ' plugin: ' . $pluginFileName . '. Permission problem.';
return false;
}
return true;
}
public static function uninstall($plugin_name): bool
{ {
$filename = BASE . 'plugins/' . $plugin_name . '.json'; $filename = BASE . 'plugins/' . $plugin_name . '.json';
if(!file_exists($filename)) { if(!file_exists($filename)) {
@ -443,9 +477,8 @@ class Plugins {
return false; return false;
} }
$string = file_get_contents($filename); $string = file_get_contents($filename);
$string = self::removeComments($string);
$plugin_info = json_decode($string, true); $plugin_info = json_decode($string, true);
if($plugin_info == false) { if(!$plugin_info) {
self::$error = 'Cannot load plugin info ' . $plugin_name . '.json'; self::$error = 'Cannot load plugin info ' . $plugin_name . '.json';
return false; return false;
} }
@ -527,22 +560,6 @@ class Plugins {
return self::$plugin_json; return self::$plugin_json;
} }
public static function removeComments($string) {
$string = preg_replace('!/\*.*?\*/!s', '', $string);
$string = preg_replace('/\n\s*\n/', "\n", $string);
// Removes multi-line comments and does not create
// a blank line, also treats white spaces/tabs
$string = preg_replace('!^[ \t]*/\*.*?\*/[ \t]*[\r\n]!s', '', $string);
// Removes single line '//' comments, treats blank characters
$string = preg_replace('![ \t]*//.*[ \t]*[\r\n]!', '', $string);
// Strip blank lines
$string = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $string);
return $string;
}
/** /**
* Install menus * Install menus
* Helper function for plugins * Helper function for plugins

View File

@ -6,6 +6,7 @@
<table class="table table-striped table-bordered table-responsive d-md-table" id="tb_plugins"> <table class="table table-striped table-bordered table-responsive d-md-table" id="tb_plugins">
<thead> <thead>
<tr> <tr>
<th>Enabled</th>
<th>Name</th> <th>Name</th>
<th>Version</th> <th>Version</th>
<th>Author</th> <th>Author</th>
@ -16,6 +17,17 @@
<tbody> <tbody>
{% for plugin in plugins %} {% for plugin in plugins %}
<tr> <tr>
<td>
{% if plugin.enabled %}
<a href="?p=plugins&disable={{ plugin.file }}" class="btn btn-success" onclick="return confirm('Are you sure you want to disable plugin {{ plugin.name }}?');" title="Disable">
<i class="fas fa-check"></i> Enabled
</a>
{% else %}
<a href="?p=plugins&enable={{ plugin.file }}" class="btn btn-danger" onclick="return confirm('Are you sure you want to enable plugin {{ plugin.name }}?');" title="Enable">
<i class="fas fa-ban"></i> Disabled
</a>
{% endif %}
</td>
<td><b>{{ plugin.name }}</b><br> <td><b>{{ plugin.name }}</b><br>
<small>{{ plugin.description|raw }}</small> <small>{{ plugin.description|raw }}</small>
</td> </td>
@ -29,7 +41,8 @@
<a href="?p=plugins&uninstall={{ plugin.file }}" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?');" title="Uninstall"> <a href="?p=plugins&uninstall={{ plugin.file }}" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?');" title="Uninstall">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</a> </a>
{% endif %}</td> {% endif %}
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -39,6 +52,6 @@
<script> <script>
$(function () { $(function () {
$('#tb_plugins').DataTable() $('#tb_plugins').DataTable();
}) })
</script> </script>