]> jfr.im git - irc/unrealircd/unrealircd-webpanel.git/commitdiff
Make the plugins page actually work (to a degree)
authorValerie Pond <redacted>
Sun, 25 Jun 2023 09:39:48 +0000 (10:39 +0100)
committerValerie Pond <redacted>
Sun, 25 Jun 2023 09:39:48 +0000 (10:39 +0100)
Classes/class-paneluser.php
Classes/class-plugin-git.php
api/plugin.php [new file with mode: 0644]
inc/defines.php
settings/add-plugin.php

index e4c5a7c8e3952392a37365111eec79744010cbca..0a3cf8a9548e10a8a2e476ff15ad4614699b25c9 100644 (file)
@@ -27,6 +27,8 @@ define('PERMISSION_SPAMFILTER_ADD', 'sf_add');
 define('PERMISSION_SPAMFILTER_DEL', 'sf_del'); 
 /** Can rehash servers */
 define('PERMISSION_REHASH', 'rhs');
+/** Can install and uninstall plugins */
+define('PERMISSION_MANAGE_PLUGINS', 'mng_plg');
 /**
  * PanelUser
  * This is the User class for the SQL_Auth plugin
@@ -340,6 +342,7 @@ function get_panel_user_permission_list()
 {
        $list = [
                "Can add/delete/edit Admin Panel users" => PERMISSION_MANAGE_USERS,
+               "Can add/delete/manage plugins" => PERMISSION_MANAGE_PLUGINS,
                "Can ban/kill IRC users" => PERMISSION_BAN_USERS,
                "Can change properties of a user, i.e. vhost, modes and more" => PERMISSION_EDIT_USER,
                "Can change properties of a channel, i.e. topic, modes and more" => PERMISSION_EDIT_CHANNEL,
@@ -352,6 +355,7 @@ function get_panel_user_permission_list()
                "Can remove server ban exceptions" => PERMISSION_BAN_EXCEPTION_DEL,
                "Can add Spamfilter entries" => PERMISSION_SPAMFILTER_ADD,
                "Can remove Spamfilter entries" => PERMISSION_SPAMFILTER_DEL
+
        ];
        Hook::run(HOOKTYPE_USER_PERMISSION_LIST, $list); // so plugin writers can add their own permissions
        return $list;
index 4f091613643424c1e661ac07e5b85b856d0f39c4..232fc2073de6a66e8ffe7ac33f40012afb56eb5d 100644 (file)
@@ -3,28 +3,43 @@
        
        class PluginRepo
        {
-           public $plugins;
+           public $plugins ;
            public $data;
            public $err;
            function __construct($url = DEFAULT_PLUGINS_DIR)
            {
+            global $config;
+            if (!isset($config['third-party-plugins']))
+            {
+                $config['third-party-plugins']['data'] = NULL;
+                $config['third-party-plugins']['timestamp'] = 0;
+            }
+            if (time() - $config['third-party-plugins']['timestamp'] > 200) // Cache for 3.333 minutes lol
+            {
+                // come simba it is taem
+                $curl = curl_init($url);
        
-           
-               // Initialize curl
-               $curl = curl_init($url);
-       
-               // Set the options
-               curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);  // Return the response instead of printing it
-               curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);  // Set the content type to JSON
-               curl_setopt($curl, CURLOPT_USERAGENT, "UnrealIRCd Admin Panel");
-               // Execute the request
-               $response = curl_exec($curl);
-       
-               // Check for errors
-               if ($response === false)
-                   $this->err = curl_error($curl);
-               else
-                   $this->data = json_decode($response, false);
+                // Set the options
+                curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);  // Return the response instead of printing it
+                curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);  // Set the content type to JSON
+                curl_setopt($curl, CURLOPT_USERAGENT, "UnrealIRCd Admin Panel"); // This is Secret Agent UnrealIRCd Admin Panel reporting for doody
+                // Execute the request
+                $response = curl_exec($curl);
+        
+                // Check for errors
+                if ($response === false)
+                    $this->err = curl_error($curl);
+                else
+                {
+                    $this->data = json_decode($response, false);
+                    $config['third-party-plugins']['data'] = $this->data;
+                    $config['third-party-plugins']['timestamp'] = time();
+                    write_config('third-party-plugins');
+                }
+            }
+            else
+                $this->data = $config['third-party-plugins']['data'];
+               
            }
 
 
         {
             if ($installed)
             {   ?>
-                <div class="badge rounded-pill badge-success">Installed ✅</div>
+                <div style="margin-left:40px;" class="badge rounded-pill badge-success">✔ Installed</div>
     <?php   }
             else if (Plugins::plugin_exists($name))
             {
                 ?>
-                    <div class="badge rounded-pill badge-success">Installed ✅</div>
+                    <div style="margin-left:40px;" class="badge rounded-pill badge-success">✔ Installed</div>
                 <?php
             }
         }
 
     public function do_list()
     {
-        ?>
-<div class="row">
-    <?php     
+        global $config;
+        if ($this->err)
+            die("Could not fetch list.\n");
+        global $config;
+            ?>
+               <div class="row">
+            <?php     
         $counter = 0;
 
 
         foreach($this->data as $p)
         {
-            $installed = Plugins::plugin_exists($p->name) ? true : false;
+            $installed = in_array($p->name, $config['plugins']) ? true : false;
 
             if (is_string($p))
                 continue;
             // use a default image if there was none
             $p->icon = $p->icon ?? get_config("base_url")."img/no-image-available.jpg";
                 ?>
-        <!-- Widget for plugins -->
-        <div id="<?php echo $p->name ?>" class="<?php if ($installed) echo "installed " ?>card text-dark bg-light ml-4 mb-3 w-25">
-            <div class="card-header">
-                <div class="font-weight-bold">
-                    <div><h5><img class="mr-3" src="<?php echo $p->icon ?>" height="50" width="55">
-                        <?php echo $p->title; $this->ifInstalledLabel($p->name); ?></h5></div>
+            <!-- Widget for plugins -->
+            <div id="<?php echo $p->name ?>" class="<?php if ($installed) echo "installed" ?> plugin-card card text-dark bg-light ml-4 mb-3 w-25" style="min-width:300px">
+
+                <!-- Card header -->
+                <div class="card-header">
+                    <div class="media">
+                        <img class="align-self-start mr-3" src="<?php echo $p->icon ?>" height="50" width="55">
+                        <div class="media-body">
+                            <div style="position:relative;float:inline-end"><?php echo $this->ifInstalledLabel($p->name); ?></div>
+                            <h4 class="mb-0 mt-0"><?php echo $p->title ?></h4>
+                            <small>By <a href="<?php echo "mailto:$p->email" ?>" target="_blank"><?php echo $p->author ?></a></small>
+                        </div>
+                    </div>
                 </div>
-            </div>
-            <div class="card-body">
-                <h5 class="card-title"><?php echo $p->title ?> <small><code>v<?php echo $p->version ?></code></small></h5>
-                <p class="card-text"><?php echo $p->description ?> </p>
-            </div>
-            <div class="card-footer d-flex justify-content-between align-items-center">
-            <ul class="list-unstyled">
-                <li class="list-item">Author: <a href="<?php echo "mailto:".$p->contact ?? "#" ?>"></li><i><?php echo $p->author ?></i></a>
-            </ul>
-            <div class="btn-group">
-                <div class="btn btn-secondary">More Info</div>
-                <div id="<?php echo $p->name ?>install" class="btn btn-primary">Install</div>
-            </div></div>
-            
-        </div>
-        <?php
-            $counter++;
-            if ($counter >= 3) // only do three per row. WARNING: untested due to not having more than 2 plugins atm...
-            {
-                ?>
+
+                <!-- Card body -->
+                <div class="card-body">
+                    <h6 class="card-title"><?php echo $p->title ?> <small><code>v<?php echo $p->version ?></code></small></h6>
+                    <p class="card-text"><?php echo $p->description ?> </p>
                 </div>
-                <div class="row"><?php
-                $counter = 0;
-            }
+
+                <!-- Card footer -->
+                <div class="card-footer d-flex justify-content-between align-items-center">
+                    <ul class="list-unstyled">
+                        <li class="list-item"><i><a href="<?php echo "mailto:".$p->contact ?? "#" ?>">By <?php echo $p->author ?></i></a></li>
+                    </ul>
+                    <div>
+                        <div id="<?php echo $p->name ?>" class="more-info btn btn-info">More Info</div>
+                        <div id="<?php echo $p->name ?>install" class="btn-install-plugin btn btn-primary">Install</div>
+                    </div>
+                </div>
+            </div>
+            <?php
+                /** only do three per row.
+                 *  WARNING: untested due to not having more than 2 plugins atm...
+                */
+                if ($counter >= 3)
+                {
+                    ?><!-- New row please mr html -->
+                        </div>
+                        <div class="row"> 
+                    <?php
+                    $counter = 0;
+                }
+                $counter++;
 
 
-        }
-        ?></div>
+            }
+            ?>
+        </div>
+        <i>Want to see your plugin listed here? <a href="https://github.com/unrealircd/unrealircd-webpanel-plugins" target="__blank">Make a pull request to our GitHub Repository</a>!</i>
     <?php
     }
 }
diff --git a/api/plugin.php b/api/plugin.php
new file mode 100644 (file)
index 0000000..11a3710
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+
+session_start();
+if (!isset($_SESSION['id']))
+    die("Access denied");
+require_once('common_api.php');
+
+header("Content-type: application/json; charset=utf-8");
+
+if (!current_user_can(PERMISSION_MANAGE_PLUGINS))
+    die(json_encode(['error' => "Access denied"]));
+if (empty($_GET))
+    die(json_encode($config['third-party-plugins']['data']));
+
+elseif(isset($_GET['install']))
+{
+    install_plugin($_GET['install']);
+}
+elseif (isset($_GET['uninstall']))
+{
+    uninstall_plugin($_GET['uninstall']);
+}
+
+function uninstall_plugin($name)
+{
+    global $config;
+    if (!Plugins::plugin_exists($name))
+        die(json_encode(['error' => "Plugin not loaded"]));
+    
+    foreach($config['plugins'] as $k => $v)
+        if ($v == $name)
+            unset($config['plugins'][$k]);
+    write_config();
+
+    deleteDirectory(UPATH."/plugins/$name");
+    die(json_encode(["success" => "Plugin was deleted successfully"]));
+}
+
+/**Attempt to install the plugin 
+ * @param string $name name of the plugin
+ * @return void
+ */
+function install_plugin($name)
+{
+    global $config;
+    if (in_array($name, $config['plugins']))
+        die(json_encode(["error" => "Plugin already installed"]));
+    $url = get_plugin_install_path_from_name($name);
+    $pluginfile = file_get_contents($url);
+    if (!is_dir(UPATH."/data/tmp"))
+        mkdir(UPATH."/data/tmp");
+
+    $path = UPATH."/data/tmp/";
+    $file = $path.md5(time()).".tmp";
+    if (!file_put_contents($file, $pluginfile))
+        die(json_encode(["error" => "Cannot write to directory: Need write permission"]));
+
+    unset($pluginfile);
+
+    $zip = new ZipArchive;
+    $res = $zip->open($file);
+    if ($res !== true) {
+        unlink($file);
+        die(json_encode(["error" => "Could not open file we just wrote lol"]));
+    }
+
+    // ensure we have no conflicts
+    $extractPath = UPATH."/plugins/$name";
+    // lazy upgrade for now.
+    if (is_dir($extractPath))
+    {
+        deleteDirectory($extractPath);
+    }
+    mkdir($extractPath);
+    $zip->extractTo($extractPath);
+    $zip->close();
+
+    //clear up our temp shit
+    unset($zip);
+    unlink($file);
+    unset($res);
+
+    // load it in the config
+    $config['plugins'][] = $name;
+    write_config();
+    
+    // wahey
+    die(json_encode(['success' => "Installation was complete"]));
+}
+
+/**
+ * @param string $name Name of plugin
+ * @return NULL|string Path or NULL
+ */
+function get_plugin_install_path_from_name($name)
+{
+    global $config;
+    foreach($config['third-party-plugins']['data'] as $p)
+    {
+        if (!strcmp($p->name,$name))
+            return $p->download_link;
+    }
+    return NULL;
+}
+
+function deleteDirectory($dir) {
+    if (!file_exists($dir)) {
+        return true;
+    }
+
+    if (!is_dir($dir)) {
+        return unlink($dir);
+    }
+
+    foreach (scandir($dir) as $item) {
+        if ($item == '.' || $item == '..') {
+            continue;
+        }
+
+        if (!deleteDirectory($dir . DIRECTORY_SEPARATOR . $item)) {
+            return false;
+        }
+
+    }
+
+    return rmdir($dir);
+}
\ No newline at end of file
index 2639e1a791075b93950bff030d20e06fe95fc230..622804a26ebb03c0374708878954a2d41b375dce 100644 (file)
@@ -17,4 +17,4 @@ define('DEFAULT_CHAN_DETAIL_QUICK_BAN_REASON', "You have been removed from this
 /**
  * The version of our webpanel
  */
-define('WEBPANEL_VERSION', "1.0-git");
+define('WEBPANEL_VERSION', "0.9-git");
index 21ecf9018f0a342e26cbf2809d0e797162cf43c8..4ca6df2f5d3f959391f7c7de8d180f425336be54 100644 (file)
@@ -4,6 +4,9 @@ require_once "../inc/common.php";
 require_once "../inc/header.php";
 require_once "../Classes/class-plugin-git.php";
 
+if (!current_user_can(PERMISSION_MANAGE_PLUGINS))
+    die("Access denied");
+
 $p = new PluginRepo();
 ?>
 
@@ -26,8 +29,128 @@ $p = new PluginRepo();
 
 ?>
 
-<!-- Remove when page is finished -->
 <script>
-const modal = bsModal("Important", "This is a work in progress.<br><br>Please do not expect anything to work properly on this page",
-"<div class=\"btn btn-primary\" onClick=\"$('#'+modal).modal('hide')\">Ok</div>", size = null, static = true, show = true, closebutton = true);
-</script>
\ No newline at end of file
+
+    const ibtns = document.querySelectorAll(".btn-install-plugin");
+    ibtns.forEach((ib) => {
+        ib.addEventListener('click', (e) => {
+            console.log("Button clicked! " +ib.innerHTML);
+            if (ib.innerHTML !== "Install" && ib.innerHTML !== "Uninstall") // some point between, don't do anything
+            {}
+            else if (ib.innerHTML == "Install") // install button pressed!
+            {
+                let req = requestInstall(ib.id.slice(0,-7))
+                if (req == true)
+                {
+                    ib.classList.replace("btn-primary", "btn-secondary");
+                    ib.innerHTML = "Installing...";
+                }
+                else
+                {
+                    let uhoh = new bsModal("Error", "Could not install: "+req, "", null, false, true);
+                }
+            }
+            else if (ib.innerHTML == "Uninstall")
+            {
+                let req = requestInstall(ib.id.slice(0,-7), true); // true = uninstall
+                if (req == true)
+                {
+                    ib.classList.replace("btn-outline-danger", "btn-secondary");
+                    ib.innerHTML = "Uninstalling...";
+                }
+                else
+                {
+                    let uhoh = new bsModal("Error", "Could not uninstall: "+req, "", null, false, true);
+                }
+            }
+        });
+    })
+    const installed = document.querySelectorAll(".installed");
+    installed.forEach((el) => {
+        let btn = document.getElementById(el.id + 'install');
+        btn.classList.replace("btn-primary", "btn-outline-danger");
+        btn.innerHTML = "Uninstall";
+    });
+
+    function requestInstall(name, uninstall = false)
+    {
+        let inst = (uninstall) ? "uninstall" : "install";
+        var xhr = new XMLHttpRequest();
+
+        xhr.onload = function() {
+            if (xhr.status === 200) {
+                var response = JSON.parse(xhr.responseText);
+                console.log(response.success);
+                let install_button = document.getElementById(name+'install');
+                if (response.success !== undefined)
+                {
+                    if (install_button)
+                    {
+                        install_button.innerHTML = (inst == "uninstall") ? "Install" : "Uninstall";
+                        install_button.classList.replace('btn-secondary', (inst == "uninstall") ? 'btn-primary' : 'btn-outline-danger');
+                        let icomplete = bsModal(((inst == "uninstall") ? "Uninstall" : "Install") + " Plugin", response.success,"<div id=\""+name+"closebtn\" class=\"btn btn-danger\">Close</div>", null, true, true, false);
+                        let closebtn = document.getElementById(name+"closebtn");
+                        closebtn.addEventListener('click', e => {
+                            location.reload();
+                        });
+                    }
+                }
+                else
+                {
+                    if (install_button)
+                    {
+                        install_button.innerHTML = (inst == "uninstall") ? "Uninstall" : "Install";
+                        install_button.classList.replace('btn-secondary', (inst == "uninstall") ? 'btn-outline-danger' : 'btn-primary');
+                        let icomplete = bsModal(((inst == "uninstall") ? "Uninstall" : "Install") + " Plugin", response.error,"", null, false, true);
+                        let closebtn = document.getElementById(name+"closebtn");
+                        closebtn.addEventListener('click', e => {
+                            location.reload();
+                        });
+                    }
+                }
+            }
+        };
+
+        xhr.open('GET', BASE_URL + 'api/plugin.php?'+inst+'=' + name, true);
+        xhr.send();
+        return true;
+    }
+
+    function create_info_modal(modname)
+    {
+        fetch('https://api.dalek.services/plugins.list')
+        .then(response => response.json()) // Parse the response as JSON
+        .then(data => {
+            for (let i = 0; data[i]; i++)
+            {
+                if (data[i].name == modname)
+                {
+                    const modal = bsModal(
+                        "<i>Information about " + data[i].title + "</i>", // title
+                        "<div class=\"" + data[i].name + "_screenshots\"><i class=\"fa fa-spinner\" aria-hidden=\"true\"></i></div><div class=\"" + data[i].name + "_description\"><i class=\"fa fa-spinner\" aria-hidden=\"true\"></i></div>",
+                        "<div id=\""+modname+"closebtn\" class=\"btn btn-danger\">Close</div>", null, true, true, false
+                    );
+                    let modalclose = document.getElementById(modal);
+                    modalclose.addEventListener('click', (e) => {
+                        $("#"+modal).modal('hide');
+                    });
+                    console.log(modal + '-body');
+                    boobs = document.getElementById(modal + '-body');
+                    boobs.innerHTML = data[i].description;
+                }
+            }
+        })
+        .catch(error => {
+            // Handle any errors that occur during the request
+            console.error('Error:', error);
+        });
+    }
+
+    const infoButtons = document.querySelectorAll('.more-info');
+    infoButtons.forEach((el) => {
+        el.addEventListener('click', (event) => {
+            create_info_modal(el.id);
+            
+        });
+    });
+</script>