1
0

Initial New Repo & Env new method

This commit is contained in:
Kryscau 2025-06-17 17:14:57 +02:00
commit e859270e65
15 changed files with 2019 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
_conf/.env
.git

5
_conf/.env.example Normal file
View File

@ -0,0 +1,5 @@
DB_HOST=localhost
DB_PORT=3306
DB_NAME=my_webapp
DB_USER=my_webapp
DB_PASSWORD=mypassword

19
_conf/db.php Normal file
View File

@ -0,0 +1,19 @@
<?php
include_once __DIR__ . '/env.php';
// Database configuration
$db['host'] = $env['DB_HOST'] ?? 'localhost';
$db['name'] = $env['DB_NAME'] ?? 'my_webapp';
$db['user'] = $env['DB_USER'] ?? 'my_webapp';
$db['pass'] = $env['DB_PASS'] ?? 'my_webapp_pass';
$db['port'] = $env['DB_PORT'] ?? '3306';
if (!$db['host'] || !$db['name'] || !$db['user'] || !$db['pass'] || !$db['port']) {
die("DB configuration error: missing parameters. Please check your environment variables.");
}
$db['dsn'] = 'mysql:host=' . $db['host'] . ';port='. $db['port'] .';dbname=' . $db['name'] . ';charset=utf8mb4';
?>

24
_conf/env.php Normal file
View File

@ -0,0 +1,24 @@
<?php
function loadEnv($path) {
if (!file_exists($path)) return;
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (str_starts_with(trim($line), '#')) continue;
list($key, $value) = explode('=', $line, 2);
$key = trim($key);
$value = trim($value);
putenv("$key=$value");
$_ENV[$key] = $value;
$_SERVER[$key] = $value;
}
}
loadEnv(__DIR__ . '/.env');
$env = $_ENV;
?>

14
_conf/global.php Normal file
View File

@ -0,0 +1,14 @@
<?php
$config['site'] = [
'name' => "KVS.FYI",
'main_domain' => "kvs.fyi",
'imgs_domain' => "xbb.kvs.fyi"
];
$config['author'] = [
'name' => "Kryscau",
'bio' => "https://e-z.bio/kryscau"
];
?>

153
admin/--edit-pwd Normal file
View File

@ -0,0 +1,153 @@
<?php
// Message d'erreur/succès
$message = '';
// Traitement du formulaire
if (isset($_POST['submit'])) {
$new_password = $_POST['password'];
if (empty($new_password)) {
$message = '<div style="color: red; margin-bottom: 15px;">Le mot de passe ne peut pas être vide!</div>';
} else {
// Générer le hash avec bcrypt
$hash = password_hash($new_password, PASSWORD_BCRYPT);
try {
// Connexion à la base de données
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8mb4", $db_user, $db_pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Vérifier si la table admin existe
$tables = $pdo->query("SHOW TABLES LIKE 'admin'")->fetchAll();
if (count($tables) === 0) {
// Créer la table admin si elle n'existe pas
$pdo->exec("CREATE TABLE IF NOT EXISTS `admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`password_hash` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
// Insérer le nouveau mot de passe
$stmt = $pdo->prepare("INSERT INTO admin (password_hash) VALUES (?)");
$stmt->execute([$hash]);
} else {
// Vérifier si un enregistrement existe déjà
$count = $pdo->query("SELECT COUNT(*) FROM admin")->fetchColumn();
if ($count > 0) {
// Mettre à jour le mot de passe existant
$stmt = $pdo->prepare("UPDATE admin SET password_hash = ? WHERE id = 1");
$stmt->execute([$hash]);
} else {
// Insérer un nouveau mot de passe
$stmt = $pdo->prepare("INSERT INTO admin (password_hash) VALUES (?)");
$stmt->execute([$hash]);
}
}
$message = '<div style="color: green; margin-bottom: 15px;">
<p><strong>Mot de passe mis à jour avec succès!</strong></p>
<p>Votre nouveau mot de passe: <strong>' . htmlspecialchars($new_password) . '</strong></p>
<p>Hash généré: <code>' . $hash . '</code></p>
<p><strong>IMPORTANT:</strong> Supprimez ce fichier immédiatement après utilisation!</p>
</div>';
} catch (PDOException $e) {
$message = '<div style="color: red; margin-bottom: 15px;">Erreur: ' . $e->getMessage() . '</div>';
}
}
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mise à jour du mot de passe admin</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #f8fafc;
}
h1 {
color: #4f46e5;
margin-bottom: 20px;
}
.card {
background-color: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}
input[type="password"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
margin-bottom: 20px;
}
button {
background-color: #4f46e5;
color: white;
border: none;
border-radius: 4px;
padding: 10px 15px;
font-size: 16px;
cursor: pointer;
}
button:hover {
background-color: #4338ca;
}
.warning {
background-color: #fee2e2;
border-left: 4px solid #ef4444;
padding: 10px 15px;
margin: 20px 0;
color: #b91c1c;
}
code {
background-color: #f1f5f9;
padding: 2px 4px;
border-radius: 4px;
font-family: monospace;
word-break: break-all;
}
</style>
</head>
<body>
<h1>Mise à jour du mot de passe admin</h1>
<div class="warning">
<strong>ATTENTION:</strong> Ce fichier est destiné à un usage unique. Supprimez-le immédiatement après avoir mis à jour votre mot de passe!
</div>
<?php echo $message; ?>
<div class="card">
<form method="post">
<div>
<label for="password">Nouveau mot de passe admin:</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit" name="submit">Mettre à jour le mot de passe</button>
</form>
</div>
<div class="warning" style="margin-top: 20px;">
<strong>RAPPEL:</strong> N'oubliez pas de supprimer ce fichier après utilisation!
</div>
</body>
</html>

View File

@ -0,0 +1,303 @@
/* Custom CSS */
:root {
--primary-color: #6366f1;
--secondary-color: #4f46e5;
--background-color: #f8fafc;
--card-bg: #ffffff;
--text-color: #1e293b;
--shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}
body {
background-color: var(--background-color);
color: var(--text-color);
min-height: 100vh;
padding: 2rem;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 2rem;
}
.title {
font-size: 2.5rem;
font-weight: 800;
margin-bottom: 0.5rem;
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.card {
background-color: var(--card-bg);
border-radius: 12px;
padding: 1.5rem;
box-shadow: var(--shadow);
margin-bottom: 1.5rem;
}
.form-group {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 0.75rem;
border: 1px solid #e2e8f0;
border-radius: 0.375rem;
font-size: 1rem;
}
button {
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 0.375rem;
padding: 0.75rem 1.5rem;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.2s;
}
button:hover {
background-color: var(--secondary-color);
}
.btn-danger {
background-color: #ef4444;
}
.btn-danger:hover {
background-color: #dc2626;
}
.btn-secondary {
background-color: #64748b;
}
.btn-secondary:hover {
background-color: #475569;
}
.alert {
padding: 1rem;
border-radius: 0.375rem;
margin-bottom: 1rem;
}
.alert-danger {
background-color: #fee2e2;
color: #b91c1c;
}
.alert-success {
background-color: #dcfce7;
color: #15803d;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid #e2e8f0;
}
th {
font-weight: 600;
}
.service-icon {
font-size: 1.5rem;
color: var(--primary-color);
}
.actions {
display: flex;
gap: 0.5rem;
}
.back-link {
display: inline-block;
margin-top: 1rem;
color: #64748b;
text-decoration: none;
}
.back-link:hover {
color: var(--primary-color);
}
.drag-handle {
cursor: move;
color: #64748b;
}
/* Icon Selector Styles */
.icon-input-group {
position: relative;
display: flex;
align-items: center;
}
.icon-input {
flex-grow: 1;
padding-right: 40px;
}
.icon-preview {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
font-size: 1.2rem;
color: var(--primary-color);
}
.icon-select-btn {
margin-left: 10px;
background-color: #e2e8f0;
color: #475569;
padding: 0.5rem 1rem;
}
.icon-select-btn:hover {
background-color: #cbd5e1;
}
.icon-selector-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 2000;
overflow-y: auto;
}
.icon-selector-content {
position: relative;
background-color: white;
margin: 50px auto;
padding: 20px;
width: 90%;
max-width: 800px;
border-radius: 8px;
max-height: 80vh;
overflow-y: auto;
}
.icon-selector-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
position: sticky;
top: 0;
background-color: white;
padding: 10px 0;
z-index: 10;
}
.icon-selector-close {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #64748b;
}
.icon-search {
padding: 8px 12px;
border: 1px solid #e2e8f0;
border-radius: 4px;
width: 100%;
max-width: 300px;
margin-bottom: 15px;
}
.icon-categories {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 15px;
}
.icon-category {
background-color: #e2e8f0;
border: none;
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
font-size: 0.9rem;
}
.icon-category.active {
background-color: var(--primary-color);
color: white;
}
.icon-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
gap: 10px;
}
.icon-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 10px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}
.icon-item:hover {
background-color: #f1f5f9;
}
.icon-item.selected {
background-color: #e0e7ff;
}
.icon-item i {
font-size: 1.5rem;
margin-bottom: 5px;
color: var(--primary-color);
}
.icon-item span {
font-size: 0.7rem;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
}

View File

@ -0,0 +1,952 @@
$(document).ready(function () {
// Drag-and-drop functionality to rearrange services
$("#sortable").sortable({
handle: ".drag-handle",
update: function () {
updateServiceIds();
},
});
// Update service IDs for the reorganization form
function updateServiceIds() {
var serviceIds = [];
$("#sortable tr").each(function () {
serviceIds.push($(this).data("id"));
});
// Empty the ID container
$("#service-ids-container").empty();
// Add hidden fields for each ID
$.each(serviceIds, function (index, id) {
$("#service-ids-container").append(
'<input type="hidden" name="service_ids[]" value="' + id + '">'
);
});
}
// Initialize service IDs
updateServiceIds();
// Editing functionality
$(".edit-btn").click(function () {
var id = $(this).data("id");
var name = $(this).data("name");
var icon = $(this).data("icon");
var url = $(this).data("url");
$("#edit-id").val(id);
$("#edit-name").val(name);
$("#edit-icon").val(icon);
$("#edit-url").val(url);
$("#edit-icon-preview").attr("class", icon + " icon-preview");
$("#edit-modal").show();
});
// Close the edit modal
$("#cancel-edit").click(function () {
$("#edit-modal").hide();
});
// Update icon preview during input
$("#icon").on("input", function () {
$("#icon-preview").attr("class", $(this).val() + " icon-preview");
});
$("#edit-icon").on("input", function () {
$("#edit-icon-preview").attr("class", $(this).val() + " icon-preview");
});
// Initialize the icon selector
initIconSelector();
});
// Global variables for the icon selector
let currentIconInput = "";
let currentIconPreview = "";
// Function to open the icon selector
function openIconSelector(inputId, previewId) {
currentIconInput = inputId;
currentIconPreview = previewId;
// Select the current icon if it exists
const currentIcon = $("#" + inputId).val();
if (currentIcon) {
$(".icon-item").removeClass("selected");
$(`.icon-item[data-icon="${currentIcon}"]`).addClass("selected");
}
$("#icon-selector-modal").show();
}
// Function to close the icon selector
function closeIconSelector() {
$("#icon-selector-modal").hide();
}
// Function to select an icon
function selectIcon(iconClass) {
$("#" + currentIconInput).val(iconClass);
$("#" + currentIconPreview).attr("class", iconClass + " icon-preview");
closeIconSelector();
}
// Function to initialize the icon selector
function initIconSelector() {
// List of popular icons by category
const icons = {
solid: [
"fa-solid fa-envelope",
"fa-solid fa-home",
"fa-solid fa-user",
"fa-solid fa-cog",
"fa-solid fa-bell",
"fa-solid fa-calendar",
"fa-solid fa-search",
"fa-solid fa-heart",
"fa-solid fa-star",
"fa-solid fa-check",
"fa-solid fa-times",
"fa-solid fa-plus",
"fa-solid fa-minus",
"fa-solid fa-trash",
"fa-solid fa-edit",
"fa-solid fa-save",
"fa-solid fa-download",
"fa-solid fa-upload",
"fa-solid fa-link",
"fa-solid fa-image",
"fa-solid fa-video",
"fa-solid fa-music",
"fa-solid fa-file",
"fa-solid fa-folder",
"fa-solid fa-lock",
"fa-solid fa-unlock",
"fa-solid fa-key",
"fa-solid fa-shield",
"fa-solid fa-chart-bar",
"fa-solid fa-chart-line",
"fa-solid fa-chart-pie",
"fa-solid fa-globe",
"fa-solid fa-map",
"fa-solid fa-location-dot",
"fa-solid fa-phone",
"fa-solid fa-mobile",
"fa-solid fa-tablet",
"fa-solid fa-laptop",
"fa-solid fa-desktop",
"fa-solid fa-server",
"fa-solid fa-database",
"fa-solid fa-cloud",
"fa-solid fa-cloud-upload",
"fa-solid fa-cloud-download",
"fa-solid fa-comments",
"fa-solid fa-comment",
"fa-solid fa-message",
"fa-solid fa-paper-plane",
"fa-solid fa-share",
"fa-solid fa-reply",
"fa-solid fa-thumbs-up",
"fa-solid fa-thumbs-down",
"fa-solid fa-clock",
"fa-solid fa-sync",
"fa-solid fa-spinner",
"fa-solid fa-exclamation",
"fa-solid fa-question",
"fa-solid fa-info",
"fa-solid fa-warning",
"fa-solid fa-ban",
"fa-solid fa-sign-in",
"fa-solid fa-sign-out",
"fa-solid fa-arrow-up",
"fa-solid fa-arrow-down",
"fa-solid fa-arrow-left",
"fa-solid fa-arrow-right",
"fa-solid fa-expand",
"fa-solid fa-compress",
"fa-solid fa-print",
"fa-solid fa-copy",
"fa-solid fa-paste",
"fa-solid fa-code",
"fa-solid fa-terminal",
"fa-solid fa-bug",
"fa-solid fa-filter",
"fa-solid fa-sort",
"fa-solid fa-camera",
"fa-solid fa-microphone",
"fa-solid fa-volume-up",
"fa-solid fa-volume-down",
"fa-solid fa-play",
"fa-solid fa-pause",
"fa-solid fa-stop",
"fa-solid fa-forward",
"fa-solid fa-backward",
"fa-solid fa-list",
"fa-solid fa-th",
"fa-solid fa-th-large",
"fa-solid fa-align-left",
"fa-solid fa-align-center",
"fa-solid fa-align-right",
"fa-solid fa-align-justify",
"fa-solid fa-bold",
"fa-solid fa-italic",
"fa-solid fa-underline",
"fa-solid fa-font",
"fa-solid fa-table",
"fa-solid fa-tasks",
"fa-solid fa-check-square",
"fa-solid fa-square",
"fa-solid fa-circle",
"fa-solid fa-ellipsis-h",
"fa-solid fa-ellipsis-v",
"fa-solid fa-bars",
"fa-solid fa-sliders",
"fa-solid fa-toggle-on",
"fa-solid fa-toggle-off",
"fa-solid fa-caret-up",
"fa-solid fa-caret-down",
"fa-solid fa-caret-left",
"fa-solid fa-caret-right",
"fa-solid fa-chevron-up",
"fa-solid fa-chevron-down",
"fa-solid fa-chevron-left",
"fa-solid fa-chevron-right",
"fa-solid fa-angle-up",
"fa-solid fa-angle-down",
"fa-solid fa-angle-left",
"fa-solid fa-angle-right",
"fa-solid fa-external-link",
"fa-solid fa-share-square",
"fa-solid fa-plus-square",
"fa-solid fa-minus-square",
"fa-solid fa-times-circle",
"fa-solid fa-check-circle",
"fa-solid fa-question-circle",
"fa-solid fa-info-circle",
"fa-solid fa-exclamation-circle",
"fa-solid fa-exclamation-triangle",
"fa-solid fa-lightbulb",
"fa-solid fa-flag",
"fa-solid fa-bookmark",
"fa-solid fa-tag",
"fa-solid fa-tags",
"fa-solid fa-book",
"fa-solid fa-newspaper",
"fa-solid fa-sticky-note",
"fa-solid fa-credit-card",
"fa-solid fa-money-bill",
"fa-solid fa-dollar-sign",
"fa-solid fa-euro-sign",
"fa-solid fa-pound-sign",
"fa-solid fa-yen-sign",
"fa-solid fa-ruble-sign",
"fa-solid fa-rupee-sign",
"fa-solid fa-won-sign",
"fa-solid fa-bitcoin-sign",
"fa-solid fa-credit-card",
"fa-solid fa-wallet",
"fa-solid fa-shopping-cart",
"fa-solid fa-shopping-bag",
"fa-solid fa-shopping-basket",
"fa-solid fa-cart-plus",
"fa-solid fa-cart-arrow-down",
"fa-solid fa-cash-register",
"fa-solid fa-receipt",
"fa-solid fa-gift",
"fa-solid fa-box",
"fa-solid fa-boxes",
"fa-solid fa-box-open",
"fa-solid fa-truck",
"fa-solid fa-shipping-fast",
"fa-solid fa-dolly",
"fa-solid fa-warehouse",
"fa-solid fa-store",
"fa-solid fa-store-alt",
"fa-solid fa-shop",
"fa-solid fa-cash-register",
"fa-solid fa-person",
"fa-solid fa-people-group",
"fa-solid fa-user-group",
"fa-solid fa-users",
"fa-solid fa-user-plus",
"fa-solid fa-user-minus",
"fa-solid fa-user-check",
"fa-solid fa-user-times",
"fa-solid fa-user-edit",
"fa-solid fa-user-cog",
"fa-solid fa-user-gear",
"fa-solid fa-user-shield",
"fa-solid fa-user-lock",
"fa-solid fa-user-clock",
"fa-solid fa-user-doctor",
"fa-solid fa-user-nurse",
"fa-solid fa-user-graduate",
"fa-solid fa-user-tie",
"fa-solid fa-user-astronaut",
"fa-solid fa-user-ninja",
"fa-solid fa-user-secret",
"fa-solid fa-address-book",
"fa-solid fa-address-card",
"fa-solid fa-id-badge",
"fa-solid fa-id-card",
"fa-solid fa-signature",
"fa-solid fa-passport",
"fa-solid fa-vcard",
"fa-solid fa-building",
"fa-solid fa-city",
"fa-solid fa-hotel",
"fa-solid fa-house",
"fa-solid fa-school",
"fa-solid fa-university",
"fa-solid fa-church",
"fa-solid fa-mosque",
"fa-solid fa-synagogue",
"fa-solid fa-cross",
"fa-solid fa-star-of-david",
"fa-solid fa-star-and-crescent",
"fa-solid fa-om",
"fa-solid fa-dharmachakra",
"fa-solid fa-yin-yang",
"fa-solid fa-peace",
"fa-solid fa-menorah",
"fa-solid fa-ankh",
"fa-solid fa-hamsa",
"fa-solid fa-pray",
"fa-solid fa-praying-hands",
"fa-solid fa-hands",
"fa-solid fa-hand",
"fa-solid fa-hand-point-up",
"fa-solid fa-hand-point-down",
"fa-solid fa-hand-point-left",
"fa-solid fa-hand-point-right",
"fa-solid fa-hand-paper",
"fa-solid fa-hand-rock",
"fa-solid fa-hand-scissors",
"fa-solid fa-hand-spock",
"fa-solid fa-hand-lizard",
"fa-solid fa-thumbs-up",
"fa-solid fa-thumbs-down",
"fa-solid fa-fist-raised",
"fa-solid fa-hands-helping",
"fa-solid fa-handshake",
"fa-solid fa-hands-wash",
"fa-solid fa-hands-bubbles",
"fa-solid fa-hands-clapping",
"fa-solid fa-sign-language",
"fa-solid fa-american-sign-language-interpreting",
"fa-solid fa-assistive-listening-systems",
"fa-solid fa-braille",
"fa-solid fa-blind",
"fa-solid fa-wheelchair",
"fa-solid fa-accessible-icon",
"fa-solid fa-closed-captioning",
],
regular: [
"fa-regular fa-envelope",
"fa-regular fa-user",
"fa-regular fa-bell",
"fa-regular fa-calendar",
"fa-regular fa-heart",
"fa-regular fa-star",
"fa-regular fa-circle",
"fa-regular fa-square",
"fa-regular fa-file",
"fa-regular fa-folder",
"fa-regular fa-image",
"fa-regular fa-comment",
"fa-regular fa-comments",
"fa-regular fa-message",
"fa-regular fa-paper-plane",
"fa-regular fa-clipboard",
"fa-regular fa-copy",
"fa-regular fa-paste",
"fa-regular fa-save",
"fa-regular fa-edit",
"fa-regular fa-bookmark",
"fa-regular fa-flag",
"fa-regular fa-eye",
"fa-regular fa-eye-slash",
"fa-regular fa-thumbs-up",
"fa-regular fa-thumbs-down",
"fa-regular fa-check-square",
"fa-regular fa-clock",
"fa-regular fa-calendar-alt",
"fa-regular fa-calendar-check",
"fa-regular fa-calendar-minus",
"fa-regular fa-calendar-plus",
"fa-regular fa-calendar-times",
"fa-regular fa-address-book",
"fa-regular fa-address-card",
"fa-regular fa-building",
"fa-regular fa-credit-card",
"fa-regular fa-file-alt",
"fa-regular fa-file-archive",
"fa-regular fa-file-audio",
"fa-regular fa-file-code",
"fa-regular fa-file-excel",
"fa-regular fa-file-image",
"fa-regular fa-file-pdf",
"fa-regular fa-file-powerpoint",
"fa-regular fa-file-video",
"fa-regular fa-file-word",
"fa-regular fa-folder-open",
"fa-regular fa-frown",
"fa-regular fa-meh",
"fa-regular fa-smile",
"fa-regular fa-grin",
"fa-regular fa-grin-alt",
"fa-regular fa-grin-beam",
"fa-regular fa-grin-beam-sweat",
"fa-regular fa-grin-hearts",
"fa-regular fa-grin-squint",
"fa-regular fa-grin-squint-tears",
"fa-regular fa-grin-stars",
"fa-regular fa-grin-tears",
"fa-regular fa-grin-tongue",
"fa-regular fa-grin-tongue-squint",
"fa-regular fa-grin-tongue-wink",
"fa-regular fa-grin-wink",
"fa-regular fa-kiss",
"fa-regular fa-kiss-beam",
"fa-regular fa-kiss-wink-heart",
"fa-regular fa-laugh",
"fa-regular fa-laugh-beam",
"fa-regular fa-laugh-squint",
"fa-regular fa-laugh-wink",
"fa-regular fa-sad-cry",
"fa-regular fa-sad-tear",
"fa-regular fa-smile-beam",
"fa-regular fa-smile-wink",
"fa-regular fa-surprise",
"fa-regular fa-tired",
"fa-regular fa-angry",
"fa-regular fa-dizzy",
"fa-regular fa-flushed",
"fa-regular fa-grimace",
"fa-regular fa-hand-lizard",
"fa-regular fa-hand-paper",
"fa-regular fa-hand-peace",
"fa-regular fa-hand-point-down",
"fa-regular fa-hand-point-left",
"fa-regular fa-hand-point-right",
"fa-regular fa-hand-point-up",
"fa-regular fa-hand-pointer",
"fa-regular fa-hand-rock",
"fa-regular fa-hand-scissors",
"fa-regular fa-hand-spock",
"fa-regular fa-handshake",
"fa-regular fa-hdd",
"fa-regular fa-hospital",
"fa-regular fa-hourglass",
"fa-regular fa-id-badge",
"fa-regular fa-id-card",
"fa-regular fa-image",
"fa-regular fa-images",
"fa-regular fa-keyboard",
"fa-regular fa-lemon",
"fa-regular fa-life-ring",
"fa-regular fa-lightbulb",
"fa-regular fa-list-alt",
"fa-regular fa-map",
"fa-regular fa-money-bill-alt",
"fa-regular fa-moon",
"fa-regular fa-newspaper",
"fa-regular fa-object-group",
"fa-regular fa-object-ungroup",
"fa-regular fa-paper-plane",
"fa-regular fa-pause-circle",
"fa-regular fa-play-circle",
"fa-regular fa-plus-square",
"fa-regular fa-question-circle",
"fa-regular fa-registered",
"fa-regular fa-save",
"fa-regular fa-share-square",
"fa-regular fa-snowflake",
"fa-regular fa-star-half",
"fa-regular fa-sticky-note",
"fa-regular fa-stop-circle",
"fa-regular fa-sun",
"fa-regular fa-thumbs-down",
"fa-regular fa-thumbs-up",
"fa-regular fa-times-circle",
"fa-regular fa-trash-alt",
"fa-regular fa-user-circle",
"fa-regular fa-window-close",
"fa-regular fa-window-maximize",
"fa-regular fa-window-minimize",
"fa-regular fa-window-restore",
],
brands: [
"fa-brands fa-500px",
"fa-brands fa-accessible-icon",
"fa-brands fa-accusoft",
"fa-brands fa-acquisitions-incorporated",
"fa-brands fa-adn",
"fa-brands fa-adobe",
"fa-brands fa-adversal",
"fa-brands fa-affiliatetheme",
"fa-brands fa-airbnb",
"fa-brands fa-algolia",
"fa-brands fa-alipay",
"fa-brands fa-amazon",
"fa-brands fa-amazon-pay",
"fa-brands fa-amilia",
"fa-brands fa-android",
"fa-brands fa-angellist",
"fa-brands fa-angrycreative",
"fa-brands fa-angular",
"fa-brands fa-app-store",
"fa-brands fa-app-store-ios",
"fa-brands fa-apper",
"fa-brands fa-apple",
"fa-brands fa-apple-pay",
"fa-brands fa-artstation",
"fa-brands fa-asymmetrik",
"fa-brands fa-atlassian",
"fa-brands fa-audible",
"fa-brands fa-autoprefixer",
"fa-brands fa-avianex",
"fa-brands fa-aviato",
"fa-brands fa-aws",
"fa-brands fa-bandcamp",
"fa-brands fa-battle-net",
"fa-brands fa-behance",
"fa-brands fa-behance-square",
"fa-brands fa-bimobject",
"fa-brands fa-bitbucket",
"fa-brands fa-bitcoin",
"fa-brands fa-bity",
"fa-brands fa-black-tie",
"fa-brands fa-blackberry",
"fa-brands fa-blogger",
"fa-brands fa-blogger-b",
"fa-brands fa-bluetooth",
"fa-brands fa-bluetooth-b",
"fa-brands fa-bootstrap",
"fa-brands fa-btc",
"fa-brands fa-buffer",
"fa-brands fa-buromobelexperte",
"fa-brands fa-buy-n-large",
"fa-brands fa-buysellads",
"fa-brands fa-canadian-maple-leaf",
"fa-brands fa-cc-amazon-pay",
"fa-brands fa-cc-amex",
"fa-brands fa-cc-apple-pay",
"fa-brands fa-cc-diners-club",
"fa-brands fa-cc-discover",
"fa-brands fa-cc-jcb",
"fa-brands fa-cc-mastercard",
"fa-brands fa-cc-paypal",
"fa-brands fa-cc-stripe",
"fa-brands fa-cc-visa",
"fa-brands fa-centercode",
"fa-brands fa-centos",
"fa-brands fa-chrome",
"fa-brands fa-chromecast",
"fa-brands fa-cloudflare",
"fa-brands fa-cloudscale",
"fa-brands fa-cloudsmith",
"fa-brands fa-cloudversify",
"fa-brands fa-codepen",
"fa-brands fa-codiepie",
"fa-brands fa-confluence",
"fa-brands fa-connectdevelop",
"fa-brands fa-contao",
"fa-brands fa-cotton-bureau",
"fa-brands fa-cpanel",
"fa-brands fa-creative-commons",
"fa-brands fa-css3",
"fa-brands fa-css3-alt",
"fa-brands fa-cuttlefish",
"fa-brands fa-d-and-d",
"fa-brands fa-dashcube",
"fa-brands fa-deezer",
"fa-brands fa-delicious",
"fa-brands fa-deploydog",
"fa-brands fa-deskpro",
"fa-brands fa-dev",
"fa-brands fa-deviantart",
"fa-brands fa-dhl",
"fa-brands fa-diaspora",
"fa-brands fa-digg",
"fa-brands fa-digital-ocean",
"fa-brands fa-discord",
"fa-brands fa-discourse",
"fa-brands fa-dochub",
"fa-brands fa-docker",
"fa-brands fa-draft2digital",
"fa-brands fa-dribbble",
"fa-brands fa-dropbox",
"fa-brands fa-drupal",
"fa-brands fa-dyalog",
"fa-brands fa-earlybirds",
"fa-brands fa-ebay",
"fa-brands fa-edge",
"fa-brands fa-elementor",
"fa-brands fa-ello",
"fa-brands fa-ember",
"fa-brands fa-empire",
"fa-brands fa-envira",
"fa-brands fa-erlang",
"fa-brands fa-ethereum",
"fa-brands fa-etsy",
"fa-brands fa-evernote",
"fa-brands fa-expeditedssl",
"fa-brands fa-facebook",
"fa-brands fa-facebook-f",
"fa-brands fa-facebook-messenger",
"fa-brands fa-facebook-square",
"fa-brands fa-fantasy-flight-games",
"fa-brands fa-fedex",
"fa-brands fa-fedora",
"fa-brands fa-figma",
"fa-brands fa-firefox",
"fa-brands fa-firefox-browser",
"fa-brands fa-first-order",
"fa-brands fa-flickr",
"fa-brands fa-flipboard",
"fa-brands fa-fly",
"fa-brands fa-font-awesome",
"fa-brands fa-fonticons",
"fa-brands fa-fort-awesome",
"fa-brands fa-forumbee",
"fa-brands fa-foursquare",
"fa-brands fa-free-code-camp",
"fa-brands fa-freebsd",
"fa-brands fa-fulcrum",
"fa-brands fa-galactic-republic",
"fa-brands fa-galactic-senate",
"fa-brands fa-get-pocket",
"fa-brands fa-gg",
"fa-brands fa-git",
"fa-brands fa-git-alt",
"fa-brands fa-github",
"fa-brands fa-github-alt",
"fa-brands fa-github-square",
"fa-brands fa-gitkraken",
"fa-brands fa-gitlab",
"fa-brands fa-gitter",
"fa-brands fa-glide",
"fa-brands fa-gofore",
"fa-brands fa-goodreads",
"fa-brands fa-google",
"fa-brands fa-google-drive",
"fa-brands fa-google-pay",
"fa-brands fa-google-play",
"fa-brands fa-google-plus",
"fa-brands fa-google-plus-g",
"fa-brands fa-google-plus-square",
"fa-brands fa-google-wallet",
"fa-brands fa-gratipay",
"fa-brands fa-grav",
"fa-brands fa-gripfire",
"fa-brands fa-grunt",
"fa-brands fa-gulp",
"fa-brands fa-hacker-news",
"fa-brands fa-hackerrank",
"fa-brands fa-hips",
"fa-brands fa-hire-a-helper",
"fa-brands fa-hooli",
"fa-brands fa-hornbill",
"fa-brands fa-hotjar",
"fa-brands fa-houzz",
"fa-brands fa-html5",
"fa-brands fa-hubspot",
"fa-brands fa-ideal",
"fa-brands fa-imdb",
"fa-brands fa-instagram",
"fa-brands fa-intercom",
"fa-brands fa-internet-explorer",
"fa-brands fa-invision",
"fa-brands fa-ioxhost",
"fa-brands fa-itch-io",
"fa-brands fa-itunes",
"fa-brands fa-java",
"fa-brands fa-jedi-order",
"fa-brands fa-jenkins",
"fa-brands fa-jira",
"fa-brands fa-joget",
"fa-brands fa-joomla",
"fa-brands fa-js",
"fa-brands fa-js-square",
"fa-brands fa-jsfiddle",
"fa-brands fa-kaggle",
"fa-brands fa-keybase",
"fa-brands fa-keycdn",
"fa-brands fa-kickstarter",
"fa-brands fa-laravel",
"fa-brands fa-lastfm",
"fa-brands fa-leanpub",
"fa-brands fa-less",
"fa-brands fa-line",
"fa-brands fa-linkedin",
"fa-brands fa-linkedin-in",
"fa-brands fa-linode",
"fa-brands fa-linux",
"fa-brands fa-lyft",
"fa-brands fa-magento",
"fa-brands fa-mailchimp",
"fa-brands fa-mandalorian",
"fa-brands fa-markdown",
"fa-brands fa-mastodon",
"fa-brands fa-maxcdn",
"fa-brands fa-mdb",
"fa-brands fa-medapps",
"fa-brands fa-medium",
"fa-brands fa-medrt",
"fa-brands fa-meetup",
"fa-brands fa-megaport",
"fa-brands fa-mendeley",
"fa-brands fa-microblog",
"fa-brands fa-microsoft",
"fa-brands fa-mix",
"fa-brands fa-mixcloud",
"fa-brands fa-mixer",
"fa-brands fa-mizuni",
"fa-brands fa-modx",
"fa-brands fa-monero",
"fa-brands fa-napster",
"fa-brands fa-neos",
"fa-brands fa-nimblr",
"fa-brands fa-node",
"fa-brands fa-node-js",
"fa-brands fa-npm",
"fa-brands fa-ns8",
"fa-brands fa-nutritionix",
"fa-brands fa-odnoklassniki",
"fa-brands fa-old-republic",
"fa-brands fa-opencart",
"fa-brands fa-openid",
"fa-brands fa-opera",
"fa-brands fa-optin-monster",
"fa-brands fa-orcid",
"fa-brands fa-osi",
"fa-brands fa-page4",
"fa-brands fa-pagelines",
"fa-brands fa-palfed",
"fa-brands fa-patreon",
"fa-brands fa-paypal",
"fa-brands fa-penny-arcade",
"fa-brands fa-periscope",
"fa-brands fa-phabricator",
"fa-brands fa-phoenix-framework",
"fa-brands fa-phoenix-squadron",
"fa-brands fa-php",
"fa-brands fa-pied-piper",
"fa-brands fa-pinterest",
"fa-brands fa-pinterest-p",
"fa-brands fa-pinterest-square",
"fa-brands fa-playstation",
"fa-brands fa-product-hunt",
"fa-brands fa-pushed",
"fa-brands fa-python",
"fa-brands fa-qq",
"fa-brands fa-quinscape",
"fa-brands fa-quora",
"fa-brands fa-r-project",
"fa-brands fa-raspberry-pi",
"fa-brands fa-ravelry",
"fa-brands fa-react",
"fa-brands fa-reacteurope",
"fa-brands fa-readme",
"fa-brands fa-rebel",
"fa-brands fa-red-river",
"fa-brands fa-reddit",
"fa-brands fa-reddit-alien",
"fa-brands fa-reddit-square",
"fa-brands fa-redhat",
"fa-brands fa-renren",
"fa-brands fa-replyd",
"fa-brands fa-researchgate",
"fa-brands fa-resolving",
"fa-brands fa-rev",
"fa-brands fa-rocketchat",
"fa-brands fa-rockrms",
"fa-brands fa-safari",
"fa-brands fa-salesforce",
"fa-brands fa-sass",
"fa-brands fa-schlix",
"fa-brands fa-scribd",
"fa-brands fa-searchengin",
"fa-brands fa-sellcast",
"fa-brands fa-sellsy",
"fa-brands fa-servicestack",
"fa-brands fa-shirtsinbulk",
"fa-brands fa-shopify",
"fa-brands fa-shopware",
"fa-brands fa-simplybuilt",
"fa-brands fa-sistrix",
"fa-brands fa-sith",
"fa-brands fa-sketch",
"fa-brands fa-skyatlas",
"fa-brands fa-skype",
"fa-brands fa-slack",
"fa-brands fa-slack-hash",
"fa-brands fa-slideshare",
"fa-brands fa-snapchat",
"fa-brands fa-snapchat-ghost",
"fa-brands fa-snapchat-square",
"fa-brands fa-soundcloud",
"fa-brands fa-sourcetree",
"fa-brands fa-speakap",
"fa-brands fa-speaker-deck",
"fa-brands fa-spotify",
"fa-brands fa-squarespace",
"fa-brands fa-stack-exchange",
"fa-brands fa-stack-overflow",
"fa-brands fa-stackpath",
"fa-brands fa-staylinked",
"fa-brands fa-steam",
"fa-brands fa-steam-square",
"fa-brands fa-steam-symbol",
"fa-brands fa-sticker-mule",
"fa-brands fa-strava",
"fa-brands fa-stripe",
"fa-brands fa-stripe-s",
"fa-brands fa-studiovinari",
"fa-brands fa-stumbleupon",
"fa-brands fa-stumbleupon-circle",
"fa-brands fa-superpowers",
"fa-brands fa-supple",
"fa-brands fa-suse",
"fa-brands fa-swift",
"fa-brands fa-symfony",
"fa-brands fa-teamspeak",
"fa-brands fa-telegram",
"fa-brands fa-telegram-plane",
"fa-brands fa-tencent-weibo",
"fa-brands fa-the-red-yeti",
"fa-brands fa-themeco",
"fa-brands fa-themeisle",
"fa-brands fa-think-peaks",
"fa-brands fa-tiktok",
"fa-brands fa-trade-federation",
"fa-brands fa-trello",
"fa-brands fa-tripadvisor",
"fa-brands fa-tumblr",
"fa-brands fa-tumblr-square",
"fa-brands fa-twitch",
"fa-brands fa-twitter",
"fa-brands fa-twitter-square",
"fa-brands fa-typo3",
"fa-brands fa-uber",
"fa-brands fa-ubuntu",
"fa-brands fa-uikit",
"fa-brands fa-umbraco",
"fa-brands fa-uniregistry",
"fa-brands fa-unity",
"fa-brands fa-unsplash",
"fa-brands fa-untappd",
"fa-brands fa-ups",
"fa-brands fa-usb",
"fa-brands fa-usps",
"fa-brands fa-ussunnah",
"fa-brands fa-vaadin",
"fa-brands fa-viacoin",
"fa-brands fa-viadeo",
"fa-brands fa-viadeo-square",
"fa-brands fa-viber",
"fa-brands fa-vimeo",
"fa-brands fa-vimeo-square",
"fa-brands fa-vimeo-v",
"fa-brands fa-vine",
"fa-brands fa-vk",
"fa-brands fa-vnv",
"fa-brands fa-vuejs",
"fa-brands fa-waze",
"fa-brands fa-weebly",
"fa-brands fa-weibo",
"fa-brands fa-weixin",
"fa-brands fa-whatsapp",
"fa-brands fa-whatsapp-square",
"fa-brands fa-whmcs",
"fa-brands fa-wikipedia-w",
"fa-brands fa-windows",
"fa-brands fa-wix",
"fa-brands fa-wizards-of-the-coast",
"fa-brands fa-wolf-pack-battalion",
"fa-brands fa-wordpress",
"fa-brands fa-wordpress-simple",
"fa-brands fa-wpbeginner",
"fa-brands fa-wpexplorer",
"fa-brands fa-wpforms",
"fa-brands fa-wpressr",
"fa-brands fa-xbox",
"fa-brands fa-xing",
"fa-brands fa-xing-square",
"fa-brands fa-y-combinator",
"fa-brands fa-yahoo",
"fa-brands fa-yammer",
"fa-brands fa-yandex",
"fa-brands fa-yandex-international",
"fa-brands fa-yarn",
"fa-brands fa-yelp",
"fa-brands fa-yoast",
"fa-brands fa-youtube",
"fa-brands fa-youtube-square",
"fa-brands fa-zhihu",
],
};
// Function to generate the icon grid
function generateIconGrid(category = "all") {
const iconGrid = $("#icon-grid");
iconGrid.empty();
let iconsToShow = [];
if (category === "all") {
iconsToShow = [...icons.solid, ...icons.regular, ...icons.brands];
} else {
iconsToShow = icons[category] || [];
}
// Filter by search if necessary
const searchTerm = $("#icon-search").val().toLowerCase();
if (searchTerm) {
iconsToShow = iconsToShow.filter((icon) =>
icon.toLowerCase().includes(searchTerm)
);
}
// Limit to 200 icons for performance reasons
const limitedIcons = iconsToShow.slice(0, 200);
// Generate HTML elements for each icon
limitedIcons.forEach((icon) => {
const iconName = icon.split(" ").pop().replace("fa-", "");
const iconItem = $(`
<div class="icon-item" data-icon="${icon}" onclick="selectIcon('${icon}')">
<i class="${icon}"></i>
<span>${iconName}</span>
</div>
`);
iconGrid.append(iconItem);
});
// If no icon is found
if (limitedIcons.length === 0) {
iconGrid.append(
'<div style="grid-column: 1 / -1; text-align: center; padding: 20px;">No icons found</div>'
);
}
}
// Generate the initial icon grid
generateIconGrid();
// Filter icons by category
$(".icon-category").click(function () {
$(".icon-category").removeClass("active");
$(this).addClass("active");
generateIconGrid($(this).data("category"));
});
// Filter icons by search
$("#icon-search").on("input", function () {
const category = $(".icon-category.active").data("category");
generateIconGrid(category);
});
}

319
admin/edit-services.php Normal file
View File

@ -0,0 +1,319 @@
<?php
session_start();
include_once("../_conf/global.php");
include_once("../_conf/db.php");
// Connecting to the database
try {
$pdo = new PDO($db['dsn'], $db['user'], $db['pass']);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Database connection error: " . $e->getMessage());
}v
// Error and success messages
$error = '';
$success = '';
// Check if the user is logged in
$is_logged_in = isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true;
// Processing the login form
if (isset($_POST['login'])) {
$password = $_POST['password'];
try {
$stmt = $pdo->query("SELECT password_hash FROM admin LIMIT 1");
$admin = $stmt->fetch(PDO::FETCH_ASSOC);
if ($admin && password_verify($password, $admin['password_hash'])) {
$_SESSION['admin_logged_in'] = true;
$is_logged_in = true;
$success = "Connection successful!";
} else {
$error = "Incorrect password!";
}
} catch (PDOException $e) {
$error = "Error while verifying password: " . $e->getMessage();
}
}
// Processing the logout form
if (isset($_POST['logout'])) {
$_SESSION['admin_logged_in'] = false;
$is_logged_in = false;
$success = "Disconnected successfully!";
}
// Processing the service addition form
if ($is_logged_in && isset($_POST['add_service'])) {
$name = $_POST['name'];
$icon = $_POST['icon'];
$url = $_POST['url'];
try {
// Find the next order
$stmt = $pdo->query("SELECT MAX(order_num) as max_order FROM services");
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$next_order = ($result['max_order'] ?? 0) + 1;
$stmt = $pdo->prepare("INSERT INTO services (name, icon, url, order_num) VALUES (?, ?, ?, ?)");
$stmt->execute([$name, $icon, $url, $next_order]);
$success = "Service added successfully!";
} catch (PDOException $e) {
$error = "Error while adding the service: " . $e->getMessage();
}
}
// Processing the service change form
if ($is_logged_in && isset($_POST['edit_service'])) {
$id = $_POST['id'];
$name = $_POST['name'];
$icon = $_POST['icon'];
$url = $_POST['url'];
try {
$stmt = $pdo->prepare("UPDATE services SET name = ?, icon = ?, url = ? WHERE id = ?");
$stmt->execute([$name, $icon, $url, $id]);
$success = "Service successfully modified!";
} catch (PDOException $e) {
$error = "Error while modifying the service: " . $e->getMessage();
}
}
// Processing the service cancellation form
if ($is_logged_in && isset($_POST['delete_service'])) {
$id = $_POST['id'];
try {
$stmt = $pdo->prepare("DELETE FROM services WHERE id = ?");
$stmt->execute([$id]);
$success = "Service successfully deleted!";
} catch (PDOException $e) {
$error = "Error while deleting the service: " . $e->getMessage();
}
}
// Processing of the service reorganization form
if ($is_logged_in && isset($_POST['reorder'])) {
$ids = $_POST['service_ids'];
try {
$pdo->beginTransaction();
foreach ($ids as $order => $id) {
$stmt = $pdo->prepare("UPDATE services SET order_num = ? WHERE id = ?");
$stmt->execute([$order + 1, $id]);
}
$pdo->commit();
$success = "Services successfully reorganized!";
} catch (PDOException $e) {
$pdo->rollBack();
$error = "Error during service reorganization: " . $e->getMessage();
}
}
// Retrieving services from the database
try {
$stmt = $pdo->query("SELECT * FROM services ORDER BY order_num ASC");
$services = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$services = [];
$error = "Error retrieving services: " . $e->getMessage();
}
$title = $config['site']['name'];
$description = "Internal system administration area of the ". $config['site']['name'] ." website.";
$url = "https://". $config['site']['main_domain'] ."/services?edit";
$meta_image = "https://". $config['site']['imgs_domain'] ."/-/WaWA7/FoRUmETa47.png/raw";
$robots = "noindex, nofollow";
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Primary Meta Tags -->
<title><?php echo $title; ?> - Admin</title>
<meta name="title" content="<?php echo $title; ?> - Admin" />
<meta name="description" content="<?php echo $description; ?>" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="<?php echo $url; ?>" />
<meta property="og:title" content="<?php echo $title; ?> - Admin" />
<meta property="og:description" content="<?php echo $description; ?>" />
<meta property="og:image" content="<?php echo $meta_image; ?>" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="<?php echo $url; ?>" />
<meta property="twitter:title" content="<?php echo $title; ?> - Admin" />
<meta property="twitter:description" content="<?php echo $description; ?>" />
<meta property="twitter:image" content="<?php echo $meta_image; ?>" />
<meta name="robots" content="<?php echo $robots; ?>" />
<link rel="stylesheet" type="text/css" href="assets/css/edit-services.css">
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- jQuery for drag and drop functionality -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
</head>
<body>
<div class="container">
<div class="header">
<h1 class="title"><?php echo $title; ?> Admin</h1>
</div>
<?php if ($error): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<?php endif; ?>
<?php if ($success): ?>
<div class="alert alert-success"><?php echo $success; ?></div>
<?php endif; ?>
<?php if (!$is_logged_in): ?>
<!-- Login form -->
<div class="card">
<h2>Login</h2>
<form method="post">
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit" name="login">Log in</button>
</form>
</div>
<?php else: ?>
<!-- Logout form -->
<form method="post" style="text-align: right; margin-bottom: 1rem;">
<button type="submit" name="logout" class="btn-secondary">Log out</button>
</form>
<!-- Service Addition Form -->
<div class="card">
<h2>Add a service</h2>
<form method="post">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="icon">Icon</label>
<div class="icon-input-group">
<input type="text" id="icon" name="icon" class="icon-input" placeholder="fa-solid fa-envelope" required>
<i id="icon-preview" class="icon-preview fa-solid fa-envelope"></i>
<button type="button" class="icon-select-btn" onclick="openIconSelector('icon', 'icon-preview')">Choose</button>
</div>
</div>
<div class="form-group">
<label for="url">URL</label>
<input type="text" id="url" name="url" placeholder="https://example.com" required>
</div>
<button type="submit" name="add_service">Add</button>
</form>
</div>
<!-- List of services -->
<div class="card">
<h2>Services</h2>
<form method="post" id="reorder-form">
<table>
<thead>
<tr>
<th width="5%"></th>
<th width="10%">Icon</th>
<th width="25%">Name</th>
<th width="40%">URL</th>
<th width="20%">Actions</th>
</tr>
</thead>
<tbody id="sortable">
<?php foreach ($services as $service): ?>
<tr data-id="<?php echo $service['id']; ?>">
<td><i class="fas fa-grip-lines drag-handle"></i></td>
<td><i class="<?php echo htmlspecialchars($service['icon']); ?> service-icon"></i></td>
<td><?php echo htmlspecialchars($service['name']); ?></td>
<td><?php echo htmlspecialchars($service['url']); ?></td>
<td class="actions">
<button type="button" class="btn-secondary edit-btn"
data-id="<?php echo $service['id']; ?>"
data-name="<?php echo htmlspecialchars($service['name']); ?>"
data-icon="<?php echo htmlspecialchars($service['icon']); ?>"
data-url="<?php echo htmlspecialchars($service['url']); ?>">
<i class="fas fa-edit"></i>
</button>
<form method="post" style="display: inline;">
<input type="hidden" name="id" value="<?php echo $service['id']; ?>">
<button type="submit" name="delete_service" class="btn-danger" onclick="return confirm('Are you sure you want to delete this service?');">
<i class="fas fa-trash"></i>
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<input type="hidden" name="reorder" value="1">
<div id="service-ids-container"></div>
<button type="submit" id="save-order" style="margin-top: 1rem;">Save order</button>
</form>
</div>
<!-- Edit modal -->
<div id="edit-modal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 1000;">
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: white; padding: 2rem; border-radius: 0.5rem; width: 90%; max-width: 500px;">
<h2>Edit the service</h2>
<form method="post" id="edit-form">
<input type="hidden" id="edit-id" name="id">
<div class="form-group">
<label for="edit-name">Name</label>
<input type="text" id="edit-name" name="name" required>
</div>
<div class="form-group">
<label for="edit-icon">Icon</label>
<div class="icon-input-group">
<input type="text" id="edit-icon" name="icon" class="icon-input" required>
<i id="edit-icon-preview" class="icon-preview"></i>
<button type="button" class="icon-select-btn" onclick="openIconSelector('edit-icon', 'edit-icon-preview')">Choose</button>
</div>
</div>
<div class="form-group">
<label for="edit-url">URL</label>
<input type="text" id="edit-url" name="url" required>
</div>
<div style="display: flex; justify-content: space-between;">
<button type="button" id="cancel-edit" class="btn-secondary">Cancel</button>
<button type="submit" name="edit_service">Save</button>
</div>
</form>
</div>
</div>
<!-- Icon selector -->
<div id="icon-selector-modal" class="icon-selector-modal">
<div class="icon-selector-content">
<div class="icon-selector-header">
<h2>Select an icon</h2>
<button type="button" class="icon-selector-close" onclick="closeIconSelector()">&times;</button>
</div>
<input type="text" id="icon-search" class="icon-search" placeholder="Search for an icon...">
<div class="icon-categories">
<button type="button" class="icon-category active" data-category="all">All</button>
<button type="button" class="icon-category" data-category="solid">Solid</button>
<button type="button" class="icon-category" data-category="regular">Regular</button>
<button type="button" class="icon-category" data-category="brands">Brands</button>
</div>
<div id="icon-grid" class="icon-grid">
<!-- The icons will be loaded here by JavaScript. -->
</div>
</div>
</div>
<?php endif; ?>
<a href="/services" class="back-link">&larr; Back to home page</a>
</div>
<script src="assets/js/icon-selector.js"></script>
</body>
</html>

5
admin/index.php Normal file
View File

@ -0,0 +1,5 @@
<?php
header('Location: /');
?>

121
assets/css/my.css Normal file
View File

@ -0,0 +1,121 @@
/* Custom CSS */
:root {
--primary-color: #6366f1;
--secondary-color: #4f46e5;
--background-color: #f8fafc;
--card-bg: #ffffff;
--text-color: #1e293b;
--shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}
body {
background-color: var(--background-color);
color: var(--text-color);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem;
}
.container {
max-width: 1200px;
width: 100%;
text-align: center;
}
.title {
font-size: 5rem;
font-weight: 800;
margin-bottom: 0.5rem;
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
letter-spacing: -0.05em;
text-shadow: 3px 3px 6px rgba(0, 0, 0, 0.1);
}
.subtitle {
font-size: 1rem;
color: #64748b;
margin-bottom: 3rem;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 1.5rem;
margin-top: 2rem;
}
.service-card {
background-color: var(--card-bg);
border-radius: 12px;
padding: 1.5rem 1rem;
box-shadow: var(--shadow);
transition: transform 0.2s ease, box-shadow 0.2s ease;
position: relative;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-decoration: none; /* Remove underline from links */
}
.service-card:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.service-icon {
font-size: 2.5rem;
margin-bottom: 0.75rem;
color: var(--primary-color);
}
.service-name {
display: block;
font-size: 0.9rem;
margin-top: 0.5rem;
color: var(--text-color);
font-weight: 500;
}
.admin-link {
position: fixed;
bottom: 20px;
right: 20px;
color: #64748b;
text-decoration: none;
font-size: 0.8rem;
opacity: 0.5;
transition: opacity 0.2s;
}
.admin-link:hover {
opacity: 1;
}
@media (max-width: 768px) {
.title {
font-size: 3.5rem;
}
.services-grid {
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 1rem;
}
}

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

5
index.php Normal file
View File

@ -0,0 +1,5 @@
<?php
header('Location: /services');
?>

11
robots.txt Normal file
View File

@ -0,0 +1,11 @@
# robots.txt
# Prevents all robots from accessing sensitive areas
User-agent: *
Disallow: /admin/ # Block access to the admin folder
Disallow: /_conf/ # Blocks access to the configuration folder
# Optional: you can add a link to your sitemap to help bots crawl the rest efficiently.
# Sitemap: https://kvs.fyi/sitemap.xml
# End of file

86
services.php Normal file
View File

@ -0,0 +1,86 @@
<?php
include_once("./_conf/global.php");
include_once("./_conf/db.php");
// Connecting to the database
try {
$pdo = new PDO($db['dsn'], $db['user'], $db['pass']);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Database connection error: " . $e->getMessage());
}
// Retrieving services from the database
try {
$stmt = $pdo->query("SELECT * FROM services ORDER BY order_num ASC");
$services = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
// If the table does not yet exist, a default table is used.
$services = [
['icon' => 'fa-solid fa-envelope', 'name' => 'Mail Server', 'url' => '#'],
['icon' => 'fa-solid fa-cloud', 'name' => 'Cloud Storage', 'url' => '#'],
['icon' => 'fa-solid fa-calendar', 'name' => 'Calendar', 'url' => '#'],
['icon' => 'fa-solid fa-comments', 'name' => 'Chat', 'url' => '#']
];
}
// Check if you are in edit mode
$edit_mode = isset($_GET['edit']);
// If you are in edit mode, redirect to edit-services
if ($edit_mode) {
header('Location: admin/edit-services');
exit;
}
$title = $config['site']['name'];
$description = "Grouping of services offered by ". $config['site']['name'] . ".";
$url = "https://". $config['site']['main_domain'] ."/services";
$meta_image = "https://". $config['site']['imgs_domain'] ."/-/WaWA7/FoRUmETa47.png/raw";
$robots = "index, follow";
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Primary Meta Tags -->
<title><?php echo $title; ?></title>
<meta name="title" content="<?php echo $title; ?>" />
<meta name="description" content="<?php echo $description; ?>" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="<?php echo $url; ?>" />
<meta property="og:title" content="<?php echo $title; ?>" />
<meta property="og:description" content="<?php echo $description; ?>" />
<meta property="og:image" content="<?php echo $meta_image; ?>" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="<?php echo $url; ?>" />
<meta property="twitter:title" content="<?php echo $title; ?>" />
<meta property="twitter:description" content="<?php echo $description; ?>" />
<meta property="twitter:image" content="<?php echo $meta_image; ?>" />
<meta name="robots" content="<?php echo $robots; ?>" />
<link rel="stylesheet" type="text/css" href="assets/css/my.css">
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
<div class="container">
<h1 class="title"><?php echo $title; ?></h1>
<p class="subtitle">Public services managed by <a href="<?php echo $config['author']['bio']; ?>" title="See my Bio" target="_blank"><?php echo $config['author']['name']; ?></a></p>
<div class="services-grid">
<?php foreach ($services as $service): ?>
<a href="<?php echo htmlspecialchars($service['url']); ?>" title="<?php echo htmlspecialchars($service['name']) ?>" target="_blank" class="service-card">
<i class="service-icon <?php echo htmlspecialchars($service['icon']); ?>"></i>
<div class="service-name"><?php echo htmlspecialchars($service['name']); ?></div>
</a>
<?php endforeach; ?>
</div>
</div>
<a href="?edit" class="admin-link">Admin</a>
</body>
</html>