Compare commits
17 Commits
cb61082775
...
ac0f445252
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac0f445252 | ||
|
|
2944f9f509 | ||
|
|
a528142c4f | ||
|
|
e191a6916e | ||
|
|
347a95a312 | ||
|
|
fb07ff7d2e | ||
|
|
91b9902c51 | ||
|
|
0c4d8aefa9 | ||
|
|
2145a0438c | ||
|
|
e464a95e35 | ||
|
|
a785e059e9 | ||
|
|
8b445fdaaa | ||
|
|
492e43b1bd | ||
|
|
761c0bd6af | ||
|
|
f8ced7fba3 | ||
|
|
f940a81187 | ||
|
|
1d7d4e1902 |
@ -519,8 +519,14 @@ $current_session_user = isset($_SESSION['user']) ? (string) $_SESSION['user'] :
|
||||
|
||||
<nav class="nav-tabs">
|
||||
<a href="admin.php" class="active">Utilisateurs</a>
|
||||
<a href="scwebhook.php">WEBHOOK</a>
|
||||
<a href="scnotification.php">NOTIF DISCORD</a>
|
||||
<a href="scitems.php">Base d'Objets</a>
|
||||
<a href="scstatsitem.php">Stats Item</a>
|
||||
<a href="scitemcustom.php">Item Custom</a>
|
||||
<a href="scmining.php">Scanner Minage</a>
|
||||
<a href="scmanufactures.php">Manufactures</a>
|
||||
<a href="scvaisseaux.php">Vaisseaux</a>
|
||||
<a href="scpreset.php">Presets Vaisseau</a>
|
||||
</nav>
|
||||
|
||||
|
||||
BIN
assets/pasted-20260408-141737-37ed7d08.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
assets/pasted-20260408-143158-7f26570a.png
Normal file
|
After Width: | Height: | Size: 630 KiB |
BIN
assets/pasted-20260408-155112-5f68f53a.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
assets/pasted-20260408-162933-15bb71d4.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
assets/pasted-20260408-230208-7fa3495f.png
Normal file
|
After Width: | Height: | Size: 193 KiB |
BIN
assets/pasted-20260408-230935-cd4834ff.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/pasted-20260409-011806-1d0a77ea.png
Normal file
|
After Width: | Height: | Size: 342 KiB |
@ -5,6 +5,10 @@ define('DB_NAME', 'app_39514');
|
||||
define('DB_USER', 'app_39514');
|
||||
define('DB_PASS', 'ee6da88c-09af-4b48-b728-7a55edfb4e42');
|
||||
|
||||
if (!defined('DISCORD_BOT_TOKEN')) {
|
||||
define('DISCORD_BOT_TOKEN', 'MTQyNTgwNjIxOTMwNTY4MTAxOA.GGq1cp.CjHC7vQogGrX_HS2WnuXLf4XOLAnWLC5Cm-XA4');
|
||||
}
|
||||
|
||||
function db() {
|
||||
static $pdo;
|
||||
if (!$pdo) {
|
||||
|
||||
450
db/scdiscord.php
Normal file
@ -0,0 +1,450 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/config.php';
|
||||
|
||||
function scdiscord_bootstrap(): void
|
||||
{
|
||||
static $bootstrapped = false;
|
||||
|
||||
if ($bootstrapped) {
|
||||
return;
|
||||
}
|
||||
|
||||
$db = db();
|
||||
|
||||
$db->exec(
|
||||
"CREATE TABLE IF NOT EXISTS tbl_scwebhooks (
|
||||
cl_scwebhook_id INT(11) NOT NULL AUTO_INCREMENT,
|
||||
cl_scwebhook_name VARCHAR(255) NOT NULL,
|
||||
cl_scwebhook_url TEXT NOT NULL,
|
||||
cl_scwebhook_is_forum TINYINT(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (cl_scwebhook_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci"
|
||||
);
|
||||
|
||||
$db->exec(
|
||||
"CREATE TABLE IF NOT EXISTS tbl_scbanners (
|
||||
cl_scbanner_id INT(11) NOT NULL AUTO_INCREMENT,
|
||||
cl_scbanner_name VARCHAR(255) NOT NULL,
|
||||
cl_scbanner_url TEXT NOT NULL,
|
||||
cl_scbanner_border_color VARCHAR(20) NOT NULL DEFAULT '#ffae00',
|
||||
PRIMARY KEY (cl_scbanner_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci"
|
||||
);
|
||||
|
||||
$columns_stmt = $db->query("SHOW COLUMNS FROM tbl_scbanners LIKE 'cl_scbanner_border_color'");
|
||||
$has_border_color = (bool) $columns_stmt->fetch();
|
||||
if (!$has_border_color) {
|
||||
$db->exec("ALTER TABLE tbl_scbanners ADD COLUMN cl_scbanner_border_color VARCHAR(20) NOT NULL DEFAULT '#ffae00' AFTER cl_scbanner_url");
|
||||
}
|
||||
|
||||
$db->exec(
|
||||
"CREATE TABLE IF NOT EXISTS tbl_scnotifications (
|
||||
cl_scnotification_id INT(11) NOT NULL AUTO_INCREMENT,
|
||||
cl_scnotification_webhook_id INT(11) NOT NULL,
|
||||
cl_scnotification_banner_id INT(11) DEFAULT NULL,
|
||||
cl_scnotification_title VARCHAR(255) NOT NULL DEFAULT '',
|
||||
cl_scnotification_message TEXT NOT NULL,
|
||||
cl_scnotification_payload LONGTEXT NOT NULL,
|
||||
cl_scnotification_response LONGTEXT DEFAULT NULL,
|
||||
cl_scnotification_success TINYINT(1) NOT NULL DEFAULT 0,
|
||||
cl_scnotification_created_by VARCHAR(190) NOT NULL,
|
||||
cl_scnotification_created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (cl_scnotification_id),
|
||||
KEY idx_scnotification_webhook (cl_scnotification_webhook_id),
|
||||
KEY idx_scnotification_banner (cl_scnotification_banner_id),
|
||||
CONSTRAINT fk_scnotification_webhook FOREIGN KEY (cl_scnotification_webhook_id)
|
||||
REFERENCES tbl_scwebhooks (cl_scwebhook_id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE RESTRICT,
|
||||
CONSTRAINT fk_scnotification_banner FOREIGN KEY (cl_scnotification_banner_id)
|
||||
REFERENCES tbl_scbanners (cl_scbanner_id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci"
|
||||
);
|
||||
|
||||
$stmt_existing_red = $db->prepare('SELECT cl_scbanner_id FROM tbl_scbanners WHERE cl_scbanner_name = :name LIMIT 1');
|
||||
$stmt_existing_red->execute(['name' => 'Alerte Rouge']);
|
||||
$existing_red_banner = $stmt_existing_red->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($existing_red_banner) {
|
||||
$stmt_update_red = $db->prepare(
|
||||
'UPDATE tbl_scbanners
|
||||
SET cl_scbanner_border_color = CASE
|
||||
WHEN cl_scbanner_border_color IS NULL OR cl_scbanner_border_color = "" OR cl_scbanner_border_color = "#ffae00"
|
||||
THEN :border_color
|
||||
ELSE cl_scbanner_border_color
|
||||
END
|
||||
WHERE cl_scbanner_id = :id'
|
||||
);
|
||||
$stmt_update_red->execute([
|
||||
'border_color' => '#ff3b30',
|
||||
'id' => $existing_red_banner['cl_scbanner_id'],
|
||||
]);
|
||||
}
|
||||
|
||||
$bootstrapped = true;
|
||||
}
|
||||
|
||||
function scdiscord_mask_webhook_url(string $url): string
|
||||
{
|
||||
$trimmed = trim($url);
|
||||
if ($trimmed === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$length = strlen($trimmed);
|
||||
if ($length <= 24) {
|
||||
return str_repeat('•', max(8, $length));
|
||||
}
|
||||
|
||||
return substr($trimmed, 0, 32) . str_repeat('•', 18) . substr($trimmed, -10);
|
||||
}
|
||||
|
||||
function scdiscord_normalize_hex_color(string $color): string
|
||||
{
|
||||
$candidate = strtoupper(trim($color));
|
||||
if ($candidate === '') {
|
||||
return '#FFAE00';
|
||||
}
|
||||
|
||||
if ($candidate[0] !== '#') {
|
||||
$candidate = '#' . $candidate;
|
||||
}
|
||||
|
||||
if (!preg_match('/^#[0-9A-F]{6}$/', $candidate)) {
|
||||
return '#FFAE00';
|
||||
}
|
||||
|
||||
return $candidate;
|
||||
}
|
||||
|
||||
function scdiscord_hex_to_decimal(string $color): int
|
||||
{
|
||||
return hexdec(ltrim(scdiscord_normalize_hex_color($color), '#'));
|
||||
}
|
||||
|
||||
function scdiscord_build_mentions(bool $notify_here, bool $notify_everyone): array
|
||||
{
|
||||
$parts = [];
|
||||
|
||||
if ($notify_here) {
|
||||
$parts[] = '@here';
|
||||
}
|
||||
|
||||
if ($notify_everyone) {
|
||||
$parts[] = '@everyone';
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
function scdiscord_build_thread_name(string $title, string $location, string $start_date): string
|
||||
{
|
||||
$parts = [];
|
||||
|
||||
if ($title !== '') {
|
||||
$parts[] = $title;
|
||||
}
|
||||
|
||||
if ($location !== '') {
|
||||
$parts[] = $location;
|
||||
}
|
||||
|
||||
if ($start_date !== '') {
|
||||
$parts[] = $start_date;
|
||||
}
|
||||
|
||||
$thread_name = trim(implode(' • ', $parts));
|
||||
|
||||
if ($thread_name === '') {
|
||||
$thread_name = 'Notification Discord';
|
||||
}
|
||||
|
||||
return mb_substr($thread_name, 0, 100);
|
||||
}
|
||||
|
||||
function scdiscord_post_webhook(string $webhook_url, array $payload): array
|
||||
{
|
||||
$target_url = $webhook_url;
|
||||
$separator = (str_contains($target_url, '?')) ? '&' : '?';
|
||||
$target_url .= $separator . 'wait=true';
|
||||
|
||||
$json_payload = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
if ($json_payload === false) {
|
||||
return [
|
||||
'success' => false,
|
||||
'http_code' => 0,
|
||||
'response' => 'Erreur d\'encodage JSON.',
|
||||
];
|
||||
}
|
||||
|
||||
if (function_exists('curl_init')) {
|
||||
$ch = curl_init($target_url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $json_payload,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . strlen($json_payload),
|
||||
],
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 20,
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
return [
|
||||
'success' => false,
|
||||
'http_code' => $http_code,
|
||||
'response' => $curl_error !== '' ? $curl_error : 'Erreur CURL inconnue.',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => $http_code >= 200 && $http_code < 300,
|
||||
'http_code' => $http_code,
|
||||
'response' => $response,
|
||||
];
|
||||
}
|
||||
|
||||
$context = stream_context_create([
|
||||
'http' => [
|
||||
'method' => 'POST',
|
||||
'header' => "Content-Type: application/json\r\n",
|
||||
'content' => $json_payload,
|
||||
'timeout' => 20,
|
||||
'ignore_errors' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$response = @file_get_contents($target_url, false, $context);
|
||||
$http_code = 0;
|
||||
if (isset($http_response_header) && is_array($http_response_header)) {
|
||||
foreach ($http_response_header as $header_line) {
|
||||
if (preg_match('#^HTTP/\S+\s+(\d{3})#', $header_line, $matches)) {
|
||||
$http_code = (int) $matches[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => $response !== false && $http_code >= 200 && $http_code < 300,
|
||||
'http_code' => $http_code,
|
||||
'response' => $response === false ? 'Erreur lors de la requête HTTP.' : $response,
|
||||
];
|
||||
}
|
||||
|
||||
function scdiscord_get_bot_token(): string
|
||||
{
|
||||
$candidates = [];
|
||||
|
||||
if (defined('DISCORD_BOT_TOKEN') && is_string(DISCORD_BOT_TOKEN)) {
|
||||
$candidates[] = DISCORD_BOT_TOKEN;
|
||||
}
|
||||
|
||||
foreach (['DISCORD_BOT_TOKEN', 'SC_DISCORD_BOT_TOKEN', 'BOT_TOKEN'] as $env_key) {
|
||||
$value = getenv($env_key);
|
||||
if ($value !== false) {
|
||||
$candidates[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($candidates as $candidate) {
|
||||
$token = trim((string) $candidate);
|
||||
if ($token !== '') {
|
||||
return $token;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function scdiscord_decode_json_response(?string $response): array
|
||||
{
|
||||
if (!is_string($response) || trim($response) === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$decoded = json_decode($response, true);
|
||||
return is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
|
||||
function scdiscord_bot_request(string $method, string $url, string $bot_token, ?array $payload = null): array
|
||||
{
|
||||
$headers = [
|
||||
'Authorization: Bot ' . $bot_token,
|
||||
];
|
||||
|
||||
$json_payload = null;
|
||||
if ($payload !== null) {
|
||||
$json_payload = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
if ($json_payload === false) {
|
||||
return [
|
||||
'success' => false,
|
||||
'http_code' => 0,
|
||||
'response' => 'Erreur d\'encodage JSON.',
|
||||
];
|
||||
}
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
$headers[] = 'Content-Length: ' . strlen($json_payload);
|
||||
}
|
||||
|
||||
if (function_exists('curl_init')) {
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_CUSTOMREQUEST => strtoupper($method),
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 20,
|
||||
]);
|
||||
|
||||
if ($json_payload !== null) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $json_payload);
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
return [
|
||||
'success' => false,
|
||||
'http_code' => $http_code,
|
||||
'response' => $curl_error !== '' ? $curl_error : 'Erreur CURL inconnue.',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => $http_code >= 200 && $http_code < 300,
|
||||
'http_code' => $http_code,
|
||||
'response' => $response,
|
||||
];
|
||||
}
|
||||
|
||||
$header_lines = implode("\r\n", $headers) . "\r\n";
|
||||
$context = stream_context_create([
|
||||
'http' => [
|
||||
'method' => strtoupper($method),
|
||||
'header' => $header_lines,
|
||||
'content' => $json_payload ?? '',
|
||||
'timeout' => 20,
|
||||
'ignore_errors' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$response = @file_get_contents($url, false, $context);
|
||||
$http_code = 0;
|
||||
if (isset($http_response_header) && is_array($http_response_header)) {
|
||||
foreach ($http_response_header as $header_line) {
|
||||
if (preg_match('#^HTTP/\S+\s+(\d{3})#', $header_line, $matches)) {
|
||||
$http_code = (int) $matches[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => $response !== false && $http_code >= 200 && $http_code < 300,
|
||||
'http_code' => $http_code,
|
||||
'response' => $response === false ? 'Erreur lors de la requête HTTP.' : $response,
|
||||
];
|
||||
}
|
||||
|
||||
function scdiscord_apply_bot_actions(array $message_data, bool $use_reactions, bool $use_publicthread, string $thread_name): array
|
||||
{
|
||||
if (!$use_reactions && !$use_publicthread) {
|
||||
return [
|
||||
'success' => true,
|
||||
'http_code' => 200,
|
||||
'response' => 'Aucune action bot demandée.',
|
||||
'details' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$message_id = trim((string) ($message_data['id'] ?? ''));
|
||||
$channel_id = trim((string) ($message_data['channel_id'] ?? ''));
|
||||
|
||||
if ($message_id === '' || $channel_id === '') {
|
||||
return [
|
||||
'success' => false,
|
||||
'http_code' => 0,
|
||||
'response' => 'Réponse Discord invalide : id de message ou channel_id manquant.',
|
||||
'details' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$bot_token = scdiscord_get_bot_token();
|
||||
if ($bot_token === '') {
|
||||
return [
|
||||
'success' => false,
|
||||
'http_code' => 0,
|
||||
'response' => 'Token bot Discord manquant. Définis DISCORD_BOT_TOKEN côté serveur.',
|
||||
'details' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$details = [];
|
||||
$last_http_code = 200;
|
||||
$failed = false;
|
||||
|
||||
if ($use_reactions) {
|
||||
foreach (['👍', '⌛', '❔', '👎'] as $emoji) {
|
||||
$url = 'https://discord.com/api/v10/channels/' . rawurlencode($channel_id) . '/messages/' . rawurlencode($message_id) . '/reactions/' . rawurlencode($emoji) . '/@me';
|
||||
$result = scdiscord_bot_request('PUT', $url, $bot_token);
|
||||
$action_success = !empty($result['success']);
|
||||
$last_http_code = (int) ($result['http_code'] ?? $last_http_code);
|
||||
$details[] = [
|
||||
'action' => 'reaction',
|
||||
'emoji' => $emoji,
|
||||
'success' => $action_success,
|
||||
'http_code' => $last_http_code,
|
||||
'response' => (string) ($result['response'] ?? ''),
|
||||
];
|
||||
|
||||
if (!$action_success) {
|
||||
$failed = true;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ($use_publicthread) {
|
||||
$thread_payload = [
|
||||
'name' => $thread_name !== '' ? $thread_name : 'Discussion - Opération',
|
||||
'auto_archive_duration' => 1440,
|
||||
'type' => 11,
|
||||
];
|
||||
$url = 'https://discord.com/api/v10/channels/' . rawurlencode($channel_id) . '/messages/' . rawurlencode($message_id) . '/threads';
|
||||
$result = scdiscord_bot_request('POST', $url, $bot_token, $thread_payload);
|
||||
$action_success = !empty($result['success']);
|
||||
$last_http_code = (int) ($result['http_code'] ?? $last_http_code);
|
||||
$details[] = [
|
||||
'action' => 'thread',
|
||||
'success' => $action_success,
|
||||
'http_code' => $last_http_code,
|
||||
'response' => (string) ($result['response'] ?? ''),
|
||||
];
|
||||
|
||||
if (!$action_success) {
|
||||
$failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => !$failed,
|
||||
'http_code' => $failed ? $last_http_code : 200,
|
||||
'response' => json_encode($details, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'details' => $details,
|
||||
];
|
||||
}
|
||||
|
||||
143
db/scitemcustom.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/config.php';
|
||||
|
||||
function scitemcustom_column_exists(PDO $db, string $table, string $column): bool
|
||||
{
|
||||
$stmt = $db->query("SHOW COLUMNS FROM `{$table}` LIKE " . $db->quote($column));
|
||||
|
||||
return (bool) $stmt->fetch();
|
||||
}
|
||||
|
||||
function scitemcustom_column_definition(PDO $db, string $table, string $column): ?array
|
||||
{
|
||||
$stmt = $db->query("SHOW COLUMNS FROM `{$table}` LIKE " . $db->quote($column));
|
||||
$column_definition = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
return $column_definition ?: null;
|
||||
}
|
||||
|
||||
function scitemcustom_index_exists(PDO $db, string $table, string $index): bool
|
||||
{
|
||||
$stmt = $db->query("SHOW INDEX FROM `{$table}` WHERE Key_name = " . $db->quote($index));
|
||||
|
||||
return (bool) $stmt->fetch();
|
||||
}
|
||||
|
||||
function scitemcustom_foreign_key_exists(PDO $db, string $table, string $constraint): bool
|
||||
{
|
||||
$stmt = $db->prepare(
|
||||
"SELECT COUNT(*)
|
||||
FROM information_schema.TABLE_CONSTRAINTS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = :table_name
|
||||
AND CONSTRAINT_NAME = :constraint_name
|
||||
AND CONSTRAINT_TYPE = 'FOREIGN KEY'"
|
||||
);
|
||||
$stmt->execute([
|
||||
'table_name' => $table,
|
||||
'constraint_name' => $constraint,
|
||||
]);
|
||||
|
||||
return (int) $stmt->fetchColumn() > 0;
|
||||
}
|
||||
|
||||
function scitemcustom_bootstrap(): void
|
||||
{
|
||||
static $scitemcustom_bootstrap_done = false;
|
||||
|
||||
if ($scitemcustom_bootstrap_done) {
|
||||
return;
|
||||
}
|
||||
|
||||
$db = db();
|
||||
|
||||
$db->exec(
|
||||
"CREATE TABLE IF NOT EXISTS tbl_scitemcustom (
|
||||
cl_scitemcustom_id INT(11) NOT NULL AUTO_INCREMENT,
|
||||
cl_scitemcustom_owner_auth_id INT UNSIGNED NOT NULL,
|
||||
cl_scitemcustom_obj_id INT(10) UNSIGNED NOT NULL,
|
||||
cl_scitemcustom_created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (cl_scitemcustom_id),
|
||||
UNIQUE KEY uq_scitemcustom_owner_obj (cl_scitemcustom_owner_auth_id, cl_scitemcustom_obj_id),
|
||||
KEY idx_scitemcustom_owner (cl_scitemcustom_owner_auth_id),
|
||||
KEY idx_scitemcustom_obj (cl_scitemcustom_obj_id),
|
||||
CONSTRAINT fk_scitemcustom_owner_auth FOREIGN KEY (cl_scitemcustom_owner_auth_id)
|
||||
REFERENCES tbl_auth (cl_auth_id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
CONSTRAINT fk_scitemcustom_obj FOREIGN KEY (cl_scitemcustom_obj_id)
|
||||
REFERENCES tbl_scobjs (cl_scobjs_id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
||||
);
|
||||
|
||||
if (!scitemcustom_column_exists($db, 'tbl_scitemcustom', 'cl_scitemcustom_owner_auth_id')) {
|
||||
$db->exec(
|
||||
'ALTER TABLE tbl_scitemcustom
|
||||
ADD COLUMN cl_scitemcustom_owner_auth_id INT UNSIGNED NULL AFTER cl_scitemcustom_id'
|
||||
);
|
||||
}
|
||||
|
||||
if (scitemcustom_index_exists($db, 'tbl_scitemcustom', 'uq_scitemcustom_obj')) {
|
||||
$db->exec('ALTER TABLE tbl_scitemcustom DROP INDEX uq_scitemcustom_obj');
|
||||
}
|
||||
|
||||
if (!scitemcustom_index_exists($db, 'tbl_scitemcustom', 'idx_scitemcustom_owner')) {
|
||||
$db->exec(
|
||||
'ALTER TABLE tbl_scitemcustom
|
||||
ADD INDEX idx_scitemcustom_owner (cl_scitemcustom_owner_auth_id)'
|
||||
);
|
||||
}
|
||||
|
||||
if (!scitemcustom_index_exists($db, 'tbl_scitemcustom', 'uq_scitemcustom_owner_obj')) {
|
||||
$db->exec(
|
||||
'ALTER TABLE tbl_scitemcustom
|
||||
ADD UNIQUE KEY uq_scitemcustom_owner_obj (cl_scitemcustom_owner_auth_id, cl_scitemcustom_obj_id)'
|
||||
);
|
||||
}
|
||||
|
||||
if (!scitemcustom_foreign_key_exists($db, 'tbl_scitemcustom', 'fk_scitemcustom_owner_auth')) {
|
||||
$db->exec(
|
||||
'ALTER TABLE tbl_scitemcustom
|
||||
ADD CONSTRAINT fk_scitemcustom_owner_auth FOREIGN KEY (cl_scitemcustom_owner_auth_id)
|
||||
REFERENCES tbl_auth (cl_auth_id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE'
|
||||
);
|
||||
}
|
||||
|
||||
$db->exec(
|
||||
"CREATE TABLE IF NOT EXISTS tbl_scitemcustomstat (
|
||||
cl_scitemcustomstat_id INT(11) NOT NULL AUTO_INCREMENT,
|
||||
cl_scitemcustomstat_itemcustom_id INT(11) NOT NULL,
|
||||
cl_scitemcustomstat_stat_id INT(11) NOT NULL,
|
||||
cl_scitemcustomstat_sign ENUM('+', '', '-') NOT NULL DEFAULT '+',
|
||||
cl_scitemcustomstat_value DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||||
cl_scitemcustomstat_created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (cl_scitemcustomstat_id),
|
||||
UNIQUE KEY uq_scitemcustomstat_item_stat (cl_scitemcustomstat_itemcustom_id, cl_scitemcustomstat_stat_id),
|
||||
KEY idx_scitemcustomstat_item (cl_scitemcustomstat_itemcustom_id),
|
||||
KEY idx_scitemcustomstat_stat (cl_scitemcustomstat_stat_id),
|
||||
CONSTRAINT fk_scitemcustomstat_item FOREIGN KEY (cl_scitemcustomstat_itemcustom_id)
|
||||
REFERENCES tbl_scitemcustom (cl_scitemcustom_id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
CONSTRAINT fk_scitemcustomstat_stat FOREIGN KEY (cl_scitemcustomstat_stat_id)
|
||||
REFERENCES tbl_scstatsitem (cl_scstatsitem_id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
||||
);
|
||||
|
||||
$sign_column = scitemcustom_column_definition($db, 'tbl_scitemcustomstat', 'cl_scitemcustomstat_sign');
|
||||
if ($sign_column && ($sign_column['Type'] ?? '') !== "enum('+','','-')") {
|
||||
$db->exec(
|
||||
"ALTER TABLE tbl_scitemcustomstat
|
||||
MODIFY COLUMN cl_scitemcustomstat_sign ENUM('+', '', '-') NOT NULL DEFAULT '+'"
|
||||
);
|
||||
}
|
||||
|
||||
$scitemcustom_bootstrap_done = true;
|
||||
}
|
||||
32
db/scstatsitem.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/config.php';
|
||||
|
||||
function scstatsitem_bootstrap(): void
|
||||
{
|
||||
static $bootstrapped = false;
|
||||
|
||||
if ($bootstrapped) {
|
||||
return;
|
||||
}
|
||||
|
||||
$db = db();
|
||||
$db->exec(
|
||||
"CREATE TABLE IF NOT EXISTS tbl_scstatsitem (
|
||||
cl_scstatsitem_id INT(11) NOT NULL AUTO_INCREMENT,
|
||||
cl_scstatsitem_name VARCHAR(255) NOT NULL,
|
||||
cl_scstatsitem_unit VARCHAR(10) NOT NULL DEFAULT '%',
|
||||
cl_scstatsitem_created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (cl_scstatsitem_id),
|
||||
UNIQUE KEY uq_scstatsitem_name (cl_scstatsitem_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci"
|
||||
);
|
||||
|
||||
$columns_stmt = $db->query("SHOW COLUMNS FROM tbl_scstatsitem LIKE 'cl_scstatsitem_unit'");
|
||||
$has_unit = (bool) $columns_stmt->fetch();
|
||||
if (!$has_unit) {
|
||||
$db->exec("ALTER TABLE tbl_scstatsitem ADD COLUMN cl_scstatsitem_unit VARCHAR(10) NOT NULL DEFAULT '%' AFTER cl_scstatsitem_name");
|
||||
}
|
||||
|
||||
$bootstrapped = true;
|
||||
}
|
||||
1070
scitemcustom.php
Normal file
14
scitems.php
@ -64,9 +64,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$uuid = $item['reference'] ?? ($item['stdItem']['UUID'] ?? '');
|
||||
if (!$uuid) continue;
|
||||
|
||||
$name = $item['stdItem']['Name'] ?? '';
|
||||
$type = $item['type'] ?? '';
|
||||
$subtype = $item['subType'] ?? '';
|
||||
$name = $item['Name'] ?? ($item['stdItem']['Name'] ?? '');
|
||||
$classification = $item['classification'] ?? ''; $parts = explode('.', $classification); $type = $parts[1] ?? ($item['type'] ?? '');
|
||||
$subtype = $parts[2] ?? ($item['subType'] ?? '');
|
||||
|
||||
$stmt_check->execute(['uuid' => $uuid]);
|
||||
if ($stmt_check->fetch()) {
|
||||
@ -401,8 +401,14 @@ if ($edit_id > 0) {
|
||||
|
||||
<nav class="nav-tabs">
|
||||
<a href="admin.php">Utilisateurs</a>
|
||||
<a href="scwebhook.php">WEBHOOK</a>
|
||||
<a href="scnotification.php">NOTIF DISCORD</a>
|
||||
<a href="scitems.php" class="active">Base d'Objets</a>
|
||||
<a href="scstatsitem.php">Stats Item</a>
|
||||
<a href="scitemcustom.php">Item Custom</a>
|
||||
<a href="scmining.php">Scanner Minage</a>
|
||||
<a href="scmanufactures.php">Manufactures</a>
|
||||
<a href="scvaisseaux.php">Vaisseaux</a>
|
||||
<a href="scpreset.php">Presets Vaisseau</a>
|
||||
</nav>
|
||||
|
||||
@ -480,7 +486,7 @@ if ($edit_id > 0) {
|
||||
<input type="text" name="search" class="form-control" placeholder="Rechercher par nom, type, uuid..." value="<?php echo htmlspecialchars($search); ?>">
|
||||
<button type="submit" class="btn-modern">Filtrer</button>
|
||||
<?php if ($search !== ''): ?>
|
||||
<a href="scitems.php" class="btn-modern danger">Reset</a>
|
||||
<a href="scitems.php" class="btn-modern danger">Reset</a>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
389
scmanufactures.php
Normal file
@ -0,0 +1,389 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/db/auth.php';
|
||||
|
||||
auth_start_session();
|
||||
auth_bootstrap();
|
||||
|
||||
if (!auth_is_admin()) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$flash = auth_flash_get();
|
||||
$flash_type = $flash['type'] ?? '';
|
||||
$flash_message = $flash['message'] ?? '';
|
||||
|
||||
$db = db();
|
||||
$csrf_token = auth_csrf_token();
|
||||
|
||||
// Handle POST actions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$submitted_csrf = $_POST['csrf_token'] ?? '';
|
||||
if (!auth_validate_csrf($submitted_csrf)) {
|
||||
auth_flash_set('error', 'Jeton CSRF invalide.');
|
||||
header('Location: scmanufactures.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
// Add manufacture
|
||||
if ($action === 'add_manufacture') {
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
if ($name !== '') {
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO tbl_scmanufactures (cl_scmanufactures_name) VALUES (:name)");
|
||||
$stmt->execute(['name' => $name]);
|
||||
auth_flash_set('success', 'Manufacture ajoutée avec succès.');
|
||||
} catch (PDOException $e) {
|
||||
if ($e->getCode() == 23000) {
|
||||
auth_flash_set('error', 'Cette manufacture existe déjà.');
|
||||
} else {
|
||||
auth_flash_set('error', 'Erreur lors de l\'ajout : ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auth_flash_set('error', 'Le nom de la manufacture est requis.');
|
||||
}
|
||||
header('Location: scmanufactures.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Update manufacture
|
||||
if ($action === 'update_manufacture') {
|
||||
$id = (int)($_POST['manufacture_id'] ?? 0);
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
if ($id > 0 && $name !== '') {
|
||||
try {
|
||||
$stmt = $db->prepare("UPDATE tbl_scmanufactures SET cl_scmanufactures_name = :name WHERE cl_scmanufactures_id = :id");
|
||||
$stmt->execute(['name' => $name, 'id' => $id]);
|
||||
auth_flash_set('success', 'Manufacture mise à jour.');
|
||||
} catch (PDOException $e) {
|
||||
auth_flash_set('error', 'Erreur lors de la mise à jour : ' . $e->getMessage());
|
||||
}
|
||||
} else {
|
||||
auth_flash_set('error', 'Données invalides.');
|
||||
}
|
||||
header('Location: scmanufactures.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Delete manufacture
|
||||
if ($action === 'delete_manufacture') {
|
||||
$id = (int)($_POST['manufacture_id'] ?? 0);
|
||||
if ($id > 0) {
|
||||
try {
|
||||
$stmt = $db->prepare("DELETE FROM tbl_scmanufactures WHERE cl_scmanufactures_id = :id");
|
||||
$stmt->execute(['id' => $id]);
|
||||
auth_flash_set('success', 'Manufacture supprimée.');
|
||||
} catch (PDOException $e) {
|
||||
auth_flash_set('error', 'Erreur lors de la suppression. Assurez-vous qu\'aucun vaisseau n\'est lié à cette manufacture.');
|
||||
}
|
||||
}
|
||||
header('Location: scmanufactures.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all manufactures
|
||||
$stmt_list = $db->query("SELECT * FROM tbl_scmanufactures ORDER BY cl_scmanufactures_name ASC");
|
||||
$manufactures = $stmt_list->fetchAll();
|
||||
|
||||
$current_session_user = $_SESSION['user'] ?? '';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Manufactures | R.E.A.C.T. Admin</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/styles.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/default.css">
|
||||
<style>
|
||||
:root {
|
||||
--primary: #a29b78;
|
||||
--primary-glow: rgba(162, 155, 120, 0.4);
|
||||
--bg-dark: #080a0f;
|
||||
--card-bg: rgba(20, 24, 33, 0.85);
|
||||
--border-glow: rgba(162, 155, 120, 0.25);
|
||||
--danger: #ff4d4d;
|
||||
--success: #00ff88;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Electrolize';
|
||||
src: url('fonts/Electrolize-Regular.ttf') format('truetype');
|
||||
}
|
||||
|
||||
body {
|
||||
background: radial-gradient(circle at top right, #1a1f2e, var(--bg-dark));
|
||||
background-attachment: fixed;
|
||||
color: #e0e0e0;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.admin-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.admin-topbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1.5rem 2rem;
|
||||
background: var(--card-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--border-glow);
|
||||
border-radius: 12px;
|
||||
margin-bottom: 2rem;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.topbar-info h1 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
background: linear-gradient(90deg, #fff, var(--primary));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.topbar-info p {
|
||||
margin: 0.25rem 0 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--primary);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
padding: 0.6rem 1.2rem;
|
||||
border: 1px solid var(--primary);
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.btn-modern:hover {
|
||||
background: var(--primary);
|
||||
color: var(--bg-dark);
|
||||
box-shadow: 0 0 15px var(--primary-glow);
|
||||
}
|
||||
|
||||
.btn-modern.danger { border-color: var(--danger); color: var(--danger); }
|
||||
.btn-modern.danger:hover { background: var(--danger); color: #fff; }
|
||||
|
||||
.btn-mini { padding: 0.3rem 0.6rem; font-size: 0.75rem; }
|
||||
|
||||
.nav-tabs { display: flex; gap: 1rem; margin-bottom: 2rem; border-bottom: 1px solid var(--border-glow); padding-bottom: 1rem; }
|
||||
.nav-tabs a { text-decoration: none; color: #888; text-transform: uppercase; font-size: 0.9rem; transition: color 0.3s; }
|
||||
.nav-tabs a:hover, .nav-tabs a.active { color: var(--primary); }
|
||||
|
||||
.admin-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.admin-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
background: var(--card-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--border-glow);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.glass-card h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary);
|
||||
border-bottom: 1px solid var(--border-glow);
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.form-group { margin-bottom: 1.5rem; }
|
||||
.form-group label { display: block; margin-bottom: 0.5rem; font-size: 0.85rem; color: #aaa; text-transform: uppercase; }
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.8rem 1rem;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid #444;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
.form-control:focus { outline: none; border-color: var(--primary); background: rgba(0, 0, 0, 0.5); }
|
||||
|
||||
.modern-table { width: 100%; border-collapse: separate; border-spacing: 0 8px; }
|
||||
.modern-table th { text-align: left; padding: 1rem; font-size: 0.8rem; text-transform: uppercase; color: var(--primary); opacity: 0.7; }
|
||||
.modern-table td { padding: 1rem; background: rgba(255, 255, 255, 0.03); border-top: 1px solid rgba(255, 255, 255, 0.05); border-bottom: 1px solid rgba(255, 255, 255, 0.05); }
|
||||
.modern-table td:first-child { border-left: 1px solid rgba(255, 255, 255, 0.05); border-radius: 8px 0 0 8px; }
|
||||
.modern-table td:last-child { border-right: 1px solid rgba(255, 255, 255, 0.05); border-radius: 0 8px 8px 0; }
|
||||
.modern-table tr:hover td { background: rgba(162, 155, 120, 0.05); }
|
||||
|
||||
.flash { padding: 1rem 1.5rem; border-radius: 8px; margin-bottom: 1.5rem; font-size: 0.9rem; border-left: 4px solid var(--primary); background: rgba(162, 155, 120, 0.1); }
|
||||
.flash.error { border-color: var(--danger); background: rgba(255, 77, 77, 0.1); color: #ffbaba; }
|
||||
.flash.success { border-color: var(--success); background: rgba(0, 255, 136, 0.1); color: #baffda; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin-layout">
|
||||
<header class="admin-topbar">
|
||||
<div class="topbar-info">
|
||||
<h1>Gestion Manufactures</h1>
|
||||
<p>Niveau d\'accès : <strong>Administrateur</strong> | Session : <strong><?php echo htmlspecialchars($current_session_user); ?></strong></p>
|
||||
</div>
|
||||
<div class="topbar-actions">
|
||||
<a href="index.php" class="btn-modern">Site</a>
|
||||
<a href="logout.php" class="btn-modern danger">Exit</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="nav-tabs">
|
||||
<a href="admin.php">Utilisateurs</a>
|
||||
<a href="scwebhook.php">WEBHOOK</a>
|
||||
<a href="scnotification.php">NOTIF DISCORD</a>
|
||||
<a href="scitems.php">Base d'Objets</a>
|
||||
<a href="scstatsitem.php">Stats Item</a>
|
||||
<a href="scitemcustom.php">Item Custom</a>
|
||||
<a href="scmining.php">Scanner Minage</a>
|
||||
<a href="scmanufactures.php" class="active">Manufactures</a>
|
||||
<a href="scvaisseaux.php">Vaisseaux</a>
|
||||
<a href="scpreset.php">Presets Vaisseau</a>
|
||||
</nav>
|
||||
|
||||
<?php if ($flash_message !== ''): ?>
|
||||
<div class="flash <?php echo htmlspecialchars($flash_type); ?>">
|
||||
<?php echo htmlspecialchars($flash_message); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="admin-grid">
|
||||
<!-- Left Column: Add/Edit -->
|
||||
<div class="side-panel">
|
||||
<section class="glass-card">
|
||||
<h2 id="formTitle">Nouvelle Manufacture</h2>
|
||||
<form id="manufactureForm" method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
|
||||
<input type="hidden" name="action" id="formAction" value="add_manufacture">
|
||||
<input type="hidden" name="manufacture_id" id="manufactureId" value="">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Nom de la Manufacture</label>
|
||||
<input type="text" name="name" id="manufactureName" class="form-control" required placeholder="ex: Roberts Space Industries">
|
||||
</div>
|
||||
|
||||
<button type="submit" id="submitBtn" class="btn-modern" style="width: 100%;">Ajouter</button>
|
||||
<button type="button" id="cancelBtn" class="btn-modern" style="width: 100%; margin-top: 10px; display: none;" onclick="resetForm()">Annuler</button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Right Column: List -->
|
||||
<main class="main-panel">
|
||||
<section class="glass-card">
|
||||
<h2>Liste des Manufactures</h2>
|
||||
<div style="overflow-x: auto;">
|
||||
<table class="modern-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Nom</th>
|
||||
<th style="text-align: right;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($manufactures)): ?>
|
||||
<tr><td colspan="3" style="text-align: center; padding: 3rem; color: #666;">Aucune manufacture enregistrée.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($manufactures as $m): ?>
|
||||
<tr>
|
||||
<td style="width: 50px; opacity: 0.5;">#<?php echo $m['cl_scmanufactures_id']; ?></td>
|
||||
<td>
|
||||
<strong style="color: var(--primary); text-transform: uppercase;"><?php echo htmlspecialchars($m['cl_scmanufactures_name']); ?></strong>
|
||||
</td>
|
||||
<td style="text-align: right;">
|
||||
<div style="display: flex; gap: 5px; justify-content: flex-end;">
|
||||
<button type="button" class="btn-modern btn-mini"
|
||||
onclick='editManufacture(<?php echo json_encode([
|
||||
"id" => $m["cl_scmanufactures_id"],
|
||||
"name" => $m["cl_scmanufactures_name"]
|
||||
]); ?>)'>
|
||||
Edit
|
||||
</button>
|
||||
<form method="post" onsubmit="return confirm('Supprimer cette manufacture ?');">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
|
||||
<input type="hidden" name="action" value="delete_manufacture">
|
||||
<input type="hidden" name="manufacture_id" value="<?php echo $m['cl_scmanufactures_id']; ?>">
|
||||
<button type="submit" class="btn-modern btn-mini danger">X</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function editManufacture(data) {
|
||||
document.getElementById('formAction').value = 'update_manufacture';
|
||||
document.getElementById('manufactureId').value = data.id;
|
||||
document.getElementById('manufactureName').value = data.name;
|
||||
|
||||
document.getElementById('submitBtn').innerText = 'Mettre à jour';
|
||||
document.getElementById('cancelBtn').style.display = 'block';
|
||||
document.getElementById('formTitle').innerText = 'Modifier Manufacture';
|
||||
|
||||
document.getElementById('manufactureForm').scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
document.getElementById('formAction').value = 'add_manufacture';
|
||||
document.getElementById('manufactureId').value = '';
|
||||
document.getElementById('manufactureForm').reset();
|
||||
|
||||
document.getElementById('submitBtn').innerText = 'Ajouter';
|
||||
document.getElementById('cancelBtn').style.display = 'none';
|
||||
document.getElementById('formTitle').innerText = 'Nouvelle Manufacture';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
204
scmining.php
@ -31,6 +31,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Add mineral to list
|
||||
if ($action === 'add_mineral') {
|
||||
$obj_id = (int)$_POST['obj_id'];
|
||||
$return_search = trim($_POST['return_search'] ?? '');
|
||||
$return_page = max(1, (int)($_POST['return_page'] ?? 1));
|
||||
|
||||
if ($obj_id > 0) {
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO tbl_scmining (cl_scmining_obj_id, cl_scmining_scan_value, cl_scmining_max_occurrence) VALUES (:obj_id, 0, 1)");
|
||||
@ -40,11 +43,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if ($e->getCode() == 23000) {
|
||||
auth_flash_set('error', 'Ce minéral est déjà dans la liste.');
|
||||
} else {
|
||||
auth_flash_set('error', 'Erreur lors de l\'ajout : ' . $e->getMessage());
|
||||
auth_flash_set('error', "Erreur lors de l'ajout : " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
header('Location: scmining.php');
|
||||
header('Location: ' . scmining_search_url($return_search, $return_page));
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -85,16 +88,77 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
|
||||
// Search for adding items
|
||||
function sc_normalize_rarity(?string $rarity): string
|
||||
{
|
||||
return strtoupper(trim((string) $rarity));
|
||||
}
|
||||
|
||||
function sc_rarity_class(?string $rarity): string
|
||||
{
|
||||
$rarity = sc_normalize_rarity($rarity);
|
||||
|
||||
return in_array($rarity, ['L', 'E', 'R', 'U', 'C'], true) ? 'rarity-' . $rarity : 'rarity-none';
|
||||
}
|
||||
|
||||
function sc_rarity_style(?string $rarity): string
|
||||
{
|
||||
return match (sc_normalize_rarity($rarity)) {
|
||||
'L' => 'color:#ff8000 !important;text-shadow:0 0 10px rgba(255,128,0,0.3);',
|
||||
'E' => 'color:#a335ee !important;text-shadow:0 0 10px rgba(163,53,238,0.3);',
|
||||
'R' => 'color:#0070dd !important;text-shadow:0 0 10px rgba(0,112,221,0.3);',
|
||||
'U' => 'color:#1eff00 !important;text-shadow:0 0 10px rgba(30,255,0,0.3);',
|
||||
'C' => 'color:#ffffff !important;',
|
||||
default => 'color:#8f96a3 !important;',
|
||||
};
|
||||
}
|
||||
|
||||
function scmining_search_url(string $search = '', int $page = 1): string
|
||||
{
|
||||
$params = [];
|
||||
|
||||
if ($search !== '') {
|
||||
$params['search'] = $search;
|
||||
}
|
||||
|
||||
if ($page > 1) {
|
||||
$params['search_page'] = $page;
|
||||
}
|
||||
|
||||
return 'scmining.php' . ($params ? '?' . http_build_query($params) : '');
|
||||
}
|
||||
|
||||
$search = isset($_GET['search']) ? trim($_GET['search']) : '';
|
||||
$search_page = max(1, (int)($_GET['search_page'] ?? 1));
|
||||
$search_per_page = 10;
|
||||
$search_results = [];
|
||||
$search_total_results = 0;
|
||||
$search_total_pages = 0;
|
||||
$configured_matches = [];
|
||||
|
||||
if ($search !== '') {
|
||||
$stmt_search = $db->prepare("SELECT * FROM tbl_scobjs WHERE cl_scobjs_name LIKE :search AND cl_scobjs_id NOT IN (SELECT cl_scmining_obj_id FROM tbl_scmining) ORDER BY cl_scobjs_name ASC LIMIT 10");
|
||||
$stmt_search->execute(['search' => "%$search%"]);
|
||||
$stmt_configured = $db->prepare("SELECT o.* FROM tbl_scobjs o JOIN tbl_scmining m ON m.cl_scmining_obj_id = o.cl_scobjs_id WHERE o.cl_scobjs_name LIKE :search ORDER BY o.cl_scobjs_name ASC, o.cl_scobjs_id ASC");
|
||||
$stmt_configured->execute(['search' => "%$search%"]);
|
||||
$configured_matches = $stmt_configured->fetchAll();
|
||||
|
||||
$search_where = "FROM tbl_scobjs o WHERE o.cl_scobjs_name LIKE :search AND o.cl_scobjs_id NOT IN (SELECT cl_scmining_obj_id FROM tbl_scmining)";
|
||||
|
||||
$stmt_search_count = $db->prepare("SELECT COUNT(*) " . $search_where);
|
||||
$stmt_search_count->execute(['search' => "%$search%"]);
|
||||
$search_total_results = (int)$stmt_search_count->fetchColumn();
|
||||
$search_total_pages = max(1, (int)ceil($search_total_results / $search_per_page));
|
||||
$search_page = min($search_page, $search_total_pages);
|
||||
$search_offset = ($search_page - 1) * $search_per_page;
|
||||
|
||||
$stmt_search = $db->prepare("SELECT o.* " . $search_where . " ORDER BY o.cl_scobjs_name ASC, o.cl_scobjs_id ASC LIMIT :limit OFFSET :offset");
|
||||
$stmt_search->bindValue(':search', "%$search%", PDO::PARAM_STR);
|
||||
$stmt_search->bindValue(':limit', $search_per_page, PDO::PARAM_INT);
|
||||
$stmt_search->bindValue(':offset', $search_offset, PDO::PARAM_INT);
|
||||
$stmt_search->execute();
|
||||
$search_results = $stmt_search->fetchAll();
|
||||
}
|
||||
|
||||
// Fetch current mining list
|
||||
$sql_list = "SELECT m.*, o.cl_scobjs_name, o.cl_scobjs_uuid, o.cl_scobjs_type, o.cl_scobjs_subtype
|
||||
$sql_list = "SELECT m.*, o.cl_scobjs_name, o.cl_scobjs_uuid, o.cl_scobjs_type, o.cl_scobjs_subtype, o.cl_scobjs_rarity
|
||||
FROM tbl_scmining m
|
||||
JOIN tbl_scobjs o ON m.cl_scmining_obj_id = o.cl_scobjs_id
|
||||
ORDER BY o.cl_scobjs_name ASC";
|
||||
@ -120,6 +184,12 @@ $current_session_user = $_SESSION['user'] ?? '';
|
||||
--border-glow: rgba(162, 155, 120, 0.25);
|
||||
--danger: #ff4d4d;
|
||||
--success: #00ff88;
|
||||
|
||||
--rarity-L: #ff8000;
|
||||
--rarity-E: #a335ee;
|
||||
--rarity-R: #0070dd;
|
||||
--rarity-U: #1eff00;
|
||||
--rarity-C: #ffffff;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@ -205,6 +275,12 @@ $current_session_user = $_SESSION['user'] ?? '';
|
||||
box-shadow: 0 0 15px var(--primary-glow);
|
||||
}
|
||||
|
||||
.btn-modern.active {
|
||||
background: var(--primary);
|
||||
color: var(--bg-dark);
|
||||
box-shadow: 0 0 15px var(--primary-glow);
|
||||
}
|
||||
|
||||
.btn-modern.danger { border-color: var(--danger); color: var(--danger); }
|
||||
.btn-modern.danger:hover { background: var(--danger); color: #fff; }
|
||||
|
||||
@ -290,7 +366,60 @@ $current_session_user = $_SESSION['user'] ?? '';
|
||||
|
||||
.search-result-info { flex: 1; }
|
||||
.search-result-name { display: block; color: var(--primary); font-weight: bold; }
|
||||
.search-result-uuid { display: block; font-size: 0.75rem; color: #777; font-family: monospace; word-break: break-all; margin-top: 2px; }
|
||||
.search-result-meta { display: block; font-size: 0.75rem; color: #888; }
|
||||
.search-results-section { margin-bottom: 1.25rem; }
|
||||
.search-results-caption {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 0.78rem;
|
||||
color: #9a9a9a;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.search-result-item.is-configured {
|
||||
border-color: rgba(162, 155, 120, 0.35);
|
||||
background: rgba(162, 155, 120, 0.08);
|
||||
}
|
||||
.search-result-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.35rem 0.65rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(162, 155, 120, 0.35);
|
||||
color: var(--primary);
|
||||
font-size: 0.72rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.search-results-summary {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.8rem;
|
||||
color: #9a9a9a;
|
||||
}
|
||||
.pagination-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.pagination-controls .btn-modern {
|
||||
min-width: 42px;
|
||||
}
|
||||
.pagination-status {
|
||||
font-size: 0.8rem;
|
||||
color: #9a9a9a;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.rarity-L { color: var(--rarity-L) !important; text-shadow: 0 0 10px rgba(255, 128, 0, 0.3); }
|
||||
.rarity-E { color: var(--rarity-E) !important; text-shadow: 0 0 10px rgba(163, 53, 238, 0.3); }
|
||||
.rarity-R { color: var(--rarity-R) !important; text-shadow: 0 0 10px rgba(0, 112, 221, 0.3); }
|
||||
.rarity-U { color: var(--rarity-U) !important; text-shadow: 0 0 10px rgba(30, 255, 0, 0.3); }
|
||||
.rarity-C { color: var(--rarity-C) !important; }
|
||||
.rarity-none { color: #8f96a3 !important; }
|
||||
|
||||
.val-input { width: 100px; text-align: center; }
|
||||
|
||||
@ -330,8 +459,14 @@ $current_session_user = $_SESSION['user'] ?? '';
|
||||
|
||||
<nav class="nav-tabs">
|
||||
<a href="admin.php">Utilisateurs</a>
|
||||
<a href="scwebhook.php">WEBHOOK</a>
|
||||
<a href="scnotification.php">NOTIF DISCORD</a>
|
||||
<a href="scitems.php">Base d'Objets</a>
|
||||
<a href="scstatsitem.php">Stats Item</a>
|
||||
<a href="scitemcustom.php">Item Custom</a>
|
||||
<a href="scmining.php" class="active">Scanner Minage</a>
|
||||
<a href="scmanufactures.php">Manufactures</a>
|
||||
<a href="scvaisseaux.php">Vaisseaux</a>
|
||||
<a href="scpreset.php">Presets Vaisseau</a>
|
||||
</nav>
|
||||
|
||||
@ -353,26 +488,79 @@ $current_session_user = $_SESSION['user'] ?? '';
|
||||
|
||||
<?php if ($search !== ''): ?>
|
||||
<div class="search-results">
|
||||
<?php if (!empty($configured_matches)): ?>
|
||||
<div class="search-results-section">
|
||||
<div class="search-results-caption">Déjà configuré<?php echo count($configured_matches) > 1 ? 's' : ''; ?> pour cette recherche</div>
|
||||
<?php foreach ($configured_matches as $configured):
|
||||
if (is_array($configured) && isset($configured['cl_scobjs_uuid']) && isset($configured['cl_scobjs_name']) && isset($configured['cl_scobjs_type']) && isset($configured['cl_scobjs_subtype'])) {
|
||||
$configured_rarity_code = $configured['cl_scobjs_rarity'] ?? '';
|
||||
$configured_rarity_class = sc_rarity_class($configured_rarity_code);
|
||||
?>
|
||||
<div class="search-result-item is-configured">
|
||||
<img src="https://cstone.space/uifimages/<?php echo $configured['cl_scobjs_uuid']; ?>.png" class="item-preview" alt="">
|
||||
<div class="search-result-info">
|
||||
<span class="search-result-name <?php echo htmlspecialchars($configured_rarity_class); ?>" style="<?php echo htmlspecialchars(sc_rarity_style($configured_rarity_code)); ?>"><?php echo htmlspecialchars($configured['cl_scobjs_name']); ?></span>
|
||||
<span class="search-result-uuid">UUID: <?php echo htmlspecialchars($configured['cl_scobjs_uuid']); ?></span>
|
||||
<span class="search-result-meta"><?php echo htmlspecialchars($configured['cl_scobjs_type']); ?> / <?php echo htmlspecialchars($configured['cl_scobjs_subtype']); ?> — déjà présent dans Configuration Minerais</span>
|
||||
</div>
|
||||
<span class="search-result-badge">Déjà configuré</span>
|
||||
</div>
|
||||
<?php } endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (empty($search_results)): ?>
|
||||
<p style="text-align: center; color: #666;">Aucun objet non listé trouvé.</p>
|
||||
<?php else: ?>
|
||||
<?php $search_first_result = (($search_page - 1) * $search_per_page) + 1; ?>
|
||||
<?php $search_last_result = min($search_total_results, $search_first_result + count($search_results) - 1); ?>
|
||||
<div class="search-results-summary">
|
||||
Résultats <?php echo $search_first_result; ?> à <?php echo $search_last_result; ?> sur <?php echo $search_total_results; ?> — page <?php echo $search_page; ?>/<?php echo $search_total_pages; ?>
|
||||
</div>
|
||||
<?php foreach ($search_results as $res):
|
||||
if (is_array($res) && isset($res['cl_scobjs_uuid']) && isset($res['cl_scobjs_name']) && isset($res['cl_scobjs_type']) && isset($res['cl_scobjs_subtype']) && isset($res['cl_scobjs_id'])) {
|
||||
$rarity_code = $res['cl_scobjs_rarity'] ?? '';
|
||||
$rarity_class = sc_rarity_class($rarity_code);
|
||||
?>
|
||||
<div class="search-result-item">
|
||||
<img src="https://cstone.space/uifimages/<?php echo $res['cl_scobjs_uuid']; ?>.png" class="item-preview" alt="">
|
||||
<div class="search-result-info">
|
||||
<span class="search-result-name"><?php echo htmlspecialchars($res['cl_scobjs_name']); ?></span>
|
||||
<span class="search-result-name <?php echo htmlspecialchars($rarity_class); ?>" style="<?php echo htmlspecialchars(sc_rarity_style($rarity_code)); ?>"><?php echo htmlspecialchars($res['cl_scobjs_name']); ?></span>
|
||||
<span class="search-result-uuid">UUID: <?php echo htmlspecialchars($res['cl_scobjs_uuid']); ?></span>
|
||||
<span class="search-result-meta"><?php echo htmlspecialchars($res['cl_scobjs_type']); ?> / <?php echo htmlspecialchars($res['cl_scobjs_subtype']); ?></span>
|
||||
</div>
|
||||
<form method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
|
||||
<input type="hidden" name="action" value="add_mineral">
|
||||
<input type="hidden" name="obj_id" value="<?php echo $res['cl_scobjs_id']; ?>">
|
||||
<input type="hidden" name="return_search" value="<?php echo htmlspecialchars($search); ?>">
|
||||
<input type="hidden" name="return_page" value="<?php echo $search_page; ?>">
|
||||
<button type="submit" class="btn-modern btn-mini">+</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php } endforeach; ?>
|
||||
|
||||
<?php if ($search_total_pages > 1): ?>
|
||||
<?php
|
||||
$page_window_start = max(1, $search_page - 2);
|
||||
$page_window_end = min($search_total_pages, $search_page + 2);
|
||||
?>
|
||||
<div class="pagination-controls">
|
||||
<?php if ($search_page > 1): ?>
|
||||
<a href="<?php echo htmlspecialchars(scmining_search_url($search, $search_page - 1)); ?>" class="btn-modern btn-mini">«</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php for ($page_number = $page_window_start; $page_number <= $page_window_end; $page_number++): ?>
|
||||
<a href="<?php echo htmlspecialchars(scmining_search_url($search, $page_number)); ?>" class="btn-modern btn-mini<?php echo $page_number === $search_page ? ' active' : ''; ?>"><?php echo $page_number; ?></a>
|
||||
<?php endfor; ?>
|
||||
|
||||
<?php if ($search_page < $search_total_pages): ?>
|
||||
<a href="<?php echo htmlspecialchars(scmining_search_url($search, $search_page + 1)); ?>" class="btn-modern btn-mini">»</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<span class="pagination-status">Navigation des homonymes activée</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
@ -401,13 +589,15 @@ $current_session_user = $_SESSION['user'] ?? '';
|
||||
<?php else: ?>
|
||||
<?php foreach ($mining_list as $item):
|
||||
if (is_array($item) && isset($item['cl_scobjs_uuid']) && isset($item['cl_scobjs_name']) && isset($item['cl_scobjs_type']) && isset($item['cl_scobjs_subtype']) && isset($item['cl_scmining_id']) && isset($item['cl_scmining_scan_value']) && isset($item['cl_scmining_max_occurrence'])) {
|
||||
$rarity_code = $item['cl_scobjs_rarity'] ?? '';
|
||||
$rarity_class = sc_rarity_class($rarity_code);
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://cstone.space/uifimages/<?php echo $item['cl_scobjs_uuid']; ?>.png" class="item-preview" alt="">
|
||||
</td>
|
||||
<td>
|
||||
<strong style="color: var(--primary);"><?php echo htmlspecialchars($item['cl_scobjs_name']); ?></strong><br>
|
||||
<strong class="<?php echo htmlspecialchars($rarity_class); ?>" style="<?php echo htmlspecialchars(sc_rarity_style($rarity_code)); ?>"><?php echo htmlspecialchars($item['cl_scobjs_name']); ?></strong><br>
|
||||
<span style="font-size: 0.75rem; color: #888;"><?php echo htmlspecialchars($item['cl_scobjs_type']); ?> / <?php echo htmlspecialchars($item['cl_scobjs_subtype']); ?></span>
|
||||
</td>
|
||||
<form method="post">
|
||||
|
||||
968
scnotification.php
Normal file
@ -0,0 +1,968 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/db/auth.php';
|
||||
require_once __DIR__ . '/db/scdiscord.php';
|
||||
|
||||
auth_start_session();
|
||||
auth_bootstrap();
|
||||
scdiscord_bootstrap();
|
||||
|
||||
if (!auth_is_logged_in()) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = db();
|
||||
$csrf_token = auth_csrf_token();
|
||||
$flash = auth_flash_get();
|
||||
$flash_type = $flash['type'] ?? '';
|
||||
$flash_message = $flash['message'] ?? '';
|
||||
$current_session_user = $_SESSION['user'] ?? '';
|
||||
$current_session_role = $_SESSION['role'] ?? 'member';
|
||||
$role_label = ($current_session_role === 'admin') ? 'Administrateur' : 'Membre';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$submitted_csrf = (string) ($_POST['csrf_token'] ?? '');
|
||||
if (!auth_validate_csrf($submitted_csrf)) {
|
||||
auth_flash_set('error', 'Jeton CSRF invalide.');
|
||||
header('Location: scnotification.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$_SESSION['scnotification_old'] = $_POST;
|
||||
|
||||
$action = (string) ($_POST['action'] ?? '');
|
||||
$is_legacy_send = $action === '' && (isset($_POST['description']) || isset($_POST['title']) || isset($_POST['webhook_choice']));
|
||||
if ($action === 'send_notification' || $is_legacy_send) {
|
||||
$legacy_webhook_choice = trim((string) ($_POST['webhook_choice'] ?? ''));
|
||||
$cl_scnotification_webhook_id = (int) ($_POST['cl_scnotification_webhook_id'] ?? 0);
|
||||
if ($cl_scnotification_webhook_id <= 0 && preg_match('/^notif(\d+)$/', $legacy_webhook_choice, $matches)) {
|
||||
$cl_scnotification_webhook_id = (int) $matches[1];
|
||||
}
|
||||
|
||||
$cl_scnotification_banner_id = (int) ($_POST['cl_scnotification_banner_id'] ?? 0);
|
||||
$use_custom_banner = isset($_POST['cl_scnotification_use_custom_banner']) || isset($_POST['use_custom_banner']);
|
||||
$cl_scnotification_custom_banner_url = trim((string) ($_POST['cl_scnotification_custom_banner_url'] ?? ($_POST['custom_banner'] ?? '')));
|
||||
$notify_here = isset($_POST['cl_scnotification_notify_here']) || isset($_POST['ping_here']);
|
||||
$notify_everyone = isset($_POST['cl_scnotification_notify_everyone']) || isset($_POST['ping_everyone']);
|
||||
$cl_scnotification_title = trim((string) ($_POST['cl_scnotification_title'] ?? ($_POST['title'] ?? '')));
|
||||
$cl_scnotification_message = trim((string) ($_POST['cl_scnotification_message'] ?? ($_POST['description'] ?? '')));
|
||||
$show_footer = isset($_POST['cl_scnotification_show_footer']);
|
||||
$cl_scnotification_footer_text = trim((string) ($_POST['cl_scnotification_footer_text'] ?? ''));
|
||||
$cl_scnotification_footer_icon_url = trim((string) ($_POST['cl_scnotification_footer_icon_url'] ?? ''));
|
||||
$show_reactions = isset($_POST['cl_scnotification_show_reactions']) || isset($_POST['use_reactions']);
|
||||
$show_thread = isset($_POST['cl_scnotification_show_thread']) || isset($_POST['use_publicthread']);
|
||||
$show_org = isset($_POST['cl_scnotification_show_org']);
|
||||
$cl_scnotification_org_value = trim((string) ($_POST['cl_scnotification_org_value'] ?? ''));
|
||||
$show_pvp = isset($_POST['cl_scnotification_show_pvp']);
|
||||
$cl_scnotification_pvp_value = trim((string) ($_POST['cl_scnotification_pvp_value'] ?? ''));
|
||||
$include_schedule = isset($_POST['cl_scnotification_include_schedule']);
|
||||
$cl_scnotification_location = trim((string) ($_POST['cl_scnotification_location'] ?? ''));
|
||||
$cl_scnotification_start_date = trim((string) ($_POST['cl_scnotification_start_date'] ?? ''));
|
||||
$cl_scnotification_departure_time = trim((string) ($_POST['cl_scnotification_departure_time'] ?? ''));
|
||||
$include_briefing_time = isset($_POST['cl_scnotification_include_briefing_time']);
|
||||
$cl_scnotification_briefing_time = trim((string) ($_POST['cl_scnotification_briefing_time'] ?? ''));
|
||||
$include_end_date = isset($_POST['cl_scnotification_include_end_date']);
|
||||
$cl_scnotification_end_date = trim((string) ($_POST['cl_scnotification_end_date'] ?? ''));
|
||||
$include_end_time = isset($_POST['cl_scnotification_include_end_time']);
|
||||
$cl_scnotification_end_time = trim((string) ($_POST['cl_scnotification_end_time'] ?? ''));
|
||||
$show_channel_url = isset($_POST['cl_scnotification_show_channel_url']);
|
||||
$cl_scnotification_channel_url = trim((string) ($_POST['cl_scnotification_channel_url'] ?? ''));
|
||||
$show_inventory_url = isset($_POST['cl_scnotification_show_inventory_url']);
|
||||
$cl_scnotification_inventory_url = trim((string) ($_POST['cl_scnotification_inventory_url'] ?? ''));
|
||||
$show_source_url = isset($_POST['cl_scnotification_show_source_url']);
|
||||
$cl_scnotification_source_url = trim((string) ($_POST['cl_scnotification_source_url'] ?? ''));
|
||||
|
||||
if ($cl_scnotification_webhook_id <= 0) {
|
||||
auth_flash_set('error', 'Veuillez sélectionner un webhook Discord.');
|
||||
header('Location: scnotification.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($cl_scnotification_message === '') {
|
||||
auth_flash_set('error', 'Le champ description est obligatoire pour envoyer la notification.');
|
||||
header('Location: scnotification.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt_webhook = $db->prepare('SELECT * FROM tbl_scwebhooks WHERE cl_scwebhook_id = :id LIMIT 1');
|
||||
$stmt_webhook->execute(['id' => $cl_scnotification_webhook_id]);
|
||||
$webhook = $stmt_webhook->fetch();
|
||||
|
||||
if (!$webhook) {
|
||||
auth_flash_set('error', 'Webhook Discord introuvable.');
|
||||
header('Location: scnotification.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$banner = null;
|
||||
if ($cl_scnotification_banner_id > 0) {
|
||||
$stmt_banner = $db->prepare('SELECT * FROM tbl_scbanners WHERE cl_scbanner_id = :id LIMIT 1');
|
||||
$stmt_banner->execute(['id' => $cl_scnotification_banner_id]);
|
||||
$banner = $stmt_banner->fetch();
|
||||
}
|
||||
|
||||
if ($use_custom_banner && $cl_scnotification_custom_banner_url !== '' && !filter_var($cl_scnotification_custom_banner_url, FILTER_VALIDATE_URL)) {
|
||||
auth_flash_set('error', 'L’URL de bannière personnalisée est invalide.');
|
||||
header('Location: scnotification.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
foreach ([
|
||||
'URL canal Discord' => [$show_channel_url, $cl_scnotification_channel_url],
|
||||
'URL inventaire' => [$show_inventory_url, $cl_scnotification_inventory_url],
|
||||
'URL source' => [$show_source_url, $cl_scnotification_source_url],
|
||||
'Icône du footer' => [$show_footer && $cl_scnotification_footer_icon_url !== '', $cl_scnotification_footer_icon_url],
|
||||
] as $label => [$enabled, $value]) {
|
||||
if ($enabled && $value !== '' && !filter_var($value, FILTER_VALIDATE_URL)) {
|
||||
auth_flash_set('error', $label . ' invalide.');
|
||||
header('Location: scnotification.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$mentions = scdiscord_build_mentions($notify_here, $notify_everyone);
|
||||
$banner_image_url = $use_custom_banner && $cl_scnotification_custom_banner_url !== ''
|
||||
? $cl_scnotification_custom_banner_url
|
||||
: (string) ($banner['cl_scbanner_url'] ?? '');
|
||||
$border_color = (string) ($banner['cl_scbanner_border_color'] ?? '#ffae00');
|
||||
|
||||
$fields = [];
|
||||
|
||||
if ($show_org && $cl_scnotification_org_value !== '' && mb_strtolower($cl_scnotification_org_value) !== 'non') {
|
||||
$fields[] = [
|
||||
'name' => 'Tenue d’organisation',
|
||||
'value' => mb_substr($cl_scnotification_org_value, 0, 1024),
|
||||
'inline' => true,
|
||||
];
|
||||
}
|
||||
|
||||
if ($show_pvp && $cl_scnotification_pvp_value !== '' && mb_strtolower($cl_scnotification_pvp_value) !== 'inexistant') {
|
||||
$fields[] = [
|
||||
'name' => 'Risques PvP',
|
||||
'value' => mb_substr($cl_scnotification_pvp_value, 0, 1024),
|
||||
'inline' => true,
|
||||
];
|
||||
}
|
||||
|
||||
if ($include_schedule) {
|
||||
if ($cl_scnotification_location !== '') {
|
||||
$fields[] = ['name' => 'Lieu de ralliement', 'value' => mb_substr($cl_scnotification_location, 0, 1024), 'inline' => false];
|
||||
}
|
||||
if ($cl_scnotification_start_date !== '') {
|
||||
$fields[] = ['name' => 'Date de début', 'value' => mb_substr($cl_scnotification_start_date, 0, 1024), 'inline' => true];
|
||||
}
|
||||
if ($cl_scnotification_departure_time !== '') {
|
||||
$fields[] = ['name' => 'Heure de départ', 'value' => mb_substr($cl_scnotification_departure_time, 0, 1024), 'inline' => true];
|
||||
}
|
||||
if ($include_briefing_time && $cl_scnotification_briefing_time !== '') {
|
||||
$fields[] = ['name' => 'Heure de briefing', 'value' => mb_substr($cl_scnotification_briefing_time, 0, 1024), 'inline' => true];
|
||||
}
|
||||
if ($include_end_date && $cl_scnotification_end_date !== '') {
|
||||
$fields[] = ['name' => 'Date de fin', 'value' => mb_substr($cl_scnotification_end_date, 0, 1024), 'inline' => true];
|
||||
}
|
||||
if ($include_end_time && $cl_scnotification_end_time !== '') {
|
||||
$fields[] = ['name' => 'Heure de fin', 'value' => mb_substr($cl_scnotification_end_time, 0, 1024), 'inline' => true];
|
||||
}
|
||||
}
|
||||
|
||||
if ($show_channel_url && $cl_scnotification_channel_url !== '') {
|
||||
$fields[] = ['name' => 'Canal Discord', 'value' => '[Ouvrir le canal](' . $cl_scnotification_channel_url . ')', 'inline' => false];
|
||||
}
|
||||
|
||||
if ($show_inventory_url && $cl_scnotification_inventory_url !== '') {
|
||||
$fields[] = ['name' => 'Inventaire A.R.I.A', 'value' => '[Consulter l’inventaire](' . $cl_scnotification_inventory_url . ')', 'inline' => false];
|
||||
}
|
||||
|
||||
if ($show_source_url && $cl_scnotification_source_url !== '') {
|
||||
$fields[] = ['name' => 'Source', 'value' => '[Ouvrir la source](' . $cl_scnotification_source_url . ')', 'inline' => false];
|
||||
}
|
||||
|
||||
|
||||
$embed = [
|
||||
'description' => mb_substr($cl_scnotification_message, 0, 4096),
|
||||
'color' => scdiscord_hex_to_decimal($border_color),
|
||||
];
|
||||
|
||||
if ($cl_scnotification_title !== '') {
|
||||
$embed['title'] = mb_substr($cl_scnotification_title, 0, 256);
|
||||
}
|
||||
|
||||
if (!empty($fields)) {
|
||||
$embed['fields'] = $fields;
|
||||
}
|
||||
|
||||
if ($banner_image_url !== '') {
|
||||
$embed['image'] = ['url' => $banner_image_url];
|
||||
}
|
||||
|
||||
if ($show_footer && ($cl_scnotification_footer_text !== '' || $cl_scnotification_footer_icon_url !== '')) {
|
||||
$embed['footer'] = [];
|
||||
if ($cl_scnotification_footer_text !== '') {
|
||||
$embed['footer']['text'] = mb_substr($cl_scnotification_footer_text, 0, 2048);
|
||||
}
|
||||
if ($cl_scnotification_footer_icon_url !== '') {
|
||||
$embed['footer']['icon_url'] = $cl_scnotification_footer_icon_url;
|
||||
}
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'embeds' => [$embed],
|
||||
];
|
||||
|
||||
if (!empty($mentions)) {
|
||||
$payload['content'] = implode(' ', $mentions);
|
||||
$payload['allowed_mentions'] = ['parse' => ['everyone']];
|
||||
}
|
||||
|
||||
$thread_name = $show_thread
|
||||
? ('Discussion - ' . scdiscord_build_thread_name(
|
||||
$cl_scnotification_title,
|
||||
$cl_scnotification_location,
|
||||
$cl_scnotification_start_date
|
||||
))
|
||||
: '';
|
||||
|
||||
$result = scdiscord_post_webhook((string) $webhook['cl_scwebhook_url'], $payload);
|
||||
$bot_actions = [
|
||||
'success' => true,
|
||||
'http_code' => 200,
|
||||
'response' => 'Aucune action bot demandée.',
|
||||
];
|
||||
|
||||
if (!empty($result['success']) && ($show_reactions || $show_thread)) {
|
||||
$message_data = scdiscord_decode_json_response((string) ($result['response'] ?? ''));
|
||||
$bot_actions = scdiscord_apply_bot_actions($message_data, $show_reactions, $show_thread, $thread_name);
|
||||
}
|
||||
|
||||
$log_response = json_encode([
|
||||
'webhook' => [
|
||||
'success' => !empty($result['success']),
|
||||
'http_code' => (int) ($result['http_code'] ?? 0),
|
||||
'response' => (string) ($result['response'] ?? ''),
|
||||
],
|
||||
'bot_actions' => [
|
||||
'success' => !empty($bot_actions['success']),
|
||||
'http_code' => (int) ($bot_actions['http_code'] ?? 0),
|
||||
'response' => (string) ($bot_actions['response'] ?? ''),
|
||||
'details' => $bot_actions['details'] ?? [],
|
||||
],
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
$stmt_log = $db->prepare(
|
||||
'INSERT INTO tbl_scnotifications (
|
||||
cl_scnotification_webhook_id,
|
||||
cl_scnotification_banner_id,
|
||||
cl_scnotification_title,
|
||||
cl_scnotification_message,
|
||||
cl_scnotification_payload,
|
||||
cl_scnotification_response,
|
||||
cl_scnotification_success,
|
||||
cl_scnotification_created_by
|
||||
) VALUES (
|
||||
:webhook_id,
|
||||
:banner_id,
|
||||
:title,
|
||||
:message,
|
||||
:payload,
|
||||
:response,
|
||||
:success,
|
||||
:created_by
|
||||
)'
|
||||
);
|
||||
$stmt_log->execute([
|
||||
'webhook_id' => $cl_scnotification_webhook_id,
|
||||
'banner_id' => $cl_scnotification_banner_id > 0 ? $cl_scnotification_banner_id : null,
|
||||
'title' => $cl_scnotification_title,
|
||||
'message' => $cl_scnotification_message,
|
||||
'payload' => json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'response' => $log_response !== false ? $log_response : (string) ($result['response'] ?? ''),
|
||||
'success' => (!empty($result['success']) && !empty($bot_actions['success'])) ? 1 : 0,
|
||||
'created_by' => $current_session_user !== '' ? $current_session_user : 'Inconnu',
|
||||
]);
|
||||
|
||||
if (empty($result['success'])) {
|
||||
auth_flash_set('error', 'Échec de l’envoi Discord (HTTP ' . (int) ($result['http_code'] ?? 0) . ').');
|
||||
} elseif (empty($bot_actions['success'])) {
|
||||
auth_flash_set('error', 'Notification envoyée, mais échec des actions bot Discord : ' . (string) ($bot_actions['response'] ?? 'Erreur inconnue.'));
|
||||
} else {
|
||||
unset($_SESSION['scnotification_old']);
|
||||
auth_flash_set('success', 'Notification Discord envoyée avec succès.');
|
||||
}
|
||||
|
||||
header('Location: scnotification.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$old = $_SESSION['scnotification_old'] ?? [];
|
||||
unset($_SESSION['scnotification_old']);
|
||||
|
||||
$stmt_webhooks = $db->query('SELECT * FROM tbl_scwebhooks ORDER BY cl_scwebhook_name ASC');
|
||||
$webhooks = $stmt_webhooks->fetchAll();
|
||||
|
||||
$stmt_banners = $db->query('SELECT * FROM tbl_scbanners ORDER BY cl_scbanner_name ASC');
|
||||
$banners = $stmt_banners->fetchAll();
|
||||
function scnotification_old_value(array $old, string $key, string $default = ''): string
|
||||
{
|
||||
$value = $old[$key] ?? $default;
|
||||
return is_string($value) ? $value : $default;
|
||||
}
|
||||
|
||||
function scnotification_old_checked(array $old, string $key, bool $default = false): bool
|
||||
{
|
||||
if (empty($old)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return array_key_exists($key, $old);
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SC Notification | R.E.A.C.T. Admin</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/styles.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/default.css">
|
||||
<style>
|
||||
:root {
|
||||
--primary: #a29b78;
|
||||
--primary-glow: rgba(162, 155, 120, 0.4);
|
||||
--bg-dark: #080a0f;
|
||||
--card-bg: rgba(20, 24, 33, 0.85);
|
||||
--border-glow: rgba(162, 155, 120, 0.25);
|
||||
--danger: #ff4d4d;
|
||||
--success: #00ff88;
|
||||
--text-soft: #d7d7d7;
|
||||
--text-muted: #9ea3ad;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Electrolize';
|
||||
src: url('fonts/Electrolize-Regular.ttf') format('truetype');
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background: radial-gradient(circle at top right, #1a1f2e, var(--bg-dark));
|
||||
background-attachment: fixed;
|
||||
color: #e0e0e0;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.admin-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.admin-topbar,
|
||||
.page-shell,
|
||||
.history-card {
|
||||
background: var(--card-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--border-glow);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.admin-topbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 1.5rem 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.topbar-info h1 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
background: linear-gradient(90deg, #fff, var(--primary));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.topbar-info p {
|
||||
margin: 0.25rem 0 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--primary);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.6rem 1.2rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--primary);
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
}
|
||||
|
||||
.btn-modern:hover {
|
||||
background: var(--primary);
|
||||
color: var(--bg-dark);
|
||||
box-shadow: 0 0 15px var(--primary-glow);
|
||||
}
|
||||
|
||||
.btn-modern.danger {
|
||||
border-color: var(--danger);
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.btn-modern.danger:hover {
|
||||
background: var(--danger);
|
||||
color: #fff;
|
||||
box-shadow: 0 0 15px rgba(255, 77, 77, 0.3);
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--border-glow);
|
||||
}
|
||||
|
||||
.nav-tabs a {
|
||||
text-decoration: none;
|
||||
color: #888;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.9rem;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.nav-tabs a:hover,
|
||||
.nav-tabs a.active {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.flash {
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem 1.15rem;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.flash.success { color: #9ff3c8; border-color: rgba(0, 255, 136, 0.25); }
|
||||
.flash.error { color: #ff9d9d; border-color: rgba(255, 77, 77, 0.25); }
|
||||
|
||||
.page-shell {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0 0 1.5rem;
|
||||
padding-bottom: 0.75rem;
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary);
|
||||
border-bottom: 1px solid var(--border-glow);
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.form-column {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: 12px;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0 0 1rem;
|
||||
padding-bottom: 0.65rem;
|
||||
color: var(--primary);
|
||||
font-size: 1rem;
|
||||
line-height: 1.2;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.field-row { margin-bottom: 0.85rem; }
|
||||
.field-row:last-child { margin-bottom: 0; }
|
||||
|
||||
label, .inline-label {
|
||||
display: block;
|
||||
margin-bottom: 0.45rem;
|
||||
font-size: 0.83rem;
|
||||
color: #aaa;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.55rem;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.7rem;
|
||||
color: #ededed;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.control,
|
||||
textarea,
|
||||
select,
|
||||
input[type="text"],
|
||||
input[type="url"],
|
||||
input[type="date"],
|
||||
input[type="time"] {
|
||||
width: 100%;
|
||||
padding: 0.8rem 1rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #444;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
color: #fff;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
transition: border-color 0.3s, background 0.3s;
|
||||
}
|
||||
|
||||
.control:focus,
|
||||
textarea:focus,
|
||||
select:focus,
|
||||
input[type="text"]:focus,
|
||||
input[type="url"]:focus,
|
||||
input[type="date"]:focus,
|
||||
input[type="time"]:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 180px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
input[disabled], select[disabled], textarea[disabled] {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.subgrid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.char-counter {
|
||||
margin-top: 0.3rem;
|
||||
font-size: 0.72rem;
|
||||
color: var(--text-muted);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.submit-wrap {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.submit-wrap .btn-modern {
|
||||
width: 100%;
|
||||
padding: 0.95rem 1.2rem;
|
||||
}
|
||||
|
||||
.history-card {
|
||||
margin-top: 1.5rem;
|
||||
padding: 1.2rem;
|
||||
}
|
||||
|
||||
.history-card h2 {
|
||||
margin: 0 0 1rem;
|
||||
padding-bottom: 0.75rem;
|
||||
color: var(--primary);
|
||||
font-size: 1.1rem;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid var(--border-glow);
|
||||
}
|
||||
|
||||
.history-list {
|
||||
display: grid;
|
||||
gap: 0.7rem;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
padding: 0.85rem 0.95rem;
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.history-meta {
|
||||
margin-top: 0.25rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.status-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.2rem 0.55rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.72rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.status-pill.ok {
|
||||
background: rgba(0, 255, 136, 0.12);
|
||||
color: #9ff3c8;
|
||||
}
|
||||
|
||||
.status-pill.ko {
|
||||
background: rgba(255, 77, 77, 0.12);
|
||||
color: #ffb0b0;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
padding: 1.2rem;
|
||||
border: 1px dashed rgba(255, 255, 255, 0.12);
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.form-grid, .subgrid-2 { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin-layout">
|
||||
<header class="admin-topbar">
|
||||
<div class="topbar-info">
|
||||
<h1>R.E.A.C.T. SC Notification</h1>
|
||||
<p>Niveau d'accès : <strong><?php echo htmlspecialchars($role_label, ENT_QUOTES, 'UTF-8'); ?></strong> | Session : <strong><?php echo htmlspecialchars($current_session_user, ENT_QUOTES, 'UTF-8'); ?></strong></p>
|
||||
</div>
|
||||
<div class="topbar-actions">
|
||||
<a href="index.php" class="btn-modern">Site</a>
|
||||
<a href="logout.php" class="btn-modern danger">Exit</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="nav-tabs">
|
||||
<?php if (auth_is_admin()): ?>
|
||||
<a href="admin.php">Utilisateurs</a>
|
||||
<a href="scwebhook.php">WEBHOOK</a>
|
||||
<a href="scnotification.php" class="active">NOTIF DISCORD</a>
|
||||
<a href="scitems.php">Base d'Objets</a>
|
||||
<a href="scstatsitem.php">Stats Item</a>
|
||||
<a href="scitemcustom.php">Item Custom</a>
|
||||
<a href="scmining.php">Scanner Minage</a>
|
||||
<a href="scmanufactures.php">Manufactures</a>
|
||||
<a href="scvaisseaux.php">Vaisseaux</a>
|
||||
<?php endif; ?>
|
||||
<a href="scpreset.php">Presets Vaisseau</a>
|
||||
</nav>
|
||||
|
||||
<?php if ($flash_message !== ''): ?>
|
||||
<div class="flash <?php echo htmlspecialchars($flash_type, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<?php echo htmlspecialchars($flash_message, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="page-shell">
|
||||
<div class="page-title">Envoi d'une notification sur Discord</div>
|
||||
<form method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<input type="hidden" name="action" value="send_notification">
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="form-column">
|
||||
<section class="section-card">
|
||||
<h2 class="section-title">Canal de notification</h2>
|
||||
<div class="field-row">
|
||||
<select class="control" name="cl_scnotification_webhook_id" required>
|
||||
<option value="">Sélectionner un webhook</option>
|
||||
<?php foreach ($webhooks as $webhook): ?>
|
||||
<?php $selected = (string) $webhook['cl_scwebhook_id'] === scnotification_old_value($old, 'cl_scnotification_webhook_id'); ?>
|
||||
<option value="<?php echo (int) $webhook['cl_scwebhook_id']; ?>" <?php echo $selected ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($webhook['cl_scwebhook_name'] . (((int) $webhook['cl_scwebhook_is_forum'] === 1) ? ' [forum]' : ''), ENT_QUOTES, 'UTF-8'); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-card">
|
||||
<h2 class="section-title">Bannière et Couleur de bordure</h2>
|
||||
<div class="field-row">
|
||||
<select class="control" name="cl_scnotification_banner_id" id="cl_scnotification_banner_id">
|
||||
<option value="">Aucune bannière</option>
|
||||
<?php foreach ($banners as $banner): ?>
|
||||
<?php $selected = (string) $banner['cl_scbanner_id'] === scnotification_old_value($old, 'cl_scnotification_banner_id'); ?>
|
||||
<option value="<?php echo (int) $banner['cl_scbanner_id']; ?>" <?php echo $selected ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($banner['cl_scbanner_name'] . ' [' . scdiscord_normalize_hex_color((string) ($banner['cl_scbanner_border_color'] ?? '#ffae00')) . ']', ENT_QUOTES, 'UTF-8'); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_use_custom_banner" id="cl_scnotification_use_custom_banner" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_use_custom_banner') ? 'checked' : ''; ?>>
|
||||
<span>Utiliser une bannière personnalisée</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<input type="url" class="control" name="cl_scnotification_custom_banner_url" id="cl_scnotification_custom_banner_url" placeholder="URL personnalisée https://.../png" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_custom_banner_url'), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-card">
|
||||
<h2 class="section-title">Message</h2>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_notify_here" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_notify_here') ? 'checked' : ''; ?>>
|
||||
<span>Notifier avec @here</span>
|
||||
</label>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_notify_everyone" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_notify_everyone', true) ? 'checked' : ''; ?>>
|
||||
<span>Notifier avec @everyone</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label for="cl_scnotification_title">Titre :</label>
|
||||
<input type="text" class="control" id="cl_scnotification_title" name="cl_scnotification_title" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_title'), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label for="cl_scnotification_message">Description :</label>
|
||||
<textarea id="cl_scnotification_message" name="cl_scnotification_message" maxlength="2500" placeholder="Compatible avec le Markdown Discord..."><?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_message'), ENT_QUOTES, 'UTF-8'); ?></textarea>
|
||||
<div class="char-counter"><span id="messageCount">2500</span> caractères restants</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-card">
|
||||
<h2 class="section-title">Options Footer</h2>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_show_footer" id="cl_scnotification_show_footer" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_show_footer', true) ? 'checked' : ''; ?>>
|
||||
<span>Afficher le footer</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label for="cl_scnotification_footer_text">Texte du footer :</label>
|
||||
<input type="text" class="control footer-toggle" id="cl_scnotification_footer_text" name="cl_scnotification_footer_text" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_footer_text', 'R.E.A.C.T Initiative'), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
<div class="field-row">
|
||||
<label for="cl_scnotification_footer_icon_url">URL icône footer :</label>
|
||||
<input type="url" class="control footer-toggle" id="cl_scnotification_footer_icon_url" name="cl_scnotification_footer_icon_url" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_footer_icon_url'), ENT_QUOTES, 'UTF-8'); ?>" placeholder="*.png">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-card">
|
||||
<h2 class="section-title">Réactions & Fils</h2>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_show_reactions" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_show_reactions') ? 'checked' : ''; ?>>
|
||||
<span>Afficher les réactions 👍 / ⏳ / ❓ / 👎</span>
|
||||
</label>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_show_thread" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_show_thread', true) ? 'checked' : ''; ?>>
|
||||
<span>Afficher le fil de discussion</span>
|
||||
</label>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="form-column">
|
||||
<section class="section-card">
|
||||
<h2 class="section-title">Tenue d'organisation & PvP</h2>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_show_org" id="cl_scnotification_show_org" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_show_org') ? 'checked' : ''; ?>>
|
||||
<span>Afficher la tenue d'organisation</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<select class="control org-toggle" name="cl_scnotification_org_value" id="cl_scnotification_org_value">
|
||||
<?php foreach (['Non', 'Tenue civile', 'Tenue organisation', 'Tenue lourde'] as $option): ?>
|
||||
<option value="<?php echo htmlspecialchars($option, ENT_QUOTES, 'UTF-8'); ?>" <?php echo scnotification_old_value($old, 'cl_scnotification_org_value', 'Non') === $option ? 'selected' : ''; ?>><?php echo htmlspecialchars($option, ENT_QUOTES, 'UTF-8'); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_show_pvp" id="cl_scnotification_show_pvp" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_show_pvp') ? 'checked' : ''; ?>>
|
||||
<span>Afficher les risques PvP</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<select class="control pvp-toggle" name="cl_scnotification_pvp_value" id="cl_scnotification_pvp_value">
|
||||
<?php foreach (['Inexistant', 'Faible', 'Modéré', 'Important', 'Extrême'] as $option): ?>
|
||||
<option value="<?php echo htmlspecialchars($option, ENT_QUOTES, 'UTF-8'); ?>" <?php echo scnotification_old_value($old, 'cl_scnotification_pvp_value', 'Inexistant') === $option ? 'selected' : ''; ?>><?php echo htmlspecialchars($option, ENT_QUOTES, 'UTF-8'); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-card">
|
||||
<h2 class="section-title">Lieu, Date et Heure</h2>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_include_schedule" id="cl_scnotification_include_schedule" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_include_schedule') ? 'checked' : ''; ?>>
|
||||
<span>Inclure date, lieu et heures</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label for="cl_scnotification_location">Lieu de ralliement :</label>
|
||||
<input type="text" class="control schedule-toggle" id="cl_scnotification_location" name="cl_scnotification_location" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_location'), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
<div class="field-row subgrid-2">
|
||||
<div>
|
||||
<label for="cl_scnotification_start_date">Date de début :</label>
|
||||
<input type="date" class="control schedule-toggle" id="cl_scnotification_start_date" name="cl_scnotification_start_date" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_start_date'), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
<div>
|
||||
<label for="cl_scnotification_departure_time">Heure de départ :</label>
|
||||
<input type="time" class="control schedule-toggle" id="cl_scnotification_departure_time" name="cl_scnotification_departure_time" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_departure_time', '21:30'), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
</div>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_include_briefing_time" id="cl_scnotification_include_briefing_time" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_include_briefing_time') ? 'checked' : ''; ?>>
|
||||
<span>Inclure une heure de briefing</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label for="cl_scnotification_briefing_time">Heure de briefing :</label>
|
||||
<input type="time" class="control briefing-toggle" id="cl_scnotification_briefing_time" name="cl_scnotification_briefing_time" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_briefing_time', '21:00'), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_include_end_date" id="cl_scnotification_include_end_date" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_include_end_date') ? 'checked' : ''; ?>>
|
||||
<span>Inclure une date de fin</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label for="cl_scnotification_end_date">Date de fin :</label>
|
||||
<input type="date" class="control enddate-toggle" id="cl_scnotification_end_date" name="cl_scnotification_end_date" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_end_date'), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_include_end_time" id="cl_scnotification_include_end_time" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_include_end_time') ? 'checked' : ''; ?>>
|
||||
<span>Inclure une heure de fin</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label for="cl_scnotification_end_time">Heure de fin :</label>
|
||||
<input type="time" class="control endtime-toggle" id="cl_scnotification_end_time" name="cl_scnotification_end_time" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_end_time', '00:00'), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section-card">
|
||||
<h2 class="section-title">URL externes</h2>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_show_channel_url" id="cl_scnotification_show_channel_url" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_show_channel_url') ? 'checked' : ''; ?>>
|
||||
<span>Afficher un lien de canal Discord</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label for="cl_scnotification_channel_url">URL Canal :</label>
|
||||
<input type="url" class="control channelurl-toggle" id="cl_scnotification_channel_url" name="cl_scnotification_channel_url" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_channel_url', 'https://discord.com/channels/...'), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_show_inventory_url" id="cl_scnotification_show_inventory_url" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_show_inventory_url') ? 'checked' : ''; ?>>
|
||||
<span>Afficher une URL de l'inventaire A.R.I.A</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label for="cl_scnotification_inventory_url">URL Inventaire :</label>
|
||||
<input type="url" class="control inventoryurl-toggle" id="cl_scnotification_inventory_url" name="cl_scnotification_inventory_url" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_inventory_url', 'https://aria.blackops-agency.fr/...'), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
<label class="check">
|
||||
<input type="checkbox" name="cl_scnotification_show_source_url" id="cl_scnotification_show_source_url" value="1" <?php echo scnotification_old_checked($old, 'cl_scnotification_show_source_url') ? 'checked' : ''; ?>>
|
||||
<span>Afficher une URL source</span>
|
||||
</label>
|
||||
<div class="field-row">
|
||||
<label for="cl_scnotification_source_url">URL Source :</label>
|
||||
<input type="url" class="control sourceurl-toggle" id="cl_scnotification_source_url" name="cl_scnotification_source_url" value="<?php echo htmlspecialchars(scnotification_old_value($old, 'cl_scnotification_source_url', 'https://...'), ENT_QUOTES, 'UTF-8'); ?>">
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="submit-wrap">
|
||||
<button type="submit" class="btn-modern">Envoyer</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const messageField = document.getElementById('cl_scnotification_message');
|
||||
const messageCount = document.getElementById('messageCount');
|
||||
|
||||
function updateCounter() {
|
||||
const remaining = 2500 - (messageField.value || '').length;
|
||||
messageCount.textContent = remaining;
|
||||
}
|
||||
|
||||
function toggleByCheckbox(checkboxId, selector, invert = false) {
|
||||
const checkbox = document.getElementById(checkboxId);
|
||||
const targets = document.querySelectorAll(selector);
|
||||
if (!checkbox) return;
|
||||
|
||||
const enabled = invert ? !checkbox.checked : checkbox.checked;
|
||||
targets.forEach((target) => {
|
||||
target.disabled = !enabled;
|
||||
});
|
||||
}
|
||||
|
||||
function syncStates() {
|
||||
toggleByCheckbox('cl_scnotification_use_custom_banner', '#cl_scnotification_custom_banner_url');
|
||||
toggleByCheckbox('cl_scnotification_show_footer', '.footer-toggle');
|
||||
toggleByCheckbox('cl_scnotification_show_org', '.org-toggle');
|
||||
toggleByCheckbox('cl_scnotification_show_pvp', '.pvp-toggle');
|
||||
toggleByCheckbox('cl_scnotification_include_schedule', '.schedule-toggle');
|
||||
const scheduleEnabled = document.getElementById('cl_scnotification_include_schedule')?.checked;
|
||||
document.getElementById('cl_scnotification_include_briefing_time').disabled = !scheduleEnabled;
|
||||
document.getElementById('cl_scnotification_include_end_date').disabled = !scheduleEnabled;
|
||||
document.getElementById('cl_scnotification_include_end_time').disabled = !scheduleEnabled;
|
||||
toggleByCheckbox('cl_scnotification_include_briefing_time', '.briefing-toggle');
|
||||
toggleByCheckbox('cl_scnotification_include_end_date', '.enddate-toggle');
|
||||
toggleByCheckbox('cl_scnotification_include_end_time', '.endtime-toggle');
|
||||
if (!scheduleEnabled) {
|
||||
document.querySelectorAll('.briefing-toggle, .enddate-toggle, .endtime-toggle').forEach(el => el.disabled = true);
|
||||
}
|
||||
toggleByCheckbox('cl_scnotification_show_channel_url', '.channelurl-toggle');
|
||||
toggleByCheckbox('cl_scnotification_show_inventory_url', '.inventoryurl-toggle');
|
||||
toggleByCheckbox('cl_scnotification_show_source_url', '.sourceurl-toggle');
|
||||
}
|
||||
|
||||
updateCounter();
|
||||
messageField.addEventListener('input', updateCounter);
|
||||
|
||||
[
|
||||
'cl_scnotification_use_custom_banner',
|
||||
'cl_scnotification_show_footer',
|
||||
'cl_scnotification_show_org',
|
||||
'cl_scnotification_show_pvp',
|
||||
'cl_scnotification_include_schedule',
|
||||
'cl_scnotification_include_briefing_time',
|
||||
'cl_scnotification_include_end_date',
|
||||
'cl_scnotification_include_end_time',
|
||||
'cl_scnotification_show_channel_url',
|
||||
'cl_scnotification_show_inventory_url',
|
||||
'cl_scnotification_show_source_url'
|
||||
].forEach((id) => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) {
|
||||
el.addEventListener('change', syncStates);
|
||||
}
|
||||
});
|
||||
|
||||
syncStates();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -325,8 +325,14 @@ $presets = $stmt_list->fetchAll();
|
||||
<nav class="nav-tabs">
|
||||
<?php if (auth_is_admin()): ?>
|
||||
<a href="admin.php">Utilisateurs</a>
|
||||
<a href="scitems.php">Base d\'Objets</a>
|
||||
<a href="scwebhook.php">WEBHOOK</a>
|
||||
<a href="scnotification.php">NOTIF DISCORD</a>
|
||||
<a href="scitems.php">Base d'Objets</a>
|
||||
<a href="scstatsitem.php">Stats Item</a>
|
||||
<a href="scitemcustom.php">Item Custom</a>
|
||||
<a href="scmining.php">Scanner Minage</a>
|
||||
<a href="scmanufactures.php">Manufactures</a>
|
||||
<a href="scvaisseaux.php">Vaisseaux</a>
|
||||
<?php endif; ?>
|
||||
<a href="scpreset.php" class="active">Presets Vaisseau</a>
|
||||
</nav>
|
||||
|
||||
497
scstatsitem.php
Normal file
@ -0,0 +1,497 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/db/auth.php';
|
||||
require_once __DIR__ . '/db/scstatsitem.php';
|
||||
|
||||
auth_start_session();
|
||||
auth_bootstrap();
|
||||
scstatsitem_bootstrap();
|
||||
|
||||
if (!auth_is_admin()) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$flash = auth_flash_get();
|
||||
$flash_type = $flash['type'] ?? '';
|
||||
$flash_message = $flash['message'] ?? '';
|
||||
|
||||
$db = db();
|
||||
$csrf_token = auth_csrf_token();
|
||||
|
||||
$allowed_units = ['%', '°C', 'RPM', 'Q', 'SCU'];
|
||||
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$submitted_csrf = $_POST['csrf_token'] ?? '';
|
||||
if (!auth_validate_csrf($submitted_csrf)) {
|
||||
auth_flash_set('error', 'Jeton CSRF invalide.');
|
||||
header('Location: scstatsitem.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
if ($action === 'add_stat') {
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$unit = trim($_POST['unit'] ?? '%');
|
||||
if (!in_array($unit, $allowed_units, true)) {
|
||||
$unit = '%';
|
||||
}
|
||||
|
||||
if ($name === '') {
|
||||
auth_flash_set('error', 'Le nom de la statistique est requis.');
|
||||
} else {
|
||||
try {
|
||||
$stmt = $db->prepare('INSERT INTO tbl_scstatsitem (cl_scstatsitem_name, cl_scstatsitem_unit) VALUES (:name, :unit)');
|
||||
$stmt->execute([
|
||||
'name' => $name,
|
||||
'unit' => $unit,
|
||||
]);
|
||||
auth_flash_set('success', 'Statistique ajoutée avec succès.');
|
||||
} catch (PDOException $e) {
|
||||
if ($e->getCode() == 23000) {
|
||||
auth_flash_set('error', 'Cette statistique existe déjà.');
|
||||
} else {
|
||||
auth_flash_set('error', 'Erreur lors de l\'ajout : ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: scstatsitem.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'update_stat') {
|
||||
$id = (int) ($_POST['stat_id'] ?? 0);
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$unit = trim($_POST['unit'] ?? '%');
|
||||
if (!in_array($unit, $allowed_units, true)) {
|
||||
$unit = '%';
|
||||
}
|
||||
|
||||
if ($id <= 0 || $name === '') {
|
||||
auth_flash_set('error', 'Données invalides.');
|
||||
} else {
|
||||
try {
|
||||
$stmt = $db->prepare('UPDATE tbl_scstatsitem SET cl_scstatsitem_name = :name, cl_scstatsitem_unit = :unit WHERE cl_scstatsitem_id = :id');
|
||||
$stmt->execute([
|
||||
'name' => $name,
|
||||
'unit' => $unit,
|
||||
'id' => $id,
|
||||
]);
|
||||
auth_flash_set('success', 'Statistique mise à jour.');
|
||||
} catch (PDOException $e) {
|
||||
if ($e->getCode() == 23000) {
|
||||
auth_flash_set('error', 'Cette statistique existe déjà.');
|
||||
} else {
|
||||
auth_flash_set('error', 'Erreur lors de la mise à jour : ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: scstatsitem.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'delete_stat') {
|
||||
$id = (int) ($_POST['stat_id'] ?? 0);
|
||||
|
||||
if ($id > 0) {
|
||||
try {
|
||||
$stmt = $db->prepare('DELETE FROM tbl_scstatsitem WHERE cl_scstatsitem_id = :id');
|
||||
$stmt->execute(['id' => $id]);
|
||||
auth_flash_set('success', 'Statistique supprimée.');
|
||||
} catch (PDOException $e) {
|
||||
auth_flash_set('error', 'Erreur lors de la suppression : ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: scstatsitem.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt_stats = $db->query('SELECT * FROM tbl_scstatsitem ORDER BY cl_scstatsitem_name ASC, cl_scstatsitem_id ASC');
|
||||
$stats_items = $stmt_stats->fetchAll();
|
||||
|
||||
$current_session_user = $_SESSION['user'] ?? '';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Stats Item | R.E.A.C.T. Admin</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/styles.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/default.css">
|
||||
<style>
|
||||
:root {
|
||||
--primary: #a29b78;
|
||||
--primary-glow: rgba(162, 155, 120, 0.4);
|
||||
--bg-dark: #080a0f;
|
||||
--card-bg: rgba(20, 24, 33, 0.85);
|
||||
--border-glow: rgba(162, 155, 120, 0.25);
|
||||
--danger: #ff4d4d;
|
||||
--success: #00ff88;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Electrolize';
|
||||
src: url('fonts/Electrolize-Regular.ttf') format('truetype');
|
||||
}
|
||||
|
||||
body {
|
||||
background: radial-gradient(circle at top right, #1a1f2e, var(--bg-dark));
|
||||
background-attachment: fixed;
|
||||
color: #e0e0e0;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.admin-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.admin-topbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1.5rem 2rem;
|
||||
background: var(--card-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--border-glow);
|
||||
border-radius: 12px;
|
||||
margin-bottom: 2rem;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.topbar-info h1 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
background: linear-gradient(90deg, #fff, var(--primary));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.topbar-info p {
|
||||
margin: 0.25rem 0 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--primary);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
padding: 0.6rem 1.2rem;
|
||||
border: 1px solid var(--primary);
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.btn-modern:hover {
|
||||
background: var(--primary);
|
||||
color: var(--bg-dark);
|
||||
box-shadow: 0 0 15px var(--primary-glow);
|
||||
}
|
||||
|
||||
.btn-modern.danger { border-color: var(--danger); color: var(--danger); }
|
||||
.btn-modern.danger:hover { background: var(--danger); color: #fff; }
|
||||
|
||||
.btn-mini { padding: 0.3rem 0.6rem; font-size: 0.75rem; }
|
||||
|
||||
.nav-tabs { display: flex; gap: 1rem; margin-bottom: 2rem; border-bottom: 1px solid var(--border-glow); padding-bottom: 1rem; }
|
||||
.nav-tabs a { text-decoration: none; color: #888; text-transform: uppercase; font-size: 0.9rem; transition: color 0.3s; }
|
||||
.nav-tabs a:hover, .nav-tabs a.active { color: var(--primary); }
|
||||
|
||||
.admin-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.side-panel, .main-panel { display: flex; flex-direction: column; gap: 2rem; }
|
||||
|
||||
.glass-card {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border-glow);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.glass-card h2 {
|
||||
margin: 0 0 1.25rem;
|
||||
color: var(--primary);
|
||||
font-size: 1.1rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.form-group { margin-bottom: 1rem; }
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--primary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.8rem;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid var(--border-glow);
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
box-sizing: border-box;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px var(--primary-glow);
|
||||
}
|
||||
|
||||
.form-help {
|
||||
margin-top: 0.75rem;
|
||||
color: #9ca3af;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.flash {
|
||||
padding: 1rem 1.25rem;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 1.5rem;
|
||||
border: 1px solid var(--border-glow);
|
||||
background: rgba(20, 24, 33, 0.9);
|
||||
}
|
||||
|
||||
.flash.success { border-color: rgba(0, 255, 136, 0.35); color: var(--success); }
|
||||
.flash.error { border-color: rgba(255, 77, 77, 0.35); color: #ff8a8a; }
|
||||
|
||||
.modern-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
min-width: 520px;
|
||||
}
|
||||
|
||||
.modern-table th,
|
||||
.modern-table td {
|
||||
padding: 0.9rem 1rem;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.modern-table th {
|
||||
text-align: left;
|
||||
color: var(--primary);
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.modern-table tr:hover td {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 3rem 1rem;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.admin-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.admin-topbar {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin-layout">
|
||||
<header class="admin-topbar">
|
||||
<div class="topbar-info">
|
||||
<h1>Stats Item</h1>
|
||||
<p>Gestion libre des statistiques d'objets</p>
|
||||
</div>
|
||||
<div style="display:flex; gap:10px; flex-wrap:wrap;">
|
||||
<span style="align-self:center; opacity:0.8;">Session: <?php echo htmlspecialchars($current_session_user, ENT_QUOTES, 'UTF-8'); ?></span>
|
||||
<a href="index.php" class="btn-modern">Retour au site</a>
|
||||
<a href="logout.php" class="btn-modern danger">Session End</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="nav-tabs">
|
||||
<a href="admin.php">Utilisateurs</a>
|
||||
<a href="scwebhook.php">WEBHOOK</a>
|
||||
<a href="scnotification.php">NOTIF DISCORD</a>
|
||||
<a href="scitems.php">Base d'Objets</a>
|
||||
<a href="scstatsitem.php" class="active">Stats Item</a>
|
||||
<a href="scitemcustom.php">Item Custom</a>
|
||||
<a href="scmining.php">Scanner Minage</a>
|
||||
<a href="scmanufactures.php">Manufactures</a>
|
||||
<a href="scvaisseaux.php">Vaisseaux</a>
|
||||
<a href="scpreset.php">Presets Vaisseau</a>
|
||||
</nav>
|
||||
|
||||
<?php if ($flash_message !== ''): ?>
|
||||
<div class="flash <?php echo htmlspecialchars($flash_type, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<?php echo htmlspecialchars($flash_message, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="admin-grid">
|
||||
<div class="side-panel">
|
||||
<section class="glass-card">
|
||||
<h2 id="formTitle">Nouvelle Stat Item</h2>
|
||||
<form id="statsItemForm" method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<input type="hidden" name="action" id="formAction" value="add_stat">
|
||||
<input type="hidden" name="stat_id" id="statId" value="">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="statName">Nom de la statistique</label>
|
||||
<input type="text" name="name" id="statName" class="form-control" required placeholder="Ex : Puissance, Résistance, Vitesse...">
|
||||
<div class="form-help">Ajoute autant de stats que tu veux. Chaque ligne représente une statistique personnalisée que tu pourras gérer librement.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="statUnit">Unité de la statistique</label>
|
||||
<select name="unit" id="statUnit" class="form-control">
|
||||
<?php foreach ($allowed_units as $unit_option): ?>
|
||||
<option value="<?php echo htmlspecialchars($unit_option, ENT_QUOTES, 'UTF-8'); ?>" <?php echo $unit_option === '%' ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($unit_option, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="submitBtn" class="btn-modern" style="width: 100%;">Ajouter</button>
|
||||
<button type="button" id="cancelBtn" class="btn-modern" style="width: 100%; margin-top: 10px; display: none;" onclick="resetForm()">Annuler</button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<main class="main-panel">
|
||||
<section class="glass-card">
|
||||
<h2>Liste des Stats Item</h2>
|
||||
<div style="overflow-x: auto;">
|
||||
<table class="modern-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Nom</th>
|
||||
<th>Unité</th>
|
||||
<th style="text-align: right;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($stats_items)): ?>
|
||||
<tr>
|
||||
<td colspan="4" class="empty-state">Aucune statistique enregistrée.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($stats_items as $stat): ?>
|
||||
<tr>
|
||||
<td style="width: 70px; opacity: 0.5;">#<?php echo (int) $stat['cl_scstatsitem_id']; ?></td>
|
||||
<td>
|
||||
<strong style="color: var(--primary); text-transform: uppercase;">
|
||||
<?php echo htmlspecialchars($stat['cl_scstatsitem_name'], ENT_QUOTES, 'UTF-8'); ?>
|
||||
</strong>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($stat['cl_scstatsitem_unit'], ENT_QUOTES, 'UTF-8'); ?></td>
|
||||
<td style="text-align: right;">
|
||||
<div style="display: flex; gap: 5px; justify-content: flex-end;">
|
||||
<button
|
||||
type="button"
|
||||
class="btn-modern btn-mini"
|
||||
onclick='editStatItem(<?php echo json_encode([
|
||||
"id" => (int) $stat["cl_scstatsitem_id"],
|
||||
"name" => $stat["cl_scstatsitem_name"],
|
||||
"unit" => $stat["cl_scstatsitem_unit"],
|
||||
], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP); ?>)'>
|
||||
Edit
|
||||
</button>
|
||||
<form method="post" onsubmit="return confirm('Supprimer cette statistique ?');">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<input type="hidden" name="action" value="delete_stat">
|
||||
<input type="hidden" name="stat_id" value="<?php echo (int) $stat['cl_scstatsitem_id']; ?>">
|
||||
<button type="submit" class="btn-modern btn-mini danger">X</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function editStatItem(data) {
|
||||
document.getElementById('formAction').value = 'update_stat';
|
||||
document.getElementById('statId').value = data.id;
|
||||
document.getElementById('statName').value = data.name;
|
||||
document.getElementById('statUnit').value = data.unit || '%';
|
||||
document.getElementById('submitBtn').innerText = 'Mettre à jour';
|
||||
document.getElementById('cancelBtn').style.display = 'block';
|
||||
document.getElementById('formTitle').innerText = 'Modifier Stat Item';
|
||||
document.getElementById('statsItemForm').scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
document.getElementById('formAction').value = 'add_stat';
|
||||
document.getElementById('statId').value = '';
|
||||
document.getElementById('statsItemForm').reset();
|
||||
document.getElementById('submitBtn').innerText = 'Ajouter';
|
||||
document.getElementById('cancelBtn').style.display = 'none';
|
||||
document.getElementById('formTitle').innerText = 'Nouvelle Stat Item';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
416
scvaisseaux.php
Normal file
@ -0,0 +1,416 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/db/auth.php';
|
||||
|
||||
auth_start_session();
|
||||
auth_bootstrap();
|
||||
|
||||
if (!auth_is_admin()) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$flash = auth_flash_get();
|
||||
$flash_type = $flash['type'] ?? '';
|
||||
$flash_message = $flash['message'] ?? '';
|
||||
|
||||
$db = db();
|
||||
$csrf_token = auth_csrf_token();
|
||||
|
||||
// Handle POST actions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$submitted_csrf = $_POST['csrf_token'] ?? '';
|
||||
if (!auth_validate_csrf($submitted_csrf)) {
|
||||
auth_flash_set('error', 'Jeton CSRF invalide.');
|
||||
header('Location: scvaisseaux.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
// Add ship
|
||||
if ($action === 'add_ship') {
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$manufacture_id = (int)($_POST['manufacture_id'] ?? 0);
|
||||
if ($name !== '' && $manufacture_id > 0) {
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO tbl_scvaisseaux (cl_scvaisseaux_name, cl_scvaisseaux_manufacture_id) VALUES (:name, :manufacture_id)");
|
||||
$stmt->execute(['name' => $name, 'manufacture_id' => $manufacture_id]);
|
||||
auth_flash_set('success', 'Vaisseau ajouté avec succès.');
|
||||
} catch (PDOException $e) {
|
||||
auth_flash_set('error', 'Erreur lors de l\'ajout : ' . $e->getMessage());
|
||||
}
|
||||
} else {
|
||||
auth_flash_set('error', 'Veuillez remplir tous les champs obligatoires.');
|
||||
}
|
||||
header('Location: scvaisseaux.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Update ship
|
||||
if ($action === 'update_ship') {
|
||||
$id = (int)($_POST['ship_id'] ?? 0);
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$manufacture_id = (int)($_POST['manufacture_id'] ?? 0);
|
||||
if ($id > 0 && $name !== '' && $manufacture_id > 0) {
|
||||
try {
|
||||
$stmt = $db->prepare("UPDATE tbl_scvaisseaux SET cl_scvaisseaux_name = :name, cl_scvaisseaux_manufacture_id = :manufacture_id WHERE cl_scvaisseaux_id = :id");
|
||||
$stmt->execute(['name' => $name, 'manufacture_id' => $manufacture_id, 'id' => $id]);
|
||||
auth_flash_set('success', 'Vaisseau mis à jour.');
|
||||
} catch (PDOException $e) {
|
||||
auth_flash_set('error', 'Erreur lors de la mise à jour : ' . $e->getMessage());
|
||||
}
|
||||
} else {
|
||||
auth_flash_set('error', 'Données invalides.');
|
||||
}
|
||||
header('Location: scvaisseaux.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Delete ship
|
||||
if ($action === 'delete_ship') {
|
||||
$id = (int)($_POST['ship_id'] ?? 0);
|
||||
if ($id > 0) {
|
||||
$stmt = $db->prepare("DELETE FROM tbl_scvaisseaux WHERE cl_scvaisseaux_id = :id");
|
||||
$stmt->execute(['id' => $id]);
|
||||
auth_flash_set('success', 'Vaisseau supprimé.');
|
||||
}
|
||||
header('Location: scvaisseaux.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all manufactures for the dropdown
|
||||
$stmt_mans = $db->query("SELECT * FROM tbl_scmanufactures ORDER BY cl_scmanufactures_name ASC");
|
||||
$manufactures = $stmt_mans->fetchAll();
|
||||
|
||||
// Fetch all ships with manufacture names
|
||||
$stmt_list = $db->query("SELECT v.*, m.cl_scmanufactures_name
|
||||
FROM tbl_scvaisseaux v
|
||||
JOIN tbl_scmanufactures m ON v.cl_scvaisseaux_manufacture_id = m.cl_scmanufactures_id
|
||||
ORDER BY m.cl_scmanufactures_name ASC, v.cl_scvaisseaux_name ASC");
|
||||
$ships = $stmt_list->fetchAll();
|
||||
|
||||
$current_session_user = $_SESSION['user'] ?? '';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vaisseaux | R.E.A.C.T. Admin</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/styles.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/default.css">
|
||||
<style>
|
||||
:root {
|
||||
--primary: #a29b78;
|
||||
--primary-glow: rgba(162, 155, 120, 0.4);
|
||||
--bg-dark: #080a0f;
|
||||
--card-bg: rgba(20, 24, 33, 0.85);
|
||||
--border-glow: rgba(162, 155, 120, 0.25);
|
||||
--danger: #ff4d4d;
|
||||
--success: #00ff88;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Electrolize';
|
||||
src: url('fonts/Electrolize-Regular.ttf') format('truetype');
|
||||
}
|
||||
|
||||
body {
|
||||
background: radial-gradient(circle at top right, #1a1f2e, var(--bg-dark));
|
||||
background-attachment: fixed;
|
||||
color: #e0e0e0;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.admin-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.admin-topbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1.5rem 2rem;
|
||||
background: var(--card-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--border-glow);
|
||||
border-radius: 12px;
|
||||
margin-bottom: 2rem;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.topbar-info h1 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
background: linear-gradient(90deg, #fff, var(--primary));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.topbar-info p {
|
||||
margin: 0.25rem 0 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--primary);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
padding: 0.6rem 1.2rem;
|
||||
border: 1px solid var(--primary);
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.btn-modern:hover {
|
||||
background: var(--primary);
|
||||
color: var(--bg-dark);
|
||||
box-shadow: 0 0 15px var(--primary-glow);
|
||||
}
|
||||
|
||||
.btn-modern.danger { border-color: var(--danger); color: var(--danger); }
|
||||
.btn-modern.danger:hover { background: var(--danger); color: #fff; }
|
||||
|
||||
.btn-mini { padding: 0.3rem 0.6rem; font-size: 0.75rem; }
|
||||
|
||||
.nav-tabs { display: flex; gap: 1rem; margin-bottom: 2rem; border-bottom: 1px solid var(--border-glow); padding-bottom: 1rem; }
|
||||
.nav-tabs a { text-decoration: none; color: #888; text-transform: uppercase; font-size: 0.9rem; transition: color 0.3s; }
|
||||
.nav-tabs a:hover, .nav-tabs a.active { color: var(--primary); }
|
||||
|
||||
.admin-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.admin-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
background: var(--card-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--border-glow);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.glass-card h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary);
|
||||
border-bottom: 1px solid var(--border-glow);
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.form-group { margin-bottom: 1.5rem; }
|
||||
.form-group label { display: block; margin-bottom: 0.5rem; font-size: 0.85rem; color: #aaa; text-transform: uppercase; }
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.8rem 1rem;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid #444;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
.form-control:focus { outline: none; border-color: var(--primary); background: rgba(0, 0, 0, 0.5); }
|
||||
select.form-control option { background: var(--bg-dark); color: #fff; }
|
||||
|
||||
.modern-table { width: 100%; border-collapse: separate; border-spacing: 0 8px; }
|
||||
.modern-table th { text-align: left; padding: 1rem; font-size: 0.8rem; text-transform: uppercase; color: var(--primary); opacity: 0.7; }
|
||||
.modern-table td { padding: 1rem; background: rgba(255, 255, 255, 0.03); border-top: 1px solid rgba(255, 255, 255, 0.05); border-bottom: 1px solid rgba(255, 255, 255, 0.05); }
|
||||
.modern-table td:first-child { border-left: 1px solid rgba(255, 255, 255, 0.05); border-radius: 8px 0 0 8px; }
|
||||
.modern-table td:last-child { border-right: 1px solid rgba(255, 255, 255, 0.05); border-radius: 0 8px 8px 0; }
|
||||
.modern-table tr:hover td { background: rgba(162, 155, 120, 0.05); }
|
||||
|
||||
.flash { padding: 1rem 1.5rem; border-radius: 8px; margin-bottom: 1.5rem; font-size: 0.9rem; border-left: 4px solid var(--primary); background: rgba(162, 155, 120, 0.1); }
|
||||
.flash.error { border-color: var(--danger); background: rgba(255, 77, 77, 0.1); color: #ffbaba; }
|
||||
.flash.success { border-color: var(--success); background: rgba(0, 255, 136, 0.1); color: #baffda; }
|
||||
|
||||
.manufacturer-text {
|
||||
font-size: 0.65rem;
|
||||
color: #888;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
display: block;
|
||||
margin-top: 2px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin-layout">
|
||||
<header class="admin-topbar">
|
||||
<div class="topbar-info">
|
||||
<h1>Gestion Vaisseaux</h1>
|
||||
<p>Niveau d\'accès : <strong>Administrateur</strong> | Session : <strong><?php echo htmlspecialchars($current_session_user); ?></strong></p>
|
||||
</div>
|
||||
<div class="topbar-actions">
|
||||
<a href="index.php" class="btn-modern">Site</a>
|
||||
<a href="logout.php" class="btn-modern danger">Exit</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="nav-tabs">
|
||||
<a href="admin.php">Utilisateurs</a>
|
||||
<a href="scwebhook.php">WEBHOOK</a>
|
||||
<a href="scnotification.php">NOTIF DISCORD</a>
|
||||
<a href="scitems.php">Base d'Objets</a>
|
||||
<a href="scstatsitem.php">Stats Item</a>
|
||||
<a href="scitemcustom.php">Item Custom</a>
|
||||
<a href="scmining.php">Scanner Minage</a>
|
||||
<a href="scmanufactures.php">Manufactures</a>
|
||||
<a href="scvaisseaux.php" class="active">Vaisseaux</a>
|
||||
<a href="scpreset.php">Presets Vaisseau</a>
|
||||
</nav>
|
||||
|
||||
<?php if ($flash_message !== ''): ?>
|
||||
<div class="flash <?php echo htmlspecialchars($flash_type); ?>">
|
||||
<?php echo htmlspecialchars($flash_message); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="admin-grid">
|
||||
<!-- Left Column: Add/Edit -->
|
||||
<div class="side-panel">
|
||||
<section class="glass-card">
|
||||
<h2 id="formTitle">Nouveau Vaisseau</h2>
|
||||
<?php if (empty($manufactures)): ?>
|
||||
<p style="color: var(--danger); font-size: 0.9rem;">Veuillez d\'abord ajouter au moins une manufacture.</p>
|
||||
<a href="scmanufactures.php" class="btn-modern" style="width: 100%;">Aller aux Manufactures</a>
|
||||
<?php else: ?>
|
||||
<form id="shipForm" method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
|
||||
<input type="hidden" name="action" id="formAction" value="add_ship">
|
||||
<input type="hidden" name="ship_id" id="shipId" value="">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Manufacture</label>
|
||||
<select name="manufacture_id" id="shipManufacture" class="form-control" required>
|
||||
<option value="">- Sélectionner -</option>
|
||||
<?php foreach ($manufactures as $m): ?>
|
||||
<option value="<?php echo $m['cl_scmanufactures_id']; ?>"><?php echo htmlspecialchars($m['cl_scmanufactures_name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Nom du Vaisseau</label>
|
||||
<input type="text" name="name" id="shipName" class="form-control" required placeholder="ex: Carrack">
|
||||
</div>
|
||||
|
||||
<button type="submit" id="submitBtn" class="btn-modern" style="width: 100%;">Ajouter</button>
|
||||
<button type="button" id="cancelBtn" class="btn-modern" style="width: 100%; margin-top: 10px; display: none;" onclick="resetForm()">Annuler</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Right Column: List -->
|
||||
<main class="main-panel">
|
||||
<section class="glass-card">
|
||||
<h2>Liste des Vaisseaux</h2>
|
||||
<div style="overflow-x: auto;">
|
||||
<table class="modern-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Manufacture / Modèle</th>
|
||||
<th style="text-align: right;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($ships)): ?>
|
||||
<tr><td colspan="2" style="text-align: center; padding: 3rem; color: #666;">Aucun vaisseau enregistré.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($ships as $s): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<span class="manufacturer-text"><?php echo htmlspecialchars($s['cl_scmanufactures_name']); ?></span>
|
||||
<strong style="color: var(--primary); text-transform: uppercase; font-size: 1.1rem;"><?php echo htmlspecialchars($s['cl_scvaisseaux_name']); ?></strong>
|
||||
</td>
|
||||
<td style="text-align: right;">
|
||||
<div style="display: flex; gap: 5px; justify-content: flex-end;">
|
||||
<button type="button" class="btn-modern btn-mini"
|
||||
onclick='editShip(<?php echo json_encode([
|
||||
"id" => $s["cl_scvaisseaux_id"],
|
||||
"name" => $s["cl_scvaisseaux_name"],
|
||||
"manufacture_id" => $s["cl_scvaisseaux_manufacture_id"]
|
||||
]); ?>)'>
|
||||
Edit
|
||||
</button>
|
||||
<form method="post" onsubmit="return confirm('Supprimer ce vaisseau ?');">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
|
||||
<input type="hidden" name="action" value="delete_ship">
|
||||
<input type="hidden" name="ship_id" value="<?php echo $s['cl_scvaisseaux_id']; ?>">
|
||||
<button type="submit" class="btn-modern btn-mini danger">X</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function editShip(data) {
|
||||
document.getElementById('formAction').value = 'update_ship';
|
||||
document.getElementById('shipId').value = data.id;
|
||||
document.getElementById('shipName').value = data.name;
|
||||
document.getElementById('shipManufacture').value = data.manufacture_id;
|
||||
|
||||
document.getElementById('submitBtn').innerText = 'Mettre à jour';
|
||||
document.getElementById('cancelBtn').style.display = 'block';
|
||||
document.getElementById('formTitle').innerText = 'Modifier Vaisseau';
|
||||
|
||||
document.getElementById('shipForm').scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
document.getElementById('formAction').value = 'add_ship';
|
||||
document.getElementById('shipId').value = '';
|
||||
document.getElementById('shipForm').reset();
|
||||
|
||||
document.getElementById('submitBtn').innerText = 'Ajouter';
|
||||
document.getElementById('cancelBtn').style.display = 'none';
|
||||
document.getElementById('formTitle').innerText = 'Nouveau Vaisseau';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
779
scwebhook.php
Normal file
@ -0,0 +1,779 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/db/auth.php';
|
||||
require_once __DIR__ . '/db/scdiscord.php';
|
||||
|
||||
auth_start_session();
|
||||
auth_bootstrap();
|
||||
scdiscord_bootstrap();
|
||||
|
||||
if (!auth_is_admin()) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = db();
|
||||
$csrf_token = auth_csrf_token();
|
||||
$flash = auth_flash_get();
|
||||
$flash_type = $flash['type'] ?? '';
|
||||
$flash_message = $flash['message'] ?? '';
|
||||
$current_session_user = $_SESSION['user'] ?? '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$submitted_csrf = (string) ($_POST['csrf_token'] ?? '');
|
||||
if (!auth_validate_csrf($submitted_csrf)) {
|
||||
auth_flash_set('error', 'Jeton CSRF invalide.');
|
||||
header('Location: scwebhook.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = (string) ($_POST['action'] ?? '');
|
||||
|
||||
if ($action === 'add_webhook' || $action === 'update_webhook') {
|
||||
$webhook_id = (int) ($_POST['webhook_id'] ?? 0);
|
||||
$cl_scwebhook_name = trim((string) ($_POST['cl_scwebhook_name'] ?? ''));
|
||||
$cl_scwebhook_url = trim((string) ($_POST['cl_scwebhook_url'] ?? ''));
|
||||
$cl_scwebhook_is_forum = 0;
|
||||
|
||||
if ($cl_scwebhook_name === '' || $cl_scwebhook_url === '') {
|
||||
auth_flash_set('error', 'Le nom et l’URL du webhook sont obligatoires.');
|
||||
header('Location: scwebhook.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!filter_var($cl_scwebhook_url, FILTER_VALIDATE_URL)) {
|
||||
auth_flash_set('error', 'L’URL du webhook Discord est invalide.');
|
||||
header('Location: scwebhook.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($action === 'add_webhook') {
|
||||
$stmt = $db->prepare('INSERT INTO tbl_scwebhooks (cl_scwebhook_name, cl_scwebhook_url, cl_scwebhook_is_forum) VALUES (:name, :url, :is_forum)');
|
||||
$stmt->execute([
|
||||
'name' => $cl_scwebhook_name,
|
||||
'url' => $cl_scwebhook_url,
|
||||
'is_forum' => $cl_scwebhook_is_forum,
|
||||
]);
|
||||
auth_flash_set('success', 'Webhook Discord ajouté avec succès.');
|
||||
} else {
|
||||
if ($webhook_id <= 0) {
|
||||
throw new RuntimeException('ID de webhook invalide.');
|
||||
}
|
||||
|
||||
$stmt = $db->prepare('UPDATE tbl_scwebhooks SET cl_scwebhook_name = :name, cl_scwebhook_url = :url, cl_scwebhook_is_forum = :is_forum WHERE cl_scwebhook_id = :id');
|
||||
$stmt->execute([
|
||||
'name' => $cl_scwebhook_name,
|
||||
'url' => $cl_scwebhook_url,
|
||||
'is_forum' => $cl_scwebhook_is_forum,
|
||||
'id' => $webhook_id,
|
||||
]);
|
||||
auth_flash_set('success', 'Webhook Discord mis à jour.');
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
auth_flash_set('error', 'Erreur webhook : ' . $e->getMessage());
|
||||
}
|
||||
|
||||
header('Location: scwebhook.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'delete_webhook') {
|
||||
$webhook_id = (int) ($_POST['webhook_id'] ?? 0);
|
||||
|
||||
if ($webhook_id > 0) {
|
||||
try {
|
||||
$stmt_usage = $db->prepare('SELECT COUNT(*) FROM tbl_scnotifications WHERE cl_scnotification_webhook_id = :id');
|
||||
$stmt_usage->execute(['id' => $webhook_id]);
|
||||
$usage_total = (int) $stmt_usage->fetchColumn();
|
||||
|
||||
if ($usage_total > 0) {
|
||||
auth_flash_set('error', 'Suppression refusée : ce webhook est déjà relié à l’historique des notifications.');
|
||||
} else {
|
||||
$stmt = $db->prepare('DELETE FROM tbl_scwebhooks WHERE cl_scwebhook_id = :id');
|
||||
$stmt->execute(['id' => $webhook_id]);
|
||||
auth_flash_set('success', 'Webhook Discord supprimé.');
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
auth_flash_set('error', 'Erreur suppression webhook : ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: scwebhook.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'add_banner' || $action === 'update_banner') {
|
||||
$banner_id = (int) ($_POST['banner_id'] ?? 0);
|
||||
$cl_scbanner_name = trim((string) ($_POST['cl_scbanner_name'] ?? ''));
|
||||
$cl_scbanner_url = trim((string) ($_POST['cl_scbanner_url'] ?? ''));
|
||||
$cl_scbanner_border_color = scdiscord_normalize_hex_color((string) ($_POST['cl_scbanner_border_color'] ?? ''));
|
||||
|
||||
if ($cl_scbanner_name === '' || $cl_scbanner_url === '') {
|
||||
auth_flash_set('error', 'Le nom et le lien d’image de la bannière sont obligatoires.');
|
||||
header('Location: scwebhook.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!filter_var($cl_scbanner_url, FILTER_VALIDATE_URL)) {
|
||||
auth_flash_set('error', 'L’URL de la bannière est invalide.');
|
||||
header('Location: scwebhook.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($action === 'add_banner') {
|
||||
$stmt = $db->prepare('INSERT INTO tbl_scbanners (cl_scbanner_name, cl_scbanner_url, cl_scbanner_border_color) VALUES (:name, :url, :border_color)');
|
||||
$stmt->execute([
|
||||
'name' => $cl_scbanner_name,
|
||||
'url' => $cl_scbanner_url,
|
||||
'border_color' => $cl_scbanner_border_color,
|
||||
]);
|
||||
auth_flash_set('success', 'Bannière Discord ajoutée avec succès.');
|
||||
} else {
|
||||
if ($banner_id <= 0) {
|
||||
throw new RuntimeException('ID de bannière invalide.');
|
||||
}
|
||||
|
||||
$stmt = $db->prepare('UPDATE tbl_scbanners SET cl_scbanner_name = :name, cl_scbanner_url = :url, cl_scbanner_border_color = :border_color WHERE cl_scbanner_id = :id');
|
||||
$stmt->execute([
|
||||
'name' => $cl_scbanner_name,
|
||||
'url' => $cl_scbanner_url,
|
||||
'border_color' => $cl_scbanner_border_color,
|
||||
'id' => $banner_id,
|
||||
]);
|
||||
auth_flash_set('success', 'Bannière Discord mise à jour.');
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
auth_flash_set('error', 'Erreur bannière : ' . $e->getMessage());
|
||||
}
|
||||
|
||||
header('Location: scwebhook.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'delete_banner') {
|
||||
$banner_id = (int) ($_POST['banner_id'] ?? 0);
|
||||
|
||||
if ($banner_id > 0) {
|
||||
try {
|
||||
$stmt = $db->prepare('DELETE FROM tbl_scbanners WHERE cl_scbanner_id = :id');
|
||||
$stmt->execute(['id' => $banner_id]);
|
||||
auth_flash_set('success', 'Bannière Discord supprimée.');
|
||||
} catch (Throwable $e) {
|
||||
auth_flash_set('error', 'Erreur suppression bannière : ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: scwebhook.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt_webhooks = $db->query('SELECT * FROM tbl_scwebhooks ORDER BY cl_scwebhook_name ASC');
|
||||
$webhooks = $stmt_webhooks->fetchAll();
|
||||
|
||||
$stmt_banners = $db->query('SELECT * FROM tbl_scbanners ORDER BY cl_scbanner_name ASC');
|
||||
$banners = $stmt_banners->fetchAll();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SC Webhook | R.E.A.C.T. Admin</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/styles.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/default.css">
|
||||
<style>
|
||||
:root {
|
||||
--primary: #a29b78;
|
||||
--primary-glow: rgba(162, 155, 120, 0.4);
|
||||
--bg-dark: #080a0f;
|
||||
--card-bg: rgba(20, 24, 33, 0.85);
|
||||
--card-bg-secondary: rgba(255, 255, 255, 0.03);
|
||||
--border-glow: rgba(162, 155, 120, 0.25);
|
||||
--danger: #ff4d4d;
|
||||
--success: #00ff88;
|
||||
--muted: #aaaaaa;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Electrolize';
|
||||
src: url('fonts/Electrolize-Regular.ttf') format('truetype');
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background: radial-gradient(circle at top right, #1a1f2e, var(--bg-dark));
|
||||
background-attachment: fixed;
|
||||
color: #e0e0e0;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.admin-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.admin-topbar,
|
||||
.glass-card {
|
||||
background: var(--card-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--border-glow);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.admin-topbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 1.5rem 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.topbar-info h1 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
background: linear-gradient(90deg, #fff, var(--primary));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.topbar-info p {
|
||||
margin: 0.25rem 0 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--primary);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.45rem;
|
||||
padding: 0.6rem 1.2rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--primary);
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
}
|
||||
|
||||
.btn-modern:hover {
|
||||
background: var(--primary);
|
||||
color: var(--bg-dark);
|
||||
box-shadow: 0 0 15px var(--primary-glow);
|
||||
}
|
||||
|
||||
.btn-modern.danger {
|
||||
border-color: var(--danger);
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.btn-modern.danger:hover {
|
||||
background: var(--danger);
|
||||
color: #fff;
|
||||
box-shadow: 0 0 15px rgba(255, 77, 77, 0.3);
|
||||
}
|
||||
|
||||
.btn-modern.secondary {
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
color: #d6d6d6;
|
||||
}
|
||||
|
||||
.btn-modern.secondary:hover {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
color: #fff;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.btn-mini {
|
||||
padding: 0.45rem 0.8rem;
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid var(--border-glow);
|
||||
}
|
||||
|
||||
.nav-tabs a {
|
||||
text-decoration: none;
|
||||
color: #888;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.9rem;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.nav-tabs a:hover,
|
||||
.nav-tabs a.active {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.flash {
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1rem 1.2rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.flash.success { border-color: rgba(0, 255, 136, 0.25); color: #9ff3c8; }
|
||||
.flash.error { border-color: rgba(255, 77, 77, 0.25); color: #ff9d9d; }
|
||||
|
||||
.page-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 420px 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.stack {
|
||||
display: grid;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.glass-card h2 {
|
||||
margin: 0 0 1.5rem;
|
||||
padding-bottom: 0.75rem;
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary);
|
||||
border-bottom: 1px solid var(--border-glow);
|
||||
}
|
||||
|
||||
.section-note {
|
||||
margin: -0.5rem 0 1.2rem;
|
||||
color: var(--muted);
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
color: #aaa;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.8rem 1rem;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid #444;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
font-family: 'Electrolize', sans-serif;
|
||||
transition: border-color 0.3s, background 0.3s;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.form-control[type="color"] {
|
||||
min-height: 48px;
|
||||
padding: 0.35rem;
|
||||
}
|
||||
|
||||
.checkbox-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.7rem;
|
||||
color: #e7e7e7;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.list-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0 8px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding: 1rem 0.75rem;
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
color: var(--primary);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 1rem 0.75rem;
|
||||
background: var(--card-bg-secondary);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.05);
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
tr:hover td {
|
||||
background: rgba(162, 155, 120, 0.08);
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: var(--muted);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.2rem 0.55rem;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.07);
|
||||
font-size: 0.72rem;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pill.forum {
|
||||
background: rgba(162, 155, 120, 0.18);
|
||||
color: #e5dcb7;
|
||||
}
|
||||
|
||||
.banner-preview {
|
||||
width: 120px;
|
||||
max-height: 42px;
|
||||
object-fit: cover;
|
||||
border-radius: 6px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
display: block;
|
||||
background: #17191e;
|
||||
}
|
||||
|
||||
.color-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
}
|
||||
|
||||
.color-chip::before {
|
||||
content: '';
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: var(--chip-color, var(--primary));
|
||||
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.actions-inline {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.45rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
padding: 2rem;
|
||||
border: 1px dashed rgba(255, 255, 255, 0.12);
|
||||
border-radius: 10px;
|
||||
color: #9a9a9a;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.page-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin-layout">
|
||||
<header class="admin-topbar">
|
||||
<div class="topbar-info">
|
||||
<h1>R.E.A.C.T. SC Webhook</h1>
|
||||
<p>Niveau d'accès : <strong>Administrateur</strong> | Session : <strong><?php echo htmlspecialchars($current_session_user, ENT_QUOTES, 'UTF-8'); ?></strong></p>
|
||||
</div>
|
||||
<div class="topbar-actions">
|
||||
<a href="index.php" class="btn-modern">Site</a>
|
||||
<a href="logout.php" class="btn-modern danger">Exit</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="nav-tabs">
|
||||
<a href="admin.php">Utilisateurs</a>
|
||||
<a href="scwebhook.php" class="active">WEBHOOK</a>
|
||||
<a href="scnotification.php">NOTIF DISCORD</a>
|
||||
<a href="scitems.php">Base d'Objets</a>
|
||||
<a href="scstatsitem.php">Stats Item</a>
|
||||
<a href="scitemcustom.php">Item Custom</a>
|
||||
<a href="scmining.php">Scanner Minage</a>
|
||||
<a href="scmanufactures.php">Manufactures</a>
|
||||
<a href="scvaisseaux.php">Vaisseaux</a>
|
||||
<a href="scpreset.php">Presets Vaisseau</a>
|
||||
</nav>
|
||||
|
||||
<?php if ($flash_message !== ''): ?>
|
||||
<div class="flash <?php echo htmlspecialchars($flash_type, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<?php echo htmlspecialchars($flash_message, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="page-grid">
|
||||
<aside class="stack">
|
||||
<section class="glass-card">
|
||||
<h2 id="webhookFormTitle">Nouveau webhook Discord</h2>
|
||||
<p class="section-note">Ajoute la nomination du webhook et son URL. Tous les webhooks enregistrés ici sont traités comme des webhooks Discord génériques.</p>
|
||||
<form method="post" id="webhookForm" class="form-grid">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<input type="hidden" name="action" id="webhookAction" value="add_webhook">
|
||||
<input type="hidden" name="webhook_id" id="webhookId" value="">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cl_scwebhook_name">Nomination du webhook</label>
|
||||
<input type="text" id="cl_scwebhook_name" name="cl_scwebhook_name" class="form-control" required placeholder="Ex : news-bops-cig">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cl_scwebhook_url">Lien du webhook</label>
|
||||
<input type="url" id="cl_scwebhook_url" name="cl_scwebhook_url" class="form-control" required placeholder="https://discord.com/api/webhooks/...">
|
||||
</div>
|
||||
|
||||
|
||||
<button type="submit" class="btn-modern" id="webhookSubmitBtn">Ajouter le webhook</button>
|
||||
<button type="button" class="btn-modern secondary" id="webhookCancelBtn" style="display:none;" onclick="resetWebhookForm()">Annuler l’édition</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="glass-card">
|
||||
<h2 id="bannerFormTitle">Nouvelle bannière</h2>
|
||||
<p class="section-note">Chaque bannière stocke un nom, un lien d’image et une couleur de bordure utilisée dans l’embed Discord.</p>
|
||||
<form method="post" id="bannerForm" class="form-grid">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<input type="hidden" name="action" id="bannerAction" value="add_banner">
|
||||
<input type="hidden" name="banner_id" id="bannerId" value="">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cl_scbanner_name">Nom de la bannière</label>
|
||||
<input type="text" id="cl_scbanner_name" name="cl_scbanner_name" class="form-control" required placeholder="Ex : Alerte Rouge">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cl_scbanner_url">Lien image</label>
|
||||
<input type="url" id="cl_scbanner_url" name="cl_scbanner_url" class="form-control" required placeholder="https://.../banniere.png">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cl_scbanner_border_color">Couleur de bordure</label>
|
||||
<input type="color" id="cl_scbanner_border_color" name="cl_scbanner_border_color" class="form-control" value="#ffae00">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-modern" id="bannerSubmitBtn">Ajouter la bannière</button>
|
||||
<button type="button" class="btn-modern secondary" id="bannerCancelBtn" style="display:none;" onclick="resetBannerForm()">Annuler l’édition</button>
|
||||
</form>
|
||||
</section>
|
||||
</aside>
|
||||
|
||||
<main class="list-grid">
|
||||
<section class="glass-card">
|
||||
<h2>Webhooks Discord enregistrés</h2>
|
||||
<?php if (empty($webhooks)): ?>
|
||||
<div class="empty-state">Aucun webhook configuré pour le moment.</div>
|
||||
<?php else: ?>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nomination</th>
|
||||
<th>URL</th>
|
||||
<th style="text-align:right;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($webhooks as $webhook): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?php echo htmlspecialchars($webhook['cl_scwebhook_name'], ENT_QUOTES, 'UTF-8'); ?></strong>
|
||||
</td>
|
||||
<td>
|
||||
<span class="muted"><?php echo htmlspecialchars(scdiscord_mask_webhook_url((string) $webhook['cl_scwebhook_url']), ENT_QUOTES, 'UTF-8'); ?></span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="actions-inline">
|
||||
<button
|
||||
type="button"
|
||||
class="btn-modern btn-mini"
|
||||
onclick='editWebhook(<?php echo json_encode([
|
||||
'id' => (int) $webhook['cl_scwebhook_id'],
|
||||
'name' => (string) $webhook['cl_scwebhook_name'],
|
||||
'url' => (string) $webhook['cl_scwebhook_url'],
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?>)'
|
||||
>Éditer</button>
|
||||
<form method="post" onsubmit="return confirm('Supprimer ce webhook Discord ?');">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<input type="hidden" name="action" value="delete_webhook">
|
||||
<input type="hidden" name="webhook_id" value="<?php echo (int) $webhook['cl_scwebhook_id']; ?>">
|
||||
<button type="submit" class="btn-modern btn-mini danger">Supprimer</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="glass-card">
|
||||
<h2>Bannières disponibles</h2>
|
||||
<?php if (empty($banners)): ?>
|
||||
<div class="empty-state">Aucune bannière enregistrée pour le moment.</div>
|
||||
<?php else: ?>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nom</th>
|
||||
<th>Prévisualisation</th>
|
||||
<th>Couleur</th>
|
||||
<th style="text-align:right;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($banners as $banner): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?php echo htmlspecialchars($banner['cl_scbanner_name'], ENT_QUOTES, 'UTF-8'); ?></strong>
|
||||
</td>
|
||||
<td>
|
||||
<img class="banner-preview" src="<?php echo htmlspecialchars($banner['cl_scbanner_url'], ENT_QUOTES, 'UTF-8'); ?>" alt="Prévisualisation bannière">
|
||||
</td>
|
||||
<td>
|
||||
<span class="color-chip" style="--chip-color: <?php echo htmlspecialchars(scdiscord_normalize_hex_color((string) ($banner['cl_scbanner_border_color'] ?? '#ffae00')), ENT_QUOTES, 'UTF-8'); ?>;">
|
||||
<?php echo htmlspecialchars(scdiscord_normalize_hex_color((string) ($banner['cl_scbanner_border_color'] ?? '#ffae00')), ENT_QUOTES, 'UTF-8'); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="actions-inline">
|
||||
<button
|
||||
type="button"
|
||||
class="btn-modern btn-mini"
|
||||
onclick='editBanner(<?php echo json_encode([
|
||||
'id' => (int) $banner['cl_scbanner_id'],
|
||||
'name' => (string) $banner['cl_scbanner_name'],
|
||||
'url' => (string) $banner['cl_scbanner_url'],
|
||||
'border_color' => (string) ($banner['cl_scbanner_border_color'] ?? '#ffae00'),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?>)'
|
||||
>Éditer</button>
|
||||
<form method="post" onsubmit="return confirm('Supprimer cette bannière ?');">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<input type="hidden" name="action" value="delete_banner">
|
||||
<input type="hidden" name="banner_id" value="<?php echo (int) $banner['cl_scbanner_id']; ?>">
|
||||
<button type="submit" class="btn-modern btn-mini danger">Supprimer</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function editWebhook(webhook) {
|
||||
document.getElementById('webhookFormTitle').textContent = 'Modifier le webhook Discord';
|
||||
document.getElementById('webhookAction').value = 'update_webhook';
|
||||
document.getElementById('webhookId').value = webhook.id || '';
|
||||
document.getElementById('cl_scwebhook_name').value = webhook.name || '';
|
||||
document.getElementById('cl_scwebhook_url').value = webhook.url || '';
|
||||
document.getElementById('webhookSubmitBtn').textContent = 'Mettre à jour le webhook';
|
||||
document.getElementById('webhookCancelBtn').style.display = 'inline-flex';
|
||||
}
|
||||
|
||||
function resetWebhookForm() {
|
||||
document.getElementById('webhookFormTitle').textContent = 'Nouveau webhook Discord';
|
||||
document.getElementById('webhookAction').value = 'add_webhook';
|
||||
document.getElementById('webhookId').value = '';
|
||||
document.getElementById('cl_scwebhook_name').value = '';
|
||||
document.getElementById('cl_scwebhook_url').value = '';
|
||||
document.getElementById('webhookSubmitBtn').textContent = 'Ajouter le webhook';
|
||||
document.getElementById('webhookCancelBtn').style.display = 'none';
|
||||
}
|
||||
|
||||
function editBanner(banner) {
|
||||
document.getElementById('bannerFormTitle').textContent = 'Modifier la bannière';
|
||||
document.getElementById('bannerAction').value = 'update_banner';
|
||||
document.getElementById('bannerId').value = banner.id || '';
|
||||
document.getElementById('cl_scbanner_name').value = banner.name || '';
|
||||
document.getElementById('cl_scbanner_url').value = banner.url || '';
|
||||
document.getElementById('cl_scbanner_border_color').value = banner.border_color || '#ffae00';
|
||||
document.getElementById('bannerSubmitBtn').textContent = 'Mettre à jour la bannière';
|
||||
document.getElementById('bannerCancelBtn').style.display = 'inline-flex';
|
||||
}
|
||||
|
||||
function resetBannerForm() {
|
||||
document.getElementById('bannerFormTitle').textContent = 'Nouvelle bannière';
|
||||
document.getElementById('bannerAction').value = 'add_banner';
|
||||
document.getElementById('bannerId').value = '';
|
||||
document.getElementById('cl_scbanner_name').value = '';
|
||||
document.getElementById('cl_scbanner_url').value = '';
|
||||
document.getElementById('cl_scbanner_border_color').value = '#ffae00';
|
||||
document.getElementById('bannerSubmitBtn').textContent = 'Ajouter la bannière';
|
||||
document.getElementById('bannerCancelBtn').style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||