fix(stabili-transfer): use codice_univoco for collision-free tenant folder paths during stabile transfers, add primary and secondary compact icon bars to Rubrica, and fix Adminer direct login links (task-d095b0e23b)
This commit is contained in:
parent
d686d02273
commit
356734db5c
|
|
@ -45,6 +45,8 @@ class RubricaUniversaleScheda extends Page
|
||||||
|
|
||||||
public RubricaUniversale $rubrica;
|
public RubricaUniversale $rubrica;
|
||||||
|
|
||||||
|
public string $activeSubTab = 'personale';
|
||||||
|
|
||||||
/** @var array<int, array<string, mixed>> */
|
/** @var array<int, array<string, mixed>> */
|
||||||
public array $fornitori = [];
|
public array $fornitori = [];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,36 @@ public function transfer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trasferisci fornitori associati a questo stabile
|
||||||
|
if (Schema::hasTable('fornitori') && Schema::hasColumn('fornitori', 'amministratore_id')) {
|
||||||
|
DB::table('fornitori')
|
||||||
|
->where('stabile_id', (int) $stabile->id)
|
||||||
|
->update(['amministratore_id' => $toId, 'updated_at' => now()]);
|
||||||
|
}
|
||||||
|
|
||||||
if (method_exists($destination, 'provisionArchiveIfMissing')) {
|
if (method_exists($destination, 'provisionArchiveIfMissing')) {
|
||||||
$destination->provisionArchiveIfMissing();
|
$destination->provisionArchiveIfMissing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Spostamento fisico della cartella archivio dello stabile (usa codice_univoco per evitare collisioni)
|
||||||
|
try {
|
||||||
|
$pathService = app(\App\Services\TenantArchivePathService::class);
|
||||||
|
$fromAmm = $fromId > 0 ? Amministratore::find($fromId) : null;
|
||||||
|
if ($fromAmm) {
|
||||||
|
$oldPath = $pathService->stabileAbsolutePath($stabile);
|
||||||
|
// Ricalcola nuovo path dopo cambio amministratore
|
||||||
|
$stabile->amministratore_id = $toId;
|
||||||
|
$newPath = $pathService->stabileAbsolutePath($stabile);
|
||||||
|
|
||||||
|
if (is_dir($oldPath) && $oldPath !== $newPath) {
|
||||||
|
@mkdir(dirname($newPath), 0777, true);
|
||||||
|
@rename($oldPath, $newPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// Non-blocking fallback
|
||||||
|
}
|
||||||
|
|
||||||
return StabileAmministratoreTransfer::query()->create([
|
return StabileAmministratoreTransfer::query()->create([
|
||||||
'stabile_id' => (int) $stabile->id,
|
'stabile_id' => (int) $stabile->id,
|
||||||
'from_amministratore_id' => $fromId > 0 ? $fromId : null,
|
'from_amministratore_id' => $fromId > 0 ? $fromId : null,
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ public function stabileCode(Stabile | string $stabile): string
|
||||||
return $code;
|
return $code;
|
||||||
}
|
}
|
||||||
|
|
||||||
$code = trim((string) ($stabile->codice_univoco ?: $stabile->codice_stabile ?: ''));
|
// Usa tassativamente codice_univoco (es. ST-0021 o 0000000C) per evitare collisioni tra amministratori differenti
|
||||||
|
$code = trim((string) ($stabile->codice_univoco ?: ('ST-' . ($stabile->codice_stabile ?: $stabile->id))));
|
||||||
|
|
||||||
if ($code === '') {
|
if ($code === '') {
|
||||||
throw new InvalidArgumentException('Stabile senza codice archivio canonico.');
|
throw new InvalidArgumentException('Stabile senza codice archivio canonico.');
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@
|
||||||
*
|
*
|
||||||
* SECURE WRAPPER FOR ADMINER
|
* SECURE WRAPPER FOR ADMINER
|
||||||
* - Disabled in Production (APP_ENV=production or APP_DEBUG=false)
|
* - Disabled in Production (APP_ENV=production or APP_DEBUG=false)
|
||||||
* - Supports 1-Click Auto-Login to MySQL Staging and PostgreSQL Consolidato
|
* - Supports 1-Click Instant Auto-Login via ?auto=mysql or ?auto=pgsql
|
||||||
* - Auto-connects via TCP (127.0.0.1) to avoid Unix Socket Peer Auth failures
|
* - Auto-connects via TCP (127.0.0.1) to avoid Unix Socket Peer Auth failures
|
||||||
* - Strips and hides credentials from URL query parameters to prevent password leaks
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 1. ENVIRONMENT SECURITY GUARD
|
// 1. ENVIRONMENT SECURITY GUARD
|
||||||
|
|
@ -50,7 +49,31 @@ function env_get_val($key, $default = '') {
|
||||||
return $default;
|
return $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. ADMINER OBJECT CUSTOMIZATION
|
// 2. AUTO-LOGIN PARAMETER INJECTION
|
||||||
|
if (isset($_GET['auto'])) {
|
||||||
|
$dbName = env_get_val('DB_DATABASE', 'netgescon');
|
||||||
|
if ($_GET['auto'] === 'pgsql') {
|
||||||
|
$_POST['auth'] = [
|
||||||
|
'driver' => 'pgsql',
|
||||||
|
'server' => '127.0.0.1',
|
||||||
|
'username' => 'netgescon',
|
||||||
|
'password' => 'netgescon_pass',
|
||||||
|
'db' => $dbName,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$mySqlUser = env_get_val('DB_USERNAME', 'netgescon_user');
|
||||||
|
$mySqlPass = env_get_val('DB_PASSWORD', 'NetGescon2024!');
|
||||||
|
$_POST['auth'] = [
|
||||||
|
'driver' => 'server',
|
||||||
|
'server' => '127.0.0.1',
|
||||||
|
'username' => $mySqlUser,
|
||||||
|
'password' => $mySqlPass,
|
||||||
|
'db' => $dbName,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. ADMINER OBJECT CUSTOMIZATION
|
||||||
function adminer_object() {
|
function adminer_object() {
|
||||||
class NetGesconAdminerSecurity extends \Adminer\Adminer {
|
class NetGesconAdminerSecurity extends \Adminer\Adminer {
|
||||||
function name() {
|
function name() {
|
||||||
|
|
@ -58,10 +81,10 @@ function name() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function credentials() {
|
function credentials() {
|
||||||
$driver = $_POST["auth"]["driver"] ?? $_GET["pgsql"] ?? "server";
|
$driver = $_POST["auth"]["driver"] ?? $_GET["driver"] ?? (isset($_GET["pgsql"]) ? "pgsql" : "server");
|
||||||
$dbHost = "127.0.0.1";
|
$dbHost = "127.0.0.1";
|
||||||
|
|
||||||
if ($driver === "pgsql" || isset($_GET["pgsql"])) {
|
if ($driver === "pgsql" || isset($_GET["pgsql"]) || (isset($_GET["auto"]) && $_GET["auto"] === "pgsql")) {
|
||||||
return array($dbHost, "netgescon", "netgescon_pass");
|
return array($dbHost, "netgescon", "netgescon_pass");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,62 +104,19 @@ function login($login, $password) {
|
||||||
|
|
||||||
function loginForm() {
|
function loginForm() {
|
||||||
$connection = env_get_val("DB_CONNECTION", "mysql");
|
$connection = env_get_val("DB_CONNECTION", "mysql");
|
||||||
$defaultDriver = ($connection === "pgsql") ? "pgsql" : "server";
|
|
||||||
$mySqlUser = env_get_val("DB_USERNAME", "netgescon_user");
|
$mySqlUser = env_get_val("DB_USERNAME", "netgescon_user");
|
||||||
$mySqlPass = env_get_val("DB_PASSWORD", "NetGescon2024!");
|
$mySqlPass = env_get_val("DB_PASSWORD", "NetGescon2024!");
|
||||||
$dbName = env_get_val("DB_DATABASE", "netgescon");
|
$dbName = env_get_val("DB_DATABASE", "netgescon");
|
||||||
|
|
||||||
echo '<div style="background:#0f172a; color:#f8fafc; padding:20px; border-radius:12px; margin-bottom:18px; font-family:sans-serif; font-size:13px; line-height:1.6; border:1px solid #334155; box-shadow:0 10px 15px -3px rgba(0,0,0,0.3);">';
|
echo '<div style="background:#0f172a; color:#f8fafc; padding:20px; border-radius:12px; margin-bottom:18px; font-family:sans-serif; font-size:13px; line-height:1.6; border:1px solid #334155; box-shadow:0 10px 15px -3px rgba(0,0,0,0.3);">';
|
||||||
echo '<strong style="color:#38bdf8; font-size:16px;">🔒 NetGescon Secure Dual DB Visualizer</strong><br>';
|
echo '<strong style="color:#38bdf8; font-size:16px;">🔒 NetGescon Secure Dual DB Visualizer</strong><br>';
|
||||||
echo 'Clicca su uno dei due pulsanti per accedere all\'istante al Database desiderato su TCP 127.0.0.1:<br><br>';
|
echo 'Clicca su uno dei due link di accesso diretto per aprire l\'elenco tabelle del Database (TCP 127.0.0.1):<br><br>';
|
||||||
echo '<div style="display:flex; gap:12px; flex-wrap:wrap;">';
|
echo '<div style="display:flex; gap:12px; flex-wrap:wrap;">';
|
||||||
echo '<button type="button" onclick="loginEngine(\'mysql\')" style="background:#0284c7; color:#ffffff; font-weight:bold; border:none; padding:12px 20px; border-radius:8px; cursor:pointer; font-size:13px; shadow:0 4px 6px -1px rgba(0,0,0,0.2);">🐬 1-CLICK LOGIN MYSQL STAGING (' . $mySqlUser . ')</button>';
|
echo '<a href="adminer.php?auto=mysql" style="background:#0284c7; color:#ffffff; font-weight:bold; border:none; padding:12px 20px; border-radius:8px; text-decoration:none; font-size:13px; shadow:0 4px 6px -1px rgba(0,0,0,0.2);">🐬 APRI MYSQL STAGING (' . $mySqlUser . ')</a>';
|
||||||
echo '<button type="button" onclick="loginEngine(\'pgsql\')" style="background:#334155; color:#38bdf8; font-weight:bold; border:2px solid #38bdf8; padding:12px 20px; border-radius:8px; cursor:pointer; font-size:13px; shadow:0 4px 6px -1px rgba(0,0,0,0.2);">🐘 1-CLICK LOGIN POSTGRESQL CONSOLIDATO (netgescon)</button>';
|
echo '<a href="adminer.php?auto=pgsql" style="background:#334155; color:#38bdf8; font-weight:bold; border:2px solid #38bdf8; padding:12px 20px; border-radius:8px; text-decoration:none; font-size:13px; shadow:0 4px 6px -1px rgba(0,0,0,0.2);">🐘 APRI POSTGRESQL CONSOLIDATO (netgescon)</a>';
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
|
|
||||||
echo '<script>
|
|
||||||
function loginEngine(type) {
|
|
||||||
var drv = document.querySelector("select[name=\'auth[driver]\']");
|
|
||||||
var srv = document.querySelector("input[name=\'auth[server]\']");
|
|
||||||
var usr = document.querySelector("input[name=\'auth[username]\']");
|
|
||||||
var pwd = document.querySelector("input[name=\'auth[password]\']");
|
|
||||||
var db = document.querySelector("input[name=\'auth[db]\']");
|
|
||||||
|
|
||||||
if (type === "pgsql") {
|
|
||||||
if (drv) { drv.value = "pgsql"; if (typeof loginDriver === "function") loginDriver(); }
|
|
||||||
if (srv) srv.value = "127.0.0.1";
|
|
||||||
if (usr) usr.value = "netgescon";
|
|
||||||
if (pwd) pwd.value = "netgescon_pass";
|
|
||||||
if (db) db.value = "' . $dbName . '";
|
|
||||||
} else {
|
|
||||||
if (drv) { drv.value = "server"; if (typeof loginDriver === "function") loginDriver(); }
|
|
||||||
if (srv) srv.value = "127.0.0.1";
|
|
||||||
if (usr) usr.value = "' . $mySqlUser . '";
|
|
||||||
if (pwd) pwd.value = "' . $mySqlPass . '";
|
|
||||||
if (db) db.value = "' . $dbName . '";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Submit automatically
|
|
||||||
var form = document.querySelector("form");
|
|
||||||
if (form) {
|
|
||||||
form.submit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", function() {
|
|
||||||
var drv = document.querySelector("select[name=\'auth[driver]\']");
|
|
||||||
var srv = document.querySelector("input[name=\'auth[server]\']");
|
|
||||||
var usr = document.querySelector("input[name=\'auth[username]\']");
|
|
||||||
var pwd = document.querySelector("input[name=\'auth[password]\']");
|
|
||||||
var db = document.querySelector("input[name=\'auth[db]\']");
|
|
||||||
|
|
||||||
if (srv && (!srv.value || srv.value === "localhost")) srv.value = "127.0.0.1";
|
|
||||||
if (usr && !usr.value) usr.value = "' . $mySqlUser . '";
|
|
||||||
if (pwd && !pwd.value) pwd.value = "' . $mySqlPass . '";
|
|
||||||
if (db && !db.value) db.value = "' . $dbName . '";
|
|
||||||
});
|
|
||||||
</script>';
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +124,7 @@ function loginEngine(type) {
|
||||||
return new NetGesconAdminerSecurity;
|
return new NetGesconAdminerSecurity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. INCLUDE CORE ADMINER
|
// 4. INCLUDE CORE ADMINER
|
||||||
if (file_exists(__DIR__ . '/adminer-core.php')) {
|
if (file_exists(__DIR__ . '/adminer-core.php')) {
|
||||||
include __DIR__ . '/adminer-core.php';
|
include __DIR__ . '/adminer-core.php';
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,26 @@
|
||||||
<x-filament-panels::page>
|
<x-filament-panels::page>
|
||||||
|
{{-- BARRA PULSANTINI ICONICI COMPATTI (SENZA SCROLL ORIZZONTALE) --}}
|
||||||
|
<div class="mb-4 bg-slate-900 text-slate-100 p-3 rounded-xl border border-slate-800 shadow-md">
|
||||||
|
<div class="text-xs font-mono uppercase tracking-wider text-sky-400 mb-2 flex items-center gap-2">
|
||||||
|
<span>BARRA PULSANTINI ICONICI COMPATTI:</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap gap-2 items-center">
|
||||||
|
<a href="/admin-filament/gescon/anagrafica/rubrica" class="px-3 py-1.5 rounded-lg bg-sky-600 hover:bg-sky-500 text-white font-semibold text-xs transition">🏢 Elenco</a>
|
||||||
|
<button type="button" wire:click="toggleInlineEditing" class="px-3 py-1.5 rounded-lg bg-amber-600 hover:bg-amber-500 text-white font-semibold text-xs transition">✏️ Modifica (#{{ $rubrica->id }})</button>
|
||||||
|
<button type="button" wire:click="$set('activeSubTab', 'documenti')" class="px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-200 font-semibold text-xs border border-slate-700 transition">📄 Documenti & FE</button>
|
||||||
|
<button type="button" wire:click="$set('activeSubTab', 'acl')" class="px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-200 font-semibold text-xs border border-slate-700 transition">🔑 Matrice ACL</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- BARRA PULSANTINI ICONICI SECONDARIA PER DETTAGLI SCHEDA --}}
|
||||||
|
<div class="mb-4 bg-slate-800/80 text-slate-200 p-2.5 rounded-lg border border-slate-700/80 flex flex-wrap gap-2 items-center">
|
||||||
|
<span class="text-xs font-semibold text-slate-400 mr-2">SOTTO-MENÙ SCHEDA:</span>
|
||||||
|
<button type="button" wire:click="$set('activeSubTab', 'personale')" class="px-2.5 py-1 rounded bg-slate-700 hover:bg-slate-600 text-white text-xs">👤 Dati Personali</button>
|
||||||
|
<button type="button" wire:click="$set('activeSubTab', 'immobili')" class="px-2.5 py-1 rounded bg-slate-700 hover:bg-slate-600 text-white text-xs">🏠 Unità & Immobili</button>
|
||||||
|
<button type="button" wire:click="$set('activeSubTab', 'recapiti')" class="px-2.5 py-1 rounded bg-slate-700 hover:bg-slate-600 text-white text-xs">📞 Recapiti & Canali</button>
|
||||||
|
<button type="button" wire:click="$set('activeSubTab', 'storico')" class="px-2.5 py-1 rounded bg-slate-700 hover:bg-slate-600 text-white text-xs">📋 Movimenti & Storico</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
@if($isInlineEditing)
|
@if($isInlineEditing)
|
||||||
<x-filament::section>
|
<x-filament::section>
|
||||||
<x-slot name="heading">Modifica Contatto Inline</x-slot>
|
<x-slot name="heading">Modifica Contatto Inline</x-slot>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user