mirror of
https://github.com/stefanpejcic/openpanel
synced 2025-06-26 18:28:26 +00:00
1249 lines
51 KiB
HTML
1249 lines
51 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
|
<link rel="icon" type="image/x-icon" href="https://openpanel.com/img/favicon.svg">
|
|
|
|
<!-- page meta -->
|
|
<meta name="description" content="OpenPanel - Control Panel" />
|
|
<meta name="author" content="Stefan Pejcic" />
|
|
<meta name="keywords" content="openpanel, openadmin, opencli, docker, hosting, control, panel" />
|
|
<meta name="robots" content="noindex,nofollow">
|
|
|
|
<!-- performance -->
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
|
|
<!-- page title & icon -->
|
|
<link rel="icon" type="image/x-icon" href="/static/assets/img/favicon.svg">
|
|
<title>{{ title }} - {% if brand_name %}{{brand_name}}{% else %}{{brand}}{% endif %}</title>
|
|
|
|
<!-- styles -->
|
|
<link href="/static/css3/admini.min.css" rel="stylesheet" />
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&display=swap" rel="stylesheet" />
|
|
|
|
<!-- scripts -->
|
|
<script src="/static/lib/jquery/jquery.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
|
|
|
|
|
|
{% if current_route == "/server/webserver_conf" or title == "PHP.INI Editor" %}
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.min.css">
|
|
<!-- Include CodeMirror JavaScript -->
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.min.js"></script>
|
|
<!-- Include CodeMirror mode for the desired language -->
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/mode/clike/clike.min.js"></script>
|
|
<!-- Include CodeMirror theme -->
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/theme/dracula.min.css">
|
|
|
|
|
|
|
|
<style>
|
|
.CodeMirror {
|
|
min-height: 50vh;
|
|
height: auto;
|
|
}
|
|
</style>
|
|
{% endif %}
|
|
|
|
<!-- icons -->
|
|
<!--script src="/static/js3/last-icon.min.js"></script-->
|
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Two+Tone" rel="stylesheet" />
|
|
|
|
<!-- scripts -->
|
|
<script type="module" src="/static/js3/admini.min.js"></script>
|
|
|
|
|
|
{% if current_route.startswith('/usage') %}
|
|
<!-- Include Raphael Library -->
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.3.0/raphael.min.js"></script>
|
|
|
|
<!-- Include JustGage Library -->
|
|
<script src="https://cdn.jsdelivr.net/npm/justgage@1.4.0/justgage.min.js"></script>
|
|
{% endif %}
|
|
|
|
<style>
|
|
.main-header { border-bottom: 1px solid #f3f5f9;
|
|
background-color: #fff;}
|
|
.container-fluid {
|
|
background-color: #fbfcfe;}
|
|
|
|
.h6.card-title { color: rgba(233,236,239,.7);}
|
|
|
|
.nije-link {text-decoration: none;color: #212830;}
|
|
button svg path {stroke: white;}
|
|
|
|
.table>:not(caption)>*>* {
|
|
padding: 0.5rem 0.5rem; }
|
|
|
|
.card-header {
|
|
padding: 1.25em 1em 0 1em; }
|
|
|
|
/* dark side */
|
|
[data-bs-theme="dark"] .nije-link {color: unset;}
|
|
[data-bs-theme="dark"] .dash-links {color: unset;}
|
|
[data-bs-theme="dark"] .container-fluid {background-color: unset;}
|
|
[data-bs-theme="dark"] div.sidebar-storage.mb-2 i {color: #939ba2!important;}
|
|
[data-bs-theme="dark"] .card .card-title {font-size: .925rem; color: #939ba2!important;}
|
|
[data-bs-theme="dark"] .bg-light {background: #0b1a22!important; color: white !important;}
|
|
[data-bs-theme="dark"] .icon {color: #0b1a22!important;}
|
|
[data-bs-theme="dark"] .no_link { color:unset;}
|
|
[data-bs-theme="dark"] .sidebar {background: black;}
|
|
[data-bs-theme="dark"] .sidebar-brand {background: black;}
|
|
/* */
|
|
|
|
.bg-light {background: white!important; color: #000 !important;}
|
|
|
|
|
|
/*.sidebar {background-image: linear-gradient(to bottom, #212830, black);}*/
|
|
|
|
body.minimenu .sidebar_open {display:none;}
|
|
.sidebar-storage { display: flex;}
|
|
a:hover {text-decoration: none;}
|
|
.sidebar-footer, .sidebar-profile-mini {display: flex;flex: 0 0 30px;}
|
|
.minimenu .sidebar .sidebar-link l-i {font-size: x-large;}
|
|
.h1 > .card-title {font-size: x-large;}
|
|
li.breadcrumb-item.smaller, .smaller {font-size: 0.8em!important;}
|
|
.card .card-title {font-size: .925rem; color: #495057;}
|
|
div.card-header{display: inline-flex;}
|
|
.container-fluid {padding-top: calc(var(--bs-gutter-x));}
|
|
.sidebar-profile .dropdown-menu {left: 1px;width: -webkit-fill-available;bottom: 4rem; top:auto;}
|
|
|
|
|
|
.dropdown-menu.dropdown-menu-dark.show { background: #020c11; border-top: 1px solid rgba(255,255,255,.15);}
|
|
.dropdown-item:hover {color: #e9ecef; background: #020c11; border-left-color: rgba(0,0,0,0);}
|
|
|
|
/* Styles for mobile */
|
|
@media (max-width: 767px) {
|
|
.desktop-only {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
/* Styles for desktop */
|
|
@media (min-width: 768px) {
|
|
.mobile-only {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
{% block custom_header %}
|
|
<!-- custom code in header: https://dev.openpanel.com/customize.html#Code-in-Header -->
|
|
{% include 'custom_code/in_header.html' %}
|
|
{% endblock %}
|
|
|
|
<!-- custom css code: https://dev.openpanel.com/customize.html#Custom-CSS -->
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/custom.css') }}">
|
|
|
|
</head>
|
|
|
|
<!-- minimenu class should be applied on render -->
|
|
|
|
<body>
|
|
<div class="wrapper">
|
|
<sco-pe id="sidebar-scope">{% include 'partials/sidebar.html' %}</sco-pe>
|
|
|
|
|
|
|
|
{% set applications_available_for_install = 0 %}
|
|
{% if 'wordpress' in enabled_modules %}
|
|
{% set applications_available_for_install = applications_available_for_install + 1 %}
|
|
{% endif %}
|
|
{% if 'pm2' in enabled_modules %}
|
|
{% set applications_available_for_install = applications_available_for_install + 1 %}
|
|
{% endif %}
|
|
{% if 'mautic' in enabled_modules %}
|
|
{% set applications_available_for_install = applications_available_for_install + 1 %}
|
|
{% endif %}
|
|
{% if 'flarum' in enabled_modules %}
|
|
{% set applications_available_for_install = applications_available_for_install + 1 %}
|
|
{% endif %}
|
|
|
|
|
|
<div class="modal fade" id="cardModal" tabindex="-1" role="dialog" aria-labelledby="cardModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header text-center">
|
|
<h5 class="modal-title" id="cardModalLabel">{{ _('What kind of site would you like to create?') }}</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
|
|
<div class="row row-cols-1 row-cols-md-2 g-4">
|
|
|
|
<div class="col">
|
|
<div class="card h-100" style="margin-botton:0;">
|
|
<div class="row g-0">
|
|
<div class="col-md-4">
|
|
<img src="/static/images/install_site.avif" class="img-fluid rounded-start" alt="Install an Application">
|
|
</div>
|
|
<div class="col-md-8">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Install an Application</h5>
|
|
<p class="card-text">Create your site by installing a web app, such as WordPress or Mautic.</p>
|
|
<p class="card-text"><small class="text-body-secondary"><b>{{ applications_available_for_install }}</b> Applications Available</small></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer">
|
|
<a href="/auto-installer" class="btn btn-outline-primary w-100">Install Apps</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="col">
|
|
<div class="card h-100" style="margin-botton:0;">
|
|
<div class="row g-0">
|
|
<div class="col-md-4">
|
|
<img src="/static/images/upload_site.avif" class="img-fluid rounded-start" alt="Create a Custom Website">
|
|
</div>
|
|
<div class="col-md-8">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Create a Custom Website</h5>
|
|
<p class="card-text">Upload your web content and add databases.</p>
|
|
<p class="card-text"><small class="text-body-secondary"> </small></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer">
|
|
<div class="btn-group w-100" role="group" aria-label="Actions">
|
|
<a class="btn btn-outline-primary w-50" href="/files">Files</a>
|
|
<a class="btn btn-outline-primary btn-mobile-collapse w-50" href="/databases#wizard">
|
|
|
|
<span>Databases</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<sco-pe id="main-scope">
|
|
<main class="main">
|
|
|
|
|
|
|
|
{% if current_route.startswith('/website') %}
|
|
|
|
|
|
<!-- optional selector -->
|
|
{% if user_websites %}
|
|
|
|
|
|
{% if 'wordpress' in enabled_modules or 'pm2' in enabled_modules or 'mautic' in enabled_modules or 'flarum' in enabled_modules %}
|
|
|
|
<!--div class="sidemenu offcanvas-lg offcanvas-end d-none" style="width: 320px" id="my-scroll-panel">
|
|
<header class="sidemenu-header">
|
|
<strong>{{ _('Websites') }}</strong>
|
|
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" data-bs-target="#my-scroll-panel" aria-label="Close"></button>
|
|
</header>
|
|
<section class="scroller">
|
|
<div class="p-3">
|
|
{% for site in user_websites %}
|
|
<span class="">
|
|
<a class="btn btn-outline-primary mb-3 d-block text-start" href="/{% if site[1] == 'NodeJS' %}pm2{% elif site[1] == 'Python' %}pm2{% else %}website?domain={{ site[0] }}{% endif %}" style="text-overflow: ellipsis; overflow: hidden;white-space: nowrap;line-break: anywhere;"><img src="https://www.google.com/s2/favicons?domain={{ site[0] }}" alt="{{ site[0] }} Favicon" style="width: 16px; height: 16px; margin-right:10px;">{{ site[0] }}</a>
|
|
</span>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
|
|
<footer class="d-none sidemenu-footer">
|
|
<div>
|
|
<span class="badge bg-dark">X</span>
|
|
websites
|
|
</div>
|
|
<div class="dropup dropend">
|
|
<button class="btn btn-default" id="custom-dropdown-btn" data-bs-toggle="dropdown">
|
|
<l-i name="insert_comment"><i class="material-icons-two-tone">hey you</i></l-i>
|
|
</button>
|
|
<ul class="dropdown-menu dropdown-menu-dark" aria-labelledby="custom-dropdown-btn">
|
|
<li><a class="dropdown-item" href="#">want a job?</a></li>
|
|
<li><a class="dropdown-item" href="#">info at openpanel dot com</a></li>
|
|
<li>
|
|
<hr class="dropdown-divider">
|
|
</li>
|
|
<p class="m-3 mb-0">
|
|
<small>Soon..</small>
|
|
</p>
|
|
</ul>
|
|
</div>
|
|
</footer>
|
|
</div-->
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
<div class="main-container">
|
|
{% if current_route.startswith('/website') %}
|
|
|
|
{% endif %}
|
|
|
|
|
|
{% if session.get('impersonate') %}
|
|
<div class="main-header" id="impersonate-banner" style="display:table; background: rgb(255, 208, 62);justify-content: space-around;height: 35px;">
|
|
<div class="main-header-info row">
|
|
<p class="mb-0 col">{{ _("You are currently impersonating user") }} '{{current_username}}'</p>
|
|
<a id="admin-return-link" href="#" class="col-md-2 btn rounded-0 btn-dark">Return to admin</a>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
var currentHost = window.location.hostname;
|
|
var newPort = '2087';
|
|
var currentUsername = '{{current_username}}';
|
|
var newUrl = 'http://' + currentHost + ':' + newPort + '/users/' + encodeURIComponent(currentUsername);
|
|
var link = document.getElementById('admin-return-link');
|
|
link.href = newUrl;
|
|
});
|
|
</script>
|
|
|
|
|
|
|
|
{% endif %}
|
|
|
|
{% if current_route.startswith('/files') %}
|
|
<script>
|
|
|
|
$(document).ready(function(){
|
|
var breadcrumbContainer = document.querySelector(".breadcrumb");
|
|
|
|
if (breadcrumbContainer) {
|
|
var breadcrumbItems = breadcrumbContainer.querySelectorAll("li");
|
|
var maxVisibleItems = 13;
|
|
|
|
// Create "Copy Path to Clipboard" button
|
|
var copyButton = document.createElement("li");
|
|
copyButton.innerHTML = '<a class="black-white" href="#" data-toggle="tooltip" data-placement="bottom" title="{{ _('Copy Path to Clipboard') }}" id="copy-button"><i class="bi bi-copy"></i></a>';
|
|
|
|
if (breadcrumbItems.length > maxVisibleItems) {
|
|
copyButton.className = "breadcrumb-item";
|
|
}
|
|
|
|
if (breadcrumbItems.length <= maxVisibleItems) {
|
|
var spaceItem = document.createElement("li");
|
|
spaceItem.innerHTML = '<a> </a>';
|
|
breadcrumbContainer.appendChild(spaceItem);
|
|
}
|
|
|
|
breadcrumbContainer.appendChild(copyButton);
|
|
|
|
if (breadcrumbItems.length > maxVisibleItems) {
|
|
for (var i = 1; i < breadcrumbItems.length - 1; i++) {
|
|
breadcrumbItems[i].style.display = "none";
|
|
}
|
|
|
|
// Create ellipsis link
|
|
var ellipsis = document.createElement("li");
|
|
ellipsis.className = "breadcrumb-item";
|
|
ellipsis.innerHTML = '<a href="#" data-toggle="tooltip" data-placement="bottom" title="{{ _('Click to display the full path') }}" id="breadcrumb-ellipsis"><i class="bi bi-three-dots"></i></a>';
|
|
breadcrumbItems[0].parentNode.insertBefore(ellipsis, breadcrumbItems[1]);
|
|
|
|
var ellipsisLink = document.getElementById("breadcrumb-ellipsis");
|
|
|
|
ellipsisLink.addEventListener("click", function(event) {
|
|
event.preventDefault();
|
|
|
|
for (var i = 1; i < breadcrumbItems.length - 1; i++) {
|
|
breadcrumbItems[i].style.display = (breadcrumbItems[i].style.display === "none") ? "" : "none";
|
|
}
|
|
|
|
// Toggle the visibility of the ellipsis link
|
|
ellipsis.style.display = (ellipsis.style.display === "none") ? "" : "none";
|
|
});
|
|
}
|
|
|
|
// Event listener for the "Copy Path to Clipboard" button
|
|
var copyButtonLink = document.getElementById("copy-button");
|
|
|
|
copyButtonLink.addEventListener("click", function(event) {
|
|
event.preventDefault();
|
|
|
|
// Get the full path and strip brackets from the first part
|
|
var fullPath = breadcrumbItems[0].textContent.trim().replace(/^\((.*?)\)/, '$1');
|
|
|
|
for (var i = 1; i < breadcrumbItems.length; i++) {
|
|
fullPath += '/' + breadcrumbItems[i].textContent.trim();
|
|
}
|
|
|
|
// Strip out everything before and including the first `(`, then remove `)`
|
|
fullPath = fullPath.replace(/.*\((.*?)\)/, '$1');
|
|
|
|
// Replace double slashes `//` with a single slash `/`
|
|
fullPath = fullPath.replace(/\/\//g, '/');
|
|
|
|
// Use Clipboard API if available, otherwise fallback
|
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
// Use Clipboard API
|
|
navigator.clipboard.writeText(fullPath)
|
|
.then(function() {
|
|
console.log('Path copied to clipboard: ' + fullPath);
|
|
var copyButton = document.getElementById('copy-button');
|
|
if (copyButton) {
|
|
var originalTitle = copyButton.getAttribute('data-bs-original-title');
|
|
copyButton.setAttribute('data-bs-original-title', 'Copied to Clipboard!');
|
|
|
|
// Manually show the tooltip to update the text
|
|
var tooltip = bootstrap.Tooltip.getInstance(copyButton);
|
|
if (tooltip) {
|
|
tooltip.update(); // Update the tooltip to reflect the new title
|
|
} else {
|
|
// Initialize tooltip if it doesn't exist
|
|
new bootstrap.Tooltip(copyButton);
|
|
}
|
|
|
|
// Optionally show the tooltip immediately
|
|
copyButton.setAttribute('data-bs-toggle', 'tooltip');
|
|
bootstrap.Tooltip.getInstance(copyButton).show();
|
|
// Revert tooltip text after 2 seconds
|
|
setTimeout(function() {
|
|
copyButton.setAttribute('data-bs-original-title', originalTitle);
|
|
var tooltip = bootstrap.Tooltip.getInstance(copyButton);
|
|
if (tooltip) {
|
|
tooltip.update(); // Update the tooltip to reflect the reverted title
|
|
}
|
|
}, 2000);
|
|
|
|
|
|
|
|
}
|
|
})
|
|
.catch(function(err) {
|
|
console.error('Unable to copy path to clipboard', err);
|
|
});
|
|
} else {
|
|
// Fallback to document.execCommand('copy')
|
|
var tempTextarea = document.createElement("textarea");
|
|
tempTextarea.value = fullPath;
|
|
tempTextarea.style.position = "absolute";
|
|
tempTextarea.style.left = "-9999px";
|
|
document.body.appendChild(tempTextarea);
|
|
tempTextarea.select();
|
|
try {
|
|
document.execCommand('copy');
|
|
console.log('Path copied to clipboard (fallback): ' + fullPath);
|
|
|
|
var copyButton = document.getElementById('copy-button');
|
|
if (copyButton) {
|
|
var originalTitle = copyButton.getAttribute('data-bs-original-title');
|
|
copyButton.setAttribute('data-bs-original-title', 'Copied to Clipboard!');
|
|
|
|
// Manually show the tooltip to update the text
|
|
var tooltip = bootstrap.Tooltip.getInstance(copyButton);
|
|
if (tooltip) {
|
|
tooltip.update(); // Update the tooltip to reflect the new title
|
|
} else {
|
|
// Initialize tooltip if it doesn't exist
|
|
new bootstrap.Tooltip(copyButton);
|
|
}
|
|
|
|
// Optionally show the tooltip immediately
|
|
copyButton.setAttribute('data-bs-toggle', 'tooltip');
|
|
bootstrap.Tooltip.getInstance(copyButton).show();
|
|
// Revert tooltip text after 2 seconds
|
|
setTimeout(function() {
|
|
copyButton.setAttribute('data-bs-original-title', originalTitle);
|
|
var tooltip = bootstrap.Tooltip.getInstance(copyButton);
|
|
if (tooltip) {
|
|
tooltip.update(); // Update the tooltip to reflect the reverted title
|
|
}
|
|
}, 2000);
|
|
|
|
}
|
|
|
|
|
|
} catch (err) {
|
|
console.error('Fallback: Unable to copy', err);
|
|
}
|
|
document.body.removeChild(tempTextarea);
|
|
}
|
|
});
|
|
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
<header class="main-header">
|
|
<div class="main-header-sidebar">
|
|
<button type="button" class="btn btn-primary btn-flex btn-square rounded-0" data-bs-toggle="offcanvas"
|
|
data-bs-target="#sidebar">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="28.418" height="16.333" viewBox="0 0 28.418 16.333"><g id="Group_1778" data-name="Group 1778" transform="translate(-306.83 -126.235)"><path id="Path_19286" data-name="Path 19286" d="M0,0,4.693,3.713,9.324-.044,14.13,3.669l5.285-3.76,4.513,3.715" transform="matrix(1, -0.017, 0.017, 1, 308.973, 128.797)" fill="none" stroke="#03040a" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"></path><path id="Path_19287" data-name="Path 19287" d="M0,0,4.693,3.713,9.324-.044,14.13,3.669l5.285-3.76,4.513,3.715" transform="matrix(1, -0.017, 0.017, 1, 309.113, 136.796)" fill="none" stroke="#03040a" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"></path></g></svg>
|
|
|
|
|
|
</button>
|
|
</div>
|
|
<div class="main-header-info">
|
|
<!--a class="btn btn-default btn-square btn-flex rounded-0 main-header-info-back" href="/">
|
|
<l-i name="navigate_before"></l-i>
|
|
<span class="visually-hidden">Navigate up a folder</span>
|
|
</a-->
|
|
<nav aria-label="breadcrumb">
|
|
<div class="breadcrumb">
|
|
<li class="breadcrumb-item active" aria-current="page">{% if wp_name %}{{ wp_name }}{% else %}{{ title }}{% endif %}</li>
|
|
<li class="breadcrumb-break"></li>
|
|
<li class="breadcrumb-item smaller"><a href="{{ url_for('files') }}"><i class="bi bi-house-fill"></i> (/home/{{ current_username }})</a></li>
|
|
{% set path_parts = path_param.split('/') %}
|
|
{% for index in range(path_parts|length) %}
|
|
{% set part = path_parts[index] %}
|
|
{% set breadcrumb_path = path_parts[:index+1]|join('/') %}
|
|
<li class="breadcrumb-item smaller active"><a aria-current="page" href="{{ url_for('files', path_param=breadcrumb_path) }}">{{ part }}</a>/</li>
|
|
{% endfor %}
|
|
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
<div class="main-header-nav">
|
|
|
|
<div class="btn-group" aria-label="{{ _('Navigation') }}">
|
|
|
|
<input type="text" class="form-control search-bar" id="searchInput" style="display: none;" placeholder="{{ _('Search Files & Folders') }}">
|
|
<ul class="dropdown-menu scroller" style="top: 100%; width: 100%; right: 0px; overflow-x: hidden; overflow-y: overlay; max-height: 90vh;" aria-labelledby="searchInput" id="filteredDropdown">
|
|
</ul>
|
|
<button class="btn rounded-0 btn-primary-outlined" id="refresh-button">
|
|
<span class="desktop-text"><i class="bi bi-arrow-clockwise"></i> {{ _('Reload') }}</span>
|
|
<i class="mobile-icon bi bi-arrow-clockwise"></i>
|
|
</button>
|
|
|
|
|
|
|
|
<script>
|
|
// Use 'shown.bs.modal' event to autofocus inputs
|
|
window.onload = function() {
|
|
var createfilemodal_shown = new bootstrap.Modal(document.getElementById('createFileModal'));
|
|
createfilemodal_shown._element.addEventListener('shown.bs.modal', function () {
|
|
document.getElementById('fileName').focus();
|
|
});
|
|
|
|
var createfilemodal_shown = new bootstrap.Modal(document.getElementById('MmoveModal'));
|
|
createfilemodal_shown._element.addEventListener('shown.bs.modal', function () {
|
|
document.getElementById('MmoveDestination').focus();
|
|
});
|
|
|
|
|
|
var createfilemodal_shown = new bootstrap.Modal(document.getElementById('McopyModal'));
|
|
createfilemodal_shown._element.addEventListener('shown.bs.modal', function () {
|
|
document.getElementById('McopyDestination').focus();
|
|
});
|
|
|
|
|
|
var createfilemodal_shown = new bootstrap.Modal(document.getElementById('MextractModal'));
|
|
createfilemodal_shown._element.addEventListener('shown.bs.modal', function () {
|
|
document.getElementById('MextractPath').focus();
|
|
});
|
|
|
|
var createfilemodal_shown = new bootstrap.Modal(document.getElementById('McompressModal'));
|
|
createfilemodal_shown._element.addEventListener('shown.bs.modal', function () {
|
|
document.getElementById('McompressArchiveName').focus();
|
|
});
|
|
|
|
|
|
|
|
var createfilemodal_shown = new bootstrap.Modal(document.getElementById('renameModal'));
|
|
createfilemodal_shown._element.addEventListener('shown.bs.modal', function () {
|
|
document.getElementById('newRenameFileName').focus();
|
|
});
|
|
|
|
|
|
var createfilemodal_shown = new bootstrap.Modal(document.getElementById('createFolderModal'));
|
|
createfilemodal_shown._element.addEventListener('shown.bs.modal', function () {
|
|
document.getElementById('folderName').focus();
|
|
});
|
|
|
|
var createfilemodal_shown = new bootstrap.Modal(document.getElementById('uploadModal'));
|
|
createfilemodal_shown._element.addEventListener('shown.bs.modal', function () {
|
|
document.getElementById('fileUpload').focus();
|
|
});
|
|
|
|
|
|
});
|
|
</script>
|
|
|
|
|
|
|
|
<button class="btn rounded-0 btn-dark" id="newFileButton" data-bs-toggle="modal" data-bs-target="#createFileModal">
|
|
<span class="desktop-text"><i class="bi bi-plus-lg"></i> {{ _('New File') }}</span>
|
|
<i class="mobile-icon bi bi-file-earmark-plus"></i>
|
|
</button>
|
|
<button class="btn rounded-0 btn-dark" id="newFolderButton" data-bs-toggle="modal" data-bs-target="#createFolderModal">
|
|
<span class="desktop-text"><i class="bi bi-folder-plus"></i> {{ _('New Folder') }}</span>
|
|
<i class="mobile-icon bi bi-folder-plus"></i>
|
|
</button>
|
|
<button class="btn rounded-0 btn-dark" id="uploadButton" data-bs-toggle="modal" data-bs-target="#uploadModal">
|
|
<span class="desktop-text"><i class="bi bi-upload"></i> {{ _('Upload') }}</span>
|
|
<i class="mobile-icon bi bi-upload"></i>
|
|
</button>
|
|
|
|
<button class="btn" id="searchIcon" onclick="toggleSearchBar()">
|
|
<i class="bi bi-search" style="font-size: large;"></i>
|
|
</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</header>
|
|
{% else %}
|
|
<header class="main-header">
|
|
<div class="main-header-sidebar">
|
|
<button type="button" class="btn btn-primary btn-flex btn-square rounded-0" data-bs-toggle="offcanvas"
|
|
data-bs-target="#sidebar" aria-controls="sidebar">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="28.418" height="16.333" viewBox="0 0 28.418 16.333"><g id="Group_1778" data-name="Group 1778" transform="translate(-306.83 -126.235)"><path id="Path_19286" data-name="Path 19286" d="M0,0,4.693,3.713,9.324-.044,14.13,3.669l5.285-3.76,4.513,3.715" transform="matrix(1, -0.017, 0.017, 1, 308.973, 128.797)" fill="none" stroke="#03040a" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"></path><path id="Path_19287" data-name="Path 19287" d="M0,0,4.693,3.713,9.324-.044,14.13,3.669l5.285-3.76,4.513,3.715" transform="matrix(1, -0.017, 0.017, 1, 309.113, 136.796)" fill="none" stroke="#03040a" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"></path></g></svg>
|
|
</button>
|
|
</div>
|
|
<div class="main-header-info">
|
|
<nav aria-label="breadcrumb">
|
|
<div class="breadcrumb">
|
|
<li class="breadcrumb-item active" aria-current="page">{{ title }}</li>
|
|
|
|
{% if current_route == '/dashboard' or current_route == '/' %}
|
|
{% else %}
|
|
|
|
<li class="breadcrumb-break"></li>
|
|
{% if current_route.startswith('/databases') %}
|
|
<li class="breadcrumb-item smaller"><a href="/databases">MySQL</a></li>
|
|
{% elif current_route.startswith('/backups') or current_route.startswith('/inodes') or current_route.startswith('/disk') or current_route.startswith('files') or current_route.startswith('/ftp') or current_route.startswith('/malware-scanner') or current_route.startswith('/fix-permissions') %}
|
|
<li class="breadcrumb-item smaller"><a href="/files">Files</a></li>
|
|
{% elif current_route.startswith('/emails')%}
|
|
<li class="breadcrumb-item smaller"><a href="/emails">Emails</a></li>
|
|
{% elif current_route.startswith('/website') or current_route.startswith('/pm2') or current_route.startswith('/wordpress') or current_route.startswith('website') or current_route.startswith('/auto-installer') or current_route.startswith('auto-installer') %}
|
|
<li class="breadcrumb-item smaller"><a href="/sites">Applications</a></li>
|
|
{% elif current_route.startswith('/cache') or current_route.startswith('/search')%}
|
|
<li class="breadcrumb-item smaller"><a href="/">Cache & Search</a></li>
|
|
{% elif current_route.startswith('/php')%}
|
|
<li class="breadcrumb-item smaller"><a href="/php">PHP</a></li>
|
|
{% elif current_route.startswith('/domains/log/') or current_route.startswith('/activity') or current_route.startswith('/usage')%}
|
|
<li class="breadcrumb-item smaller"><a href="/">Analytics</a></li>
|
|
{% elif current_route.startswith('/domains') or current_route.startswith('/ssl')%}
|
|
<li class="breadcrumb-item smaller"><a href="/domains">Domains</a></li>
|
|
{% elif current_route.startswith('/cronjobs') or current_route.startswith('/ssh') or current_route.startswith('/terminal') or current_route.startswith('/process-manager')%}
|
|
<li class="breadcrumb-item smaller"><a href="/">Advanced</a></li>
|
|
{% elif current_route.startswith('/settings')%}
|
|
<li class="breadcrumb-item smaller"><a href="/account">Account</a></li>
|
|
{% elif current_route.startswith('/server')%}
|
|
<li class="breadcrumb-item smaller"><a href="/server/settings">Server</a></li>
|
|
{% endif %}
|
|
<i class="bi bi-chevron-compact-right"></i>
|
|
|
|
<li class="breadcrumb-item smaller active">
|
|
<a aria-current="page" href="{{ current_route }}">
|
|
{% if wp_name %}{{ wp_name }}
|
|
{% elif domain_name %}
|
|
{{ title }}</a> <i class="bi bi-chevron-compact-right"></i> {{ domain_name }}
|
|
{% elif domain %}
|
|
{{ title }}</a> <i class="bi bi-chevron-compact-right"></i> {{ domain }}
|
|
{% else %}
|
|
{{ title }}</a>
|
|
{% endif %}
|
|
</a></li>
|
|
|
|
{% endif %}
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
|
|
<div class="main-header-nav">
|
|
|
|
|
|
{% if current_route.startswith('/files') or title == "j" %}
|
|
|
|
{% else %}
|
|
<script>
|
|
$(document).ready(function(){
|
|
$('#searchInput').on('input', function() {
|
|
var searchText = $(this).val().toLowerCase();
|
|
var dropdownItems = '';
|
|
|
|
$.when(
|
|
$.get('/core/search_filter'),
|
|
$.get('/core/search_websites')
|
|
).done(function(jsonData, websiteData) {
|
|
jsonData = jsonData[0];
|
|
websiteData = websiteData[0];
|
|
|
|
$.each(jsonData, function(index, item) {
|
|
var itemName = item.name || '';
|
|
var linkTarget = '';
|
|
|
|
if (itemName.toLowerCase().includes(searchText)) {
|
|
if (item.link.includes('/phpmyadmin')) {
|
|
linkTarget = 'target="_blank"'; // Open in a new tab
|
|
}
|
|
|
|
dropdownItems += '<li style="border-bottom: 1px solid #e2e5ec;"><h5><a class="dropdown-item" href="' + item.link + '" ' + linkTarget + '>' + item.name + '<br><p class="dropdown-item-description" style="font-size: 0.7em;margin-bottom: 0px;">' + item.description + '</p></a></h5></li>';
|
|
}
|
|
});
|
|
|
|
|
|
$.each(websiteData, function(index, item) {
|
|
var itemName = item[0] || ''; // Get the site name from the first element
|
|
var itemLink = '/sites'; // The link for all websites
|
|
if (itemName.toLowerCase().includes(searchText)) {
|
|
dropdownItems += '<li><h5><a class="dropdown-item" href="' + itemLink + '"><i class="bi bi-globe"></i> ' + itemName + '</a></h5></li>';
|
|
}
|
|
});
|
|
|
|
if (dropdownItems) {
|
|
$('#filteredDropdown').html(dropdownItems).show();
|
|
} else {
|
|
$('#filteredDropdown').html('').hide();
|
|
}
|
|
});
|
|
});
|
|
|
|
// Hide dropdown when clicking outside
|
|
$(document).on('click', function(event) {
|
|
if (!$(event.target).closest('#searchGroup').length) {
|
|
$('#filteredDropdown').hide();
|
|
}
|
|
});
|
|
|
|
});
|
|
</script>
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="btn-group" aria-label="Navigation">
|
|
|
|
{% if 'favorites' in enabled_modules %}
|
|
|
|
|
|
|
|
<div class="project-favorite_item__wcDLn">
|
|
<span aria-hidden="true">
|
|
<div class="project-favorite_container__9W5Nt">
|
|
|
|
<button class="btn" id="addFavoriteBtn"><l-i class="bi bi-star" value="light" style="font-size: large;"></l-i>
|
|
|
|
<div class="project-favorite_sparklesContainer__K_v7d" data-toggle="tooltip" data-placement="bottom" title="{{ _('Left-click to add to Favorites') }}"> <div class="project-favorite_sparkleContainer__qTghH" style=" --starting-position-x: 6px; --starting-position-y: 6px; --starting-scale: 0; --ending-position-x: 1px; --ending-position-y: 1px; --ending-scale: 1; --color: var(--ds-purple-700); --delay: 100ms; --duration: 150ms; " > <svg fill="none" height="6" viewBox="0 0 6 6" width="6" xmlns="http://www.w3.org/2000/svg"> <path d="M2.5 0.5V0H3.5V0.5C3.5 1.60457 4.39543 2.5 5.5 2.5H6V3V3.5H5.5C4.39543 3.5 3.5 4.39543 3.5 5.5V6H3H2.5V5.5C2.5 4.39543 1.60457 3.5 0.5 3.5H0V3V2.5H0.5C1.60457 2.5 2.5 1.60457 2.5 0.5Z" fill="currentColor" ></path> </svg> </div> <div class="project-favorite_sparkleContainer__qTghH" style=" --starting-position-x: 13px; --starting-position-y: 6px; --starting-scale: 0; --ending-position-x: 16px; --ending-position-y: 0px; --ending-scale: 0.7; --color: var(--ds-amber-700); --delay: 50ms; --duration: 260ms; " > <svg fill="none" height="6" viewBox="0 0 6 6" width="6" xmlns="http://www.w3.org/2000/svg"> <path d="M2.5 0.5V0H3.5V0.5C3.5 1.60457 4.39543 2.5 5.5 2.5H6V3V3.5H5.5C4.39543 3.5 3.5 4.39543 3.5 5.5V6H3H2.5V5.5C2.5 4.39543 1.60457 3.5 0.5 3.5H0V3V2.5H0.5C1.60457 2.5 2.5 1.60457 2.5 0.5Z" fill="currentColor" ></path> </svg> </div> <div class="project-favorite_sparkleContainer__qTghH" style=" --starting-position-x: 2px; --starting-position-y: 13px; --starting-scale: 0; --ending-position-x: 0px; --ending-position-y: 13px; --ending-scale: 0.5; --color: var(--ds-pink-700); --delay: 80ms; --duration: 200ms; " > <svg fill="none" height="6" viewBox="0 0 6 6" width="6" xmlns="http://www.w3.org/2000/svg"> <path d="M2.5 0.5V0H3.5V0.5C3.5 1.60457 4.39543 2.5 5.5 2.5H6V3V3.5H5.5C4.39543 3.5 3.5 4.39543 3.5 5.5V6H3H2.5V5.5C2.5 4.39543 1.60457 3.5 0.5 3.5H0V3V2.5H0.5C1.60457 2.5 2.5 1.60457 2.5 0.5Z" fill="currentColor" ></path> </svg> </div> <div class="project-favorite_sparkleContainer__qTghH" style=" --starting-position-x: 9px; --starting-position-y: 15px; --starting-scale: 0; --ending-position-x: 9px; --ending-position-y: 18px; --ending-scale: 0.7; --color: var(--ds-teal-700); --delay: 120ms; --duration: 340ms; " > <svg fill="none" height="6" viewBox="0 0 6 6" width="6" xmlns="http://www.w3.org/2000/svg"> <path d="M2.5 0.5V0H3.5V0.5C3.5 1.60457 4.39543 2.5 5.5 2.5H6V3V3.5H5.5C4.39543 3.5 3.5 4.39543 3.5 5.5V6H3H2.5V5.5C2.5 4.39543 1.60457 3.5 0.5 3.5H0V3V2.5H0.5C1.60457 2.5 2.5 1.60457 2.5 0.5Z" fill="currentColor" ></path> </svg> </div> <div class="project-favorite_sparkleContainer__qTghH" style=" --starting-position-x: 15px; --starting-position-y: 12px; --starting-scale: 0; --ending-position-x: 18px; --ending-position-y: 13px; --ending-scale: 0.5; --color: var(--ds-blue-700); --delay: 0ms; --duration: 180ms; " > <svg fill="none" height="6" viewBox="0 0 6 6" width="6" xmlns="http://www.w3.org/2000/svg"> <path d="M2.5 0.5V0H3.5V0.5C3.5 1.60457 4.39543 2.5 5.5 2.5H6V3V3.5H5.5C4.39543 3.5 3.5 4.39543 3.5 5.5V6H3H2.5V5.5C2.5 4.39543 1.60457 3.5 0.5 3.5H0V3V2.5H0.5C1.60457 2.5 2.5 1.60457 2.5 0.5Z" fill="currentColor" ></path> </svg> </div> </div>
|
|
</button>
|
|
</div>
|
|
</span>
|
|
</div>
|
|
|
|
<script>
|
|
var addFavoriteBtn = document.getElementById("addFavoriteBtn");
|
|
if (addFavoriteBtn) {
|
|
|
|
// RIGHT CLICK - Remove from favorites
|
|
addFavoriteBtn.addEventListener("contextmenu", function(event) {
|
|
// Prevent the default context menu from appearing
|
|
event.preventDefault();
|
|
|
|
|
|
// Check if the current page is already NOT a favorite
|
|
if (addFavoriteBtn.dataset.isFavorite === 'false') {
|
|
// Show a message that it's already in favorites
|
|
const toastMessage = "{{ _('This page is not in your favorites! Left-click to add it.') }}";
|
|
const toast = toaster({
|
|
body: toastMessage,
|
|
className: 'border-0 text-white bg-warning',
|
|
});
|
|
return; // prevent DELETE
|
|
}
|
|
|
|
|
|
var currentPagePathFull = new URL(window.location.href);
|
|
var currentPagePath = currentPagePathFull.pathname + currentPagePathFull.search + currentPagePathFull.hash;
|
|
|
|
// Prepare the data
|
|
var data = {
|
|
link: currentPagePath
|
|
};
|
|
|
|
let btnClass, toastMessage;
|
|
|
|
// Send the DELETE request to remove from favorites
|
|
fetch('/favorites', {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
btnClass = 'success';
|
|
toastMessage = "{{ _('Successfully removed from favorites!') }}";
|
|
const toast = toaster({
|
|
body: toastMessage,
|
|
className: `border-0 text-white bg-${btnClass}`,
|
|
});
|
|
|
|
document.getElementById('addFavoriteBtn').innerHTML = `
|
|
<l-i class="bi bi-star" value="light" style="color:gray;font-size: large;"></l-i><div class="project-favorite_sparklesContainer__K_v7d"> <div class="project-favorite_sparkleContainer__qTghH" style=" --starting-position-x: 6px; --starting-position-y: 6px; --starting-scale: 0; --ending-position-x: 1px; --ending-position-y: 1px; --ending-scale: 1; --color: var(--ds-purple-700); --delay: 100ms; --duration: 150ms; " > <svg fill="none" height="6" viewBox="0 0 6 6" width="6" xmlns="http://www.w3.org/2000/svg"> <path d="M2.5 0.5V0H3.5V0.5C3.5 1.60457 4.39543 2.5 5.5 2.5H6V3V3.5H5.5C4.39543 3.5 3.5 4.39543 3.5 5.5V6H3H2.5V5.5C2.5 4.39543 1.60457 3.5 0.5 3.5H0V3V2.5H0.5C1.60457 2.5 2.5 1.60457 2.5 0.5Z" fill="currentColor" ></path> </svg> </div> <div class="project-favorite_sparkleContainer__qTghH" style=" --starting-position-x: 13px; --starting-position-y: 6px; --starting-scale: 0; --ending-position-x: 16px; --ending-position-y: 0px; --ending-scale: 0.7; --color: var(--ds-amber-700); --delay: 50ms; --duration: 260ms; " > <svg fill="none" height="6" viewBox="0 0 6 6" width="6" xmlns="http://www.w3.org/2000/svg"> <path d="M2.5 0.5V0H3.5V0.5C3.5 1.60457 4.39543 2.5 5.5 2.5H6V3V3.5H5.5C4.39543 3.5 3.5 4.39543 3.5 5.5V6H3H2.5V5.5C2.5 4.39543 1.60457 3.5 0.5 3.5H0V3V2.5H0.5C1.60457 2.5 2.5 1.60457 2.5 0.5Z" fill="currentColor" ></path> </svg> </div> <div class="project-favorite_sparkleContainer__qTghH" style=" --starting-position-x: 2px; --starting-position-y: 13px; --starting-scale: 0; --ending-position-x: 0px; --ending-position-y: 13px; --ending-scale: 0.5; --color: var(--ds-pink-700); --delay: 80ms; --duration: 200ms; " > <svg fill="none" height="6" viewBox="0 0 6 6" width="6" xmlns="http://www.w3.org/2000/svg"> <path d="M2.5 0.5V0H3.5V0.5C3.5 1.60457 4.39543 2.5 5.5 2.5H6V3V3.5H5.5C4.39543 3.5 3.5 4.39543 3.5 5.5V6H3H2.5V5.5C2.5 4.39543 1.60457 3.5 0.5 3.5H0V3V2.5H0.5C1.60457 2.5 2.5 1.60457 2.5 0.5Z" fill="currentColor" ></path> </svg> </div> <div class="project-favorite_sparkleContainer__qTghH" style=" --starting-position-x: 9px; --starting-position-y: 15px; --starting-scale: 0; --ending-position-x: 9px; --ending-position-y: 18px; --ending-scale: 0.7; --color: var(--ds-teal-700); --delay: 120ms; --duration: 340ms; " > <svg fill="none" height="6" viewBox="0 0 6 6" width="6" xmlns="http://www.w3.org/2000/svg"> <path d="M2.5 0.5V0H3.5V0.5C3.5 1.60457 4.39543 2.5 5.5 2.5H6V3V3.5H5.5C4.39543 3.5 3.5 4.39543 3.5 5.5V6H3H2.5V5.5C2.5 4.39543 1.60457 3.5 0.5 3.5H0V3V2.5H0.5C1.60457 2.5 2.5 1.60457 2.5 0.5Z" fill="currentColor" ></path> </svg> </div> <div class="project-favorite_sparkleContainer__qTghH" style=" --starting-position-x: 15px; --starting-position-y: 12px; --starting-scale: 0; --ending-position-x: 18px; --ending-position-y: 13px; --ending-scale: 0.5; --color: var(--ds-blue-700); --delay: 0ms; --duration: 180ms; " > <svg fill="none" height="6" viewBox="0 0 6 6" width="6" xmlns="http://www.w3.org/2000/svg"> <path d="M2.5 0.5V0H3.5V0.5C3.5 1.60457 4.39543 2.5 5.5 2.5H6V3V3.5H5.5C4.39543 3.5 3.5 4.39543 3.5 5.5V6H3H2.5V5.5C2.5 4.39543 1.60457 3.5 0.5 3.5H0V3V2.5H0.5C1.60457 2.5 2.5 1.60457 2.5 0.5Z" fill="currentColor" ></path> </svg> </div> </div>
|
|
`;
|
|
|
|
loadFavorites();
|
|
})
|
|
.catch((error) => {
|
|
console.error('Error:', error);
|
|
btnClass = 'danger';
|
|
toastMessage = "{{ _('Failed to remove favorite.') }}";
|
|
const toast = toaster({
|
|
body: toastMessage,
|
|
className: `border-0 text-white bg-${btnClass}`,
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
// LEFT CLICK - Add to favorites
|
|
addFavoriteBtn.addEventListener("click", function(event) {
|
|
|
|
// Check if the current page is already a favorite
|
|
if (addFavoriteBtn.dataset.isFavorite === 'true') {
|
|
// Show a message that it's already in favorites
|
|
const toastMessage = "{{ _('This page is already in your favorites! Right-click to remove it.') }}";
|
|
const toast = toaster({
|
|
body: toastMessage,
|
|
className: 'border-0 text-white bg-warning',
|
|
});
|
|
return; // prevent PUT
|
|
}
|
|
|
|
// Get the link
|
|
var currentPagePathFull = new URL(window.location.href);
|
|
var currentPagePath = currentPagePathFull.pathname + currentPagePathFull.search + currentPagePathFull.hash;
|
|
|
|
// Get the page title
|
|
var currentPageTitle = document.title;
|
|
if (currentPageTitle.includes(" - ")) {
|
|
currentPageTitle = currentPageTitle.split(" - ")[0];
|
|
}
|
|
|
|
// Prepare the data
|
|
var data = {
|
|
|
|
link: currentPagePath,
|
|
title: currentPageTitle
|
|
};
|
|
|
|
let btnClass, toastMessage;
|
|
|
|
// Send the PUT request using fetch API
|
|
fetch('/favorites', {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
let btnClass;
|
|
let toastMessage;
|
|
|
|
if (data.error) {
|
|
// Error case
|
|
btnClass = 'danger'; // Red or other error color
|
|
toastMessage = data.error; // Use the error message from the response
|
|
} else {
|
|
// Success case
|
|
btnClass = 'success'; // Green or other success color
|
|
toastMessage = "{{ _('Successfully added to favorites!') }}";
|
|
|
|
|
|
// Update the button HTML to show the filled star icon
|
|
document.getElementById('addFavoriteBtn').innerHTML = `
|
|
<l-i class="bi bi-star-fill" value="light" style="color:orange;font-size: large;"></l-i>
|
|
`;
|
|
|
|
|
|
|
|
|
|
// Optionally load favorites if needed
|
|
loadFavorites();
|
|
}
|
|
|
|
// Show the toast message
|
|
const toast = toaster({
|
|
body: toastMessage,
|
|
className: `border-0 text-white bg-${btnClass}`,
|
|
});
|
|
})
|
|
.catch((error) => {
|
|
console.error('Error:', error);
|
|
btnClass = 'danger';
|
|
toastMessage = "{{ _('Failed to add favorite.') }}";
|
|
const toast = toaster({
|
|
body: toastMessage,
|
|
className: `border-0 text-white bg-${btnClass}`,
|
|
});
|
|
});
|
|
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.nested-link-block_block__JdHXp.nested-link-block_block__JdHXp {
|
|
display: grid;
|
|
color: inherit;
|
|
cursor: pointer;
|
|
outline: none;
|
|
overflow: visible;
|
|
pointer-events: none;
|
|
text-decoration: none
|
|
}
|
|
|
|
.nested-link-block_block__JdHXp [role=button],.nested-link-block_block__JdHXp [role=link],.nested-link-block_block__JdHXp a,.nested-link-block_block__JdHXp button {
|
|
pointer-events: auto
|
|
}
|
|
|
|
.nested-link-block_block__JdHXp>* {
|
|
grid-area: 1/1;
|
|
min-width: 0
|
|
}
|
|
|
|
.project-favorite_item__wcDLn {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between
|
|
}
|
|
|
|
.project-favorite_item__wcDLn:focus-visible svg {
|
|
transform: translate(var(--ending-position-x),var(--ending-position-y)) scale(var(--ending-scale))!important;
|
|
transition: transform .1s ease-out 0
|
|
}
|
|
|
|
@media (hover: hover) {
|
|
.project-favorite_item__wcDLn:hover svg {
|
|
transform:translate(var(--ending-position-x),var(--ending-position-y)) scale(var(--ending-scale))!important;
|
|
transition: transform .1s ease-out 0
|
|
}
|
|
}
|
|
|
|
.project-favorite_container__9W5Nt {
|
|
position: relative
|
|
}
|
|
|
|
.project-favorite_container__9W5Nt .project-favorite_sparklesContainer__K_v7d {
|
|
height: 24px;
|
|
width: 24px;
|
|
display: block;
|
|
position: absolute;
|
|
top: 5px;
|
|
left: 8px;
|
|
}
|
|
|
|
.project-favorite_container__9W5Nt .project-favorite_sparkleContainer__qTghH {
|
|
position: absolute;
|
|
height: 6px;
|
|
width: 6px;
|
|
color: var(--color)
|
|
}
|
|
|
|
.project-favorite_container__9W5Nt .project-favorite_sparkleContainer__qTghH svg {
|
|
display: block;
|
|
transition: transform var(--duration,.2s) ease-out var(--delay);
|
|
transform: translate(var(--starting-position-x),var(--starting-position-y)) scale(var(--starting-scale))
|
|
}
|
|
|
|
.styles_gitInfo__SZNNj {
|
|
flex-grow: 1;
|
|
min-height: 4rem
|
|
}
|
|
|
|
.styles_projectCard__eGrlb {
|
|
display: block;
|
|
background: var(--ds-background-100);
|
|
border-radius: var(--geist-marketing-radius);
|
|
overflow: hidden;
|
|
min-height: 168px;
|
|
min-width: 300px;
|
|
box-shadow: var(--ds-shadow-border),0 4px 6px rgba(0,0,0,.04);
|
|
transition: box-shadow .15s ease,background .15s ease
|
|
}
|
|
|
|
@media (hover: hover) {
|
|
.styles_projectCardWrapper__vds4q:hover:not([data-placeholder]) .styles_projectCard__eGrlb {
|
|
box-shadow:var(--ds-shadow-border),0 6px 14px rgba(0,0,0,.08)
|
|
}
|
|
|
|
.dark-theme .styles_projectCardWrapper__vds4q:hover:not([data-placeholder]) .styles_projectCard__eGrlb {
|
|
background: var(--ds-gray-100)
|
|
}
|
|
}
|
|
|
|
.styles_projectCardDetails__Fm3po {
|
|
display: flex;
|
|
height: 100%;
|
|
padding: 20px 16px 20px 24px
|
|
}
|
|
|
|
.styles_minWidth0__gVCVc {
|
|
min-width: 0
|
|
}
|
|
|
|
.styles_maxWidth100__KxVhj {
|
|
max-width: 100%
|
|
}
|
|
|
|
.styles_branch__uOEEI {
|
|
max-width: calc(100% - 36px);
|
|
vertical-align: middle;
|
|
white-space: nowrap
|
|
}
|
|
|
|
.styles_projectCardWrapper__vds4q {
|
|
position: relative;
|
|
box-sizing: border-box
|
|
}
|
|
|
|
.styles_visitButton__RSHut {
|
|
position: absolute;
|
|
right: 0;
|
|
top: 0;
|
|
transform: translate(25%,-25%);
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transition: .15s ease;
|
|
transition-property: opacity,transform
|
|
}
|
|
|
|
@media (hover: hover) {
|
|
.styles_projectCardWrapper__vds4q:hover:not([data-placeholder]) .styles_visitButton__RSHut {
|
|
opacity:1;
|
|
pointer-events: unset;
|
|
transform: translate(25%,-35%)
|
|
}
|
|
}
|
|
|
|
@media (max-width: 600px) {
|
|
.styles_visitButton__RSHut {
|
|
display:none
|
|
}
|
|
}
|
|
|
|
.styles_stale__V3sb9>.styles_projectCard__eGrlb {
|
|
box-shadow: inset 0 4px 0 var(--geist-warning),var(--ds-shadow-border),0 4px 6px rgba(0,0,0,.04)
|
|
}
|
|
|
|
@media (hover: hover) {
|
|
.styles_projectCardWrapper__vds4q.styles_stale__V3sb9:hover:not([data-placeholder]) .styles_projectCard__eGrlb {
|
|
box-shadow:inset 0 4px 0 var(--geist-warning),var(--ds-shadow-border),0 6px 14px rgba(0,0,0,.08)
|
|
}
|
|
|
|
.dark-theme .styles_projectCardWrapper__vds4q.styles_stale__V3sb9:hover:not([data-placeholder]) .styles_projectCard__eGrlb {
|
|
box-shadow: inset 0 4px 0 var(--geist-warning),0 0 0 1px var(--geist-foreground)
|
|
}
|
|
}
|
|
|
|
/*# sourceMappingURL=bd4237834785bd8d.css.map*/
|
|
</style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
<button class="btn js-darkmode-toggle" id="toggleDarkModeIcon">
|
|
<l-i class="bi bi-moon" hidden value="light" style="font-size: large;"></l-i>
|
|
<l-i class="bi bi-sun" hidden value="dark" style="color:orange;font-size: large;"></l-i>
|
|
</button>
|
|
|
|
<input type="text" class="form-control search-bar" id="searchInput" style="display: none;" placeholder="{{ _('Search') }}">
|
|
<ul class="dropdown-menu scroller" style="top: 100%; width: 100%; right: 0px; overflow-x: hidden; overflow-y: overlay; max-height: 90vh;" aria-labelledby="searchInput" id="filteredDropdown">
|
|
</ul>
|
|
|
|
<button class="btn" id="searchIcon" onclick="toggleSearchBar()">
|
|
<i class="bi bi-search" style="font-size: large;"></i>
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
</header>
|
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
<section class="scroller">
|
|
<div class="container-fluid">
|
|
{% block content %}{% endblock %}
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</main>
|
|
</sco-pe>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- templates/partials/notifications.html -->
|
|
{% include 'partials/notifications.html' %}
|
|
|
|
|
|
<!-- templates/partials/_shortcuts.html -->
|
|
{% include 'partials/_shortcuts.html' %}
|
|
|
|
|
|
|
|
|
|
<script>
|
|
function toggleSearchBar() {
|
|
var searchBar = document.querySelector('.search-bar');
|
|
var searchIcon = document.getElementById('searchIcon');
|
|
var refreshbutton = document.getElementById('refresh-button');
|
|
var newFileButton = document.getElementById('newFileButton');
|
|
var favoritesButton = document.getElementById('addFavoriteBtn');
|
|
var newFolderButton = document.getElementById('newFolderButton');
|
|
var uploadButton = document.getElementById('uploadButton');
|
|
var searchInput = document.getElementById('searchInput');
|
|
var toggleDarkModeIcon = document.getElementById('toggleDarkModeIcon');
|
|
|
|
if (searchBar.style.display === 'none' || searchBar.style.display === '') {
|
|
// Show the search bar and change the icon to "x-lg"
|
|
searchBar.style.display = 'block';
|
|
searchIcon.innerHTML = '<i class="bi bi-x-lg" style="font-size: large;"></i>';
|
|
|
|
// Set focus on the input field
|
|
if (searchInput) {
|
|
searchInput.style.display = 'block'; // Make sure the input field is visible
|
|
searchInput.focus();
|
|
}
|
|
|
|
if (toggleDarkModeIcon) {
|
|
toggleDarkModeIcon.style.display = 'none';
|
|
}
|
|
|
|
if (favoritesButton) {
|
|
favoritesButton.style.display = 'none';
|
|
}
|
|
|
|
if (refreshbutton) {
|
|
refreshbutton.style.display = 'none';
|
|
newFileButton.style.display = 'none';
|
|
newFolderButton.style.display = 'none';
|
|
uploadButton.style.display = 'none';
|
|
}
|
|
} else {
|
|
// Hide the search bar and change the icon back to "search"
|
|
searchBar.style.display = 'none';
|
|
searchIcon.innerHTML = '<i class="bi bi-search" style="font-size: large;"></i>';
|
|
if (toggleDarkModeIcon) {
|
|
toggleDarkModeIcon.style.display = 'block';
|
|
}
|
|
if (favoritesButton) {
|
|
favoritesButton.style.display = 'block';
|
|
}
|
|
if (refreshbutton) {
|
|
refreshbutton.style.display = 'block';
|
|
newFileButton.style.display = 'block';
|
|
newFolderButton.style.display = 'block';
|
|
uploadButton.style.display = 'block';
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
|
|
|
|
</body>
|
|
<script>
|
|
$(document).ready(function() {
|
|
$('[data-toggle="tooltip"]').tooltip();
|
|
});
|
|
</script>
|
|
|
|
{% block custom_footer %}
|
|
<!-- custom code in footer: https://dev.openpanel.com/customize.html#Code-in-Footer -->
|
|
{% include 'custom_code/in_footer.html' %}
|
|
{% endblock %}
|
|
|
|
{% if url_for('static', filename='js/custom.js') %}
|
|
<!-- custom js code: https://dev.openpanel.com/customize.html#Custom-JS -->
|
|
<script src="{{ url_for('static', filename='js/custom.js') }}"></script>
|
|
{% endif %}
|
|
|
|
</html>
|