2026-05-26 08:14:29 +02:00
@ extends ( 'layouts.adminlte' )
@ section ( 'title' , 'Documenti' )
@ section ( 'page_title' , 'Gestione Documenti' )
@ php
$canWriteDocumenti = Auth :: user () -> canManage ( 'documenti' );
$canDeleteDocumenti = Auth :: user () -> canDelete ( 'documenti' );
@ endphp
2026-05-28 09:34:28 +02:00
@ section ( 'css' )
< style >
. list - group - item . folder - actions { display : inline - flex ; }
. list - group - item . folder - actions . btn - link : hover i { opacity : 0.7 ; }
</ style >
@ endsection
2026-05-26 08:14:29 +02:00
@ section ( 'content' )
2026-05-28 09:34:28 +02:00
< div class = " row " >
{{ -- Sidebar cartelle -- }}
< div class = " col-md-3 " >
< div class = " card card-secondary card-outline " >
< div class = " card-header " >
< h3 class = " card-title " >
< i class = " fas fa-folder mr-2 " ></ i > Cartelle
</ h3 >
2026-05-26 08:14:29 +02:00
@ if ( $canWriteDocumenti )
2026-05-28 09:34:28 +02:00
< div class = " card-tools " >
< button type = " button " class = " btn btn-tool " onclick = " $ ('#newFolderModal').modal('show') " title = " Nuova cartella " >
< i class = " fas fa-plus " ></ i >
</ button >
</ div >
2026-05-26 08:14:29 +02:00
@ endif
</ div >
2026-05-28 09:34:28 +02:00
< div class = " card-body p-0 " >
< div class = " list-group list-group-flush " >
< a href = " /documenti "
class = " list-group-item list-group-item-action d-flex align-items-center py-2 { { ! $currentFolder && ! $currentRepo ? 'active' : '' }} " >
< i class = " fas fa-folder-open { { ! $currentFolder && ! $currentRepo ? 'text-white' : 'text-primary' }} mr-2 " ></ i >
< span class = " small " > Tutti i documenti </ span >
</ a >
@ if ( $cartelle -> count () > 0 )
@ include ( 'documenti._folder_tree' , [
'cartelle' => $cartelle ,
'currentFolderId' => $currentFolder ? $currentFolder -> id : null ,
'level' => 0 ,
'canWrite' => $canWriteDocumenti ,
'canDelete' => $canDeleteDocumenti ,
])
@ elseif ( ! $repositories -> count ())
< div class = " text-center text-muted py-3 small " >
< i class = " fas fa-folder-open d-block mb-1 " ></ i >
Nessuna cartella
</ div >
@ endif
@ if ( $repositories -> count () > 0 )
< div class = " list-group-item list-group-item-info d-flex align-items-center py-1 bg-light " >
< i class = " fas fa-cloud mr-2 text-info " ></ i >
< span class = " small font-weight-bold " > Repository Remoti </ span >
</ div >
@ foreach ( $repositories as $repo )
@ php
$isRepoActive = $currentRepo && $currentRepo -> id === $repo -> id ;
$repoIcon = $repo -> tipo === 'webdav' ? 'fa-server' : 'fa-google-drive' ;
$repoColor = $repo -> tipo === 'webdav' ? 'text-secondary' : 'text-danger' ;
@ endphp
< a href = " # "
onclick = 'event.preventDefault(); browseRepo({{ $repo->id }}, {{ json_encode($repo->nome) }}, "/"); return false;'
class = " list-group-item list-group-item-action d-flex align-items-center py-2 repo-link { { $isRepoActive ? 'active' : '' }} "
data - repo - id = " { { $repo->id }} "
style = " padding-left: 40px !important; " >
< i class = " fab { { $repoIcon }} { { $isRepoActive ? 'text-white' : $repoColor }} mr-2 " ></ i >
< span class = " small text-truncate " > {{ $repo -> nome }} </ span >
</ a >
@ endforeach
@ endif
</ div >
</ div >
</ div >
</ div >
{{ -- Contenuto principale -- }}
< div class = " col-md-9 " >
< div class = " card card-primary card-outline " >
< div class = " card-header " >
{{ -- Breadcrumb -- }}
< nav aria - label = " breadcrumb " >
< ol class = " breadcrumb mb-0 p-0 bg-transparent " >
< li class = " breadcrumb-item " >
< a href = " /documenti " >< i class = " fas fa-home mr-1 " ></ i > Documenti </ a >
</ li >
@ if ( $currentRepo )
< li class = " breadcrumb-item active " >
< i class = " fab { { $currentRepo->tipo === 'webdav' ? 'fa-server' : 'fa-google-drive' }} mr-1 " ></ i >
{{ $currentRepo -> nome }}
</ li >
@ else
@ foreach ( $breadcrumb as $anc )
< li class = " breadcrumb-item " >
< a href = " /documenti?folder_id= { { $anc->id }} " > {{ $anc -> nome }} </ a >
</ li >
@ endforeach
@ if ( $currentFolder )
< li class = " breadcrumb-item active " > {{ $currentFolder -> nome }} </ li >
2026-05-26 08:14:29 +02:00
@ endif
2026-05-28 09:34:28 +02:00
@ endif
</ ol >
</ nav >
< div class = " card-tools d-flex align-items-center " >
< div class = " btn-group btn-group-sm mr-2 " role = " group " >
< button type = " button " class = " btn btn-outline-secondary " id = " viewGridBtn " onclick = " setView('grid') " title = " Vista griglia " >
< i class = " fas fa-th-large " ></ i >
</ button >
< button type = " button " class = " btn btn-outline-secondary " id = " viewListBtn " onclick = " setView('list') " title = " Vista lista " >
< i class = " fas fa-list " ></ i >
</ button >
</ div >
@ if ( $canWriteDocumenti )
< button type = " button " class = " btn btn-sm btn-success " onclick = " $ ('#uploadModal').modal('show') " >
< i class = " fas fa-upload mr-1 " ></ i > Carica
</ button >
@ endif
</ div >
</ div >
2026-06-09 11:02:01 +02:00
@ include ( 'partials._tag-filter-bar' , [ 'filterTags' => $allTags ])
2026-05-28 09:34:28 +02:00
< div class = " card-body p-0 " >
{{ -- Toolbar azioni massive -- }}
< form id = " massForm " method = " POST " >
@ csrf
< input type = " hidden " name = " _method " id = " formMethod " value = " POST " >
@ if ( $canDeleteDocumenti )
< div class = " p-3 bg-light border-bottom d-flex align-items-center flex-wrap gap-2 " >
< span class = " font-weight-bold mr-2 " > Azioni :</ span >
< button type = " button " class = " btn btn-sm btn-danger " onclick = " massAction('delete') " >
< i class = " fas fa-trash mr-1 " ></ i > Elimina selezionati
</ button >
@ if ( $canWriteDocumenti )
< button type = " button " class = " btn btn-sm btn-warning " onclick = " $ ('#associateModal').modal('show') " >
< i class = " fas fa-link mr-1 " ></ i > Associa a ...
</ button >
< button type = " button " class = " btn btn-sm btn-info " onclick = " $ ('#updateModal').modal('show') " >
< i class = " fas fa-edit mr-1 " ></ i > Cambia tipo / contesto
</ button >
< button type = " button " class = " btn btn-sm btn-secondary " onclick = " $ ('#massMoveModal').modal('show') " >
< i class = " fas fa-folder-open mr-1 " ></ i > Sposta selezionati in ...
</ button >
2026-06-09 11:02:01 +02:00
< button type = " button " class = " btn btn-sm btn-info " onclick = " $ ('#massTagModal').modal('show') " >
< i class = " fas fa-tags mr-1 " ></ i > Tag
</ button >
2026-05-28 09:34:28 +02:00
< button type = " button " class = " btn btn-sm btn-success " onclick = " massDownload() " >
< i class = " fas fa-download mr-1 " ></ i > Scarica
</ button >
@ endif
< span class = " ml-auto text-muted small " id = " selectedCount " > 0 selezionati </ span >
</ div >
@ endif
< div id = " localContent " >
{{ -- View : Griglia -- }}
< div id = " viewGrid " >
< div class = " row p-3 " >
@ if ( $sottoCartelle -> count () > 0 )
@ foreach ( $sottoCartelle as $cartella )
< div class = " col-md-4 col-sm-6 mb-3 " >
< div class = " card card-outline card-warning h-100 " >
< div class = " card-body p-3 text-center " >
< div class = " py-3 " style = " font-size: 2.5rem; " >
< i class = " fas fa-folder text-warning " ></ i >
</ div >
< h6 class = " font-weight-bold text-truncate mb-0 " title = " { { $cartella->nome }} " >
< a href = " /documenti?folder_id= { { $cartella->id }} " > {{ $cartella -> nome }} </ a >
</ h6 >
</ div >
@ if ( $canWriteDocumenti || $canDeleteDocumenti )
< div class = " card-footer p-2 text-center " >
@ if ( $canWriteDocumenti )
< button type = " button " class = " btn btn-xs btn-warning " onclick = 'renameFolder({{ $cartella->id }}, {{ json_encode($cartella->nome) }})' title = " Rinomina " >
< i class = " fas fa-pen " ></ i >
</ button >
< button type = " button " class = " btn btn-xs btn-info " onclick = 'moveFolder({{ $cartella->id }}, {{ json_encode($cartella->nome) }})' title = " Sposta " >
< i class = " fas fa-folder-open " ></ i >
</ button >
@ endif
@ if ( $canDeleteDocumenti )
< button type = " button " class = " btn btn-xs btn-danger " onclick = 'deleteFolder({{ $cartella->id }}, {{ json_encode($cartella->nome) }})' title = " Elimina " >
< i class = " fas fa-times " ></ i >
</ button >
@ endif
</ div >
@ endif
</ div >
</ div >
@ endforeach
2026-05-26 08:14:29 +02:00
@ endif
2026-05-28 09:34:28 +02:00
@ if ( $documenti -> count () > 0 )
@ foreach ( $documenti as $documento )
@ php
$icon = match ( $documento -> mime_type ) {
'application/pdf' => 'fa-file-pdf text-danger' ,
'image/jpeg' , 'image/png' , 'image/gif' , 'image/webp' , 'image/svg+xml' => 'fa-file-image text-success' ,
'text/plain' , 'text/csv' , 'text/html' => 'fa-file-alt text-info' ,
'application/msword' , 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'fa-file-word text-primary' ,
'application/vnd.ms-excel' , 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'fa-file-excel text-success' ,
'application/zip' , 'application/x-rar-compressed' => 'fa-file-archive text-warning' ,
default => 'fa-file text-secondary' ,
};
@ endphp
< div class = " col-md-4 col-sm-6 mb-3 " >
< div class = " card card-outline card-secondary h-100 " >
< div class = " card-body p-3 " >
@ if ( $canDeleteDocumenti )
< div class = " float-right " >
< input type = " checkbox " name = " ids[] " value = " { { $documento->id }} " class = " doc-checkbox " onchange = " updateCount() " >
</ div >
@ endif
< h6 class = " card-title font-weight-bold text-truncate mb-2 " title = " { { $documento->nome_file }} " >
{{ $documento -> nome_file }}
</ h6 >
< div class = " text-center py-2 " style = " font-size: 2.5rem; " >
< i class = " fas { { $icon }} " ></ i >
</ div >
< div class = " text-center mb-2 " >
< span class = " badge badge- { { $documento->tipologia === 'avatar' ? 'primary' : ( $documento->tipologia === 'galleria' ? 'success' : 'secondary') }} " >
{{ ucfirst ( $documento -> tipologia ) }}
</ span >
@ if ( $documento -> dimensione )
< span class = " badge badge-light " >
{{ number_format ( $documento -> dimensione / 1024 , 1 ) }} KB
</ span >
@ endif
</ div >
2026-06-09 11:02:01 +02:00
< div class = " text-center mb-1 " style = " display:flex;flex-wrap:wrap;gap:2px;justify-content:center; " >
@ foreach ( $documento -> tags as $tag )
< a href = " { { route('documenti.index', ['tag[]' => $tag->slug ]) }} " class = " badge " style = " background-color: { { $tag->color ?? '#6c757d' }}; color: #fff; font-size:0.7rem; " > {{ $tag -> name }} </ a >
@ endforeach
</ div >
2026-05-28 09:34:28 +02:00
< div class = " small text-muted text-center " >
@ if ( $documento -> user )
{{ $documento -> user -> name }}
@ endif
& middot ; {{ $documento -> created_at -> format ( 'd/m/Y' ) }}
</ div >
</ div >
< div class = " card-footer p-2 text-center " >
@ if ( $documento -> file_path )
< button type = " button " class = " btn btn-xs btn-primary " onclick = " previewDoc( { { $documento->id }}, ' { { $documento->mime_type }}') " title = " Anteprima " >
< i class = " fas fa-eye " ></ i >
</ button >
< a href = " /documenti/ { { $documento->id }}/download " class = " btn btn-xs btn-info " title = " Scarica " >
< i class = " fas fa-download " ></ i >
</ a >
@ endif
@ if ( $canWriteDocumenti )
< a href = " /documenti/ { { $documento->id }}/edit " class = " btn btn-xs btn-warning " title = " Modifica " >
< i class = " fas fa-edit " ></ i >
</ a >
< button type = " button " class = " btn btn-xs btn-secondary " onclick = " showMoveModal( { { $documento->id }}, ' { { e( $documento->nome_file ) }}') " title = " Sposta in altra cartella " >
< i class = " fas fa-folder-open " ></ i >
</ button >
< button type = " button " class = " btn btn-xs btn-info " onclick = " showAssociateModal( { { $documento->id }}) " title = " Associa a... " >
< i class = " fas fa-link " ></ i >
</ button >
< button type = " button " class = " btn btn-xs btn-secondary " onclick = " showUpdateModal( { { $documento->id }}) " title = " Cambia tipo/contesto " >
< i class = " fas fa-tag " ></ i >
</ button >
@ endif
@ if ( $canDeleteDocumenti )
< button type = " button " class = " btn btn-xs btn-danger " onclick = " deleteDocumento( { { $documento->id }}, ' { { e( $documento->nome_file ) }}') " title = " Elimina " >
< i class = " fas fa-trash " ></ i >
</ button >
@ endif
</ div >
</ div >
</ div >
@ endforeach
2026-05-26 08:14:29 +02:00
@ endif
2026-05-28 09:34:28 +02:00
</ div >
@ if ( $sottoCartelle -> count () === 0 && $documenti -> count () === 0 )
< div class = " text-center text-muted py-5 " >
< i class = " fas { { $currentRepo ? ( $currentRepo->tipo === 'webdav' ? 'fa-server' : 'fa-google-drive') : 'fa-folder-open' }} fa-3x mb-3 " ></ i >
< p class = " mb-0 " > Nessun elemento presente </ p >
@ if ( $currentFolder )
< p class = " small " > Questa cartella è vuota </ p >
@ elseif ( $currentRepo )
< p class = " small " > Il repository remoto < strong > {{ $currentRepo -> nome }} </ strong > è vuoto </ p >
2026-05-26 08:14:29 +02:00
@ endif
2026-05-28 09:34:28 +02:00
</ div >
@ endif
</ div >
{{ -- View : Lista -- }}
< div id = " viewList " style = " display:none; " >
@ if ( $sottoCartelle -> count () > 0 || $documenti -> count () > 0 )
< table class = " table table-bordered table-hover table-striped mb-0 " >
< thead class = " thead-light " >
< tr >
@ if ( $canDeleteDocumenti )
< th style = " width: 40px; " >
< input type = " checkbox " id = " selectAll " onchange = " toggleAll(this) " >
</ th >
@ endif
< th > Nome </ th >
< th style = " width: 100px; " > Tipologia </ th >
< th style = " width: 90px; " > Dimensione </ th >
< th style = " width: 120px; " > Contesto </ th >
2026-06-09 11:02:01 +02:00
< th style = " width: 100px; " > Tag </ th >
2026-05-28 09:34:28 +02:00
< th style = " width: 110px; " > Data </ th >
< th style = " width: 130px; " > Azioni </ th >
</ tr >
</ thead >
< tbody >
@ foreach ( $sottoCartelle as $cartella )
< tr class = " table-warning " >
@ if ( $canDeleteDocumenti )
< td ></ td >
@ endif
< td >
< i class = " fas fa-folder text-warning mr-2 " ></ i >
< a href = " /documenti?folder_id= { { $cartella->id }} " class = " font-weight-bold " > {{ $cartella -> nome }} </ a >
< div class = " small text-muted " > Cartella </ div >
</ td >
< td >< span class = " badge badge-warning " > Cartella </ span ></ td >
< td class = " small text-muted " >-</ td >
< td class = " small text-muted " >-</ td >
< td class = " small text-muted " >-</ td >
2026-06-09 11:02:01 +02:00
< td class = " small text-muted " >-</ td >
2026-05-28 09:34:28 +02:00
< td >
< a href = " /documenti?folder_id= { { $cartella->id }} " class = " btn btn-xs btn-warning " title = " Apri cartella " >
< i class = " fas fa-folder-open " ></ i >
</ a >
@ if ( $canWriteDocumenti )
< button type = " button " class = " btn btn-xs btn-warning " onclick = 'renameFolder({{ $cartella->id }}, {{ json_encode($cartella->nome) }})' title = " Rinomina " >
< i class = " fas fa-pen " ></ i >
</ button >
< button type = " button " class = " btn btn-xs btn-info " onclick = 'moveFolder({{ $cartella->id }}, {{ json_encode($cartella->nome) }})' title = " Sposta " >
< i class = " fas fa-folder-open " ></ i >
</ button >
@ endif
@ if ( $canDeleteDocumenti )
< button type = " button " class = " btn btn-xs btn-danger " onclick = 'deleteFolder({{ $cartella->id }}, {{ json_encode($cartella->nome) }})' title = " Elimina " >
< i class = " fas fa-times " ></ i >
</ button >
@ endif
</ td >
</ tr >
@ endforeach
@ foreach ( $documenti as $documento )
@ php
$icon = match ( $documento -> mime_type ) {
'application/pdf' => 'fa-file-pdf text-danger' ,
'image/jpeg' , 'image/png' , 'image/gif' , 'image/webp' , 'image/svg+xml' => 'fa-file-image text-success' ,
'text/plain' , 'text/csv' , 'text/html' => 'fa-file-alt text-info' ,
'application/msword' , 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'fa-file-word text-primary' ,
'application/vnd.ms-excel' , 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'fa-file-excel text-success' ,
'application/zip' , 'application/x-rar-compressed' => 'fa-file-archive text-warning' ,
default => 'fa-file text-secondary' ,
};
@ endphp
< tr >
@ if ( $canDeleteDocumenti )
< td >
< input type = " checkbox " name = " ids[] " value = " { { $documento->id }} " class = " doc-checkbox " onchange = " updateCount() " >
</ td >
@ endif
< td >
< i class = " fas { { $icon }} mr-2 " ></ i >
< span class = " font-weight-bold " > {{ $documento -> nome_file }} </ span >
< div class = " small text-muted " >
@ if ( $documento -> user ){{ $documento -> user -> name }} @ endif
@ if ( $documento -> cartella ) in < a href = " /documenti?folder_id= { { $documento->cartella_id }} " class = " text-muted " > {{ $documento -> cartella -> nome }} </ a >@ endif
</ div >
</ td >
< td >
< span class = " badge badge- { { $documento->tipologia === 'avatar' ? 'primary' : ( $documento->tipologia === 'galleria' ? 'success' : 'secondary') }} " >
{{ ucfirst ( $documento -> tipologia ) }}
</ span >
</ td >
< td class = " small " >
@ if ( $documento -> dimensione )
{{ number_format ( $documento -> dimensione / 1024 , 1 ) }} KB
@ else
< span class = " text-muted " >-</ span >
@ endif
</ td >
< td >
@ switch ( $documento -> visibilita )
@ case ( 'individuo' )
@ if ( $documento -> target instanceof \App\Models\Individuo )
< a href = " /individui/ { { $documento->target ->id }} " class = " small " >
< i class = " fas fa-user text-info mr-1 " ></ i > {{ $documento -> target -> cognome }} {{ $documento -> target -> nome }}
</ a >
@ else
< span class = " badge badge-info " > Individuo </ span >
@ endif
@ break
@ case ( 'gruppo' )
@ if ( $documento -> target instanceof \App\Models\Gruppo )
< a href = " /gruppi/ { { $documento->target ->id }} " class = " small " >
< i class = " fas fa-folder text-warning mr-1 " ></ i > {{ $documento -> target -> nome }}
</ a >
@ else
< span class = " badge badge-success " > Gruppo </ span >
@ endif
@ break
@ case ( 'evento' )
@ if ( $documento -> target instanceof \App\Models\Evento )
< a href = " /eventi/ { { $documento->target ->id }} " class = " small " >
< i class = " fas fa-calendar text-primary mr-1 " ></ i > {{ $documento -> target -> nome_evento }}
</ a >
@ else
< span class = " badge badge-warning " > Evento </ span >
@ endif
@ break
@ case ( 'mailing' )
@ if ( $documento -> target instanceof \App\Models\MailingList )
< a href = " /mailing-liste/ { { $documento->target ->id }} " class = " small " >
< i class = " fas fa-envelope text-secondary mr-1 " ></ i > {{ $documento -> target -> nome }}
</ a >
@ else
< span class = " badge badge-secondary " > Mailing </ span >
@ endif
@ break
@ case ( 'pubblico' )
< span class = " badge badge-light " > Pubblico </ span >
@ break
@ case ( 'associazione' )
< span class = " badge badge-dark " > Associazione </ span >
@ break
@ case ( 'federazione' )
< span class = " badge badge-purple " > Federazione </ span >
@ break
@ default
< span class = " text-muted small " >-</ span >
@ endswitch
</ td >
2026-06-09 11:02:01 +02:00
< td >
@ foreach ( $documento -> tags as $tag )
< a href = " { { route('documenti.index', ['tag[]' => $tag->slug ]) }} " class = " badge " style = " background-color: { { $tag->color ?? '#6c757d' }}; color: #fff; " > {{ $tag -> name }} </ a >
@ endforeach
</ td >
2026-05-28 09:34:28 +02:00
< td class = " small " > {{ $documento -> created_at -> format ( 'd/m/Y' ) }} </ td >
< td >
@ if ( $documento -> file_path )
< button type = " button " class = " btn btn-xs btn-primary " onclick = " previewDoc( { { $documento->id }}, ' { { $documento->mime_type }}') " title = " Anteprima " >
< i class = " fas fa-eye " ></ i >
</ button >
< a href = " /documenti/ { { $documento->id }}/download " class = " btn btn-xs btn-info " title = " Scarica " >
< i class = " fas fa-download " ></ i >
</ a >
@ endif
@ if ( $canWriteDocumenti )
< a href = " /documenti/ { { $documento->id }}/edit " class = " btn btn-xs btn-warning " title = " Modifica " >
< i class = " fas fa-edit " ></ i >
</ a >
< button type = " button " class = " btn btn-xs btn-secondary " onclick = " showMoveModal( { { $documento->id }}, ' { { e( $documento->nome_file ) }}') " title = " Sposta in altra cartella " >
< i class = " fas fa-folder-open " ></ i >
</ button >
< button type = " button " class = " btn btn-xs btn-info " onclick = " showAssociateModal( { { $documento->id }}) " title = " Associa a... " >
< i class = " fas fa-link " ></ i >
</ button >
< button type = " button " class = " btn btn-xs btn-secondary " onclick = " showUpdateModal( { { $documento->id }}) " title = " Cambia tipo/contesto " >
< i class = " fas fa-tag " ></ i >
</ button >
@ endif
@ if ( $canDeleteDocumenti )
< button type = " button " class = " btn btn-xs btn-danger " onclick = " deleteDocumento( { { $documento->id }}, ' { { e( $documento->nome_file ) }}') " title = " Elimina " >
< i class = " fas fa-trash " ></ i >
</ button >
@ endif
</ td >
</ tr >
@ endforeach
</ tbody >
</ table >
@ else
< div class = " text-center text-muted py-5 " >
< i class = " fas { { $currentRepo ? ( $currentRepo->tipo === 'webdav' ? 'fa-server' : 'fa-google-drive') : 'fa-folder-open' }} fa-3x mb-3 " ></ i >
< p class = " mb-0 " > Nessun elemento presente </ p >
</ div >
@ endif
</ div >
{{ -- Paginazione -- }}
@ if ( $documenti -> hasPages ())
< div class = " p-3 border-top " >
{{ $documenti -> links () }}
</ div >
@ endif
</ div > {{ -- / localContent -- }}
< div id = " remoteContent " style = " display:none; " >
< div class = " p-3 border-bottom bg-light d-flex align-items-center " >
< i class = " fas fa-cloud mr-2 text-info " ></ i >
< span id = " remoteBreadcrumb " class = " small font-weight-bold " ></ span >
< button type = " button " class = " btn btn-sm btn-outline-secondary ml-auto " onclick = " showLocalContent() " >
< i class = " fas fa-folder mr-1 " ></ i > Documenti locali
</ button >
</ div >
< div id = " remoteGrid " class = " row p-3 " ></ div >
< div id = " remoteList " style = " display:none; " ></ div >
< div id = " remoteEmpty " class = " text-center text-muted py-5 " style = " display:none; " >
< i class = " fas fa-folder-open fa-3x mb-3 " ></ i >
< p class = " mb-0 " > Repository vuoto </ p >
</ div >
< div id = " remoteLoading " class = " text-center py-5 " style = " display:none; " >
< i class = " fas fa-spinner fa-spin fa-3x text-muted " ></ i >
< p class = " mt-2 text-muted " > Caricamento contenuti remoti ...</ p >
</ div >
</ div >
</ div >
</ div >
</ div >
</ div >
</ form > {{ -- / massForm -- }}
{{ -- MODAL : Nuova Cartella -- }}
< div class = " modal fade " id = " newFolderModal " tabindex = " -1 " role = " dialog " >
2026-06-01 22:55:50 +02:00
< div class = " modal-dialog modal-dialog-scrollable " role = " document " >
2026-05-28 09:34:28 +02:00
< div class = " modal-content " >
< div class = " modal-header " >
< h5 class = " modal-title " >< i class = " fas fa-folder-plus mr-2 " ></ i > Nuova Cartella </ h5 >
< button type = " button " class = " close " onclick = " $ ('#newFolderModal').modal('hide') " >
< span >& times ; </ span >
</ button >
</ div >
< form id = " newFolderForm " >
@ csrf
< div class = " modal-body " >
< div class = " form-group " >
< label > Nome cartella *</ label >
< input type = " text " name = " nome " class = " form-control " placeholder = " Nome della cartella " required >
</ div >
@ if ( $currentFolder )
< input type = " hidden " name = " parent_id " value = " { { $currentFolder->id }} " >
< p class = " text-muted small mb-0 " > Sarà creata all 'interno di: <strong>{{ $currentFolder->nome }}</strong></p>
@else
<p class="text-muted small mb-0">Sarà creata nella radice</p>
@endif
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="$(' #newFolderModal').modal('hide')">Annulla</button>
< button type = " submit " class = " btn btn-primary " >
< i class = " fas fa-save mr-1 " ></ i > Crea
</ button >
</ div >
</ form >
2026-05-26 08:14:29 +02:00
</ div >
</ div >
</ div >
2026-05-28 09:34:28 +02:00
{{ -- MODAL : Upload -- }}
< div class = " modal fade " id = " uploadModal " tabindex = " -1 " role = " dialog " >
2026-06-01 22:55:50 +02:00
< div class = " modal-dialog modal-dialog-scrollable " role = " document " >
2026-05-28 09:34:28 +02:00
< div class = " modal-content " >
< div class = " modal-header " >
< h5 class = " modal-title " >< i class = " fas fa-upload mr-2 " ></ i > Carica Documento </ h5 >
< button type = " button " class = " close " onclick = " $ ('#uploadModal').modal('hide') " >
< span >& times ; </ span >
</ button >
</ div >
2026-06-01 22:55:50 +02:00
< div class = " modal-body " >
< form action = " /documenti " method = " POST " enctype = " multipart/form-data " id = " uploadForm " >
@ csrf
2026-05-28 09:34:28 +02:00
< div class = " form-group " >
< label > Nome *</ label >
< input type = " text " name = " nome_file " class = " form-control " placeholder = " Nome del documento " required >
</ div >
< div class = " form-group " >
< label > Tipologia *</ label >
< select name = " tipologia " class = " form-control " required >
@ foreach ( $tipologie as $tip )
< option value = " { { $tip->nome }} " > {{ $tip -> descrizione ? ? ucfirst ( $tip -> nome ) }} </ option >
@ endforeach
</ select >
</ div >
2026-06-01 22:55:50 +02:00
< div class = " form-group " >
< label > Visibilità *</ label >
< select name = " visibilita " id = " uploadVisibilita " class = " form-control " onchange = " loadUploadTargets() " >
< option value = " pubblico " > Pubblico </ option >
< option value = " individuo " > Individuo </ option >
< option value = " gruppo " > Gruppo </ option >
</ select >
</ div >
2026-05-28 09:34:28 +02:00
< div class = " form-group " id = " uploadTargetGroup " style = " display:none; " >
< label > Riferimento </ label >
< select name = " visibilita_target_id " class = " form-control " id = " uploadTargetId " >
< option value = " " > Seleziona ...</ option >
</ select >
< input type = " hidden " name = " visibilita_target_type " id = " uploadTargetType " >
</ div >
@ if ( $repositories -> count () > 0 )
< div class = " form-group " >
< label > Repository di destinazione </ label >
< select name = " repository_id " id = " uploadRepository " class = " form-control " onchange = " toggleUploadRepo() " >
< option value = " " > Locale ( predefinito ) </ option >
@ foreach ( $repositories as $repo )
< option value = " { { $repo->id }} " {{ $currentRepo && $currentRepo -> id === $repo -> id ? 'selected' : '' }} >
< i class = " fas { { $repo->tipo === 'webdav' ? 'fa-server' : 'fa-google-drive' }} " ></ i >
{{ $repo -> nome }} ({{ \App\Models\StorageRepository :: etichettaTipo ( $repo -> tipo ) }})
</ option >
@ endforeach
</ select >
< small class = " text-muted " > Seleziona un repository remoto o lascia " Locale " per il salvataggio predefinito </ small >
</ div >
@ endif
< div class = " form-group " >
< label > File *</ label >
< input type = " file " name = " file " class = " form-control " required >
< small class = " text-muted " > Max 10 MB </ small >
</ div >
2026-06-01 22:55:50 +02:00
< div class = " form-group " id = " uploadFolderGroup " >
< label > Cartella </ label >
< select name = " cartella_id " id = " uploadCartellaId " class = " form-control " >
< option value = " " > — Nessuna cartella — </ option >
@ foreach ( $cartelle as $cartella )
< option value = " { { $cartella->id }} " {{ $currentFolder && $currentFolder -> id === $cartella -> id ? 'selected' : '' }} > {{ $cartella -> nome }} </ option >
@ if ( $cartella -> children -> count () > 0 )
@ foreach ( $cartella -> children as $child )
< option value = " { { $child->id }} " {{ $currentFolder && $currentFolder -> id === $child -> id ? 'selected' : '' }} >& nbsp ; & nbsp ; & nbsp ;{{ $child -> nome }} </ option >
@ endforeach
@ endif
@ endforeach
</ select >
< button type = " button " class = " btn btn-sm btn-outline-success mt-2 " id = " uploadNewFolderBtn " >
< i class = " fas fa-plus mr-1 " ></ i > Nuova cartella
</ button >
</ div >
2026-05-28 09:34:28 +02:00
@ if ( $currentRepo )
< input type = " hidden " name = " repository_id " value = " { { $currentRepo->id }} " >
< p class = " text-muted small mb-0 " > Caricamento su repository remoto : < strong > {{ $currentRepo -> nome }} </ strong ></ p >
@ endif
2026-06-01 22:55:50 +02:00
</ form >
</ div >
< div class = " modal-footer " >
< button type = " button " class = " btn btn-secondary " onclick = " $ ('#uploadModal').modal('hide') " > Annulla </ button >
< button type = " submit " class = " btn btn-primary " form = " uploadForm " >
< i class = " fas fa-upload mr-1 " ></ i > Carica
</ button >
</ div >
2026-05-28 09:34:28 +02:00
</ div >
</ div >
</ div >
{{ -- MODAL : Associa -- }}
2026-05-26 08:14:29 +02:00
< div class = " modal fade " id = " associateModal " tabindex = " -1 " role = " dialog " >
2026-06-01 22:55:50 +02:00
< div class = " modal-dialog modal-dialog-scrollable " role = " document " >
2026-05-26 08:14:29 +02:00
< div class = " modal-content " >
< div class = " modal-header " >
< h5 class = " modal-title " >< i class = " fas fa-link mr-2 " ></ i > Associa a </ h5 >
< button type = " button " class = " close " onclick = " $ ('#associateModal').modal('hide') " >
< span >& times ; </ span >
</ button >
</ div >
< div class = " modal-body " >
< form id = " associateForm " method = " POST " action = " /documenti/mass-associate " >
@ csrf
< div class = " form-group " >
< label > Tipo collegamento </ label >
< select name = " target_type " class = " form-control " required onchange = " loadTargets(this.value) " >
< option value = " " > Seleziona ...</ option >
< option value = " individuo " > Individuo </ option >
< option value = " gruppo " > Gruppo </ option >
< option value = " evento " > Evento </ option >
< option value = " mailing " > Mailing List </ option >
</ select >
</ div >
< div class = " form-group " >
< label > Riferimento </ label >
< select name = " target_id " class = " form-control " required id = " targetSelect " >
< option value = " " > Seleziona prima il tipo ...</ option >
</ select >
</ div >
< input type = " hidden " name = " ids " id = " associateIds " >
</ form >
</ div >
< div class = " modal-footer " >
< button type = " button " class = " btn btn-secondary " onclick = " $ ('#associateModal').modal('hide') " > Annulla </ button >
< button type = " button " class = " btn btn-primary " onclick = " submitAssociate() " > Associa </ button >
</ div >
</ div >
</ div >
</ div >
2026-05-28 09:34:28 +02:00
{{ -- MODAL : Cambia tipo / contesto -- }}
2026-05-26 08:14:29 +02:00
< div class = " modal fade " id = " updateModal " tabindex = " -1 " role = " dialog " >
2026-06-01 22:55:50 +02:00
< div class = " modal-dialog modal-dialog-scrollable " role = " document " >
2026-05-26 08:14:29 +02:00
< div class = " modal-content " >
< div class = " modal-header " >
< h5 class = " modal-title " >< i class = " fas fa-edit mr-2 " ></ i > Cambia tipo / contesto </ h5 >
< button type = " button " class = " close " onclick = " $ ('#updateModal').modal('hide') " >
< span >& times ; </ span >
</ button >
</ div >
< div class = " modal-body " >
< form id = " updateForm " method = " POST " action = " /documenti/mass-update " >
@ csrf
< div class = " form-group " >
< label > Nuova tipologia </ label >
< select name = " tipologia " class = " form-control " >
< option value = " " > Non modificare </ option >
@ foreach ( $tipologie as $tip )
< option value = " { { $tip->nome }} " > {{ $tip -> descrizione ? ? ucfirst ( $tip -> nome ) }} </ option >
@ endforeach
</ select >
</ div >
< div class = " form-group " >
< label > Nuovo contesto </ label >
2026-05-28 09:34:28 +02:00
< select name = " visibilita " id = " updateVisibilita " class = " form-control " onchange = " toggleUpdateTarget(this.value) " >
2026-05-26 08:14:29 +02:00
< option value = " " > Non modificare </ option >
< option value = " pubblico " > Pubblico </ option >
< option value = " individuo " > Individuo </ option >
< option value = " gruppo " > Gruppo </ option >
< option value = " evento " > Evento </ option >
< option value = " associazione " > Associazione </ option >
< option value = " federazione " > Federazione </ option >
< option value = " mailing " > Mailing List </ option >
</ select >
</ div >
2026-05-28 09:34:28 +02:00
< div class = " form-group " id = " updateTargetGroup " style = " display:none; " >
< label > Elemento collegato </ label >
< select name = " visibilita_target_id " class = " form-control " id = " updateTargetSelect " >
< option value = " " > Seleziona ...</ option >
</ select >
< input type = " hidden " name = " visibilita_target_type " id = " updateTargetType " >
</ div >
2026-05-26 08:14:29 +02:00
< input type = " hidden " name = " ids " id = " updateIds " >
</ form >
</ div >
< div class = " modal-footer " >
< button type = " button " class = " btn btn-secondary " onclick = " $ ('#updateModal').modal('hide') " > Annulla </ button >
< button type = " button " class = " btn btn-primary " onclick = " submitUpdate() " > Aggiorna </ button >
</ div >
</ div >
</ div >
</ div >
2026-05-28 09:34:28 +02:00
{{ -- MODAL : Preview -- }}
2026-05-26 08:14:29 +02:00
< div class = " modal fade " id = " previewModal " tabindex = " -1 " role = " dialog " >
< div class = " modal-dialog modal-lg " style = " max-width: 90vw; " >
< div class = " modal-content " >
< div class = " modal-header " >
< h5 class = " modal-title " >< i class = " fas fa-eye mr-2 " ></ i > Anteprima </ h5 >
< button type = " button " class = " close " data - dismiss = " modal " >
< span >& times ; </ span >
</ button >
</ div >
< div class = " modal-body p-0 " style = " min-height: 400px; " >
< iframe id = " previewFrame " src = " " style = " width: 100%; height: 70vh; border: none; " ></ iframe >
</ div >
< div class = " modal-footer " >
< a id = " previewDownloadBtn " href = " # " class = " btn btn-primary " download >
< i class = " fas fa-download mr-1 " ></ i > Scarica
</ a >
< button type = " button " class = " btn btn-secondary " data - dismiss = " modal " > Chiudi </ button >
</ div >
</ div >
</ div >
</ div >
2026-05-28 09:34:28 +02:00
{{ -- MODAL : Conferma eliminazione singola -- }}
2026-05-26 08:14:29 +02:00
< div class = " modal fade " id = " deleteConfirmModal " tabindex = " -1 " role = " dialog " >
2026-06-01 22:55:50 +02:00
< div class = " modal-dialog modal-dialog-scrollable " role = " document " >
2026-05-26 08:14:29 +02:00
< div class = " modal-content " >
< div class = " modal-header bg-danger text-white " >
< h5 class = " modal-title " >< i class = " fas fa-exclamation-triangle mr-2 " ></ i > Conferma Eliminazione </ h5 >
< button type = " button " class = " close text-white " onclick = " $ ('#deleteConfirmModal').modal('hide') " >
< span >& times ; </ span >
</ button >
</ div >
< div class = " modal-body " >
< p id = " deleteConfirmMessage " ></ p >
< div id = " deleteConfirmLinks " class = " mt-3 " ></ div >
</ div >
< div class = " modal-footer " >
< button type = " button " class = " btn btn-secondary " onclick = " $ ('#deleteConfirmModal').modal('hide') " > Annulla </ button >
< button type = " button " class = " btn btn-danger " id = " confirmDeleteBtn " >
< i class = " fas fa-trash mr-1 " ></ i > Elimina comunque
</ button >
</ div >
</ div >
</ div >
</ div >
2026-05-28 09:34:28 +02:00
{{ -- MODAL : Conferma eliminazione multipla -- }}
2026-05-26 08:14:29 +02:00
< div class = " modal fade " id = " massDeleteConfirmModal " tabindex = " -1 " role = " dialog " >
2026-06-01 22:55:50 +02:00
< div class = " modal-dialog modal-dialog-scrollable " role = " document " >
2026-05-26 08:14:29 +02:00
< div class = " modal-content " >
< div class = " modal-header bg-danger text-white " >
< h5 class = " modal-title " >< i class = " fas fa-exclamation-triangle mr-2 " ></ i > Conferma Eliminazione Multipla </ h5 >
< button type = " button " class = " close text-white " onclick = " $ ('#massDeleteConfirmModal').modal('hide') " >
< span >& times ; </ span >
</ button >
</ div >
< div class = " modal-body " >
< p > Stai per eliminare < strong id = " massDeleteCount " > 0 </ strong > documenti .</ p >
< div id = " massDeleteLinks " class = " mt-3 " ></ div >
</ div >
< div class = " modal-footer " >
< button type = " button " class = " btn btn-secondary " onclick = " $ ('#massDeleteConfirmModal').modal('hide') " > Annulla </ button >
< button type = " button " class = " btn btn-danger " id = " confirmMassDeleteBtn " >
< i class = " fas fa-trash mr-1 " ></ i > Elimina comunque
</ button >
</ div >
</ div >
</ div >
</ div >
2026-05-28 09:34:28 +02:00
{{ -- MODAL : Sposta massivo -- }}
< div class = " modal fade " id = " massMoveModal " tabindex = " -1 " role = " dialog " >
2026-06-01 22:55:50 +02:00
< div class = " modal-dialog modal-dialog-scrollable " role = " document " >
2026-05-28 09:34:28 +02:00
< div class = " modal-content " >
< div class = " modal-header " >
< h5 class = " modal-title " >< i class = " fas fa-folder-open mr-2 " ></ i > Sposta documenti selezionati </ h5 >
< button type = " button " class = " close " onclick = " $ ('#massMoveModal').modal('hide') " >
< span >& times ; </ span >
</ button >
</ div >
< form id = " massMoveForm " >
@ csrf
< input type = " hidden " name = " ids " id = " massMoveIds " >
< div class = " modal-body " >
< p > Spostare < strong id = " massMoveCount " > 0 </ strong > documenti nella cartella :</ p >
< div class = " form-group " >
< label > Cartella di destinazione </ label >
< select name = " cartella_id " class = " form-control " >
< option value = " " > — Radice ( nessuna cartella ) — </ option >
@ foreach ( \App\Models\DocumentoCartella :: with ( 'children' ) -> whereNull ( 'parent_id' ) -> orderBy ( 'nome' ) -> get () as $cartella )
< option value = " { { $cartella->id }} " > {{ $cartella -> nome }} </ option >
@ if ( $cartella -> children -> count () > 0 )
@ foreach ( $cartella -> children as $child )
< option value = " { { $child->id }} " >& nbsp ; & nbsp ; & nbsp ;{{ $child -> nome }} </ option >
@ endforeach
@ endif
@ endforeach
</ select >
2026-06-01 22:55:50 +02:00
< button type = " button " class = " btn btn-sm btn-outline-success mt-2 " id = " massMoveNewFolderBtn " >
< i class = " fas fa-plus mr-1 " ></ i > Nuova cartella
</ button >
2026-05-28 09:34:28 +02:00
</ div >
</ div >
< div class = " modal-footer " >
< button type = " button " class = " btn btn-secondary " onclick = " $ ('#massMoveModal').modal('hide') " > Annulla </ button >
< button type = " submit " class = " btn btn-primary " >
< i class = " fas fa-folder-open mr-1 " ></ i > Sposta
</ button >
</ div >
</ form >
</ div >
</ div >
</ div >
2026-06-09 11:02:01 +02:00
{{ -- MODAL : Gestione Tag massiva -- }}
< div class = " modal fade " id = " massTagModal " tabindex = " -1 " role = " dialog " >
< div class = " modal-dialog modal-dialog-scrollable " role = " document " >
< div class = " modal-content " >
< div class = " modal-header " >
< h5 class = " modal-title " >< i class = " fas fa-tags mr-2 " ></ i > Gestione Tag </ h5 >
< button type = " button " class = " close " onclick = " $ ('#massTagModal').modal('hide') " >
< span >& times ; </ span >
</ button >
</ div >
< form id = " massTagForm " method = " POST " action = " /documenti/mass-tag " >
@ csrf
< div class = " modal-body " >
< p class = " mb-2 " > Operazione su < strong id = " massTagCount " > 0 </ strong > documenti selezionati .</ p >
< div class = " form-group mb-3 " >
< label class = " font-weight-bold " > Azione </ label >
< div class = " d-flex " style = " gap:1.5rem; " >
< div class = " form-check " >
< input class = " form-check-input " type = " radio " name = " mode " id = " mtModeAssign " value = " assign " checked >
< label class = " form-check-label " for = " mtModeAssign " > Assegna tag </ label >
</ div >
< div class = " form-check " >
< input class = " form-check-input " type = " radio " name = " mode " id = " mtModeRemove " value = " remove " >
< label class = " form-check-label " for = " mtModeRemove " > Rimuovi tag </ label >
</ div >
</ div >
</ div >
@ include ( 'partials._tag-selector' , [ 'label' => 'Seleziona tag' ])
< input type = " hidden " name = " ids " id = " massTagIds " value = " " >
</ div >
< div class = " modal-footer " >
< button type = " button " class = " btn btn-secondary " onclick = " $ ('#massTagModal').modal('hide') " > Annulla </ button >
< button type = " submit " class = " btn btn-primary " >
< i class = " fas fa-tags mr-1 " ></ i > Applica
</ button >
</ div >
</ form >
</ div >
</ div >
</ div >
2026-05-28 09:34:28 +02:00
{{ -- MODAL : Sposta documento -- }}
< div class = " modal fade " id = " moveModal " tabindex = " -1 " role = " dialog " >
2026-06-01 22:55:50 +02:00
< div class = " modal-dialog modal-dialog-scrollable " role = " document " >
2026-05-28 09:34:28 +02:00
< div class = " modal-content " >
< div class = " modal-header " >
< h5 class = " modal-title " >< i class = " fas fa-folder-open mr-2 " ></ i > Sposta documento </ h5 >
< button type = " button " class = " close " onclick = " $ ('#moveModal').modal('hide') " >
< span >& times ; </ span >
</ button >
</ div >
< form id = " moveForm " >
@ csrf
< input type = " hidden " name = " documento_id " id = " moveDocumentoId " >
< div class = " modal-body " >
< p > Spostare il documento < strong id = " moveDocumentoNome " ></ strong > in :</ p >
< div class = " form-group " >
< label > Cartella di destinazione </ label >
< select name = " cartella_id " id = " moveCartellaId " class = " form-control " >
< option value = " " > — Radice ( nessuna cartella ) — </ option >
@ foreach ( \App\Models\DocumentoCartella :: with ( 'children' ) -> whereNull ( 'parent_id' ) -> orderBy ( 'nome' ) -> get () as $cartella )
< option value = " { { $cartella->id }} " > {{ $cartella -> nome }} </ option >
@ if ( $cartella -> children -> count () > 0 )
@ foreach ( $cartella -> children as $child )
< option value = " { { $child->id }} " >& nbsp ; & nbsp ; & nbsp ;{{ $child -> nome }} </ option >
@ endforeach
@ endif
@ endforeach
</ select >
</ div >
</ div >
< div class = " modal-footer " >
< button type = " button " class = " btn btn-secondary " onclick = " $ ('#moveModal').modal('hide') " > Annulla </ button >
< button type = " submit " class = " btn btn-primary " >
< i class = " fas fa-folder-open mr-1 " ></ i > Sposta
</ button >
</ div >
</ form >
</ div >
</ div >
</ div >
2026-05-26 08:14:29 +02:00
@ endsection
@ section ( 'scripts' )
< script >
const individui = @ json ( $individui -> map ( fn ( $i ) => [ 'id' => $i -> id , 'label' => $i -> cognome . ' ' . $i -> nome ]));
const gruppi = @ json ( $gruppi -> map ( fn ( $g ) => [ 'id' => $g -> id , 'label' => $g -> nome ]));
const eventi = @ json ( $eventi -> map ( fn ( $e ) => [ 'id' => $e -> id , 'label' => $e -> nome_evento ]));
2026-05-28 09:34:28 +02:00
const mailingLists = @ json ( $mailingLists ? $mailingLists -> map ( fn ( $m ) => [ 'id' => $m -> id , 'label' => $m -> nome ]) : []);
2026-05-29 06:54:07 +02:00
const cartelle = @ json ( $cartelleMoveOptions );
2026-05-26 08:14:29 +02:00
2026-06-01 22:55:50 +02:00
$ ( '#uploadModal' ) . on ( 'shown.bs.modal' , function () {
loadUploadTargets ();
toggleUploadRepo ();
});
$ ( '#newFolderModal' ) . on ( 'show.bs.modal' , function () {
$ ( this ) . css ( 'z-index' , 1060 );
$ ( '.modal-backdrop:last' ) . css ( 'z-index' , 1050 );
});
$ ( '#newFolderModal' ) . on ( 'hidden.bs.modal' , function () {
window . _uploadNewFolder = false ;
$ ( this ) . css ( 'z-index' , '' );
});
2026-05-26 08:14:29 +02:00
function toggleAll ( source ) {
document . querySelectorAll ( '.doc-checkbox' ) . forEach ( cb => cb . checked = source . checked );
updateCount ();
}
function updateCount () {
const count = document . querySelectorAll ( '.doc-checkbox:checked' ) . length ;
document . getElementById ( 'selectedCount' ) . textContent = count + ' selezionati' ;
}
function getSelectedIds () {
return Array . from ( document . querySelectorAll ( '.doc-checkbox:checked' )) . map ( cb => cb . value );
}
let pendingMassDeleteIds = [];
function massAction ( action ) {
const ids = getSelectedIds ();
if ( ids . length === 0 ) {
alert ( 'Seleziona almeno un documento' );
return ;
}
if ( action === 'delete' ) {
fetch ( '/documenti/check-links' , {
method : 'POST' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
'Content-Type' : 'application/json' ,
'Accept' : 'application/json' ,
},
body : JSON . stringify ({ ids : ids })
})
. then ( response => response . json ())
. then ( data => {
pendingMassDeleteIds = ids ;
document . getElementById ( 'massDeleteCount' ) . textContent = ids . length ;
if ( data . linked_count > 0 ) {
let linksHtml = '<div class="alert alert-warning mb-0"><p><strong>Attenzione:</strong> ' + data . linked_count + ' documento(i) sono collegati a elementi nel database:</p><ul class="mb-0">' ;
data . links . forEach ( function ( link ) {
if ( link . url ) {
linksHtml += '<li><a href="' + link . url + '" target="_blank">' + link . name + '</a></li>' ;
} else {
linksHtml += '<li>' + link . name + '</li>' ;
}
});
linksHtml += '</ul></div>' ;
document . getElementById ( 'massDeleteLinks' ) . innerHTML = linksHtml ;
} else {
document . getElementById ( 'massDeleteLinks' ) . innerHTML = '<div class="alert alert-info mb-0">Nessun documento risulta collegato.</div>' ;
}
document . getElementById ( 'confirmMassDeleteBtn' ) . onclick = function () { performMassDelete (); };
$ ( '#massDeleteConfirmModal' ) . modal ( 'show' );
})
. catch ( error => {
alert ( 'Errore controllo collegamenti: ' + error . message );
2026-05-28 09:34:28 +02:00
});
}
}
function massDownload () {
const ids = getSelectedIds ();
if ( ids . length === 0 ) {
alert ( 'Seleziona almeno un documento' );
return ;
2026-05-26 08:14:29 +02:00
}
2026-05-28 09:34:28 +02:00
const form = document . createElement ( 'form' );
form . method = 'POST' ;
form . action = '/documenti/mass-download' ;
const csrf = document . createElement ( 'input' );
csrf . type = 'hidden' ;
csrf . name = '_token' ;
csrf . value = '{{ csrf_token() }}' ;
form . appendChild ( csrf );
ids . forEach ( id => {
const input = document . createElement ( 'input' );
input . type = 'hidden' ;
input . name = 'ids[]' ;
input . value = id ;
form . appendChild ( input );
});
document . body . appendChild ( form );
form . submit ();
document . body . removeChild ( form );
2026-05-26 08:14:29 +02:00
}
function performMassDelete () {
const ids = pendingMassDeleteIds ;
$ ( '#massDeleteConfirmModal' ) . modal ( 'hide' );
fetch ( '/documenti/mass-destroy' , {
method : 'POST' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
'Content-Type' : 'application/x-www-form-urlencoded' ,
},
body : 'ids=' + ids . join ( ',' )
})
. then ( response => {
if ( response . redirected ) {
window . location . href = response . url ;
} else {
window . location . reload ();
}
})
. catch ( error => {
alert ( 'Errore: ' + error . message );
});
}
function deleteDocumento ( id , nomeFile ) {
fetch ( '/documenti/' + id + '/check-links' , {
method : 'GET' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
'Accept' : 'application/json' ,
}
})
. then ( response => response . json ())
. then ( data => {
if ( data . has_links ) {
let linksHtml = '<div class="alert alert-warning mb-0"><p class="mb-2"><strong>Attenzione:</strong> Questo documento è collegato a:</p><ul class="mb-0">' ;
data . links . forEach ( function ( link ) {
if ( link . url ) {
linksHtml += '<li><a href="' + link . url + '" target="_blank">' + link . name + '</a></li>' ;
} else {
linksHtml += '<li>' + link . name + '</li>' ;
}
});
linksHtml += '</ul></div>' ;
document . getElementById ( 'deleteConfirmLinks' ) . innerHTML = linksHtml ;
document . getElementById ( 'deleteConfirmMessage' ) . textContent = 'Eliminare il documento "' + nomeFile + '"?' ;
document . getElementById ( 'confirmDeleteBtn' ) . onclick = function () { performDelete ( id ); };
$ ( '#deleteConfirmModal' ) . modal ( 'show' );
} else {
if ( confirm ( 'Eliminare il documento "' + nomeFile + '"?' )) {
performDelete ( id );
}
}
})
. catch ( error => {
alert ( 'Errore: ' + error . message );
});
}
function performDelete ( id ) {
fetch ( '/documenti/' + id , {
method : 'POST' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
'Content-Type' : 'application/x-www-form-urlencoded' ,
},
body : '_method=DELETE'
})
. then ( response => {
if ( response . redirected ) {
window . location . href = response . url ;
} else {
window . location . reload ();
}
})
. catch ( error => {
alert ( 'Errore: ' + error . message );
});
}
function previewDoc ( id , mimeType ) {
document . getElementById ( 'previewFrame' ) . src = '/documenti/' + id + '/preview' ;
document . getElementById ( 'previewDownloadBtn' ) . href = '/documenti/' + id + '/download' ;
$ ( '#previewModal' ) . modal ( 'show' );
}
function loadUploadTargets () {
const visibilita = document . getElementById ( 'uploadVisibilita' ) . value ;
const group = document . getElementById ( 'uploadTargetGroup' );
const select = document . getElementById ( 'uploadTargetId' );
const type = document . getElementById ( 'uploadTargetType' );
if ( visibilita === 'pubblico' ) {
group . style . display = 'none' ;
select . innerHTML = '<option value="">Seleziona...</option>' ;
type . value = '' ;
return ;
}
group . style . display = 'block' ;
let data = [];
if ( visibilita === 'individuo' ) {
data = individui ;
type . value = 'individuo' ;
} else if ( visibilita === 'gruppo' ) {
data = gruppi ;
type . value = 'gruppo' ;
}
select . innerHTML = '<option value="">Seleziona...</option>' ;
data . forEach ( item => {
const opt = document . createElement ( 'option' );
opt . value = item . id ;
opt . textContent = item . label ;
select . appendChild ( opt );
});
}
function loadTargets ( type ) {
const select = document . getElementById ( 'targetSelect' );
select . innerHTML = '<option value="">Caricamento...</option>' ;
let data = [];
if ( type === 'individuo' ) data = individui ;
else if ( type === 'gruppo' ) data = gruppi ;
else if ( type === 'evento' ) data = eventi ;
else if ( type === 'mailing' ) data = mailingLists ;
select . innerHTML = '<option value="">Seleziona...</option>' ;
data . forEach ( item => {
const opt = document . createElement ( 'option' );
opt . value = item . id ;
opt . textContent = item . label ;
select . appendChild ( opt );
});
}
function submitAssociate () {
const ids = getSelectedIds ();
if ( ids . length === 0 ) {
alert ( 'Seleziona almeno un documento' );
return ;
}
const targetType = document . querySelector ( '#associateForm select[name="target_type"]' ) . value ;
const targetId = document . getElementById ( 'targetSelect' ) . value ;
if ( ! targetType || ! targetId ) {
alert ( 'Seleziona tipo e riferimento' );
return ;
}
document . getElementById ( 'associateIds' ) . value = ids . join ( ',' );
document . getElementById ( 'associateForm' ) . submit ();
}
2026-05-28 09:34:28 +02:00
function toggleUpdateTarget ( value ) {
const group = document . getElementById ( 'updateTargetGroup' );
const select = document . getElementById ( 'updateTargetSelect' );
const type = document . getElementById ( 'updateTargetType' );
if ( ! value || value === 'pubblico' || value === 'associazione' || value === 'federazione' ) {
group . style . display = 'none' ;
select . innerHTML = '<option value="">Seleziona...</option>' ;
type . value = '' ;
return ;
}
group . style . display = 'block' ;
let data = [];
let targetType = '' ;
if ( value === 'individuo' ) { data = individui ; targetType = 'individuo' ; }
else if ( value === 'gruppo' ) { data = gruppi ; targetType = 'gruppo' ; }
else if ( value === 'evento' ) { data = eventi ; targetType = 'evento' ; }
else if ( value === 'mailing' ) { data = mailingLists ; targetType = 'mailing' ; }
type . value = targetType ;
select . innerHTML = '<option value="">Seleziona...</option>' ;
data . forEach ( item => {
const opt = document . createElement ( 'option' );
opt . value = item . id ;
opt . textContent = item . label ;
select . appendChild ( opt );
});
}
2026-05-26 08:14:29 +02:00
function submitUpdate () {
const ids = getSelectedIds ();
if ( ids . length === 0 ) {
alert ( 'Seleziona almeno un documento' );
return ;
}
const tipologia = document . querySelector ( '#updateForm select[name="tipologia"]' ) . value ;
const visibilita = document . querySelector ( '#updateForm select[name="visibilita"]' ) . value ;
if ( ! tipologia && ! visibilita ) {
alert ( 'Seleziona almeno una tipologia o un contesto da modificare' );
return ;
}
document . getElementById ( 'updateIds' ) . value = ids . join ( ',' );
document . getElementById ( 'updateForm' ) . submit ();
}
2026-05-28 09:34:28 +02:00
// Associa documento singolo
function showAssociateModal ( id ) {
document . getElementById ( 'associateIds' ) . value = id ;
document . querySelector ( '#associateForm select[name="target_type"]' ) . value = '' ;
document . getElementById ( 'targetSelect' ) . innerHTML = '<option value="">Seleziona prima il tipo...</option>' ;
$ ( '#associateModal' ) . modal ( 'show' );
}
// Cambia tipo/contesto documento singolo
function showUpdateModal ( id ) {
document . getElementById ( 'updateIds' ) . value = id ;
document . querySelector ( '#updateForm select[name="tipologia"]' ) . value = '' ;
document . querySelector ( '#updateForm select[name="visibilita"]' ) . value = '' ;
document . getElementById ( 'updateTargetGroup' ) . style . display = 'none' ;
document . getElementById ( 'updateTargetSelect' ) . innerHTML = '<option value="">Seleziona...</option>' ;
$ ( '#updateModal' ) . modal ( 'show' );
}
// Sposta documento
function showMoveModal ( id , nome ) {
document . getElementById ( 'moveDocumentoId' ) . value = id ;
document . getElementById ( 'moveDocumentoNome' ) . textContent = nome ;
$ ( '#moveModal' ) . modal ( 'show' );
}
document . getElementById ( 'massMoveModal' ) ? . addEventListener ( 'show.bs.modal' , function () {
const ids = getSelectedIds ();
document . getElementById ( 'massMoveIds' ) . value = ids . join ( ',' );
document . getElementById ( 'massMoveCount' ) . textContent = ids . length ;
});
2026-06-09 11:02:01 +02:00
document . getElementById ( 'massTagModal' ) ? . addEventListener ( 'show.bs.modal' , function () {
const ids = getSelectedIds ();
document . getElementById ( 'massTagIds' ) . value = ids . join ( ',' );
document . getElementById ( 'massTagCount' ) . textContent = ids . length ;
});
2026-05-28 09:34:28 +02:00
document . getElementById ( 'massMoveNewFolderBtn' ) ? . addEventListener ( 'click' , function () {
Swal . fire ({
title : 'Nuova cartella' ,
input : 'text' ,
inputLabel : 'Nome della cartella' ,
inputPlaceholder : 'Inserisci il nome...' ,
showCancelButton : true ,
confirmButtonText : 'Crea' ,
cancelButtonText : 'Annulla' ,
confirmButtonColor : '#28a745' ,
inputValidator : ( value ) => {
if ( ! value || ! value . trim ()) {
return 'Il nome non può essere vuoto' ;
}
},
preConfirm : ( nome ) => {
return fetch ( '/documenti/cartelle' , {
method : 'POST' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
'Content-Type' : 'application/x-www-form-urlencoded' ,
'Accept' : 'application/json' ,
},
body : 'nome=' + encodeURIComponent ( nome . trim ()),
})
. then ( response => {
if ( ! response . ok ) {
return response . json () . then ( err => { throw new Error ( err . message || 'Errore creazione cartella' ); });
}
return response . json ();
})
. then ( data => {
if ( ! data . success ) {
throw new Error ( data . message || 'Errore creazione cartella' );
}
return data ;
})
. catch ( error => {
Swal . showValidationMessage ( error . message );
});
}
}) . then (( result ) => {
if ( result . isConfirmed ) {
const data = result . value ;
const select = document . querySelector ( '#massMoveModal select[name="cartella_id"]' );
const opt = document . createElement ( 'option' );
opt . value = data . cartella . id ;
opt . textContent = data . cartella . nome ;
opt . selected = true ;
select . appendChild ( opt );
Swal . fire ({
icon : 'success' ,
title : 'Cartella creata' ,
text : 'Cartella "' + data . cartella . nome + '" creata e selezionata.' ,
timer : 2000 ,
showConfirmButton : false ,
});
}
});
});
2026-06-01 22:55:50 +02:00
document . getElementById ( 'uploadNewFolderBtn' ) ? . addEventListener ( 'click' , function ( e ) {
e . preventDefault ();
window . _uploadNewFolder = true ;
$ ( '#uploadModal' ) . modal ( 'hide' );
$ ( '#newFolderModal' ) . modal ( 'show' );
});
2026-05-28 09:34:28 +02:00
document . getElementById ( 'massMoveForm' ) ? . addEventListener ( 'submit' , function ( e ) {
e . preventDefault ();
const form = this ;
const formData = new FormData ( form );
fetch ( '/documenti/mass-move' , {
method : 'POST' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
},
body : formData ,
})
. then ( response => {
if ( response . redirected ) {
window . location . href = response . url ;
} else {
window . location . reload ();
}
})
. catch ( error => {
alert ( 'Errore: ' + error . message );
});
});
document . getElementById ( 'moveForm' ) ? . addEventListener ( 'submit' , function ( e ) {
e . preventDefault ();
const form = this ;
const formData = new FormData ( form );
const id = document . getElementById ( 'moveDocumentoId' ) . value ;
fetch ( '/documenti/' + id + '/move' , {
method : 'POST' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
'Accept' : 'application/json' ,
},
body : formData ,
})
. then ( response => response . json ())
. then ( data => {
if ( data . success ) {
$ ( '#moveModal' ) . modal ( 'hide' );
window . location . reload ();
} else {
alert ( 'Errore: ' + ( data . message || 'Impossibile spostare il documento' ));
}
})
. catch ( error => {
alert ( 'Errore: ' + error . message );
});
});
// Toggle vista griglia/lista
function setView ( view ) {
localStorage . setItem ( 'documenti_view' , view );
var isGrid = view === 'grid' ;
document . getElementById ( 'viewGrid' ) . style . display = isGrid ? '' : 'none' ;
document . getElementById ( 'viewList' ) . style . display = isGrid ? 'none' : '' ;
document . getElementById ( 'viewGridBtn' ) . classList . toggle ( 'active' , isGrid );
document . getElementById ( 'viewListBtn' ) . classList . toggle ( 'active' , ! isGrid );
if ( currentRepoBrowse ) {
loadRemoteContents ( currentRepoBrowse . id , currentRepoBrowse . path );
}
}
// Init vista da localStorage
( function () {
const saved = localStorage . getItem ( 'documenti_view' );
if ( saved === 'list' ) {
setView ( 'list' );
} else {
setView ( 'grid' );
}
})();
// Nuova cartella AJAX
document . getElementById ( 'newFolderForm' ) ? . addEventListener ( 'submit' , function ( e ) {
e . preventDefault ();
const form = this ;
const formData = new FormData ( form );
fetch ( '/documenti/cartelle' , {
method : 'POST' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
'Accept' : 'application/json' ,
},
body : formData ,
})
. then ( response => response . json ())
. then ( data => {
if ( data . success ) {
2026-06-01 22:55:50 +02:00
if ( window . _uploadNewFolder ) {
window . _uploadNewFolder = false ;
$ ( '#newFolderModal' ) . modal ( 'hide' );
$ ( '#uploadModal' ) . modal ( 'show' );
const select = document . getElementById ( 'uploadCartellaId' );
if ( select ) {
const opt = document . createElement ( 'option' );
opt . value = data . cartella . id ;
opt . textContent = data . cartella . nome ;
opt . selected = true ;
select . appendChild ( opt );
}
} else {
window . location . reload ();
}
2026-05-28 09:34:28 +02:00
} else {
alert ( 'Errore: ' + ( data . message || 'Impossibile creare la cartella' ));
}
})
. catch ( error => {
alert ( 'Errore: ' + error . message );
});
});
function toggleUploadRepo () {
const repoSelect = document . getElementById ( 'uploadRepository' );
2026-06-01 22:55:50 +02:00
const folderGroup = document . getElementById ( 'uploadFolderGroup' );
const cartellaSelect = document . getElementById ( 'uploadCartellaId' );
2026-05-28 09:34:28 +02:00
if ( repoSelect && repoSelect . value !== '' ) {
2026-06-01 22:55:50 +02:00
if ( folderGroup ) folderGroup . style . display = 'none' ;
if ( cartellaSelect ) cartellaSelect . disabled = true ;
2026-05-28 09:34:28 +02:00
} else {
2026-06-01 22:55:50 +02:00
if ( folderGroup ) folderGroup . style . display = '' ;
if ( cartellaSelect ) cartellaSelect . disabled = false ;
2026-05-28 09:34:28 +02:00
}
}
// === Remote Repository AJAX Browser ===
2026-05-29 06:23:57 +02:00
var currentRepoBrowse = null ;
2026-05-28 09:34:28 +02:00
function showLocalContent () {
document . getElementById ( 'localContent' ) . style . display = '' ;
document . getElementById ( 'remoteContent' ) . style . display = 'none' ;
document . querySelectorAll ( '.repo-link' ) . forEach ( el => el . classList . remove ( 'active' ));
currentRepoBrowse = null ;
}
function browseRepo ( repoId , repoNome , path ) {
currentRepoBrowse = { id : repoId , nome : repoNome , path : path };
document . getElementById ( 'localContent' ) . style . display = 'none' ;
document . getElementById ( 'remoteContent' ) . style . display = 'block' ;
document . querySelectorAll ( '.repo-link' ) . forEach ( el => el . classList . remove ( 'active' ));
const link = document . querySelector ( '.repo-link[data-repo-id="' + repoId + '"]' );
if ( link ) link . classList . add ( 'active' );
updateRemoteBreadcrumb ( repoNome , path );
loadRemoteContents ( repoId , path );
}
function updateRemoteBreadcrumb ( nome , path ) {
const bc = document . getElementById ( 'remoteBreadcrumb' );
const parts = path . replace ( /^ \ /+| \ /+ $ / g , '' ) . split ( '/' ) . filter ( Boolean );
2026-05-28 13:25:47 +02:00
let html = '<i class="' + ( currentRepoBrowse && currentRepoBrowse . id ? repoIconFromId ( currentRepoBrowse . id ) : 'fa fa-server' ) + ' mr-1"></i> ' + escHtml ( nome );
2026-05-28 09:34:28 +02:00
if ( parts . length > 0 && parts [ 0 ] !== '' ) {
html += ' / ' + parts . map ( p => escHtml ( p )) . join ( ' / ' );
}
bc . innerHTML = html ;
}
function repoIconFromId ( repoId ) {
const link = document . querySelector ( '.repo-link[data-repo-id="' + repoId + '"]' );
if ( ! link ) return 'fa-server' ;
const icon = link . querySelector ( 'i' );
return icon ? icon . className : 'fa-server' ;
}
function loadRemoteContents ( repoId , path ) {
document . getElementById ( 'remoteGrid' ) . innerHTML = '' ;
document . getElementById ( 'remoteList' ) . innerHTML = '' ;
document . getElementById ( 'remoteEmpty' ) . style . display = 'none' ;
document . getElementById ( 'remoteLoading' ) . style . display = 'block' ;
const view = localStorage . getItem ( 'documenti_view' ) || 'grid' ;
2026-05-29 06:23:57 +02:00
const url = '/storage-repositories/' + repoId + '/browse?path=' + encodeURIComponent ( path );
console . log ( 'Loading remote contents from:' , url );
fetch ( url , {
method : 'GET' ,
headers : {
'Accept' : 'application/json' ,
'X-Requested-With' : 'XMLHttpRequest'
}
})
. then ( response => {
console . log ( 'Response status:' , response . status );
if ( ! response . ok ) {
throw new Error ( 'HTTP Error: ' + response . status );
}
return response . json ();
2026-05-28 09:34:28 +02:00
})
. then ( data => {
2026-05-29 06:23:57 +02:00
console . log ( 'Data received:' , data );
2026-05-28 09:34:28 +02:00
document . getElementById ( 'remoteLoading' ) . style . display = 'none' ;
2026-05-29 06:23:57 +02:00
if ( data . success && data . contents !== undefined ) {
console . log ( 'Contents count:' , data . contents . length );
2026-05-28 09:34:28 +02:00
renderRemoteContents ( data . contents , repoId , path , view );
2026-05-29 06:23:57 +02:00
} else if ( data . contents !== undefined && data . contents . length === 0 ) {
document . getElementById ( 'remoteEmpty' ) . style . display = 'block' ;
document . getElementById ( 'remoteEmpty' ) . querySelector ( 'p' ) . textContent = 'Cartella vuota' ;
2026-05-28 09:34:28 +02:00
} else {
document . getElementById ( 'remoteEmpty' ) . style . display = 'block' ;
2026-05-29 06:23:57 +02:00
const msg = data . message || 'Errore caricamento repository.' ;
console . error ( 'Error response:' , msg );
document . getElementById ( 'remoteEmpty' ) . querySelector ( 'p' ) . textContent = msg ;
2026-05-28 09:34:28 +02:00
}
})
. catch ( error => {
document . getElementById ( 'remoteLoading' ) . style . display = 'none' ;
document . getElementById ( 'remoteEmpty' ) . style . display = 'block' ;
2026-05-29 06:23:57 +02:00
console . error ( 'Fetch error:' , error );
2026-05-28 09:34:28 +02:00
document . getElementById ( 'remoteEmpty' ) . querySelector ( 'p' ) . textContent = 'Errore: ' + error . message ;
});
}
function renderRemoteContents ( contents , repoId , currentPath , view ) {
contents . sort ( function ( a , b ) {
if ( a . type === 'dir' && b . type !== 'dir' ) return - 1 ;
if ( b . type === 'dir' && a . type !== 'dir' ) return 1 ;
return ( a . basename || '' ) . localeCompare ( b . basename || '' );
});
if ( contents . length === 0 ) {
document . getElementById ( 'remoteEmpty' ) . style . display = 'block' ;
document . getElementById ( 'remoteEmpty' ) . querySelector ( 'p' ) . textContent = 'Cartella vuota' ;
return ;
}
var gridContainer = document . getElementById ( 'remoteGrid' );
var listContainer = document . getElementById ( 'remoteList' );
gridContainer . innerHTML = '' ;
listContainer . innerHTML = '' ;
if ( view === 'grid' ) {
document . getElementById ( 'remoteGrid' ) . style . display = '' ;
document . getElementById ( 'remoteList' ) . style . display = 'none' ;
var html = '' ;
contents . forEach ( function ( item ) {
if ( item . type === 'dir' ) {
html += '<div class="col-md-4 col-sm-6 mb-3">' ;
html += '<div class="card card-outline card-warning h-100" style="cursor:pointer;" onclick="browseRepo(' + repoId + ',\'' + escJs ( currentRepoBrowse ? currentRepoBrowse . nome : '' ) + '\',\'' + escJs ( item . path ) + '\')">' ;
html += '<div class="card-body p-3 text-center">' ;
html += '<div class="py-3" style="font-size:2.5rem;"><i class="fas fa-folder text-warning"></i></div>' ;
html += '<h6 class="font-weight-bold text-truncate mb-0" title="' + escHtml ( item . basename ) + '">' + escHtml ( item . basename ) + '</h6>' ;
html += '</div></div></div>' ;
} else {
var icon = getFileIcon ( item . mimeType , item . basename );
var size = item . fileSize ? formatFileSize ( item . fileSize ) : '-' ;
var isPreviewable = item . mimeType && item . mimeType . match ( /^ ( image\ /| application\ / pdf | text\ / ) / );
html += '<div class="col-md-4 col-sm-6 mb-3">' ;
html += '<div class="card card-outline card-secondary h-100">' ;
html += '<div class="card-body p-3">' ;
html += '<h6 class="card-title font-weight-bold text-truncate mb-2" title="' + escHtml ( item . basename ) + '">' + escHtml ( item . basename ) + '</h6>' ;
html += '<div class="text-center py-2" style="font-size:2.5rem;"><i class="fas ' + icon + '"></i></div>' ;
html += '<div class="text-center mb-2"><span class="badge badge-light">' + size + '</span></div>' ;
html += '</div>' ;
html += '<div class="card-footer p-2 text-center">' ;
if ( isPreviewable ) {
html += '<button type="button" class="btn btn-xs btn-primary" onclick="previewRepoFile(' + repoId + ',\'' + escJs ( item . path ) + '\',\'' + escJs ( item . mimeType || '' ) + '\')" title="Anteprima"><i class="fas fa-eye"></i></button> ' ;
}
2026-05-29 06:54:07 +02:00
html += '<button type="button" class="btn btn-xs btn-info" onclick="downloadRepoFile(' + repoId + ',\'' + escJs ( item . path ) + '\')" title="Scarica"><i class="fas fa-download"></i></button> ' ;
html += '<button type="button" class="btn btn-xs btn-success" onclick="importRepoFile(' + repoId + ',\'' + escJs ( item . path ) + '\',\'' + escJs ( item . basename ) + '\')" title="Salva tra i documenti"><i class="fas fa-save"></i></button>' ;
2026-05-28 09:34:28 +02:00
html += '</div></div></div>' ;
}
});
gridContainer . innerHTML = html ;
} else {
document . getElementById ( 'remoteGrid' ) . style . display = 'none' ;
document . getElementById ( 'remoteList' ) . style . display = '' ;
2026-05-29 06:54:07 +02:00
var tblHtml = '<table class="table table-bordered table-hover table-striped mb-0"><thead class="thead-light"><tr><th>Nome</th><th style="width:90px;">Dimensione</th><th style="width:100px;">Data</th><th style="width:150px;">Azioni</th></tr></thead><tbody>' ;
2026-05-28 09:34:28 +02:00
contents . forEach ( function ( item ) {
if ( item . type === 'dir' ) {
tblHtml += '<tr class="table-warning" style="cursor:pointer;" onclick="browseRepo(' + repoId + ',\'' + escJs ( currentRepoBrowse ? currentRepoBrowse . nome : '' ) + '\',\'' + escJs ( item . path ) + '\')">' ;
tblHtml += '<td><i class="fas fa-folder text-warning mr-2"></i><span class="font-weight-bold">' + escHtml ( item . basename ) + '</span></td>' ;
tblHtml += '<td class="small text-muted">-</td>' ;
tblHtml += '<td class="small text-muted">' + ( item . lastModified ? formatTimestamp ( item . lastModified ) : '-' ) + '</td>' ;
tblHtml += '<td><button class="btn btn-xs btn-warning" onclick="event.stopPropagation(); browseRepo(' + repoId + ',\'' + escJs ( currentRepoBrowse ? currentRepoBrowse . nome : '' ) + '\',\'' + escJs ( item . path ) + '\')"><i class="fas fa-folder-open"></i></button></td>' ;
tblHtml += '</tr>' ;
} else {
var icon = getFileIcon ( item . mimeType , item . basename );
var size = item . fileSize ? formatFileSize ( item . fileSize ) : '-' ;
var isPreviewable = item . mimeType && item . mimeType . match ( /^ ( image\ /| application\ / pdf | text\ / ) / );
tblHtml += '<tr>' ;
tblHtml += '<td><i class="fas ' + icon + ' mr-2"></i><span class="font-weight-bold">' + escHtml ( item . basename ) + '</span></td>' ;
tblHtml += '<td class="small">' + size + '</td>' ;
tblHtml += '<td class="small">' + ( item . lastModified ? formatTimestamp ( item . lastModified ) : '-' ) + '</td>' ;
tblHtml += '<td>' ;
if ( isPreviewable ) {
tblHtml += '<button type="button" class="btn btn-xs btn-primary" onclick="previewRepoFile(' + repoId + ',\'' + escJs ( item . path ) + '\',\'' + escJs ( item . mimeType || '' ) + '\')" title="Anteprima"><i class="fas fa-eye"></i></button> ' ;
}
2026-05-29 06:54:07 +02:00
tblHtml += '<button type="button" class="btn btn-xs btn-info" onclick="downloadRepoFile(' + repoId + ',\'' + escJs ( item . path ) + '\')" title="Scarica"><i class="fas fa-download"></i></button> ' ;
tblHtml += '<button type="button" class="btn btn-xs btn-success" onclick="importRepoFile(' + repoId + ',\'' + escJs ( item . path ) + '\',\'' + escJs ( item . basename ) + '\')" title="Salva tra i documenti"><i class="fas fa-save"></i></button>' ;
2026-05-28 09:34:28 +02:00
tblHtml += '</td></tr>' ;
}
});
tblHtml += '</tbody></table>' ;
listContainer . innerHTML = tblHtml ;
}
}
function downloadRepoFile ( repoId , path ) {
window . location . href = '/storage-repositories/' + repoId + '/download?path=' + encodeURIComponent ( path );
}
function previewRepoFile ( repoId , path , mimeType ) {
document . getElementById ( 'previewFrame' ) . src = '/storage-repositories/' + repoId + '/preview?path=' + encodeURIComponent ( path );
document . getElementById ( 'previewDownloadBtn' ) . href = '/storage-repositories/' + repoId + '/download?path=' + encodeURIComponent ( path );
$ ( '#previewModal' ) . modal ( 'show' );
}
2026-05-29 06:54:07 +02:00
function toggleImportTarget ( value ) {
var group = document . getElementById ( 'swal-target-group' );
var select = document . getElementById ( 'swal-target-select' );
if ( ! value || value === 'pubblico' ) {
group . style . display = 'none' ;
select . innerHTML = '<option value="">-- Nessuno --</option>' ;
return ;
}
group . style . display = 'block' ;
var data = [];
if ( value === 'individuo' ) data = individui ;
else if ( value === 'gruppo' ) data = gruppi ;
else if ( value === 'evento' ) data = eventi ;
else if ( value === 'mailing' ) data = mailingLists ;
select . innerHTML = '<option value="">-- Seleziona --</option>' ;
data . forEach ( function ( item ) {
var opt = document . createElement ( 'option' );
opt . value = item . id ;
opt . textContent = item . label ;
select . appendChild ( opt );
});
}
function importRepoFile ( repoId , path , basename ) {
var folderOptions = '<option value="">-- Nessuna --</option>' ;
cartelle . forEach ( function ( f ) {
folderOptions += '<option value="' + f . id + '">' + escHtml ( f . nome ) + '</option>' ;
if ( f . children ) {
f . children . forEach ( function ( c ) {
folderOptions += '<option value="' + c . id + '"> ' + escHtml ( c . nome ) + '</option>' ;
});
}
});
Swal . fire ({
title : 'Importa come documento locale' ,
html :
'<div class="text-left">' +
'<p class="mb-1"><strong>File:</strong> ' + escHtml ( basename ) + '</p>' +
'<div class="form-group mb-2">' +
'<label class="small">Tipologia</label>' +
'<select id="swal-tipologia" class="form-control form-control-sm">' +
'<option value="documento" selected>Documento</option>' +
'<option value="avatar">Avatar</option>' +
'<option value="galleria">Galleria</option>' +
'<option value="statuto">Statuto</option>' +
'<option value="altro">Altro</option>' +
'</select></div>' +
'<div class="form-group mb-2">' +
'<label class="small">Cartella (opzionale)</label>' +
'<select id="swal-cartella" class="form-control form-control-sm">' + folderOptions + '</select></div>' +
'<div class="form-group mb-2">' +
'<label class="small">Visibilità</label>' +
'<select id="swal-visibilita" class="form-control form-control-sm" onchange="toggleImportTarget(this.value)">' +
'<option value="pubblico" selected>Pubblico</option>' +
'<option value="individuo">Individuo</option>' +
'<option value="gruppo">Gruppo</option>' +
'<option value="evento">Evento</option>' +
'<option value="mailing">Mailing List</option>' +
'</select></div>' +
'<div id="swal-target-group" class="form-group mb-0" style="display:none;">' +
'<label class="small">Collega a</label>' +
'<select id="swal-target-select" class="form-control form-control-sm">' +
'<option value="">-- Seleziona --</option>' +
'</select></div>' +
'</div>' ,
showCancelButton : true ,
confirmButtonText : 'Importa' ,
cancelButtonText : 'Annulla' ,
preConfirm : function () {
var visibilita = document . getElementById ( 'swal-visibilita' ) . value ;
var targetEl = document . getElementById ( 'swal-target-select' );
return {
tipologia : document . getElementById ( 'swal-tipologia' ) . value ,
cartella_id : document . getElementById ( 'swal-cartella' ) . value ,
visibilita : visibilita ,
visibilita_target_id : visibilita !== 'pubblico' && targetEl && targetEl . value ? targetEl . value : null
};
}
}) . then ( function ( result ) {
if ( ! result . isConfirmed ) return ;
var data = result . value ;
fetch ( '/storage-repositories/' + repoId + '/import' , {
method : 'POST' ,
headers : {
'Content-Type' : 'application/json' ,
'X-CSRF-TOKEN' : document . querySelector ( 'meta[name=\"csrf-token\"]' ) . getAttribute ( 'content' ),
'Accept' : 'application/json'
},
body : JSON . stringify ({
path : path ,
basename : basename ,
tipologia : data . tipologia ,
cartella_id : data . cartella_id || null ,
visibilita : data . visibilita ,
visibilita_target_id : data . visibilita_target_id
})
})
. then ( function ( r ) { return r . json (); })
. then ( function ( r ) {
if ( r . success ) {
Swal . fire ( 'Importato!' , r . message , 'success' );
} else {
Swal . fire ( 'Errore' , r . message || 'Errore durante l\'importazione.' , 'error' );
}
})
. catch ( function ( e ) {
Swal . fire ( 'Errore' , 'Errore di rete: ' + e . message , 'error' );
});
});
}
2026-05-28 09:34:28 +02:00
function getFileIcon ( mimeType , basename ) {
if ( ! mimeType ) {
var ext = ( basename || '' ) . split ( '.' ) . pop () . toLowerCase ();
mimeType = '' ;
if ([ 'jpg' , 'jpeg' , 'png' , 'gif' , 'webp' , 'svg' ] . indexOf ( ext ) >= 0 ) mimeType = 'image/' ;
else if ( ext === 'pdf' ) mimeType = 'application/pdf' ;
else if ([ 'doc' , 'docx' ] . indexOf ( ext ) >= 0 ) mimeType = 'application/msword' ;
else if ([ 'xls' , 'xlsx' ] . indexOf ( ext ) >= 0 ) mimeType = 'application/vnd.ms-excel' ;
else if ([ 'zip' , 'rar' , '7z' , 'tar' , 'gz' ] . indexOf ( ext ) >= 0 ) mimeType = 'application/zip' ;
else if ([ 'txt' , 'csv' , 'html' , 'htm' ] . indexOf ( ext ) >= 0 ) mimeType = 'text/plain' ;
}
if ( mimeType === 'application/pdf' ) return 'fa-file-pdf text-danger' ;
if ( mimeType . match ( /^ image\ //)) return 'fa-file-image text-success';
if ( mimeType . match ( /^ text\ //)) return 'fa-file-alt text-info';
if ( mimeType . match ( / word / )) return 'fa-file-word text-primary' ;
if ( mimeType . match ( / excel | spreadsheet / )) return 'fa-file-excel text-success' ;
if ( mimeType . match ( / zip | rar | compress / )) return 'fa-file-archive text-warning' ;
return 'fa-file text-secondary' ;
}
function formatFileSize ( bytes ) {
if ( ! bytes ) return '-' ;
return ( bytes / 1024 ) . toFixed ( 1 ) + ' KB' ;
}
function formatTimestamp ( ts ) {
if ( ! ts ) return '-' ;
var d = new Date ( ts * 1000 );
return d . toLocaleDateString ( 'it-IT' );
}
function escHtml ( str ) {
if ( ! str ) return '' ;
var div = document . createElement ( 'div' );
div . appendChild ( document . createTextNode ( str ));
return div . innerHTML ;
}
function escJs ( str ) {
if ( ! str ) return '' ;
return str . replace ( / '/g, "\\' " ).replace(/ " / g , '"' ) . replace ( / \n / g , '\\n' );
}
// === End Remote Repository AJAX Browser ===
// Folder rename
function renameFolder ( id , currentName ) {
Swal . fire ({
title : 'Rinomina cartella' ,
input : 'text' ,
inputLabel : 'Nuovo nome' ,
inputValue : currentName ,
showCancelButton : true ,
confirmButtonText : 'Salva' ,
cancelButtonText : 'Annulla' ,
inputValidator : ( value ) => {
if ( ! value || ! value . trim ()) return 'Il nome non può essere vuoto' ;
},
preConfirm : ( nome ) => {
return fetch ( '/documenti/cartelle/' + id , {
method : 'PUT' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
'Content-Type' : 'application/x-www-form-urlencoded' ,
'Accept' : 'application/json' ,
},
body : 'nome=' + encodeURIComponent ( nome . trim ()),
})
. then ( response => response . json ())
. then ( data => {
if ( ! data . success ) throw new Error ( data . message || 'Errore' );
return data ;
})
. catch ( error => {
Swal . showValidationMessage ( error . message );
});
}
}) . then (( result ) => {
if ( result . isConfirmed ) {
window . location . reload ();
}
});
}
// Folder move
function moveFolder ( id , nome ) {
const folders = @ json ( $cartelleMoveOptions );
let options = '<option value="">— Radice —</option>' ;
folders . forEach ( function ( f ) {
if ( f . id !== id ) {
options += '<option value="' + f . id + '">' + escHtml ( f . nome ) + '</option>' ;
( f . children || []) . forEach ( function ( c ) {
if ( c . id !== id ) {
options += '<option value="' + c . id + '"> ' + escHtml ( c . nome ) + '</option>' ;
}
});
}
});
Swal . fire ({
title : 'Sposta cartella' ,
html : '<p class="mb-2">Spostare la cartella <strong>' + escHtml ( nome ) + '</strong> in:</p>' +
'<select id="moveFolderParent" class="form-control">' + options + '</select>' ,
showCancelButton : true ,
confirmButtonText : 'Sposta' ,
cancelButtonText : 'Annulla' ,
preConfirm : () => {
const parentId = document . getElementById ( 'moveFolderParent' ) . value ;
return fetch ( '/documenti/cartelle/' + id , {
method : 'PUT' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
'Content-Type' : 'application/x-www-form-urlencoded' ,
'Accept' : 'application/json' ,
},
body : 'nome=' + encodeURIComponent ( nome ) + '&parent_id=' + encodeURIComponent ( parentId ),
})
. then ( response => response . json ())
. then ( data => {
if ( ! data . success ) throw new Error ( data . message || 'Errore spostamento' );
return data ;
})
. catch ( error => {
Swal . showValidationMessage ( error . message );
});
}
}) . then (( result ) => {
if ( result . isConfirmed ) {
window . location . reload ();
}
});
}
// Folder delete
function deleteFolder ( id , nome ) {
Swal . fire ({
title : 'Elimina cartella' ,
text : 'Eliminare la cartella "' + nome + '"?' ,
icon : 'warning' ,
showCancelButton : true ,
confirmButtonColor : '#dc3545' ,
confirmButtonText : 'Elimina' ,
cancelButtonText : 'Annulla' ,
preConfirm : () => {
return fetch ( '/documenti/cartelle/' + id , {
method : 'DELETE' ,
headers : {
'X-CSRF-TOKEN' : '{{ csrf_token() }}' ,
'Accept' : 'application/json' ,
},
})
. then ( response => response . json ())
. then ( data => {
if ( ! data . success ) throw new Error ( data . message || 'Errore' );
return data ;
})
. catch ( error => {
Swal . showValidationMessage ( error . message );
});
}
}) . then (( result ) => {
if ( result . isConfirmed ) {
window . location . reload ();
}
});
}
2026-05-26 08:14:29 +02:00
</ script >
2026-05-28 09:34:28 +02:00
@ endsection