diff --git a/database/full.sql b/database/full.sql
index 43f0078..1ccdc5f 100644
--- a/database/full.sql
+++ b/database/full.sql
@@ -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,
diff --git a/database/schema.sql b/database/schema.sql
index ae6fe80..5d4b61e 100644
--- a/database/schema.sql
+++ b/database/schema.sql
@@ -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,
diff --git a/db/sccharacters.php b/db/sccharacters.php
index 54c1b55..695ca88 100644
--- a/db/sccharacters.php
+++ b/db/sccharacters.php
@@ -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) {
diff --git a/sccharacter.php b/sccharacter.php
index 68f8e49..021a241 100644
--- a/sccharacter.php
+++ b/sccharacter.php
@@ -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(
+
+
+ Rôle / Classe
+
+
+
+ Tag
+
+
+
+ Handle
+
+
+
-
-
-
-
-
-
Créé par
équipement(s)
diff --git a/sccharacters.php b/sccharacters.php
index 53ec7ce..ed51597 100644
--- a/sccharacters.php
+++ b/sccharacters.php
@@ -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', 'L’URL RSI de l’organisation n’est 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', 'L’URL RSI de l’organisation n’est 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) {
-
-
+
+
+
+
+
+
@@ -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) {
-
-
+
+ Org:
+
+
+ Handle:
objet(s)
@@ -2922,13 +3004,21 @@ if ($selected_character) {
📌
-
@@ -2987,8 +3077,12 @@ if ($selected_character) {
-
-
+
+
+
+
+
+