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.
*/
define('HOOKTYPE_USER_CREATE', 111);
+/** HOOKTYPE_GET_USER_LIST
+ * @param array $userlist []
+ */
define('HOOKTYPE_GET_USER_LIST', 112);
define('HOOKTYPE_USER_DELETE', 113);
define('HOOKTYPE_EDIT_USER', 117);
+define('HOOKTYPE_RIGHTCLICK_MENU', 118);
+
define('HOOKTYPE_AUTH_MOD', 200);
/** An upgrade has been detected.
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();
}
$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);
}
}
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;
}
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;
}
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;
}
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>");
}
/* 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 = [];
}
/* - 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)
}
$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"],
*/
$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);
</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";
<?php
$nav_shown = true;
$arr = []; Hook::run(HOOKTYPE_PRE_HEADER, $arr);
+
?>
<!DOCTYPE html>
<head>
<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;
<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>
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
* @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;
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">×</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">×</span></button>`;
mBody.classList.add("modal-body");
mBody.id = id + "-body";
if (show)
$('#' + m1.id).modal('show');
+
+ return m1.id;
}
--- /dev/null
+/**
+ * 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
--- /dev/null
+<?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