]> jfr.im git - irc/unrealircd/unrealircd-webpanel.git/commitdiff
Some work on right-click menus and other things
authorValerie Pond <redacted>
Tue, 23 May 2023 17:57:12 +0000 (18:57 +0100)
committerValerie Pond <redacted>
Tue, 23 May 2023 17:57:12 +0000 (18:57 +0100)
This is largely unavailable yet because it's unfinished, for example the right-click menu in firefox can't figure out copy and paste, which is annoying.

Working on a feedback dialog which pops up and asks for feedback every 3 months (unifinished, but that's the plan).

The right-click menu will aim to be hookable in  PHP as well as javascript, i.e. you can add to it via javascript within your plugin or you could run a hook so your option displays on the api endpoint, which is probably a bit cleaner.

Classes/class-hook.php
inc/common.php
inc/footer.php
inc/header.php
js/bs-modal.js
js/right-click-menus.js [new file with mode: 0755]
misc/right-click.php [new file with mode: 0755]

index 0ec4cfafab5ee0b884c56074ac733543db9067bc..e9ba379554ebf2e6612ad78cc20a13120e239d7b 100644 (file)
@@ -121,6 +121,9 @@ define('HOOKTYPE_USERMETA_GET', 110);
  */
 define('HOOKTYPE_USER_CREATE', 111);
 
+/** HOOKTYPE_GET_USER_LIST
+ * @param array $userlist []
+ */
 define('HOOKTYPE_GET_USER_LIST', 112);
 
 define('HOOKTYPE_USER_DELETE', 113);
@@ -133,6 +136,8 @@ define('HOOKTYPE_USER_PERMISSION_LIST', 116);
 
 define('HOOKTYPE_EDIT_USER', 117);
 
+define('HOOKTYPE_RIGHTCLICK_MENU', 118);
+
 define('HOOKTYPE_AUTH_MOD', 200);
 
 /** An upgrade has been detected.
index f342d1dd153b18d722a71c422068e4acc91502d6..1354e268d0690dba3d8e53a824530f98ef20e86d 100644 (file)
@@ -4,11 +4,11 @@ function check_requirements()
        if (version_compare(PHP_VERSION, '8.0.0', '<'))
        {
                die("This webserver is using PHP version ".PHP_VERSION." but we require at least PHP 8.0.0.<br>".
-                   "If you already installed PHP8 but are still seeing this error, then it means ".
-                   "apache/nginx/.. is loading an older PHP version. Eg. on Debian/Ubuntu you need ".
-                   "<code>apt-get install libapache2-mod-php8.2</code> (or a similar version) and ".
-                   "<code>apt-get remove libapache2-mod-php7.4</code> (or a similar version). ".
-                   "You may also need to choose again the PHP module to load in apache via <code>a2enmod php8.2</code>");
+                       "If you already installed PHP8 but are still seeing this error, then it means ".
+                       "apache/nginx/.. is loading an older PHP version. Eg. on Debian/Ubuntu you need ".
+                       "<code>apt-get install libapache2-mod-php8.2</code> (or a similar version) and ".
+                       "<code>apt-get remove libapache2-mod-php7.4</code> (or a similar version). ".
+                       "You may also need to choose again the PHP module to load in apache via <code>a2enmod php8.2</code>");
        }
 
        $loaded_extensions = get_loaded_extensions();
@@ -29,8 +29,8 @@ function check_requirements()
                }
                $text .= "</ul>\n";
                $text .= "You need to install/enable these PHP packages and restart the webserver.<br>".
-                        "If you are on Debian/Ubuntu then run <code>$cmd</code> ".
-                        "and restart your webserver (eg: <code>systemctl restart apache2</code> for apache).";
+                                "If you are on Debian/Ubuntu then run <code>$cmd</code> ".
+                                "and restart your webserver (eg: <code>systemctl restart apache2</code> for apache).";
                die($text);
        }
 }
@@ -93,7 +93,7 @@ function get_current_page(&$title)
 function page_requires_no_config()
 {
        if (str_ends_with($_SERVER['SCRIPT_FILENAME'],"install.php") ||
-           str_ends_with($_SERVER['SCRIPT_FILENAME'],"installation.php"))
+               str_ends_with($_SERVER['SCRIPT_FILENAME'],"installation.php"))
        {
                return TRUE;
        }
@@ -103,7 +103,7 @@ function page_requires_no_config()
 function page_requires_no_login()
 {
        if (str_ends_with($_SERVER['SCRIPT_FILENAME'],"login/index.php") ||
-           page_requires_no_config())
+               page_requires_no_config())
        {
                return TRUE;
        }
@@ -161,9 +161,9 @@ function read_config_db()
 function config_is_file_item($name)
 {
        if (($name == "plugins") ||
-           ($name == "mysql") ||
-           ($name == "base_url") ||
-           ($name == "secrets"))
+               ($name == "mysql") ||
+               ($name == "base_url") ||
+               ($name == "secrets"))
        {
                return true;
        }
@@ -191,10 +191,10 @@ function write_config_file()
        if ($str === null)
                die("Error while running write_config_file() -- weird!");
        if (!fwrite($fd, "<?php\n".
-                   "/* This config file is written automatically by the UnrealIRCd webpanel.\n".
-                   " * You are not really supposed to edit it manually.\n".
-                   " */\n".
-                   '$config = '.$str.";\n"))
+                       "/* This config file is written automatically by the UnrealIRCd webpanel.\n".
+                       " * You are not really supposed to edit it manually.\n".
+                       " */\n".
+                       '$config = '.$str.";\n"))
        {
                die("Error writing to config file $tmpfile (on fwrite).<br>");
        }
@@ -227,7 +227,7 @@ function write_config($setting = null)
 
        /* Otherwise write the whole config.
         * TODO: avoid writing settings file if unneeded,
-        *       as it is more noisy than db settings.
+        *         as it is more noisy than db settings.
         */
        $db_settings = [];
 
@@ -367,8 +367,8 @@ function upgrade_check()
        }
        /* - encrypting rpc_password */
        if (isset($config['unrealircd']) &&
-           isset($config['unrealircd']['rpc_password']) &&
-           !str_starts_with($config['unrealircd']['rpc_password'], "secret:"))
+               isset($config['unrealircd']['rpc_password']) &&
+               !str_starts_with($config['unrealircd']['rpc_password'], "secret:"))
        {
                $ret = secret_encrypt($config['unrealircd']['rpc_password']);
                if ($ret !== false)
@@ -476,10 +476,10 @@ if (!page_requires_no_config())
 }
 
 $pages = [
-       "Overview"     => ["script"=>""],
-       "Users"        => ["script"=>"users/index.php"],
-       "Channels"     => ["script"=>"channels/index.php"],
-       "Servers"      => ["script"=>"servers/index.php"],
+       "Overview"       => ["script"=>""],
+       "Users"         => ["script"=>"users/index.php"],
+       "Channels"       => ["script"=>"channels/index.php"],
+       "Servers"         => ["script"=>"servers/index.php"],
        "Server Bans"  => [
                "Server Bans" => ["script" => "server-bans/index.php"],
                "Name Bans" => ["script" => "server-bans/name-bans.php"],
@@ -553,3 +553,21 @@ Hook::run(HOOKTYPE_NAVBAR, $pages);
 */
 
 $current_page = get_current_page($current_page_name);
+
+
+global $rightClickMenu;
+$rightClickMenu = [
+       [
+               "text" => "Copy",
+               "onclick" => "copy_to_clipboard(window.getSelection().toString())",
+               "icon" => "fa-clipboard"
+       ],
+       [
+               "text" => "Paste",
+               "onclick" => "paste_from_clipboard()",
+               "icon" => "fa-paint-brush",
+       ],
+];
+
+// register our menu
+Hook::run(HOOKTYPE_RIGHTCLICK_MENU, $rightClickMenu);
index a2cd28ee5007cfe9724a013e7fb7798c43ec6b50..229674fe2d4e1776f845a89cb95a0ceb62b08015 100644 (file)
                
 </div>
 </div><!-- Second div is because there is a container div in the header-->
-
 </footer>
 </body>
 </html>
+<!--
 <script>
        var bugreport = document.getElementById('bugreport');
        bugreport.addEventListener('click', (e) => {
                window.open("https://github.com/unrealircd/unrealircd-webpanel/issues/new?labels=bug&template=bug_report.md", '_blank');
        });
-</script>
\ No newline at end of file
+
+       // Function to check if the feedback prompt should be shown
+       function showFeedbackPrompt()
+       {
+               var showPrompt = localStorage.getItem('showFeedbackPrompt');
+
+               // Check if the prompt has never been shown or the user opted to see it again
+               if (showPrompt === null || showPrompt === 'true' || showPrompt == 'false')
+               {
+                       const id = bsModal(
+
+                               // Modal header (1st param)
+                               "Help Us Improve Your Experience!",
+
+                               // Modal content (2nd param)
+                               `We value your opinion and would greatly appreciate your feedback. Your input will help us enhance the features, usability,
+                               and overall performance of the panel, making it even better for managing your UnrealIRCd server/network.<br><br>
+                               Please take a moment to share your thoughts, suggestions, or any issues you've encountered.
+                               We welcome feedback on the user interface, functionality, ease of use, or any other aspect you deem important.
+                               Your feedback is valuable to us in shaping the future of the web panel.<br><br>
+                               Thank you for your time and support in making UnrealIRCd web panel the best it can be!`,
+                               
+                               // Modal footer (3rd param) buttons
+                               `<div class="btn btn-sm btn-danger" onclick="remindFeedback()">Remind me later!</div>
+                               <div class="btn btn-sm btn-primary" onclick="submitFeedback()">Feedback</div>`,
+
+                               // Optional bootstrap size param (e.g. sm, lg, xl, etc) or null
+                               null,
+
+                               // Optional "static" bool
+                               true,
+
+                               // Optional "show" option
+                               false,
+
+                               // Optional "closebutton" display
+                               false
+                       );
+               }
+               $('#' + id).modal('show');
+
+       }
+       
+       // Function to handle user feedback submission and store preferences
+       function submitFeedback()
+       {
+               $('#'+id).modal('hide');
+               // Handle feedback submission
+               // ...
+
+               // Store user preference not to show the prompt again
+               localStorage.setItem('showFeedbackPrompt', 'false');
+       }
+       
+       // don't show it yet lol
+       // showFeedbackPrompt();
+</script> -->
+<?php
+
+// don't let people use this yet, still work in progress.
+// require_once UPATH . "/misc/right-click.php";
index d87fcd11ab4af9b14e459cfbffe96b36f0760f5e..62f433a06f76c309a8f48cb196f66c7bc781d715 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 $nav_shown = true;
 $arr = []; Hook::run(HOOKTYPE_PRE_HEADER, $arr);
+
 ?>
 <!DOCTYPE html>
 <head>
@@ -11,6 +12,7 @@ $arr = []; Hook::run(HOOKTYPE_PRE_HEADER, $arr);
        <meta name="HandheldFriendly" content="true">
 
 <link href="<?php echo get_config("base_url"); ?>css/unrealircd-admin.css" rel="stylesheet">
+<link href="<?php echo get_config("base_url"); ?>css/right-click.css" rel="stylesheet">
 <style>
 .big-page-item:hover, .big-page-item:active, .nav-link {
        color: black;
@@ -41,6 +43,9 @@ $arr = []; Hook::run(HOOKTYPE_PRE_HEADER, $arr);
 <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
 <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js" integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+" crossorigin="anonymous"></script>
 <script src="<?php echo get_config("base_url"); ?>js/unrealircd-admin.js"></script>
+<!-- <script defer src="<?php echo get_config("base_url"); ?>js/right-click-menus.js"></script> -- We're not doing this yet XD -->
+<script src="<?php echo get_config("base_url"); ?>js/bs-modal.js"></script>
+<script src="<?php echo get_config("base_url"); ?>js/bs-toast.js"></script>
 <script src="<?php echo get_config("base_url"); ?>js/datatables.min.js"></script>
 <script src="<?php echo get_config("base_url"); ?>js/datatables-natural-sort.js"></script>
 <script src="<?php echo get_config("base_url"); ?>js/datatables-ellipsis.js"></script>
@@ -104,7 +109,7 @@ $arr = []; Hook::run(HOOKTYPE_PRE_HEADER, $arr);
                color: black;
        }
 </style>
-<nav id="sidebarlol" style="left: 0" class="w3-sidebar navbar-expand-md bg-dark padding-top me-5 ma-5">
+<nav id="sidebarlol" style="left:0;overflow:auto" class="w3-sidebar navbar-expand-md bg-dark padding-top me-5 ma-5">
 <div class="list-group">
        <div class="badge badge-secondary rounded-pill">Main Menu</div>
        <?php 
index d87f6c12270bd3731781557519c0299d5798b5a3..2fea1da7a44c1c43416149a70ad85fe42c8535c2 100644 (file)
@@ -6,13 +6,14 @@
  * @param {string} footer - HTML for the footer
  * 
  * Optional:
- * @param {string} size - the bootstrap size category for modals (sm, lg, xl)
- * @param {boolean} static - whether or not to make the backdrop static, forcing the user to respond to the dialog
- * @param {boolean} show - whether or not to automatically show the modal
+ * @param {string|null} size - the bootstrap size category for modals (sm, lg, xl). Default is null.
+ * @param {boolean} static - whether or not to make the backdrop static, forcing the user to respond to the dialog. Default is false
+ * @param {boolean} show - whether or not to automatically show the modal. Default is false.
+ * @param {boolean} closebutton - display and allow the close button. Default is true.
  * @returns {string} returns the ID
  */
 
-function bsModal(title, body, footer, size = null, static = false, show = false)
+function bsModal(title, body, footer, size = null, static = false, show = false, closebutton = true)
 {
     /* generate a random number between 1000 and 90000 to use as an id */
     const min = 1000;
@@ -46,9 +47,11 @@ function bsModal(title, body, footer, size = null, static = false, show = false)
 
     mHeader.classList.add("modal-header");
     mHeader.id = id + "-header";
-    mHeader.innerHTML =`<h5 class="modal-title" id="` + id + `"-title">` + title + `</h5>
-                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                        <span aria-hidden="true">&times;</span></button>`;
+    mHeader.innerHTML =`<h5 class="modal-title" id="` + id + `"-title">` + title + `</h5>`;
+
+    if (closebutton)
+        mHeader.innerHTML +=   `<button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                                <span aria-hidden="true">&times;</span></button>`;
     
     mBody.classList.add("modal-body");
     mBody.id = id + "-body";
@@ -71,4 +74,6 @@ function bsModal(title, body, footer, size = null, static = false, show = false)
 
     if (show)
         $('#' + m1.id).modal('show');
+
+    return m1.id;
 }
diff --git a/js/right-click-menus.js b/js/right-click-menus.js
new file mode 100755 (executable)
index 0000000..b3aad0c
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * Right-click menus for use around the webpanel
+ */
+let selection = null;
+let click_target = null;
+function can_clipboard()
+{
+       if (typeof navigator.clipboard !== "undefined" && typeof navigator.clipboard.writeText === "function"
+               && typeof navigator.clipboard.readText === "function")
+               return true;
+       return false;
+}
+
+async function paste_from_clipboard()
+{
+       let text = await navigator.clipboard.readText();
+       click_target.value = text;
+}
+
+async function copy_to_clipboard()
+{
+       navigator.clipboard.writeText(selection);
+}
+
+function build_rclick_menu()
+{
+       const m = document.createElement('div');
+       m.classList.add('nav-item','list-group');
+       m.id = 'rclickmenu';
+
+       const m1 = document.createElement('div');
+       m1.classList.add('item', 'list-group-item-action');
+       m1.id = 'rclick_opt1';
+}
+
+var rclickmenu = document.getElementById('rclickmenu');
+
+document.addEventListener("click", (e) =>
+{
+       rclickmenu.classList.remove("visible");
+});
+
+
+document.addEventListener("contextmenu", (event) =>
+{
+       event.preventDefault();
+       click_target = event.target;
+
+       rclickmenu.classList.remove("visible"); // hide it if it was already elsweyr
+       var { clientX: mouseX, clientY: mouseY } = event;
+       
+       rclickmenu.style.top = `${mouseY}px`;
+       rclickmenu.style.left = `${mouseX}px`;
+
+       /* "Copy" option */
+       selection = window.getSelection().toString();
+
+       if (selection.length == 0 || !can_clipboard())
+               document.getElementById('rclick_opt1').style.display = 'none';
+
+       else if (can_clipboard())
+               document.getElementById('rclick_opt1').style.display = '';
+
+       /* Check if the browser supports pasting */
+       if (!can_clipboard() || (!click_target || click_target.tagName.toLowerCase() !== "input"))
+               document.getElementById('rclick_opt2').style.display = 'none';
+
+       else if (click_target && click_target.tagName.toLowerCase() === "input")
+               document.getElementById('rclick_opt2').style.display = '';
+
+       setTimeout(() => { rclickmenu.classList.add("visible"); });
+});
+document.addEventListener('keydown', (event) => {
+       if (event.key === 'Escape')
+       {
+               rclickmenu.classList.remove("visible");
+       }
+});
\ No newline at end of file
diff --git a/misc/right-click.php b/misc/right-click.php
new file mode 100755 (executable)
index 0000000..a504aa9
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Right-click menus
+ */
+if (!isset($config)) die;
+
+$menu = [
+       [
+               "text" => "Copy",
+               "onclick" => "copy_to_clipboard(window.getSelection().toString())",
+               "icon" => "fa-clipboard"
+       ],
+       [
+               "text" => "Paste",
+               "onclick" => "paste_from_clipboard()",
+               "icon" => "fa-paint-brush",
+       ],
+];
+
+// register our menu
+Hook::run(HOOKTYPE_RIGHTCLICK_MENU, $menu);
+
+?>
+<!-- Right-click menu -->
+
+<div id='rclickmenu' class="nav-item list-group">
+       <div id="rclick_opt1" class="item list-group-item-action" onclick="copy_to_clipboard(window.getSelection().toString())">Copy</div>
+       <div id="rclick_opt2" class="item list-group-item-action" onclick="paste_from_clipboard()">Paste</div>
+       <div id="rclick_opt3" class="item list-group-item-action">Search</div>
+
+       <a class="item list-group-item-action" href="https://www.unrealircd.org/docs/UnrealIRCd_webpanel" target="_blank">Documentation Wiki</a>
+</div>
\ No newline at end of file