This commit is contained in:
Flatlogic Bot 2026-04-16 09:44:15 +00:00
parent 5504c68916
commit a472ed9a8f
5 changed files with 231 additions and 21 deletions

View File

@ -18734,6 +18734,9 @@ CREATE TABLE IF NOT EXISTS tbl_sccharacters (
cl_sccharacter_name VARCHAR(190) NOT NULL,
cl_sccharacter_role VARCHAR(190) NOT NULL DEFAULT '',
cl_sccharacter_faction VARCHAR(190) NOT NULL DEFAULT '',
cl_sccharacter_org_rsi_url VARCHAR(255) NOT NULL DEFAULT '',
cl_sccharacter_is_player TINYINT(1) NOT NULL DEFAULT 0,
cl_sccharacter_player_handle VARCHAR(190) NOT NULL DEFAULT '',
cl_sccharacter_avatar_url VARCHAR(255) NOT NULL DEFAULT '',
cl_sccharacter_description TEXT DEFAULT NULL,
cl_sccharacter_notes TEXT DEFAULT NULL,

View File

@ -213,6 +213,9 @@ CREATE TABLE IF NOT EXISTS tbl_sccharacters (
cl_sccharacter_name VARCHAR(190) NOT NULL,
cl_sccharacter_role VARCHAR(190) NOT NULL DEFAULT '',
cl_sccharacter_faction VARCHAR(190) NOT NULL DEFAULT '',
cl_sccharacter_org_rsi_url VARCHAR(255) NOT NULL DEFAULT '',
cl_sccharacter_is_player TINYINT(1) NOT NULL DEFAULT 0,
cl_sccharacter_player_handle VARCHAR(190) NOT NULL DEFAULT '',
cl_sccharacter_avatar_url VARCHAR(255) NOT NULL DEFAULT '',
cl_sccharacter_description TEXT DEFAULT NULL,
cl_sccharacter_notes TEXT DEFAULT NULL,

View File

@ -51,6 +51,9 @@ function sccharacters_bootstrap(): void
cl_sccharacter_name VARCHAR(190) NOT NULL,
cl_sccharacter_role VARCHAR(190) NOT NULL DEFAULT '',
cl_sccharacter_faction VARCHAR(190) NOT NULL DEFAULT '',
cl_sccharacter_org_rsi_url VARCHAR(255) NOT NULL DEFAULT '',
cl_sccharacter_is_player TINYINT(1) NOT NULL DEFAULT 0,
cl_sccharacter_player_handle VARCHAR(190) NOT NULL DEFAULT '',
cl_sccharacter_avatar_url VARCHAR(255) NOT NULL DEFAULT '',
cl_sccharacter_description TEXT DEFAULT NULL,
cl_sccharacter_notes TEXT DEFAULT NULL,
@ -99,6 +102,27 @@ function sccharacters_bootstrap(): void
);
}
if (!sccharacters_column_exists($db, 'tbl_sccharacters', 'cl_sccharacter_org_rsi_url')) {
$db->exec(
"ALTER TABLE tbl_sccharacters
ADD COLUMN cl_sccharacter_org_rsi_url VARCHAR(255) NOT NULL DEFAULT '' AFTER cl_sccharacter_faction"
);
}
if (!sccharacters_column_exists($db, 'tbl_sccharacters', 'cl_sccharacter_is_player')) {
$db->exec(
'ALTER TABLE tbl_sccharacters
ADD COLUMN cl_sccharacter_is_player TINYINT(1) NOT NULL DEFAULT 0 AFTER cl_sccharacter_org_rsi_url'
);
}
if (!sccharacters_column_exists($db, 'tbl_sccharacters', 'cl_sccharacter_player_handle')) {
$db->exec(
"ALTER TABLE tbl_sccharacters
ADD COLUMN cl_sccharacter_player_handle VARCHAR(190) NOT NULL DEFAULT '' AFTER cl_sccharacter_is_player"
);
}
if (!sccharacters_column_exists($db, 'tbl_sccharacters', 'cl_sccharacter_category_order')) {
$db->exec(
'ALTER TABLE tbl_sccharacters
@ -287,6 +311,44 @@ function sccharacters_generate_share_token(PDO $db): string
return $token;
}
function sccharacters_extract_org_tag(?string $url): string
{
$url = trim((string) $url);
if ($url === '') {
return '';
}
$path = trim((string) parse_url($url, PHP_URL_PATH));
if ($path === '') {
return '';
}
if (preg_match('~/(?:[a-z]{2}/)?orgs/([^/?#]+)~i', $path, $matches)) {
$tag = rawurldecode((string) ($matches[1] ?? ''));
} else {
$segments = array_values(array_filter(explode('/', trim($path, '/')), static fn (string $segment): bool => $segment !== ''));
$tag = $segments !== [] ? rawurldecode((string) end($segments)) : '';
}
$tag = preg_replace('/[^A-Za-z0-9._-]+/', '', (string) $tag);
if (!is_string($tag)) {
return '';
}
return strtoupper(trim($tag));
}
function sccharacters_resolve_org_tag(array $character): string
{
return sccharacters_extract_org_tag((string) ($character['cl_sccharacter_org_rsi_url'] ?? ''));
}
function sccharacters_has_player_handle(array $character): bool
{
return trim((string) ($character['cl_sccharacter_player_handle'] ?? '')) !== '';
}
function sccharacters_reindex_character_items(PDO $db, int $character_id): void
{
if ($character_id <= 0) {

View File

@ -16,6 +16,8 @@ $share_token = trim((string) ($_GET['share'] ?? ''));
$character = null;
$character_items = [];
$custom_stats_by_itemcustom = [];
$character_org_tag = '';
$character_has_player_handle = false;
if ($share_token !== '') {
$stmt_character = $db->prepare(
@ -31,6 +33,8 @@ if ($share_token !== '') {
}
if ($character) {
$character_org_tag = sccharacters_resolve_org_tag($character);
$character_has_player_handle = sccharacters_has_player_handle($character);
sccharacters_reindex_character_items($db, (int) $character['cl_sccharacter_id']);
$stmt_items = $db->prepare(
@ -227,6 +231,42 @@ $character_items_by_category = sccharacters_sort_items_by_category_order(
align-items: center;
}
.identity-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 0.7rem;
margin-bottom: 0.8rem;
}
.identity-item {
display: flex;
flex-direction: column;
gap: 0.24rem;
padding: 0.76rem 0.84rem;
border-radius: 14px;
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.1);
}
.identity-label {
font-size: 0.7rem;
letter-spacing: 0.09em;
text-transform: uppercase;
color: var(--text-soft);
}
.identity-value {
font-size: 0.96rem;
font-weight: 600;
color: #f6f7fb;
line-height: 1.35;
word-break: break-word;
}
.meta {
margin-top: 0.2rem;
}
.tag {
display: inline-flex;
align-items: center;
@ -389,13 +429,21 @@ $character_items_by_category = sccharacters_sort_items_by_category_order(
<?php endif; ?>
<div>
<h1><?php echo htmlspecialchars((string) $character['cl_sccharacter_name'], ENT_QUOTES, 'UTF-8'); ?></h1>
<div class="identity-grid">
<div class="identity-item">
<span class="identity-label">Rôle / Classe</span>
<span class="identity-value"><?php echo htmlspecialchars(trim((string) $character['cl_sccharacter_role']) !== '' ? (string) $character['cl_sccharacter_role'] : '—', ENT_QUOTES, 'UTF-8'); ?></span>
</div>
<div class="identity-item">
<span class="identity-label">Tag</span>
<span class="identity-value"><?php echo htmlspecialchars($character_org_tag !== '' ? $character_org_tag : '—', ENT_QUOTES, 'UTF-8'); ?></span>
</div>
<div class="identity-item">
<span class="identity-label">Handle</span>
<span class="identity-value"><?php echo htmlspecialchars(trim((string) ($character['cl_sccharacter_player_handle'] ?? '')) !== '' ? (string) $character['cl_sccharacter_player_handle'] : '—', ENT_QUOTES, 'UTF-8'); ?></span>
</div>
</div>
<div class="meta">
<?php if (trim((string) $character['cl_sccharacter_role']) !== ''): ?>
<span class="tag tag-primary"><?php echo htmlspecialchars((string) $character['cl_sccharacter_role'], ENT_QUOTES, 'UTF-8'); ?></span>
<?php endif; ?>
<?php if (trim((string) $character['cl_sccharacter_faction']) !== ''): ?>
<span class="tag"><?php echo htmlspecialchars((string) $character['cl_sccharacter_faction'], ENT_QUOTES, 'UTF-8'); ?></span>
<?php endif; ?>
<span class="tag muted">Créé par <?php echo htmlspecialchars((string) $character['cl_sccharacter_creator_name'], ENT_QUOTES, 'UTF-8'); ?></span>
<span class="tag muted"><?php echo count($character_items); ?> équipement(s)</span>
</div>

View File

@ -886,7 +886,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($action === 'create_character') {
$name = sccharacters_clean_text($_POST['character_name'] ?? '');
$role = sccharacters_clean_text($_POST['character_role'] ?? '');
$faction = sccharacters_clean_text($_POST['character_faction'] ?? '');
$faction = '';
$org_rsi_url = sccharacters_clean_text($_POST['character_org_rsi_url'] ?? '');
$player_handle = sccharacters_clean_text($_POST['character_player_handle'] ?? '');
$is_player = $player_handle !== '' ? 1 : 0;
$avatar_url = sccharacters_clean_text($_POST['character_avatar_url'] ?? '');
$description = sccharacters_clean_text($_POST['character_description'] ?? '');
$notes = sccharacters_clean_text($_POST['character_notes'] ?? '');
@ -907,12 +910,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
exit;
}
if (!sccharacters_is_valid_url($org_rsi_url)) {
auth_flash_set('error', 'LURL RSI de lorganisation nest pas valide.');
header('Location: sccharacters.php?mode=create');
exit;
}
$stmt = $db->prepare(
'INSERT INTO tbl_sccharacters (
cl_sccharacter_owner_auth_id,
cl_sccharacter_name,
cl_sccharacter_role,
cl_sccharacter_faction,
cl_sccharacter_org_rsi_url,
cl_sccharacter_is_player,
cl_sccharacter_player_handle,
cl_sccharacter_avatar_url,
cl_sccharacter_description,
cl_sccharacter_notes,
@ -924,6 +936,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
:name,
:role,
:faction,
:org_rsi_url,
:is_player,
:player_handle,
:avatar_url,
:description,
:notes,
@ -937,6 +952,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
'name' => $name,
'role' => $role,
'faction' => $faction,
'org_rsi_url' => $org_rsi_url,
'is_player' => $is_player,
'player_handle' => $player_handle,
'avatar_url' => $avatar_url,
'description' => $description !== '' ? $description : null,
'notes' => $notes !== '' ? $notes : null,
@ -963,7 +981,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = sccharacters_clean_text($_POST['character_name'] ?? '');
$role = sccharacters_clean_text($_POST['character_role'] ?? '');
$faction = sccharacters_clean_text($_POST['character_faction'] ?? '');
$faction = '';
$org_rsi_url = sccharacters_clean_text($_POST['character_org_rsi_url'] ?? '');
$player_handle = sccharacters_clean_text($_POST['character_player_handle'] ?? '');
$is_player = $player_handle !== '' ? 1 : 0;
$avatar_url = sccharacters_clean_text($_POST['character_avatar_url'] ?? '');
$description = sccharacters_clean_text($_POST['character_description'] ?? '');
$notes = sccharacters_clean_text($_POST['character_notes'] ?? '');
@ -984,11 +1005,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
exit;
}
if (!sccharacters_is_valid_url($org_rsi_url)) {
auth_flash_set('error', 'LURL RSI de lorganisation nest pas valide.');
header('Location: sccharacters.php?character=' . $character_id);
exit;
}
$stmt = $db->prepare(
'UPDATE tbl_sccharacters
SET cl_sccharacter_name = :name,
cl_sccharacter_role = :role,
cl_sccharacter_faction = :faction,
cl_sccharacter_org_rsi_url = :org_rsi_url,
cl_sccharacter_is_player = :is_player,
cl_sccharacter_player_handle = :player_handle,
cl_sccharacter_avatar_url = :avatar_url,
cl_sccharacter_description = :description,
cl_sccharacter_notes = :notes,
@ -1001,6 +1031,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
'name' => $name,
'role' => $role,
'faction' => $faction,
'org_rsi_url' => $org_rsi_url,
'is_player' => $is_player,
'player_handle' => $player_handle,
'avatar_url' => $avatar_url,
'description' => $description !== '' ? $description : null,
'notes' => $notes !== '' ? $notes : null,
@ -1581,12 +1614,17 @@ $create_character = [
'cl_sccharacter_name' => '',
'cl_sccharacter_role' => '',
'cl_sccharacter_faction' => '',
'cl_sccharacter_org_rsi_url' => '',
'cl_sccharacter_is_player' => 0,
'cl_sccharacter_player_handle' => '',
'cl_sccharacter_avatar_url' => '',
'cl_sccharacter_description' => '',
'cl_sccharacter_notes' => '',
'cl_sccharacter_share_enabled' => 0,
'cl_sccharacter_is_pinned' => 0,
];
$selected_character_org_tag = $selected_character ? sccharacters_resolve_org_tag($selected_character) : '';
$selected_character_has_player_handle = $selected_character ? sccharacters_has_player_handle($selected_character) : false;
$item_has_previous_page = $item_page > 1;
$item_has_next_page = $item_page < $item_total_pages;
$item_query_base_params = [];
@ -2066,6 +2104,41 @@ if ($selected_character) {
flex-wrap: wrap;
}
.hero-meta-secondary {
margin-top: 0.7rem;
}
.identity-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 0.7rem;
}
.identity-item {
display: flex;
flex-direction: column;
gap: 0.22rem;
padding: 0.72rem 0.82rem;
border-radius: 14px;
background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.1);
}
.identity-label {
font-size: 0.7rem;
letter-spacing: 0.09em;
text-transform: uppercase;
color: var(--text-soft);
}
.identity-value {
font-size: 0.95rem;
font-weight: 600;
color: #f6f7fb;
line-height: 1.35;
word-break: break-word;
}
.badge {
display: inline-flex;
align-items: center;
@ -2797,8 +2870,12 @@ if ($selected_character) {
<input type="text" id="newCharacterRole" name="character_role" maxlength="190" value="<?php echo htmlspecialchars((string) $create_character['cl_sccharacter_role'], ENT_QUOTES, 'UTF-8'); ?>">
</div>
<div class="field">
<label for="newCharacterFaction">Faction / Groupe</label>
<input type="text" id="newCharacterFaction" name="character_faction" maxlength="190" value="<?php echo htmlspecialchars((string) $create_character['cl_sccharacter_faction'], ENT_QUOTES, 'UTF-8'); ?>">
<label for="newCharacterOrgRsiUrl">Page RSI (URL)</label>
<input type="url" id="newCharacterOrgRsiUrl" name="character_org_rsi_url" placeholder="https://robertsspaceindustries.com/en/orgs/REACT" value="<?php echo htmlspecialchars((string) $create_character['cl_sccharacter_org_rsi_url'], ENT_QUOTES, 'UTF-8'); ?>">
</div>
<div class="field">
<label for="newCharacterPlayerHandle">Handle</label>
<input type="text" id="newCharacterPlayerHandle" name="character_player_handle" maxlength="190" value="<?php echo htmlspecialchars((string) $create_character['cl_sccharacter_player_handle'], ENT_QUOTES, 'UTF-8'); ?>">
</div>
<div class="field-full">
<label for="newCharacterAvatar">Avatar (URL dimage)</label>
@ -2857,6 +2934,8 @@ if ($selected_character) {
$initial = function_exists('mb_substr')
? mb_strtoupper(mb_substr((string) $character_row['cl_sccharacter_name'], 0, 1, 'UTF-8'), 'UTF-8')
: strtoupper(substr((string) $character_row['cl_sccharacter_name'], 0, 1));
$character_row_org_tag = sccharacters_resolve_org_tag($character_row);
$character_row_has_player_handle = sccharacters_has_player_handle($character_row);
$character_link = 'sccharacters.php?character=' . $character_id . '&item_source=' . rawurlencode($item_source);
if ($item_search !== '') {
$character_link .= '&item_search=' . rawurlencode($item_search);
@ -2879,8 +2958,11 @@ if ($selected_character) {
<?php if (trim((string) $character_row['cl_sccharacter_role']) !== ''): ?>
<span class="badge badge-primary"><?php echo htmlspecialchars((string) $character_row['cl_sccharacter_role'], ENT_QUOTES, 'UTF-8'); ?></span>
<?php endif; ?>
<?php if (trim((string) $character_row['cl_sccharacter_faction']) !== ''): ?>
<span class="badge"><?php echo htmlspecialchars((string) $character_row['cl_sccharacter_faction'], ENT_QUOTES, 'UTF-8'); ?></span>
<?php if ($character_row_org_tag !== ''): ?>
<span class="badge">Org: <?php echo htmlspecialchars($character_row_org_tag, ENT_QUOTES, 'UTF-8'); ?></span>
<?php endif; ?>
<?php if ($character_row_has_player_handle): ?>
<span class="badge">Handle: <?php echo htmlspecialchars((string) $character_row['cl_sccharacter_player_handle'], ENT_QUOTES, 'UTF-8'); ?></span>
<?php endif; ?>
<span class="badge badge-muted"><?php echo (int) $character_row['cl_sccharacter_item_count']; ?> objet(s)</span>
<?php if (!empty($character_row['cl_sccharacter_share_enabled'])): ?>
@ -2922,13 +3004,21 @@ if ($selected_character) {
<span class="pin-indicator" title="Personnage épinglé" aria-label="Personnage épinglé">📌</span>
<?php endif; ?>
</div>
<div class="hero-meta">
<?php if (trim((string) $selected_character['cl_sccharacter_role']) !== ''): ?>
<span class="badge badge-primary"><?php echo htmlspecialchars((string) $selected_character['cl_sccharacter_role'], ENT_QUOTES, 'UTF-8'); ?></span>
<?php endif; ?>
<?php if (trim((string) $selected_character['cl_sccharacter_faction']) !== ''): ?>
<span class="badge"><?php echo htmlspecialchars((string) $selected_character['cl_sccharacter_faction'], ENT_QUOTES, 'UTF-8'); ?></span>
<?php endif; ?>
<div class="identity-grid">
<div class="identity-item">
<span class="identity-label">Rôle / Classe</span>
<span class="identity-value"><?php echo htmlspecialchars(trim((string) $selected_character['cl_sccharacter_role']) !== '' ? (string) $selected_character['cl_sccharacter_role'] : '—', ENT_QUOTES, 'UTF-8'); ?></span>
</div>
<div class="identity-item">
<span class="identity-label">Tag organisation</span>
<span class="identity-value"><?php echo htmlspecialchars($selected_character_org_tag !== '' ? $selected_character_org_tag : '—', ENT_QUOTES, 'UTF-8'); ?></span>
</div>
<div class="identity-item">
<span class="identity-label">Handle</span>
<span class="identity-value"><?php echo htmlspecialchars(trim((string) ($selected_character['cl_sccharacter_player_handle'] ?? '')) !== '' ? (string) $selected_character['cl_sccharacter_player_handle'] : '—', ENT_QUOTES, 'UTF-8'); ?></span>
</div>
</div>
<div class="hero-meta hero-meta-secondary">
<span class="badge badge-muted"><?php echo count($selected_character_items); ?> équipement(s)</span>
</div>
</div>
@ -2987,8 +3077,12 @@ if ($selected_character) {
<input type="text" id="editCharacterRole" name="character_role" maxlength="190" value="<?php echo htmlspecialchars((string) $selected_character['cl_sccharacter_role'], ENT_QUOTES, 'UTF-8'); ?>">
</div>
<div class="field">
<label for="editCharacterFaction">Faction / Groupe</label>
<input type="text" id="editCharacterFaction" name="character_faction" maxlength="190" value="<?php echo htmlspecialchars((string) $selected_character['cl_sccharacter_faction'], ENT_QUOTES, 'UTF-8'); ?>">
<label for="editCharacterOrgRsiUrl">Page RSI (URL)</label>
<input type="url" id="editCharacterOrgRsiUrl" name="character_org_rsi_url" placeholder="https://robertsspaceindustries.com/en/orgs/REACT" value="<?php echo htmlspecialchars((string) ($selected_character['cl_sccharacter_org_rsi_url'] ?? ''), ENT_QUOTES, 'UTF-8'); ?>">
</div>
<div class="field">
<label for="editCharacterPlayerHandle">Handle</label>
<input type="text" id="editCharacterPlayerHandle" name="character_player_handle" maxlength="190" value="<?php echo htmlspecialchars((string) ($selected_character['cl_sccharacter_player_handle'] ?? ''), ENT_QUOTES, 'UTF-8'); ?>">
</div>
<div class="field-full">
<label for="editCharacterAvatar">Avatar (URL dimage)</label>